| Как подключить DLL библиотеки к HiAsm? | ||||||||||||||||||||||||
|
Компонент CallDLL осуществляет взаимодействие DLL библиотек, написанных пользователем, с другими компонентами схемы, используя любые из всех четырех видов точек входа. Взаимодействие программы и DLL библиотеки осуществляется всего 3-мя процедурами: Pascal: procedure DllInit(_onEvent,_Data:TdllInitProc;
_Param:pointer); cdecl; где _onEvent - указатель на процедуру вызова событий, _Data - указатель на процедуру получения данных, _Param - поле, содержащее указатель на внутренний класс-обработчик в программе(используется исключительно компонентом CallDLL). Pascal: procedure GetVar (var _Data:TValue;
Index:word); cdecl; где _Data - переменная, содержащая переданные процедуре данные, Index - индекс точки входа, на которую поступили данные(индекс первой точки равен 0) Pascal: procedure doWork (var _Data:TValue;
Index:word); cdecl; параметры аналогичны параметрам процедуры GetVar. Типы данных, которые может содержать поле _Data.Value в зависимости от значения в поле _Data.DType:
Написание DLL библиотек разберем на простом примере: |
||||||||||||||||||||||||
| Pascal (Delphi) | ||||||||||||||||||||||||
Скачать тестовый пример программы и исходный код DLL библиотеки из примера
можно тут(37Kb)
library test;
uses
Windows;
type
TValue = record
DType:byte; //поле, указывающее тип данных
Value:pointer; //указатель на данные
end;
TdllInitProc = procedure (var _Data:TValue; Index:word; Param:pointer); cdecl;
var
Param:pointer; // здесь сохраняем параметр, переданный процедурой DllInit
onEvent:TdllInitProc; // указатель на обработку точек входа "Событие"
Data:TdllInitProc; // указатель на обработку точек входа "Данные"
procedure doWork (var _Data:TValue; Index:word); cdecl;
var dt:TValue;
i:integer;
begin
Data(dt,0,Param); // извлекаем данные с нулевой(т.е. первой в схеме) точки входа
dt.DType := 1; // задаем выходной тип данных - Integer
i := integer(_Data.Value^) * integer(dt.Value^); // вычесляем результат
dt.Value := @i; // записываем результат в выходную переменную
if assigned(onEvent) then
onEvent(dt,0,Param); // вызываем событие 0, если оно доступно, с данными dt
end;
procedure GetVar (var _Data:TValue; Index:word); cdecl;
begin
_Data.DType := 2; // задаем выходной тип данных - String
_data.Value := PChar('Hello world!!!'); // выдаем строку(напомню, что PChar - это указатель)
end;
procedure DllInit(_onEvent,_Data:TdllInitProc; _Param:pointer); cdecl;
begin
Param := _Param; // сохраняем в переменных все переданные нам параметры
onEvent := _onEvent;
Data := _Data;
end;
exports
doWork,
GetVar,
DllInit;
begin
end.
|
||||||||||||||||||||||||
| C++ | ||||||||||||||||||||||||
Скачать тестовый пример программы и исходный код DLL библиотеки из примера
можно тут(34Kb). Пример для языка C++ писался в среде MS Visual Studio
.NET с типом проекта Win32 DLL.
#include "windows.h"
// макрос для экспорта ф-ций из DLL библиотеки
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif
HINSTANCE HInstance;
typedef struct{
BYTE DType; //поле, указывающее тип данных
void *Value; //указатель на данные
}TValue;
typedef void * __cdecl TdllInitProc(TValue &,WORD,void*);
void *Param; // здесь сохраняем параметр, переданный процедурой DllInit
TdllInitProc *onEvent; // указатель на обработку точек входа "Событие"
TdllInitProc *Data; // указатель на обработку точек входа "Данные"
EXPORT void __cdecl doWork (TValue &_Data,WORD Index)
{
TValue dt;
int i;
Data(dt,0,Param); // извлекаем данные с нулевой(т.е. первой в схеме) точки входа
dt.DType = 1; // задаем выходной тип данных - Integer
i = (*(int *)_Data.Value) * (*(int*)dt.Value); // вычесляем результат
dt.Value = &i; // записываем результат в выходную переменную
if( onEvent )
onEvent(dt,0,Param); // вызываем событие 0, если оно доступно, с данными dt
}
EXPORT void __cdecl GetVar (TValue &_Data,WORD Index)
{
_Data.DType = 2; // задаем выходной тип данных - String
_Data.Value = "Hello world!!!"; // выдаем строку
}
EXPORT void __cdecl DllInit(TdllInitProc _onEvent,TdllInitProc _Data,void *_Param)
{
Param = _Param; // сохраняем в переменных все переданные нам параметры
onEvent = _onEvent;
Data = _Data;
}
BOOL APIENTRY DllMain(HINSTANCE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
HInstance = hModule;
return true;
}
|
||||||||||||||||||||||||
| Советы | ||||||||||||||||||||||||
|
В примере, приведенном выше, вы могли заметить, что нет никакой проверки типа входных данных, т.к. я предпологаю, что мне на вход могут поступать только данные типа Integer. На самом же деле никто не гарантирует, что туда не могут поступить данные, к примеру, типа String, и тогда число integer(_Data.Value^) или (*(int*)dt.Value) будут содержать неизвестно что, и как следствие результат получится непредсказуемым. Поэтому, если вы сомневаетесь в том, что вы всегда будете передавать данные нужного формата, то лучше предусмотреть проверку типа в поле _Data.DType и в зависимости от этого разъименовывать указатель, а затем конвертировать(если необходимо) данные в нужный тип. Второй монент связан с использованием процедур Data и onEvent. Так в приведенном примере указатель Data не проверяется на равенство 0, а onEvent - проверяется. Но причина проверки тут иная. Единственный вариант, при котором Data(или onEvent) будет содержать 0, это ваша собственная ошибка при написание кода, потому что компонент CallDLL всегда передает методы обработки точек входа "Данные" и "События". А поэтому, если вы верно написали код и корректно сохранили указатели(строчка onEvent = _onEvent), то проверку вставлять не требуется. |