Showing posts with label Windows. Show all posts
Showing posts with label Windows. Show all posts

Monday, July 27, 2026

Installing through IAssemblyCache

 

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:

  1.  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'.
  2. extract the public key and store in a *.cer file.
  3. 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.
  4. create a manifest (see below). The entries in the manifest file described the side by side assembly
  5. create a cdf file listing all files in the manifest file
  6. create a cat file from the cdf; e.g. 'makecat.exe Test.cdf'
  7. 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

Note that since Windows 7 an alternative exist using application config files and probing. This works the same as the first option but with a fragile global PATH variable.

 

Saturday, August 16, 2025

Debugging GDI drawing

GDI debugging

 The other day I had to debug a hard to track drawing bug. The application is built with the MFC framework so it still uses GDI on places to draw custom controls.

 The incorrect drawing artifact was displayed after an invocation of 'DrawText' with the flag 'DT_CALCRECT'. This was unexpected since with the flag the function doesn't draw and only measures the size. Eventually I realized that GDI batches invocations so perhaps the buggy overdrawing had already taken place before. What was needed to prove this hypothesis:

  •  suppress GDI's caching mechanism through 'GdiSetBatchLimit'.
  •  use direct drawing; so no memory device context

 With this in place indeed it could be seen that the mistake happened earlier in the code and that the 'DrawText' invocation was merely a flush of the GDI batch.

 Be aware that suppressing  GDI's batch might not always work. When the window where the drawing took place was on the primary monitor the batch mode could be turned off but on the second monitor it still cached its calls.

Sunday, February 20, 2022

Windows spotlight not working

Windows spotlight

 Windows spotlight is responsible for those nice aesthetic pictures on the logon screen. Somehow it was broken on my machine and I was deprived of this feature. Even when one selected the option (under 'settings', 'personalization' 'lock screen'), it would jump to normal picture mode. 

 Google didn't help at first (their search results become worse and worse over the years) until I got the tip from 'Software Test Tips': some necessary background apps where disabled. After enabling the app 'settings' (under 'settings'; 'privacy'; 'background apps') rebooting and waiting some time it works now.

Wednesday, August 25, 2021

Unicode characters and ADO Jet.OleDB

Problem

 The other day we had a problem that databases with Chinese characters couldn't be opened with ADO. I am pretty sure this has worked years ago but that was probably Windows 7 back then. 

Example code


#include "tappch.hpp"
#include <cassert>
#include <comutil.h>
#include <filesystem>
#include <iostream>
#include <string>
#include <tchar.h>

#import "msado15.dll"  rename("EOF", "EndOfFile")

namespace
{
   const _bstr_t     g_bstrEmpty;
   constexpr wchar_t g_szDbTest[]   = _T("WL11-旷场.evxt.mdb");
   constexpr wchar_t g_szProvider[] = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source='");
  
   std::wstring MakeConnectionStringOleDb(const std::filesystem::path& rpthDatabase)
   {
      const std::wstring strConnection = g_szProvider 
                                       + rpthDatabase.wstring()
                                       + _T("';Persist Security Info=False");
                                       
       return strConnection;
   }
}


int main()
{
   HRESULT hr = ::CoInitialize(nullptr);
      
   try
   {
      ADODB::_ConnectionPtr ptrConnection;   
      hr = ptrConnection.CreateInstance(__uuidof(ADODB::Connection));
         
      const _bstr_t bstrConnection = MakeConnectionStringOleDb(g_szDbTest).c_str();
      hr = ptrConnection->Open(bstrConnection, g_bstrEmpty, g_bstrEmpty, ADODB::adConnectUnspecified);
         
      const long n = ptrConnection->State;
         
      hr = ptrConnection->Close();
   }
   catch (const _com_error& re)
   {
      std::cout << re.Description() << std::endl;
   }
      
   ::CoUninitialize();
      
   return 0;
}

 I was trying to track down the bug in Visual Studio by spitting through assembly code of the Windows DLL of ADO and OleDB. Visual Studio tough crashed suddenly and it took all my carefully created breakpoint locations with it. So I gave up and assume a bug.

 

Wednesday, March 24, 2021

FILE_ATTRIBUTE_UNPINNED

GetFileAttributes

 The other day I encountered an assert in some external library code. The code asserted that the high DWORD bits of the file attribute (from GetFileAttributes) should be zero. The file attribute value was 0x00100020. 

 It turned out that the high part comes from a file attribute defined in 'winnt.h':


#define FILE_ATTRIBUTE_UNPINNED             0x00100000

  This file attribute is not described in MSDN (yet). It seems to be related to 'OneDrive'.

Installing through IAssemblyCache

  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 ...