Skip to content

add initial implementation of foo and main for Windows DLL example#7298

Merged
waruqi merged 14 commits intoxmake-io:devfrom
luadebug:winlinks
Feb 7, 2026
Merged

add initial implementation of foo and main for Windows DLL example#7298
waruqi merged 14 commits intoxmake-io:devfrom
luadebug:winlinks

Conversation

@luadebug
Copy link
Member

@luadebug luadebug commented Feb 5, 2026

    [ 52%]: linking.release test_mingw_dll.exe
    "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC\\14.44.35207\\bin\\HostX64\\x64\\link.exe" -nologo -dynamicbase -nxcompat -machine:x64 -libpath:build\windows\x64\release /opt:ref /opt:icf foo.lib psapi.lib -out:build\windows\x64\release\test_mingw_dll.exe build\.objs\test_mingw_dll\windows\x64\release\src\main.c.obj /implib:build\windows\x64\release\test_mingw_dll.lib
    error: execv(C:\Users\lin\xmake-luadebug\projects\test_windows_links\build\windows\x64\release\test_mingw_dll.exe ) failed(-1073741515): system error 0xC0000135 (STATUS_DLL_NOT_FOUND).
    The application failed to start because the following DLLs were not found:
      - foo.dll
    Please check your PATH environment variable or copy the missing DLLs to the executable directory.

#7176

I am unsure my approach is correct.
Idea was printing them in console instead of using original handler that is up to MessageBox windows GUI.

If policy is turned on so we would acquire MessageBox with error of missing link, otherwise this MessageBox with error of missing link would not appear.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @luadebug, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request adds a practical example for building and linking Windows DLLs using Xmake, which is crucial for Windows development workflows. Beyond the example, it significantly improves the developer experience by implementing detailed error reporting for missing DLLs on Windows, helping users quickly diagnose and resolve common linking issues.

Highlights

  • New Windows DLL Example Project: Introduced a new example project under projects/test_windows_links to demonstrate the creation and usage of Windows Dynamic Link Libraries (DLLs) with Xmake.
  • DLL Export and Import Implementation: Added foo.c which defines and exports a simple foo function using __declspec(dllexport), and main.c which imports and calls this foo function using __declspec(dllimport), also demonstrating Windows API calls like GetProcessMemoryInfo.
  • Xmake Build Configuration for Windows DLLs: Provided an xmake.lua file that configures the build process for the new example, creating a shared library for foo and a binary executable for main, correctly linking them and handling system libraries like psapi.
  • Enhanced Windows DLL Missing Error Reporting: Modified xmake/core/sandbox/modules/os.lua to include a new utility function that parses PE (Portable Executable) headers of Windows binaries. This enhancement allows Xmake to detect and list specific missing DLL dependencies when a STATUS_DLL_NOT_FOUND error (0xC0000135) occurs during program execution, providing more actionable error messages.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • projects/test_windows_links/src/foo.c
    • Added a C source file that defines and exports a foo function, marked for DLL export on Windows.
  • projects/test_windows_links/src/main.c
    • Added a C source file for an executable that imports the foo function from a DLL and includes calls to Windows API functions for process memory information.
  • projects/test_windows_links/xmake.lua
    • Added an Xmake build script to define a shared library target for foo.c and a binary executable target for main.c, specifying dependencies and system links for Windows.
  • xmake/core/sandbox/modules/os.lua
    • Added path module dependency.
    • Implemented _get_missing_dlls function to parse PE headers of Windows executables and identify missing DLL dependencies.
    • Enhanced the sandbox_os.execv function to provide a more informative error message when a Windows executable fails to run due to STATUS_DLL_NOT_FOUND (0xC0000135), listing the specific DLLs that could not be found.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a valuable feature for Windows developers by providing more informative error messages when a DLL is missing. The implementation involves parsing PE file headers, which is a complex task. I've identified a critical issue in the PE parsing logic that affects 32-bit executables, and another potential crash scenario. Additionally, there's a minor formatting issue in the new test code. Once these points are addressed, this will be a solid improvement.

Comment on lines +393 to +399
-- skip standard fields and some windows fields to reach DataDirectories
-- Standard(24/22) + Windows(68/88)
-- PE32: 24 + 68 = 92 bytes from magic to DataDirectories
-- PE32+: 24 + 88 = 112 bytes from magic to DataDirectories
-- Minus magic(2) that we just read
local skip = (is_pe64 and (24 + 88 - 2) or (24 + 68 - 2))
file:seek("cur", skip)
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

There's an error in calculating the skip offset for 32-bit PE files. The size of the standard fields in IMAGE_OPTIONAL_HEADER32 is 28 bytes, not 24 as your comment and code imply. Your calculation is missing the 4-byte BaseOfData field. This will cause the import table to be read from the wrong offset for 32-bit executables. The calculation for 64-bit executables is correct. The comments are also misleading. I suggest correcting the calculation and comments.

                -- skip standard fields and some windows fields to reach DataDirectories
                -- Standard fields: 28 bytes for PE32, 24 for PE32+.
                -- Windows-specific fields: 68 bytes for PE32, 88 for PE32+.
                -- Offset to DataDirectory = size of standard fields + size of windows-specific fields.
                -- We have already read `magic` (2 bytes).
                local skip = (is_pe64 and (24 + 88 - 2) or (28 + 68 - 2))
                file:seek("cur", skip)

Comment on lines +451 to +455
while true do
local b = string.byte(file:read(1))
if b == 0 then break end
table.insert(chars, string.char(b))
end
Copy link
Contributor

Choose a reason for hiding this comment

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

high

This loop for reading the DLL name is not robust against reaching the end of the file. If file:read(1) returns nil, string.byte(nil) will raise an error, crashing the script. You should check if file:read(1) returns a value before passing it to string.byte.

                                    while true do
                                        local char = file:read(1)
                                        if not char or string.byte(char) == 0 then
                                            break
                                        end
                                        table.insert(chars, char)
                                    end

printf("Calling GetProcessMemoryInfo...\n");
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
printf("PageFaultCount: %lu\n", pmc.PageFaultCount);
printf("WorkingSetSize: %lu\n", pmc.WorkingSetSize);
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The pmc.WorkingSetSize is of type SIZE_T, which is a 64-bit unsigned integer on 64-bit Windows. The format specifier %lu is for unsigned long, which is 32-bit on MSVC even in 64-bit builds. This can lead to incorrect output. The correct, portable format specifier for SIZE_T is %zu.

        printf("WorkingSetSize: %zu\n", pmc.WorkingSetSize);

@luadebug luadebug requested a review from waruqi February 6, 2026 08:11
set_kind("shared")
add_files("src/foo.c")

target("test_mingw_dll")
Copy link
Member

Choose a reason for hiding this comment

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

mingw?

Copy link
Member Author

Choose a reason for hiding this comment

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

Well I am confused here as 0xC0000135 error code is probably MSVC only behavior... I am unsure such error MessageBox would appear on MinGW...

Copy link
Member Author

@luadebug luadebug Feb 6, 2026

Choose a reason for hiding this comment

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

image Alrighty so this also works for MinGW on Windows host... I see error MessageBox present as well...


-- execute command with arguments list
-- get missing dlls
local function _get_missing_dlls(program)
Copy link
Member

Choose a reason for hiding this comment

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

Use the same code definition style as other OS-private functions.

Copy link
Member Author

@luadebug luadebug Feb 6, 2026

Choose a reason for hiding this comment

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

Done I guess? %_% 6007059


-- restore error mode
if old_mode then
winos.seterrormode(old_mode)
Copy link
Member

@waruqi waruqi Feb 6, 2026

Choose a reason for hiding this comment

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

I'm unsure whether the error mode will still be followed when the process exits later.

Copy link
Member Author

Choose a reason for hiding this comment

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

If we place print(old_mode) before this if statement it would say old_mode = 3. So it's still worth of doing I guess.

@luadebug luadebug requested a review from waruqi February 6, 2026 12:40
@waruqi
Copy link
Member

waruqi commented Feb 6, 2026

#7302

@waruqi waruqi merged commit 6007059 into xmake-io:dev Feb 7, 2026
38 checks passed
@waruqi waruqi added this to the v3.0.7 milestone Feb 7, 2026
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.

2 participants