The following are illustrated in this section
__declspec (property)
Interface Pointers
CComPtr<>
_com_ptr_t<>
Strings
Unicode
Prerequisites
BSTR
_bstr_t
CComBSTR
Variants
VARIANT
_variant_t
CComVariant
SAFEARRAY
Exceptions
__declspec(property()) declares a non-static data member as a virtual data member by associating it with public accessor (Get) and mutator (Set) methods.
// CAR.H
class CCar
{
private:
int nSpeed;
int nItems[10];
public:
CCar();
virtual ~CCar();
__declspec(property(get=GetSpeed,put=SetSpeed)) int Speed;
int GetSpeed();
void SetSpeed( int nNum );
__declspec(property(get=GetItem,put=SetItem)) int Items[];
int GetItem( long nIndex);
void SetItem( long nIndex, long nVal);
};
// CAR.CPP
#include "stdafx.h"
#include "car.h"
/* Using __declspec(property()) improves readability. Consider the following -
CCar ob;
ob.SetItem( 0, 20);
ob.Items[0] = 20; // uses __declspec( property() ) */
CCar::CCar() : nSpeed(0) {}
CCar::~CCar() {}
int CCar::GetSpeed()
{
return nSpeed;
}
void CCar::SetSpeed( int nNum )
{
nSpeed = nNum;
}
int CCar::GetItem( long nIndex)
{
return nItems[nIndex];
}
void CCar::SetItem( long nIndex, long nVal)
{
nItems[nIndex] = nVal;
}
#include "stdafx.h"
#include <comdef.h> // Brings in IDispatch definition
struct __declspec(uuid("aeca53e3-f3a6-11d3-8eb4-000000000000")) Object1;
struct __declspec(uuid("9d5ace00-f934-11d3-8ebc-000000000000")) IEngine : IDispatch
{};
struct __declspec(uuid("aeca53e2-f3a6-11d3-8eb4-000000000000")) IObject1 : IDispatch
{
virtual HRESULT __stdcall get_Number ( long * pVal ) = 0;
virtual HRESULT __stdcall put_Number ( long pVal ) = 0;
virtual HRESULT __stdcall raw_SomeFunc ( long nNumber ) = 0;
virtual HRESULT __stdcall raw_GetEngine ( struct IEngine ** ppEngine ) = 0;
};
void RawInterfacePointers()
{
HRESULT hr;
IObject1 *pItf = NULL;
IUnknown *pUnk = NULL;
// Create the 'Object1' COM object
hr = CoCreateInstance
(
__uuidof(Object1), // aeca53e3-f3a6-11d3-8eb4-000000000000
NULL,
CLSCTX_ALL,
__uuidof( IObject1), // aeca53e2-f3a6-11d3-8eb4-000000000000
reinterpret_cast<void**>(&pItf)
);
// Call some functions on the newly
created object
hr = pItf->raw_SomeFunc( 10 );
/* This cast slices off IObject1 methods. Do not do this! Note that pUnk
will get the same pointer value as pItf (casting a pointer to another does
not change the pointer value */
pUnk = static_cast<IUnknown*>(pItf);
/* Although the two pUnk and pUnk2
interfaces are equal, we should only
retrieve IUnknown from QueryInterface(). On the same note, to compare
if two interface point to the same object, QueryInterface() for
IUnknown
pointer and compare the two resulting pointers. Never compare two
IUnknown's for object identity if they were obtained from a cast */
IUnknown *pUnk2 = NULL;
pItf->QueryInterface( __uuidof(IUnknown), (void**)&pUnk2);
// Clean up
pItf->Release();
pUnk2->Release();
}
#import "Generic.tlb" raw_interfaces_only // removes all wrapper methods
#import "Generic.tlb" no_namesapce
// removes the library namespace
#import "Generic.tlb" inject_statement(" any statement ")
#import "Generic.tlb" exclude(), rename()
#import "Generic.tlb" no_namespace
void CreateObjectWithTypeLib()
{
long nNum = 10;
HRESULT hr;
/* Standard way to create an object and
retrieve a pointer to it. To remember
what to supply as argument, recall that retrieving an interface pointer
requires the CLSID of its coclass. Therefore, pass the CLSID. */
IObject1Ptr p( __uuidof(Object1) );
/* Two ways to access the same METHOD
*/
hr = p->SomeFunc( nNum ); // high-level method that wraps COM errors
hr = p->raw_SomeFunc( nNum ); // raw interface method
/* Three ways to access the same PROPERTY
*/
hr = p->get_Number( &nNum); // raw interface method
nNum = p->GetNumber();
// high-level method that wraps COM errors
nNum = p->Number;
// virtual member variable using __declspec(property())
/* Using __uuidof() */
CLSID clsidObject = __uuidof( Object1) ;
// Using the CoClass class
IID iidID1 = __uuidof( IObject1 ); // Using the interface class
IID iidID2 = __uuidof( p );
// Using interface instance
/* GetEngine() retrieves an interface pointer via an [out]
param, which
implies that AddRef() has been called from within GetEngine(). The
implementation of GetEngine() has the following code (from
the .tli) -
inline IEnginePtr IObject1::GetEngine ( )
{
struct IEngine * _result;
HRESULT _hr =
raw_GetEngine(&_result);
if (FAILED(_hr))
_com_issue_errorex(_hr, this, __uuidof(this));
return
IEnginePtr(_result, false);
}
IEnginePtr is created with a call to the constructor that will NOT call
AddRef(). When execution moves to the next line (the end of
GetEngine), the
temp object is destroyed and its destructor calls Release() soaking up the
extra reference count from the assignment operator. (the assignment operator
is called BEFORE the temp object is destroyed). Therefore, when pEngine is
destroyed, the Release() will be matched by the AddRef() from
GetEngine()
*/
IEnginePtr pEngine;
pEngine = p->GetEngine();
/* We can release pEngine by letting it go out of scope OR by calling its
Release() function */
pEngine.Release();
// NEVER: pEngine->Release(); NO NO NO
}
// Object1 object
struct __declspec(uuid("aeca53e3-f3a6-11d3-8eb4-000000000000")) Object1;
// IEngine Interface
struct __declspec(uuid("9d5ace00-f934-11d3-8ebc-000000000000")) IEngine : IDispatch
{};
// IObject1 Interface
struct __declspec(uuid("aeca53e2-f3a6-11d3-8eb4-000000000000")) IObject1 : IDispatch
{
virtual HRESULT __stdcall get_Number ( long * pVal ) = 0;
virtual HRESULT __stdcall put_Number ( long pVal ) = 0;
virtual HRESULT __stdcall raw_SomeFunc ( long nNumber ) = 0;
virtual HRESULT __stdcall raw_GetEngine ( struct IEngine ** ppEngine ) = 0;
};
void CreateObjectCComPtr()
{
long nNum;
{
CComQIPtr<IObject1, &__uuidof(IObject1)> sp1;
// REF COUNT = 1
sp1.CoCreateInstance( __uuidof(Object1) );
sp1->put_Number( 10 );
sp1->get_Number( &nNum );
/* Calling Release() is required if you want to reuse the smart
pointer. Note that ~CComQIPtr<> is called and it will release the
wrapped pointer if it is not already release
sp1.Release();
// Optional. Can do without.
sp1->Release();
// Will not compile. See note above
*/
/* Some manipulations.
Note that the last statement calls the constructor
explicitly. Just
like -
CString str1 = CString("hello");
// OR same as
CString str1( CString ("hello") );
In essence, the right side of the last statement acts just like a
casting operator for interfaces - it converts the given interface
pointer to the required type using CComQIPtr<> which will do a QI()
based on the supplied type. Note that
the second argument for
CComQIPtr<> defaults to __uuidof(T)
'CComPtr<IObject1> sp2 = CComQIPtr<IObject1>(pUnk)'
calls these
functions:
CComQIPtr::CComQIPtr(IUnknown* lp)
// get IObject1 interface from pUnk
CComQIPtr::operator T*()
// conversion operator - returns raw ptr
CComPtr( T * )
// sp2 is initialied with the raw ptr
CComQIPtr::~CComQIPtr()
// destroy temp object
Consider this example to illustrate the conversion function
class Money
{
public:
Money();
operator double() { return _amount; }
private:
double _amount;
};
Money Account;
double CachOnHand = Account;
// calls operator double() to perform a
// conversion from type Account to type double.
*/
IUnknown *pUnk = NULL;
sp1.QueryInterface( &pUnk );
// AddRef()d. Must release! REF COUNT = 2
CComPtr<IObject1> sp2 = CComQIPtr<IObject1>(pUnk); // REF COUNT = 3
/* This should help explain the previous code snippet
'IObject1 *pOb1 = CComQIPtr<IObject1>(pUnk)' calls these functions:
CComQIPtr::CComQIPtr(IUnknown* lp) // get IObject1 interface from pUnk
CComQIPtr::operator T*()
// conversion operator - returns raw ptr
CComQIPtr::~CComQIPtr()
// destroy temp object
*/
IObject1 *pOb1 = CComQIPtr<IObject1>(pUnk); // Ref count not AddRef()ed
pUnk->Release(); // REF COUNT = 2
// Other methods - IsEqualObject()
bool bSame = sp2.IsEqualObject( sp1 );
// Other methods - CopyTo() - calls AddRef()
IObject1 *pOb2;
sp2.CopyTo( &pOb2 ); // AddRef() called because a copy is make
// .. Use pOb2
pOb2->Release(); // release it because it's not a smart pointer.
} // smart pointer release
}
template<class _IIID>
class _com_ptr_t<> { ... };
template<class _Interface, const IID* _IID>
class _com_IIID { ... };
_com_IIID< IObject1, & __uuidof(IObject1) > comIIID;
_com_ptr_t< comIIID >;
IObject1Ptr p;
p->CreateInstance( CLSID_Object1 );
IObject1Ptr p( _uuidof( Object1 ) );
Note that in methods 2 and 3, CoCreateInstance() is called, rather then CoCreateInstanceEx(). Remote objects must be created with method 1 only.
IEnginePtr p( __uuidof(Object1));
p->SomeMethod;
p.Release(); // This stmt optional but NEVER use p->Release()
_com_ptr_t<Interface *pInterface, bool fAddRef) throw()
// Used to create IObject1 with CoCreateInstance()
struct __declspec(uuid("aeca53e3-f3a6-11d3-8eb4-000000000000")) Object1;
struct __declspec(uuid("9d5ace00-f934-11d3-8ebc-000000000000")) IEngine : IDispatch
{ };
struct __declspec(uuid("aeca53e2-f3a6-11d3-8eb4-000000000000")) IObject1 : IDispatch
{
virtual HRESULT __stdcall get_Number ( long * pVal ) = 0;
virtual HRESULT __stdcall put_Number ( long pVal ) = 0;
virtual HRESULT __stdcall raw_SomeFunc ( long nNumber ) = 0;
virtual HRESULT __stdcall raw_GetEngine ( struct IEngine ** ppEngine ) = 0;
};
// Used to create IObject1 with smart pointers
//#import "Generic.tlb" no_namespace
void CreateObjectComPtrT()
{
/* Create an object using CoCreateInstance() and attach it to a smart
pointer. Note how _com_ptr_t<> needs to take _com_IIID<> as its template
argument. Also note how _com_IIID<> takes the address of the interface IID */
{
HRESULT hr;
IObject1 *pItf = NULL;
// Create an object
hr = CoCreateInstance
(
__uuidof(Object1), // aeca53e3-f3a6-11d3-8eb4-000000000000
NULL,
CLSCTX_ALL,
__uuidof( IObject1), // aeca53e2-f3a6-11d3-8eb4-000000000000
reinterpret_cast<void**>(&pItf)
);
// Initialize smart pointer to NULL.
_com_ptr_t< _com_IIID<IObject1, & __uuidof(IObject1)> > sp1(NULL);
sp1.Attach( pItf );
} // smart pointer released here
/* This segment illustrates creating an object using method 2*/
{
// This takes care of creating IObject1Ptr. No need to use #import
_COM_SMARTPTR_TYPEDEF(IObject1, __uuidof(IObject1));
IObject1Ptr p1;
p1.CreateInstance( __uuidof(Object1) );
/* Assignment operator can be used to assign homogenous or
heterogeneous
pointers. In the code below, the assignment
operator= will do a QI() for
the required interface type (IObject1) if p2 was not of type IObject1 */
IObject1Ptr p2;
p2 = p1; // Calls AddRef() on p2 since we are making a copy
// No need to call AddRef() ourselves
} // smart pointers released here
}
typedef short
wchar_t;
typedef char CHAR;
typedef wchar_t WCHAR
typedef WCHAR OLECHAR
typedef OLECHAR *LPOLESTR;
typedef const OLECHAR LPCOLESTR;
#define OLESTR(str) L##str;
PassAsString1( [in] OLECHAR str[256] ); // cannot pass more than 256 !
PassAsString2( [in] long Len, [in, size_is(Len)] LPOLESTR str);
PassAsString3( [in, string] LPOLESTR str);
Conclusion: The marshaller layer must always know how many bytes to trasnsmit. The BSTR data type works exactly in this way: A BSTR is actually an OLECHAR* and can be treated as such when programming. However, when a BSTR is marshalled it is marshalled as a wireBSTR which is a BSTR and its associated length.
// Forward declaration
BSTR TransformBSTR( BSTR bstrIn );
void DemoStrings()
{
/* Example 1
Most COM functions use LPOLESTR and OLECHAR for strings. COM functions
that return strings to the caller are allocated by the calle
and freed
by the caller. COM functions that receive strings must be allocated by
caller and freedby caller.
RULE - Functions taking a pointer to a pointer
Passing a pointer to a pointer is the same as passing the address of the
pointer. This is used when we wish to change the value of the pointer
stored inside the variable (which is of type pointer). Typical situation
is passing a string pointer to have the called functions allocated a
string. See below -
StringFromCLSID takes an address of a pointer. Think of it as follows -
lpString is a variable (a box) with uninitialized content and since it's a
pointer, the uninitialized content is an invalid memory address. The
variable's address it sent and we get back the variable containing a valid
memory address; a pointer to a string.
*/
LPOLESTR
lpString;
const CLSID cls = {0xAECA53E3,0xF3A6,0x11D3,{0x8E,0xB4,0x00,0x00,0x00,0x00,0x00,0x00}};
HRESULT hr = StringFromCLSID( cls, &lpString );
CoTaskMemFree( lpString );
// Defining literal strings
WCHAR wsz[] = OLESTR("going in");
WCHAR wsz2[] = L"in in";
/* Any function that creates/returns a BSTR must ensure that the length is
prefixed, else you can treate a BSTR as an 'const OLECHAR*' or LPCOLESTR
which mean we can read from it but not write to it. Note that SysAllocString()
takes an OLECHAR* which use a NULL character to indicate end of string. To get
around having a null in the string to be allocated, use SysAllocStringLen() */
BSTR bstr
= SysAllocString( L"A BSTR \0 String"); // allocates "A BSTR "
OLECHAR * oleStr1 = (LPOLESTR) bstr;
// Casting is not important
OLECHAR * oleStr2 = bstr;
// Casting can be ignored
int nLen
= wcslen( oleStr1 );
SysFreeString( bstr );
/*Manipulating a BSTR: COPY into an LPOLESTR buffer, manipulate, then recopy
into the BSTR again */
// BSTR bstr1 = SysAllocString( "hello there"); // ERROR. Must have an OLESTR
BSTR bstr1 = SysAllocString( L"hello there" ); // Get BSTR
//
OLECHAR *pStr = new OLECHAR[64];
// Copy
wcscpy( pStr, bstr1);
// Do not use: OLECHAR *pStr = bstr1;
//
wcscat( pStr, L"Mr. Diranieh");
// Manipulate
//
SysReAllocString( &bstr1, pStr);
// Re-assign
//
SysFreeString( bstr1 );
// Free memory
delete pStr;
/* Using SysAllocStringLen() */
// bstr2 contains "Te". Char length = 2, Byte length = 4
BSTR bstr2 = SysAllocStringLen( L"Text", 2);
SysFreeString( bstr2 );
// bstr3 contains "Text" followed by \0 and a a junk char - a total of
six char and then the EXTRA terminating null character.
BSTR bstr3 = SysAllocStringLen( L"Text", 6);
SysFreeString( bstr3 );
/* Rule 7 (see below) - Must create a BSTR in order to return it
*/
BSTR bstr5 = TransformBSTR( bstr3 );
SysFreeString( bstr5 );
/* NULL and empty BSTRs. However, BOTH have a length of zero */
BSTR bstr6 = SysAllocString( L"" );
BSTR bstr7 = SysAllocString( NULL );
int nLen6 = SysStringLen( bstr6 );
int nLen7 = SysStringLen( bstr7 );
SysFreeString( bstr6 );
}
// Rule 7 (see below) Must create a BSTR in order to return it (cannot return
// the input param)
BSTR TransformBSTR( BSTR bstrIn )
{
BSTR bstr = SysAllocString( bstrIn );
return bstr;
}
BSTR TransformThisString( BSTR bstrIn )
{
BSTR bstr = SysAllocString( bstrIn );
bstr.ToUpper();
return bstr;
}
BSTR bstr6 = SysAllocString( L"" );
BSTR bstr7 = SysAllocString( NULL );
ASSRT( SysStringLen( bstr6 ) == SysStringLen( bstr7 ) ) // length = 0
// Forward declaration
void PassBSTR( BSTR bstr);
void Demo_bstr_t()
{
/* A _bstr_t constructor can take a UNICODE string. Normal form */
_bstr_t bstr1( L"This is bstr1" );
/* A _bstr_t constructor can take an ANSI string. Multibyte to UNICODE
is performed before calling SysAllocString() */
_bstr_t bstr2( "This is bstr2" );
/* A _bstr_t constructor can take a BSTR. The supplied BSTR can be attached
(_bstr_t wraps it), or a totally new copy of the BSTR can be created
In both cases, SysFreeString() is managed by _bstr_t */
BSTR bstr = SysAllocString( L"This is BSTR" );
_bstr_t bstr3( bstr, false );
_bstr_t bstr4( bstr, true );
/* Illustrates ref count on _bstr_t
At end of 2nd line, both bstrs point to the same string. The copy constructor
of the bstr6 increments the ref count. However, in the third line, the ref
count is decremented and a new BSTR is assigned. Use F11 to watch the
_bstr_t
code for each of the steps below to verify the use of the ref count */
_bstr_t bstr5( L"This is bstr5" );
_bstr_t bstr6 = bstr5;
bstr5 = L"Still bstr5";
/* To print the contents of a _bstr_t use operator char*() */
printf("string is %s", (LPSTR)bstr4); // same as (char*)bstr5
/* Using operator+=( const _bstr_t &).
Since we are supplying an OLESTR and operator+=() takes a 'const _bstr_t &',
we must create a temp object using _bstr_t::_bstr_t(const wchar_t *) conversion
constructor. The temp object is later destroyed */
{
_bstr_t bstr7 = L"This is";
bstr7 += L"bstr7";
}
/* Passing _bstr_t to COM functions.
Consider the following
p->SetString( bstr6 );
...
inline HRESULT IObject1::SetString ( _bstr_t bstrString )
{
HRESULT _hr = raw_SetString(bstrString);
..
}
First, the copy constructor is called since we're passing by value.
SetString()
expects a BSTR but we're passing a _bstr_t. This will cause operator wchar_t*()
to be invoked in order to convert _bstr_t to BSTR */
IObject1Ptr p( __uuidof(Object1) );
p->SetString( bstr6 );
_bstr_t bstr8 = p->GetString();
/* Use _bstr_t wherever BSTR is
used */
// The following calls _bstr_t::_bstr_t(const wchar_t* s)
_bstr_t bstr9 = SysAllocString( L"This is a
bstr" );
// The following calls _bstr_t::operator wchar_t*()
PassBSTR( bstr9 );
}
void PassBSTR( BSTR bstr)
{
// Do something with with the bstr
return;
}
// Forward declaration
void PassSomeBSTR( BSTR bstr );
void Demo_CComBSTR()
{
USES_CONVERSION;
// CComBSTR() constructors handle UNICODE and ANSI strings
CComBSTR bstr1( L"UNICODE String" );
CComBSTR bstr2( "ANSI String" ); // Uses A2WBSTR() to convert ANSI to UNICODE
// There is no operator+(). Use CComBSTR::Append()
CComBSTR bstr3( L"CComBSTR3" );
bstr3.Append( " With Append" );
// AppendBSTR() is used to append only a BSTR
CComBSTR bstr4( L"CComBSTR4" );
BSTR bstr = SysAllocString( L" IS A BSTR" );
bstr4.AppendBSTR( bstr );
// Use CComBSTR anywhere a BSTR is used
CComBSTR bstr5 = SysAllocString( L"BSTR5" ); // CComBSTR::CComBSTR( LPCOLESTR )
PassSomeBSTR( bstr5 );
// CComBSTR::CComBSTR operator BSTR()
/* Using Attach/Detach() : Use Attach() if you have an existing BSTR and
you want to wrap with a CComBSTR. Use Detach() to retrieve the wrapped BSTR */
{
CComBSTR bstr6;
BSTR bstrBSTR1 = SysAllocString( L"Another BSTR" );
bstr6.Attach( bstrBSTR1 );
BSTR bstrBSTR2 = bstr6.Detach();
// set bstr6.m_strto NULL
SysFreeString( bstrBSTR2 );
// who else is gonna call it?
} // bstr6 destructor calls SysFreeString(m_str) on a NULL m_str
/* ATL conversion macros - Uses when we need to convert between ANSI and
UNICODE strings. */
LPSTR lpszString
= "This is a T string";
LPOLESTR lpoleString = A2OLE( lpszString );
BSTR bstrString = A2BSTR( lpszString ); // must call SysFreeString();
SysFreeString( bstrString );
}
void PassSomeBSTR( BSTR bstr )
{
CComBSTR bstr1;
bstr1 = bstr; // CComBSTR& operator=(LPCOLESTR pSrc)
return;
}
void DemoVariants()
{
/* Using a variant - always initialize it first then clear it when
finished */
VARIANT var1;
VariantInit( &var1 );
// Initialize
var1.vt = VT_INT;
var1.iVal = 10;
int nSize = sizeof( var1 ); // size = 16 bytes.
Parenthesis required.
nSize = sizeof VARIANT;
// size = 16 bytes. Parenthesis not required
VariantClear( &var1 );
/* For variants of type VT_BSTR, call
VariantClear() to free the string */
BSTR bstr = SysAllocString( L"My BSTR" );
VARIANT var2;
VariantInit( &var2 );
var2.vt = VT_BSTR;
// or: V_VT( &var2 ) = VT_BSTR
var2.bstrVal = bstr;
// or: V_BSTR( &var2 ) = bstr;
VariantClear( &var2 );
// Calls SysFreeString()
/* VT_BYREF is used to store pointers to the associated data type */
BSTR bstr2 = SysAllocString( L"BSTR2" );
VARIANT var3;
VariantInit( &var3 );
var3.vt = VT_BSTR | VT_BYREF;
var3.pbstrVal = &bstr2;
VariantClear( &var3 );
// Calls SysFreeString()
/* Changing variant types. Use the VarT1FromT2 functions. The following
retrieves the integer value into a long value */
long lVal;
VARIANT var4;
VariantInit( &var4 );
var4.vt = VT_I2;
var4.iVal = 10;
// Extract a long from a variant containing a short
VarI4FromI2( var4.iVal, &lVal ); // lVal contains 10
// Use a short to assign data to variable of type long
lVal = lVal + 100;
VarI2FromI4( lVal, &var4.iVal );
VariantClear( &var4 );
/* Changing variant types. The following gets the system time and stores it
in a variant. We then change the variant from type DATE to type BSTR
*/
SYSTEMTIME
sysTime;
double date;
VARIANT var5;
VariantInit( &var5 );
//
GetSystemTime( &sysTime );
SystemTimeToVariantTime( &sysTime, &date );
//
var5.vt = VT_DATE;
var5.date = date;
HRESULT hr = VariantChangeType( &var5, &var5, VARIANT_NOVALUEPROP, VT_BSTR);
//
VariantClear( &var5 );
/* Accomplishing the same DATE to BSTR conversion using VarBstrFromDate */
SYSTEMTIME sysTime2;
double date2;
VARIANT var6;
VariantInit( &var6 );
//
GetSystemTime( &sysTime2 );
SystemTimeToVariantTime( &sysTime2, &date2 );
//
BSTR bstrDate;
LCID lcid = GetUserDefaultLCID();
VarBstrFromDate( date2, lcid, 0, &bstrDate );
//
var6.vt = VT_BSTR;
var6.bstrVal = bstrDate;
//
VariantClear( &var6 );
}
void Demo_variant_t()
{
{
// ANSI is converted to BSTR (A2BSTR)
_variant_t vart1( "ANSI String");
} // VariantClear(this) is called
here
{
_bstr_t bstrVart2;
_variant_t vart2( L"UNICODE String" );
// Calls SysAllocString() directly
bstrVart2 = (_bstr_t) vart2;
// _variant_t::operator _bstr_t()
}
{
/* This sequence show how to attach a variant, use it, and then detach
it and clear it */
_variant_t vart;
//
VARIANT var;
VariantInit( &var );
var.vt = VT_I2;
var.iVal = 10;
//
vart.Attach( var );
// VARIANT var is now wrapped by _variant_t
// VARIANT var is also cleared and set to VT_EMPTY
// Start using the attached VARIANT
vart.ChangeType( VT_I4 );
//
var = vart.Detach(); // Set var back to what it originally was
VariantClear( &var ); // Now clear 'var' since it's no longer wrapped
}
}
void DemoCComVariant()
{
/* Basic usage. It will convert the ANSI string to BSTR using ATL conversio
macro - A2COLE() and assign vt = VT_BSTR.
To access
*/
{
CComVariant var1( "ANSI" );
int nVT = var1.vt;
BSTR bstr = var1.bstrVal;
} // the bstr is freed when ~CComVariant is called.
// Basic usage as above but with a UNICODE string
{
CComVariant var2( L"UNICODE" );
BSTR bstr2 = var2.bstrVal;
}
/* Illustrating attaching/detaching a variant
When attaching, the contents of the source (attached) variant are copied
into the target variant (the CComVaraint) and the source is set to
VT_EMPTY.
The opposite happens when detaching - CComVaraint contents are copied into
the VARIANT using memcpy(), and the CComVariant is set to
VT_EMPTY. */
{
CComVariant var3; // Set to VT_EMPTY
VARIANT var;
// Create a variant
VariantInit( &var );
var.vt = VT_I2;
var.iVal = 10;
// Attach variant and use it
// contents of var are copied into var3 using memcpy(). var3 is set to
// VT_EMPTY
var3.Attach( &var );
int iValue = var3.iVal;
// Detach variant and clear it
// contents of var3 are copied into var using memcpy(). var is set
// to VT_EMPTY
var3.Detach( &var );
VariantClear( &var );
}
/* CComVaraint::Copy copies the varinat using VariantCopy(). The original
VARIANT must still be freed since it was not attached */
{
CComVariant var4; // Set to VT_EMPTY
VARIANT var;
// Create a variant
VariantInit( &var );
var.vt = VT_I2;
var.iVal = 10;
// Copy the variant into a CComVariant
var4.Copy( &var );
// Clear VARIANT since it was copied NOT attached
VariantClear( &var );
}
/* CComVariant::operator () functions */
{
CComVariant var1;
var1 = 20;
CComVariant var2;
var2 = SysAllocString( L"UNICODE" );
CComVariant var3;
var3 = CComBSTR( "ANSI" );
// Incorrect way of converting BSTR to LPSTR. Use conversion macros
BSTR bstr2 = SysAllocString( L"UNICODE" );
char* lpString = (char*) bstr2;
}
}
typedef struct tagSA
{
USHORT cDims;
// Number of dimensions
USHORT fFeatures; // array attributes used by safearray routines
ULONG cbElements; // Aize of a single element in the array
ULONG cLocks;
// Lock count. Cannot access data when cLocks = 0.
PVOID pvData;
// Actual array data
SAFEARRAYBOUND rgsaBound[1]; // Number of elements in each dimension
}
// Elements in each dimension are defined by SAFEARRAYBOUND -
typedef struct tagSAFEARRAYBOUND
{
ULONG cElements; // Number of elements in a dimension
LONG lLBound;
// lower bound index (for C++ it is 0)
}
GetArray( VARIANT *pVar )
{
// Create a safe array and populate it with data
pSa = SafeArrayCreate(...)
...
// Set variant to be an array of some type, say BSTRs and then set the
// variant array data to point to the safe array
VariantInit( pVar );
pVar->vt = VT_ARRAY | VT_BSTR;
pVar->parray = pSA;
}
SetArray( VARIANT var )
{
// Ensure variant is an array
ASSERT( var.vt & VT_ARRAY == 0 );
// Get safe array out of the VARAIANT
SAFEARRAY *pSA = var.parray;
// Access the safe array and use its data
SafeArrayAccessData( pSA, ... );
...
}
On the client side, for the SetArray() method we populate the [in] VARAINT using same exact procdures in
GetArray(). For the GetArray() method, we retrieve data from the variant using same steps iin SetArray().
void DemoSafeArray()
{
/* Create a one dimensional safearray */
{
// Set dimension information. Two dimensions
SAFEARRAYBOUND saBound[1];
saBound[0].cElements = 10; // 10 elements in first dimension
saBound[0].lLbound = 0;
// lower bound of 0
// Create safearray
SAFEARRAY *pSA;
pSA = SafeArrayCreate( VT_I4,
// data type held in safearray is 'long'
1, // 1 dimension
saBound ); // data about the only dimension
// Access the safearray by retrieving a pointer to the data
HRESULT hr;
long *plData;
hr = SafeArrayAccessData( pSA, (void**)&plData );
// Use the data
for (int i = 0; i < 10; i++)
{
plData[i] = i;
}
// UnAccess the safearray
hr = SafeArrayUnaccessData( pSA );
// Destroy safe array
SafeArrayDestroy( pSA );
}
/* Create a two dimensional safearray */
{
// Set dimension information. Two dimensions
SAFEARRAYBOUND saBound[2];
saBound[0].cElements = 5;
// 5 elements in first dimension
saBound[0].lLbound = 0;
// lower bound of 0
saBound[1].cElements = 5;
// 5 elements in second dimension
saBound[1].lLbound = 0;
// lower bound of 0
// Create safearray
SAFEARRAY *pSA;
pSA = SafeArrayCreate( VT_I4,
// data type held in safearray is 'long'
2, // 2 dimensions
saBound ); // data about the 2 dimension
// Use the data - first dimension
long l2DData[5][5] = {
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20},
{21,22,23,24,25}
};
long ix[2];
long lData;
// Set / Get first row first column
ix[0] = 0;
ix[1] = 0;
lData = l2DData[0][0];
SafeArrayPutElement( pSA, ix, (void*)&lData );
//
lData = 0;
SafeArrayGetElement( pSA, ix, &lData);
// Set / Get fifth row third colums
ix[0] = 4;
ix[1] = 2;
lData = l2DData[4][2];
SafeArrayPutElement( pSA, ix, (void*)&lData );
//
lData = 0;
SafeArrayGetElement( pSA, ix, &lData );
// Another way to access a particular element in the SAFEARRAY. ix[]
// indicates the index of the data to be accessed. The data is copied
// to pData;
long *pData;
SafeArrayLock( pSA );
SafeArrayPtrOfIndex( pSA, ix, (void**)&pData );
lData = *pData;
SafeArrayUnlock( pSA );
// Get dimensions using SafeArrayX() functions
long lBound, uBound;
SafeArrayGetLBound( pSA, 1, &lBound );
SafeArrayGetUBound( pSA, 1, &uBound );
// Destroy safe array
SafeArrayDestroy( pSA );
}
}
ICreateErrorInfo * pCEI;
CreateErrorInfo( &pCEI );
pCEI->SetDescription( "The error description ");
//
IErrorInfo *pEI;
pCEI->QueryInterface( _uuidof(IErrorInfo), (void**)&pEI );
SetErrorInfo( 0, pEI );
//
pEI->Release();
pCEI->Release();
void DemoErrors()
{
// Play with HRESULT
HRESULT hr = 0x800706ba;
// simulate an error
long lFacility = HRESULT_FACILITY( hr ); // used to determine who made the error
long lCode = HRESULT_CODE( hr );
// used to get err description
if (FAILED(hr) ) // testing for failure.
{
hr = 0x00000000;
}
// TO DO: code to get an error object form an interface method.
}