I am attempting to understand creating/using COM components without the help of MFC/ATL to know its inner workings.
I am using this codeguru article for reference.Following are the steps followed by me.
Created a Wind32 Dll,
Added a MIDL file and declared the interface IAdd and library name DemoMath; compiled the code using MIDL compiler.
Created CAddObj class deriving IAdd interface,provided implementation for IAdd and IUnknown interfaces.
Created class CAddFactory deriving from IClassFactory interface;provided implementation for IClassFactory methods.
Now creating DllGetClassObject to give client an option to invoke this function to get an instance of the class factory.
Following is the code:
#include "stdafx.h"
#include <objbase.h>
#include "AddObjFactory.h"
#include "IAdd_i.c"
STDAPI DllGetClassObject(const CLSID& clsid,
const IID& iid,
void** ppv)
{
//
//Check if the requested COM object is implemented in this DLL
//There can be more than 1 COM object implemented in a DLL
//
if (clsid == CLSID_AddObject)
{
//
//iid specifies the requested interface for the factory object
//The client can request for IUnknown, IClassFactory,
//IClassFactory2
//
CAddFactory *pAddFact = new CAddFactory;
if (pAddFact == NULL)
return E_OUTOFMEMORY;
else
{
return pAddFact->QueryInterface(iid , ppv);
}
}
//
//if control reaches here then that implies that the object
//specified by the user is not implemented in this DLL
//
return CLASS_E_CLASSNOTAVAILABLE;
}
Now where is CLSID_AddObject constant suppose to be defined
or Is it generated while compiling MIDL file(I didn't find it though)?
coclass IDL item will typically get you CLSID:
library Foo
{
//...
[
//...
]
coclass AddObject
{
//...
};
then on your "IAdd_i.c" you are already including:
MIDL_DEFINE_GUID(CLSID, CLSID_AddObject, ...);
this is what defines CLSID_AddObject.
Related
in short:
Is it possible to create a C++-based COM component that I can reference and use from VBA, without needing to register the DLL (in windows registry)?
In long:
I'm a COM novice, so maybe what I'm tryong to do here structurally doesn't work or doesn't make sense, but:
I want to create a C++ COM library that is usable from VBA registration-free.
So I've created an C++ ATL project in VS, added a custom COM type (MyClass) and added a test method to it ("AddNumbers", taking 2 long parameters and returning 1).
When building this project, I'm getting an error "Failed to register output. Please try anebling Per-User Redirection or register the component from a comment prompt with elevated permissions", likely because I'm running Visual Studio without admin permissions. However, the DLL builds fine, I assume it's just not registered after building it.
Right, so when I reference this DLL in my VBA project (Tools -> Referewnces -> Browse... and add), intellisesne works, i.e. VBA knows the type "MyClass", knows it has a method "AddNumbers" and so on.
But, it can't instantiate it:
Sub test()
Dim q As MyClass
Set q = New MyClass ' This will fail: "Run-time error '429': ActiveX component can't create object"
End Sub
Why is that? Am I right in assuming it likely fails because this COM component isn't properly registered on the machine?
If so, can I somehow circumvent this? I've read some infos about registration-free .NET based COM components, but I have no idea if this also works - or is even needed?! - for C++ based components.
MyClass.h
// MyClass.h : Declaration of the CMyClass
#pragma once
#include "resource.h" // main symbols
#include "ATLProject4_i.h"
#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;
// CMyClass
class ATL_NO_VTABLE CMyClass :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CMyClass, &CLSID_MyClass>,
public IDispatchImpl<IMyClass, &IID_IMyClass, &LIBID_ATLProject4Lib, /*wMajor =*/ 0xFFFF, /*wMinor =*/ 0xFFFF>
{
public:
CMyClass()
{
}
DECLARE_REGISTRY_RESOURCEID(106)
BEGIN_COM_MAP(CMyClass)
COM_INTERFACE_ENTRY(IMyClass)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
STDMETHOD(AddNumbers)(LONG Num1, LONG Num2, LONG* ReturnVal);
};
OBJECT_ENTRY_AUTO(__uuidof(MyClass), CMyClass)
MyClass.cpp
// MyClass.cpp : Implementation of CMyClass
#include "stdafx.h"
#include "MyClass.h"
// CMyClass
STDMETHODIMP CMyClass::AddNumbers(LONG Num1, LONG Num2, LONG* ReturnVal)
{
// TODO: Add your implementation code here
*ReturnVal = Num1 + Num2;
return S_OK;
}
After porting a project from visual studio to mingw. I am getting the following linker error
undefined reference to `g_Templates'
undefined reference to `g_cTemplates'
The code which it points to looks something like this
#include <tchar.h>
#endif // DEBUG
#include <strsafe.h>
#include <combase.h>
extern CFactoryTemplate g_Templates[];
extern int g_cTemplates;
HINSTANCE g_hInst;
DWORD g_amPlatform; // VER_PLATFORM_WIN32_WINDOWS etc... (from GetVersionEx)
OSVERSIONINFO g_osInfo;
//
// an instance of this is created by the DLLGetClassObject entrypoint
// it uses the CFactoryTemplate object it is given to support the
// IClassFactory interface
class CClassFactory : public IClassFactory, public CBaseObject
{
private:
const CFactoryTemplate *const m_pTemplate;
...
public:
CClassFactory(const CFactoryTemplate *);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, __deref_out void ** ppv);
STDMETHODIMP_(ULONG)AddRef();
STDMETHODIMP_(ULONG)Release();
// IClassFactory
STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, __deref_out void **pv);
STDMETHODIMP LockServer(BOOL fLock);
// allow DLLGetClassObject to know about global server lock status
static BOOL IsLocked() {
return (m_cLocked > 0);
};
};
// --- COM entrypoints -----------------------------------------
//
// Call any initialization routines
//
void DllInitClasses(BOOL bLoading)
{
// traverse the array of templates calling the init routine
// if they have one
for (i = 0; i < g_cTemplates; i++) //<---------Cannot recognize this symbol
{
const CFactoryTemplate * pT = &g_Templates[i];
if (pT->m_lpfnInit != NULL)
{
(*pT->m_lpfnInit)(bLoading, pT->m_ClsID);
}
}
}
....
....
I have been searching on this issue for a while and have not made any progress. It seems that that this symbol exists in strmbasd.lib (debug version) and is generated from DirectShow base classes. I generated strmbasd.lib using mingw64 however I am still getting this linker error. I wanted to know if there was any other approach I could try .
I have used Direct Show for Microsoft Visual C++. And found no such issue. Microsoft SDK provides the libraries and headers as well as the base classes. You may want to check the link. I haven't used MingW, so I don't know about the issue of MingW. You may try it in MSVC, MSDN provides some handful informations and references for Direct Show. Please check the previous link mentioned above.
Your including dllentry.cpp/dllsetup.cpp from DirectShow BaseClasses assumes that you develop a filter library and you are expected to define template symbols in your code (example) to satisfy linker.
If you don't see how your code is referencing factories, you can define fake array and g_cTemplates of zero to pass through, however eventually there is something that makes linker drag these symbols into output.
I have a .dll that contains some directshow filters (COM) with specific/custom interfaces to query.
Most 3rd party directshow components contain embedded .tlb files that can be used for cross-enviroment communication (C# typelib import).
I would hate to have to attempt to manually create the interfaces needed for c# because no idl/tlb files were provided.
Is it possible to generate a tlb (or at least, an idl, which I can MIDL compile) from a COM .dll?
Yes, it is possible to reverse engineer/disassemble IDL (or something very close to it). What you need to do is give yourself a new C++ Console Project which gives the default code of
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
and then you insert an #import statement underneath the #include statement. So I have been playing with C# assembly marked up to function as a COM Interop DLL and I have called it ComExample2 and it lives in the same solution as the C++ console project that I added which means I can use a nice relative pathname. So my #import statement looks like
#import "..\ComExample2\bin\Debug\ComExample2.tlb" no_namespace named_guids
Then you build your console application. If you delve into the files generated during the build you will find a file that ends with .TLH which stands for type library header. So my path is
..\ComExample2\ConsoleApplication1\Debug\comexample2.tlh
Inside my file is something which looks very much like idl. Here is an edited snippet to give you a flavour....
struct __declspec(uuid("515b1b18-1602-4d42-b743-f1b3c458a0d0"))
/* LIBID */ __ComExample2;
struct /* coclass */ ComExampleClass2;
//
// Type library items
//
struct __declspec(uuid("713007fe-e74c-4fec-b91a-5ef8df279929"))
IFoo : IDispatch
{
//
// Wrapper methods for error-handling
//
_bstr_t Greeting ( );
long Sim (
long a,
long b );
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall raw_Greeting (
/*[out,retval]*/ BSTR * pRetVal ) = 0;
virtual HRESULT __stdcall raw_Sim (
/*[in]*/ long a,
/*[in]*/ long b,
/*[out,retval]*/ long * pRetVal ) = 0;
};
struct __declspec(uuid("efe233b5-8ab3-4414-855e-1f027e0a72d5"))
ComExampleClass2;
// interface _Object
// [ default ] interface IFoo
All of this is generated code so that you can script C++ code against a COM library easily. You'll have to pick through what you need but hopefully that should be enough.
Kind regards,
Lord BattenBerg
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
Hi I have a pure C++ project (DLL) which I would like to convert to COM project, e.g. all public interfaces defined in headers, will now be exposed as COM interfaces (IDL etc...). And the final product should be COM Dll.
How do I start? How do I define Any high level guidelines? good articles?
There are at least two parts to this problem:
First, COM objects are created using CoCreateInstance. CoCreateInstance looks for COM registrations in memory (via CoRegisterClassObject), in the applications manifest as a zero reg COM object, and finally, in the registry.
For zero reg, create an assembly manifest describing your dll so that consumers of your object can add a dependantAssembly reference to their application manifest.
Then, a COM dll needs at least two entry points: DllGetClassObject and DllCanUnloadNow
You implement DllGetClassObject by creating an instance of a factory object - that supports IClassFactory - that can be used to make instances of your actual object.
So, to summarize - a kind of TDD driven approach to implementing a COM dll:
Create a DLL with 'DllGetClassObject' and 'DllCanUnloadNow' entry points.
Create a new GUID to represent your object, and create a assembly manifest describing the COM object your dll (will) contain.
Create a test application, that calls CoCreateInstance with that GUID.
Calling CoCreateInstance should now land up in your DllGetClassObject call. Implement the class factory object.
Implement the CreateInstance method to create a new instance of your c++ class. Ensure that all your interfaces derive from (at least) IUnknown. Call QueryInterface on your own newly created object to get and return the desired interface.
Assuming Visual Studio (Express is ok) is your build environment:
Create a test exe:
// main.cpp
#include <windows.h>
#include <objbase.h>
#include <initguid.h>
DEFINE_GUID(CLSID_RegFreeOcx,0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
#if defined _MSC_VER
#if !defined _WINDLL && !defined (_CONSOLE)
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
#endif
#pragma comment(linker, "/manifestDependency:\"name='acme.RegFreeOcx' processorArchitecture='*' version='1.0.0.0' type='win32' \"")
int main(){
CoInitialize(NULL);
IUnknown* pUnk;
CoCreateInstance(CLSID_RegFreeOcx,NULL,CLSCTX_ALL,IID_IUnknown,(void**)&pUnk);
if(pUnk)
pUnk->Release();
}
Create the manifest for reg free activation of the COM dll:
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="Acme.RegFreeOcx" processorArchitecture="x86" version="1.0.0.0" type="win32" />
<file name = "RegFreeOcx.dll">
<comClass clsid="{00000000-0000-0000-0000-000000000000}" threadingModel="Apartment" />
</file>
</assembly>
Create a dll project
// dllmain.cpp
#include <windows.h>
#pragma comment(linker,"/export:DllGetClassObject=_DllGetClassObject#12,PRIVATE")
#pragma comment(linker,"/export:DllCanUnloadNow=_DllCanUnloadNow#0,PRIVATE")
#include <objbase.h>
#include <initguid.h>
DEFINE_GUID(CLSID_RegFreeOcx,0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
#include "MyClassFactory.hpp"
STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid,void** ppvObj)
{
if(CLSID_RegFreeOcx == clsid){
MyClassFactory* pFactory = new MyClassFactory();
HRESULT result = pFactory->QueryInterface(riid,ppvObj);
pFactory->Release();
return result;
}
return E_FAIL;
}
STDAPI DllCanUnloadNow()
{
return E_FAIL;
}
//MyClassFactory.hpp
#include <MyClass.hpp>
class MyClassFactory : public IClassFactory {
volatile ULONG _cRef;
public:
MyClassFactory():_cRef(1){}
virtual ~MyClassFactory(){}
public: // IUnknown
STDMETHODIMP_(ULONG)AddRef(){
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG)Release(){
ULONG result = InterlockedDecrement(&_cRef);
if(!result) delete this;
return result;
}
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObj){
if(riid == IID_IUnknown || riid == IID_IClassFactory)
*ppvObj = (IClassFactory*)this;
else {
*ppvObj=0;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
public: // IClassFactory
STDMETHODIMP CreateInstance(IUnknown* pUnkOuter,REFIID riid,void** ppvObj){
if(pUnkOuter)
return E_INVALIDARG;
MyClass* pClass = new MyClass();
HRESULT result = pClass->QueryInterface(riid,ppvObj);
pClass->Release();
return result;
}
STDMETHODIMP LockServer(BOOL fLock){
return E_NOTIMPL;
}
};
And finally, define your own class. You could use IDL to do this but, for other cpp consumers, you can just define it in a header file.
// IMyClass.h
#include <objbase.h>
DEFINE_GUID(IID_MyInterface,0x00000000,0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00);
interface IMyInterface : IUnknown {
STDMETHOD(MyMethod)(void)PURE;
};
Implement the class that implements IMyInterface in a cpp file in much the same way that the ClassFactory was implemented (in terms of the IUnknown methods) - swapping out references to IClassFactory with your own interface.
THe canonical gentle introduction is Inside COM by Dale Rogenson. For real depth, try Essential COM by Don Box
The three big areas to worry about are:
Threading
Memory management and memory ownership conventions]
Types passed across COM interface boundaries - particularly not using STL
THe Box book also covers some of the really nasty things people do with COM - like the free threaded marshaller - which you might be applicable if your library is already thread safe and you want to avoid marshalling penalties.
I followed this tuorial to create a COM dll in Visual Basic. http://www.codeproject.com/KB/COM/Basics_of_Idl_file.aspx
I now want to use this dll in a C++ project. I used OLE/COM Viewer to create an .idl file as is described in the second half this tutorial.
http://www.codeproject.com/KB/COM/vb_from_vc.aspx
I compiled the .idl with the midl compiler and included the .h file that was created in my c++ project.
Here is my Visual Basic Code
<ComClass(MyComClass.ClassId, MyComClass.InterfaceId, MyComClass.EventsId)> _
Public Class MyComClass
#Region "COM GUIDs"
' These GUIDs provide the COM identity for this class
' and its COM interfaces. If you change them, existing
' clients will no longer be able to access the class.
Public Const ClassId As String = "46604f8a-85a2-4027-9728-0390534c9209"
Public Const InterfaceId As String = "30274029-711d-459a-9270-f9d73ad8737f"
Public Const EventsId As String = "5e234d69-5263-4001-86ff-c475b113a77d"
#End Region
' A creatable COM class must have a Public Sub New()
' with no parameters, otherwise, the class will not be
' registered in the COM registry and cannot be created
' via CreateObject.
Public Sub New()
MyBase.New()
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
Public Sub DisplayMessage()
MsgBox("Hello from MyComClass!")
End Sub
End Class
Here is my c++ Code
// Declare an HRESULT and a pointer to the clsVBTestClass interface
HRESULT hr;
_MyComClass *IVBTestClass = NULL;
// Now we will intilize COM
hr = CoInitialize(0);
// Use the SUCCEEDED macro and see if we can get a pointer
// to the interface
if(SUCCEEDED(hr))
{
hr = CoCreateInstance( CLSID_MyComClass,
NULL,
CLSCTX_INPROC_SERVER,
IID__MyComClass,
(void**) &IVBTestClass);
// If we succeeded then call the CountStringLength method,
// if it failed then display an appropriate message to the user.
if(SUCCEEDED(hr))
{
long ReturnValue;
_bstr_t bstrValue("Hello World");
// We can test this HR as well if we wanted to
hr = IVBTestClass->DisplayMessage();
hr = IVBTestClass->Release();
}
else
{
}
}
// Uninitialize COM
CoUninitialize();
I receive the following errors when I compile my c++ project
error LNK2001: unresolved external symbol _CLSID_MyComClass
error LNK2001: unresolved external symbol IID_MyComClass
Could someone help me figure out what I am doing wrong?
If you did not get to the last part of creating a Type Library, that is important.
You then need an #import statement in your C++ code to use the .tlb file (or .dll if the type library is embedded in the dll, which is common).
#import is the equivalent to including a header file with COM, but generates a .tlh file (header) and a .tli (implementation) automatically.