Call .TLB COM Library from C# - 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.

Related

Run two versions of the same unmanaged dll (referenced by C#-app)

Background
I have two softwares, written in C#, using the same third-party dll to get telephony-capabilities.
Problem
When I deploy a new version of one of these softwares, I also deploy a new version of the dll. This one overwrites the references to the older dll, causing my other software to stop working, since the new dll isn't backwards compatible.
Attempted solutions
Deploy the new software with a registration-free version of the dll.
This doesn't work since the dll doesn't have a strong name. Result from sn:
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools>sn.exe -e "theDll.dll" key
Microsoft (R) .NET Framework Strong Name Utility Version 4.0.30319.17929
Copyright (c) Microsoft Corporation. All rights reserved.
theDll.dll does not represent a strongly named assembly
Ask the vendor for a strongly named version
This resulted in a "huh?". The dll works well however, so I don't want to look for a new vendor if I dont really have to.
Give the dll a strong name using ildasm:
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools>ildasm.exe "theDll.dll" /out=theDll.il
error : 'theDll.dll' has no valid CLR header and cannot be disassembled
Which seems to imply that the dll is unmanaged, and thus not possible to strongly name (if I'm not mistaken).
Use SetDllDirectory and LoadLibrary to load the correct version of the dll.
This seems to kinda work, at least I can load the correct dll from a specified place without registering it. This seems like a rather involved way to do it though.
Questions
Has anyone done this the "LoadLibrary"-way and can verify that it works well for a dll where you need to instantiate some classes?
Does anyone have a better solution?
This instruction told me how to construct an appropriate manifest file for an unmanaged COM: https://msdn.microsoft.com/en-us/library/ms973913.aspx
A few important points I don't feel they covered so that I easily understood:
Oleview.exe is one way of getting the info you'll need for the manifest. It is possible to find your dll both by using Object classes-> All objects (provided it is registered with regsvr32) or by using File-> View TypeLib. I felt both these were needed before I had all information (they display different information).
When you have your manifest file, it is sufficient to chose "Add reference" in Visual studio, selecting the "Browse"-tag and then the COM-manifest you created. After that, programming is exactly as if the COM had been registered. VS will create the manifest for your program.
Of course you'll need to make sure your setup project deploys the .dll and its manifest to the same folder as your .exe.

Using registration free com in a .NET app

I'm trying to use a 3rd party COM DLL (I don't believe its a .NET component) from a .NET service without registering the COM DLL but I'm having no luck so far.
I've copied the manifest files from here (http://stackoverflow.com/questions/465882/generate-manifest-files-for-registration-free-com) to use as a starting point (I generated the COM DLL manifest using the referenced mt.exe/regsvr42.exe). However all I get is the following error:
Exception: System.InvalidCastException
Message: Unable to cast COM object of type 'LOGICLib.LogicClass' to interface type 'LOGICLib.ILogic'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{AAAAAAAA-AAAA-AAAA-AAAA-AAAAAA3E8FB4}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
Source: Interop.LOGICLib
at LOGICLib.LogicSecuredClass.Connect(String IP, UInt16 Value, Int32& Result)
at My.Server.MyAssembly.Loader.Connect() in D:\MyProject\Source\Server\MyAssembly\Loader.cs:line 461
The application manifest is named after the exe that starts the service - I've also tried naming it after the assembly that calls the COM DLL. I've tried starting on the command line and via Visual Studio's debugger. I've also tried using the Interop file supplied by the third party and generating my own.
(Note - I've only tested under Windows XP so far.)
I've spent two days on this now and have not progressed at all. Any ideas what I may have missed?
The application manifest is named after the exe that starts the service
Yes, this does not work. Windows always looks for a manifest in the EXE itself, embedded as an unmanaged resource. Only when it cannot find one in there will it look for a .manifest file on disk. Problem is, a managed program built with VS2008 and up already has a manifest. The default one says "I'm Vista aware" only.
You can verify this for yourself by using File + Open + File and selecting your EXE. Open the RT_MANIFEST node and double-click resource 1. If you don't see your reg-free COM manifest entries there then it isn't going to work.
To fix, use Project + Add New Item and select the Application Manifest File item template. You'll get the boilerplate manifest, copy and paste your regfree COM entries in there.
Well, from the exception, you're getting a cast error when trying to cast an object of type LogicClass to an interface type of ILogic. Looks like LogicClass doesn't implement ILogic.
You didn't supply what the DLL is or where you got it, so you're best bet is to look at the documentation for the library you're trying to use. Just a wild guess, but it looks like you're implementing it incorrectly.

Using Someone else's COM Interop DLL

I'm trying to use an API that my client has a license to. I was given a COM Interop DLL.
I can write all of the code I need, but when I go to run it, I receive the following error:
Retrieving the COM class factory for component with CLSID {73C527F2-C6C0-4F4B-92F7-1448EC342FC5} failed due to the following error: 80040154.
Any ideas? Is there something I need to register?
Thanks,
Jack
Do you have the actual COM objects/software installed on the development machine, or did you just copy the interop assembly? If it's the latter, the interop assembly is going to have all the interfaces and the object profiles which will make intellisense work in Visual Studio, but it does not contain actual executable code.
Error 0x80040154 means "Class not registered". You need to register COM object. You can use Regsvr32 tool.

Configure .NET-Based Components for Registration-Free Activation

I've been trying to get a registration-free .NET based COM DLL to work, but without success.
In Visual Studio 2008 I added a new C# class library.
I enabled the 'make assembly COM-visible' and 'register for COM interop' options.
I added a public interface and class with some functions.
I added a manifest dependency to my C++ client application: #pragma comment(linker,"/manifestdependency ...
But when I start my application I get 'the application has failed to start because the application configuration is incorrect'.
I've used Microsoft's mt tool to extract the manifest files of both the C++ client application and the C# COM DLL and the information in both is the same (the dependentAssembly in the C++ manifest file contains the same name and version as the assemblyIdentity in the COM manifest file).
I've also tried the approach described on http://msdn.microsoft.com/en-us/library/eew13bza.aspx but with similar results.
Similarly I tried to add a reference to my COM project in 'Framework and References' of my C++ client application. The information on that property page looked promising (it shows options like 'copy local', 'copy dependencies', etc and properties like the 'assemblyIdentity'), but Visual Studio neither copies the DLLs nor adds a dependency to the manifest file automatically.
Note that the 'registered variant' works fine.
Anyone have any ideas of what I'm doing wrong?
Update:
When I create a simple C++ DLL and embed a manifest with the same name and version of my .NET COM DLL (same assemblyIdentity) my application starts up fine. So the problem lies with the manifest file of my .NET COM DLL.
I can successfully extract the manifest from the DLL with mt -managedassemblyname:... and then embed the same manifest with mt -outputresource:..., but this also doesn't cause Windows to successfully resolve the dependency.
I found the steps needed to get registration-free .NET COM interop working myself :-)
Run: mt -managedassemblyname:"myDll.dll" -out:"myDll.manifest"
Clean manifest (see format at http://msdn.microsoft.com/en-us/library/eew13bza.aspx). Mainly I needed to remove all tags except for assemblyIdentity, clrClass and file (and specifically remove the runtime, mvid and dependency tags).
Run mt -outputresource:"myDll.dll" -manifest "myDll.manifest". Basically this adds the modified manifest as a resource to the DLL. Note that this is apparently not the same manifest (location)! If I reextract the manifest with the managedassemblyname option I still get the 'old' manifest. If I extract it with the inputresource option I get the new one.
I pretty much found this all thanks to Windows Vista. Unlike my Windows XP it contains a tool called sxstrace that gives rather detailed information about the problems with side-by-side execution.
When I did this, I started with a very simple, basic component to get the COM stuff sorted. Also I used a script client in the initial development.
I did not use Visual Studio, but instead a text editor for the .NET code. I inserted the GUIDs for the assembly and for the Interfaces, and marked the interfaces for AutoDispatch.
using System;
using Interop=System.Runtime.InteropServices;
namespace Whatever
{
[Interop.Guid("xxxxxxxx-9120-4283-b972-changethis05")]
[Interop.ComVisible(true)]
[Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)]
public partial class MyClass :
...
}
I made sure my class had a default (no-arguments) constructor.
I ran regasm /codebase by hand, from the command line, specifying the .NET assembly.
I hand-coded the javascript to instantiate the object.
When things were confusing, I checked the ProgId with OleView.exe.
Once you have the very basic stuff working, add complexity gradually, until you get the working solution.
You can also use the approach from the other direction; from the client. .NET Assemblies like System.Random are marked for COM interop when you install .NET, so you can use them to verify that your approach in C++ is correct. Instantiating a System.Random ProgId is something like the "hello, World" of C++-to-.NET-via-COM. If that succeeds, then you know the basic approach in C++ is sound.

Interop.MSXML2 throwing exceptions because its not registered BUT IT WONT REGISTER!

Ok, this line of code:
XMLHTTP40 http = new XMLHTTP40();
Throws a System.Runtime.InteropServices.COMException and complains that:
Retrieving the COM class factory for component with CLSID {88D969C5-F192-11D4-A65F-0040963251E5} failed due to the following error: 80040154.
I have googled that and I guess the DLL needs to be registered because its a COM DLL. That's fine, but when I try to register it I get this error:
C:\some\directory\path\etc\Interop.MSXML2.dll was loaded, but the DllRegisterServer entry point was not found.This file can not be registered.
Apparently this is working on another guys box (he wrote the code, so yeah.. :P). This COM object does not show up in my list of available COM references so I just reference the DLL which is sitting in the bin\Debug directory of the project. When I add the reference to this DLL to my project, I get access to all of the symbols in VisualStudio. I'm wondering, is there something that I can install that would register the DLL and have it show up in my list of COM objects that I can reference? I installed what I thought was the MSXML 4.0 library but when I looked in the directory there wasn't anything in there.
Any ideas?
You need to actually have MSXML4 installed on your box for it to work. THe interop dll doesn't contain any runtime code it only helps .NET talk to MSXML4.
However why would you not use the System.Xml namespace instead of using a COM component?
You need to register (regsvr32) msxml4.dll, not the interop assembly. msxml4.dll is the COM server.

Categories

Resources