Registry keys for out-of-process COM server - com

I'm in the process of implementing my first out-of-process COM server (my first COM server altogether, for that matter). I have followed the steps to write an IDL file, generate the code for the proxy/stub DLL, compile the DLL, and register it.
When I check the registry keys, I have
A key named HKEY_CLASSES_ROOT/Interface/<GUID>, whose vaue is (say) IMyApp and
A key named HKEY_CLASSES_ROOT/Interface/<GUID>/ProxyStubClsid32, whose value is <GUID>, i.e. the same value as in the key name
I don't understand how the the second key's value can be the same <GUID> value as in the key name, because my current understanding is that
In HKEY_CLASSES_ROOT/Interface/<GUID>, GUID is an interface ID
The value of ProxyStubClsid32 is not an interface ID, but a class ID referring to the component that implements the above interface
The value of HKEY_CLASSES_ROOT/CLSID/<GUID>/InprocServer32 (where GUID is the above class ID) points to the proxy DLL
How, then, can the value of HKEY_CLASSES_ROOT/Interface/<GUID>/ProxyStubClsid32 hold the same value GUID if one is an interface ID and the other is a class ID?
EDIT: I'm still hoping for an answer to this one. To put it short: Since a component and an interface are two different things, how can the same ID be used for both?

Your basic understanding of the way Guids are used in COM is correct. Notable first is that an interface and a coclass having the same guid is not a problem. They live in different registry keys, HKCR\Interface vs HKCR\CLSID and it is always clear in COM whether you are looking up a IID or a CLSID.
Second is the IDL you wrote. Note that there's no place there to specify the CLSID of the proxy, only the IIDs supported by the proxy and stub can be declared there.
Next, you need a wild goose chase through the way that the proxy/stub is autogenerated. The core Windows SDK header is RpcProxy.h, open it in a text editor to have a look see. The macro soup is very heavy but it does have a few decent comments that describe what's going on. The important RPC helper function is NdrDllRegisterProxy(), it registers the proxy and is called when you use Regsvr32.exe. Its 3rd argument specifies the CLSID of the proxy. I'll let you do the reading and just quote the important bits in the .h file:
Compiler switches:
-DPROXY_CLSID=clsid
Specifies a class ID to be used by the proxy DLL.
This one you specify with Project + Properties, C/C++, Preprocessor, Preprocessor Definitions setting. Note that your project will not specify it.
Chasing through the soup then lands you on this one:
// if the user specified an override for the class id, it is
// PROXY_CLSID at this point
#ifndef PROXY_CLSID
#define GET_DLL_CLSID \
( aProxyFileList[0]->pStubVtblList[0] != 0 ? \
aProxyFileList[0]->pStubVtblList[0]->header.piid : 0)
#else //PROXY_CLSID
#define GET_DLL_CLSID &PROXY_CLSID
#endif //PROXY_CLSID
In other words, if you didn't specify the CLSID yourself (you didn't) then it uses the first IID in the stub table.
And that makes the ProxyStubClsid32 guid the same as the IID of your first interface. Feature, not a bug.

A case of Beginner's Confusion (tm). The class registered by calling regsrv32 is not the one with my CLSID. It is one generated specifically for the proxy/stub DLL (the friendly name PSFactory also indicates this). So as Roman R. suspected, there are two classes where I thought there was only one. My own CLSID is registered by the EXE server when it's called with the /Embedding switch.

For what i know, all the proxy/stub mayhem is managed now by the MIDL (inheriting from IDispatch not IUnknown, which you probably already do because you have the ProxyStubClsid32 reg key).
The only thing needed is to register the server properly (just building it or doing /RegServer did not register it properly for us... and for many others), to do so just a call to LoadTypeLibEx is needed (after building the server or installing it).
So just create a small exe with this code and call it after building and on installation:
String^ l_TLB = l_Path + "\\MyServer.tlb";
IntPtr l_TLBP = System::Runtime::InteropServices::Marshal::StringToBSTR(l_TLB);
ITypeLib *pTypeLib;
HRESULT hr = LoadTypeLibEx(static_cast<LPCOLESTR>(l_TLBP.ToPointer()), REGKIND_REGISTER, &pTypeLib);
if(SUCCEEDED(hr))
pTypeLib->Release();

