Начало » Программирование » Delphi » Использование форм в DLL (Корректный вызов формы и освобождение памяти при закрытии) 
	
		
		
			| Использование форм в DLL [сообщение #3189] | 
			Wed, 27 September 2023 17:00   | 
		 
		
			
				
				
				
					
						  
						LeGO
						 Сообщений: 4 Зарегистрирован: September 2023 
						
					 | 
					Junior Member  | 
					 | 
		 
		 
	 | 
 
	
		 Всем привет, не ругайтесь, но мы используем формы в dll  :d . 
 В общем и целом момент такой:  
 Разрабатываем приложение MDI, дочерние окна(модули) которой содержаться в dll. При нажатии кнопки меню в главном окне соответственно вызывается нужный модуль с формой. Обновление этих dll (если что-то в проекте поменяли), происходит в момент запуска программы (сравниваются версии dll на пользовательском ПК и сервере и, если есть различие, заменяются более свежими). Стоит задача проверять наличие более новой версии dll не только при запуске программы, но и в момент ее работы, при вызове этой dll по нажатию кнопки в меню. Сделали, но есть момент: обновление проходит нормально только если dll с формой до этого ни разу не вызывалась в рамках текущего запуска основной формы. Если же dll хоть раз вызвали, при замене файла - ошибка 5 "Отказано в доступе". В ручную тоже не дает удалять - "Файл занят основным приложением". 
 Накидал тестовый проект с вызовом dll. В самой dll - Форма пустая и одна экспортируемая функция(просто возвращает рандомное число). В основном проекте две кнопки, одна вызывает функцию из этой dll, вторая показывает из нее же форму. Ок, запускаем: при вызове просто функции (программу не закрываем), она отрабатывает, и есть возможность dll удалить вручную. При нажатии на вторую кнопку показываем форму из dll, закрываем ее, и... не можем удалить файл dll. Занят. Чем, не понятно. Freelibrary, CloseHandle, TerminateProcess не помогают. Может кто сталкивался с таким? Что может держать dll и как с этим бороться? 
 
С уважением, LeGO   
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	
		
		
			| Re: Использование форм в DLL [сообщение #3196 является ответом на сообщение #3189] | 
			Thu, 28 September 2023 14:15    | 
		 
		
			
				
				
				
					
						  
						LeGO
						 Сообщений: 4 Зарегистрирован: September 2023 
						
					 | 
					Junior Member  | 
					 | 
		 
		 
	 | 
 
	
		В мониторе ресурсов не видно ничего такого криминального, ну или я не понимаю может чего-то в нем) 
 
Кому интересно попробовать вот исходники 
Код основного приложения: 
Показать скрытый текст 
unit Unit1; 
 
interface 
 
uses 
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
  Dialogs, Vcl.StdCtrls; 
 
type 
  TForm1 = class(TForm) 
    edt1: TEdit; 
    btn1: TButton; 
    edt2: TEdit; 
    btn2: TButton; 
    procedure btn1Click(Sender: TObject); 
    procedure btn2Click(Sender: TObject); 
  private 
    { Private declarations } 
  public 
    { Public declarations } 
  end; 
 
  Tfunc = function(n: integer): integer; 
 
var 
  Form1: TForm1; 
 
implementation 
 
function ShowForm: integer; external 'mylib.dll'; 
{$R *.dfm} 
 
procedure TForm1.btn1Click(Sender: TObject); 
var 
  LibHandle: THandle; 
  i: integer; 
  func: Tfunc; 
begin 
  LibHandle := loadLibrary('mylib.dll'); 
  try 
    if LibHandle <> 0 then 
    begin 
 
      @func := GetProcAddress(LibHandle, 'fn_calc'); 
      if addr(func) <> nil then 
      begin 
        i := strtoint(edt1.text); 
        edt2.text := IntToStr(func(i)); 
        edt1.text := IntToStr(Random(10)); 
      end 
      else 
        ShowMessage('Функция не найдена!'); 
    end 
    else 
      ShowMessage('Библиотека не загружена!'); 
  finally 
    FreeLibrary(LibHandle); 
  end; 
end; 
 
procedure TForm1.btn2Click(Sender: TObject); 
var 
  LibHandle: THandle; 
  i: integer; 
  func: Tfunc; 
begin 
  LibHandle := loadLibrary('mylib.dll'); 
 
  try 
    if LibHandle <> 0 then 
    begin 
      @func := GetProcAddress(LibHandle, 'ShowForm'); 
      if addr(func) <> nil then 
        ShowForm 
      else 
        ShowMessage('Функция не найдена!'); 
    end 
    else 
      ShowMessage('Библиотека не загружена!'); 
  finally 
    FreeLibrary(LibHandle); 
  end; 
end; 
 
end. 
  
Код dll: 
Показать скрытый текст 
library mylib; 
 
{ Important note about DLL memory management: ShareMem must be the 
  first unit in your library's USES clause AND your project's (select 
  Project-View Source) USES clause if your DLL exports any procedures or 
  functions that pass strings as parameters or function results. This 
  applies to all strings passed to and from your DLL--even those that 
  are nested in records and classes. ShareMem is the interface unit to 
  the BORLNDMM.DLL shared memory manager, which must be deployed along 
  with your DLL. To avoid using BORLNDMM.DLL, pass string information 
  using PChar or ShortString parameters. } 
 
uses 
  ShareMem, 
  madExcept, 
  madLinkDisAsm, 
  madListHardware, 
  madListProcesses, 
  madListModules, 
  Windows, 
  Dialogs, 
  ActiveX, 
  SysUtils, 
  Classes, 
  Unit2 in 'Unit2.pas' {Form2}; 
 
{$R *.res} 
 
procedure DLLEntryPoint(Reason: Word); 
begin 
  if Reason = DLL_PROCESS_DETACH then 
    ShowMessage('DETACH'); 
end; 
 
function fn_calc(n: integer): integer; 
begin 
  Result := n * 256 
end; 
 
exports 
  fn_calc, ShowForm; 
 
begin 
  DLLProc := @DLLEntryPoint; 
  DLLEntryPoint(DLL_PROCESS_ATTACH); 
end. 
  
Код формы в dll: 
Показать скрытый текст 
unit Unit2; 
 
interface 
 
uses 
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs; 
 
type 
  TForm2 = class(TForm) 
    procedure FormClose(Sender: TObject; var Action: TCloseAction); 
  private 
    { Private declarations } 
  public 
    { Public declarations } 
  end; 
 
function ShowForm: integer; 
 
var 
  Form2: TForm2; 
 
implementation 
 
{$R *.dfm} 
 
function ShowForm: integer; 
begin 
  Form2 := TForm2.Create(Application); 
  Result := Form2.ShowModal; 
  FreeAndNil(Form2); 
end; 
 
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); 
begin 
  CloseHandle(Form2.Handle); 
  Action := cafree; 
