Приведенный ниже текст подразумевает, что вы обладаете базовыми знаниями о принципе работы потоков и умеете создавать DLL.
Техническая сторона вопроса будет сфокусирована на потоках и функции DllEntryPoint. Функция DllEntryPoint не должна объявляться в ваших Delphi DLL. Фактически, большую часть, если не всю, Delphi DLL будет правильно работать и без вашего явного объявления DllEntryPoint. Тем не менее, я включил данный совет для тех Win32-программистов, которые понимают эту функцию и хотят связать с ней свое функциональное назначение, чтобы оно являлось частью DLL. Чтобы быть более конкрентым, это будет интересно тем программистам, которые хотят вызывать одну и ту же DLL из многочисленных потоков одной программы.
Исходный код данного проекта находится на FTP компании Borland и доступен по адресу:
Данный код также доступен на Compuserve в секции Borland в виде файла BI42.ZIP.
При первом вызове DLL сначала выполняется секция инициализации, расположенная в нижней части кода. При загрузке двух модулей, каждый из которых использует DLL, секция инициализации будет вызвана дважды, для каждого модуля. Вот пример минимального кода Delphi DLL, который компилится, но пока ничего не делает:
library MyDll; // Здесь код// экспорта begin// Расположенный здесь код выполняется в первую// очередь при каждом вызове DLL любым exe-файлом.end. |
Как вы можете здесь увидеть, здесь нет традиционного DLLEntryPoint, имеющегося в стандартных C/C++ DLL. Для тех, кто только начал изучать Win32, я сообщу, что DLLEntryPoint берет начало от функций LibMain и WEP, работающих в Windows 3.1. LibMain и WEP теперь считаются устаревшими, вместо них необходимо использовать DLLEntryPoint.
Для явной установки DLLEntryPoint в Delphi, используйте следующий код-скелет, имеющий преимущество перед переменной DLLProc, объявленной глобально в SYSTEM.PAS:
|
library DllEntry; procedure DLLEntryPoint(Reason: DWORD);begin// Здесь организуется блок Case для Dll_Process_Attach, и др.end; // Здесь реализация экспортируемых функций // экспорт beginif DllProc = nil then beginDllProc := @DLLEntryPoint;DllEntryPoint(Dll_Process_Attach);end;end. |
var .DllProc: Pointer; { Вызывается каждый раз при вызове точки входа DLL } |
DLL_PROCESS_ATTACH = 1; // Программа подключается к DLLDLL_THREAD_ATTACH = 2; // Поток программы подключается к DLLDLL_THREAD_DETACH = 3; // Поток "оставляет" DLLDLL_PROCESS_DETACH = 0; // Exe "отсоединяется" от DLL |
procedure DLLEntryPoint(Reason: DWORD);begincase Reason ofDll_Process_Attach:MessageBox(DLLHandle, 'Подключение процесса', 'Инфо', mb_Ok);Dll_Thread_Attach:MessageBox(DLLHandle, 'Подключение потока', 'Инфо', mb_Ok);Dll_Thread_Detach:MessageBox(DLLHandle, 'Отключение потока', 'Инфо', mb_Ok);Dll_Process_Detach:MessageBox(DLLHandle, 'Отключение процесса', 'Инфо', mb_Ok);end; // caseend; |
function MyFunc: ShortString;external 'DLLENTRY1' name 'MyFunc'; procedure ThreadFunc(P: Pointer); stdcall;varS: array[0..255] of Char;beginStrPCopy(S, MyFunc);MessageBox(Form1.Handle, S, 'Инфо', mb_Ok);end; procedure TForm1.UseThreadClick(Sender: TObject);varThreadID: DWORD;HThread: THandle;beginHThread := CreateThread(nil, 0, @ThreadFunc,nil, 0, ThreadID);if HThread = 0 then ShowMessage('Нет потоков');end; |
var DWORD = Integer; function CreateThread(lpThreadAttributes: Pointer; // атрибуты безопасности потокаdwStackSize: DWORD; // размер стека для потокаlpStartAddress: TFNThreadStartRoutine; // функция потокаlpParameter: Pointer; // аргумент для нового потока dwCreationFlags: DWORD; // флаги созданияvar lpThreadId: DWORD): // Возвращаемый идентификатор потокаTHandle; // Возвращаемый дескриптор потока |
procedure DLLEntryPoint(Reason: DWORD);begincase Reason ofDll_Process_Attach:MessageBox(DLLHandle, 'Процесс подключен', 'Инфо', mb_Ok);Dll_Thread_Attach:MessageBox(DLLHandle, 'Поток подключен', 'Инфо', mb_Ok);Dll_Thread_Detach:MessageBox(DLLHandle, 'Поток отключен', 'Инфо', mb_Ok);Dll_Process_Detach:MessageBox(DLLHandle, 'Процесс отключен', 'Инфо', mb_Ok);end; // caseend; |