Проблема в Pinvoke - PullRequest
       30

Проблема в Pinvoke

2 голосов
/ 16 мая 2011

У меня есть следующая функция в C ++ native dll, и я хочу использовать ее в приложении C #.

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 является структурой, и мне не нужен ее вывод, поэтому я собираю ее как объект

Ответы [ 4 ]

3 голосов
/ 16 мая 2011

Эта ошибка, скорее всего, означает, что у вас проблема с маршалингом.

Вы не показываете нам, что такое тип ErroneousWord, но я предполагаю, что это какой-то класс, определенный в вашем коде C ++.Я предполагаю, что он неправильно маршалируется в .NET object.

Учитывая, что это указатель (или указатель на указатель), попробуйте изменить этот параметр на тип IntPtr для представления указателя.Это не должно иметь значения, поскольку в любом случае вы просто передаете NULL для аргумента, легко представляемого с помощью статического IntPtr.Zero поля .

Возможно, вы также захотите выполнить маршал 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 , чтобы выделить неуправляемую память из вашего процесса, доступную для кода C ++.Убедитесь, что в этом случае вы также вызываете соответствующий метод Marshal.FreeHGlobal для освобождения памяти.

2 голосов
/ 16 мая 2011

Спасибо Коди за ваш ответ, но я хочу сделать отдельный, первый вывод создается 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;
    }
2 голосов
/ 16 мая 2011

Учитывая ответ Коди и комментарии, вы должны будете сделать это следующим образом:

[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.

1 голос
/ 16 мая 2011

Независимо от типа ErroneousWord, вы не можете упорядочить массив как отдельный объект. Если это вообще возможно, чтобы маршал как объект ...

...