Related

Implements vs Binary Compatibility

I have one VB6 ActiveX DLL that exposes a class INewReport. I added some new methods to this class and I was able to rebuild it and keep binary compatibility.
I have a second DLL that exposes a class clsNewReport, which implements the first class using:
Implements RSInterfaces.INewReport
Since I added new methods to INewReport, I had to also add those new methods to clsNewReport.
However, when I try to compile the second DLL, I get the binary-compatibility error "...class implemented an interface in the version-compatible component, but not in the current project".
I'm not sure what is happening here. Since I'm only adding to the class, why can't I maintain binary compatibility with the second DLL? Is there any way around this?
I think this is a correct explanation of what is happening, and some potential workarounds.
I made up a test case which reproduced the problem in the description and then dumped the IDL using OLEView from the old & new DLL which contained the interface.
Here is a diff of the old (left) and new IDL from INewReport:
Important differences:
The UUID of interface _INewReport has changed
A typedef called INewReport___v0 has been added which refers to the original UUID of the interface
(I assume that this is also what is happening to the code referred to in the question.)
So now in the client project the bincomp DLL refers to the original interface UUID; but that UUID only matches against a different name (INewReport___v0 instead of INewReport) than it did originally. I think this is the reason VB6 thinks there is a bincomp mismatch.
How to fix this problem? I've not been able to do anything in VB6 that would allow you to use the updated interface DLL with the client code without having to break bincomp of the client code.
A (bad) option could be to just change the client DLL to use project compatibility... but that may or may not be acceptable in your circumstances. It could cause whatever uses the client DLL to break unless all the consumers were also recompiled. (And this could potentially cause a cascade of broken bincomp).
A better but more complex option would be to define the interface in IDL itself, use the MIDL compiler to generate a typelib (TLB file), and reference that directly. Then you would have full control over the interface naming, etc. You could use the IDL generated from OLEView as a starting point for doing this.
This second option assumes that the interface class is really truly an interface only and has no functional code in it.
Here's how I setup a case to reproduce this:
Step 1. Original interface definition - class called INewReport set to binary compatible:
Sub ProcA()
End Sub
Sub ProcB()
End Sub
Step 2. Create a test client DLL which implements INewReport, also set to binary compatible:
Implements INewReport
Sub INewReport_ProcA()
End Sub
Sub INewReport_ProcB()
End Sub
Step 3: Add ProcC to INewReport and recompile (which also registers the newly built DLL):
(above code, plus:)
Sub ProcC()
End Sub
Step 4: Try to run or compile the test client DLL - instantly get the OP's error. No need to change any references or anything at all.
I was able to recreate your problem, using something similar to DaveInCaz's code. I tried a number of things to fix it, probably repeating things you've already tried. I came up with a possible hypothesis as to why this is happening. It doesn't fix the problem, but it may throw some additional light on it.
Quoting from This doc page:
To ensure compatibility, Visual Basic places certain restrictions on changes you make to default interfaces. Visual Basic allows you to add new classes, and to enhance the default interface of any existing class by adding properties and methods. Removing classes, properties, or methods, or changing the arguments of existing properties or methods, will cause Visual Basic to issue incompatibility warnings.
Another quote:
The ActiveX rule you must follow to ensure compatibility with multiple interfaces is simple: once an interface is in use, it can never be changed. The interface ID of a standard interface is fixed by the type library that defines the interface.
So, here's a hypothesis. The first quote mentions the default interface, which suggests that it may not be possible to alter custom interfaces in any way. That's suggested by the second quote as well. You're able to alter the interface class, because you are essentially altering its default interface. However, when you attempt to alter the implementing class in kind, to reflect the changes in your interface, your implementation reference is pointing to the older version of the interface, which no longer exists. Of course, the error message doesn't hint at this at all, because it appears to be based on the idea that you didn't attempt to implement the interface.
I haven't been able to prove this, but looking at DaveInCaz's answer, the fact that the UUID has changed seems to bear this idea out.

