[程式設計]關於C#使用DllImport的問題

大家好,小弟目前碰到的問題如下:

前提:1.要寫一個在PDA讀取RFID tag的功能

2.廠商有給RFIDAPI.dll以及Dll檔裡function的說明書

問題開始:

我想要呼叫Dll裡面的SelectTag函式

以下是廠商說明文件對SelectTag函式說明的內容:

----------------------------------------------------------------------------------------

Function Description:

  Reader will automatically select one available tag from field and then read tag ID

Function call:

  long SelectTag(int iTagProtocol,LPTSTR &lpBuf)

Parameter(Input)

  iTagProtocol : refer to TagProtocol Property

Parameter(output)

  lpBuf : tag id string

Return code:

  Refer to Error Code Property

Example code:

LPTSTR strTAG;

int protocol = 6;

long ErrCode = SelectTag(protocol,strTAG);

if (ErrCode == 0) {

}

---------------------------------------------------------------------------------------------

以下是我叫用函式的程式碼片段:

[DllImport("RFIDAPI.dll", EntryPoint = "?SelectTag@@YAJHAAPAG@Z")]

 public static extern Int32 SelectTag(int iTagProtocol, [MarshalAs(UnmanagedType.LPTStr)]ref String lpBuf);

String OutputTagID = "";

int protocol = 6;

Int32 ErrCode = SelectTag(protocol,ref OutputTagID);

----------------------------------------------------------------------------------------------

執行結果出來還是在呼叫SelectTag的地方

Int32 ErrCode = SelectTag(protocol, ref OutputTagID);

跳出NotSupportedException

我在想是在叫用函式時,是不是第二個參數封送不對?

看廠商的說明第二個參數是用來接收Output結果。該怎麼寫才會正確呢?

謝謝大家的幫忙

2 Answers

