Custom Action with regsvr - wix

I would like to run a custom action on a dll during my installation process.
There is no clean description that I found. So how would you implement this command:
regsvr32.exe /c /n /i:"[PathToIncFolder]" "[BIN]MyFile.dll"
in Wix during the Installation or maybe degister during uninstall?

Preferrably not. The recommended way to install COM components nowadays is to manually add the registry entries.

You definitely should be performing manual registration of the keys and values needed to install the COM component. The reason self-registration is frowned upon is because of problems concerning rollback and uninstall.
The OLE/COM specification should give you information on what keys need to be registered but at a minimum you will need to add a set of entries to a new subkey at HKCR\CLSID\.
Inside this subkey you will need at a minimum, the path to the component and the threading model.
Depending on what your COM component does you may also need to register a PROGID, file-extension maps and APPID.
If the COM object is a managed assembly then your task is slightly different as you need to refer to the .NET bootstrap assembly instead of your own.
If your COM object is written in ATL then your project probably contains a .rgs file which contains all the registry information you'll need.
For further information on COM registry entries check the MSDN here.

Related

WiX Setup: When do I need to change the GUID of a component?

I am creating a new version of an existing setup with WiX.
In the process, the version of an included third party product has been updated. It consists of several files (DLLs, Configs, exes), each of which is in its own component.
Do I need to change the GUIDs of all these components?
The new version of the third party product requires a newer VC redist package than the old version, so it is not backward compatible.
The names and destinations of the affected files are the same.
I have read
https://learn.microsoft.com/en-us/windows/win32/msi/changing-the-component-code?redirectedfrom=MSDN
Change my component GUID in wix?
but I am honestly even more confused now.
If it wasn't for the redist dependency change thing, I would have just put the new files in the places of the old ones and left the GUIDs the same.
But now?
The names and destinations of the affected files are the same.
Overall: This basically means that you should keep the same component GUIDs as before. The component GUID essentially identifies a target destination. If the target destination (the absolute path) changes you need a new component GUID.
File Name Change: Keep in mind that renaming a file changes the absolute path even if the path stays the same - hence you need a new component GUID for such file name changes.
Runtime: The incompatibility of the runtime should be irrelevant for the component GUID issue. What third party product is this? Perhaps it has its own installer? If it does it can potentially interfere with your installation in other ways (COM registration etc...).
Setup.exe Launcher: The standard procedure is to bundle runtimes in a setup.exe wrapping your MSI and all runtimes it requires. WiX offers the Burn framework to make such setup.exe launchers. Perhaps try my deployment info search grid for info on Burn. Also: Make yourself a single page PDF explaining your application's runtime requirements for corporate use and include it in your setup.exe so your setup is easy to deploy large scale.
The MSI File Itself: You can also add launch conditions to the MSI to identify that a required runtime is missing (so you can abort) - or you can use a custom action to inspect the system (I find this more flexible, but custom actions in general are complex. Use them read-only - for inspection only, and they are safer).
Links:
Will change in ComponentID for component in Windows Installer effect during upgrade scenerio.?

Several short questions about COM .net assemblies, regasm, dll, tlb and guids

