태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.
VC6(Visual Studio 6.0에 들어있는 VC++)에서는 유니코드 문자(wchar_t)를 unsigned short로 처리하고,
VC8(Visual Studio .NET 2005에 들어있는 VC++)에서는 native type으로 처리한다.
그래서 VC6으로 제작된 유니코드 문자열을 다루는 DLL을 VC8에서 사용할 때는 Link에러가 나기 쉽상이다.

// VC6의 DLL 함수 선언
TEST_API const wchar_t* fnTest_wchar(void);

// VC8에서 VC6 DLL 사용
const wchar_t* text = fnTest_wchar();

코드상에서는 아무 문제가 없다.
하지만 VC8에서 컴파일해보면 아래와 같이 링크 에러가 난다.
error LNK2001: "__declspec(dllimport) wchar_t const * __cdecl fnTest_wchar(void)" (__imp_?fnTest_wchar@@YAPB_WXZ) 외부 기호를 확인할 수 없습니다.

VC6은 컴파일과 동시에 wchar_t를 unsigned short로 바꿔 버렸는데, VC8은 링크할 때 wchar_t의 함수를 찾기 때문에 찾을 수가 없어서 오류가 발생하는 것이다.
이런 문제는 클래스를 링크할 때 더욱 심각해진다.

// VC6 DLL의 클래스 선언
class TEST_API cStrTool
{
protected:
    wchar_t* m_pBuff;
public:
    cStrTool(const wchar_t*);
    ...
    const wchar_t* getText(void) const;
};

// VC8에서 VC6 DLL의 클래스 사용
cStrTool use = new cStrTool(L"test string");
const wchar_t* output = use.getText();

역시나 코드상으로는 전혀 문제가 없지만 생성과 사용 둘 다 링크 에러를 발생한다.

그래서 VC6쪽에서 약간의 수정을 가해주어야 한다.

// VC6 DLL의 클래스를 수정
class TEST_API cStrTool
{
protected:
    wchar_t* m_pBuff;
public:
    cStrTool(const void*); // <-- void*로 받아서 생성하도록 수정
    ...
    const wchar_t* getText(void) const;
};

// 구현은 형변환을 한 후 수행하도록 수정
cStrTool::cStrTool(const void* init_)
{
    const wchar_t* init = static_cast<const wchar_t*>(init_);
    ...
}

이제 VC6과 VC8에서 아무 문제없이 생성할 수 있다.
하지만 여전히 getText 메소드는 링크 에러를 발생시킨다.
게다가 본의 아니게 유니코드 문자열이 아닌 다른 종류의 포인터를 넘겨서 오류를 발생시킬 수도 있다.

문제를 일반적으로 해결하기 위해 Visual C++의 wchar_t에 대해 좀 더 자세한 정보를 찾아보았다.

VC6은 wchar.h 헤더에 아래와 같이 선언되어 있다.
#define _WCHAR_T_DEFINED
typedef unsigned short wchar_t;

그리고 VC8은 컴파일러 자체에 내장형으로 선언되어 있으며, 아래와 같은 매크로를 포함하고 있다.
내장형 type : wchar_t, __wchar_t
#define _WCHAR_T_DEFINED
#define _NATIVE_WCHAR_T_DEFINED

불행하게도..
코드상으로 완전하게 호환되게 하기 위해서는 VC6 DLL을 다소 수정하고 재컴파일해야 한다.
아래와 같이 사용되는 클래스를 확장하여 연결하는 방식으로 고쳐서 VC6에서 사용할 때도 VC8에서 사용할 때도 문제가 없도록 만들 수 있다.

// 앞으로 DLL 내부의 모든 유니코드 문자는 아래의 wchar을 사용한다. #if defined(_NATIVE_WCHAR_T_DEFINED) typedef unsigned short wchar; #elif defined(_WCHAR_T_DEFINED) typedef wchar_t wchar; #else typedef unsigned short wchar; #endif // VC6 DLL에서 제공될 클래스 class TEST_API cStrTool_ { protected: wchar* m_pBuff; public: cStrTool_(); cStrTool_(const wchar* init); virtual ~cStrTool_(); int getLength(void) const; const wchar* getText(void) const; }; #if !defined(_NATIVE_WCHAR_T_DEFINED) // VC6에서 사용할 때는 그냥 바로 연결해서 사용 typedef cStrTool_ cStrTool; #else // VC8에서 사용할 때는 상속 받아서 변환하기 위한 클래스 선언해서 사용 class cStrTool : public cStrTool_ { public: // 모든 종류의 생성자를 다 연결해줘야 똑같이 사용할 수 있다. cStrTool() {} cStrTool(const wchar_t* init) : cStrTool_( (const wchar*)init ) {} // 메서드는 wchar에 관련된 것만 재정의해줘도 된다. const wchar_t* getText(void) const { return (const wchar_t*)__super::getText(); } }; #endif
그리고 VC6이던 VC8이던 다음과 같이 그냥 사용하면 된다.

#include "stdafx.h" #include <iostream> using namespace std; #include <windows.h> #include "vc6_dll.h" #pragma comment(lib, "vc6_dll") int main(int argc, char* argv[]) { cStrTool str1(L"문자열 test"); const wchar_t* output = str1.getText(); cout << "str1 length: " << str1.getLength() << endl; MessageBoxW(NULL, output, L"DLL Link test : wchar_t", MB_OK); cStrTool str2; cout << "str2 length: " << str2.getLength() << endl; MessageBoxW(NULL, str2.getText(), L"DLL Link test : wchar_t", MB_OK); return 0; }


예제 프로젝트

저작자 표시 비영리 동일 조건 변경 허락
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

 체크하면 블로그 관리자에게만 내용을 공개합니다.