簡體   English   中英

從C ++調用C ++ DLL可以,但是不能從C#調用

[英]Calling C++ DLL from C++ works, but not from C#

我有一個名為tccdvc.dll的DLL,它是此處提供的SDK的一部分:

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)。

我已經在Windows XP SP3(32位)上的Visual Studio 2010中編寫了一個C ++(不是.NET)程序來訪問此tccdvc.dll。 使用提供的.lib文件和使用LoadLibrary / GetProcAddress時,此方法均能正常工作。 我還編寫了一個使用tccdvc.dll的C ++ DLL(我們將其稱為mywrapper.dll),並且又有兩個版本,一個使用.lib文件,另一個使用LoadLibrary / GetProcAddress。 同樣,這很好。 此mywrapper.dll使用__cdecl調用約定。 它包含一個名為InitMyWrapperDLL()的函數,該函數會加載tccdvc.dll。 使用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時,它也會失敗,錯誤代碼為0x8007045a(也稱為1114),表示DLL初始化失敗,並且將mywrapper.dll作為DLL的名稱。 事實證明,失敗的原因是tccdvc.dll,該文件通過mywrapper.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。 在使用C#時,Process Monitor顯示的任何內容都沒有給我任何提示,為什么它失敗了,但是在使用C ++時,卻沒有。

有任何想法嗎? 謝謝!

我為您提供一些建議:

  • 您可以創建一個C ++ / CLI類庫項目,然后在您的C#項目中引用它。
  • 就我而言,我發現UnmanagedFunctionPointerAttribute對於調用至關重要,
  • 在某些情況下,無論我做什么,從C#調用.DLL都不會起作用,只有.LIB對我有用(這意味着要實現我的第一個建議)。 故障排除使我認為“ DLL空間”不適用於該特定庫。

(關於最后一句話:我絕對不是C ++專家,實際上這是我到目前為止所做的唯一項目。這當然值得更多的細節,但是我不知道也沒有知識來找到問題的根源,但是它得到了解決,因為我只需要它 ,感謝您指出任何錯誤/更好的解釋。)

這是適用於我的問題的一些解決方案:

如何使用C#中的C回調? 如果您想查看我所有的代碼,則有指向.DLL的鏈接。

另外,在此領域對我有用的一些工具:

願原力與你同在 :-)

所以,你的C代碼工作從C ++應用程序調用的時候,但是從.NET二進制文件,它在失敗時調用相同的代碼失敗LoadLibrary ...

這只是預感,所以我不確定是否描述您的情況,但是LoadLibrary具有一些復雜的標准來解析相對DLL名稱的路徑,該標准可能會因某些參數而異。 我不了解所有人。 .NET二進制文件的EXE清單中的某些內容可能阻止它根據這些規則查找DLL。 您可以嘗試調整PATH環境變量或使用絕對路徑加載DLL。 (您可以使用GetModuleFileName等來找到您自己的代碼的絕對路徑...)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM