Hello last week, I take some time to optimize and understand Addins for Inventor. After I realize that it's just a simple DLL with a DllGetClassObject entry and some query interface. I decide to flush the entire actual ATL template, IDL, DEF and re-begin on a new form, for my part, are really more understandable and clear. Now I've a true question about C++ COM DLL, but before let me explain where I stuck. First, this Addins can be based on 2 class.
IRxApplicationAddInServer where is only a IUnknown based object (require queryinterface and addref)
or
ApplicationAddInServer this one was IUnknown and IDispatch based object (require additional GetTypeInfoCount, GetTypeInfo, GetIDsOfNames and Invoke)
I decide to implement the simplest version, do some research on COM and kick out ATL, compile and it's working... The addins trigger Activate, my ribbons appear in the program. Then, i continue and made some change in the code to convert IRxApplicationAddInServer to ApplicationAddInServer and also include the missing virtual function that IDispatch requiert. Launch it with no result. But I realize if I trim a part of my query interface to avoid main class creation this one give me strange behavior (calling twice same interface) call GetIDsOfNames with a trace of Activate function and also call Invoke after.. but not really a true Activate trigger.. no ribbon
if (IsEqualGUID(riid, IID_IUnknown))
*ppv = (IUnknown*)this;
else if (IsEqualGUID(riid, IID_IDispatch))
*ppv = (IDispatch*)this;
#ifndef _QueryPatch //Trimmed Part
else if (IsEqualGUID(riid, CLSID_InventorAddInServer))
*ppv = (TARGETSERVER*)this;
#endif
Q: How relay information between GetIDsOfNames and Invoke?
I send you the simple 1 file code and an Image of code result.
#include <windows.h>
#include "InventorUtils.h"
#define _WithDispatch
#define _QueryPatch
#ifdef _WithDispatch
#define TARGETSERVER ApplicationAddInServer
#define TARGETSITE ApplicationAddInSite
#else
#define TARGETSERVER IRxApplicationAddInServer
#define TARGETSITE IRxApplicationAddInSite
#endif // _WithDispatch
extern "C" __declspec(selectany) const _GUID CLSID_InventorAddInLite = { 0x0B047779, 0x4EFD, 0x4ADF, {0x99, 0x24, 0x76, 0x8B, 0xF9, 0x76, 0xA6, 0x48}};
extern "C" __declspec(selectany) const _GUID CLSID_InventorAddInServer = { 0xE3571292, 0xDB40, 0x11D2, {0xB7, 0x83, 0x00, 0x60, 0xB0, 0xF1, 0x59, 0xEF }};
unsigned long g_lockCount;
unsigned long g_objCount;
void GuidToObjectName(_GUID riid, wchar_t* StrOut)
{
if (IsEqualGUID(riid, GUID_NULL))
wcscpy_s(StrOut, 2048, L"Null");
if (IsEqualGUID(riid, IID_IUnknown))
wcscpy_s(StrOut, 2048, L"IUnknown");
if (IsEqualGUID(riid, IID_IDispatch))
wcscpy_s(StrOut, 2048, L"IDispatch");
if (IsEqualGUID(riid, IID_IClassFactory))
wcscpy_s(StrOut, 2048, L"IClassFactory");
if (IsEqualGUID(riid, CLSID_InventorAddInLite))
wcscpy_s(StrOut, 2048, L"Inventor Addin");
if (IsEqualGUID(riid, CLSID_InventorAddInServer))
wcscpy_s(StrOut, 2048, L"Inventor Server");
}
class CInventorLite : public TARGETSERVER
{
public :
CInventorLite()
{
m_cRef = 0;
g_objCount++;
}
~CInventorLite()
{
g_objCount--;
}
virtual _declspec(nothrow) long _stdcall Activate(TARGETSITE* pAddInSite, char FirstTime)
{
wchar_t Str[2048];
swprintf_s(Str, L"Begin\nFirst Time : %d", FirstTime);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - Activate", MB_OK);
if (pAddInSite == NULL)
return E_INVALIDARG;
m_pAddInSite = pAddInSite;
long hr;
#ifdef _WithDispatch
if (FAILED(hr = m_pAddInSite->get_Application(&m_pApplication)))
return hr;
#else
CComPtr<IUnknown> pAppUnk;
if (FAILED(hr = m_pAddInSite->get_Application(&pAppUnk)))
return hr;
if (FAILED(hr = pAppUnk->QueryInterface(&m_pApplication)))
return hr;
#endif
/* Ribbon */
CComPtr<UserInterfaceManager> pUserInterfaceMgr;
hr = m_pApplication->get_UserInterfaceManager(&pUserInterfaceMgr);
if (FAILED(hr)) return hr;
CComPtr<Ribbons> pRibbons;
hr = pUserInterfaceMgr->get_Ribbons(&pRibbons);
if (FAILED(hr)) return hr;
CComPtr<Ribbon> pPartRibbon;
hr = pRibbons->get_Item(CComVariant(_T("Assembly")), &pPartRibbon);
if (FAILED(hr)) return hr;
//get the tabs associated with part ribbon
CComPtr<RibbonTabs> pRibbonTabs;
hr = pPartRibbon->get_RibbonTabs(&pRibbonTabs);
if (FAILED(hr)) return hr;
CComPtr<RibbonTab> pRibbonTab;
hr = pRibbonTabs->Add(CComBSTR("My Ribbon"), CComBSTR("id_Tab_MyRibbon"), CComBSTR("MyID"), CComBSTR(""), false, false, &pRibbonTab);
if (FAILED(hr)) return hr;
CComPtr<RibbonPanel> pCustomPanel;
pRibbonTab->GetRibbonPanels()->Add(CComBSTR("Sample"), CComBSTR("MYSample"), CComBSTR(""), CComBSTR(""), false, &pCustomPanel);
if (FAILED(hr)) return hr;
/* Combo */
CComPtr<CommandManager> pCommandManager;
hr = m_pApplication->get_CommandManager(&pCommandManager);
if (FAILED(hr)) return hr;
CComPtr<ControlDefinitions> pControlDefinitions;
hr = pCommandManager->get_ControlDefinitions(&pControlDefinitions);
if (FAILED(hr)) return hr;
CComPtr<ComboBoxDefinitionObject> m_pSlotWidthComboBoxDef;
hr = pControlDefinitions->AddComboBoxDefinition(CComBSTR(_T("Slot Width")),
CComBSTR(_T("Autodesk:SimpleAddIn:SlotWidthCboBox")),
kShapeEditCmdType,
50,
CComVariant(_T("{DB59D9A7-EE4C-434A-BB5A-F93E8866E872}")),
CComBSTR(_T("Specifies slot width")),
CComBSTR(_T("Slot width")),
vtMissing,
vtMissing,
kAlwaysDisplayText,
&m_pSlotWidthComboBoxDef);
CComPtr<ButtonDefinitionObject> pAddSlotOptionCmdBtnDef;
hr = pControlDefinitions->AddButtonDefinition(CComBSTR(_T("Add Slot")),
CComBSTR(_T("Autodesk:SimpleAddIn:AddSlotOptionCmd")),
kShapeEditCmdType,
CComVariant(_T("{DB59D9A7-EE4C-434A-BB5A-F93E8866E872}")),
CComBSTR(_T("Adds option for slot width/height")),
CComBSTR(_T("Add slot option")),
vtMissing,
vtMissing,
kDisplayTextInLearningMode,
&pAddSlotOptionCmdBtnDef);
if (FAILED(hr)) return hr;
CComPtr<CommandControls> pCommandControls;
hr = pCustomPanel->get_CommandControls(&pCommandControls);
if (FAILED(hr)) return hr;
CComPtr<CommandControl> pSlotWidthCmdCboBoxCmdCtrl;
hr = pCommandControls->AddComboBox(m_pSlotWidthComboBoxDef, CComBSTR(""), true, &pSlotWidthCmdCboBoxCmdCtrl);
if (FAILED(hr)) return hr;
CComPtr<CommandControl> pSlotWidthCmdButtonCmdCtrl;
hr = pCommandControls->AddButton(pAddSlotOptionCmdBtnDef,false, true, CComBSTR(_T("")), false, &pSlotWidthCmdButtonCmdCtrl);
if (FAILED(hr)) return hr;
/* End Combo*/
//MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - Activate", MB_OK);
return S_OK;
}
virtual _declspec(nothrow) long _stdcall Deactivate()
{
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - Desactivate", MB_OK);
return S_OK;
}
/*** Never Trigged *****/
virtual _declspec(nothrow) long _stdcall ExecuteCommand(LONG CommandID)
{
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - ExecuteCommand", MB_OK);
return S_OK;
}
/*** Never Trigged *****/
virtual _declspec(nothrow) long _stdcall get_Automation(IUnknown** ppResult)
{
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - get_Automation", MB_OK);
return S_OK;
}
virtual _declspec(nothrow) long _stdcall QueryInterface(const _GUID& riid, void** ppv)
{
*ppv = NULL;
long Result = E_NOINTERFACE;
OLECHAR* guidString;
StringFromCLSID(riid, &guidString);
wchar_t Str[2048];
wchar_t typName[2048];
GuidToObjectName(riid, typName);
swprintf_s(Str, L"Begin\nriid : %s - %s", guidString, typName);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - Query Interface", MB_OK);
::CoTaskMemFree(guidString);
if (IsEqualGUID(riid, IID_IUnknown))
*ppv = (IUnknown*)this;
else if (IsEqualGUID(riid, IID_IDispatch))
*ppv = (IDispatch*)this;
#ifndef _QueryPatch
else if (IsEqualGUID(riid, CLSID_InventorAddInServer))
*ppv = (TARGETSERVER*)this;
#endif
if (*ppv)
{
AddRef();
Result = S_OK;
}
return Result;
}
virtual _declspec(nothrow) unsigned long _stdcall AddRef()
{
//MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - AddRef", MB_OK);
return ++m_cRef;
}
virtual _declspec(nothrow) unsigned long _stdcall Release()
{
if (--m_cRef == 0)
{
delete this;
return 0;
}
//MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - Release", MB_OK);
return m_cRef;
}
virtual _declspec(nothrow) long _stdcall GetTypeInfoCount(UINT* pctinfo)
{
wchar_t Str[2048];
swprintf_s(Str, L"Begin\npctinfo : %d", *pctinfo);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - GetTypeInfoCount", MB_OK);
if (pctinfo == NULL)
{
return E_INVALIDARG;
}
*pctinfo = 1;
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - GetTypeInfoCount", MB_OK);
return NOERROR;
}
virtual _declspec(nothrow) long _stdcall GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo)
{
wchar_t Str[2048];
swprintf_s(Str, L"Begin\niT Info : %d, lcid : %d", iTInfo, lcid);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - GetTypeInfo", MB_OK);
if (0 != iTInfo)
return E_INVALIDARG;
(*ppTInfo = m_pTypeInfo)->AddRef();
return S_OK;
}
virtual _declspec(nothrow) long _stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId)
{
OLECHAR* guidString;
StringFromCLSID(riid, &guidString);
wchar_t Str[2048];
wchar_t typName[2048];
GuidToObjectName(riid, typName);
swprintf_s(Str, L"Begin\nriid : %s - %s\nregName : %s\ncNames : %d\nlcid : %d\nrgDispId : %d\n", guidString, typName, rgszNames[0], cNames, lcid, *rgDispId);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - GetIDsOfNames", MB_OK);
::CoTaskMemFree(guidString);
return S_OK;
}
virtual _declspec(nothrow) long _stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
OLECHAR* guidString;
StringFromCLSID(riid, &guidString);
wchar_t Str[2048];
wchar_t typName[2048];
GuidToObjectName(riid, typName);
swprintf_s(Str, L"Begin\nrgDispId : %d\nriid : %s - %s\nlcid : %d\n", dispIdMember, guidString, typName, lcid);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - Invoke", MB_OK);
::CoTaskMemFree(guidString);
return S_OK;
}
private:
ITypeInfo* m_pTypeInfo;
unsigned long m_cRef;
CComPtr<Application> m_pApplication;
CComPtr<TARGETSITE> m_pAddInSite;
};
class CInventorLiteFactory : public IClassFactory
{
public:
CInventorLiteFactory()
{
m_cRef = 0;
g_objCount++;
}
~CInventorLiteFactory()
{
g_objCount--;
}
virtual _declspec(nothrow) long _stdcall QueryInterface(const _GUID& riid, void** ppv)
{
OLECHAR* guidString;
StringFromCLSID(riid, &guidString);
wchar_t typName[2048];
GuidToObjectName(riid, typName);
wchar_t Str[2048];
swprintf_s(Str, L"Begin\nriid : %s - %s", guidString, typName);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - IClassFactory Query Interface", MB_OK);
::CoTaskMemFree(guidString);
long Result = E_NOINTERFACE;
*ppv = NULL;
if (IsEqualGUID(riid, IID_IClassFactory))
{
*ppv = (IClassFactory*)this;
AddRef();
Result = NOERROR;
}
//MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - IClassFactory QueryInterface", MB_OK);
return Result;
}
virtual _declspec(nothrow) unsigned long _stdcall AddRef()
{
//MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - IClassFactory Add Ref", MB_OK);
return ++m_cRef;
}
/*** NEVER TRIG **************/
virtual _declspec(nothrow) unsigned long _stdcall Release()
{
if (--m_cRef == 0)
{
delete this;
return 0;
}
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - IClassFactory Release", MB_OK);
return m_cRef;
}
virtual _declspec(nothrow) long _stdcall CreateInstance(IUnknown* pUnkOuter, const _GUID &riid, void** ppvObject)
{
OLECHAR* guidString;
StringFromCLSID(riid, &guidString);
wchar_t typName[2048];
GuidToObjectName(riid, typName);
wchar_t Str[2048];
swprintf_s(Str, L"Begin\nriid : %s - %s", guidString, typName);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - IClassFactory CreateInstance", MB_OK);
::CoTaskMemFree(guidString);
long hr;
CInventorLite* pInvLite = new CInventorLite();
if (FAILED(hr = pInvLite->QueryInterface(riid, ppvObject)))
delete pInvLite;
return hr;
}
/*** NEVER TRIG **************/
virtual _declspec(nothrow) long _stdcall LockServer(int fLock)
{
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - IClassFactory LockServer", MB_OK);
fLock ? g_lockCount++ : g_lockCount--;
return S_OK;
}
private:
unsigned long m_cRef;
};
extern "C" long _stdcall DllGetClassObject(const _GUID & rclsid, const _GUID & riid, void** ppv)
{
OLECHAR* guidString1;
OLECHAR* guidString2;
StringFromCLSID(rclsid, &guidString1);
StringFromCLSID(riid, &guidString2);
wchar_t Str[2048];
wchar_t typName1[2048];
GuidToObjectName(rclsid, typName1);
wchar_t typName2[2048];
GuidToObjectName(riid, typName2);
swprintf_s(Str, L"Begin\nclsid : %s - %s\nriid : %s - %s", guidString1, typName1, guidString2, typName2);
MessageBox(NULL, Str, L"Inventor Addins - C++ Native - DllGetClassObject", MB_OK);
::CoTaskMemFree(guidString1);
::CoTaskMemFree(guidString2);
long Result = NOERROR;
*ppv = NULL;
if (!IsEqualGUID(riid, IID_IUnknown) && !IsEqualGUID(riid, IID_IClassFactory))
Result = E_NOINTERFACE;
else if (IsEqualGUID(rclsid, CLSID_InventorAddInLite))
{
if (*ppv = new CInventorLiteFactory())
((IUnknown*)*ppv)->QueryInterface(riid, ppv);
else
return E_OUTOFMEMORY;
}
else
Result = E_FAIL;
//MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - DllGetClassObject", MB_OK);
return Result;
}
/*** NEVER TRIG **************/
extern "C" long _stdcall DllCanUnloadNow()
{
MessageBox(NULL, L"End", L"Inventor Addins - C++ Native - DllCanUnloadNow", MB_OK);
return g_lockCount == 0 && g_objCount == 0 ? S_OK : S_FALSE;
}
#pragma comment(linker, "/EXPORT:DllCanUnloadNow=DllCanUnloadNow,PRIVATE")
#pragma comment(linker, "/EXPORT:DllGetClassObject=DllGetClassObject,PRIVATE")