Programmatically determine if a COM library (.DLL) is installed - c#

Is there a programmatic way in C# to determine whether a particular COM DLL has been installed? Or is this a matter of scanning the registry for the classId?

What I usually did (and would do, if I needed this again) is try to create an object instance of a class you know is in the COM library - either by ProgID or GUID - and checking for failure.

Try and create it, and handle the error if not.
Under Win32 CoCreateInstance will return REGDB_E_CLASSNOTREG if not installed (including, IIRC, if registered but the dll/exe implementing it is then deleted).
Under .NET the generated COM interop assembly will throw some error (need to check this, don't have convenient code to test for which exception type). Note. if the interop assembly is missing then that will be treated as missing assembly possibly leading to an application load error.

Related

Unable to detect COM wrapped InterOp

I have a library in C#, which i have exposed through COM to an unmanaged application in C++. On one machine, everything seems to have gone well. The interface is visible, is exposed, and the application is able to view it successfully, and use its functions.
On another machine, however, the .NET COM InterOp DLL is simply not visible to the calling application, I cannot debug on that machine so it is not immediately clear where the error is. I get a COM exception which indicates that I failed to create the COM object.
The DLL is present in the GAC, and the class is also registered in the registry, with the same class GUID as I have specified for it.
I have tried moving the assembly to the local folder, but still no detection. The DLL is dependent on another assembly which is present with it in the same folder. Moreover, the object creation for the other DLL, is dynamic and so it should not affect the object creation for the first assembly.
I might be overlooking a simple, fundamental step, since COM is new to me.
What could be the possible reason?

Call .TLB COM Library from C#

I have an old .TLB file which is called 'GrpSvr.tlb', it contains a class called GrpCall. I have registered the .tlb on my Win7 x64 machine using regtlibv12.exe which worked correctly. I want to invoke the methods withing this library from C#, so first I tried:
Type objectType = System.Type.GetTypeFromProgID("GrpSvr.GrpCall");
dynamic thirdPartyDLLObject = System.Activator.CreateInstance(objectType);
but this returns null for objectType.
Question 1. can I invoke my .TLB file this way and what I am I doing wrong in this case?
Moving away from this method I then decided to follow
Import TLB into C#
which describes how to create a C# DLL using MSs Type Library Importer. I followed this and created GrouperServer.dll from GrpSrv.tlb using the command:
C:\Program Files (x86)\Microsoft Visual Studio 12.0>tlbimp F:\Groupers\DRGROUP\GrpSvr.tlb /out:C:\GrouperServer.dll /nam
espace:GrouperServer
Microsoft (R) .NET Framework Type Library to Assembly Converter 4.0.30319.33440
Copyright (C) Microsoft Corporation. All rights reserved.
TlbImp : Type library imported to C:\GrouperServer.dll
I then imported this DLL into my C# project (and set Embed interop Types = false following this answer) and attempted to invoke the class via
GrouperServer.GrpCallClass grouperServer = new GrouperServer.GrpCallClass();
but this does not work and at run-time I get the following error:
A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in DrGroupIN.exe
Additional information: Retrieving the COM class factory for component with CLSID {FFB54BC4-B15E-11D1-99BC-0000E803C444} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
Following the advice given in
Retrieving the COM class factory for component with CLSID {XXXX} failed due to the following error: 80040154
I then set the project to target x86, but this does not help in this case.
Question 2: Why is this error being thrown and how can I resolve it?
Yeah, you are doing this wrong. A type library is not a COM server, using regtlibv12.exe is never necessary. You'll need to read the exception for what it is trying to tell you, as tens of thousands of Google hits do, the COM server is not registered.
A COM server is executable code, usually stored in a .dll. Sometimes an .ocx or an .exe. An installation step is required to register that file so that COM can find it back later when a program tries to create a COM object that the server implements. The Regsvr32.exe utility is a very common way to do that, but you should always use the installer provided by the vendor or author. Which ensures that the executable file(s) are copied onto your machine and writes the registry entries. Including the ones for the type library. The exception tells you that this wasn't done, COM cannot find the registry entry that tells it where the file is stored.
A .tlb file only describes the COM component, it tells a compiler what the declarations looks like. Helping the compiler to generate correct code to use the component and telling you when your code is wrong. It is metadata, very similar to the metadata that's present in a .NET assembly that describes the .NET types that are implemented by the assembly. It is usually embedded in the DLL or EXE as a resource, much like metadata is embedded in a .NET assembly. You can look at it with OleView.exe, File + View Typelib. Also used by Tlbimp.exe, a .NET utility that converts a type library to a .NET interop assembly, converting the declarations into a format that the CLR can easily understand.
Can't help you find the correct installer, it isn't anything standard.
Put GrpSvr.Dll and its dependencies under the exe folder, and try again. The error is because it can not the find the GrpSvr.Dll based on the GUID in the registry.
Or you can search the registry with that GUID, you will find an entry which specify where the DLL is.
One tool to debug this kind error is the process monitor in the system internal kit, the log will give you a great detail on how the exe is searching registry and files, from the log, you should find the missing parts.

How can a C# program use a COM dll of any version?

This question is a sequel of this question
We're creating a dll, written in C++, providing access to some hardware. This dll implements and is accessed using COM interfaces. We also have a C# program that uses this dll through the COM objects.
We're having an issue with the versions. Indeed, when running the C# program, it absolutely wants to use the exact COM/C++ dll version it used when compiling. I.e. if the C# program was compiled using COM/C++ dll 1.2.3.4, then the program will refuse to run with COM/C++ dll 1.2.3.5.
Unhandled Exception: System.TypeInitializationException: The type initializer for 'MyDllVerify.App' threw an exception. ---> System.IO.FileNotFoundException: Could not load file or assembly 'MyCorp.MyDll.Interop, Version=1.2.3.4, Culture
=neutral, PublicKeyToken=ced78d295d1e0f2b' or one of its dependencies. The system cannot find the file specified.
File name: 'MyCorp.MyDll.Interop, Version=1.2.3.4, Culture=neutral, PublicKey Token=ced78d295d1e0f2b' at MyDllVerify.App..cctor()
I'd like to instruct the C# program to use any COM/C++ dll with version 1.2.anything.
Where can I configure this in the C# project?
I would suggest you to not directly reference to the COM-dll in your C# project. If you do that, at build time there is always a new COM-interop-dll generated. This can lead to a lot of problems.
It would be a better approach to create a COM-interop-dll, store it in your library folder and reference this library in your C# project. Keep this COM-interop-dll as static as possible. If you do it like that, you can replace the used COM-dll as much as you want, as long as your interfaces do not change.
You could try to manipulate this interop assembly and make the version checking for 1.2.* there, if you realy want that (I would not recommend to that, it could cause serious confusion).
Explanation:
The COM-interop-dll is a regular .NET assembly. It works like a wrapper between your C# code and the COM-C++-code you want to use in the C# code.
The COM-interop-dll don't have to be registered for COM. You can install this assembly so many times you like. But it requires that your COM-dll is registered for COM.
Useful tools:
tlbimp
regasm
sn
Nothing is different from the way I documented it in your previous question. You still use <bindingRedirect> to allow the wrong version of the interop assembly to be loaded.
It is fairly unlikely to work in practice, messing with DLL Hell when you use COM is extremely unwise. If you use early binding then COM has no way to verify that you are calling the correct method. Very unlike .NET where the jitter can make checks like these at runtime from the metadata in the assembly. If the C++ programmer did it right then he changed the guids of the types that he changed. Which will make your code bomb with E_NOINTERFACE since you'll use the guid of the old version.
If he didn't, unfortunately way too common, then your program is liable to crash with something nasty like an AccessViolationException. Or worse, it won't crash but will call the completely wrong method.
The failure mode is more benign when you use late binding, you'll get one of the IDispatch errors when the method doesn't exist or if its arguments have changed. Not that this ultimately solves anything, you still have a program that doesn't work. Mess with DLL Hell like this only if you like to live dangerously.

How to latebind COM event without interface

I need to late bind to a 3rd party VB6 COM object in a 3.5 C# application (to avoid version dependencies that we currently have). The dll that was provided is not consumable in most non-latebound ways due to some bug that causes errors when we try to consume it normally. Currently, we are using a custom VB6 wrapper that makes things VERY version specific, however I have found that I can use late-binding to access properties and methods. Now, I am trying to late-bind to events, however everything I have read says that I need to inherit from the COM wrapper's interface to create the event sinks that are needed. Here is one such article.
So, my question is whether it is possible to perform late-bound event handling without having any reference to the dll at compile time?
UPDATE
Here are the errors I have with the VB6 wrapper (Which is still being actively updated).
In OleViewer, I get
Could not decompile selected item Error loading type library/DLL.
TYPE_E_CANTLOADLIBRARY ($80029C4A)
In Visual Studio I get:
Could not determine the dependencies of the COM reference
"3rdPartyDLL". Error loading type library/DLL. (Exception from
HRESULT: 0x80029C4A (TYPE_E_CANTLOADLIBRARY))
From here:
I found that the problem is caused when the IDL contains an importlib
to another project's .tlb typelib.
This seems to create a dependency between one dll and the other.
If dependant dll is missing OLEView refuses to display the dependent
dll, which also manifests itself by not allowing #import from C++
code.
Therefore I would look carefully at the COM dependencies of the DLL in question and make sure they are all registered as well.
It also goes on to add:
...because both dlls are co-dependent,
components from each interact (via interface declarations on method
signatures) and use #import from each others typelib.
Therefore, unless both target dlls are present, neither can be
rebuilt. As you can imagine, this causes a terrible problem when you
try to completely rebuild the project's from scratch.
I've experimented with separating the interface definitions into
smaller IDL files...
Edit: here's a recent example of this problem coming about (I believe). I had a C# library exported to COM. Modifications to that library were made which changed the interface of several classes, but the library GUID was not changed. Also see here about risks of AutoDual which was in use.
Here's the odd part - the VB6 DLL was rebuilt referencing the modified C# DLL. It compiled fine. no errors. But its typelib was corrupt - OleView couldn't open it, failing with TYPE_E_CANTLOADLIBRARY. Changing the C# DLL GUID was necessary to get the VB6 DLL recompiled successfully.
Clearly a pitfall of VB6 / C# interop.
The problem is most probably caused by the platform you are using. I just had a similar problem yesterday. Make sure that you are setting your project platform to x86 / x64 when you are late binding a x86/x64 COM type library.
The same applies to oleview. Use the x86/x64 version to view x86/x64 type libraries. (Possibly you need to install the x64 Windows SDK if you are on an x64 system to get the correct executeable).

Can I control version number assigned to interop assembly?

I have a C# program that uses a native C++ COM object. Visual Studio generates an interop assembly with wrappers for the types in the COM object. Each time I recompile the C# program interop assembly has version 1.0.0.0.
This is bad for the installer - sometimes we extend the COM object interfaces (add new methods at the end of some interface) so the interop assembly has to be changed. when the installer tries to update an existing installation it thinks that the interop assembly hasn't changed (since it still has version 1.0.0.0) and skips updating it and the program doesn't work.
How can I control the version number assigned to the interop assembly?
It's been a little while so I might be remembering incorrectly how this works but I think that you might be able to do what you want if you use Tlbimp rather than having VS create the wrappers.
Possibly you could do it using the asmversion parameter and otherwise it might be possible if you give it a strong name using the keyfile parameter.

Categories

Resources