2012-02-20 5 views
2

Я хочу передать структуру функции C, и я напишу следующий код.PInvoke DLL в C#

Когда я запустил его, первая функция - Foo1 работает, а затем функция Foo получает исключение. Можете ли вы помочь мне понять, в чем проблема ...

Код C:

typedef struct 
{ 
    int Size; 
    //char *Array; 
}TTest; 

__declspec(dllexport) void Foo(void *Test); 
__declspec(dllexport) int Foo1(); 

void Foo(void *Test) 
{ 
    TTest *X = (TTest *)Test; 
    int i = X->Size; 
    /*for(int i=0;i<Test->Size;Test++) 
    { 
     Test->Array[i] = 127; 
    }*/ 
} 

int Foo1() 
{ 
    return 10; 
} 

C# код:

using System; 
using System.Runtime.InteropServices; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    public class TTest 
    { 
     public int Size; 
    } 

    class Program 
    { 
     [DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)] 
     public static extern void Foo(
      [MarshalAs(UnmanagedType.LPStruct)] 
      TTest lplf // characteristics 
     ); 

     [DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)] 
     public static extern int Foo1(); 

     static void Main(string[] args) 
     { 
      TTest Test = new TTest(); 
      Test.Size = 25; 

      int XX = Program.Foo1(); 
      Program.Foo(Test); 
     } 
    } 
} 
+2

Избавьтесь от '[MarshalAs (UnmanagedType.LPStruct)]' и добавьте 'CallingConvention = CallingConvention.Cdecl' на оба из' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' и это должно быть хорошо. – ildjarn

+0

, пожалуйста, добавьте сведения об исключении на свой вопрос и любую другую информацию, которая могла бы помочь людям ответить на него. – CharlesB

ответ

2

К downvoters: Ответ на этот вопрос решает две проблемы: непосредственная проблема вызывающего соглашения/атрибут MarhsalAs, и проблема, которую он скоро найдет, где его параметр TTest не будет работать, если он примет мое предложение об обращении TTest в структуру.

Ваш родной код просит void*, который на C# является . Сначала вы должны определить TTest как структуру, а не класс. Во-вторых, вы должны изменить объявление о Foo на:

[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] 
public static extern void Foo(IntPtr lplf); 

И третье, вы должны прикрепить TTest используя fixed ключевое слово и передать это указатель на Foo. Если вы используете класс, вы можете использовать Marhsal.StructureToPtr, чтобы получить от вашего TTest.

Это обеспечивает ту же функциональность с обеих сторон, где может быть передан указатель на любой тип. Вы также можете писать перегрузки со всеми типами классов, которые вы хотите использовать, поскольку все они равны void* с внутренней стороны. С помощью структуры ваши параметры будут добавляться с ref.

Что мне интересно, почему ваш нативный код хочет void* вместо TTest*, когда первое, что вы делаете в неуправляемом коде отливают в TTest*. Если вы переключили параметр на TTest*, то предоставление идентичных функций упростится. You декларация стала бы:

[DllImport(@"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)] 
public static extern void Foo(ref TTest lplf); 

И вы могли бы назвать функцию Program.Foo(ref Test);

Если вы используете класс, то ref не требуется, поскольку классы ссылочные типы.

+0

-1 Все эти изменения совершенно не нужны и по-прежнему не могут решить фактическую ошибку, получаемую OP (дисбаланс стека - это мое предположение). – ildjarn

+0

@ildjarn Я добавил CallingConvention и удалил атрибут MarshalAs как часть моего ответа. Это решит проблему. После исправления ошибки он ударит другую ошибку, где его параметр «TTest» не будет работать как «void *». –

+0

"* После исправления ошибки он ударил бы еще одну ошибку, где его параметр' TTest' не будет работать как 'void *'. * «Нет, он не стал бы - C#,' TTest * 'и 'void * 'на 100% идентичны (поскольку' TTest' _ _ current current_ определен). – ildjarn

Смежные вопросы