1. ホーム
  2. c#

[解決済み] 実行時に[DllImport]のパスを指定するにはどうしたらいいですか?

2022-04-30 04:53:12

質問内容

実際、私はC++の(動作する)DLLを持っていて、それを私のC#プロジェクトにインポートしてその関数を呼び出したいと思っています。

DLLへのフルパスを指定すると、このように動作します。

string str = "C:\\Users\\userName\\AppData\\Local\\myLibFolder\\myDLL.dll";
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);

問題は、インストール型のプロジェクトなので、実行するコンピュータやセッションによって、ユーザーのフォルダが同じにならないことです(例:pierre, paul, jack, mum, dad, ...)。

だから、私のコードはもう少し一般的なものにしたいのです。

/* 
goes right to the temp folder of the user 
    "C:\\Users\\userName\\AppData\\Local\\temp"
then go to parent folder
    "C:\\Users\\userName\\AppData\\Local"
and finally go to the DLL's folder
    "C:\\Users\\userName\\AppData\\Local\\temp\\myLibFolder"
*/

string str = Path.GetTempPath() + "..\\myLibFolder\\myDLL.dll"; 
[DllImport(str, CallingConvention = CallingConvention.Cdecl)]
public static extern int DLLFunction(int Number1, int Number2);

重要なのは、"DllImport" が DLL のディレクトリに対する "const string" パラメータを欲していることです。

そこで質問です:。 この場合、どうしたらいいのでしょうか?

どのように解決するのですか?

他の回答者の提案に反して、このメソッドでは DllImport 属性は、今でも正しい方法です。

正直なところ、なぜ世界の他の人と同じように 相対 のパスが必要です。確かに、アプリケーションがインストールされるパスは人によって異なりますが、デプロイメントに関しては、基本的にこれが普遍的なルールです。そのため DllImport の仕組みは、このことを念頭に置いて設計されています。

実際、それは DllImport がそれを処理します。これは、便利なマネージド・ラッパー(P/Invokeマーシャラーは、単に LoadLibrary ). これらのルールは、非常に詳細に列挙されています こちら しかし、重要なものはここに抜粋されている。

<ブロッククオート

システムはDLLを検索する前に、次のことを確認します。

  • 同じモジュール名のDLLがすでにメモリにロードされている場合、システムはロードされたDLLを使用します(それがどのディレクトリにあるかは関係ありません)。システムは、DLL を検索しません。
  • DLLがアプリケーションを実行しているWindowsのバージョンの既知のDLLのリストにある場合、システムは既知のDLLのコピー(および既知のDLLの依存DLLがある場合は、そのコピー)を使用します。システムは、DLLを検索しません。

もし SafeDllSearchMode を有効にすると(デフォルト)、検索順序は次のようになります。

  1. アプリケーションがロードされたディレクトリ。
  2. システムディレクトリです。を使用します。 GetSystemDirectory 関数を使用して、このディレクトリのパスを取得します。
  3. 16ビットシステムのディレクトリです。このディレクトリのパスを取得する関数はありませんが、検索されます。
  4. Windowsのディレクトリです。Windowsのディレクトリです。 GetWindowsDirectory 関数を使用して、このディレクトリのパスを取得します。
  5. 現在のディレクトリです。
  6. にリストされるディレクトリは PATH 環境変数を使用します。これには、レジストリキーApp Pathsで指定されたアプリケーションごとのパスは含まれないことに注意してください。App Pathsレジストリキーは、DLL検索パスの計算には使用されません。

つまり、DLLにシステムDLLと同じ名前を付けていない限り(どんな状況でも、明らかにそうすべきではない)、デフォルトの検索順序は、アプリケーションがロードされたディレクトリから検索を開始することになります。インストール時にそこにDLLを配置すれば、見つかるはずです。相対パスを使用すれば、複雑な問題はすべて解決します。

書くだけです。

[DllImport("MyAppDll.dll")] // relative path; just give the DLL's name
static extern bool MyGreatFunction(int myFirstParam, int mySecondParam);

しかし、もしその はない。 が何らかの理由で動作せず、アプリケーションに DLL を別のディレクトリで探させる必要がある場合、デフォルトの検索パスを変更するために SetDllDirectory 機能 .

なお、ドキュメントにあるように

を呼び出した後 SetDllDirectory であれば、標準的なDLL検索パスは

  1. アプリケーションがロードされたディレクトリ。
  2. で指定されたディレクトリは lpPathName パラメータを使用します。
  3. システムディレクトリを指定します。を使用します。 GetSystemDirectory 関数を使用して、このディレクトリのパスを取得します。
  4. 16ビットシステムのディレクトリです。このディレクトリのパスを取得する関数はありませんが、検索されます。
  5. Windowsのディレクトリです。Windowsのディレクトリです。 GetWindowsDirectory 関数を使用して、このディレクトリのパスを取得します。
  6. にリストされるディレクトリは PATH 環境変数を使用します。

つまり、DLLからインポートされた関数を初めて呼び出す前にこの関数を呼び出す限り、DLLの検索に使用されるデフォルトの検索パスを変更することができるのです。もちろん、この関数の利点は、DLLの検索に使われるデフォルトの検索パスを変更できることです。 ダイナミック の値は、実行時に計算されるこの関数に渡されます。これは DllImport 属性を使用する場合、相対パス (DLL の名前のみ) を使用し、新しい検索順序に依存することになります。

この関数をP/Invokeする必要があります。宣言は次のようになります。

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);