1

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

调用者.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;

调用者.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)

在示例文件上运行它的输出是:

活动:

事件:类型 = 1,文本 = 存储开始,位置 = 0.000000 秒

事件:类型 = 2,文本 = 存储停止,位置 = 110,825682 秒

换句话说,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 字段指定为字符串而不是字符,但这只会导致 AccessViolationException 错误,可能是因为我没有正确实现它。

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

谢谢!

/埃尔芬达尔

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

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

我不知道上面的代码在您的屏幕上看起来是否像在我的屏幕上一样糟糕(省略了缩进和空白新行),但我一直无法看起来更好。

4

1 回答 1

0

This is the code that finally did the trick:

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

A big thank you to leppie and Panos Rontogiannis who helped me figure it out!

于 2012-12-13T14:42:55.023 回答