2011-05-16 24 views
2

我在C++ native dll中有以下功能,我想在C#應用程序中使用它。Pinvoke中的一個問題

DWORD __cdecl Foo(
     LPCTSTR    Input, 
     TCHAR**    Output,   
     DWORD    Options,   
     ErroneousWord**  List = NULL, 
     LPDWORD    Count = 0 
     ); 

使用的PInvoke

[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 
     public static extern UInt32 Foo(string InputWord, out string Output, UInt32 Options, out object List,out UInt32 Count); 

調用代碼:

  string output; 
      object dummyError = null; 
      uint dummyCount = 0; 
      uint x = 0; 
      Foo(Text, out output, x | y,out dummyError,out dummyCount); 

我得到了以下異常

試圖讀取或寫入受保護的內存 。這往往是一個跡象 其他內存已損壞

PS: ErroneousWord是結構,我並不需要它的輸出,所以我名帥它作爲對象

+1

什麼是'ErroneousWord'?不管它是什麼,我都無法想象它可以編組成C#對象。那麼'Output'參數呢?什麼是協議的marhsalling呢? – 2011-05-16 14:21:07

+0

輸出是從本機代碼創建的,並且有暴露的函數來釋放它,所以本機代碼分配輸出並公開一個函數以釋放它。 – 2011-05-16 14:31:16

+0

我認爲對於輸出,您將需要使用'Marshal.PtrToStructure'。我對「錯誤的詞」不太樂觀。 – 2011-05-16 14:32:28

回答

3

該錯誤更可能意味着你有一個編組問題。

您不會告訴我們什麼是ErroneousWord類型,但我認爲它是某種在您的C++代碼中定義的類。我的猜測是,它沒有正確編組.NET object

考慮到它是指針(或指向指針的指針),請嘗試將該參數更改爲IntPtr type以代表指針。不要緊,因爲無論如何你只是簡單地傳遞NULL,很容易用靜態IntPtr.Zero field表示。

你可能也想要以相同的方式編組Output。如果將參數更改爲IntPtr類型,則會收到一個指向TCHAR*的指針,然後可以將其傳遞給其他非託管函數,但您認爲合適(例如釋放它)。

試試下面的代碼:

[ 
DllImport("dllName", 
CharSet = CharSet.Unicode, 
CallingConvention = CallingConvention.Cdecl) 
] 
public static extern UInt32 Foo(
           string InputWord, 
           out IntPtr Output,  // change to IntPtr 
           UInt32 Options, 
           out IntPtr List,  // change to IntPtr 
           out UInt32 Count); 

IntPtr output; 
IntPtr dummyError = IntPtr.Zero; 
uint dummyCount = 0; 
uint x = 0; 
Foo(Text, out output, x | y, out dummyError, out dummyCount); 

您可能還需要使用Marshal.AllocHGlobal method從你的過程,是在C++代碼訪問分配非託管內存。確保如果你這樣做,你也可以調用相應的Marshal.FreeHGlobal method來釋放內存。

+0

我覺得很難相信'out string Output'可以工作。 – 2011-05-16 14:34:00

+0

Output參數是從本地dll創建的,dll公開了另一個函數來釋放它,所以爲什麼我需要使用StringBuilder? – 2011-05-16 14:44:24

+0

@Ahmed:Uhh,輸出*由本地DLL創建*那麼爲什麼你將它*返回*到本機DLL?如果你不關心它的返回值,只要將它作爲'IntPtr'類型傳遞,就像你爲'List'做的一樣。把它當作一個不透明的手柄。 – 2011-05-16 14:47:20

1

無論ErroneousWord類型是什麼,都無法將數組編組爲單個輸出對象。如果它是在所有可能的元帥爲對象...

2

由於科迪的回答和評論,你將不得不做這種方式:

[DllImport("dllName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 
extern static UInt32 Foo(string InputWord, out IntPtr Output, UInt32 Options, out IntPtr List, out UInt32 Count); 

我們得到編組在輸出字符串值到管理內存,你會做什麼:

string outputValue = Marshal.PtrToStringAnsi(Output); 

你必須知道,如果TCHAR是ANSI或Unicode和使用適當的元帥。

請記住掛在Output IntPtr上,以便將其傳遞給本機Free方法。

2

感謝科迪爲你的答案,但我想做一個單獨的,第一個輸出由Foo從本地創建,我打電話給FreeFoo釋放由Foo分配的內存。 以下是代碼

[DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 
     public static extern UInt32 Correct(string InputWord, out IntPtr Output, UInt32 Options, out object List,out UInt32 Count); 

     [DllImport("dllname", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)] 
     public static extern void FreeFoo(IntPtr Output); 

} 

要使用它:

public string FooWrapper(string Text) 
    { 
     IntPtr output; 
     object dummyError = null; 
     uint dummyCount = 0; 
     uint x = 0; 
     Foo(Text, out output, x,out dummyError,out dummyCount); 
     string str = Marshal.PtrToStringUni(output); 
     FreeFoo(output); 
     return str; 
    }