簡體   English   中英

如何使用指針調用非托管dll填充C#中的結構

[英]How to call unmanaged dll to populate struct in C# using a pointer

我正在嘗試使用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)

在示例文件上運行此命令的輸出是:

活動:

事件:類型= 1,文本=開始存儲,位置= 0.000000sec

事件:類型= 2,文本=存儲停止,位置= 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?

謝謝!

/艾芬達爾

編輯:更新了具有事件結構的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;
}

我不知道上面的代碼在屏幕上的顯示格式是否像在我的屏幕上那樣差(縮進和空白的新行被省略了),但是我一直無法看起來更好。

這是最終解決問題的代碼:

[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幫我弄清楚了!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM