CLSIDFromProgID is successful but CreateInstace fails! Why? - com

I am trying to create an instance of a COM object. I have the class name that implements the interface and I get a CLSID by using CLSIDFromProgID(). So since I am getting a CLSID I thought everything should be fine from now on. However when I do a call to CreateInstance and pass in the CLSID, I get an error saying "Class not registered". Also I get this error only in some computers. It runs error free on several computers. I don't understand where the problem could be. Is my registry dirty? Does anyone know what is going on here? Thanks for your help!
I just want to add that this is a .NET COM class. The appropriate entries are in the registry and the DLL is in the GAC.

CLSIDFromProgId is simply looking up the ProgId's name in the registry and translating it to a CLSID, it doesn't have to look at anything beyond the registry or even check that something is actually implementing that CLSID.
When you call CreateInstance on the CLSID, Windows will look up in the registry to find out how the object should be instantiated (usually a exe or dll). It will then try to load the dll (or start up the exe) and create the object from it.
There is a lot of documentation in MSDN on the processes involved, for example see "COM Class Objects and CLSIDs", and if you do a lot of COM work it is worthwhile learning the process from first principals since it can save a lot of time and hassle when debugging this type of issue.

It's a two step process in the registry. You used the ProgID to get the CLSID. Then, when you call CreateInstance, COM then uses the CLSID to find the path to the dll. You can used regedit yourself to lookup the CLSID and see what that entry looks like.

Thanks for your answers. The .Net assemblies were registered properly and were present in the GAC. One application that absolutely confirmed this was Process Explorer. You can view the dlls that are loaded by each application. So from here I was able to see if the application that was instantiating the COM objects was actually able to load the DLLs or not. I found out that this was indeed happening. The problem was due to different Regional settings. We found that the application threw an exception when the region was not set to US. This issue was fixed. The error message "Class not registered" was not very helpful. Thankfully it was a quick fix.

Using shell32 as an example, you can create a new instance like so;
var shl = (Shell) Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
This will aquire a refernce to an existing component;
var shl2 = (Shell) Marshal.GetActiveObject("Shell.Application");
Here's a reference to how to do the same in IronPython.
** Note, this used the progid, clsid would be nearly identical, just use Type.GetTypeFromCLSID({GUID}).

Related

COM "class is not registered" in case if using additional dll. How to debug this error?

The situation is that. I run Labview and from one via ActiveX pallet call my COM object method. And it works, I walk through my code with debugger.
But when I start to use (uncomment) code from side dll I see "class is not registered" error in Labview. My additional dll and its dependencies located in separate directory. So I tried to set PATH environment variable to this directory and after that run Labview. But is still doesn't work.
So the question is how to debug this situation? I looked through event logger but didn't found anything related.
P.S. I created my own synthetic application in C++ which calls the same method as Labview via COM too. And it works.
The problem was in hard defined base dll address. Labview used this address and COM dlls couldn't use that space.
I suppose report is incorrect ("class is not registered") because class is registered, but corresponding dll couldn't be loaded.

COM DLL c++ not visible in Object Viewer

