Windows Side-by-Side
When you want to share your DLL's across applications there are a couple of options:
- put in a directory and add a path variable
- dump in the Windows system directory
- put in the Windows Side-by-Side (WinSxS)
Under Windows XP the first two options were not recommended. The third option was then the way to go but is not very well documented so one may question if it is still recommended.
To put something in the WinSxS one need:
- a manifest
- a signed cat file which lists all the installed components
- one or more DLL's to install
Using Gemini I came up with the following steps:
- Create a certificate in PowerShell with e.g. 'New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=SxSTestCert" -CertStoreLocation "Cert:\CurrentUser\My"'. This creates a SxSTestCert in the personnel section of the certification store. One can check this with 'cermgr.msc'.
- extract the public key and store in a *.cer file.
- create a publicKeyToken with SDK tool pktextract.exe from the just created *.cer file. Use calculated value for in the manifest file. this is a crucial step; with a wrong publicKeyToken one get all kinds of errors.
- create a manifest (see below). The entries in the manifest file described the side by side assembly
- create a cdf file listing all files in the manifest file
- create a cat file from the cdf; e.g. 'makecat.exe Test.cdf'
- sign the cat file with the certificate with signtool. For example use 'signtool.exe sign /s My /n "SxSTestCert" /fd SHA256 /t http://timestamp.digicert.com Test.MyCompany.MyAssembly.cat'
A manifest file may look like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="Test.MyCompany.MyAssembly"
version="1.0.0.0"
processorArchitecture="amd64"
publicKeyToken="91cb7a3ae2229a15"/>
<!-- Add your DLL file here -->
<file name="MyPayload.dll">
</file>
</assembly>
The associated cdf file may look like this:
[CatalogHeader]
Name=Test.MyCompany.MyAssembly.cat
ResultDir=.
PublicVersion=1
CatalogVersion=2
HashAlgorithms=SHA256
[CatalogFiles]
<HASH>Test.MyCompany.MyAssembly.manifest=Test.MyCompany.MyAssembly.manifest
<HASH>MyPayload.dll=MyPayload.dll
Now that you have all files in code you have to invoke 'InstallAssembly'. Note that there is no sxs.lib to link against so the function 'CreateAssemblyCache' must be extracted from 'sxs.dll' through LoadLibrary / GetProcAddress.
#include <winsxs.h>
#include <atlbase.h>
void AssemblyCreate()
{
CComPtr<IAssemblyCache> ptrCache;
HMDULE hModule = ::LoadLibrary(sxs.dll);
using PfCreateAssemblyCache = HRESULT (*) (IAssemblyCache**, DWORD);
PfCreateAssemblyCache pfCreateAssemblyCache = reinterpret_cast<PfCreateAssemblyCache>(::GetProcAddress(hModule, "CreateAssemblyCache"));
HRESULT hr = pfCreateAssemblyCache(&ptrCache, 0);
FUSION_INSTALL_REFERENCE ref = { 0 };
ref.cbSize = sizeof(FUSION_INSTALL_REFERENCE);
ref.dwFlags = 0;
ref.guidScheme = FUSION_REFCOUNT_OPAQUE_STRING_GUID; // Required!
ref.szIdentifier = L"MyTestInstallerApp";
ref.szNonCannonicalData = L"Test Installation";
constexpr wchar_t szManifestFilePath[] = L"Test.MyCompany.MyAssembly.manifest";
hr = ptrCache->InstallAssembly(0, szManifestFilePath, &ref);
if (SUCCEEDED(hr))
{
}
::FreeLibrary(hModule);
}
This creates a side by side package:
- on Windows 10 on C:\Windows\WinSxS and Manifests folders
- on Windows 11 on C:\Windows\WinSxS\Fusion folder