Rating
  • 生鏽
    Lv 5
    1 decade ago
    Favorite Answer

    從你呼叫的名稱看來,原始的 DLL 是 C++ 作出來的,目前 .NET CF 不能呼叫 C++ 的物件。

    你必須另外建立一個 "C" 型式的 DLL (裡面有 module definition file),當作 wrapper,最終才可以使用這個 RFIDAPI.dll。

    應用程式資料流程會變成

    Your App -- Your wrapper DLL -- RFIDAPI.dll。

    另外要傳回字串有一個更簡單的作法,用 StringBuilder 或是 char[],範例可以看:

    http://www.pinvoke.net/default.aspx/kernel32/GetPr...

    裡面的第四個參數。

    2008-02-25 09:25:59 補充:

    C++ 是可以撰寫 "C" 型式的 DLL,關鍵是不要用

    __declspec(dllexport)

    改成用 module definition file 指定輸出的函數名稱。

    2008-02-25 09:29:14 補充:

    原廠商真是沒有經驗,一般為了讓 DLL 能發揮最大效用 (尤其是要給其他 user 使用的 DLL),我們都會建立 module definition file。這樣無論客戶使用 C / C++ / .NET / eVC / eVB 都可以使用。

    建議請原廠加上 module definition file,要不了他們一小時就可以解決。

    2008-02-26 16:07:08 補充:

    SelectTag 是否有多型?

    2008-02-26 16:15:55 補充:

    我看到 Unitech 的文件了,待我研究一下...

    2008-02-26 17:44:23 補充:

    我自己寫了一個 WIN32 DLL 作測試 (抱歉,機器沒有安裝 Mobile SDK),然後以緩衝區的方式使用作 P/Invoke:

    [DllImport("MyWin32DLL.dll", EntryPoint = "?SelectTag@@YAJHAAPA_W@Z", CharSet=CharSet.Auto)]

    public static extern Int32 SelectTag(int iTagProtocol, [MarshalAs(UnmanagedType.LPTStr)] ref StringBuilder lpBuf);

    2008-02-26 17:46:44 補充:

    呼叫的程式碼:

    int protocol= 6;

    StringBuilder sb = new StringBuilder(256);

    Int32 ErrCode = SelectTag(protocol, ref sb);

    String a = sb.ToString();

    Console.WriteLine(a);

    結果是成功的。我的 DLL 有成功的填寫字串到緩衝區。你試試看。再不行的話,恐怕真的是 .NET CF 的問題。

    2008-02-26 17:48:46 補充:

    註:我的 DLL 的 SelectTag 是依照 Unitech 的原型撰寫:

    long SelectTag(int iTagProtocol,LPTSTR &lpBuf)

    2008-02-28 00:35:47 補充:

    既然用 StringBuilder 可以做,試試看改變 CharSet 來改變傳回的字串內容,因為 Unitech 沒有說明他的字串內碼方式。

    http://msdn2.microsoft.com/en-us/library/7b93s42f....

    2008-02-28 00:54:44 補充:

    你提到 ushort 傳回來的可能是位址,試試看 Marshal.PtrToStringAnsi 或是 Marshal.PtrToStringAuto 等等函數轉回字串。

    Unitech 的 API 設計實在可以改進,假如是由它 allocate 記憶體,可以做成

    LPCTSTR SelectTag(int iTagProtocol)

    假如是我們 allocate 記憶體可設計成

    long SelectTag(int iTagProtocol,LPTSTR lpBuf,size_t bufSize)

    小小抱怨一下。

  • 我還有在DLL中呼叫另一個函式

    [DllImport("RFIDAPI.dll", EntryPoint = "?ActivateReader@@YAJH@Z")]

    public static extern Int32 ActivateReader(int iCom);

    這函式可以被我叫用,執行時也沒錯

    而呼叫了上面所說的函式卻不行,這真的是因為dll是C++所做出來的原因嗎?

    2008-02-27 11:41:04 補充:

    感謝你的回應!! T_T

    你的寫法我之前用過了,他還是在

    Int32 ErrCode = SelectTag(protocol,ref OutputTagID);

    跳出NotSupportException

    找了好久的資料,到現在還是無法除錯....

    你說的.net compact framework應該是有支援的

    http://msdn2.microsoft.com/en-us/library/aa446536....

    看這篇文章的Passing String的部分

    我也不曉得為什麼這樣寫會跳出錯誤

    2008-02-27 13:02:39 補充:

    後來用undname.exe去看SelectTag

    Undecoration of :- "?SelectTag@@YAJHAAPAG@Z"

    is :- "long __cdecl SelectTag(int,unsigned short * &)"

    2008-02-27 13:03:06 補充:

    我改寫成

    [DllImport("RFIDAPI.dll" , EntryPoint = "#13")]

    public static unsafe extern Int32 SelectTag(int iTagProtocol,ref ushort lpBuf);

    ushort OutputTagID = 0;

    int protocol= 6;

    Int32 ErrCode = SelectTag(protocol,ref OutputTagID);

    MessageBox.Show(OutputTagID.ToString());

    2008-02-27 13:04:00 補充:

    改成這樣子可以執行成功,但我讓PDA來讀取我的TAG時,結果回傳MessageBox顯示的是

    56332

    這是Tag字串的位址值嗎?

    2008-02-27 13:54:40 補充:

    [DllImport("RFIDAPI.dll", CharSet = CharSet.Auto, EntryPoint = "?SelectTag@@YAJHAAPAG@Z")]

    public static extern Int32 SelectTag(int iTagProtocol, StringBuilder lpBuf);

    改成如此,可以執行但得到的是亂碼.......

    2008-02-27 13:57:45 補充:

    [DllImport("RFIDAPI.dll", CharSet = CharSet.Auto, EntryPoint = "?SelectTag@@YAJHAAPAG@Z")]

    public static extern Int32 SelectTag(int iTagProtocol,[MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpBuf);

    跟上面情況一樣,執行後得到亂碼

    2008-02-27 14:01:25 補充:

    若用成MarshalAs(UnmanagedType.LPTStr),就是會跳出NotSupportException

    加上ref也是會跳出NotSupportException........

    用StringBuilder應該就不用ref,但為什麼指定LPTStr不行就不知道了.....

Still have questions? Get your answers by asking now.