[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;
}
Все работает, но очень уж громоздко получилось.
Подскажите плиз как все это оформить проще? Может есть такие то контструкции которые облегчат этот код?
Здравствуйте, Oxy, Вы писали:
Oxy>Все работает, но очень уж громоздко получилось. Oxy>Подскажите плиз как все это оформить проще? Может есть такие то контструкции которые облегчат этот код?
Самый простой вариант для маршалинга это взять C++/CLI
Здравствуйте, Oxy, Вы писали:
Oxy>То есть для вызова я должен создать структуру, выделить память для pszVersionText размером nVersionTextMax и передать это все методу который заполнит эту структуру данными.
1. Судя по приведенному вами коду, память для pszVersionText выделяете не вы, а функция, которую вы вызываете в DLL, т.к. вы (а) не инициализируете pszVersionText перед вызовом этой функции и (б) у вас в TcVersionInfoA поле pszVersionText указано как IntPtr.
2. Непонятен фокус со структурой TAllocatedString. Зачем она? Не лучше ли для освобождения памяти, выделенной функцией DLL под строку, использовать FreeHGlobal?
Здравствуйте, Fortnum, Вы писали:
F>Здравствуйте, Oxy, Вы писали:
Oxy>>То есть для вызова я должен создать структуру, выделить память для pszVersionText размером nVersionTextMax и передать это все методу который заполнит эту структуру данными.
F>1. Судя по приведенному вами коду, память для pszVersionText выделяете не вы, а функция, которую вы вызываете в DLL, т.к. вы (а) не инициализируете pszVersionText перед вызовом этой функции и (б) у вас в TcVersionInfoA поле pszVersionText указано как IntPtr.
Нет, вы плохо прочитали код. Память я выделил специально вот этой строчкой
F>2. Непонятен фокус со структурой TAllocatedString. Зачем она? Не лучше ли для освобождения памяти, выделенной функцией DLL под строку, использовать FreeHGlobal?
Фокус прост. Я должен выделить для метода буфер под строку и передать ему указатель на этот буфер pszVersionText, а так же размер буфера verInfo.nVersionTextMax = 250; Для унимфикации я создал структуру с одним единственным строковым полем (по сути буфер для строки) и дальше не забочусь об кодировках и прочих моментах. Иначе мне пришлось бы делать кучу ручной работы, создавать буфер и прочее.
F>
Здравствуйте, Oxy, Вы писали:
Oxy>Нет, вы плохо прочитали код. Память я выделил специально вот этой строчкой
Звиняюсь, не заметил.
Oxy>Фокус прост. Я должен выделить для метода буфер под строку и передать ему указатель на этот буфер pszVersionText, а так же размер буфера verInfo.nVersionTextMax = 250; Для унимфикации я создал структуру с одним единственным строковым полем (по сути буфер для строки) и дальше не забочусь об кодировках и прочих моментах. Иначе мне пришлось бы делать кучу ручной работы, создавать буфер и прочее.
Ну, не такю уж и кучу... Зато без квазиструктур. Представляете, кому-то если придется в вашем коде разбираться? А если в структуре таких строковых полей 15 штук?
public VersionInfoEx GetVersionEx()
{
TcVersionInfo verInfo = new TcVersionInfo();
verInfo.nVersionTextMax = 250;
verInfo.pszVersionText = Marshal.AllocHGlobal(verInfo.nVersionTextMax * 2);
...
Здравствуйте, Fortnum, Вы писали:
F>Здравствуйте, Oxy, Вы писали:
Oxy>>Нет, вы плохо прочитали код. Память я выделил специально вот этой строчкой
F>Звиняюсь, не заметил.
Oxy>>Фокус прост. Я должен выделить для метода буфер под строку и передать ему указатель на этот буфер pszVersionText, а так же размер буфера verInfo.nVersionTextMax = 250; Для унимфикации я создал структуру с одним единственным строковым полем (по сути буфер для строки) и дальше не забочусь об кодировках и прочих моментах. Иначе мне пришлось бы делать кучу ручной работы, создавать буфер и прочее.
F>Ну, не такю уж и кучу... Зато без квазиструктур. Представляете, кому-то если придется в вашем коде разбираться? А если в структуре таких строковых полей 15 штук? F>
Не спорю, может и зря ввел все это. Но меня больше интересует вопрос как это сделать еще проще. В идеале что бы компилятор автоматом делал большинство работы (как это с обычными строками происходит)
Здравствуйте, Oxy, Вы писали:
Oxy>Не спорю, может и зря ввел все это. Но меня больше интересует вопрос как это сделать еще проще. В идеале что бы компилятор автоматом делал большинство работы (как это с обычными строками происходит)
Написать свой маршалер для структуры TcVersionInfoA (MarshalAs.MarshalType[Ref]).
Маршалер пишется на C#, просто весь код по преобразованию Managed-Unmanaged туда инкапсулируется, и вызов метода становится прозрачным.