Skip to content

Commit 98b9e4c

Browse files
Fix DLL load failure on Windows for paths containing non-ANSI characters (#743)
The JS SDK's native add-on fails to load onnxruntime.dll (and other dependency libraries) when the path contains characters outside the active Windows ANSI code page. Customers running the SDK from a profile directory with non-ascii characters see: ``` Failed to load dependency library: C:\Users\<non-ascii-name>\…\onnxruntime.dll ``` `sdk/js/native/foundry_local_napi.c` used `LoadLibraryA`, which interprets the path bytes as the active ANSI code page. N-API hands us a lossless UTF-8 string, but `LoadLibraryA` then mis-decodes those bytes and the loader returns `ERROR_MOD_NOT_FOUND`. This pull-request routesWindows library loads through LoadLibraryW with a UTF-16 path fetched via napi_get_value_string_utf16. POSIX behaviour is unchanged (dlopen is already UTF-8). --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent daa21d0 commit 98b9e4c

1 file changed

Lines changed: 78 additions & 36 deletions

File tree

sdk/js/native/foundry_local_napi.c

Lines changed: 78 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,15 @@
3434
#define WIN32_LEAN_AND_MEAN
3535
#include <windows.h>
3636
typedef HMODULE lib_handle_t;
37-
#define LIB_OPEN(path) LoadLibraryA(path)
37+
/* Library loads on Windows go through open_lib_from_napi(), which uses
38+
* LoadLibraryW. LoadLibraryA would mangle path bytes that lie outside
39+
* the process's active ANSI code page and fail with ERROR_MOD_NOT_FOUND.
40+
*/
3841
#define LIB_SYM(handle, sym) GetProcAddress(handle, sym)
3942
#define LIB_CLOSE(handle) FreeLibrary(handle)
4043
#else
4144
#include <dlfcn.h>
4245
typedef void* lib_handle_t;
43-
#define LIB_OPEN(path) dlopen(path, RTLD_NOW | RTLD_LOCAL)
4446
#define LIB_SYM(handle, sym) dlsym(handle, sym)
4547
#define LIB_CLOSE(handle) dlclose(handle)
4648
#endif
@@ -219,6 +221,77 @@ static void preload_isolated_openssl(void) {
219221
#endif
220222
}
221223

224+
/* ── Helper: open a library from a napi string path ──────────────────────
225+
*
226+
* On Windows the load uses LoadLibraryW with a UTF-16 path so that paths
227+
* containing any character outside the process's active ANSI code page
228+
* resolve correctly.
229+
*
230+
* Returns the loaded library handle on success. On any failure (bad
231+
* argument, allocation failure, or loader rejection), throws a JS error
232+
* and returns NULL. `error_prefix` scopes the loader error message
233+
* (e.g. "Failed to load core library").
234+
*/
235+
static lib_handle_t open_lib_from_napi(napi_env env, napi_value path_val,
236+
const char* error_prefix) {
237+
size_t len8 = 0;
238+
if (napi_get_value_string_utf8(env, path_val, NULL, 0, &len8) != napi_ok) {
239+
napi_throw_type_error(env, NULL, "library path must be a string");
240+
return NULL;
241+
}
242+
char* path_utf8 = (char*)malloc(len8 + 1);
243+
if (!path_utf8) {
244+
napi_throw_error(env, NULL, "Out of memory");
245+
return NULL;
246+
}
247+
if (napi_get_value_string_utf8(env, path_val, path_utf8, len8 + 1, &len8) != napi_ok) {
248+
free(path_utf8);
249+
napi_throw_error(env, NULL, "Failed to read library path");
250+
return NULL;
251+
}
252+
253+
lib_handle_t handle = NULL;
254+
#ifdef _WIN32
255+
size_t len16 = 0;
256+
if (napi_get_value_string_utf16(env, path_val, NULL, 0, &len16) != napi_ok) {
257+
free(path_utf8);
258+
napi_throw_error(env, NULL, "Failed to read library path");
259+
return NULL;
260+
}
261+
char16_t* path_utf16 = (char16_t*)malloc((len16 + 1) * sizeof(char16_t));
262+
if (!path_utf16) {
263+
free(path_utf8);
264+
napi_throw_error(env, NULL, "Out of memory");
265+
return NULL;
266+
}
267+
if (napi_get_value_string_utf16(env, path_val, path_utf16, len16 + 1, &len16) != napi_ok) {
268+
free(path_utf16);
269+
free(path_utf8);
270+
napi_throw_error(env, NULL, "Failed to read library path");
271+
return NULL;
272+
}
273+
/* char16_t and Windows wchar_t are both 16-bit; cast only at the OS boundary. */
274+
handle = LoadLibraryW((LPCWSTR)path_utf16);
275+
free(path_utf16);
276+
#else
277+
handle = dlopen(path_utf8, RTLD_NOW | RTLD_LOCAL);
278+
#endif
279+
280+
if (!handle) {
281+
char err_msg[1024];
282+
#ifdef _WIN32
283+
DWORD win_err = GetLastError();
284+
snprintf(err_msg, sizeof(err_msg), "%s: %s (LoadLibraryW error %lu)", error_prefix, path_utf8, (unsigned long)win_err);
285+
#else
286+
const char* dl_err = dlerror();
287+
snprintf(err_msg, sizeof(err_msg), "%s: %s (%s)", error_prefix, path_utf8, dl_err ? dl_err : "dlopen failed");
288+
#endif
289+
napi_throw_error(env, NULL, err_msg);
290+
}
291+
free(path_utf8);
292+
return handle;
293+
}
294+
222295
/* ── Helper: clean up loaded libraries on error ───────────────────────── */
223296

224297
static void cleanup_loaded_libs(void) {
@@ -324,54 +397,23 @@ static napi_value napi_load_library(napi_env env, napi_callback_info info) {
324397
napi_value elem;
325398
NAPI_CALL(env, napi_get_element(env, argv[1], i, &elem));
326399

327-
size_t len = 0;
328-
NAPI_CALL(env, napi_get_value_string_utf8(env, elem, NULL, 0, &len));
329-
char* dep_path = (char*)malloc(len + 1);
330-
if (!dep_path) {
331-
cleanup_loaded_libs();
332-
napi_throw_error(env, NULL, "Out of memory");
333-
return NULL;
334-
}
335-
NAPI_CALL(env, napi_get_value_string_utf8(env, elem, dep_path, len + 1, &len));
336-
337-
g_dep_libs[i] = LIB_OPEN(dep_path);
400+
g_dep_libs[i] = open_lib_from_napi(
401+
env, elem, "Failed to load dependency library");
338402
if (!g_dep_libs[i]) {
339-
char err_msg[512];
340-
snprintf(err_msg, sizeof(err_msg),
341-
"Failed to load dependency library: %s", dep_path);
342-
free(dep_path);
343403
cleanup_loaded_libs();
344-
napi_throw_error(env, NULL, err_msg);
345404
return NULL;
346405
}
347-
free(dep_path);
348406
}
349407
}
350408
}
351409
}
352410

353411
/* Load the core library */
354-
size_t core_len = 0;
355-
NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], NULL, 0, &core_len));
356-
char* core_path = (char*)malloc(core_len + 1);
357-
if (!core_path) {
358-
cleanup_loaded_libs();
359-
napi_throw_error(env, NULL, "Out of memory");
360-
return NULL;
361-
}
362-
NAPI_CALL(env, napi_get_value_string_utf8(env, argv[0], core_path, core_len + 1, &core_len));
363-
364-
g_core_lib = LIB_OPEN(core_path);
412+
g_core_lib = open_lib_from_napi(env, argv[0], "Failed to load core library");
365413
if (!g_core_lib) {
366-
char err_msg[512];
367-
snprintf(err_msg, sizeof(err_msg),
368-
"Failed to load core library: %s", core_path);
369-
free(core_path);
370414
cleanup_loaded_libs();
371-
napi_throw_error(env, NULL, err_msg);
372415
return NULL;
373416
}
374-
free(core_path);
375417

376418
/* Resolve function pointers */
377419
g_execute_command = (ExecuteCommandFn)LIB_SYM(g_core_lib, "execute_command");

0 commit comments

Comments
 (0)