i have create a c++ DLL with COM interface with Visual Studio 2013.
The DLL get's installed along with registration.
In the Ole Object Viewer, i can see typelibrary of this DLL with all
exported functions.
regsvr32 completes without any error.
Just within C# i can't use, because creation fails with error 0x80040154 -
class not found or not registered.
It is not a platform issue. The 64bit version is in system32 and the 32bit
version in syswow64 and they are registered there and typelibary information
in OLE Object Viewer confirms this.
But the class is not listed in the OLE Object Viewer tree.
Habe noe idea what's missing or wrong.
More over, i have a simliar VC++ project and this COM/DLL can be seen
in the view in the OLE Object viewer. It is compiled, linked
and installed in exactly the same manner.
I already compared all Compiler, Linker and MIDL settings, checked the .idl
file in the projects, the .rgs files... all seems to be the same, except
different names and guids.
So it is really strange: One is shown as COM object in the tree
of OLE Object viewer and can be used in C# program, the other not.
Please note: There is no compiler error in C# project using this DLL/COM.
There is a runtime error on creation 0x80040154.
Summary: i have to COM/DLL, both visual studion projects, deployed in the same
manner, the one can be seen in the OLE object tree and can be used in C#, the other not.
Are there any key points i could check and which are required for a successfull
listing as OLE COM object ?
PS: The only difference is the MSIL compiler version indicated in the type library view: The good COM/DLL has MSIL 7.xxx the bad one 8.xxx
but i don't know where at all to selected MSIL compiler. Both DLL/COM are built
by VS2013
OLE/COM Object Viewer shows the registration. When an application attempts to create an instance, there are further steps involved: registration points to server implementation, the library is loaded, class factory is located, class factory is called to created an instance. A failure in these steps results in instantiation failure nevertheless the registration itself is present and valid.
Your typical steps to troubleshoot the problems are:
Setting a break point in constructor of your COM class, in class factory construction, in DllGetClassObject exported function of your DLL, finally in its DllMain - to find out how close the system reaches trying to create an instance. Then step from there to get to the root of the problem.
Using Process Monitor to track registry/file activity around instantiation call and identify issues there (esp. if your DLL with COM server implementation is not even loaded).
If the class is not even listed in OLE/COM Object Viewer, then there is a problem even at registration stage. Your first troubleshooting attempt is to re-register manually and see if you have any registration error, or if it fixes the problem. There is a number of reasons for the registration to fail, a typical is that you have your COM class in your type library, however there is no implementation connected and referenced by OBJECT_ENTRY. With failed registration instantiation is expectedly not working because system cannot pick your implementation up and you see what you see: REGDB_E_CLASSNOTREG error code.
Found the problem: The typelibrary was not associated with object, because the typelibrary CLSID in .rgs file was different from that in .idl file, just by a space which was most likey introduced accidently.
in .rgs file:
TypeLib = s '{7DAA7049 -AAB2-4689-8635-FB6E03423F34}'
in .idl
uuid(7DAA7049-AAB2-4689-8635-FB6E03423F34),
Now i can use the DLL as COM in my C# project.
The COM/DLL was not listed in the object tree, because in the .rgs file was no name defined. This is a definition with name and this is the name of the COM/DLL in object viewer; the name follows to s which was previously empty (s'').
ForceRemove {4763F309-D922-227A-A1A8-CDFF29893BBD} = s 'myDllCom Class'

GUID/ProgId/CLSID confusion

Trying to track down a COM problem, I'm debugging my code and seeming to see the same GUID represented different ways...
I have a line in our code:
class __declspec(uuid("{D4F83347-E58E-11d1-9D47-006008098294}"))
And various registry stuff in between, then a call to:
CLSID clsid;
::CLSIDFromProgID("myProgId",&clsid);
In the debugger, clsid is displayed as {000AFC9A-3347-D4F8-8EE5-D1119D470060}. To me this is too similar not to be right, but it's not something I can check automatically... we've got the D4F8 and 3347, 9D47, but E58E becomes 8EE5 etc.
Is there a way I can understand why this is happening, and a way I can get them to look the same for comparison?
EDIT
To clear up some side-tracking, I've checked and the CLSID in the Windows registry and our registration scripts is presented as {D4F83347-E58E-11d1-9D47-006008098294} too - so the issue over my uuid(...) is not relevant I think.
Using CLSIDFromProgID() when you already have the guid doesn't make much sense. The function looks in the registry to map the "ProgId" string to the CLSID {guid}. Which of course makes it important that the progid is registered properly. Sure sounds like it is not. When your class is already decorated with __declspec(uuid) then simply use the __uuidof() operator to retrieve the guid.
The similarity in the byte values suggests that your registration code is broken.
"__declspec(uuid" is only an association of the identifier to your class, nothing else. Using CLSIDFromProgID API you are resolving ProgID to CLSID using registration infromation in system registry. That is, the two don't have to match. They typically do match though if you do everything neatly and your COM class is registered with the same identifier as the one being attached in source code to the C++ class.
After some testing, I discovered that the issue was simply how the visual C++ debugger was displaying the value, nothing more. e.g the registry value is {D4F83347-E58E-11d1-9D47-006008098294}, calling ::StringToCLSID() on the result of CLSIDFromProgID() gives {D4F83347-E58E-11d1-9D47-006008098294} - but in the debugger MSVC++ displays the variable as {000AFC9A-3347-D4F8-8EE5-D1119D470060}.
Why it does that, is another question!

