Smart pointer to atl com interface - c++

I have an atl-com dll that has an interface IMyInterface. I created a client with #import "Project.tlb" and it was generated the correct Project.tlh and Project.tli. So I included Project.tlb in my client. Now I'm trying to call the IMyInterfacePtr that #import generated like this:
IMyInterfacePtr *i;
CLSID clsid = __uuidof(Test);
REFIID iid = __uuidof(IMyInterfacePtr);
HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (void**) &i);
So using the pointer to interface:
i->MyMethod();
But it didn't recognize "MyMethod", it's only showing the IDispatch interface that is from which my IMyInterface inherits.
What Am I doing wrong?
Thanks!

Related

Using IManagedAddin in VC++ to load VSTO VBA add-ins

I was given a VSTO Outlook add-in written in VBA, and have been tasked to reduce the start-up time. I'm rather new to the whole add-in and COM objects thing so I need some help.
The add-in takes anywhere from 0.2s to 2.0s to startup, and Outlook disables the plugin if the average startup time is >1000ms. Using the registry hack to force enable the add-in is unfortunately not an option. I've also tested it with an empty add-in, which also can take up to 1.8s to startup. I've searched SO and other such sites for a solution, and across one involving writing a "stub" in an un-managed language such as Delphi or C++, which does nothing but load the actual add-in. The interface that's supposed to do this is IManagedAddin.
My issue is with implementing this interface. I've created a simple add-in in VC++ The class that implements _IDTExtensibility2 is in Connect.h, shown below. However, I don't have a clue how to implement IManagedAddin::Load to load my add-in into Outlook, and there doesn't seem to be a lot of documentation on this. Any help would be much appreciated!
EDIT: Updated code below
// Connect.h : Declaration of the CConnect
#pragma once
#include "resource.h" // main symbols
#include "NativeAddin_i.h"
#include "IManagedAddin.h"
#include <Windows.h>
#include <iostream>
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
#endif
using namespace ATL;
// CConnect
class ATL_NO_VTABLE CConnect :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CConnect, &CLSID_Connect>,
public IDispatchImpl<IConnect, &IID_IConnect, &LIBID_PixelLLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IDispatchImpl<_IDTExtensibility2, &__uuidof(_IDTExtensibility2), &LIBID_AddInDesignerObjects, /* wMajor = */ 1, /* wMinor = */ 0>
{
public:
static Outlook::_Application* outlookApp;
static ext_ConnectMode connectMode;
static LPDISPATCH addInInst;
static SAFEARRAY** customArr;
static UINT_PTR timerId;
CConnect()
{
}
DECLARE_REGISTRY_RESOURCEID(106)
BEGIN_COM_MAP(CConnect)
COM_INTERFACE_ENTRY(IConnect)
COM_INTERFACE_ENTRY2(IDispatch, _IDTExtensibility2)
COM_INTERFACE_ENTRY(_IDTExtensibility2)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
static VOID CALLBACK TimerCallback(HWND hWnd, UINT nMsg, UINT timerId, DWORD dwTime)
{
KillTimer(NULL, timerId);
HRESULT hr;
BSTR manifestUrl = SysAllocString(L"file://path/to/manifest.vsto");
// load add-in
IID clsid;
hr = IIDFromString(OLESTR("{99D651D7-5F7C-470E-8A3B-774D5D9536AC}"), &clsid); // VSTOAddinLoader CLSID
IID iid;
hr = IIDFromString(OLESTR("{B9CEAB65-331C-4713-8410-DDDAF8EC191A}"), &iid);
IManagedAddin* loader;
hr = CoCreateInstance(clsid, NULL, CLSCTX_ALL, iid, (void**)&loader);
hr = loader->Load(manifestUrl, outlookApp);
_IDTExtensibility2* ext;
hr = loader->QueryInterface(IID__IDTExtensibility2, (void**)&ext);
hr = ext->OnConnection(outlookApp, connectMode, addInInst, customArr);
MSO::IRibbonExtensibility* ribbon;
hr = (???)->QueryInterface(MSO::IID_IRibbonExtensibility, (void**)&ribbon);
}
STDMETHOD(OnConnection)(LPDISPATCH App, ext_ConnectMode ConnectMode, LPDISPATCH AddInInst, SAFEARRAY * * custom)
{
HRESULT hr;
UINT time = 200;
Outlook::_Application* app;
hr = App->QueryInterface(__uuidof(Outlook::_Application), (void**)&app);
// init static members
outlookApp = app;
connectMode = ConnectMode;
addInInst = AddInInst;
customArr = custom;
timerId = SetTimer(NULL, 0, time, (TIMERPROC)&TimerCallback);
return S_OK;
}
STDMETHOD(OnDisconnection)(ext_DisconnectMode RemoveMode, SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnAddInsUpdate)(SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnStartupComplete)(SAFEARRAY * * custom)
{
return S_OK;
}
STDMETHOD(OnBeginShutdown)(SAFEARRAY * * custom)
{
return S_OK;
}
};
OBJECT_ENTRY_AUTO(__uuidof(Connect), CConnect)
Outlook::_Application* CConnect::outlookApp = NULL;
ext_ConnectMode CConnect::connectMode = ext_cm_AfterStartup;
LPDISPATCH CConnect::addInInst = NULL;
SAFEARRAY** CConnect::customArr = NULL;
UINT_PTR CConnect::timerId = 0;
// IManagedAddin.h
#pragma once
#include "resource.h"
#include "NativeAddin_i.h"
struct __declspec(uuid("B9CEAB65-331C-4713-8410-DDDAF8EC191A"))
IManagedAddin : IUnknown
{
public:
virtual STDMETHOD(Load)(BSTR bstrManifestUrl, LPDISPATCH pdisApplication) = 0;
virtual STDMETHOD(Unload)() = 0;
};
// pch.h
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#import "libid:AC0714F2-3D04-11D1-AE7D-00A0C90F26F4" raw_interfaces_only, raw_native_types, named_guids, auto_search, no_namespace
#import "libid:2DF8D04C-5BFA-101B-BDE5-00AA0044DE52" raw_interfaces_only, raw_native_types, named_guids, auto_search, rename_namespace("MSO")
#import "libid:00062FFF-0000-0000-C000-000000000046" raw_interfaces_only, raw_native_types, named_guids, auto_search, rename_namespace("Outlook")
#endif //PCH_H
What I did was to wait until OnConnection callback fired and started a timer - you might be able to use a separate thread, but in my case the processing had to be done on the main thread due to some functionality that had thread affinity. OnConnection will give you Outlook.Application object.
In the timer callback (Outlook is not looking), create an instance of the IManagedAddin COM object using CoCreateInstance(CLSID_IManagedAddin, ...). Call IManagedAddin::Load. The path must be in the form file://c:/the/folder/myaddin.vsto.
QI the IManagedAddin object for IDTExtensibility2 interface and call IDTExtensibility2::OnConnection() using the parameters saved from the native OnConnection callback.
If Outlook has already called your C++ implementation of OnStartupComplete, call OnStartupComplete on the VSTO addin. If not, you can do it later.
QI for IRibbonExtensibility and call IRibbonExtensibility::GetCustomUI. Or you can hardcode the ribbon XML in the C++ addin. Note that if Outlook calls IRibbonExtensibility::GetCustomUI on your C++ addin, and you have to delegate the call to the VSTO addin before you had a chance to run your timer code, you have no choice but to call the above code immediately rather than in a timer callback since GetCustomUI cannot be postponed.
If you are using task panes (i.e. ICustomTaskPaneConsumer interface is implemented by your VSTO addin), your C++ addin must also implement it (besides the IDTExtensibility2 and ICustomTaskPaneConsumer interfaces). QI IManagedAddin for IServiceProvider interface and use it to call IServiceProvider::QueryService(GUID_NULL, IID_ICustomTaskPaneConsumer, ...). then call ICustomTaskPaneConsumer.CTPFactoryAvailable - note that ICustomTaskPaneConsumer does not come from your VSTO addin's IDTExtensibility2 interface but rather though the IServiceProvider object off the IManagedAddin interface.

