Starting in December, we will archive content from the community that is 10 years and older. This FAQ provides more information.
All begin last year when I decide to create my own Autodesk Inventor plugins. After testing and installing all type of Inventor plugins (C#, C++ Managed, C++ vtable and C++ IDispatch), I got some size conclusion.
Then I decide to understand and optimize the C++ model, After 3 month in my spare time, I've stop to Events. This year I give another try and I made it.
Now how achieve five time smaller that original code? It's easy, let's begin by what I didn't love. First, the IDL. If you begin to read the code generated by the interface definition, believe me, it's a total carnage of redefinition upon redefinition of code. Also, when you work with IDL you must also use a program like OLEVIEW to get function number to transfer inside your IDL. Second thing is the ATL of Microsoft. I'm not really a fan and not really a hater, it's just ATL was a developed for different use and it's just too massive and huge for the simplicity of the plugins.
DLL can have multiple entry point. For this one, we got DllGetClassObject. Then we facing COM Dll form based on IUnknown and IDispatch. Now IUnknown and IDispatch class was really annoying to code, because each time that you redefine a new class derived you must also redefine a lot of pure virtual function. You need a good class helper to get rid of task. First, with pure virtual function, you can't declare your helper on the same level that your derived class, you need 2 level to transform your pure virtual into virtual function.
class MyClass : public Helper, public IUnknown // Dosen't work properly
class Helper : public IUnknown // Level 1
class MyClass : public Helper // Level 2
Now AddRef and Release was thing of past and with the magic of template you can automate your QueryInterface.
template <typename BaseClass>
class IUnknownHelper : public BaseClass
template <typename BaseClass>
IDispatchHelper : public IUnknownHelper<BaseClass>
But before automate the QueryInterface, let's talk about UUID. There is two way to define and get UUID or GUID, we gonna say from variable and from function.
From variable: You define a global constant and you simply use this variable.
extern "C" const GUID __declspec(selectany) DIID_ButtonDefinitionSink =
{0x9b5d2ec6,0xa4bd,0x4b3a,{0x8a,0x34,0x06,0x9e,0xe5,0x2b,0x00,0x9d}};
From function : You define your UUID inside your class or your structure and you the __uuidof function on your class
class __declspec(uuid("0B047779-4EFD-4ADF-9924-768BF976A648")) CInventorLite;
__uuidof(CInventorLite)
In this form with the magic of template can operate on QueryInterface
template <typename BaseClass>
long IUnknownHelper<BaseClass>::QueryInterface(const _GUID& riid, void** ppv)
{
*ppv = NULL;
long Result = E_NOINTERFACE;
if (riid == __uuidof(BaseClass))
{
*ppv = (BaseClass*)this;
if (*ppv)
{
AddRef();
Result = S_OK;
}
}
return Result;
}
Now no code require anymore for IUnknown derived class. Unfortunately, we can't automate the Invoke function from IDispatch. Class helper was now fix, but we must create some DLL helper.
The first one is where I've fall last year. We must create a advise function that sink the class when event was call. The sink object must also be member of the class. My first error is that I declare it inside the function. My second error was I follow Autodesk idea to use CComPtr. I've nothing about CComPtr, is fine, is the part of smart pointer and it's great idea. But IConnectionPoint::Advide didn't work with CComPtr (Or I've not re-code Advise to be compatible with CComPtr). For the moment I've remove all CComPtr and with or without it's have no effect on final size.
The second, if you want use resource you need HModule of DLL. Habitually HModule was given into DLLMain, thing that COM DLL dosen't have. You need to use this trick.
HMODULE GetThisDllHandle()
{
MEMORY_BASIC_INFORMATION info;
size_t len = VirtualQueryEx(GetCurrentProcess(), (void*)GetThisDllHandle, &info, sizeof(info));
return (len == sizeof(info)) ? (HMODULE)info.AllocationBase : NULL;
}
Now you can flush ATL and only use the precept of IUnknown, IDispatch and COM DLL.
I also invite you do download the Visual Studio 2022 plugins (x64) or the CPP example to analyze the entire code (C++ Native - Define Switchable). At this simplified form I have some question.
Some class like IRxApplicationEvents or IRxApplicationAddInServer use direct Virtual function call. Other like ApplicationAddInServer or ButtonDefinitionSink pass by the Invoke function. In this time you must return to OLEVIEW at get function address to re-code invoke function. In this form, what's wrong with Virtual call? I've optimization dilemma ApplicationEvents (Invoke) doesn't exist like IRxButtonDefinitionSink (Virtual) doesn't exist too. But I really like this new form. It's clear and understandable. I'm gonna continue my research with MFC dialog and some other stuff like this.
What I really love with virtual style, it's all became IntelliSense compatible. That mean, that you don't need other external program. You can have a ton of online information about class, function and member. You type a class with dot and BAM! you access to member function. When your header was well documented, you can code something without documentation. No special function number to extract and rebind to the function.
Can't find what you're looking for? Ask the community or share your knowledge.