2

アンマネージ Win32 C++ DLL に共通の構造があります。

// FirstElemPtrContainer.h
#include "stdafx.h"

typedef unsigned char elem_type; // a byte

typedef struct FirstElemPtrContainer {
    unsigned char num_elems;
    void *allocd_ary;
} FirstElemPtrContainer;

構造体の void* は、割り当てられたバイト配列の最初の要素へのポインターを含むことを意味します。

この定義を使用する DLL は、関数をエクスポートして、構造体を割り当て、設定し、割り当てを解除します。

// The exported allocator function.
extern "C" _declspec(dllexport) 
    FirstElemPtrContainer *BuildStruct(int elem_count)
{
    FirstElemPtrContainer *fepc_ptr = new FirstElemPtrContainer;
    fepc_ptr->num_elems = elem_count;
    elem_type *ary = new elem_type[fepc_ptr->num_elems];
    for (int i = 0; i < fepc_ptr->num_elems; i++)
    {
        ary[i] = ((i + 1) * 5); // multiples of 5
    }
    fepc_ptr->allocd_ary = ary;

    return fepc_ptr;
}

// The exported deallocator function.
extern "C" _declspec(dllexport) void 
    DestroyStruct(FirstElemPtrContainer *fepc_ptr)
{
    delete[] fepc_ptr->allocd_ary;
    delete fepc_ptr;
}

これらは、ネイティブの呼び出し元にとっては問題なく機能します。

C# では、PInvoke を介してこれと同じ構造を記述しようとしています。

[StructLayout(LayoutKind.Sequential)]
public struct FirstElemPtrContainer
{
    public byte num_elems;
    [MarshalAs(UnmanagedType.LPArray, 
        ArraySubType = UnmanagedType.U1, SizeConst = 4)]
    public IntPtr allocd_ary;
}

...そして呼び出しインターフェースを次のように記述します:

public static class Imports
{
    [DllImport("MyLib", CallingConvention = CallingConvention.Winapi)]
    public static extern IntPtr BuildStruct(int elem_count);

    [DllImport("MyLib", CallingConvention = CallingConvention.Winapi)]
    public static extern void DestroyStruct(IntPtr fepc_ptr);
}

今、私は自分のインターフェースを呼び出そうとします:

class Program
{
    const int NUM_ELEMS = 4;
    static void Main(string[] args)
    {
        IntPtr fepc_ptr = Imports.BuildStruct(NUM_ELEMS);
        if ( fepc_ptr == IntPtr.Zero ) 
        {
            Console.WriteLine("Error getting struct from PInvoke.");
            return;
        }

        FirstElemPtrContainer fepc =
            (FirstElemPtrContainer)Marshal.PtrToStructure(fepc_ptr, 
        typeof(FirstElemPtrContainer));
        //...
    }
}

PtrToStructure() を呼び出すと、「タイプ 'MyLibInvoke.FirstElemPtrContainer' のフィールド 'allocd_ary' をマーシャリングできません: マネージド型とアンマネージド型の組み合わせが無効です (Int/UInt は SysInt または SysUInt とペアにする必要があります)」というエラーが表示されます。

特定の数の要素をハードコーディングしたことがわかります。これは、呼び出し元が遵守していると想定します。ArraySubType 句も追加しましたが、違いはないようです。型の不一致の苦情はなぜですか?

4

1 に答える 1

2

構造体は次のように宣言する必要があります。

[StructLayout(LayoutKind.Sequential)]
public struct FirstElemPtrContainer
{
    public byte num_elems;
    public IntPtr allocd_ary;
}

allocd_aryはアンマネージ メモリへのポインターであり、p/invoke マーシャラーによってマーシャリングできないため、この方法で実行する必要があります。

の内容を読むには、allocd_aryを使用できますMarshal.Copy

FirstElemPtrContainer fepc = (FirstElemPtrContainer)Marshal.
    PtrToStructure(fepc_ptr, typeof(FirstElemPtrContainer));
byte[] ary = new byte[fepc.num_elems];
Marshal.Copy(fepc.allocd_ary, ary, 0, ary.Length);

それCallingConvention.Winapiは間違っているので、 を使用する必要があると思いますCallingConvention.Cdecl

于 2012-04-09T18:06:18.727 に答える