Pinvoke中的一个问题

时间:2011-05-16 14:13:54

标签: c# .net interop pinvoke marshalling

我在C ++ native dll中有以下功能,我想在C#app中使用它。

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

我收到了以下异常

  

尝试读取或写入受保护的   记忆。这通常是一个迹象   其他内存已损坏

P.S: ErroneousWord是struct,我不需要它的输出,所以我将它作为对象

编组

4 个答案:

答案 0 :(得分: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来释放内存。

答案 1 :(得分:2)

考虑到Cody的回答和评论,你必须这样做:

[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);

现在要将Output中的字符串值编组到托管内存中,您将执行以下操作:

string outputValue = Marshal.PtrToStringAnsi(Output);

您必须知道TCHAR是Ansi还是Unicode并使用适当的编组。

请记住挂起输出IntPtr,以便将其传递给本机Free方法。

答案 2 :(得分:2)

感谢Cody的答案,但是我想要单独制作,首先输出是由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;
    }

答案 3 :(得分:1)

无论ErroneousWord类型是什么,您都无法将数组编组为单个输出对象。如果有可能作为一个对象编组...