i21sys/Docu.Работа с DLL/calling-from-BB.odc

Вызов DLL из Блэкбокса
Приводимые ниже примеры используют некоторые из DLL, описанных в документе  СозданиеDLLвБлэкбоксе. Если упоминаемых ниже DLL нет в папке Блэкбокса, нужно выполнить действия, описанные в указанном документе.. 
Для компиляции любого модуля Блэкбокса в нижеследующем тексте нужно выделить текст модуля (или просто ключевого слова MODULE, например, двойным кликом) и выполнить команду меню Dev, Compile Selection.
Как обычно с DLL, сначала для каждой функции, которую требуется вызывать, нужно выяснить:
 ее сигнатуру;
 используемые ею соглашения о передаче параметров (stdcall или ccall);
 ее точное имя или порядковый номер в DLL (имя в списке экспорта DLL может отличаться от имени в соответствующем исходнике).
Последнее делается с помощью такого инструмента, как DependencyWalker.
В общем случае есть два способа загрузить DLL для использования любой программой: статически и динамически.
Статическая загрузка обсуждается в документации Блэкбокса (см. Особенностиплатформы).
Мы только отметим, что наши тесты показывают, что в отличие от сказанного в Особенностиплатформы  можно указывать полный путь для DLL в заголовке интерфейсного модуля.
Например, если anyname.dll еще не находится в папке Блэкбокса, создадим ее, следуя инструкциям в Примере 0 документа СозданиеDLLвБлэкбоксе, из модуля Epse21Dll0. Затем скопируем ее,  например, в папку c:\!\. Затем по очереди скомпилируем следующие два модуля:

MODULE Epse21Dll0Interface ["c:\!\anyname.dll"];
	PROCEDURE  Add* ( a, b: INTEGER ): INTEGER;
END Epse21Dll0Interface.

MODULE  Epse21Dll0Test;
	IMPORT  StdLog, Dll := Epse21Dll0Interface;
	
	PROCEDURE Do*;
		VAR  a, b: INTEGER;
	BEGIN
		a := 1;
		FOR  b := 0  TO  1000  DO
			a := Dll.Add( a, b )
		END;
		StdLog.Int( a );  StdLog.Ln
	END Do;
	
END  Epse21Dll0Test.

Теперь вызовем Epse21Dll0Test.Do и убедимся, что пример работает.
NB Если после выполнения теста сделаны изменения в DLL, нужно будет перезапустить Блэкбокс, чтобы провести тест с новой версией DLL. Это единственый способ выгрузить старые версии DLL, если они были загружены статически. Эта проблема обходится динамической загрузкой.
Динамическая загрузка DLL
Прежде всего, Блэкбокс разрешает только использование соглашений stdcall для динамически загружаемых DLL (на момент написания данного текста, т.е. в марте 2004). Поэтому может понадобиться перекомпилировать DLL с соответствующими опциями компилятора. 
Если исходники недоступны, а DLL использует соглашения ccall, то возможна только статическая загрузка.
Динамическая загрузка имеет следующие преимущества:
1) DLL может находиться где угодно;
2) DLL может быть выгружена без выхода из Блэкбокск  с DLL можно экспериментировать;
3) имена загружаемых DLL и выполняемых процедур могут быть определены во время выполнения.
Предположим, что была создана anyname.dll, как описано в примере 0 в  СозданиеDLLвБлэкбоксе, и скопирована, скажем, в папку c:\!\. Напомним, что anyname.dll содержит функцию со следующей сигнатурой:
	PROCEDURE Add (a, b: INTEGER): INTEGER;.
Следующий модуль показывает, что нужно сделать, чтобы загрузить эту DLL, вызвать Add, а затем выгрузить DLL:

MODULE Epse21DllDyn0;
	IMPORT  SYSTEM,  WinApi,  Log := StdLog;
	
	TYPE  ProcType = PROCEDURE ( a, b: INTEGER ): INTEGER;
	
	PROCEDURE Do*;
		VAR
			dll: WinApi.HINSTANCE;
			MyProc: ProcType;
			a, b, c, res: INTEGER;
	BEGIN
		(* загрузка DLL: *)
		dll := WinApi.LoadLibrary( "anyname.dll" );
		ASSERT( dll # 0, 100 );

		(* получение доступа к нужной функции: *)
		MyProc := SYSTEM.VAL( ProcType, WinApi.GetProcAddress( dll, "Add" ) );
		
		(* работа с функцией: *)
		a := 1;  b := 3;
		c := MyProc( a, b );
		Log.Int( a );  Log.Int( b );  Log.Int( c );  Log.Ln;

		(* выгрузка DLL: *)
		res := WinApi.FreeLibrary( dll )
	END Do;
	
END Epse21DllDyn0.

Epse21DllDyn0.Do
NB  Поскольку полный путь к DLL не указан в инструкции загрузки, ОС ищет DLL по правилам, описанным в документе Особенности платформа.
В команде загрузки путь можно указать следующим образом:
		dll := WinApi.LoadLibrary( "c:\!\anyname.dll" );
Указание процедур посредством их порядковых номеров
MODULE Epse21DllDyn1;
	IMPORT  SYSTEM,  WinApi,  Log := StdLog;
	
	TYPE  ProcType = PROCEDURE ( a, b: INTEGER ): INTEGER;
	
	PROCEDURE Do*;
		VAR
			dll: WinApi.HINSTANCE;
			ord: INTEGER;
			MyProc: ProcType;
			a, b, c, res: INTEGER;
	BEGIN
		(* загрузка DLL: *)
		dll := WinApi.LoadLibrary( "anyname.dll" );
		ASSERT( dll # 0, 100 );

		(* получение доступа к нужной функции: *)
		ord := 1;  (* the required procedure's ordinal *)
		MyProc := SYSTEM.VAL( ProcType, WinApi.GetProcAddress( dll, SYSTEM.VAL( WinApi.PtrSTR, ord ) ) );
		
		(* работа с функцией: *)
		a := 1;  b := 3;
		c := MyProc( a, b );
		Log.Int( a );  Log.Int( b );  Log.Int( c );  Log.Ln;

		(* выгрузка DLL: *)
		res := WinApi.FreeLibrary( dll )
	END Do;
	
END Epse21DllDyn1.

Epse21DllDyn1.Do

Для c должна быть напечатана сумма a и b, т.к. процедура с номером 1  это Add.