60796


こんばんは。質問させてください。

グラフ作成ライブラリ[GR](https://github.com/sciapp/gr)のバインディングをRubyで作成しております。Ruby標準のfiddleというライブラリを使っており、LinuxとMacでは動作します。

ところが、WindowsでDLLファイルを読み込んでくれません。具体的には、

[code]

    require 'fiddle/import'
    
    module M
       extend extend Fiddle::Importer
       dlload File.expand_path('gr/bin/libGR.dll').gsub("/", "\\")
    end
    
[/code]

を実行すると、下記のようなエラーが発生します。

[code]

    Traceback (most recent call last):
            7: from hoge.rb:3:in `<main>'
            6: from hoge.rb:5:in `<module:M>'
            5: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:77:in `dlload'
            4: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:77:in `collect'
            3: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:87:in `block in dlload'
            2: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle.rb:47:in `dlopen'
            1: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle.rb:47:in `new'
    C:/Ruby26-x64/lib/ruby/2.6.0/fiddle.rb:47:in `initialize': No such file or directory (Fiddle::DLError)
            5: from hoge.rb:3:in `<main>'
            4: from hoge.rb:5:in `<module:M>'
            3: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:77:in `dlload'
            2: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:77:in `collect'
            1: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:86:in `block in dlload'
    C:/Ruby26-x64/lib/ruby/2.6.0/fiddle/import.rb:89:in `rescue in block in dlload': can't load C:\Users\kojix2\(中略)gr\bin\libgr.dll (Fiddle::DLError)
    
[/code]

全く同じようなコードでruby-ffiを使用する場合はとくに問題なく読み込みます。

[code]

    require 'ffi'
    
    module M
       extend FFI::Library
       ffi_lib File.expand_path('gr/bin/libGR.dll').gsub("/", "\\")
    end
    
[/code]

環境  
* Windows10  
* Ruby 2.6.5 (Rubyinstaller) + DevKit

他のDLLファイルを使って、私のWindows +
Ruby環境でfiddleで読み込めるDLLファイルが存在することは確認しています。ひょっとするとlibGR.dll固有の問題もあるかもしれません。

WindowsとFiddleを組み合わせた情報が少なくC言語の知識もないため、何をすればよいのかわからず困っています。推測・憶測・断片的な情報でかまいませんので、どのような順番で何をチェックしていけばよいか教えてくださると助かります。

よろしくお願いします。


CC BY-SA 4.0
2019-11-23T09:03:03.177
60784
2020-03-19T16:16:05.803
2019-11-24T00:24:47.970
4236
32772
question
ruby windows dll
Windows の Ruby の fiddle で lib○○.dll が読み込めない時、何をチェックすればよいでしょうか？


コメントありがとうございます。  
自己解決しました。英語版のstackoverflowで、dllが他のdllに依存しており、そのために読み込めないのではないか、DLLを読み込む前にSetDllDirectory()を呼んでgr/binのディレクトリを指定してはどうかと言われました。

[code]

    require 'fiddle/import'
    require 'fiddle/types'
    module WinAPI
      extend Fiddle::Importer
      dlload 'kernel32.dll'
      include Fiddle::Win32Types
      extern 'int SetDllDirectory(LPCSTR)'
    end
    WinAPI.SetDllDirectory(File.expand_path('bin', ENV['GRDIR'])
    
[/code]

そこで上記のようにしたところ、動作は成功しました。

(追記)  
他の人からもっと良い方法も教わりました。

RubyInstallerのWikiには DLL Loading という項目があって、それによると  
<https://github.com/oneclick/rubyinstaller2/wiki/For-gem-developers#-dll-
loading>

Rubyの場合、環境変数 `PATH` を設定しただけでは、DLLは読み込まれないようです。そこで `RUBY_DLL_PATH`
設定する必要があるけれども、これはRubyをスタートした時点で読み込まれるため、Rubyのコード内で変更したいとこは、`RubyInstaller.add_dll_directory`
を使用するとのことです。


CC BY-SA 4.0
2019-11-23T12:08:20.463
60788
2020-03-19T16:16:05.803
2020-03-19T16:16:05.803
32772
32772
60784
answer


>
[code]

>     1: from C:/Ruby26-x64/lib/ruby/2.6.0/fiddle.rb:47:in `new'
>  
[/code]

の先はネイティブのC言語コードになっています。`new`の実体は[`rb_fiddle_handle_initialize`](https://github.com/ruby/ruby/blob/v2_6_5/ext/fiddle/handle.c#L469)で、[その中](https://github.com/ruby/ruby/blob/v2_6_5/ext/fiddle/handle.c#L179)で

[code]

    ptr = dlopen(clib, cflag);
    
[/code]

`dlopen`を呼んています。Windowsには`dlopen`はありませんが、[`fiddle.h`](https://github.com/ruby/ruby/blob/v2_6_5/ext/fiddle/fiddle.h#L30)で

[code]

    #   define dlopen(name,flag) ((void*)LoadLibrary(name))
    
[/code]

フラグを全て無視してWindows APIの[`LoadLibrary`](https://docs.microsoft.com/en-
us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya)呼び出しとなります。

`LoadLibrary`がDLLを検索する順序は[Dynamic-Link Library Search
Order](https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-
library-search-order#search-order-for-desktop-
applications)で説明されています。セキュリティと歴史的経緯により、検索順序は６種類ぐらいあるみたいです。

基本的には _Standard Search Order for Desktop Applications_ の _SafeDllSearchMode is
enabled_ となります。

>   1. The directory from which the application loaded.
>   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 current directory.
>   6. The directories that are listed in the PATH environment variable. Note
> that this does not include the per-application path specified by the **App
> Paths** registry key. The **App Paths** key is not used when computing the
> DLL search path.
>

この1.～6.のいずれかのディレクトリに配置されていれば読み込みに成功します。具体的には、読み込みの直前に **カレントディレクトリを変更する** 、
**環境変数`PATH`にディレクトリを追加する**辺りが可能です。

この他に自己回答されていますが、 **`SetDllDirectory`を呼ぶ**ことで検索順序そのものを変更することでも読み込みできます。


CC BY-SA 4.0
2019-11-24T00:23:21.407
60796
2019-11-24T00:23:21.407
4236
60784
answer