惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
博客园 - Franky
MyScale Blog
MyScale Blog
L
LangChain Blog
Martin Fowler
Martin Fowler
Recent Announcements
Recent Announcements
Stack Overflow Blog
Stack Overflow Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 司徒正美
量子位
A
About on SuperTechFans
C
Check Point Blog
大猫的无限游戏
大猫的无限游戏
Last Week in AI
Last Week in AI
小众软件
小众软件
Apple Machine Learning Research
Apple Machine Learning Research
I
InfoQ
V
Visual Studio Blog
Vercel News
Vercel News
B
Blog
爱范儿
爱范儿
aimingoo的专栏
aimingoo的专栏
U
Unit 42

The Old New Thing

Why do Microsoft job levels start in the high 50's instead of starting at a sane number like 1? - The Old New Thing Why didn't Read­Directory­ChangesW provide a way to correlate the two sides of a rename operation? - The Old New Thing How can I remove the Close button from my window caption? - The Old New Thing Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing What algorithm did Windows XP use to choose your initial user picture? - The Old New Thing A sample use of the winstart.bat file in Windows 95 - The Old New Thing Why don't we allow stacks to be sparse, instead of forcing them to be contiguous? - The Old New Thing What happens if you change a window class's GCL_CB­WND­EXTRA? - The Old New Thing The case of the progress callback that never got called when progress happened - The Old New Thing The perils of binding to value types in XAML - The Old New Thing Microspeak: Funded / unfunded - The Old New Thing AWE does not require PAE, though PAE makes it much more useful - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 2 - The Old New Thing On forcing all derived classes to implement a specific non-virtual method, part 1 - The Old New Thing In the product end game, every change carries significant risk, episode 2 - The Old New Thing Why didn't the Windows Entertainment Pack just run the MS-DOS version inside an emulator? - The Old New Thing Comparing the two holograms on the Windows 95 box - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function, practical exam - The Old New Thing Reducing C++ template bloat by factoring out the type-dependent portions of the function - The Old New Thing On wrapping a callable in a lambda that just calls it with the same parameters - The Old New Thing Why did the Microsoft Entertainment Pack for Windows have a special sticker announcing that it also had Tetris? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing How do functions like alloca allocate memory from the stack? - The Old New Thing A little helper class for managing LPPROC_THREAD_ATTRIBUTE_LISTs - The Old New Thing The comments that go into code versus those that go into the pull request description - The Old New Thing The little-known winstart.bat batch file - The Old New Thing How can I perform a Copy­File&shy in unbuffered mode? - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 5 - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 4 - The Old New Thing Creating a fake agile wrapper that is technically agile but is not useful outside its home apartment, part 3 - The Old New Thing
Forcing an ARM64X executable to run as a specific archite...
Raymond Chen · 2026-08-14 · via The Old New Thing

ARM64X is a fat binary Windows executable and DLL format for 64-bit ARM systems. For DLLs, the choice is clear, since only one of them will work: The version of the DLL that is loaded is the one that matches the host process. If the host process uses the Windows ARM64 ABI, then the ARM64 version of the DLL is used, and if the host process is x86-64-based or uses the Windows ARM64EC ABI¹

For executables, the system has a choice. It could run the process as ARM64 or it could run it as ARM64EC. How can you force the system to choose the architecture you prefer?

You may want to do this if you have a program that is compiled as ARM64X because you have a plug-in model, and you want to be able to support plug-ins that are written either as ARM64 or x86-64. You compile an ARM64 version for ARM64 plug-ins, and you compile an ARM64EC version for x86-64 plug-ins. At run time, you realize that the user passed a plug-in for the other architecture, so you want to relaunch yourself as the matching architecture.

You can do it with the PROC_THREAD_ATTRIBUTE_MACHINE_TYPE attribute.

Here’s a program that takes a DLL on the command line. It tries to load it as the native architecture, but if that fails, and the native architecture is ARM64, then it relaunches itself as x86-64 to try again.

#include <windows.h>
#include <stdio.h>
#include <wil/result_macros.h>
#include <wil/resource.h>
#include <wil/stl.h>
#include <wil/win32_helpers.h>

int wmain(int argc, wchar_t** argv)
{
    if (argc < 2) {
        printf("Oops\n");
        return 0;
    }

    wil::unique_hmodule dll{ LoadLibraryExW(path, nullptr, 0) };
    if (dll) {
        return RunPlugin(dll);
    }

    if (GetLastError() != ERROR_BAD_EXE_FORMAT) {
        printf("Can't load DLL, sorry\n");
        return 0;
    }

    SYSTEM_INFO info{};
    GetSystemInfo(&info);
    if (info.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM64) {
        printf("Can't load DLL, sorry\n");
        return 0;
    }

    printf("Trying again as x86-64\n");

    WORD arch = IMAGE_FILE_MACHINE_AMD64;          
    auto single = make_proc_thread_attribute_list({
        {PROC_THREAD_ATTRIBUTE_MACHINE_TYPE, &arch}
    });                                            

    wchar_t self[MAX_PATH + 1];
    std::wstring self;
    THROW_IF_FAILED(wil::GetModuleFileNameW(nullptr, self));

    wil::unique_process_information pi;

    STARTUPINFOEXW info{ sizeof(STARTUPINFOEXW) };
    info.lpAttributeList = single.get();

    if (!CreateProcessW(self.data(), GetCommandLineW(), nullptr, nullptr,
            false, EXTENDED_STARTUPINFO_PRESENT, nullptr, nullptr,
            &info.StartupInfo, &pi)) {
        printf("Can't relaunch as x86-64, sorry\n");
        return 0;
    }

    WaitForSingleObject(pi.hProcess, INFINITE);
    // destructors will close the handles
}

If we can load the DLL, then great! We run it as usual.

If we can’t load the DLL because it’s in the wrong format, then we will retry as x86-64 if the current process is running as ARM64. To do that, we create an attribute list with the PROC_THREAD_ATTRIBUTE_MACHINE_TYPE attribute whose value is the architecture we want to try, namely AMD64 (which is the Windows name for x86-64), and relaunch ourselves with the same command line.²

If the DLL fails to load even as x86-64, then the x86-64 version of our program just gives up without trying again as ARM64. (You don’t want to have the x86-64 version try again as ARM64 because that would create an infinite loop.)

¹ You can think of ARM64EC as “pre-jitted x86-64 on ARM64.” It is like taking an x86-64 binary and compiling it to ARM64 code that is equivalent to (but presumably has better performance than) the version the emulator would have created on the fly from your x86-64 version. Instead of shipping an x86-64 version that the emulator has to translate to ARM64, just ship the translated version.

² In real life, you probably would add some safety precautions to prevent accidental fork bombs. While writing up this article, I fork bombed my machine a few times by mistake.

Category

Author

Raymond Chen

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.