Implement COM interface registered in system

How can i implement and extend COM interface registered in system. Let's asume it is delivered with some software, not part of OS.
Should i write IDL first ?
I know Microsoft's ATL samples, but none of them shows what i'm looking for.
For example here is idl for Foxit Reader Control, generated with OleView
[odl,
uuid(62F8A765-322B-41E3-B767-2D057829D66D),
helpstring("IFoxitCtl Interface"),
dual,
oleautomation
]
interface IFoxitCtl : IDispatch {
[id(0x00000001), propget, helpstring("property src")]
HRESULT src([out, retval] BSTR* pVal);
[id(0x00000001), propput, helpstring("property src")]
HRESULT src([in] BSTR pVal);
[id(0x00000002), helpstring("method OpenFile")]
HRESULT OpenFile(BSTR strFilePath);
[id(0x00000003), helpstring("method PrintFile")]
HRESULT PrintFile();
[id(0x00000004), helpstring("method SetProgress")]
HRESULT SetProgress(
int nOffset,
int nSum);
[id(0x00000005), helpstring("method postMesage")]
HRESULT postMessage([in] VARIANT strArray);
[id(0x00000006), propget, helpstring("property messageHandler")]
HRESULT messageHandler([out, retval] VARIANT* pVal);
[id(0x00000006), propput, helpstring("property messageHandler")]
HRESULT messageHandler([in] VARIANT pVal);
};
What should i do to implement this interface?
Notice: i took random interface from registered in OS, if you have working example with any other COM interface please share it
I've tried to use "Implement Interface" feature in visual studio but many interfaces are not available there

Issue with initializing COM object