how to ildasm/ilasm round trip a dll exposing COM objects

I want to round trip a dll that exposes COM objects (interop).
With ildasm i dumped the dll.
In the ildasm dump i renamed all occurances of the class name.
An Ilasm with DLL switch and inclusion of the resources produced the new dll.
I dont know how to register this new dll (Win7).
Regasm/Regedit complains about a strong name.
What else do i have to adjust?
Thank you.
Seppe
A strong name for an assembly explicitly prevents you from doing this. Strong names were designed to detect somebody tampering with the code of an assembly, useful when the assembly is stored in an insecure location like a website. You are definitely tampering with the DLL so you'll break the strong name. Resigning it is required and that requires access to the private key that was used originally.
That said, there are some mitigating factors in the specific case of a [ComVisible] assembly. There isn't any way for an application to verify the strong name since the client of such an assembly is native code that doesn't know beans about strong names. The strong name is only required to allow the assembly to be registered in the GAC. Which in general is a good place for such assemblies since it helps to avoid DLL Hell.
So there are two things you can do to solve this problem:
Read the regasm.exe message carefully. It probably only displays a warning if you use the /codebase option. "RegAsm : warning RA0000 : Registering an unsigned assembly with /codebase can cause your assembly to interfere with other applications that may be installed on the same computer. The /codebase switch is intended to be used only with signed assemblies. Please give your assembly a strong name and re-register it." Which really means "You are about to commit yourself to DLL Hell". There are various degrees of DLL Hell, the name+location of the DLL is a fairly mild one. The far more important one is that the interfaces and classes get a new [Guid]. Which they'll automatically get since you changed the names, assuming that there is not a [Guid] attribute present in the original code.
Just sign the assembly with your own key, using sn.exe. Since nobody can actually check the strong name, any is good enough.
And do keep your eye on the ball, the actual name of the [ComVisible] interface and class names is immaterial in COM. Only the [Guid] matters, that's what the COM client uses to find the types back.

.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

How to add and implement a new interface to an Inproc COM server

I've implemented a windows deskband (using the windows SDK sample) and need a way to communicate (one call to start IPC with another application, IPC is already working) with it.
My COM experience is very limited but extrapolating from what I've seen, I think it should be possible to create a new COM interface, implement it in the deskband object (which I have access to via IBandSite), call QueryInterface() for my own interface on it and then use it to call directly into the deskband.
I've tried this but ran into problems very quickly (main reason being: I've no idea what I'm actually doing most of the time ...)
So, my questions are: Is this a viable approach and can someone give me an outline on how to proceed if it is (or point to some resource that could be helpful - short of reading a COM book, which would be my last approach). If it is not, do alternatives come to mind ?
Thank you for your time and best wishes,
Rene.
Here's you path: you add a new interface into .idl file and also if you have a co-class in the .idl file that corresponds to you COM object you list that new interface in the co-class definition. Then you compile the .idl and this gets you a .h file and a .c file with identifiers - the C++ IID and C++ interface definition.
Then you inherit your COM object C++ class from the C++ interface and implement all methods of it. If for whatever reason you can't or don't want to implement a method you have to return E_NOTIMPL from that method implementation.
One very important final thing: you have to change QueryInterface() behavior in you COM object class. If you use ATL you have to add an entry into the COM map. If you don't use ATL change you QueryInterface() - see this question for how to implement QueryInterface() in case of implementing several COM interfaces.

Discovering registered COM components

