Automating an Application using COM - com

I am building an automation interface for an existing application. After implementing a DLL server and an EXE server (mainly for getting familiar with the basics of COM) I am now at the point where I generate a type library from an IDL file and can, for example, basically automate my application from VBScript:
Set oApp = CreateObject("MyApp.1")
oApp.ShowAboutBox()
This simple call to a function that takes no parameters works. The next step I want to take is call a function that takes a parameter.
The signature of the function in the IDL file is
HRESULT CreateSomeChildWindow([out, retval] MyChildWindow** ppChildWindow);
and in VBScript I assume it would be
Dim oWnd As MyChildWindow
oWnd = oApp.CreateSomeChildWindow()
This call already works in C++ although MyChildWindow is not currently registered as a COM object in the registry. The reason MyChildWindow doesn't need to be registered is that CreateSomeChildWindow simply returns the interface pointer to the created MyChildWindow object in a parameter. And the reason it isn't registered is that I want to avoid redundancy, and also I don't want MyChildWindow to be instantiated directly (e.g. by calling CreateObject in VBScript).
Question:
Now I'm trying to find out whether it will be necessary to register MyChildWindow after all. Is my assumption correct that in order to call CreateSomeChildWindow in VBScript
I need to write Dim oWnd As MyChildWindow
For this to work, MyChildWindow must be registered
And if the answer to that is yes, hopefully clients still can't MyChildWindow directly, since I don't implement a class object for it? Or will I have to implement a class object?

Your out/retval is not an object (on the scripting side), it is an interface pointer. And since the method CreateSomeChildWindow is on IDL, in type library, in registered type library - scripting/automation is aware of interface definition, such as methods etc, because the whole type library is already registered. You are already well set, no additional registration required.
When caller receives an interface pointer, it does not care what object the pointer belongs to. Interface pointer alone is good enough, and scripting/automation environment known how to deal with it.
On the callee side however, you need to return an interface pointer, and you are dealing with objects. So you need some class which implements this interface and you return this object's interface.

Related

How to make registration-free interface call from a DLL to a COM object implemented in EXE of the same process?

It is now well known how to make interface call to a DLL-implemented COM object in the registration-free environment. The question is the opposite:
How to make interface call from a DLL to a COM object implemented in EXE? Which manifests and which manifest sections should be used?
EDIT: May be it was not quite clear, but I meant in-process call. I want to call an object in the same process, but implemented in EXE rather than in DLL.
EDIT2: OK, I see things goes complicated, so I must explain the original problem. A C++ application implements IApplication interface and keeps pointer to it as a member. This member is returned by a public method accessible from anywhere by means of AfxGetApp().
There is also a mechanism allowing any customer to add his own C# "plugin" DLLs to our application. For such DLLs the IApplication interface is a key access point to the application's features. But these DLLs have no access to the main application's object.
To solve the problem, I added an auxiliary "ICreateApplication" object, which returns the original IApplication pointer. Any "plugin" DLL may create this auxiliary object and obtain from it the required interface. I implemented this object in some arbitrarily chosen DLL, and equipped it with necessary manifests.
This solution works well, but this "arbitrary choice" bothers me. I wonder, is there a possibility to implement "ICreateApplication" in the application object?

COM-class without being able to instantiate