How does .NET/COM work with multiple versions registered via Regasm?

I have a .NET DLL (that happens to be written in C++/CLI). Parts of it I want to expose via COM. I do this and register it using "regasm my.dll /codebase". So far so good. But then I change some things and the version number of the assembly changes plus I move the dll to a different folder. I register it again and look at my COM object in OLE/COM Viewer. I see something like this
InprocServer32 [Codebase] = file://c://foo/bar/my.dll
7.0.0.0 [Class] = My.Blah.Class
7.0.0.0 [Assembly] = Sync, Version=7.0.0.0, Culture=neutral, PublicKeyToken=1dd19234234
7.0.0.0 [RuntimeVersion] = v2.0.50727
7.0.0.0 [CodeBase] = file://c:/dooby/do/my.dll
7.0.0.27397 [Class] = My.Blah.Class
7.0.0.27397 [Assembly] = Sync, Version=7.0.0.27397, Culture=neutral, PublicKeyToken=1dd19234234
7.0.0.27397 [RuntimeVersion] = v2.0.50727
7.0.0.27397 [CodeBase] = file://c://foo/bar/my.dll
Questions about multiple versions:
So I think that the last COM object that was registered wins. It doesn't matter if I have my old 7.0.0.0 COM object registered, the 7.0.0.27397 is the one that will be created when I instantiate my COM object because I registered it last. Is that correct?
Oops I didn't keep around the 7.0.0.0 object. Is there any way to get rid of it? Is there any way to remove all versions of a COM object other than going into the registry and whacking it by hand?
Just out of curiosity, if I specifically wanted to instantiate a particular version of my COM object is there any way to do that? (I'm using C++ if you wanted to give a code example).
Is there any way I can just tell regasm to not store the version number because it just seems to be cluttering things up and I can't see what the benefit is. If my COM object went through significant API change I'd just change the GUID and progid, right? What if I don't want to register multiple versions (I don't).
I always set my COM visible assemblies up with a static AssemblyVersion for just this reason. If you want to have binaries tagged with a version, use AssemblyFileVersion instead.
Last registered object wins: yep
Not really. You can put stuff in your assembly's ComRegisterFunction/ComUnregisterFunction attributed methods to automate the cleanup, but if you leave old version droppings around, this is about the only way.
You'd do it with a different coclass GUID and/or ProgID (eg, MyCoClass.1, .2, etc). CoCreateInstance doesn't know anything about the version values- they're used by the CLR's activator to ensure it loaded the right assembly.
No- best thing to do is never change your assembly version (see above).
Components with the same CLSID should be compatible, especially if you've only changed the build number between assemblies. Here's the only relevant thing I found to confirm this by googling quickly.
To answer your questions directly:
Correct.
regasm /unregister
Look into Binding redirects.
Probably not.

"Object variable or With block variable not set" when attempting to create COM object

I have a VB6 dll that is trying to create a COM object using the following line of code:
Set CreateObj = CreateObject("OPSValuer.OPSValue")
However this fails with the error "Object variable or With block variable not set".
I can see OPSValuer.OPSValue in dcomcnfg and it appears to be registered fine. Does anyone have any ideas about what may be causing the problem?
It's possible that the class you are trying to instantiate is not installed correctly or is missing some dependencies. If you have access to OLE View, you can try instantiating that class outside of VB. If it won't instantiate then you have a bad installation or missing dependency. OLE View ships with Visual Studio, search for OleView.exe on your system.
It was located here on my system: D:\Program Files\Microsoft Visual Studio 8\Common7\Tools\Bin
DMKing is right about OleView. Also try looking at the control in Dependency Walker, any missing dependencies should come quickly to the surface.
Since this is a DCom component there also may be something failing in the components constructor, if anything fails in the constructor you will get that error. Is this a local DCom object or something running on another tier?
Instead of CreateObject try instantiating it with a standard New and see if it gives you a different error. Adding the reference itself may help out with determining that error. Is there a reason you are using late binding, rather than early binding?
The error may be thrown within the object initializing routine. That I don't find "OPSValuer.OPSValue" on Google makes me think it is custom code that encounters a bug.
Assuming OPSValuer.OPSValue is a component written in VB, this is probably an error raised in the Class_Initialize event of that component. If you have the source code of the component it should be easy to debug.