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:
<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.<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?
This works for me:
<Component Id="cmp502C27298171EA4E966A386B188D734C" Guid="{7A5ADAA7-8D43-4E91-80DB-764BB1E13887}">
<File Id="filB4BA1D295EA5EC7D92FD7FEFDD1251C2" KeyPath="yes" Source="$(var.BinComPlusFolder)\MyComponent.dll" />
<complus:ComPlusApplication Id="MyApp" Name="MyApp" Description="My App" ApplicationAccessChecksEnabled="no" AccessChecksLevel="applicationComponentLevel" Authentication="packet" ImpersonationLevel="impersonate" Activation="local" ApplicationDirectory="[ServicesBinFolder]" Identity="[SERVICE_USERNAME]" Password="[SERVICE_PASSWORD]" ShutdownAfter="3" Deleteable="yes" CRMEnabled="yes" ThreeGigSupportEnabled="no" ConcurrentApps="3" RecycleLifetimeLimit="60" RecycleMemoryLimit="500000" RecycleExpirationTimeout="15" RecycleCallLimit="0" RecycleActivationLimit="0" DumpEnabled="no" DumpOnException="no" DumpOnFailfast="no" DumpPath="%systemroot%\system32\com\dmp" QueuingEnabled="no" />
<complus:ComPlusAssembly Id="filB4BA1D295EA5EC7D92FD7FEFDD1251C2" Application="MyApp" DllPath="[#filB4BA1D295EA5EC7D92FD7FEFDD1251C2]" TlbPath="[#fil447CD49CDBE45EACEE125A362902CF2F]" Type=".net" RegisterInCommit="yes" />
</Component>
Perhaps you should omit the DllPathFromGAC attribute?
Edit: This is on Wix v3.6 on Windows XP and Windows 2008 32bit. The other thing to ensure is that you also install all the dependencies required by the COM+ component. Use the Assembly Binding Log Viewer during the installation to see if your component is failing to load because of missing or incorrect (wrong versions) dependencies.
There seems to be a lot of examples of this but even if you follow them you might not succed.
This is how I did it:
After compile and link, cl *.cs -o foo.dll
tlbexp foo.dll
heat file foo.dll -o foodll.wxs
heat file foo.tlb -o foodlltlb.wxs
Some manually edit of the foodlltlb.wxs file because the genereated wxs from heat will generate warnings/error with candle (3.8) (Basic, TypeLib must be a child of File)
Fix your guid and component id, reference them in you main wxs, voila!
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?
User contributions licensed under CC BY-SA 3.0