this pointer through a static callback that has no
place to save it as callback data. ATL_NO_VTABLE.
CComObject can have differing base classes, and how smart pointers for CComPtr and CComQIPtr are made.
_QIThunk used for debug reference counting when _ATL_DEBUG_INTERFACES is defined.
| COM value type | COM reference type | System type |
|---|---|---|
| bool | bool * | System.Int32 |
| char, small | char *, small * | System.SByte |
| short | short * | System.Int16 |
| long, int | long *, int * | System.Int32 |
| Hyper | hyper * | System.Int64 |
| unsigned char, byte | unsigned char *, byte * | System.Byte |
| wchar_t, unsigned short | wchar_t *, unsigned short * | System.UInt16 |
| unsigned long, unsigned int | unsigned long *, unsigned int * | System.Int32 |
| unsigned hyper | unsigned hyper * | System.UInt64 |
| float | float * | System.Single |
| double | double * | System.Double |
| VARIANT_BOOL | VARIANT_BOOL * | System.Boolean |
| void * | void ** | System.IntPtr |
| HRESULT | HRESULT * | System.Int16 or System.IntPtr |
| SCODE | SCODE * | System.Int32 |
| BSTR | BSTR * | System.String |
| LPSTR or [string, ...] char * | LPSTR * | System.String |
| LPWSTR or [string, ...] wchar_t * | LPWSTR * | System.String |
| VARIANT | VARIANT * | System.Object |
| DECIMAL | DECIMAL * | System.Decimal |
| DATE | DATE * | System.DateTime |
| GUID | GUID * | System.Guid |
| CURRENCY | CURRENCY * | System.Decimal |
| IUnknown * | IUnknown ** | System.Object |
| IDispatch * | IDispatch ** | System.Object |
| SAFEARRAY(type) | SAFEARRAY(type) * | type[] |
Do the following steps to enable PARAM support.
public IPersistStreamInitImpl<scenegraphxcontrol>,
BEGIN_COM_MAP(SceneGraphXControl)COM_INTERFACE_ENTRY(IPersistPropertyBag)| Macro | |
|---|---|
| CW2A asciiStr( wideStr) |
SetDlgItemText( IDC_EDIT_ServerURL, CComBSTR_URL.m_str);SendDlgItemMessage( IDC_RADIO_ServerNeededNo, BM_CLICK);
SendDlgItemMessage( IDC_RADIO_ServerNeededYes, (WPARAM)BST_CHECKED, TRUE);Note: here is a good article about adding a connection point to an existing COM object:
The title is very misleading "Dispinterface vs. Events and Runtime Sinks" http://www.codeguru.com/Cpp/COM-Tech/atl/article.php/c3573/
// proto:
STDMETHOD(put_Sides)(SHORT newVal);
// impl:
STDMETHODIMP CPolyCtl::put_Sides(SHORT newVal)
{
return S_OK;
}
Make a .reg file containing this:
REGEDIT4
[HKEY_CLASSES_ROOT\.dll]
@="dllfile"
[HKEY_CLASSES_ROOT\.ocx]
@="ocxfile"
[HKEY_CLASSES_ROOT\.olb]
@="olbfile"
[HKEY_CLASSES_ROOT\.exe]
@="exefile"
[HKEY_CLASSES_ROOT\dllfile\shell\Register\command]
@="regsvr32.exe %1"
[HKEY_CLASSES_ROOT\ocxfile\shell\Register\command]
@="regsvr32.exe %1"
[HKEY_CLASSES_ROOT\olbfile\shell\Register\command]
@="regsvr32.exe %1"
[HKEY_CLASSES_ROOT\exefile\shell\Register\command]
@="%1 /register"
[HKEY_CLASSES_ROOT\dllfile\shell\Register (Silent)\command]
@="regsvr32.exe /s %1"
[HKEY_CLASSES_ROOT\ocxfile\shell\Register (Silent)\command]
@="regsvr32.exe /s %1"
[HKEY_CLASSES_ROOT\olbfile\shell\Register (Silent)\command]
@="regsvr32.exe /s %1"
[HKEY_CLASSES_ROOT\dllfile\shell\UnRegister\command]
@="regsvr32.exe /u %1"
[HKEY_CLASSES_ROOT\ocxfile\shell\UnRegister\command]
@="regsvr32.exe /u %1"
[HKEY_CLASSES_ROOT\olbfile\shell\UnRegister\command]
@="regsvr32.exe /u %1"
[HKEY_CLASSES_ROOT\exefile\shell\UnRegister\command]
@="%1 /unregister"
[HKEY_CLASSES_ROOT\dllfile\shell\UnRegister (Silent)\command]
@="regsvr32.exe /u /s %1"
[HKEY_CLASSES_ROOT\ocxfile\shell\UnRegister (Silent)\command]
@="regsvr32.exe /u /s %1"
[HKEY_CLASSES_ROOT\olbfile\shell\UnRegister (Silent)\command]
@="regsvr32.exe /u /s %1"
regsvr32.exe
c:\windows\system32\regsrv32.exe
CRegKey key;
retval = key.Open( HKEY_CLASSES_ROOT,
"CLSID\\{0C234ACE-FF67-49F7-ABFD-C62A8D1F94EF}\\InprocServer32", KEY_READ);
TCHAR buf[1000];
key.QueryStringValue( "", buf, &nChars);
Some stl container types need the operator& of a object to return its address.
But CComBSTR and other classes override it. So you need to make them
'unoverride it'. To do this, wrapthen in CAdapt<>, example:
std::list<CAdapt<CComBSTR>>
OBJECT_ENTRY_NON_CREATEABLE_EX_AUTO( clsid, class)
CComCoClass<...> from the class
.rgs file
noncreatable to the [ ] section just above coclass
in the .idl file
.idl file, right
after the imports. Make it look like this: interface INonCreatable;
CComCoClass, define a Category Map:
BEGIN_CATEGORY_MAP(classname) END_CATEGORY_MAP()
STDMETHODIMP CCreatorObject::CreateIt( INonCreatable **ppNonCreatable) {
*ppNonCreatable = NULL;
return CoComCreator< CComObject< CNonCreatable>>::
CreateInstance( NULL, IID_INonCreatable, reinterpret_cast<void**>(ppNonCreatable));
}CComObject<CNonCreatable>::CreateInstance(...)
be used instead ofCoComCreator< CComObject< CNonCreatable>>::CreateInstance(...) if the
newly created class needs special initialization before it is returned. (?something about
this version of CreateInstance not incrementing the Reference count.Categories are used to group COM Objects. For Example you can say your object belongs to
CATID_MyCoolControls - you can have the thing that uses it look for all the
members of CATID_MyCoolControls and load only them
// defining a CATID:
struct __declspec(uuid("{EA4A7D89-7E4E-4450-B607-4E44E1F71CFD}")) CATID_MyCoolControls;
// Put a map like this COM classes header file somewhere:
BEGIN_CATEGORY_MAP(MyControl5)
IMPLEMENTED_CATEGORY(__uuidof(CATID_MyCoolControls))
END_CATEGORY_MAP()
Add a DECLARE_CLASSFACTORY2({classname}) macro to your Interfaces .h file. Example:
class ATL_NO_VTABLE DvoResourceManager :
...
{
public:
DvoResourceManager();
DECLARE_REGISTRY_RESOURCEID(IDR_DVORESOURCEMANAGER)
DECLARE_CLASSFACTORY2(DvoResourceManager)
BEGIN_COM_MAP(DvoResourceManager)
...
Add a DECLARE_CLASSFACTORY_SINGLETON({classname}) macro to your Interfaces .h file. Example:
class ATL_NO_VTABLE DvoResourceManager :
...
{
public:
DvoResourceManager();
DECLARE_REGISTRY_RESOURCEID(IDR_DVORESOURCEMANAGER)
DECLARE_CLASSFACTORY_SINGLETON(DvoResourceManager)
BEGIN_COM_MAP(DvoResourceManager)
...
#import "{name}.tlb" provides the following:
(Note: you can see this implementation of this stuff in the .tli file)
namespace named after the typelibrary name.
DVOCLASSESLib::CLSID_Quad by default you need to specify the #import attribute named_guids#import "DvoClasses.tlb" named_guids__uuidof( DVOCLASSESLib::Quad)
// pseudo .idl
interface Test1
{
[id(1), helpstring("method Add")] HRESULT Add( [out] LONG* pOut, LONG a, LONG b);
[propget, id(2), helpstring("")] HRESULT val([out, retval] short *pVal);
[propput, id(2), helpstring("")] HRESULT val([in] short val);
}
coclass Test
{
[default] interface Test1;
interface Test2;
}
// pseudo .cpp file
#import "Test.tlb"
try {
// smart pointer
Test::ITest1Ptr p( __uuidof( Test::Test1));
// returns a value and throws an exception if failure
int sum = spITest->Add( 5, 5);
// Access properties as data members
int val = spITest->val;
spITest->val = 37;
}
catch (const _com_error & Err) {
// err number is:
Err.Error();
}
Note: you don't always need enable exception handling in the compiler (Project Properties | C/C++ | Code Generation | Enable C++ Exceptions), because that has to do with unwinding locally declared objects.
For each interface entry in coclass (in the .idl file)
the method ObjectMain( bool starting) will be called. It is called with true
on startup and false on shutdown. These are called once when the COM server starts up
and shuts down. Override it do do your own startup initialization and shutdown cleanup.
Since Interface methods cannot be called from the constructor, due to no vtable until
after the constructor returns; Initialization should occur in FinalConstruct
(which should be defined as an empty inline method in the COM classes header file)
Note: FinalConstruct MUST return S_OK on success (any other success code is considered a failure)
Another good thing about using FinalConstuct, is that you can fail out
of it to prevent your object from being returned (I assume it just destroys it)
Beware of the gotcha 'Premature deletion of an object in FinalConstruct'
There is also a FinalRelease, but I don't know why you would need to use it
instead of the destructor.
#import "file.tlb"It is reached by {Namespace}{enum val name} be careful
not to use {Namespace}::{enum typedef name}::{enum val name}
#import "name.tlb" always defines a namespace, use the namespace to
get to the ComPtr you want.
The namespace is named after the library name
(which can be found in the .idl file)
ATL (by default) doesn't include the full CRT (C Runtime library), and you hit some code
that needs part of it that isn't available. Remove the definition of _ATL_MIN_CRT
so that it will include the full CRT. It looks like the way to turn this off is now from
Project Properties | General | Minimize CRT Use in ATL
If FinalConstruct causes an AddRef() and a Release()
to be called it will delete itself (this may happen if you do a QueryInterface in
FinalConstruct). To prevent this from happening, add this macro to your
COM classes header file: DECLARE_PROTECT_FINAL_CONSTRUCT() (this seems to be
added by default now)
The .tlb is probably loaded in some app, and cannot be written.
.idl file and copy the coclass guid into the clipboard
coclass
guid (Make sure the value is surrounded by braces { } (just like the other
entries)
Default to edit it
Value to
the name of coclass
InprocServer32
Default value to the fully specified path of the COM .dll
String value to InprocServer32
ThreadingModel and set its value to Apartment
OLE/COM Object Viewer it is OleView.exe
or in the tools menu of Visual Studio. It will be at Object Classes | All Objects
//CLSID if using import
#import "Something.tlb"
// Note: use the class name, not the Interface name ISomething
// you can find it in the .idl file as the coclass value
__uuidof(Something)
//CLSID from a String
CLSID clsId;
CLSIDFromString(L"{145AE31A-E4AE-4400-A485-A39900E03C84}", &clsId);
//CLSID from ProgID
CLSIDFromProgID(); // inverse is: ProgIDFromCLSID();
| S_OK |
| S_FAIL |
| if (SUCCEEDED(hr)) |
| if (FAILED(hr)) |
| Macro | Purpose |
|---|---|
| _ATL_DEBUG_INTERFACES | Shows Interface Leaks when _Module.Term is called. |
| _ATL_DEBUG_QI | Shows when QueryInterface is called. |
| ATLTRACE2 | See Micosofts docs, controls lots of settings. |
_ATL_DEBUG_INTERFACES is defined before the atl headers are included (best to just put it in stdafx.h)
QIThunk - 10 Release : Object = 0x01d1aa6c Refcount = 0 CGLX2DOGL - IDvoGLX2D
_AtlDebugInterfacesModule.m_nIndexBreakAt = {QIThinkNumberGoesHere};DebugBreak();
will be called when there is Reference count activity on the specified Object
Class.
atlbase.h in bool CAtlDebugInterfacesModule::DumpLeakedThunks() at the line m_aThunks.RemoveAll();, then it will break just after dumping the leaked list to the output window (so you don't have to scroll around for it)._AtlDebugInterfacesModule is of type CAtlDebugInterfacesModule there are some other fields you can access in it ( m_nIndexQI, m_cs, and m_aThunks )
It seems like there is a way to remove a thunk from _AtlDebugInterfacesModule at runtime (from something I read about it).
Here is the article that tipped me off about this way to debug reference counts: http://codeguru.earthweb.com/atl/atldebug.shtml
WARNING: Do NOT make a BSTR like this: BSTR str = L"Hello";. While it may work in many cases, it is not right and will not delete correctly.
BSTRs are pointers to the 5th byte of a memory block. When they are 'free()ed', they are 'free()ed' from 4 bytes before where they point to. They start with a count of the number of bytes of character data they contain, excluding 2 terminating 0 bytes at the end of the string.

BSTR str1 = SysAllocString( L"Hello");
BSTR str2 = SysAllocString( OLESTR("Hello"));
SysFreeString(str2);
SysFreeString(str1);
| BSTR SysAllocString( WideString); | Takes the Wide Char string, and copies it into a newly allocated BSTR |
| BSTR SysAllocStringLen(?) | |
| BSTR SysAllocStringByteLen(?) | |
| BSTR SysReAllocString(?) | |
| BSTR SysReAllocStringLen(?) | |
| ???? SysStringLen(?) | |
| ???? SysStringByteLen(?) | |
| ???? VectorFromBstr(?) | SAFEARRAY Vector from a BSTR |
| ???? BstrFromVector(?) | BSTR to a SAFEARRAY Vector |
| ???? VarBstrCat(?) | (undocumented) Concatinates |
| ???? VarBstrCmp(?) | (undocumented)Compares 2 BSTRs and returns one of:VTCMP_LT, VTCMP_EQ, VTCMP_GT, VTCMP_NULL |
| SysFreeString( BSTR); |
Try using CComBSTR
| CString( BSTR); | Construct directly from a BSTR |
| ?? BSTR CString::AllocSysString(); | To Read it out |
| ?? BSTR CString::SetSysString(?); | To realloc |
CString str1( BSTR bstr);
Microsoft Visual C++® version 5.0 and later support the _bstr_t class to provide a more sophisticated wrapper for BSTR. In addition to the functionality of CComBSTR, _bstr_t also provides comparison operators. In addition, _bstr_t avoids allocating extra BSTR objects by using reference counting—more efficient, but not as thin a wrapper as the CComBSTR.
You can also use the C++ Standard Library's wstring class, but you'll have to convert to and from BSTR. You can convert from BSTR to wstring by constructing a new wstring object using the appropriate constructor. You convert to a BSTR by getting a pointer to the string wrapped by the wstring object and calling one of the COM APIs that allocate a BSTR, such as SysAllocString.
[id(15), helpstring("method DeleteIcon")]
HRESULT DeleteIcon( [in] long IconHandle);
STDMETHOD(DeleteIcon)( long iconHandle );
STDMETHODIMP ClassName::DeleteIcon( long iconHandle)
{
return S_OK;
}
I made an ActiveX control in Visual Studio .NET as follows
I made the html file to test the control look like this:
// NOTE: get the CLSID from coclass JbpCon6 uuid
<!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ATL 7.0 test page for object JbpCon6</title>
</HEAD>
<body>
<object
id="JbpCon6"
classid="CLSID:807C00B5-0A73-485F-81AC-8630EF2FCD41"
viewastext>
</object>
<script language="javascript" for=JbpCon6 event=OnJBP>
<!--
JbpCon6_OnClick()
//-->
</script>
<script id=clientEventHandlersJS language="javascript">
function JbpCon6_OnClick()
{
alert("hello");
}
</script>
</body>
</HTML>
And the alert box would pop up when I hit the button (after an ActiveX warning box)
IComThing is IComThingPtr and is found in the file: {somename}Tlb.h
pLutFactory.CreateInstance( CLSID_DvoLutFactory);
_com_ptr_t
operator bool() const throw() to tell if a smart pointer is pointing to NULL
__uuidof( DVOCLASSESLib::Quad)
#include "{???}Tlb.h"
// Connects on its own
{
IComThingPtr spComThing;
spComThing.CreateInstance( CLSID_ComThing);
spComThing->foo();
// or
IComThing2Ptr spComThing2( CLSID_ComThing2); // ???
spComThing2->foo();
}
// or Connects to an old style com object
{
IComThing *pComThing = ...;
IComThingPtr spComThing;
// it adds a ref when it adds it
spComThing = pComThing;
spComThing->foo();
// it removes a ref when it goes out of scope
}
// Switching Interfaces 'Casting'
IGooPtr spIGoo(__uuidof(MyObject)); // create object
spIGoo->Gunc(); // call method
IFooPtr spIFoo = spIGoo; // QI different interface, same object
spIFoo->Func1(); // call method on new interface
if the linker cannot find something like: IID_IRasterBandCollection
make sure StdAfx.h contains something like: #include <idl/rdotlb.h>
See also VARIANT and SAFEARRAY info
When creating the SAFEARRAY:
SAFEARRAYBOUND *sab = new SAFEARRAYBOUND[2];
sab[0].lLbound = 0;
sab[0].cElements = pCalcBins; // least significant index
sab[1].lLbound = 0;
sab[1].cElements = pCalcBins; // most significant index
When reading the SAFEARRAY:
for ( j = 0; j < 3; j++) {
for ( i = 0; i < pCalcBins; i++) {
long indices[] = { i, j}; // { least significant, most significant }
SafeArrayGetElement( psaOutLUT, indices, &val);
}
}
| SafeArrayCreate |
| SafeArrayCreateVector |
| SafeArrayCopy |
| SafeArrayCreateEx |
| SafeArrayCreateVectorEx |
| SafeArrayGetDim |
| SafeArrayGetLBound |
| SafeArrayGetUBound |
| SafeArrayGetElemsize |
| SafeArrayGetElement |
| SafeArrayPutElement |
| SafeArrayLock |
| SafeArrayAccessData |
| SafeArrayPtrOfIndex |
| SafeArrayUnlock |
| SafeArrayUnaccessData |
| SafeArrayRedim |
| SafeArrayDestroy |
| SafeArrayAllocDescriptor |
| SafeArrayAllocDescriptorEx |
| SafeArrayAllocData |
| SafeArrayCopyData |
| SafeArrayDestroyDescriptor |
| SafeArrayDestroyData |
| (undoc) SafeArraySetIID |
| (undoc) SafeArrayGetIID |
| (undoc) SafeArrayGetRecordInfo |
| (undoc) SafeArraySetRecordInfo |
| (undoc) SafeArrayGetVartype |
VARIANT v;
VariantInit( &v); // Sets the Variant to VT_EMPTY
SAFEARRAYBOUND rgb [] = {100, 0}; // numElements, lowerBounds
v.vt = VT_ARRAY | VT_I4; // Array of longs
v.parray = SafeArrayCreate(VT_I4, 1, rgb); // Type, Dimensions, BoundsInfo
long *rgelems; // Pointer for long elements
SafeArrayAccessData(v.parray, (void**)&rgelems); // Get pointer to data and lock array
for (int c = 0; c < 100; c++)
rgelems[c] = c;
SafeArrayUnaccessData(v.parray); // Release the lock on the array
// The VARIANT now owns the SafeArray, and will free it when VariantClear is called
CComVariant for VARIANTS, since it calls VariantInit
on creation, VariantClear on destruction, and
has many overloaded '=' operators
Article: http://www.whooper.co.uk/excelvariants.htm
{ClassName}.cpp
{ClassName}.h
{ClassName}.rgs
.idl file.
resource.h (hmm, does _APS_NEXT_SYMED_VALUE need
to be considered? I am assuming not)
.rc file
Thunking, or how to get a classes this pointer through a static callback that has no place to save it as callback data.
You basically use the fact that a function pointer is an address that is called to jump into a function.
The change it so that it jumps into some assembly that you build, as opposed to the actual function start that it expects to jmp into.
For the WINDPROC case, you swap out the HWND parameter with the 'this' pointer you want. then you execute a jmp to the static WINDPROC of the class the 'this' belongs to.
At that point you cast the HWND parameter to a 'this' of the class the static function belongs to, and then you can call a member func with it.
regsvr32 /u ./file.dll
// or
regsvr32 /u ./file.ocx