본문 바로가기
IT-개발,DB

[VC++/MFC] COM/ATL/STL - BSTR과 VARIANT, string, CComBSTR

by SB리치퍼슨 2010. 9. 30.
COM/ATL/STL - BSTR과 VARIANT, string, CComBSTR

COM 관련 코드를 작성하려고 보면 여기서만 쓰이는 생소한 데이터 형들이 등장하는데, 그중 문자열관련해서 다음과 같은 것을
볼 수 있다.


BSTR
Pascal-Style(길이값 내장)과 C-Style(널종료문자)을 섞어 놓은 형식으로 기본 구조는 다음과 같다.
-    4Byte(길이정수) + 문자값(2Byte) + 종료문자( 0 2개 )
즉, 최초에 DWORD의 정수데이터가 붙고 그 뒤로 Unicode식의 2Byte Encoding 문자열이 붙는형식이다.
그러나 이 앞쪽의 정수 부분은 C++ 코드 작성시에는 없다고 생각해야 한다.
왜냐하면, typedef OLECHAR* BSTR; 로 선언 되어 있기 때문.
COM을 통해 데이터가 전송될 때 알아서 마샬링 되는 것 같다.
아무튼 실제로 BSTR은 WCHAR과 다름에도 불구하고 내부적으로 같은 형식으로 인식되기 때문에 주의를 해야한다.
함수에 인자로 넘길경우 컴파일 오류가 안지 않더라도 내부적으로 오류가 발생하기 쉽다.

게다가 BSTR 은 COM 라이브러리를 통해 마샬링 되어야 하기 때문에 메모리 관리를 다른 곳에서 해야한다.
즉, 사용할 때 메모리 할당과 해제를 명시적으로 API를 통해서 해야한다.
SysAllocString() : 메모리 할당시 사용
SysFreeString() : 메모리 해제시 사용
BSTR bstr = NULL;

bstr = SysAllocString ( L"Hi Bob!" );

if ( NULL == bstr )
  // out of memory error

// Use bstr here...

SysFreeString ( bstr );

이렇게 사용한다.


_bstr_t
BSTR데이터형을 사용하는게 귀찮은 사람들을 위한 BSTR Wrapper 클래스다.
BSTR대신 함수에 넘겨줄수는 없다 ? 가능하지만 상당히 제약적이다.
직접 내부 BSTR에 접근이 안돼기 때문에 함수에 BSTR대신 넘겨주기 위해서는 ATL에서 지원하는 CComBSTR클래스를
사용하면 된다.
다음과 같이 생성 및 문자열 encoding 변환을 할 수 있다.
// Constructing
_bstr_t bs1 = "char string"; & // construct from a LPCSTR
_bstr_t bs2 = L"wide char string"; // construct from a LPCWSTR
//내부적으로 2Byte Unicode 형식이지만, char / wchar_t 양쪽에서 생성 할 수 있다.

_bstr_t bs3 = bs1;  // copy from another _bstr_t
_variant_t v = "Bob";
_bstr_t bs4 = v;  // construct from a _variant_t that has a string

// Extracting data
LPCSTR psz1 = bs1;  // automatically converts to MBCS string
LPCSTR psz2 = (LPCSTR) bs1;  // cast OK, same as previous line
LPCWSTR pwsz1 = bs1;  // returns the internal Unicode string
LPCWSTR pwsz2 = (LPCWSTR) bs1; // cast OK, same as previous line 변환된다!!
BSTR  bstr = bs1.copy()// copies bs1, returns it as a BSTR

// ...
SysFreeString ( bstr ); // 수동적으로 메모리를 해제해줘야 한다.




_variant_t
COM에서 사용되는 VARIANT의 wrapper 클래스다.
당연히 BSTR과 _bstr_t의 관계처럼 좀더 사용하기 용이하다.
기본적으로 VARIANT는 문자열외에 다양한 데이터를 저장하기 위한 구조체지만, 문자열을 저장할 경우 BSTR형식으로
저장이 된다.
_bstr_t와는 다르게 _variant_t는 VARIANT를 상속받은 클래스로 모든 함수에 VARIANT 대신 넘겨주는 것이 가능하다.
내부의 VARIANT는 감춰져있다.
// Constructing
_variant_t v1 = "char string"// construct from a LPCSTR
_variant_t v2 = L"wide char string"; // construct from a LPCWSTR
_bstr_t bs1 = "Bob";
_variant_t v3 = bs1;  // copy from a _bstr_t object

// Extracting data
_bstr_t bs2 = v1;  // extract BSTR from the VARIANT
_bstr_t bs3 = (_bstr_t) v1; // cast OK, same as previous line
위와 같이 _variant_t와 _bstr_t 사이에 전환이 용이 하다.



basic_string::string / basic_string::wstring
STL의 문자열 클래스 basic_string에는 MBSC/Unicode용으로 각각 string/ wstring이 존재 한다.
string은 char / wstring은 wchar_t를 저장하는데, TCHAR은 존재하지않는다.
TCHAR과 STL을 함께 사용하려면 간단히 다음처럼 직접 만들어주면된다.
// Specializations
typedef basic_string<TCHAR> tstring; // string of TCHARs 새로 정의 해준다.

// Constructing 이렇게 각각 생성할 수 있다.
string str = "char string"// construct from a LPCSTR
wstring wstr = L"wide char string"; // construct from a LPCWSTR
tstring tstr = _T("TCHAR string"); // construct from a LPCTSTR

