|
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
| 2 | +// Licensed under the MIT License. |
| 3 | + |
| 4 | +// == workaround for delay loading of dependencies of onnxruntime.dll == |
| 5 | +// |
| 6 | +// Problem: |
| 7 | +// |
| 8 | +// When onnxruntime.dll uses delay loading for its dependencies, the dependencies are loaded using LoadLibraryEx, |
| 9 | +// which search the directory of process (.exe) instead of this library (onnxruntime.dll). This is a problem for |
| 10 | +// usages of Node.js binding and python binding, because Windows will try to find the dependencies in the directory |
| 11 | +// of node.exe or python.exe, which is not the directory of onnxruntime.dll. |
| 12 | +// |
| 13 | +// Solution: |
| 14 | +// |
| 15 | +// By using the delay load hook `__pfnDliNotifyHook2`, we can intervene the loading procedure by loading from an |
| 16 | +// absolute path. The absolute path is constructed by appending the name of the DLL to load to the directory of |
| 17 | +// onnxruntime.dll. This way, we can ensure that the dependencies are loaded from the same directory as onnxruntime.dll. |
| 18 | +// |
| 19 | +// See also: |
| 20 | +// - https://learn.microsoft.com/en-us/cpp/build/reference/understanding-the-helper-function?view=msvc-170#structure-and-constant-definitions |
| 21 | +// - https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#alternate-search-order-for-unpackaged-apps |
| 22 | +// |
| 23 | +// The DLL DelayLoad hook is only enabled when: |
| 24 | +// - The compiler is MSVC |
| 25 | +// - at least one of USE_WEBGPU or USE_DML is defined |
| 26 | +// |
| 27 | +#if defined(_MSC_VER) && (defined(USE_WEBGPU) || defined(USE_DML)) |
| 28 | + |
| 29 | +#include <Windows.h> |
| 30 | +#include <delayimp.h> |
| 31 | +#include <stdlib.h> |
| 32 | +#include <string> |
| 33 | + |
| 34 | +namespace { |
| 35 | + |
| 36 | +#define DEFINE_KNOWN_DLL(name) {#name ".dll", L#name L".dll"} |
| 37 | + |
| 38 | +constexpr struct { |
| 39 | + const char* str; |
| 40 | + const wchar_t* wstr; |
| 41 | +} known_dlls[] = { |
| 42 | +#if defined(USE_WEBGPU) |
| 43 | + DEFINE_KNOWN_DLL(webgpu_dawn), |
| 44 | +#endif |
| 45 | +#if defined(USE_DML) |
| 46 | + DEFINE_KNOWN_DLL(DirectML), |
| 47 | +#endif |
| 48 | +}; |
| 49 | +} // namespace |
| 50 | + |
| 51 | +FARPROC WINAPI delay_load_hook(unsigned dliNotify, PDelayLoadInfo pdli) { |
| 52 | + if (dliNotify == dliNotePreLoadLibrary) { |
| 53 | + for (size_t i = 0; i < _countof(known_dlls); ++i) { |
| 54 | + if (_stricmp(pdli->szDll, known_dlls[i].str) == 0) { |
| 55 | + // Try to load the DLL from the same directory as onnxruntime.dll |
| 56 | + |
| 57 | + // First, get the path to onnxruntime.dll |
| 58 | + DWORD pathLen = MAX_PATH; |
| 59 | + std::wstring path(pathLen, L'\0'); |
| 60 | + HMODULE moduleHandle = nullptr; |
| 61 | + |
| 62 | + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, |
| 63 | + reinterpret_cast<LPCWSTR>(&delay_load_hook), &moduleHandle); |
| 64 | + |
| 65 | + DWORD getModuleFileNameResult = GetModuleFileNameW(moduleHandle, const_cast<wchar_t*>(path.c_str()), pathLen); |
| 66 | + while (getModuleFileNameResult == 0 || getModuleFileNameResult == pathLen) { |
| 67 | + int ret = GetLastError(); |
| 68 | + if (ret == ERROR_INSUFFICIENT_BUFFER && pathLen < 32768) { |
| 69 | + pathLen *= 2; |
| 70 | + path.resize(pathLen); |
| 71 | + getModuleFileNameResult = GetModuleFileNameW(moduleHandle, const_cast<wchar_t*>(path.c_str()), pathLen); |
| 72 | + } else { |
| 73 | + // Failed to get the path to onnxruntime.dll. In this case, we will just return NULL and let the system |
| 74 | + // search for the DLL in the default search order. |
| 75 | + return NULL; |
| 76 | + } |
| 77 | + } |
| 78 | + |
| 79 | + path.resize(path.rfind(L'\\') + 1); |
| 80 | + path.append(known_dlls[i].wstr); |
| 81 | + |
| 82 | + return FARPROC(LoadLibraryExW(path.c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR)); |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + return NULL; |
| 87 | +} |
| 88 | + |
| 89 | +extern "C" const PfnDliHook __pfnDliNotifyHook2 = delay_load_hook; |
| 90 | + |
| 91 | +#endif |
0 commit comments