All question are related to a .net project dll in .net framework 2.0 that exposes itself as COM.
1) If we don't specify any GUIDs in the source code (typelib, classes, interfaces) who is generating the GUIDs? The compiler or regasm?
2) The GUIDs values exists in the dll, in the tlb or in both files?
3) Any developer with the same source code would generate the very same GUIDs independently on the machine where she builds or run regasm?
4) If I run regasm passing existing dll and tlb files, what happens If the dll and the tlb doesn't match? Regasm regenerate the tlb file with uptodate elements and GUIDs? Or it registers the TypeLib with the current tlb file?
5) What is the point of running regasm with dll and tlb parameters set?
Tlb file is part of what you deploy or it is best practice to only deploy the dll and let regasm generate the tlb on the fly?
6) And last question, is tlb really required? What is the point of having a tlb file? Is not all the information already in the registry? What extra info it provides?
7) When unregistering with regasm, what we need to provide? The dll? The Tlb? Both? What happens if dll (or tlb) doesn't match with existing reg entries? If already registered with tlb option but I run regasm unregister with dll only it would delete the TypeLyb entry too?
8) Regarding bitness, regasm will always generate entries under SysWow64 too? The regasm under Framework64 do the same as the one under Framework?
A type library is the exact equivalent of .NET metadata. It is most of all useful to the client programmer, it makes the compiler and the IDE smart about your library. Providing auto-completion and syntax checking so the odds of a mismatch between his code and yours are minimal. The registration step is necessary so your files can be found back. The type library is normally embedded as a resource in the DLL itself, like .NET metadata, but the .NET build model does not make that easy to do. The client compiler uses the type library info to generate the appropriate COM calls. Guids are a big deal because that is what the client compiler needs to use, identifier names play no role. There is a way to use "late binding" using names, the exact equivalent of Reflection in .NET, but that does not involve a type library.
who is generating the GUIDs?
The CLR does. Every .NET interface or class has one, regardless if it is [ComVisible(true)]. Exposed also through the Type.GUID property. If you didn't use the [Guid] attribute on the type then it runs an algorithm to generate the Guid that uses the type declaration as input. Or in other words, if you make any changes to the type then you can be sure that the Guid will have a different value. Which is the basic reason you should never use the [Guid] attribute, unless you have to create an exact drop-in replacement and cannot recompile client code. The TLBID comes from the AssemblyInfo.cs file that was auto-generated when you created the project.
in the dll, in the tlb or in both files?
It only exists in the DLL when you used the [Guid] attribute, but normally it is generated at runtime as explained above. It is always present in the type library, that's how the client compiler knows to create an object of your class and use its interface(s).
would generate the very same GUIDs
Yes, only the type declaration plays a role.
If I run regasm passing existing dll and tlb files
Regasm can only create a type library, as requested with its /tlb option, it cannot take an existing one. It otherwise does the exact same thing as Tlbexp.exe does, use Reflection to enumerate the types in the assembly to find the [ComVisible(true)] ones and generate the matching type library declaration. The extra thing it does is write the registry key for the type library to HKLM/Software/Classes/Typelib. So the client IDE can find it back.
What is the point of running regasm with dll and tlb parameters set?
No real idea with "dll parameter" might mean. As noted above, use /tlb to generate the type library. Whether or not you deploy the type library depends on its usage, if you don't also provide the client code then you should always deploy it so the client programmer can use it. Other usage of the type library is the subject of this post. If you're not sure how the client programmer is going to use your code then always deploy.
Is not all the information already in the registry?
What's in the registry is limited, only enough info to find the type library file back. The description of your interfaces, their method signatures, guids and the CLSID that the factory function needs is in the type library.
When unregistering with regasm, what we need to provide?
Exact same thing as registering it, you only add /unregister. You must also provide /tlb if you used it previously so the TypeLib registry key can be deleted. It can be pretty important to automate this while you are busy developing and testing the library, since the guids are normally auto-generated you can produce a lot of garbage in the registry. As well as ugly head-scratching when you forget to run Regasm. Project > Properties > Build tab, "Register for COM interop" checkbox. But with the downside that you have to run VS elevated so it can write to the registry.
regasm will always generate entries under SysWow64 too?
SysWow64 plays no role, do always avoid deploying to c:\windows. But yes, bitness does matter, the registry is structured so a 64-bit app cannot accidentally create an object in a 32-bit library and die on an ugly exception. And the other way around. A 32-bit client app will read registry keys from HKLM/Software/WOW6432Node, you only get your registry keys there is you used the 32-bit version of Regasm. Notable perhaps is that it is usually fine to run both flavors of Regasm, given that C# code can run on any platform.

WiX bootstrapper, global install directory

I have an application with a bootstrapper that installs multiple components below it. The code below uses ninject to have a loosely coupled database layer.
With regards to the bundle/bootstrapper, I would like to move the database layer out into a separate msi in order to support optional data layer choices on install (e.g.: radio buttons to install SQLite/SQLExpress/MySQL etc..).
I am struggling to figure out how to get the install directory working though, as the database components needs to install into the root/install directory of the main application. (rather than using the gac etc).
How can I do this? I have tried the steps mentioned here: How to use properties to set the installation path? but to no avail. I must have something wrong.. but given the code is spread over 5(ish) files, it seems a little large to load here!
Any pointers to get started trying to implement this, or should I go ahead and upload the code?
The method you refer to should work. Note however that the name of the property given in
<MsiProperty Name="INSTALLLOCATION"
should match the name used in wxs file of the corresponding MSI package. Typically if the MSI was created based on WixUI_InstallDir template the name would be APPLICATIONFOLDER and in the default template without the UI it is INSTALLFOLDER.

WIX: COM registration fails when installing COM dll into another folder