I compiled the following VB code and got the dll ("TestVB.dll") and tlb ("TestVB.tlb") files as output.
Imports System
Imports System.Runtime.InteropServices
Namespace TesterNS
<ComVisible(True),
Guid("4B673F5A-A953-4C20-9A90-8F94ED2F6DDF"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)>
Public Interface _Tester
Function GetMonth() As Integer
End Interface
<ComVisible(True),
Guid("FEC833EE-37E9-4406-9344-8A8BD5C43B07"),
ClassInterface(ClassInterfaceType.None),
ProgId("Tester.Numbers1")> Public Class Tester
Implements _Tester
Public Tester()
Public Function GetMonth() As Integer Implements _Tester.GetMonth
GetMonth = DateTime.Now.Month
End Function
End Class
End Namespace
I selected the
Register for COM interop
option in the project settings for the VB project.
I am trying to access the above functionality from a C++ code.
I included the following import statement in the stdafx.h file in C++.
#import "TestVB.tlb" no_function_mapping, no_namespace, named_guids
and trying to access the COM object as follows:
HRESULT hr = CoInitialize(NULL);
_TesterPtr t1 = NULL;
CLSID cls;
std::string s = "";
bool flag = false;
hr = CLSIDFromProgID(L"Tester.Numbers1", &cls);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(cls, NULL, CLSCTX_INPROC_SERVER, __uuidof(_Tester), (void**)&t1);
if (SUCCEEDED(hr))
s = "success";
else
s = "fail";
}
I am seeing hr = REGDB_E_CLASSNOTREG Class not registered error after making the CoCreateInstance call.
I am not sure what I am doing wrong.
In my C++ application, there is a call to AfxOleInit() before CoInitialize(NULL). If I remove the call to CoInitialize(NULL), I was able to get it working.

COM dispinterface events

I am a COM beginner and trying to use a managed library in my c++ project through COM.. I could able to create instance for the dual interfaces and call methods... it is working fine...
I would like to know how to construct client interface which can receive events from any dispinterface COM object ?
#include "stdafx.h"
#import "../COMLib/bin/Debug/COMLib.tlb" named_guids
#include <comutil.h>
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
COMLib::ICalcPtr pCalc;
HRESULT hRes = pCalc.CreateInstance(__uuidof(COMLib::Calc));
if(FAILED(hRes))
printf("ICalcPtr::CreateInstance failed w/err 0x%08lx\n", hRes);
else
{
printf("%d\n", pCalc->Factorial(10));
}
CoUninitialize();
return 0;
}
I know how to create instance for dual interface like above. But I need help to instantiate dispinterface for events. Your help is much appreciated.
Once you have you Com instance just go ahead and queryinterface for any further interfaces you need to use. Assuming it implements IDispatch you'll have a pointer to that interface.
COMLib::ICalcPtr pCalc;
HRESULT hRes = pCalc.CreateInstance(__uuidof(COMLib::Calc));
if(FAILED(hRes))
printf("ICalcPtr::CreateInstance failed w/err 0x%08lx\n", hRes);
else
{
IDispatchPtr pDisp;
hRes = pCalc->QueryInterface(IID_IDispatch, &pDisp);
ASSERT(SUCCEEDED(hRes));
printf("%d\n", pCalc->Factorial(10));
}
If you are looking to sink COM events from this object you probably really want the IConnectionPoint set of interfaces. This article has good explanation and examples of how to attach to and sink connection point events from COM objects in C++.

#import COM DLL generate wrong method signature

I'm writing a C++ application which imports a COM DLL as following,
#import "MyLib.dll" no_namespace, raw_interfaces_only
There is a problem using method '_GetObject' which is declared in the idl file like this,
[
object,
uuid(f022c0e0-1234-5678-abcd-c17d63954f4b),
dual,
nonextensible,
helpstring("IStorageProxy Interface"),
pointer_default(unique)
]
interface IStorageProxy : IDispatch
{
[hidden, helpstring("method _GetObject")]
HRESULT _GetObject(
[in] BSTR entryId,
[in] REFCLSID rclsid,
[in] REFIID riid,
[out, iid_is(riid), retval] IUnknown** stgObject);
};
But the generated tlh file has changed the types of the second and third parameters.
struct __declspec(uuid("f022c0e0-1234-5678-abcd-c17d63954f4b"))
IStorageProxy : IDispatch
{
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall _GetObject (
/*[in]*/ BSTR entryId,
/*[in]*/ GUID * rclsid,
/*[in]*/ GUID * riid,
/*[out,retval]*/ IUnknown * * stgObject ) = 0;
};
As I'm coding against the original function signature (defined in the idl), so now the C++ code can't compile. I'm not sure why the types changed to 'GUID *'. Is there any way to stop the compiler from doing this?
No, that's normal. Both REFGUID and REFIID are just a typedef for GUID*. Same kind of idea as a HWND and HDC, typedefs for HANDLE. These typedefs catch mistakes in C++ code, they are not really appropriate in a type library that supplies typeinfo to many languages.
You could technically keep these typedefs but those types will then have to be defined in the type library as well so that the COM client knows what they mean. The component author would have to include WTypes.idl. That's probably too late by now, you can't do anything about it in a COM client.