2011-05-16 61 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; 
    }