I am using heat to harvest the COM dll and tlb files (let's call them MyLib.*, developed in VB.NET) to do the COM registration. Everything was working fine, when I install MyLib.dll and MyLib.tlb into my application's installation folder, i.e., INSTALLDIR. However, since we want to allow different versions of our SW to be installed on the same machine, and if they are using the same version of COM component, only one copy of the dll (I think each version of our SW should have its own tlb, please correct me if I am wrong) should be installed, we now want to install MyLib.dll into another folder, specifically PROGRAM_FILES\Common Files\SHARED_FOLDER_NAME, so now if one version of our SW is uninstalled, the MyLib.dll will not be removed and can still be used by other versions.
But here comes the problem: my COM registration is just simply not working any more after I install this dll into this another folder, and it keeps saying that can't find file specified when I am calling the COM function which indicates registration failure. In the WIX installer project, everything is the same except this folder for MyLib.dll.
Here is the registry structure after installation:
Firstly I have HKCR\CLSID{MYCLSIDs}, each of them represents one of my COM class. in the sub-key named "InprocServer32", I have Assembly, Class, CodeBase, RuntimeVersion, threadingModel. And the CodeBase is either common file folder (not working) or MyApp's installation folder(working), which is the different locations I put the dll. I thought there would be another sub-key TypeLib under {MYCLSIDs}, since Access only sees the TypeLib and I think there should be some link from the TypeLib to the actual dll, however, at both cases this sub-key is missing but in the second case it is still working. Is there a problem of it?
Secondly I have HKLM\Software\Classes\CLSID{MYCLSIDs}, these keys are of course the same structure as described above.
Thirdly, the HKCR{MYPROGIDs}, these are just ProgIDs of my classes
Fourthly, HKCR\Typelib{LibID}, which includes the information from tlb file, and this ID is from the Assembly GUID of COM component project.
Finally, the HKEY_CLASSES_ROOT\Interface{InterfaceID}, there is sub-keys named ProxyStubClsid32 with value {00020424-0000-0000-C000-000000000046}, and the one named TypeLib and the value is my LibID.
As I mentioned, the only difference is the CodeBase, which stores where MyLib.dll is located. In order to verify that, I did two tests: after I install MyLib.dll into the shared folder, the COM calling fails. But if I replace all the CodeBase values for SHARED_FOLDER\MyLib.dll to INSTALLDIR\MyLib.dll, and copy MyLib.dll into INSTALLDIR, it actually works. Vise versa, after I install MyLib.dll into INSTALLDIR(in which case COM is working), I change the CodeBase values from INSTALLDIR\MyLib.dll to SHARED_FOLDER\MyLib.dll, and make a copy to SHARED_FOLDER, this time it fails. So it seems that it is exactly the installation location's problem, which is the opposite to my understanding of COM. And I don't think there is a permission issue for the SHARED_FOLDER(I could be wrong) since it is in a folder that my installer creates.
Please help, thanks!
It turned out that MyLib.dll is using some other libraries, which are still installed in the MyApp installation folder. And so in that case that MyLib.dll is installed in the shared folder, it tries to find those libraries in the same libraries, which of course fails. When I install those libraries in the shared folder too, it is working.
BTW I found fulogvw.exe very helpful when tracking down the assembly loading problem. For example in my case in the failed log it says can't load file xxx.dll in SHARED_FOLDER, the xxx.dll is some library that MyLib.dll is using, and I had no idea that MyLib.dll needs it until I see the log.

WiX register assemblies for COM Interop

I'm really struggling with WiX. I have .NET assemblies to install that require registration for COM Interop, AND they must be registered with another framework that requires calling a Register() method in a .NET assembly that's in the GAC. This registration method is a 'black box' with a hidden storage mechanism so I can't perform this operation declaratively.
I get that the declaritive approach is best for COM registration, but I have two problems with using heat.exe:
RegAsm works, but Heat.exe chokes on my assembly with the message:
heat.exe : warning HEAT5151 : Could
not harvest data from a file that was
expected to be an assembly:
C:[...].dll.
If this file is not an assembly you
can ignore this warning. Otherwise,
this error detail may be helpful to
diagnose the failure: Exception has
been thrown by the t arget of an
invocation.
The secondary registration that I need to do relies on the [ComRegisterFunction] attribute, which normally triggers further actions at the time the assembly is registered for COM Interop. This normally happens when the assembly is registered by RegAsm.exe or by calling System.Runtime.InteropServices.RegistrationServices. So, I need that ComRegisterFunction in my assembly to execute during the installation.
I don't mind taking the declarative approach to COM registration (or I wouldn't mind if heat worked on my assembly) but I need to call that ComRegisterFunction as part of the install. Ideally, I'd like to look at all of the executables I'm installing, reflect on them for any methods with the [ComRegisterFunction] attribute and call those methods, this would be done after all files are installed.
How can I achieve this in WiX? Or, is there another approach? If it makes any difference, I am using the 'Votive' Visual Studio integration with project references.
These are opposing goals. The point of using the declarative approach is to not use Regasm.exe or Regsvr32.exe to call the registration function. In other words, your [ComRegisterFunction] attributed method won't be called. You can't have both.
The exception that heat.exe dies on isn't healthy, it indicates that there's something wrong with your registration function or class contructor. Debug this by making your DLL the startup project. Project + Properties, Debug tab and make heat.exe the startup program. Set the command line to your DLL. Debug + Exceptions and tick the Thrown box for CLR exceptions, the debugger will stop when the exception is thrown.
Oh, and don't forget to call RegistrationServices.RegisterAssembly in your register function. Regasm.exe won't do it automatically anymore since you used the attribute.
I'll caveat my answer by stating that the correct way to do this is as Hans states - via heat.
An alternative however is to use the SelfRegCost attribute of the File element under a Component element.
Below is an example from one of our older setup kits that's thus far been working with no issues.
<File Source="..\..\External References\MSCAL.OCX" SelfRegCost="1"/>
As with any SO answer, best to check this works in your situation and test thoroughly.