I wish I knew why: just found it in another discourse topic as an aside, like “(on Linux you have to add RTLD_GLOBAL)”. I think that RTLD_GLOBAL is the default on Apple so it works without any flags.
That’s probably the key for this. On Windows not all symbols are exported by default from a library. Dependency Walker or Depends let you see which symbols are exported.
Other things that at times happens is that some dependency is not in Windows path and therefore can’t be found. The dependency tools mentioned above are your only friends.
Let me just add that how fuzzy this might seems finding dependency hells on Windows is much easier than on *nix.
Unfortunately I do not have access to a Windows machine, but am relying on Github Actions to test the Julia package on Windows.
I have found a work-around, but I wonder if it indicates that there is a simpler solution?
The original problem is that calling the symbols directly fails:
wind_module = @ccall ssc_module_create("windpower"::Cstring)::Ptr{Cvoid}
could not load symbol "ssc_module_create":
The specified procedure could not be found.
However, if I first load the symbol with dlsym then I can call it without an error:
I tested this with setting and getting a parameter in the C module as well and it worked. So I guess the solution is to load all of the C functions in with dlsym?
No, you can’t run Windows executables from inside this shell, this is simply a small Alpine Linux environment where we have some tools for cross-compilation.
You can try using Wine: I once tried running Julia under Wine, it was an… interesting experience.
But if you can reproduce the error on GitHub Actions, you can log into the runners.
I tried the syntax with hdl and ccall on Apple, but maybe I’m doing something wrong?
julia> hdl = Libc.Libdl.dlopen("libssc.dylib")
Ptr{Nothing} @0x00007fb4c0dcf330
julia> @ccall hdl.ssc_module_create("windpower"::Cstring)::Ptr{Cvoid}
ERROR: TypeError: in ccall, expected Symbol, got a value of type Ptr{Nothing}
julia> @ccall $(hdl.ssc_module_create)("windpower"::Cstring)::Ptr{Cvoid}
ERROR: type Ptr has no field ssc_module_create
julia> @ccall ssc_module_create("windpower"::Cstring)::Ptr{Cvoid}
Ptr{Nothing} @0x00007fb4c3fcec30
The last ccall works on Linux and Apple but not Windows.
I marked the ObjectFile check for exported symbols as the solution since it is the simplest way to check the exported symbols using Julia.
I did not know that you could call the library with just the path to library file. Are there any issues with memory leaks using this method as opposed to opening a handle to the library using Libc.Libdl.dlopen (and closing the handle with Libc.Libdl.dlclose)?
I did not compile the Windows dll but the person that did is asking “what compiler flags need to be set for it to export function names?” Do you know the answer to this or a source that I can review? I believe that the issue may be related to compiling with Visual Studio - but I heard that anecdotally.
No. This is what all JLL packages do: they dlopen the library at __init__ time (withoutRTLD_GLOBAL), and then you call functions into them by specifying the names of the libraries (e.g. ccall((:func_name, "lib_path"), ...), or the @ccall equivalent). No need to manually dlclose the libraries.
Note that Libdl is a standard library, you don’t need to qualify it with the Libc submodule of Base, but you need to add Libdl to your dependencies in case you go by this route.
I may be wrong, I’m not super familiar with Windows, but I don’t think this is a compiler flag. In some cases you may need to explicitly mark functions to be exported, see for example winapi - Exporting functions from a DLL with dllexport - Stack Overflow. The fact that compilers on Unix systems export by default all symbols doesn’t encourage me to dig into Windows-specific oddities
So… I have confirmed via logging into the Github Action runner as you suggested that the string path to the library works with ccall on Windows in a Julia REPL (also confirmed on two other Windows machines). But something is up with using ccall and the Windows .dll in a module. I have:
defined the string path variable as global hdl = nothing at the module level.
Then in the calling function I have global hdl = "path/to/ssc.dll" (based off of OS).
But when I @ccall hdl.function_name(...) I get **ERROR:** UndefVarError: function_name not defined.
Is there something wrong with this method? (It works on Linux and Apple). The example in the documentation for calling external C libraries defines the string path as a const, but should I do that if I need to change the string path based on the OS?
Nevermind! I had a triple string block for taking notes on the ccall issues, which turns out was affecting my definition of the string path variable for Windows! Yargh!
Even though it’s 2025, I had a similar problem today, after compiling the dynamic library using GCC in MSYS-UCRT64, everything worked fine . BTW my main julia info is :
Julia Version 1.10.6
Platform Info:
OS: Windows (x86_64-w64-mingw32)
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-15.0.7 (ORCJIT, goldmont)