Правильный маршалинг структур
От: Oxy  
Дата: 10.02.10 13:31
Оценка:
Есть структура

[cсode]
typedef struct
{
LPSTR pszVersionText;
INT nVersionTextMax;
INT nVersionTextLen;
} TcVersionInfoA, *PcVersionInfoA;
[/сcode]
И есть метод из DLL в который передается эта структура

typedef int (__stdcall* TcGetVersionExA) (PcVersionInfoA VInfo);


pszVersionText это ссылка на предварительно выделенный блок памяти размером nVersionTextMax.
То есть для вызова я должен создать структуру, выделить память для pszVersionText размером nVersionTextMax и передать это все методу который заполнит эту структуру данными.
Я сделал уже рабочий вариант типа


public struct TcVersionInfoA
{
    public IntPtr pszVersionText;
    INT nVersionTextMax;
    INT nVersionTextLen;
}

[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct TAllocatedString
{
    [MarshalAs(UnmanagedType.LPStr, SizeConst=250)]
    public string Str;
}

[DllImport(DLLName, EntryPoint = "cGetVersionExW@4", CallingConvention = CallingConvention.StdCall)]
private static extern int cGetVersionEx(IntPtr VInfo);

И делаю такой вызов

public VersionInfoEx GetVersionEx()
{
    TcVersionInfo verInfo = new TcVersionInfo();
    verInfo.pszVersionText = GetStructureBlockInHeap(new TAllocatedString());
    verInfo.nVersionTextMax = 250;

    IntPtr verInfoPtr = GetStructureBlockInHeap(verInfo);
    int status = 0;
    VersionInfoEx res = new VersionInfoEx();
    try
    {
        status = cGetVersionEx(verInfoPtr);
        verInfo = (TcVersionInfo) Marshal.PtrToStructure(verInfoPtr, verInfo.GetType());
        res.pszVersionText = Marshal.PtrToStringUni(verInfo.pszVersionText);
    }
    finally
    {
        Marshal.DestroyStructure(verInfoPtr, verInfo.GetType());
        Marshal.DestroyStructure(verInfo.pszVersionText, typeof(TAllocatedString));
    }
    return res;
}

private IntPtr GetStructureBlockInHeap(object structure)
{
    if (structure == null)
        return IntPtr.Zero;
    int structSizeInBytes = Marshal.SizeOf(structure.GetType());
    // выделить в куче буфер для нашей структуры
    IntPtr structurePtr = Marshal.AllocHGlobal(structSizeInBytes);
    // скопировать .NET структуру в только что выделенный unmanaged кусок памяти
    Marshal.StructureToPtr(structure, structurePtr, false);
    return structurePtr;
}

Все работает, но очень уж громоздко получилось.
Подскажите плиз как все это оформить проще? Может есть такие то контструкции которые облегчат этот код?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.