Is there a way to determine if a registered COM component is creatable as a stand-alone component simply by parsing the information available in the registry? In particular, by the information found in HKCR/ClsId?
My system has over 12,000 entries in this key, and I am already excluding any items that do not have an InProcServer32 or LocalServer32 key, but this only eliminates about half of the items. I believe there are still another couple thousand that are not creatable objects. I really don't want to have to attempt to do a CreateObject() on every one of them to distinguish the ones that can be created from the ones that cannot. Is there a more efficient way?
Oleview
I used Oleview
for this purpose (back in the day :))
Manual/programmatic
If I remember correctly (no Windows PC nearby):
the class should link to a typelibrary
the typelib will point to a binary (dll, ocx, exe)
this binary contains the physical typelibrary, which you should parse
the midl compiler can do that (generate stubs/C headers)
oleview can do that (extract IDL)
tlbimp can do that
you can do it with Win32 API
any creatable objects should be marked coclass (not interface or source; there were also global modules which I suppose are creatable too: I'm just not sure whether they are defined as coclasses
Show me the code
It is possible to read the information within a type library with the ITypeLib and ITypeInfo interfaces. They can be created with the ICreateTypeLib and ICreateTypeInfo interfaces. However, the Microsoft IDL compiler (MIDL) is probably the only application to ever use ICreateType and ICreateTypeInfo.
A quick google turned up this useful page: Reading Type Libraries with C++.
It contains just the code to get started. Just to see whether it was worth anything, I fired up a cloud Windows instance, grabbed all the sources and compiled it.
In contrast with the options mentioned on the site, I simply compiled on windows with
cl.exe *.cpp /EHs ole32.lib oleaut32.lib
Just for fun, I compiled the stuff on Linux (64 bit) using MingW:
i586-mingw32msvc-g++ *.cpp -loleaut32 -lole32 -o Typelib.exe
To save you the work I have put a zip-file up for download containing:
win32_Unicode.cpp - sources by René Nyffenegger
win32_Unicode.h
TestTypelib.cpp
Typelib.cpp
Typelib.h
VariantHelper.cpp
VariantHelper.h
TestTypelib.exe - binary compiled on windows
A test run:
# linux: ./a.exe ~/.wine/drive_c/windows/system32/msxml6.dll
C:\Games\Stacko>TestTypelib.exe c:\Windows\System32\msxml6.dll
MSXML2: Microsoft XML, v6.0
Nof Type Infos: 149
IXMLDOMImplementation
----------------------------
Interface: Dispatch
functions: 8
variables: 0
Function : QueryInterface
returns : VT_VOID
flags :
invoke kind: function
params : 2
params opt : 0
Parameter : riid type = VT_PTR (VT_USERDEFINED (GUID)) in
Parameter : ppvObj type = VT_PTR (VT_PTR) out
Function : AddRef
returns : VT_UI4
flags :
invoke kind: function
params : 0
params opt : 0
(snip) and 15499 lines more
Concluding
I hope this gives you a good starting point in scanning your system for installed, creatable, COM components
Depends what you mean by "createable". If it has a LocalServer32 or InprocServer32 key it should be locally creatable. It may also be creatable remotely if it has an AppID and the AppID has either LocalService or RemoteServer keys.
However consulting the registry will only answer the question "does it look like it ought to be creatable".
You might still not be able to create it:
The registration might be broken, or "fossil" registry entries from uninstalled components.
The component might be an internal Windows component of some sort that you have no idea how to use since it is intentionally not documented.
The component might be an internal component of an installed application which has additional requirements not documented.
You might not have permission.
There may be other components you could create:
There might be registration-free COM components, such as WSC scriptlets.
there might be registration-free COM DLLs. There is no law saying you have to be registered to be a COM component. Registration is an optional service that most people opt into.
So I guess the answer is you should be able to get a mostly complete list using the registry, but what is the list for?
Without knowing what you want the list for, it is impossible to know if the list is good enough.