I'm hitting a wall that I understand. I have a simple COM server that I want to instantiate. I can do so with simple VBS or PS commands outside of my EXE:
$sms_client = New-Object -COM 'CPApplet.CPAppletMgr'
Set controlPanelAppletManager = CreateObject("CPApplet.CPAppletMgr")
In my C# code I tried early binding, late binding, a PS runspace/pipeline and lastly Process.Start( cscript myVBS ). In all of those scenarios it comes back with class not registered and fails.
//throws exception
CPAPPLETLib.CPAppletMgr cpAppletMgr= new CPAPPLETLib.CPAppletMgr();
// myType does not come back null
Type myType = Type.GetTypeFromProgID("CPApplet.CPAppletMgr");
// throws exception
object myObject = Activator.CreateInstance(myType);
Update: I can create the Scripting.FileSystemObject without issue. I don't need that... it was just a test.
I've tried compiling 32bit, 64bit. Running as standard user and elevated. I've tried registering the DLL again but something about my C# .NET EXE is doing something that it just can't see the COM. I even tried creating a brand new console app project and it does it there also.
Am I missing something that is a .NET 101 COM-Interop fundamental?
Retrieving the COM class factory for component with CLSID {096C5BA8-044B-4BAC-9914-144723BA4F2A} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
Update:
Setting the platform to x64 made the error go away. AnyCPU and x86 fail. Does this mean I have to create 2 versions of the EXE for my 32bit and 64bit workstations?
It doesn't necessarily mean you have to create two instances. You should be able to create just one as long as you make it AnyCPU. The thing is, you have to register it with the correct runtime. By default, Visual Studio registers AnyCPU with the 32-bit runtime on a 64-bit machine. If you make sure to run the regasm.exe with the 64-bit runtime and correct version of the runtime, then you should be able to call it from a 64-bit process.
However, in my shop we've generally compiled one for x86 and one for x64 and deploy the right one for the right platform.
Related
There is a third-party COM server DLL I need to include in my ASP.NET Core project. Adding via the Add > COM Reference worked fine and I end up with a COM > Interop.ThirdPartyServer under the Dependencies.
In my code I want to create an instance of one of the classes but it keeps failing with "Class not registered" even though it is.
I used
using ThirdPartyServer;
Type widgetType = Type.GetTypeFromProgID("ThirdPartyServer.Widget");
IWidget myWidget = (IWidget)Activator.CreateInstance(widgetType);
But I am seeing a "80040154 Class not registered" error.
I am sure it is registered, as I had to find the ProgID from the registry as it's not shown anywhere in the disassembled type library. In any case, I re-ran regsvr32 ThirdPartyServer.dll and that seemed to work with no errors, as it is a 32-bit DLL.
The 0x80040154 aka REGDB_E_CLASSNOTREG or "Class not registered" error usually means ... well, that the class is ... not registered. But the exact reason can be a bit complex.
COM clients and COM servers that run both in-process need to run with the same bitness, so both in x86 or both in x64. So:
If a COM client runs as x86, it talks to the x86 side of the
registry, so the COM server must registered in the x86 side of the
registry (regsvr32, regasm, custom reg code, etc. must run as x86).
If a COM client runs as x64, it talks to the x64 side of the
registry, so the COM server must registered in the x64 side of the
registry (regsvr32, regasm, custom reg code, etc. must run as x64).
Note 1: for out-of-process communications x86 clients or servers can talk to x64 servers or clients. That's one benefit of COM.
Note 2: the .NET "Any CPU" compilation feature allows to use the exact same binary for x86 and x64 but this binary still needs to be registered in the needed registry side, or in both registry sides if x86 clients and x64 clients support is needed.
I'm trying to instantiate a COM object, defined in a x86 dll written in Borland C++, in a testing program i write in C# (.net 4.7.2). The COM dll (server) is working, I have a windows service also written in C++ Borland that can use it and instantiate a COM object from the class (using CoCreateInstance). The dll is registered and the InprocServer32 entry has the correct path to the dll. There is no coclass existing in a typelib, only interfaces (those exist in the typelib). I have used the TlbImp to create dll:s which i reference in the c# project. In the project the target platform is set to x86. The way i try to instantiate an object is:
var comType = Type.GetTypeFromProgID("ins.MyComType");
object comObj = Activator.CreateInstance(comType);
however the second line gives me
"Exception thrown: 'System.IO.FileNotFoundException' in mscorlib.dll"
with the message 'Retrieving the COM class factory for component with
CLSID {C4363C5E-3831-46DF-8701-60C8D1B612BA} failed due to the
following error: 8007007e The specified module could not be found.
(Exception from HRESULT: 0x8007007E).".
It does not matter if i try to run the app as administrator. I have a vague memory of trying out a similar thing a couple of years ago and that it at that time worked. It was probably on a Win 7 machine (might even have been a 32-bit system). I have tried to open the project in DependencyWalker but i'm not sure what i'm looking at. I get a couple of errors:
*Error: At least one required implicit or forwarded dependency was not found.
*Error: Modules with different CPU types were found.
*Error: A circular dependency was detected.
*Warning: At least one delay-load dependency module was not found.
*Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.
Does any one have any idea on what it could be causing the exception? Or if i could get some hints as of how to dig deeper into dependencywalker? I get a gigantic tree of systemassembly stuff but i cannot see any obvious assembly standing out, though DW refers to many of them as being 64 bit. My guess is some dependent dll(s) somewhere should be x86 but which one(s). Is there a redist similar thingi i should have installed for this to work?
best regards
/Erik
You should write a simple VBScript that contains these lines:
set obj = CreateObject("ins.MyComType")
MsgBox TypeName(obj)
Name the file test.vbs
Execute the command:
c:\windows\syswow64\wscript.exe test.vbs
Using the version from syswow64 ensures that it uses the 32-bit version of wscript.exe which can instantiate 32-bit COM objects. The version in c:\windows\system32 can only instantiate 64-bit In-process COM objects in DLLs...you said your object is a 32-bit COM DLL server.
If the vbscript fails, it could be that the object is not automation compatible--implements IDispatch. Otherwise you will get an error message why it fails.
If it succeeds, you will know there is probably nothing on the C++ side causing problems. You THINK this is the case...but where is the runtime for Borland C++? Is everything statically linked, or is some of it dynamically linked? If it is dynamically linked, where is it in the path? It could be that the C++ service you have has the library in its path so that when it loads your COM object, the library is available. But, when you try to load from a 3rd party, like .NET or VBScript then the path to the library manifests itself. Who knows? I'm just making suggestions.
If you use ProcMon, it can see where the problems are. It will show you what registry entries are being read and which files are trying to be loaded.
I have third party DLL which I am using it in my application. when I run my application, I get Com Exception, Class not registered error.
Retrieving the COM class factory for component with CLSID
{6A587E64-35FE-47EA-99F2-BDE40978D81D} failed due to the following
error: 80040154 Class not registered (Exception from HRESULT:
0x80040154 (REGDB_E_CLASSNOTREG)).
I know there are lot of duplicate question on this exception but none of the answers have helped me so far.
I have verified Class Id (Guid) is present in Registry entry.
I have tried to re register DLL by using Regsvr32.exe but it did not fix the issue. I have Win 7 machine and have tried re register from both type of regSvr32.exe. One located in Sytem32 and other one in SystemWow64.
I am running my application by targeting platform to Any CPU. Anyway, I have tried it by running as X86 and x64. X86 is will not work in my case as I have someother DLL which are 64 bit dll. My third party DLL is also 64 Bit dll.
Additional info:
This is existing application which works fine in XP. I am moving it to Win 7 and that is when I am facing the issue...Third Party DLL which I am talking about comes with installer...I just install it and refer the DLL...I have got 64 Bit version from third Party and have installed that in Win 7 Machine and only have changed reference to point to 64 Bit version DLL. I am sure that path I am referring to is correct..Not single line of code is changed apart from reference.
Any ideas on what next I could do to solve this problem
The code below executes without problems in a 32bit c#-application.
object obj = system.Runtime.InteropServices.Marshal.GetActiveObject("Due.Application");
var due = (Due.IDueApplication2)obj;
Now, I try to get the same code working in a 64bit c#-application and therefore I followed the instructions at
http://www.gfi.com/blog/32bit-object-64bit-environment/ or http://www.codeproject.com/Tips/267554/Using-bit-COM-Object-from-bit-Application
Two cases when running the 64bit application
Due is not running and the first code-line fails with HRESULT: 0x800401E3 (MK_E_UNAVAILABLE). That's fine and happens in the 32bit version as well.
Due is running and in the first code-line obj receives a COM-object. But then, the second code-line throws an InvalidCastException because QueryInterface fails with HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG). (Obviously, this works in the 32bit version.)
Q What must I do, to make the cast in the 2nd code-line to succeed?
Edit As suggested by #HansPassant, I fired up the ProcessMonitor and monitored both the 32bit and 64bit application. I observed that the 64bit application when executing GetActiveObject (1st code-line) omitted accessing a COM-object specific dll (CreateFile, QueryBasicInformationFile, QueryNameInformationFile, ...) which in contrast the 32bit application did, even though I've added the according registry entries beforehand, i.e. AppID, DllSurrogate.
Edit1 I removed all the registry-entries I added and double-checked the results including the log-files of the ProcessMonitor. I didn't notice any difference - same symptoms as before.
Update In the directory of the 3rd party software I found a Due.tlb and I tried to create a runtime-callable-wrapper as hinted in 64 to 32 bit Interop - how?.
I created an Interop.due.dll with tlbimp.exe Due.tlb /out:Interop.due.dll. Unfortunately, when I replaced the original 32-bit Interop.deu.dll with the newly created platform-agnostic one my solution failed to compile (missing references).
The only thing which worked was an out-of-process solution. I implemented a 32-bit WCF-service which provides the access to the COM-objects for the 64-bit process. (Hence, I implemented the proxy myself.)
ad Update
good news
By utilizing the information in this post I was able to regenerate a platform agnostic version of the Interop.due.dll. Even though, TlbImp.exe warned me with warning TI3002 : Importing a type library into a platform agnostic assembly. This can cause errors if the type library is not truly platform agnostic.
bad news
The promised glue code was not provided and therefore the bridging of 64bit and 32bit did not work out. (The 32bit only version worked as before.)
I have developed a sample c# COM-Visible DLL.
Built in x86 and registered via 32-bit Regasm, it executes well by COM Client.
Built in x64 and registered via 64-bit Regasm, the COM Client call drops the following error :
Retrieving the COM class factory for component with CLSID {A0F2F58F-7B98-3605-BEC9-84724FF1E824} failed due to the following error: 80040154.
I checked and made sure that CLSID is present indeed in the registry under HKCR\CLSID.
Below is an example of simulating a COM Access via c# :
var type = Type.GetTypeFromProgID("ProgID.Interop.5683");
var obj = Activator.CreateInstance(type); // <-- blows here
I also checked via procmon that there is a successful access to the type via progId
The question is, where is the Activator looking exactly ? and how to make it "see" my x64 registered assembly ? is there something I need to add somewhere ?
I have found many "solutions" advising an x86 re-build. But Come'on ! I am not going to do that and lose all x64 benefits just for COM's sake. Is there a "REAL" solution to that ?
After a night of sleep and a little hindsight, I figured out that the Activator needed to be called from an x64 App, to find the x64 CLSID by looking in the x64 locations in the Registry.
i.e, when called from an x86 App, Activaor looks in the x86 locations in the Registry for CLSID and subsequent key including codebase.
var type = Type.GetTypeFromProgID("ProgID.Interop.5683");
var obj = Activator.CreateInstance(type);