I am working on Wix and I have to place a new dll (Let's say New.dll) as a part of MSI installation into a specific path in GAC where some other dll already resides.(Let's say Exisiting.dll. Please note this dll exists before installing my MSI).
So is there a way I can achieve this? Please note New.dll contains native code and is not .net assembly. Thanks.
The GAC is only for files in strongly-named assemblies. An assembly is a versioned set of files, one of which has a manifest. Creating multi-file assemblies is easy but most tools neither help create nor respect that an assembly may have multiple files. The basic tool is the assembly linker. See How to: Build a Multifile Assembly.
A native DLL can be made part part of an assembly with the /linkresource switch for the C# compiler. If you want to fiddle with a C# project file, you can override the standard Compile target and Csc task with your own that will pass linkresources.
WiX does not handle multi-file assemblies. You have to create File elements for each file in the assembly. All such Files should be in the same Component. This is an instance where having a single component makes sense when Organizing Applications into Components.
If want want a different behavior (that is, replace one of the files), then you have to create a new version of the assembly/Component. Hopefully, that is how the previous native DLL got into the GAC in the first place.
I've only done this a little bit years ago. I'm 99% certain that the new.dll would be a companion file of the old.dll. In other words, the old.msi has a component with old.dll as the keyfile. The new.dll has to be added as an additional file to this component so that MsiPublishAssemblies / Fusion API can publish them together into the same storage directly.
You may be able to do this in a new.msi provided that you sync up the component ID (GUID) and mark it as a shared component. You would have to rebuild old.dll with the same strong name (and AssemblyVersion) but with a higher AssemblyFileVersion / Win32 File Version.
MSI component rules require newer DLL's to be backwards compatible. This goes double for the GAC. This seems to be a substantial change to old.dll. It might make sense to just rev the AssemblyVersion and define this as a whole new component and install side by side in the GAC.
The first thing you are going to want to do is search for the file on the system... You can do this with the FileSearch WiX statement:
<Property Id="PROPFILESEARCHEXISTINGDLL" Secure="yes">
<DirectorySearch Id="EXISTINGDLLFOLDERPATH" Path="[WindowsFolder]\assembly\" Depth="5">
<FileSearch Name="Existing.dll"/>
</DirectorySearch>
</Property>
DirectorySearch Reference: http://wixtoolset.org/documentation/manual/v3/xsd/wix/directorysearch.html
FileSearch Reference: http://wixtoolset.org/documentation/manual/v3/xsd/wix/filesearch.html
This will put the full path to the file in the PROPFILESEARCHEXISTINGDLL property. From here you can then reference this property when placing the file on disk. (Assuming of course your have permissions to write to that location...)
Related
I've built a inproc com server dll which I can package as 1 file or many via the build utility py2exe. When I allow all the dependencies to remain external, I have no issues, but bundling as 1 file produces problems.
When the dll is utilized (either registering it or instantiating a com object from it), it immediately loads MSVCR90.DLL from the path c:\windows\winsxs\x86_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6871_none_50944e7cbcb706e5\MSVCR90.DLL no matter what I do, I can't change that. There is no information that I can find (using Dependency Walker) to indicate what is causing that to load. It just happens magically...
Then, later on it loads that dll again via an explicit call to LoadLibraryA("MSVCR90.dll") (part of some py2exe black box?), but this time it does not look into the winsxs manifests / directory. Instead it looks to the system path and/or will respect a dll redirection. That's when the problem occurs. If I set the system path to start with c:\windows\winsxs\x86_microsoft.vc90.crt...\ it will load the exact same dll and be happy - but if ANY other file is utilized - inclusive of a copy of the EXACT same dll - but at a different path - then the whole thing blows up. It can't handle using two different files.
How can I fix this? Ideally, I've love to make the initial magic loading of the dll draw upon a private assembly, but no matter what I do with manifests or .dll.local etc it will not respect that until this second dll loading takes place.
Note that with the non-bundled dll (external dependencies) it always uses the winsxs MSVCR90.DLL.
I can "fix" my failure to use the dll by forcing the system path to load the winsxs copy, but that is pretty useless for a deployable com server!
The reason is that you DLL has a manifest that tells the module loader to search also in the SxS storage.
You have several choices
Build your DLL using static linkage. Not using any of the MFC-DLLs (see project settings)
Don't use a side by side manifest for the DLL and still use the MFC DLLs. But beware you have to ship those DLL with your DLL in the local path (see DLL search sequence docs)
Use a later build of VS. Later versions of VS don't use the SxS storage any more and there are no manifests for those DLLs any more.
For the 2. see this article in code project. There is an update for VS-2008 [here].
2
Build your DLL
We are working on a validation script for Kofax Capture 9.0 / 10.0 in VB.NET 3.5.
We know how to create a script using the Admin Module, and how to get it operational.
The problem is that we need to reference a dll, located on a remote machine. (GAC is no option) This dll holds abstract classes we need in each validation script.
Even when putting the dlls locally (copy local), the Validation Module (index.exe) immediately throws the "cannot find reference" exception, even though the project compiled perfectly.
I guess the basic question comes down to: where do we put the dlls, in order for the Validation Module to find them?
The simple answer is to put the dll in the same folder as the application because this is one of the places which .NET will probe when trying to find it. The Validation module is run from the Capture bin directory which will be something like "C:\Program Files (x86)\Kofax\CaptureSS\ServLib\Bin\". This would need to be done on each client using Validation.
If you have a more complicated scenario, you could look implementing the AppDomain.AssemblyResolve Event and using Assembly.LoadFile to get the assembly from a custom location, but the using the bin path is less complicated.
If you end up having further trouble, you can troubleshoot by using the Assembly Binding Log Viewer (Fuslogvw.exe) which can tell you more details about why the assembly failed to load and where .NET tried to search for it. Assembly loading can fail for reasons other than just the path.
For more detail on how .NET loads assemblies, see the following:
How the Runtime Locates Assemblies
Locating the Assembly through Codebases or Probing
We found a solution: add all library files as "links" to the project. (Add --> Existing File --> small arrow next to "Add" --> Add as Link)
This ensures the files are compiled when you build the project. The Kofax Validation Module can now find the files, whereas when referencing the file, it could not. Why it could not, remains a mystery...
I am building a MSI installer with WiX for redistributing my MinGW64 application. This requires to ship MinGW dlls with the executable.
The libstdc6.dll file is not versionned. What is the cleanest approach for this?
I have tried:
Adding a companion file to the dll component, pointing to the main app exe, which is versionned. However, wix complains that I cannot set a companion to a dll
Putting the dll in the main app exe component, but this is discouraged in every wix faq, howto and forum...
Adding attribute DefaultVersion="!(bind.FileVersion.mainapp.exe)" to the dll file element for overriding the (missing) dll version. This seems to work at first glance, but wix is issuing warnings related to this:
C:\Users\Etienne\Documents\coding\ocean\ocean.wxs(116) : warning
LGHT1103 : The DefaultVersion '!(bind.FileVersion.mainapp.exe)' was used
for file 'libstdc6.dll' which has no version. No entry for this file
will be placed in the MsiFileHash table. For unversioned files,
specifying a version that is different from the actual file may result
in unexpected versioning behavior during a repair or while patching.
Version the resource to eliminate this warning.
What is the best way to solve this?
Thanks,
Etienne
IMHO, your best bet is option #2. All the rules assume that executable code would be properly versioned and thus are best deployed as KeyPaths of their respective Components. Since the .dll is not versioned, placing it as a data file with a versioned file is the next best thing.
Although, for the record, versioning the .dll is by far the most optimal solution. :)
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.
I have a .NET 4.0 assembly that needs to be made COM-visible during installation. I'm using WiX (version 3.5), so generating a WiX fragment with heat is the logical option. This works fine, except no matter how I twiddle the knobs on heat, the CodeBase RegistryValues default to "file:///C:/Windows/Microsoft.Net/assembly/GAC_32/..." while the assembly is most certainly not installed in that directory.
The registry entries are added correctly during installation, but point to a non-existent assembly. Sad day. I have this issue even in the simplest possible use case ("heat asm.dll -out asm.wxs").
The options seem to be:
Handle installation of the assembly elsewhere in the WiX project, then manually modify the CodeBase declarations in the fragment before passing it to the rest of the WiX toolchain.
Manually add an Assembly=".net" attribute to the file tag
(http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/net-COM-registration-with-heat-td3606182.html)
Transform the fragment with XSLT (2010-06-03 Accentient Blog “Adding HEAT output to the GAC”)
None of these options are particularly appealing. Is there an automated way to do this that doesn't involve fiddling with XSLT?
Does registration-free COM activation meet your requirements?
Registration-Free Activation of COM Components: A Walkthrough
http://msdn.microsoft.com/en-us/library/ms973913.aspx