如何使用指针调用非托管dll在C#中填充结构

时间:2012-12-12 06:36:20

标签: c# c dll marshalling unmanaged

我正在尝试使用C#在非托管第三方dll中使用函数从文件中获取数据。该函数将指向结构的指针作为输入,并返回操作成功的状态标志(未在下面的代码中使用)。第三方供应商在C中提供以下代码,以了解如何调用dll函数:

DllCaller.h

#pragma pack(1)
struct Event
{
    int event_type;
    double time_stamp;
    char event_text[200];
};

typedef enum Status (*_GetEventList)(struct Event* event_list);

_GetEventList GetEventList;

DllCaller.c

int event_list_cnt;
struct Event* event_list;

hInstLibrary = LoadLibrary(lib_name);

GetEventList = (_DWGetEventList)GetProcAddress(hInstLibrary, "GetEventList);

printf("\nEVENTS:\n");
event_list_cnt = 2;
event_list = malloc(sizeof(struct Event) * event_list_cnt);
GetEventList(event_list);
for(i = 0; i < event_list_cnt; i++)
{
    printf("EVENT: type = %i, text = %s, position = %fsec \n", 
        event_list[i].event_type, event_list[i].event_text, 
        event_list[i].time_stamp);
}
free(event_list);

FreeLibrary(hInstLibrary)

在示例文件上运行此命令的输出是:

事件:

EVENT:type = 1,text =存储已开始,位置= 0.000000sec

EVENT:type = 2,text = stored stopped,position = 110,825682sec

换句话说,event_list结构中的每个字段都是长度为2的数组。

在上面的代码中,我简化了供应商的示例代码,并省略了一些我认为与当前问题无关的内容。

这是我如何尝试在C#中实现相同的功能:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct Event
{
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4)]
    public int[] event_type;

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.R8)]
    public double[] time_stamp;

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 200)]
    public char[] event_text;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Status GetEventList(IntPtr ptrToEventList);

public Event GetEventList()
{
    // this.pDll is a pointer to the dll library.
    IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(this.pDll, "GetEventList");
    GetEventList getEventList = (GetEventList)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEventList));

    int eventListCount;
    this.GetEventListCount(out eventListCount, out errorMessage);

    int mem = Marshal.SizeOf(typeof(Event));

    // The multiplication by two is because I already know that
    // the struct should be populated by two events.
    IntPtr structPtr = Marshal.AllocCoTaskMem(2 * mem);

    Event eventList;

    try
    {
        getEventList(structPtr);
        eventList = (Event)Marshal.PtrToStructure(structPtr , typeof(Event));
    }
    finally
    {
        Marshal.FreeHGlobal(structPtr);
    }

    return eventList;
}

如果我在与上面的C代码相同的示例文件上运行此事件,eventList中的event_type和time_stamp向量将具有长度1,并且event_text将具有长度200.这些字段中的值将包含第一个事件的正确信息 - event_type 1之一 - 由上面的C代码打印出来。在eventList中应该填充的内容当然是包含两个事件的长度为2的向量,但是我在修改代码时没有成功。上面的代码是我设法编写的唯一一个在结构中填充任何内容的代码。我试图将event_text字段指定为字符串而不是char,但这只会导致AccessViolationException错误,可能是因为我没有正确实现它。

任何人都可以帮我修复上面的代码,以便正确填充eventList吗?

谢谢!

/ Elfendahl

编辑: 更新了更正了事件结构的C#代码,并在将其传递给非托管dll之前尝试在C#中分配Event []:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct Event
{
    [MarshalAs(UnmanagedType.I4)]
    public int event_type;

    [MarshalAs(UnmanagedType.R8)]
    public double time_stamp;

    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 200)]
    public char[] event_text;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Status GetEventList(ref Event[] eventList);

public Event[] GetEventList()
{
    // this.pDll is a pointer to the dll library.
    IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(this.pDll, "GetEventList");
    GetEventList getEventList = (GetEventList)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEventList));

    Event[] eventList = new Event[2];
    getEventList(ref eventList);

    return eventList;
}

我不知道上面的代码在屏幕上的格式是否与在我的屏幕上看起来一样(缩进和空白的新行被省略)但是我无法让它看起来更好。

1 个答案:

答案 0 :(得分:0)

这是最终成功的代码:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct Event
{
    [MarshalAs(UnmanagedType.I4)]
    public int event_type;

    [MarshalAs(UnmanagedType.R8)]
    public double time_stamp;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 200)]
    public string event_text;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate Status GetEventList([out]Event[] eventList);

public Event[] GetEventList()
{
    // this.pDll is a pointer to the dll library.
    IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(this.pDll, "GetEventList");
    GetEventList getEventList = (GetEventList)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(GetEventList));

    Event[] eventList = new Event[2];
    getEventList(eventList);

    return eventList;
}

非常感谢leppie和Panos Rontogiannis帮助我弄明白了!