Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Problem with COM object registration/Class not registered
Hi -

I've been running my C# COM object on my development machine and it works well. I'm now trying to make this run on another machine that has QM installed (I'll put this in an *.exe as the next step) but it's failing & I'm sure I'm missing something very basic in how COM objects work.

I've built my COM object in Visual Studio 2012 on a Windows 7 machine. I've tried compiling the *.dll for x86 and for 'any cpu'.

When I try to run this code I get the following error:
Copy      Help
Error (RT) in EMIPacsWebService.GetStudyData:  0x80040154, Class not registered.    ?

In QM the COM object methods and types are visible. In the Type Libraries menu I can do a Find for my interfaces and they are all there.

In the Type Libraries menu the 'Register' fails on the *.dll but works on the *.tlb.

On the Windows command line
c:\windows\syswow64\regsvr32 PACSWebServiceWrapper.dll
will fail because the entry point DLLRegisterServer was not found. But regasm.exe works:

Copy      Help
C:\RadFusionDistro\PACSWebServiceDLLs>c:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe PACSWebServiceWrapper.dll
Microsoft (R) .NET Framework Assembly Registration Utility 4.0.30319.1
Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.

Types registered successfully

But after this the message changes to
Copy      Help
Error (RT) in EMIPacsWebService.GetStudyData:  0x80070002, The system cannot find the file specified.

Any ideas on how to proceed?
I know 3 ways of registering .NET COM objects.
1. regasm.exe. It puts information in registry, and optionally creates tlb file. It worked for me, don't know why file not found.
2. Manifests in dll and in exe. I tested but don't like it.
3. Manifest in dll, and COM activation API used in macro. It was my choice. In QM added class __ComActivator and function __ComActivator_CreateManifest.
QM 2.3.5
Not entirely sure how to do #3. Is this manifest file created in Visual Studio or is it generated by your CreateManifest call?

I put the DLL files in C:\Program Files (x86)\Quick Macros 2\RadFusion. When I run
Macro TestManifest
Copy      Help
__ComActivator_CreateManifest "RadFusion\PACSWebServiceWrapper.dll"
I get:
Error (RT) in TestManifest: Error loading type library/DLL. ?

One idea: when I ran RegAsm it's the one in .NET 4.0 framework:
Copy      Help
C:\Program Files (x86)\Quick Macros 2\RadFusion>c:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe PACSWebServiceWrapper.dll
Microsoft (R) .NET Framework Assembly Registration Utility 4.0.30319.1
Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.

Types registered successfully

Should I try redoing this in an earlier version of .NET? Thanks
The dll must be in QM folder, not in a subfolder. At run time too (in QM or exe folder).

__ComActivator_CreateManifest "PACSWebServiceWrapper.dll" 1

The manifest can be used as a file in QM folder or as a manifest resource in your dll. To add manifest resource in VS 2008: add new manifest (menu Project -> Add new item -> Manifest) and replace its text with text of the manifest generated by __ComActivator_CreateManifest. Then VS adds the manifest to dll resources with resource id 2.

The first sentence in __ComActivator_CreateManifest remarks is incorrect. The function uses regasm.exe from the latest installed .NET version, and it works with dlls created for earlier .NET versions too.

__ComActivator_CreateManifest uses regasm.exe just to extract assembly info, not to register.

Updated help text.
Macro __ComActivator help
Copy      Help
;Allows this thread to use COM components that are not registered in registry.
;COM object creation functions will use component info from manifest, not from registry.
;Supports COM dlls, ActiveX controls and .NET COM-visible components.
;Minimum system requirements: Windows XP SP2. For .NET components - .NET framework 1.1.

;1. You need a manifest for the dll file. It is an XML file or resource. Contains info about the dll and its COM classes.
;;;;To create manifest, call <help>__ComActivator_CreateManifest</help>, like in the example below. Do it once; also for new versions of the component.
;;;;Or you can create manifest manually, or with other manifest creation tools.
;;;;If you are developing the component, you can add the manifest to resources of your dll file. For example, in Visual Studio 2008, add new manifest (menu Project -> Add new item -> Manifest) and replace its text with text of the manifest file generated by __ComActivator_CreateManifest. Then VS adds the manifest to dll resources with resource id 2.
;;;;Some existing .NET components may already have manifest, usually with resource id 1 or 2.
;2. Call <help>__ComActivator.Activate</help> each time before using the component. See examples below.

;When creating manifest with __ComActivator_CreateManifest:
;;;The component file (dll etc) must be in QM folder. Can be in a subfolder, except with .NET.
;;;The manifest file will be created in QM folder.

;When using the component:
;;;The component file must be in the same folder relative to QM (or exe) as when creating manifest.
;;;The manifest file must be in QM folder or in dll resources.

;A manifest created on one computer can be used on other computers too.
;It works in exe too.

;A thread can use single active manifest at a time. Activating other manifest will deactivate previous manifest.
;If you use several such components in thread, you can create single manifest file for all: pass list of files to __ComActivator_CreateManifest. Not with .NET.
;Or activate and create component 1, then activate and create component 2...

;There is other way of using COM components without registration. Specify dll path with <help "::/_COM/IDP_COM_FUNC.html">_create</help> or in <help "::/User/IDH_DIALOG_EDITOR.html#a15">dialog definition</help>. It's easier, faster and works on all Windows versions. Does not work with .NET COM components and some other components.


;create manifest

__ComActivator_CreateManifest "ComDll.dll" ;;or "ComDllFolder\ComDll.dll" (not with .NET)
;__ComActivator_CreateManifest "file1.dll[]file2.dll[]file3.dll" ;;use this if need single manifest for several COM components (not with .NET)


;use component

;add this somewhere before calling _create etc
__ComActivator x.Activate("ComDll.X.manifest") ;;use manifest file
__ComActivator x.Activate("ComDll.dll,2") ;;use manifest resource, resource id 2
Gintaras -

I'm still stuck. I've moved the DLL to the QuickMacros directory and I can register it with regasm.exe:
Copy      Help
c:\Program Files (x86)\Quick Macros 2>c:\windows\Microsoft.NET\Framework\v4.0.30
319\RegAsm.exe /verbose PACSWebServiceWrapper.dll
Microsoft .NET Framework Assembly Registration Utility version 4.0.30319.17929
for Microsoft .NET Framework version 4.0.30319.17929
Copyright (C) Microsoft Corporation.  All rights reserved.

Types registered successfully

It does have a dummy manifest in it - I haven't able to create the contents of the manifest from __ComActivator:

__ComActivator_CreateManifest "PACSWebServiceWrapper.dll"

This returns the value
Error (RT) in Macro: Error loading type library/DLL. ?

In the directory I also put the *.tlb file:
Copy      Help
c:\Program Files (x86)\Quick Macros 2>dir PACSWebServiceWrapper.*
Volume in drive C has no label.
Volume Serial Number is A41E-A7F8

Directory of c:\Program Files (x86)\Quick Macros 2

03/26/2013  08:06 AM            41,472 PACSWebServiceWrapper.dll
03/25/2013  04:30 PM             2,036 PACSWebServiceWrapper.dll.config
03/26/2013  08:06 AM            87,552 PACSWebServiceWrapper.pdb
03/26/2013  08:06 AM            18,284 PACSWebServiceWrapper.tlb

Perhaps I should try to call COMActivator in a .NET context to try to get a more detailed error message?
With __ComActivator_CreateManifest use flag 1. If also need type library, use 3. I forgot it in previous post, sorry.

Don't need to register with regasm. If #3 does not work, try to unregister, and also uncheck 'Register for COM interop' in project properties.
Gintaras -

Thanks- that works perfectly (using flag 3).

So - just to make sure - when I'm making my application I use the __ComActivator_CreateManifest; take the manifest & put it into the DLLs, then use these DLLs in my distribution.

In the distributed code the DLLs go into the same directory as the *.exe. At runtime I just need to invoke
Member function EMIPacsWebService.GetStudyUID
Copy      Help
__ComActivator ca.Activate("PACSWebServiceWrapper.X.manifest")
typelib TypelibName "$qm$\PACSWebServiceWrapper.tlb" 1
and then just use the objects? Thanks!

If you use manifest like __ComActivator ca.Activate("PACSWebServiceWrapper.X.manifest"), don't need to add the manifest to the dll.

If you don't want to distribute the manifest file, then you can add the manifest to the dll, and __ComActivator ca.Activate("PACSWebServiceWrapper.dll,2")

If Activate speed is important, use flag 1.

The ComActivator variable must be still alive when the macro uses _create. If need, use thread variable, not local. Never use global variables.

If your macro need to _create objects of classes from several dlls, create manifests for each dll. Then:
__ComActivator ca1.Activate("PACSWebServiceWrapper1.X.manifest")
Typelib1.Class1 x1._create
__ComActivator ca2.Activate("PACSWebServiceWrapper2.X.manifest")
Typelib2.Class2 x2._create

Forum Jump:

Users browsing this thread: 1 Guest(s)