I am trying to produce a C# wrapper for a COM object that I have (named SC_COM.dll), but am having some issues linking it with Visual Studio 2008 (running Vista). I need to do this registration-free with the COM DLL--I'm using a manifest file to let Visual Studio know about SC_COM.dll, and that appears to be working. I used TblImp.exe to generate a type library (SC_COMtlb.dll) that I'm referencing in Visual Studio 2008 so I can do early binding with the DLL that I need. The DLLs are both in the same directory as the manifest and the executable.
Here's the issue: When I instantiate the object and try and call one of its methods in C#, it throws the following error:
Error detected: Unable to cast COM object of type 'SC_COMtlb.SCAccessObjClass' to interface type 'SC_COMtlb.ISCUploader'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{C677308A-AC0F-427D-889A-47E5DC990138}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
I'm not entirely certain what this error means--I've done a search on the error code, and it appears to be a relatively general C# error. So am I going about linking the COM object the wrong way here, or is there some other important step I may be missing?
I should probably note that I'm not entirely sure how the type library (S\C_COMtlb.dll) that I produced knows where the actual COM DLL is, since it's not registered with the system--I assume it just looks in the same directory. Could this potentially be the issue, and if so, how can I better link the two?
Try adding this to your App.exe.manifest:
<comInterfaceExternalProxyStub
name="ISCUploader"
iid="{C677308A-AC0F-427D-889A-47E5DC990138}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
baseInterface="{00000000-0000-0000-C000-000000000046}"
tlbid = "{PUT-YOUR-TLB-GUID-HERE}" />
Where TLBID can be found from your Visual Studio generated Native.Namespace.Assembly.Name.manifest, looking like this:
<typelib tlbid="{A-GUID-IS-HERE--USE-IT}"
version="1.0" helpdir="" resourceid="0" flags="HASDISKIMAGE" />
I was banging my head against this for quite some time, but I found these helpful references and pieced it together and it's working for me:
Why do I get E_NOINTERFACE when creating an object that supports that interface?
Registration-Free Skype4Com and multithreaded apartment
Registration-Free Activation of COM Components: A Walkthrough
Which version of Windows? Since Windows Vista, an internal manifest overrides an external manifest. By default, C# executables have internal manifests, which means that your whatever.exe.manifest file will be ignored.
If you go to the properties page for your C# EXE, you'll see there's an "Icon and manifest" section on the "Application tab". Set "Manifest" to the name of your manifest file, and it'll be embedded instead of the default one.
If that doesn't work, you might have to do some post-build steps with MT.EXE in order to merge your external manifest with the default internal one and to put the merged manifest back into the .EXE file.
the error code means that Visual Studio thinks that a certain object is supposed to implement a certain interface, but when I tries to "connect" to that interface the object responds that it doesn't know about it.
I would guess that the problem is in SC_COM.dll. TLBIMP.EXE extracts class and interface information from metadata stored within the DLL and builds wrappers for the class.
For example, if SC_COM is written in C++, this could happen if the creator of the DLL indicated in the IDL file that a class implements that interface, but the actual code doesn't support that interface.
Here's another common source of problems this DLL might have: sometimes you have a class implementing an ISomething2 interface which derives from an ISomething interface, but the class implementation to only recognize ISomething2. If you implement a derived interface, you must recognize its base interface as well. This is a common mistake.
Do you have (and control) the source code for the DLL?
Related
I want to use third-party COM dll in my c# project. I've added "Reference" in Visual studio and got access to the COM types. All works fine.
Question: What if target machine where end-user will work has no that COM dll installed?
My app will not run at all or I can handle this situation safely somehow?
It works the same way as when you run your program with a missing .NET assembly, you'll get an exception when you try to create an instance of the object. The underlying COMException is for error code 0x80040154, "Class not registered".
This is easily tested btw, just unregister the COM server with Regsvr32.exe, /u option. Writing excessive amounts of code to deal with a missing DLL isn't typically that useful, given how easy it is to fix the real problem.
The binaries to your program should contain all the necessary libraries that you are using. It's all in the bin folder. If you give the computer everything that's in there your program will work fine.
I am in the process of convering a rather large project written in VB6 into C#. Given the size of the project being moved, it is being done in phases over the course of 18-months. I am running into an issue with adding a reference of a VB6 ActiveX dll to a .Net project.
If you follow exactly these steps, you too should be able to recreate the problem.
I have written an interface in .Net that is COM visible:
<ComVisible(True)>
Public Interface ITestInterface
Property A As String
Function TestMethod() As String
End Interface
By selecting "Register for COM interop" in Compile tab of project properties, you get a TLB file.
I've created a VB6 project that references this TLB and a class that implements the interface exposed.
Implements ITestInterface
Private mA As String
Public Property Get ITestInterface_A() As String
ITestInterface_A = mA
End Property
Public Property Let ITestInterface_A(ByVal value As String)
mA = value
End Property
Public Function ITestInterface_TestMethod() As String
ITestInterface_TestMethod = "From VB6"
End Function
If I set the Component tab of project properties in VB6 to use "Remote Server Files" then a TLB is automatically created when compiling. I can view that TLB in OleView and see the following (in addition to the details of the concrete implementation done in VB6 of the interface defined in the .Net project):
// typelib filename: TestVB6Interface.dll
[
uuid(**EF005573-BFC7-436D-A382-F906CA09F94A**),
version(3.0)
]
// ... some other stuff
// TLib : // TLib : : {79EC733A-0267-4506-8D38-C4D4655E0755}
importlib("SimpleDotNetLibrary.tlb");
Now, I create a completely new .Net project. If I add a reference to the VB6 dll, I get the following error:
Could not resolve COM reference "ef005573-bfc7-436d-a382-f906ca09f94a" version 3.0. The type library importer encountered an error during type verification. Try importing without class members.
However, if I launch a Visual Studio Command Prompt and run the following:
tlbimp TestVB6Interface.tlb /out:TestVB6Interface.MyInterop.dll
Then I can add that dll as a reference in my .Net solution and it works perfectly fine.
My question. What is tlbimp doing on the command line that is not being done when I just add the reference directly? When the message in Visual Studio says "try importing without class members" how exactly do I do that within Visual studio? I know how to do that in tlbimp.
I apologize for the wall of text, but I wanted to describe the situation as best I could keeping the information I felt was relevant.
The Visual Studio IDE definately takes a different path when registering DLLs for COM Interop then it does when running the command line tools from a command prompt.
I doubt that Microsoft has documented this anywhere. However, my years of experience have proven this to be the case. I once ran into a situation in which a "regsvcs" command from the .NET 2.0 Framework would actually cause an infinite loop. If you Google it you'll probably find others that have had this problem. I was able to make it one step further by using the VS IDE to perform the COM registration of a .NET Serviced Component. However, it inevitably ended in error. The error was a step forward over the infinite loop. Either way it proved to me that the VS IDE takes a different code path / business logic when dealing with COM Interop and registry entries.
I'm trying to use COM functions exposed by an EXE. I've created a C# project using Visual Studio 2010 (on a windows7/x64 machine) and added the reference to that EXE, then set the Isolated flag to true. When I build the solution, I get errors for each of the classes that it exposes.
Problem isolating COM reference 'FNCClient11Lib': Registry
key
'HKEY_CURRENT_USER\SOFTWARE\CLASSES\CLSID{e49b30c9-6d7e-48f5-91da-f2f0414c6a13}\InProcServer32'
is missing value '(Default)'.
These entries don't exist in the registry in that location but DO exist here (below)
HKEY_CURRENT_USER\Software\Classes\Wow6432Node\CLSID{E49B30C9-6D7E-48F5-91DA-F2F0414C6A13}
Is there any way I can point this to the right location in the registry when building?
Can I reference an EXE? All the examples I've seen so far reference DLLs only.
This problem occurs when the type library contains classes that are marked as 'uncreatable' in the COM interface. You can check this by using the OLE/COM Viewer (as Admin), navigating to the type library that causes the problem, opening it and look up the CoClass definitions. If the one using the reported uuid is declared 'uncreatable', you got it. Also, in the VS object browser, these clases don't expose a constructor in the interface.
My solution was to rebuild the COM component with a public constructor for these classes but of course this is only possible if you have the sources.
Finally, there may be other reasons for this symptom though ...
I have a project with a single interface marked with ComVisible(true). If I build the project, I get warning MSB3214: "" does not contain any types that can be registered for COM Interop.
If, however, I create an empty class, give it a default constructor and mark it with ComVisible(true), I do not get warning MSB3214. I see the registered class but I do not see the interface in my registry.
What am I missing?
This is by design, in spite of Jays link. Regasm.exe only writes registry keys for concrete classes that implement an interface. The ProgID and the CLSID keys. COM does have registration for interfaces (HKCR\Interfaces) but only proxies get registered there. .NET has no mechanism to create proxies.
An assembly with only interface declarations is only useful to create a type library, allowing other projects to implement them. Creating a type library is not a problem, use Tlbexp.exe
Looks like it could be a Visual Studio 2008 bug reported in July 2008: VS creates no type library for a project containing solely C# interfaces (MSB3214)
There are no listed workarounds.
In a response data Sept 2009 Microsoft they were (presumably) able to fix the issue:
We were able to fix this issue and the
bug will be available in the next
release of the product.
(let's assume he meant the bug fix will be available in the next release)
I couldn't find any evidence the bug was fixed.
We're writing a simple .NET C# COM applicaton for Photoshop, which is designed to run on all versions from CS2 to CS5 and everything in between. The same application also exists in JavaScript form and it works with all aforementioned versions, as we've avoided to implement version specific functionality.
The problem we've run into is related to the COM interface. For instance, if our application is compiled with the Interop.Photoshop DLL from Photoshop CSx, it does not run on Photoshop CSy. This seems to be because the registry CLSID is specific to each version of Photoshop, causing our application to not find the correct COM interface DLL, if run on a system where a different version of Photoshop than what we compiled for is installed (assuming both applications are 32 bit).
If this is indeed the problem, we are wondering if it would be possible to re-register the COM interface of Photoshop version CSx with the CSy CLSID, ignoring the fact that functionality may differ between versions.
More specific information follow:
Our primary Photoshop.Application CLSID is located here in the registry: HKEY_CLASSES_ROOT\Photoshop.Application\CLSID
This CLSID must match the CLSID for which our application was built for. For instance, this ID differs between CS5 and CS5.1.
Our only solution today is to build specific versions of our application for specific versions of Photoshop, and this is only possible if we have that specific application version installed.
The error code we are getting is 0x80040154, "Retrieving the COM class factory for component with CLSID {116EE066-135E-4F63-8D0E-78F62705FBFC} failed". This application was built with CS5.1 but run on CS5.04 which resulted in the COM interface to not be found. This CLSID is specific to CS5.1. In conclusion, we need to re-register the COM interface to match the CLSID of CS5.04 to be able to run our application on that specific version. Is this possible or is there another way?
Any help or hint we can get on the matter is extremely appreciated.
Changing the GUIDs of COM classes and interface is a hard requirement, it avoids DLL Hell. To invoke the normal problem you are dealing with right now: Version Hell. When you try to use a version of a chunk of code that's not installed on the machine you get a diagnostic message instead of random undiagnosable failure. You cannot reliably re-register, that can seriously mess up your customer's machine.
Yes, you can do it the way Javascript does it: use late binding. You don't use the interop library. It is very painful to do in C# if you use a version earlier than 4, you have to use reflection. But easy to do with VB.NET or with C# version 4's dynamic keyword. This KB article shows late binding in C# using the old way. With the dynamic keyword you can write the code you way you have it written now, other than the object creation syntax. Any kind of version mismatch problem will still cause an exception but only at the property or method call and only at runtime, not compile time.