// Extracting data
// 값을 사용할 때에는 .c_str()을 통해 해당하는 원 데이터형으로 반환된다.
LPCSTR psz = str.c_str()// read-only pointer to str's buffer
LPCWSTR pwsz = wstr.c_str(); // read-only pointer to wstr's buffer
LPCTSTR ptsz = tstr.c_str(); // read-only pointer to tstr's buffer
_bstr_t에 바로 할당하기 위해서는
_bstr_t bs1 = wstr.c_str(); 와 같은 식으로 내부 데이터 값을 받아오면 된다.



CComBSTR
ATL의 BSTR wrapper 클래스로 _bstr_t보다 좀더 유용한 기능들이 있다.
우선 COM함수에 BSTR대신 넘겨줄 수 있고, BSTR 메모리 관리를 자동으로 해준다.
내부에 MBCS 변환 기능은 없다.
문자열 변환에는 ATL 변환 Macro를 사용하면 된다. <- 매우 편리하다
// Constructing
CComBSTR bs1 = "char string"// construct from a LPCSTR
CComBSTR bs2 = L"wide char string"; // construct from a LPCWSTR
CComBSTR bs3 = bs1;  // copy from another CComBSTR
CComBSTR bs4;

bs4.LoadString ( IDS_SOME_STR ); // load string from string table

// Extracting data
BSTR bstr1 = bs1;  // returns internal BSTR, but don't modify it!
BSTR bstr2 = (BSTR) bs1; // cast ok, same as previous line
BSTR bstr3 = bs1.Copy(); // copies bs1, returns it as a BSTR
BSTR bstr4;

// CComBSTR의 메모리 관리를 꺼버릴 수 있다.
bstr4 = bs1.Detach(); // bs1 no longer manages its BSTR, 메모리 관리는 수동으로 해야한다.

// ...
SysFreeString ( bstr3 );
SysFreeString ( bstr4 );


추가로, 연산자&는 내부의 BSTR*를 리턴하도록 오버로딩 되어있기 때문에 사용상 고려할점이 존재한다.
STL의 list같은 컬렉션에서 CComBSTR을 사용하기 위해서는 &연산자 오버로딩으로 일반 데이터타입과는 다르게,
CAdapt 를 사용해야 한다. 즉 다음과 같다.
std::list< CAdapt<CComBSTR> > bstr_list;



CComVariant
ATL의 VARIANT wrapper 클래스다. _variant_t와는 다르게 내부의 VARIANT가 감춰져 있지 않아서 직접 접근이 가능.
게다가 CComBSTR과 간단히 형 변환되지는 않기 때문에 값을 넣기 위해서는 검사를 해야한다.
CComVariant v4 = ... // Init v4 from somewhere
CComBSTR bs3;
// 검사하고 변환이 가능하면 넣는다.
if ( SUCCEEDED( v4.ChangeType ( VT_BSTR ) ))
bs3 = v4.bstrVal;



ATL Conversion Macros ? MBCS / Unicode / BSTR 간의 Encoding 변환
Macro 함수의 이름은 다음과 같은 구조로 돼어있다.
[원본 타입]2[새 타입] / [원본타입]2C[새 타입]
2는 그냥 변환 / 2C는 constant pointer를 말하는 C이다.

A : MBCS 문자열 char*
W : Unicode 문자열 wchar_t*
T : TCHAR 문자열 TCHAR*
OLE : OLECHAR 문자열 OLECHAR*
BSTR : BSTR

예를 들어, W2A() 매크로는 Unicode문자열을 MBCS문자열로 변환한다.
매크로를 사용하기 위해서는 atlconv.h 헤더를 포함해야 하는데, 해당 헤더파일만 포함하면 굳이 ATL프로젝트가 아니라도
사용이 가능하다.

사용시에는 우선 USES_CONVERSION 이라는 매크로를 사용전에 호출해주어 변환에 필요한 기본 변수들을 정의 한뒤,
실제 변환 매크로를 사용하면 된다.
// Functions taking various strings:
void Foo ( LPCWSTR wstr );
void Bar ( BSTR bstr );
// Functions returning strings:
void Baz ( BSTR* pbstr );

#include &lt;atlconv.h&gt;

main()
{
using std::string;
USES_CONVERSION;  // declare locals used by the ATL macros

// Example 1: Send an MBCS string to Foo()
LPCSTR psz1 = "Bob";
string str1 = "Bob";

Foo ( A2CW(psz1) );
Foo ( A2CW(str1.c_str()) );

// Example 2: Send a MBCS and Unicode string to Bar()
LPCSTR psz2 = "Bob";
LPCWSTR wsz = L"Bob";
BSTR bs1;
CComBSTR bs2;

bs1 = A2BSTR(psz2)// create a BSTR
bs2.Attach ( W2BSTR(wsz) ); // ditto, assign to a CComBSTR

Bar ( bs1 );
Bar ( bs2 );

SysFreeString ( bs1 )// free bs1 memory
// No need to free bs2 since CComBSTR will do it for us.

// Example 3: Convert the BSTR returned by Baz()
BSTR bs3 = NULL;
string str2;

Baz ( &bs3 ); // Baz() fills in bs3

str2 = W2CA(bs3); // convert to an MBCS string
SysFreeString ( bs3 ); // free bs3 memory
}
반응형

댓글