MSDN:
"You implement an IClassFactory interface for each (COM) class of object that you offer to be instantiated."
Is it useful to create COM class without Class Factory?
The IClassFactory is provided for objects which are instantiated directly by the caller, for example using CreateObject or CoCreateInstance, or GetObject.
Other objects can be obtained by getting them indirectly from objects that are created.
For example, the Scripting.FileSystemObject in VB or VBScript is created directly. You can use the methods to obtain File or Folder objects, which cannot be directly created. Instead these are created by the FileSystemObject and returned from FileSystemObject methods.
Is it useful to create COM class without Class Factory?
Yes, why not? It can still be fully featured COM class, just except that you cannot launch it via CoCreateInstance API. For some reason you might want to make it available otherwise, e.g. as a returned [out] parameter on a method of another interface/class, or via GetActiveObject API.
Class factories let you expose your class for direct instantiation.
I'm not entirely sure the wording is quite correct here. You almost always implement IClassFactory once for all COM classes of objects that your host will instantiate. In particular, your implementation of IClassFactory is what gets returned from DllGetClassObject.
In particular, when someone calls CoCreateInstance(CLSID_foo, pUnkOuter, CLSCTX_INPROC, IID_IFoo, (void **) &pFoo), the following things happen (assuming you're not doing remoting):
COM looks up CLSID_foo in the registry. (In particular, HKEY_CLASSES_ROOT\CLSID\\InprocServer32).
COM loads that DLL and calls DllGetClassObject with IID_IClassFactory.
Your DLL returns a function pointer to the implementation of IClassFactory.
COM calls your implementation of IClassFactory::CreateInstance with pUnkOuter, IID_IFoo, and pFoo.
There are many other situations where you would have COM objects that aren't directly creatable, as #Ben mentioned. There are even other standard factory interfaces, like IServiceProvider, that exist so that classes can expose a dynamic set of interfaces.

VB connect to COM object

Either I don't understand COM objects, either it's all that confusing.
I frequently 'create' , 'dispatch' COM objects - either in Python, either in VB ( Obj = win32com.client.Dispatch('Visum.Visum') or Obj = CreateObject("Excel.Application") respectively).
That's easy, obvious and everything's fine.
But how can I:
a) connect to COM object which is already running
b) get list of running processes which are COM objects
in VB what is the reference, and additional info on: CreateObject command. In Python it's part of well defined, comprehensive library of win32com whereas in VB it's just single method without reference: http://msdn.microsoft.com/en-us/library/7t9k08y5(v=vs.80).aspx
Highly confusing (COM object,server? connection, dispatching, registering??)
Thanks for claryfying
Rafal
i2
First of all, I would suggest reading a good introduction to COM. Personally, i think that 'Essential COM' by Don Box is the best book about COM. Spending a few hours with it will save you many hours later.
That being said, let's move to your questions.
a) In order to retrieve an existing COM object from VB, you need to call GetObject function. This will only work for COM objects which are registered with Running Object Table. Excel does this, so there shouldn't be any problems with that.
b) as Hans Passant correctly noted, this question doesn't make sense. Processes and COM objects are completely unrelated things, except for the fact that processes host the COM objects. It is similar to asking 'which processes are instances of class X'. However, a process can host a COM object (or many of them). These object can be alive only temporarily, so what would you expect there? Besides, many COM classes are implemented within dlls, but created instances obviously are hosted within a process - so what would you expect there?
COM technology is something I didn't not use for a while, so sorry if some info might be wrong (anyway, I've got my book "Inside COM+ base services" with me!)
Briefly speaking, let's give a simplified definition of a COM object: it nothing else than a standard DLL with classes exposing a standard interface IUnkown with the following methods:
AddRef: to register a client consuming the COM object
Release: to unregister a client (usually unused objects stop running, but other might remain resident)
QueryInterface: to get the address of a function
QueryInterface is used to dynamically retrieve the address of a function (late-binding) which is powerful (you don't have to know the DLL at compile time) but time consuming. On the other hand, you can directly reference the addresses of the functions at compile-time because those DLL are registered in the system (all the informations are stored in the registry)
Creating a new object or getting the address of a running object is done through RPC (remote procedure call) which will load and/or play the role of a proxy
So to create a new instance of a COM object, you will call CreateObject (in VB) (Co)CreateInstance in VC++ (or even c#?), while to get an executing instance, you might want to call GetObject().
Getting the list of COM objects is not that easy (I guess not possible at all) because, as told earlier, a COM object is nothing else but a DLL: this means that the library will mainly load in the address space of each process which access is private. Even for out-process COM objects (opposite to in-process) which are object shared between many processes (i.e.: Excel is an out-of-process COM object: you don't load the DLL in your application's address space), they are loaded by a host (rpc.exe or a proprietary host)
Hope this helps
Serge

.NET class as callback object in COM DLL

I am a relative newcomer to Windows programming and VB.net. What we are trying to do is call a function in an existing, 3rd party COM dll. This function requires a callback parameter, whose type is an interface also defined in the dll.
In our VB.net application, we have added the dll as a COM reference, and created a class that implements the interface in the DLL. We then send an object of that type as the callback parameter. This compiles fine. However, when we run the application, we receive an error stating that the program is unable to cast the object from our existing type to the interface type.
I suspect that there is a simple fix, but so far, my efforts to find a solution have come up short. If anyone could point me in the right direction, I'd be very grateful.
Thanks very much.
I suggest you to check whether the COM interface which you are implementing has a registerd proxy/stub or a correspondent typelibrary (tlb). You may check this by searching the interface's IID (this is a guid assossiated with this interface) in the registry:
Proxy/stub case:
HKEY_CLASSES_ROOT
Interface
{iid}
(Default) = ICustomInterfaceName
ProxyStubClsid32 = {clsid}
Typelibrary case:
HKEY_CLASSES_ROOT\TypeLib\{F37C8060-4AD5-101B-B826-00DD01103DE1}
HKEY_CLASSES_ROOT\TypeLib\{F37C8060-4AD5-101B-B826-00DD01103DE1}\2.0 = Automation Hello 2.0 Type Library.
HKEY_CLASSES_ROOT\TypeLib\{F37C8060-4AD5-101B-B826-00DD01103DE1}\2.0\HELPDIR =
; U.S. English.
HKEY_CLASSES_ROOT\TypeLib\{F37C8060-4AD5-101B-B826-00DD01103DE1}\2.0\9\win32 = hello.tlb
Replace {F37C8060-4AD5-101B-B826-00DD01103DE1} with a guid assossiated with your typelibrary.
In case if the typelibrary is registered. Check if its file is present on your PC

Create each COM-instance in it's own exe-container

Is there possible to create a COM-instance in it's own, dedicated, host-process?
I guess some background is needed.
We have an end-user client which has it's central logical components inside an singleton-COM object. (Not propper singleton, but it uses global variables internally, so it would fail.) So that there should be only one instance per exe-file. Convenient while making the client.
However, I should now make a "client-simulator" to test the server-side. I therefore which to make 20 instances of the client-component.
If I could make each instance instanciate in its own exe-host, then the singleton-issue would be handled.
Regards
Leif
I have been struggling with this problem for a few days. I finally found a solution that works. My COM object is written using ATL, so my code snippet will be geared toward that, but the technical solution should be clear. It all hinges on how the class objects are registered. The REGCLS_SINGLEUSE flag is the key. I now have separate processes for each object instance.
In the ATL module, override the RegisterClassObjects() function as follows:
HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags) throw()
{
return base::RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_SUSPENDED | REGCLS_SINGLEUSE);
}
From MSDN regarding REGCLS_SINGLEUSE:
REGCLS_SINGLEUSE
After an application is connected to a class object with
CoGetClassObject, the class object is removed from public view so that
no other applications can connect to it. This value is commonly used
for single document interface (SDI) applications. Specifying this
value does not affect the responsibility of the object application to
call CoRevokeClassObject; it must always call CoRevokeClassObject when
it is finished with an object class.
My theory is that because the registration was removed from public view, it causes a new process to be created for the subsequent instantiations.
This other question mentioned a description of how to use DLLHost as a surrogate process:
http://support.microsoft.com/kb/198891
I've never tried this myself, and I don't know off-hand if you can specify flags for the factories (which control if surrogates can be reused for multiple objects), but maybe you can tweak that via DCOMCNFG or OLEVIEW.
My COM days are long gone, but as far as I remember, there's no built-in way to do that.
It might be easier to rewrite your code so it supports multiple instances than to go the one-process-per-instance route with COM, but here's what you could do:
Use thread-local storage for your global variables and write another CoClass, where each instance owns its own thread through which accesses to the class with the global variables are marshaled. This would at least allow you to avoid the performance impact of DCOM.
Write your own out-of-process exe server (similar to windows' DllHost.exe) to host your COM instances. This requires IPC (Inter-Process Communication), so you either have to code something yourself that marshals calls to the external process or use DCOM (presuming your COM object implements IDispatch)