Как подключить DLL библиотеки к HiAsm?

Компонент CallDLL осуществляет взаимодействие DLL библиотек, написанных пользователем, с другими компонентами схемы, используя любые из всех четырех видов точек входа.

 Взаимодействие программы и DLL библиотеки осуществляется всего 3-мя процедурами:

 Pascal: procedure DllInit(_onEvent,_Data:TdllInitProc; _Param:pointer); cdecl;
 C++: void DllInit(TdllInitProc _onEvent,TdllInitProc _Data,void *_Param)

 где _onEvent - указатель на процедуру вызова событий, _Data - указатель на процедуру получения данных, _Param - поле, содержащее указатель на внутренний класс-обработчик в программе(используется исключительно компонентом CallDLL).

 Pascal: procedure GetVar (var _Data:TValue; Index:word); cdecl;
 C++: void GetVar (TValue &_Data,WORD Index)

 где _Data - переменная, содержащая переданные процедуре данные, Index - индекс точки входа, на которую поступили данные(индекс первой точки равен 0)

 Pascal: procedure doWork (var _Data:TValue; Index:word); cdecl;
 C++: void doWork (TValue &_Data,WORD Index)

 параметры аналогичны параметрам процедуры GetVar.

Типы данных, которые может содержать поле _Data.Value в зависимости от значения в поле _Data.DType:
Тип Описание Pascal C++
0 Пустые данные --- ---
1 Данные целого типа(Integer) integer(Value^) (*(int*)Value)
2 Данные cтрокового типа(String) PChar(Value) (char *)Value
3 Данные действительного типа(Real) Real(Value^) (*(double*)Value)
11 Картинка(Bitmap) PBitmap(Value) ---

 Написание 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), то проверку вставлять не требуется.