简体   繁体   中英

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

I'm trying to use C# to get data from a file using a function in an unmanaged 3rd party dll. The function takes a pointer to a struct as an input, and returns a status flag for the success of the operation (not used in the code below). The 3rd party vendor supplies the following code in C for how to call the dll function:

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)

The output from running this on an example file is:

EVENTS:

EVENT: type = 1, text = storing started, position = 0.000000sec

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

In other words each field in the event_list struct is an array of length two.

In the above code I have simplified the vendor's example code and omitted some things that I deem irrelevant to the present issue.

This I how I have tried to implement the same functionality in 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;
}

If I run this on the same example file as the C code above the event_type and time_stamp vectors in eventList will have length 1, and the event_text will have length 200. The values in these fields will contain correct information for the first event - the one of event_type 1 - as printed out by the C code above. What should be populated in the eventList is of course vectors of length 2 containing the two events, but I have been unsuccessfull in modifying the code to do this. The above code is the only one I have managed to write that populates anything at all in the struct. I have tried to play around with specifying the event_text field as a string instead of a char, but that only results in AccessViolationException errors, probably because I have not implemented it correctly.

Can anyone help me fix the above code so that the eventList is populated correctly?

Thanks!

/Elfendahl

EDIT: Updated C# code with Event structure corrected and attempt to allocate Event[] in C# before passing it to the unmanaged 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;
}

I don't know if the above code looks as poorly formatted on your screen as it does on mine (indents and blank new lines are omitted) but I have been unable to get to look any better.

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM