1

C# を使用して、管理されていないサード パーティの dll の関数を使用してファイルからデータを取得しようとしています。この関数は構造体へのポインターを入力として受け取り、操作が成功したことを示すステータス フラグを返します (以下のコードでは使用されていません)。サード パーティ ベンダーは、dll 関数を呼び出す方法について、C で次のコードを提供しています。

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 = 格納開始、position = 0.000000sec

EVENT: type = 2、text = 保存停止、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 つのイベントを含む長さ 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;
}

上記のコードが私の画面と同じように画面上でフォーマットされていないように見えるかどうかはわかりません (インデントと空白の改行は省​​略されています)。

4

1 に答える 1

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とPanosRontogiannisに大いに感謝します!

于 2012-12-13T14:42:55.023 に答える