8

ここで利用可能な SDK の一部である tccdvc.dll という DLL があります。

http://www.commell.com.tw/Download/Driver/Industrial%20Peripheral/Driver/MPX-885/MPX-885%20SDK%20(1.2)/SetupCOMMELL%20MPX-885_20100627.rar

DLL は C++ で作成されており、DLL を調べると、リンカー バージョン 6.0 でリンクされていることがわかります。したがって、VC++ 6.0 で作成されたものと推測されます。DLL にはソース コードが付属しておらず、.lib ファイルと .h ファイルのみが付属しています。エクスポートされたすべての関数は、extern "C" (C++ 名マングリングなし) として宣言され、APIENTRY (__stdcall) を使用して宣言されます。

この tccdvc.dll にアクセスするために、Windows XP SP3 (32 ビット) 上の Visual Studio 2010 で C++ (.NET ではない) プログラムを作成しました。これは、提供された .lib ファイルを使用する場合と、LoadLibrary/GetProcAddress を使用する場合の両方で正常に機能します。また、tccdvc.dll を使用する C++ DLL (mywrapper.dll と呼びましょう) も作成しました。これも 2 つのバージョンで、1 つは .lib ファイルを使用し、もう 1 つは LoadLibrary/GetProcAddress を使用します。繰り返しますが、これはうまくいきます。この mywrapper.dll は、__cdecl 呼び出し規約を使用します。これには、tccdvc.dll をロードする InitMyWrapperDLL() という関数が含まれています。LoadLibrary/GetProcAddress を使用する mywrapper.dll のバージョンには、次のようなコードがあります。

typedef int (APIENTRY *TCCPROCTYPE01)();

HMODULE TCCmodule;
TCCPROCTYPE01 Proc_TCC_DVCOpen;

extern "C" __declspec(dllexport) void InitMyWrapperDLL ()
{ TCCmodule = LoadLibrary("tccdvc.dll");
  Proc_TCC_DVCOpen = (TCCPROCTYPE01)GetProcAddress(TCCmodule, "TCC_DVCOpen");
  ...
}

繰り返しますが、C++ フロントエンドを使用すると、これは正常に機能します。ただし、C# (同じマシン上) から呼び出すと、LoadLibrary("tccdvc.dll") 呼び出しは NULL を返します。C# では、次を使用しています。

[DllImport("mywrapper.dll", CallingConvention=CallingConvention.Cdecl, CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="InitMyWrapperDLL")]
private static extern void InitMyWrapperDLL ();

...

InitMyWrapperDLL();

代わりに提供された tccdvc.lib ファイルを使用して mywrapper.dll をコンパイルすると、DLL の初期化が失敗したことを意味するエラー コード 0x8007045a (1114 とも呼ばれます) で失敗し、DLL の名前として mywrapper.dll が与えられます。この失敗は、mywrapper.dll を介してロードされる tccdvc.dll が原因であることが判明しました。

C# で以下を使用しても失敗します。

[DllImport("tcc.dll", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Ansi, ExactSpelling=true, EntryPoint="TCC_DVCOpen")]
private static extern Int32 TCC_DVCOpen ();

...

TCC_DVCOpen();

宣言で「安全でない」も使用しましたが、違いはありませんでした。LoadLibrary() が失敗し、TCC_DVCOpen() にさえ到達しないため、予測可能です。

問題を特定するために、mywrapper.dll の LoadLibrary/GetProcAddress バージョンを再度使用し、C# プログラムに次のコードを追加しました。

[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary (string lpLibFileName);

[DllImport("kernel32.dll")]
private static extern Int32 GetLastError ();

...

IntPtr hdll1 = LoadLibrary("mywrapper.dll");
IntPtr hdll2 = LoadLibrary("tccdvc.dll");
Int32 errcode = GetLastError();

この後、hdll1 には有効な値がありますが、hdll2 は 0 です。.NET 3.5 Framework を使用している場合、GetLastError() は再び 0x8007045a を返しますが、.NET 4.0 を使用している場合、GetLastError() は 0 (ERROR_SUCCESS) を返します。

Sysinternals の Process Monitor を使用して詳細情報を取得したところ、tccdvc.dll が正常に読み取られ、マップされていることがわかりました。Process Monitor が表示するものは、C# を使用している場合に失敗する理由についてのヒントを与えてくれませんが、C++ を使用している場合にはそうではありません。

何か案は?ありがとう!

4

2 に答える 2

1

私はあなたにいくつかの提案があります:

  • C++/CLI クラス ライブラリ プロジェクトを作成し、それを C# プロジェクトで参照できます。
  • 私の場合、 UnmanagedFunctionPointerAttribute が呼び出しに不可欠であることがわかりました。
  • 私が何をしようとも、.DLL への呼び出しが C# から機能せず、.LIB だけが機能するというケースがありました (これは、私の最初の提案を実装することを意味します)。トラブルシューティングの結果、「DLL スペース」がその特定のライブラリには適していないことがわかりました。

(最後の文について: 私は決して C++ の専門家ではありません。実際、これまでに行った唯一のプロジェクトです。これは確かに詳細に値しますが、問題の原因を特定する知識も知識もありませんでしたが、修正されたため、修正されました。私は単にそれが必要でした.間違い/より良い説明を指摘してくれてありがとう.)

私の問題に適用された解決策の一部を次に示します。

C# から C コールバックを使用するには? 私のコードをすべて見たい場合は、.DLL へのリンクがあります。

また、この分野で私に役立ったいくつかのツール:

力があなたと共にありますように:-)

于 2012-07-09T21:27:57.837 に答える
0

したがって、C コードは C++ アプリケーションから呼び出されたときに機能しますが、同じコードが .NET バイナリから呼び出されたときに失敗し、そこで失敗しLoadLibraryます...

これは単なる予感なので、あなたの状況を説明しているかどうかはわかりませんがLoadLibrary、相対 DLL 名のパスを解決するための複雑な基準があり、特定のパラメーターによって異なる場合があります。私は頭の上からそれらすべてを知っているわけではありません。.NET バイナリの EXE マニフェスト内の何かが、これらのルールに従って DLL を見つけることを妨げている可能性があります。環境変数を調整するPATHか、絶対パスで DLL をロードしてみてください。( GetModuleFileNameetc を使用して、独自のコードの絶対パスを見つけることができます...)

于 2012-07-09T22:52:05.350 に答える