Skip to content

Using GR plugins without setting PATH on Windows #489

@mkitti

Description

@mkitti

Currently, we set ENV["PATH"] on Windows since we need to communicate the DLL search path to GR so that it can dynamically load plugins.

ENV["PATH"] = join((lp, get(ENV, "PATH", "")), ';')

The normal Windows search order is specified in the documentation with PATH being sixth in the priority list.

https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#search-order-for-desktop-applications

If SafeDllSearchMode is disabled, the search order is as follows:

The directory from which the application loaded.

  1. The current directory.
  2. The system directory. Use the GetSystemDirectory function to get the path of this directory.
  3. The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
  4. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  5. The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified 6. by the App Paths registry key. The App Paths key is not used when computing the DLL search path.

As @giordano pointed out, this "looks like a bad idea".

The GR plugin loading code is located here:
https://github.com/sciapp/gr/blob/9aa4f55f75101b76ba8e4406b78601e410642cc6/lib/gks/plugin.c#L46-L55

#ifdef _WIN32
  handle = LoadLibrary(pathname);
  if (handle == NULL)
    {
      grdir = gks_getenv("GRDIR");
      if (grdir == NULL) grdir = GRDIR;
      snprintf(grbin, MAXPATHLEN, "%s/bin", grdir);
      SetDllDirectory(grbin);
      handle = LoadLibrary(pathname);
    }

Noticeably, this uses SetDllDirectory which maps to either SetDllDirectoryA or SetDllDirectoryW based on the value of the environment variable GRDIR.

Setting GRDIR works for a single directory. The following works in VSCode after commenting out line 38 of funcptrs.jl above.

julia> using GR

julia> x = 0:2π/360:2π; y = sin.(x);

julia> ENV["GRDIR"] = dirname(dirname(GR.GRPreferences.GR_jll.libGR_path))
"~\\.julia\\artifacts\\e40afe22fcfc9fe1caea4af17a4016e7ef36cd66"

julia> plot(x,y) # No Error

julia> contains(ENV["PATH"], ENV["GRDIR"])
false

If we did not set GRDIR, this error occurs: GKS: svgplugin.dll: can't load library, error 126 (0x7e).

While setting GRDIR for a single directory works, there may be many directories to include in the DLL search path:

julia> length(GR.GRPreferences.GR_jll.LIBPATH_list)
35

One strategy then would be for us to call SetDllDirectoryA directly. This works in VSCode:

julia> using GR

julia> kernel32 = :kernel32
:kernel32

julia> @ccall kernel32.SetDllDirectoryA(dirname(GR.GRPreferences.GR_jll.libGR_path)::Cstring)::UInt8
0x01

julia> x = 0:2π/360:2π; y = sin.(x);

julia> plot(x,y) # No error

julia> haskey(ENV, "GRDIR")
false

julia> contains(ENV["PATH"], dirname(dirname(GR.GRPreferences.GR_jll.libGR_path)))
false

We could of course do this for all directories in LIBPATH_list. This also works in VSCode.

julia> using GR

julia> kernel32 = :kernel32
:kernel32

julia> all([@ccall kernel32.SetDllDirectoryA(d::Cstring)::Bool for d in GR.GRPreferences.GR_jll.LIBPATH_list])
true

julia> x = 0:2π/360:2π; y = sin.(x);

julia> plot(x,y)

cc:@t-bltg

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions