Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registering a .NET assembly in a COM+ application outside of the GAC

I developed a .NET assembly (.NET 4.0, strong-named) which exposes two Serviced Components. The assembly (dll) is supposed to be hosted in a COM+ application and it is decorated with the COM+ attributes (assembly & component levels). For example, the assembly level attributes:

//COM+ Attributes
[assembly: ApplicationID("MY_APP_GUID")] //GUID of the COM+ app
[assembly: ApplicationName("MyComPlusAppName")] //Name of the COM+ app
[assembly: ApplicationActivation(ActivationOption.Server)] //The app is hosted in it own dllhost process (out-of-process)
[assembly: ApplicationAccessControl(AccessChecksLevel = AccessChecksLevelOption.ApplicationComponent, Authentication = AuthenticationOption.None, ImpersonationLevel = ImpersonationLevelOption.Delegate, Value = false)]
[assembly: Description("COM+ app description")]

Currently (development reasons), I've been running the following script to create the COM+ application and register the assembly (with all of its components):

%windir%\Microsoft.NET\Framework\v4.0.30319\RegSvcs.exe /appdir:"%CD%" MyComPlusAssembly.dll 

The above batch file will create (in a single run) the COM+ application according to the assembly decorating attributes, register the MyComPlusAssembly.dll file in the COM+ app and register all ComVisible components in it, so everything is visible and configured as expected in dcomcnfg. This command will also generate a fresh new TLB file. The assembly is built using AnyCPU, so on x64 versions of Windows the dllhost.exe process will run as 64bit and on a x86 version of Windows it will run as 32bit. Also, my dll file should NOT be placed in the GAC (this is why I'm using the /appdir switch of the RegSvcs.exe command-line utility). All is working as expected when installing the COM+ assembly with the above batch file.

I started writing a Wix (v3.6) deployment project for my app which is supposed to do the same, that is: Create the COM+ application, register the .NET assembly and all ComVisible components. Please note that this time I'm relying on the fact that the TLB file is shipped with the installer (*.msi). The TLB was generated by the build process (VS 2010). To achieve the above I've added the following Wix component (inspired by the Wix COM+ Extension docs - WixComPlusExtension):

   <DirectoryRef Id="INSTALLDIR_SERVER">
      <Component Id="cmp_MyComPlusAssembly.dll" Guid="COMPONENT_DLL_GUID">
        <File Id="MyComPlusAssembly.dll" Name="MyComPlusAssembly.dll" DiskId="1" Source="..\install\$(var.Configuration)\Server\MyComPlusAssembly.dll" KeyPath="yes"/>
        <CreateFolder>
          <util:PermissionEx GenericAll="yes" User="NT AUTHORITY\LocalService"/>
        </CreateFolder>
        <complus:ComPlusApplication Id="ComPlusServerApp"
                                    AccessChecksLevel="applicationComponentLevel"
                                    Activation="local"
                                    ApplicationAccessChecksEnabled="no"
                                    ApplicationDirectory="[INSTALLDIR_SERVER]"
                                    ApplicationId="MyComPlusAssembly.dll"
                                    Authentication="none"
                                    Description="MyComPlusAssembly.dll"
                                    Identity="NT AUTHORITY\LocalService"
                                    ImpersonationLevel="delegate"
                                    IsEnabled="yes"
                                    RunForever="yes"
                                    Name="MyComPlusApp"
                                    Deleteable="yes">
          <complus:ComPlusAssembly Id="ComPlusServerAssembley"
                                   DllPath="[#MyComPlusAssembly.dll]"
                                   TlbPath="[#MyComPlusAssembly.tlb]"
                                   Type=".net"
                                   DllPathFromGAC="no">

            <complus:ComPlusComponent Id="COMObject_1"
                                      CLSID="COM_OBJ_1_GUID"
                                      Description="Object 1"
                                      IsEnabled="yes"/>

            <complus:ComPlusComponent Id="COMObject_2"
                                      CLSID="COM_OBJ_2_GUID"
                                      Description="Object 2"
                                      IsEnabled="yes"/>

          </complus:ComPlusAssembly>
        </complus:ComPlusApplication>        

      </Component>
      </Component>

      <Component Id="cmp_MyComPlusAssembly.tlb" Guid="COMPONENT_TLB_GUID">
        <File Id="cmp_MyComPlusAssembly.tlb" Name="cmp_MyComPlusAssembly.tlb" DiskId="1" Source="..\install\$(var.Configuration)\Server\cmp_MyComPlusAssembly.tlb" KeyPath="yes"/>
      </Component>

    </DirectoryRef>   

The MSI project builds successfully but the installation process fails and is rolled back immediately after trying to register the dll. The following error can be found in the log (for BOTH x86 & x64 versions):

Action 16:33:37: RegisterComPlusAssemblies. Registering COM+ components
RegisterComPlusAssemblies: DLL: C:\Program Files\MyApp\Server\MyComPlusAssembly.dll
ComPlusInstallExecute:  Registering assembly, key: ComPlusServerAssembley
ComPlusInstallExecute:  ExceptionInfo: Code='0', Source='System.EnterpriseServices', Description='Failed to load assembly 'c:\program files\myapp\server\MyComPlusAssembly.dll'.', HelpFile='', HelpContext='0'
ComPlusInstallExecute:  Error 0x80020009: Failed to invoke RegistrationHelper.InstallAssembly() method
ComPlusInstallExecute:  Error 0x80020009: Failed to register .NET assembly
ComPlusInstallExecute:  Error 0x80020009: Failed to register assembly, key: ComPlusServerAssembley
ComPlusInstallExecute:  Error 0x80020009: Failed to register assemblies

The above error can mean that the dll being registered in the COM+ app is missing, that is, the file is not on the disk. Although the installation process is fast, I've never seen the MyComPlusAssembly.dll file being copied to the disk (to [INSTALLDIR_SERVER]), all other files are on the disk when the installation starts rolling back (including the TLB). Is this a timing issue?

Observations:

  1. This happens for both versions of the installer (x64 & x86).
  2. When removing the "<complus:ComPlusAssembly...>" tag (including the nested components), the installation succeeds and an (empty) application is created, that is - only the container", without any assembly or COM+ hosted components.
  3. I tried adding a third "<Component.../>" which creates a simple registry key and move all of the "<complus:ComPlusApplication.../>" code to it. This component will be executed after all files are copied. Same result (error) as log above.

What am I missing here?

like image 841
OmriSela Avatar asked Dec 19 '12 01:12

OmriSela


People also ask

How can you register a .NET assembly?

You can run a command-line tool called the Assembly Registration Tool (Regasm.exe) to register or unregister an assembly for use with COM. Regasm.exe adds information about the class to the system registry so COM clients can use the . NET Framework class transparently.

Where are the. net Framework assemblies stored?

NET Framework 4, the default location for the Global Assembly Cache is %windir%\Microsoft.NET\assembly. In earlier versions of the . NET Framework, the default location is %windir%\assembly.


1 Answers

I changed COM+ assembly's target platform to 3.5 and it worked.

The issue is around System.EnterpriseServices.dll version called from WixComPlusExtension. There are several version of the library, at least: 2.0.xxxx and 4.0.xxxx. WixComPlusExtension calls RegisterDotNetAssembly (cpasmexec.cpp) method that uses System.EnterpriseServices.RegistrationHelper class to register an assembly. The fact is when you call RegistrationHelper.InstallAssembly from System.EnterpriseServices of version 2.0.xxxx against a .net COM+ library built for framework 4 or higher, you get "Failed to load assembly" error.

Any ideas how to tweak WixComPlusExtension (it's open source) to make it call System.EnterpriseServices for framework 4?

like image 107
Rustem Zinnatullin Avatar answered Sep 30 '22 18:09

Rustem Zinnatullin