Skip to content

Conversation

@rdunnington
Copy link
Contributor

Embedding manifests into .exes as covered in #17406 and #17448 totally works, but is currently using the wrong resource ID when embedding into .dlls. This change ensures that shared libs that embed manifests use the correct resource ID. See this docs page for an overview of the various kinds of resource IDs for manifests and their usage: https://learn.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies-as-a-resource

FYI @squeek502

Repro steps

Below is a simple repro that has a lib.c built as a DLL and a main.c built as an .exe. lib.c is using an API (TaskDialogIndirect) that requires use of a manifest file with a specific dependency declaration (apparently done for back compat reasons).

// lib.c
#include <windows.h>
#include <commctrl.h>

void display_dialog(void)
{
    TASKDIALOG_BUTTON buttons[] = { IDOK, L"Ok" };
    TASKDIALOGCONFIG config = {
        .cbSize = sizeof(TASKDIALOGCONFIG),
        .pszWindowTitle = L"Hello Dialog",
        .pszMainInstruction = L"hello",
        .cButtons = 1,
        .pButtons = buttons,
    };
    TaskDialogIndirect(&config, NULL, NULL, NULL);
}
<!-- lib.manifest -->
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
  <dependency>
    <dependentAssembly>
      <assemblyIdentity language="*" name="Microsoft.Windows.Common-Controls" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" type="win32" version="6.0.0.0"/>
    </dependentAssembly>
  </dependency>
</assembly>
// main.c 
#include <windows.h>

void display_dialog(void);

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    display_dialog();
    return 0;
}

Save the contents of all these into a single directory, then build and run with:

zig.exe build-lib lib.c lib.manifest -lc -lcomctl32 -dynamic
zig.exe build-exe main.c -lc -llib --library-directory .
main.exe

Expected behavior should be that it pops a dialog with the contents "hello" but instead aborts with an "entry point not found" error. You may need to double-click on it via Explorer to get the error dialog to pop - on my machine it just fails silently if run from the commandline.

image

* MSDN documentation page covering what resource IDs manifests should have:
  https://learn.microsoft.com/en-us/windows/win32/sbscs/using-side-by-side-assemblies-as-a-resource
* This change ensures shared libraries that embed win32 manifests use the
  proper ID of 2 instead of 1, which is only allowed for .exes. If the manifest
  uses the wrong ID, it will not be found and is essentially ignored.
@alexrp alexrp requested a review from squeek502 January 6, 2025 04:18
Copy link
Member

@squeek502 squeek502 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, thanks for catching this! I totally missed that documentation.

@alexrp alexrp merged commit a7a5f35 into ziglang:master Jan 6, 2025
10 checks passed
@rdunnington rdunnington deleted the fix-win32-dll-manifest branch January 6, 2025 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants