Вызов функций, возвращающих указатель на структуру, из нативной dll - C#
Формулировка задачи:
Тут на форуме уже не раз задавали вопросы, как вызвать функцию из нативной dll. И в случае, если функция возвращает "нормальный" тип, у меня с этим вопросов нет: DllImport и всё такое. Но вот если функция возвращает указатель на интерфейс, вот тут у меня возникли проблемы.
Имеется dll-ка (написана, кажется, на CBuilder), экспортирующая всего 2 функции. Так же имеются примеры работы с этой dll-кой в Delphi и в С++.
На Delphi это выглядит так:
Rtusbapi.pas
Вызываются эти функции так:
unit Rtusbapi;
interface
uses
windows;
type
pSHORT = ^SHORT;
pBYTE = ^BYTE;
type
// структура, задающая режимы ввода данных для модуля USB-3000
INPUT_PARS_USB3000 = packed record // (size=438)
size : WORD;
InputEnabled : BOOL; // флажок разрешение/запрещение ввода данных (только при чтении)
CorrectionEnabled : BOOL; // управление корректировкой входных данных (с АЦП)
InputClockSource : WORD; // источник тактовых импульсов для ввода данных
InputType : WORD; // тип вводимых с модуля даных (АЦП или ТТЛ)
SynchroType : WORD; // тип синхронизации вводимых с модуля даных
SynchroAdType : WORD; // тип аналоговой синхронизации
SynchroAdMode : WORD; // режим аналоговой сихронизации
SynchroAdChannel : WORD; // канал АЦП при аналоговой синхронизации
SynchroAdPorog : SHORT; // порог срабатывания АЦП при аналоговой синхронизации
ChannelsQuantity : WORD; // число активных логических каналов
ControlTable : array [0..127] of WORD; // управляющая таблица с активными логическими каналами
InputFifoBaseAddress : WORD; // базовый адрес FIFO буфера ввода
InputFifoLength : WORD; // длина FIFO буфера ввода
InputRate : double; // тактовая частота ввода данных в кГц
InterKadrDelay : double; // межкадровая задержка в мс
ChannelRate : double; // частота ввода фиксированного логического канала
AdcOffsetCoef : array [0..7] of double; // корректировочные коэф. смещение нуля для АЦП
AdcScaleCoef : array [0..7] of double; // корректировочные коэф. масштаба для АЦП
end;
pINPUT_PARS_USB3000 = ^INPUT_PARS_USB3000;
// структура, задающая режимы вывода данных для модуля USB-3000
OUTPUT_PARS_USB3000 = packed record // (size = 18)
size : WORD;
OutputEnabled : BOOL; // разрешение/запрещение вывода данных
OutputRate : double; // частота вывода данных в кГц
OutputFifoBaseAddress : WORD; // базовый адрес FIFO буфера вывода
OutputFifoLength : WORD; // длина FIFO буфера вывода
end;
pOUTPUT_PARS_USB3000 = ^OUTPUT_PARS_USB3000;
// структура пользовательского ППЗУ
FLASH_USB3000 = packed record // (size = 256)
CRC16 : WORD; // контрольная сумма
size : WORD; // размер структуры
SerialNumber : array [0..8] of BYTE; // серийный номер модуля
Name : array [0..10] of BYTE; // название модуля
Revision : CHAR; // ревизия модуля
DspType : array [0..16] of BYTE; // тип установленного DSP
IsDacPresented : BYTE; // флажок наличия ЦАП
DspClockout : DWORD; // тактовая частота DSP в Гц
AdcOffsetCoef : array [0..7] of single; // корректировочные коэф. смещения нуля для АЦП
AdcScaleCoef : array [0..7] of single; // корректировочные коэф. масштаба для АЦП
DacOffsetCoef : array [0..1] of single; // корректировочные коэф. смещения нуля для ЦАП
DacScaleCoef : array [0..1] of single; // корректировочные коэф. масштаба для ЦАП
ReservedByte : array [0..128] of BYTE; // зарезервировано
end;
pFLASH_USB3000 = ^FLASH_USB3000;
// структура, содержащая информацию о версии драйвера DSP
DSP_INFO_USB3000 = packed record // (size = 18)
Target : array [0..9] of BYTE; // модуль, для которого предназначен данный драйвер DSP
DrvLabel : array [0..5] of BYTE; // метка разработчика драйвера DSP
DspMajor : BYTE; // старшие число версии драйвера DSP
DspMinor : BYTE; // младшее число версии драйвера DSP
end;
pDSP_INFO_USB3000 = ^DSP_INFO_USB3000;
// интерфейс модуля USB3000 `
IRTUSB3000 = class
public
// USB интерфейс модуля
Function OpenDevice(VirtualSlot : WORD) : boolean; virtual; stdcall; abstract;
Function CloseDevice : boolean; virtual; stdcall; abstract;
Function GetModuleHandle : THandle; virtual; stdcall; abstract;
Function GetUsbSpeed(UsbSpeed : pByte) : boolean; virtual; stdcall; abstract;
Function GetModuleName(ModuleName : pCHAR) : boolean; virtual; stdcall; abstract;
Function GetModuleSerialNumber(SerialNumber : pCHAR) : boolean; virtual; stdcall; abstract;
Function GetAvrVersion(AvrVersion : pCHAR) : boolean; virtual; stdcall; abstract;
Function ReleaseInstance : boolean; virtual; stdcall; abstract;
// функции для работы с DSP модуля
Function RESET_DSP : boolean; virtual; stdcall; abstract;
Function LOAD_DSP(FileName : pCHAR = nil) : boolean; virtual; stdcall; abstract;
Function MODULE_TEST : boolean; virtual; stdcall; abstract;
Function GET_DSP_INFO(DspInfo : pDSP_INFO_USB3000): boolean; virtual; stdcall; abstract;
Function SEND_COMMAND(Command : WORD) : boolean; virtual; stdcall; abstract;
// функции для работы с памятью DSP модуля
Function PUT_VAR_WORD(Address : WORD; Data : SHORT) : boolean; virtual; stdcall; abstract;
Function GET_VAR_WORD(Address : WORD; Data : pSHORT) : boolean; virtual; stdcall; abstract;
Function PUT_DM_WORD(Address : WORD; Data : SHORT) : boolean; virtual; stdcall; abstract;
Function GET_DM_WORD(Address : WORD; Data : pSHORT) : boolean; virtual; stdcall; abstract;
Function PUT_PM_WORD(Address : WORD; Data : DWORD) : boolean; virtual; stdcall; abstract;
Function GET_PM_WORD(Address : WORD; Data : pDWORD) : boolean; virtual; stdcall; abstract;
Function PUT_DM_ARRAY(BaseAddress, NPoints : WORD; Data : pSHORT) : boolean; virtual; stdcall; abstract;
Function GET_DM_ARRAY(BaseAddress, NPoints : WORD; Data : pSHORT) : boolean; virtual; stdcall; abstract;
Function PUT_PM_ARRAY(BaseAddress, NPoints : WORD; Data : pDWORD) : boolean; virtual; stdcall; abstract;
Function GET_PM_ARRAY(BaseAddress, NPoints : WORD; Data : pDWORD) : boolean; virtual; stdcall; abstract;
// функции для работы в режиме ввода данных
Function GET_INPUT_PARS(ap : pINPUT_PARS_USB3000) : boolean; virtual; stdcall; abstract;
Function SET_INPUT_PARS(ap : pINPUT_PARS_USB3000) : boolean; virtual; stdcall; abstract;
Function START_READ : boolean; virtual; stdcall; abstract;
Function STOP_READ : boolean; virtual; stdcall; abstract;
Function READ_KADR(Data : pSHORT) : boolean; virtual; stdcall; abstract;
Function READ_SAMPLE(Channel : WORD; Sample : pSHORT) : boolean; virtual; stdcall; abstract;
Function ReadData(lpBuffer : pSHORT; nNumberOfWordsToRead, lpNumberOfBytesRead : pDWORD; lpOverlapped : POverlapped) : boolean; virtual; stdcall; abstract;
// функции для работы в режиме вывода данных
Function GET_OUTPUT_PARS(dp : pOUTPUT_PARS_USB3000) : boolean; virtual; stdcall; abstract;
Function SET_OUTPUT_PARS(dp : pOUTPUT_PARS_USB3000) : boolean; virtual; stdcall; abstract;
Function START_WRITE : boolean; virtual; stdcall; abstract;
Function STOP_WRITE : boolean; virtual; stdcall; abstract;
Function WRITE_SAMPLE(Channel : WORD; Sample : pSHORT) : boolean; virtual; stdcall; abstract;
Function WriteData(lpBuffer : pSHORT; nNumberOfWordsToWrite, lpNumberOfBytesWritten : pDWORD; lpOverlapped : POverlapped) : boolean; virtual; stdcall; abstract;
// функции для работы с ТТЛ линиями
Function ENABLE_TTL_OUT(EnabledTtlOut : boolean) : boolean; virtual; stdcall; abstract;
Function TTL_OUT(TtlOut : WORD) : boolean; virtual; stdcall; abstract;
Function TTL_IN(TtlIn : pWORD) : boolean; virtual; stdcall; abstract;
// функции для работы ППЗУ
Function ENABLE_FLASH_WRITE(EnableFlashWrite : boolean) : boolean; virtual; stdcall; abstract;
Function PUT_FLASH(fi : pFLASH_USB3000) : boolean; virtual; stdcall; abstract;
Function GET_FLASH(fi : pFLASH_USB3000) : boolean; virtual; stdcall; abstract;
// функция выдачи строки с последней ошибкой
Function GetLastErrorString(lpBuffer : pCHAR; nSize : DWORD) : Integer; virtual; stdcall; abstract;
end;
PIRTUSB3000 = ^IRTUSB3000;
const
// текущая версия библиотеки Rtusbapi.dll
VERMAJOR_RTUSBAPI = $0001;
VERMINOR_RTUSBAPI = $0005;
CURRENT_VERSION_RTUSBAPI = ((VERMAJOR_RTUSBAPI shl 16) or VERMINOR_RTUSBAPI);
// объявление экспортируемых из Rtusbapi.dll функций
Function RtGetDllVersion : DWORD; stdcall;
Function RtCreateInstance(DeviceName : PChar) : Pointer; stdcall;
implementation
Function RtGetDllVersion : DWORD; External 'Rtusbapi.dll'
Function RtCreateInstance(DeviceName : PChar) : Pointer; External 'Rtusbapi.dll'
end.var
// версия библиотеки Rtusbapi.dll
DllVersion : DWORD;
// интерфейс модуля USB3000
pModule : IRTUSB3000;
begin
// проверим версию используемой DLL библиотеки
DllVersion := RtGetDllVersion; // <-- тут всё понятно
if DllVersion <> CURRENT_VERSION_RTUSBAPI then
begin
ErrorString := 'Неверная версия DLL библиотеки Rtusbapi.dll! ' + #10#13 +
' Текущая: ' + IntToStr(DllVersion shr 16) + '.' + IntToStr(DllVersion and $FFFF) + '.' +
' Требуется: ' + IntToStr(CURRENT_VERSION_RTUSBAPI shr 16) + '.' + IntToStr(CURRENT_VERSION_RTUSBAPI and $FFFF) + '.';
TerminateApplication(ErrorString);
end
else WriteLn(' DLL Version --> OK');
// попробуем получить указатель на интерфейс для модуля USB3000
pModule := RtCreateInstance(pCHAR('usb3000')); // <-- а тут на Delphi тоже всё выглядит просто
if pModule = nil then TerminateApplication('Не могу найти интерфейс модуля USB3000!')
else WriteLn(' Module Interface --> OK');
// попробуем обнаружить модуль USB3000 в первых 127 виртуальных слотах
for i := 0 to 126 do
if pModule.OpenDevice(i) then break;
// и т.д.Решение задачи: «Вызов функций, возвращающих указатель на структуру, из нативной dll»
textual
Листинг программы
struct IRTUSB3000Test
{
public IntPtr vtable;
// поля...
}
struct IRTUSB3000TestVTable
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool Method1Delegate(IntPtr self, ushort VirtualSlot);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool Method2Delegate(IntPtr self);
public Method1Delegate OpenDevice;
public Method2Delegate CloseDevice;
// ...
}
IntPtr structPtr = Rtusbapi.RtCreateInstance("usb3000");
IRTUSB3000Test pModule = (IRTUSB3000Test)Marshal.PtrToStructure(structPtr, typeof(IRTUSB3000Test));
IRTUSB3000TestVTable vt = (IRTUSB3000TestVTable)Marshal.PtrToStructure(pModule.vtable, typeof(IRTUSB3000TestVTable));
bool result = vt.OpenDevice(structPtr, 0);
vt.CloseDevice(structPtr);