end; 
 
end. 
  
		
		
		[Обновления: Thu, 28 September 2023 17:13] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: Использование форм в DLL [сообщение #3216 является ответом на сообщение #3196] | 
			Fri, 29 September 2023 20:39    | 
		 
		
			
				
				
				
					
						  
						shalamyansky
						 Сообщений: 150 Зарегистрирован: August 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Вы в одном процессе запускаете 2 фреймворка VCL? Сильно. Да что там в одном процессе - в одном потоке! В одном потоке у вас 2 объекта Application, два главных окна, и главное - 2 цикла обработки сообщений. Даже представить трудно, к какую восьмерку или спираль превращаются эти циклы. 
 
Скорее всего, происходит вот что. Внутреннее VCL-приложение, вызванное из dll, никто не закрывает и даже не делает попытки закрыть. WM_QUIT перехватывается внешним приложением, а внутренний цикл так и продолжает крутиться, точнее, ждать у моря погоды. Невзирая ни на какие FreeLibrary, библиотека вряд ли выгрузится, пока точка исполнения находится у нее внутри. А если вдруг и выгрузится, это должно привести к фатальной ошибке. 
 
Дополнительный PostQuitMessage предположительно может помочь решить конкретно эту ситуацию, но другие ожидаемые в количестве проблемы на такой кривой козе не объедешь.
		
		
		
 |  
	| 
		
	 | 
 
 
 |  
	
		
		
			| Re: Использование форм в DLL [сообщение #3217 является ответом на сообщение #3216] | 
			Fri, 29 September 2023 20:48    | 
		 
		
			
				
				
				
					
						  
						shalamyansky
						 Сообщений: 150 Зарегистрирован: August 2022 
						
					 | 
					Senior Member  | 
					 | 
		 
		 
	 | 
 
	
		Для совместного использования памяти и классов вроде как придумали bpl, почему не используете? Советовать не могу, сам никогда с ними не работал. 
 
А если переходить к советам - надежнее и проще во всех смыслах писать отдельные приложения, прописывая только протокол их взаимодействия. Если приложения работают на уровне оконного интерфейса, как у вас, то это делается не просто, а очень просто.
		
		
		[Обновления: Fri, 29 September 2023 20:51] Известить модератора  
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	| 
		
 |  
	
		
		
			| Re: Использование форм в DLL [сообщение #3296 является ответом на сообщение #3189] | 
			Mon, 09 October 2023 11:16    | 
		 
		
			
				
				
				
					
						  
						LeGO
						 Сообщений: 4 Зарегистрирован: September 2023 
						
					 | 
					Junior Member  | 
					 | 
		 
		 
	 | 
 
	
		В основном приложении из таких dll не вызывается никаких функций, там только хранятся формы. Код был выложен из тестового exe, просто для понимания: если вызываешь функцию из dll, то dll потом можно удалить, вызвал форму, закрыл, dll не удаляется. 
 Основное приложение не мной написано, я на его развитии и поддержке сижу, формы в dll сам первый раз увидел, но менять ничего нельзя, такая концепция. 
Кому интересно, в аттаче тестовый проект. 
		
	- 
	
 
	Вложение: DllForm.rar
	 
	(Размер: 1.58MB, Загружено 669 раз)
 
 
		
		
 |  
	| 
		
	 | 
 
 
 |  
	| 
		
 |   
Переход к форуму:
 
 Текущее время: Tue Nov 04 15:45:22 GMT+3 2025 
 Общее время, затраченное на создание страницы: 0.00896 секунд 
 |