Problemas para descarregar .bpl ( UnloadPackage(); )

Delphi

23/08/2008

Olá, quero antes de mais nada dizer que procurei muito antes de postar aqui no forum, e quando digo que procurei, realmetente foi tempo, tem umas duas semanas que estou tentando terminar isso e nao consigo.

Bem vou expor aqui minha situaçao, estou mudando um sistema que desenvolvi em modulos bpl, ou seja cada form da aplicaçao esta em um bpl separado, e [b:54c1d39d9f]O SISTEMA É EM MDI[/b:54c1d39d9f], com o sistema de carga [b:54c1d39d9f]Implicita[/b:54c1d39d9f], para carga [b:54c1d39d9f]Explicita[/b:54c1d39d9f], até ai tudo certo, vou mostrar como fiz até agora.

Como dito acima o sistema é em [b:54c1d39d9f]MDI[/b:54c1d39d9f], e ai esta meu grande problema, pois eu consigo chamar os forms tudo beleza, [b:54c1d39d9f][i:54c1d39d9f]mas preciso descobrir uma forma de descarregar os bpl´s assim que os forms forem fechados, coisa que naum estou conseguindo de forma nenhuma[/b:54c1d39d9f][/i:54c1d39d9f]. Por este motivo que estou postando aqui no forum na tentativa de obter ajuda de vc´s.

vou explicar como esta a forma de chamar os meus forms.

eu cocloquei em uma Unit para ficar mais claro o que eu fiz:

unit uGeral;

interface

uses
  SysUtils;

type
  TModulo = record
    nome  : string;
    modulo: HMODULE;
  end;

var
  carregados: array of TModulo;

procedure descarga(_bpl: string); stdcall;

implementation

procedure descarga(_bpl: string); stdcall;
var
  i: Smallint;
begin
  i := -1;
  for i := Low(carregados) to High(carregados) do
    begin
      if (carregados[i].nome = _bpl) then
        begin
          UnloadPackage(carregados[i].modulo);
        end;
    end;
end;

end.


em cada formulário filho [b:54c1d39d9f]MDIChild[/b:54c1d39d9f], eu criei uma pequena procedure que chama o form:

procedure chamar; stdcall;

exports
  chamar;

implementation

procedure chamar; stdcall;
begin
  if MyFormChild = nil then
    Application.CreateForm(TMyFormChild, MyFormChild)
  else
  with MyFormChild do
    begin
      Show;
      BringToFront;
      WindowState := wsNormal;
    end;
end;


No evento OnClose dos Forms Filhos tenho o seguinte
procedure TMyFormChild.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyFormChild := nil;
  Action := caFree;
end;


no meu form Principal [b:54c1d39d9f]MDIForm[/b:54c1d39d9f], onde faço as chamadas dos filhos [b:54c1d39d9f]MDIChild[/b:54c1d39d9f] tenho assim:

procedure TfrmPrincipal.este011Click(Sender: TObject);
type
  TChamar = procedure; stdcall;
var
  _Chamar: TChamar;
  i: Smallint;
begin
  i := Length(carregados);

  SetLength(carregados, i + 1);
  carregados[i].nome := ´pkgTeste01.bpl´;
  carregados[i].modulo := LoadPackage(carregados[i].nome);

  if carregados[i].modulo > 0 then
    begin
      @_Chamar := GetProcAddress(carregados[i].modulo, ´chamar´);
      _Chamar;
    end;
end;


Se eu chamar a procedure [b:54c1d39d9f]descarga(´pkgTeste01.bpl´);[/b:54c1d39d9f] de um botao qualquer depois que o form for fechado o pacote e descarregado normalmente, porem por exemplo se eu for colocar no evento OnDestroy do formulario que pertence a este bpl, o evento simplesmente nao acontece, nao funciona.

Estou precisando muito disso se alguem puder me dar uma força ficarei muito agradecido. Obrigado desde já... té +++


Godzilla_xf

Godzilla_xf

Curtidas 0

Respostas

Godzilla_xf

Godzilla_xf

23/08/2008

Up...


GOSTEI 0
Romulocpd

Romulocpd

23/08/2008

Olá,

Em um projeto pra Lafarge estamos trabalhando desta forma também. Só não estamos utilizando MDI.

O que fizemos:

Temos um type tipo o seu mas com:

Handle do pacote (HMODULE)
Formulario (TForm)

Guardamos isso não em array mas sim em uma TList que é mmmuiiittoo mais fácil de gerenciar.

E na hora de ´matar´ o objeto (carrego em abas os formulários) primeiro matamos a isntancia do formulario e depois fazemos o unloadpackage. Se não matar o formulario antes tem horas que dá pau!

Bom, tenta ae!


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

Olá, Em um projeto pra Lafarge estamos trabalhando desta forma também. Só não estamos utilizando MDI. O que fizemos: Temos um type tipo o seu mas com: Handle do pacote (HMODULE) Formulario (TForm) Guardamos isso não em array mas sim em uma TList que é mmmuiiittoo mais fácil de gerenciar. E na hora de ´matar´ o objeto (carrego em abas os formulários) primeiro matamos a isntancia do formulario e depois fazemos o unloadpackage. Se não matar o formulario antes tem horas que dá pau! Bom, tenta ae!


Olá, antes de mais nada muito obrigado pela sua atenção, mas estou com uma duvida, pelo que vc me falou preciso guardar o HMODULE certo, mas em que momento do processo eu mandaria descarregar o pacote.

vc poderia me dar uma pequena explanaçao???

se vc pude-se me dar uma ideia mais clara eu ficaria muito grato mesmo. Obrigado desde já.


GOSTEI 0
Romulocpd

Romulocpd

23/08/2008

Depende da forma que trabalha. Geralmente usamos 1 formulario em um determinado pacote. Então quando for fechar este form o módulo deve ser descarregado.

No meu caso eu carrego os forms para abas e tenho o meu TList com estes dados de HMODULE e outros. Assim quando o cara vai fechar a aba, que é um form, eu sei que é a hora de fazer o UnloadPackage. No seu caso tem q ue ver como você trabalha.

Romulo


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

Depende da forma que trabalha. Geralmente usamos 1 formulario em um determinado pacote. Então quando for fechar este form o módulo deve ser descarregado. No meu caso eu carrego os forms para abas e tenho o meu TList com estes dados de HMODULE e outros. Assim quando o cara vai fechar a aba, que é um form, eu sei que é a hora de fazer o UnloadPackage. No seu caso tem q ue ver como você trabalha. Romulo


Entaum, eu trabalho da seguinte forma, possuo uma aplicaçao em [b:ace7055bda]MDI[/b:ace7055bda], sendo que cada um de meus forms, [b:ace7055bda]MDIChild[/b:ace7055bda], esta disposto dentro de um pacote [b:ace7055bda].bpl[/b:ace7055bda], dentro de cada form existe uma procedure que é exportada para todo o sistema, da seguinte forma:

Unit de um form MDIChild, exemplo.
unit uFormBpl2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm3 = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

procedure me; stdcall;

exports
  me;

implementation

procedure me; stdcall;
begin
  if not Assigned(Form3) then
    Application.CreateForm(TForm3, form3)
  else
    with form3 do
      begin
        show;
        BringToFront;
        WindowState := wsNormal;
      end;

(*  if not Assigned(Form3) then
     Form3 := TForm3.Create(Application)
  else
    with Form3 do
      begin
        Show;
        BringToFront;
        WindowState := wsNormal
      end;*)
end;

{$R *.dfm}

procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  Form3 := nil;
end;

end.


uma vez exportado pela are de exports do form eu faço o seguinte no form chamador(principal)

procedure TfrmPrincipal.este011Click(Sender: TObject); 
type 
  TChamar = procedure; stdcall; 
var 
  _Chamar: TChamar; 
  i: Smallint; 
begin 
  i := Length(carregados); 

  SetLength(carregados, i + 1); 
  carregados[i].nome := ´pkgTeste01.bpl´; 
  carregados[i].modulo := LoadPackage(carregados[i].nome); 

  if carregados[i].modulo > 0 then 
    begin 
      @_Chamar := GetProcAddress(carregados[i].modulo, ´chamar´); 
      _Chamar; 
    end; 
end;


até ai esta tudo certo, o sistema chama os forms de boa, inclusive como eu naum quero que ele crie mais um, quando se e clicado no mesmo botao duas vezes, ele chama diretinho o form novamente para tela.

O problema e na hora de descarregar, esta funcao que eu peguei de um colega de forum e dei uma adaptada para a minha realidade, funciona se eu chama-la de um outro lugar qualquer depois de ter fechado o form e passado [b:ace7055bda]nil[/b:ace7055bda] para ele, porem preciso que quando o form se feche ele ja descarregue o pacote correspondente, entende. E isso nao esta sendo feito.

Eu ja tentei colocar em OnDestroy, OnClose, e tals mas parece que a funcao nao se executa.

eu sei que e chato ficar pedindo as coisas para os outros, mas vc naum poderia dar um exemplo de codigo sem que comprometece o seu desenvolvimento.


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

Olá, eu só queria uma ideia de como chamar uma funcao para descarregar um bpl logo apos ter fechado o form, correspondente ao bpl. não quero nada pronto nao, somente uma ideia.

por exemplo:

procedure descarregar;
var
  _hnd: THandle;
begin
  _hnd := GetModuleHandle(´MyPackage.bpl´);
  UnloadPackage(_hnd);
end;


isso funciona bem de fora do form, porem se eu chamo esta funcao de dentro do form ele gera exception, devido ao fato do form ainda estar aberto.

Era somente ai que eu precisava de uma dica.


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

up..


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

up...


GOSTEI 0
Marco Salles

Marco Salles

23/08/2008

No onclosse desse forms (filhos que estão na bpl) mande uma mensagem
para a aplicação principal... Esta mensagem sera interceptada na Aplicação Principal e voce podera descarregar esta Packages

Exemplo

form Principal..

private
{ Private declarations }
procedure UserMessage (var Msg: TMessage);

procedure TForm1.UserMessage(var Msg: TMessage);
begin
//Aqui descarrega o seu Package
showmessage(´fechou´);
end;   



//Inclui nos Forms Filhos
formHandle:THandle;
MsgBack:integer;


//Ao cria este Formulários deve passar o Valor correto

FormFilho.formHandle:=Handle; //do form Principal
FormFilho.MsgBack:=wm_user;

e finalmente

// nos forms Filhos
procedure TFormFilhos.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 //envia uma mensagem para o Form Principal
 postMessage (FormHandle, MsgBack,0, 0);
end;


Acresdito que funciona .. A mensagem postMessage não é sincrona e será processada depois que o FormFilho for fechado

Agora é com voce atribuir e cetar corretamente essas propriedades [b:2e5b76b129]formHandle ,MsgBack[/b:2e5b76b129]


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

[quote:d64a1325a2=´Marco Salles´]No onclosse desse forms (filhos que estão na bpl) mande uma mensagem
para a aplicação principal... Esta mensagem sera interceptada na Aplicação Principal e voce podera descarregar esta Packages

Exemplo

form Principal..

private
{ Private declarations }
procedure UserMessage (var Msg: TMessage);

procedure TForm1.UserMessage(var Msg: TMessage);
begin
//Aqui descarrega o seu Package
showmessage(´fechou´);
end;   



//Inclui nos Forms Filhos
formHandle:THandle;
MsgBack:integer;


//Ao cria este Formulários deve passar o Valor correto

FormFilho.formHandle:=Handle; //do form Principal
FormFilho.MsgBack:=wm_user;

e finalmente

// nos forms Filhos
procedure TFormFilhos.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 //envia uma mensagem para o Form Principal
 postMessage (FormHandle, MsgBack,0, 0);
end;


Acresdito que funciona .. A mensagem postMessage não é sincrona e será processada depois que o FormFilho for fechado

Agora é com voce atribuir e cetar corretamente essas propriedades [b:d64a1325a2]formHandle ,MsgBack[/b:d64a1325a2][/quote:d64a1325a2]

Nossa, muito obrigado amigo vou testar e assim que tiver uma posiçao posto a resposta para vc, desde já agradeço muito sua ajuda.


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

Olá, [b:521907d64a]Marco Salles[/b:521907d64a], obrigado pela ajuda amigo, fico grato desde já, e queria postar algumas coisas que precisei arrumar para funcionar sua dica.

1. No Form Principal precisei declarar uma Constante do Tipo WM_USER; por exemplo.

depois criei uma procedure parecida com a sua assim:

const
  Msg = WM_USER;
// Esta constante deve estar antes da declaraçao de type do form Principal.

// em private ou public
procedure UserMessage(var Message: TMessage); message Msg;

// aqui a implementação
procedure TfrmPrincipal.UserMessage(var (* Note aqui esta variavel Message *) Message: TMessage);
begin
  UnloadPackage(MyHandle);
end;


depois nos forms Filho fiz assim

redeclarei a mesma constante do form principal.

const
  Msg = WM_USER;

// No OnDestroy do form filho coloquei assim
procedure TTeste02.FormDestroy(Sender: TObject);
var
  hnd: THandle;
begin
  hnd := FindWindow(nil, PChar(Application.MainForm.Caption));
  PostMessage(hnd, Msg, 0, 0);
end;


e ai deu certo, agora so preciso implementar uma forma de passar um string com o nome do pacote que eu quero fechar para a procedure [b:521907d64a]UserMessage[/b:521907d64a], vou procurar sobre isso.

fico muito agradecido pela força que me deu, obrigado mesmo. vlw té ++++


GOSTEI 0
Marco Salles

Marco Salles

23/08/2008

e ai deu certo, agora so preciso implementar uma forma de passar um string com o nome do pacote que eu quero fechar para a procedure UserMessage, vou procurar sobre isso.


Humm... Mas isto pode se feito com o Numero do Handle desses Packages

Voce deve usar o [b:0c20329f5c]Terceiro Parametro da Mensagem[/b:0c20329f5c]

procedure TForm1.UserMessage(var Msg: TMessage);
var
Hmod: HMODULE;
begin
  Hmod := Msg.WParam; //Aqui esta a DICA...
 // Ai descarregar ..
unLoadPackage(Hmod);
end;


// No OnDestroy do form filho coloquei assim
procedure TTeste02.FormDestroy(Sender: TObject);
var
hnd: THandle;
begin
hnd := FindWindow(nil, PChar(Application.MainForm.Caption));
PostMessage(hnd, Msg, TerceiroParametro, 0);
//No terceiro Parametro voce usa o Hanlde no seu caso O
// modulo: HMODULE;
end;

E como voce obtem o modulo...
Ora passa ele na procedure Chamar... Crie um parametro para esta Procedure

Private
 Modulo: HMODULE;

procedure chamar(O Handle Da Package); stdcall; 
begin 
  if MyFormChild = nil then 
    begin
      Application.CreateForm(TMyFormChild, MyFormChild) ;
      Modulo:=O Handle Da Package;
  else 
  with MyFormChild do 
    begin 
      Show; 
      BringToFront; 
      WindowState := wsNormal; 
    end; 
end; 


Da certim...


GOSTEI 0
Godzilla_xf

Godzilla_xf

23/08/2008

[quote:9e65814a71=´Marco Salles´]
e ai deu certo, agora so preciso implementar uma forma de passar um string com o nome do pacote que eu quero fechar para a procedure UserMessage, vou procurar sobre isso.


Humm... Mas isto pode se feito com o Numero do Handle desses Packages

Voce deve usar o [b:9e65814a71]Terceiro Parametro da Mensagem[/b:9e65814a71]

procedure TForm1.UserMessage(var Msg: TMessage);
var
Hmod: HMODULE;
begin
  Hmod := Msg.WParam; //Aqui esta a DICA...
 // Ai descarregar ..
unLoadPackage(Hmod);
end;


// No OnDestroy do form filho coloquei assim
procedure TTeste02.FormDestroy(Sender: TObject);
var
hnd: THandle;
begin
hnd := FindWindow(nil, PChar(Application.MainForm.Caption));
PostMessage(hnd, Msg, TerceiroParametro, 0);
//No terceiro Parametro voce usa o Hanlde no seu caso O
// modulo: HMODULE;
end;

E como voce obtem o modulo...
Ora passa ele na procedure Chamar... Crie um parametro para esta Procedure

Private
 Modulo: HMODULE;

procedure chamar(O Handle Da Package); stdcall; 
begin 
  if MyFormChild = nil then 
    begin
      Application.CreateForm(TMyFormChild, MyFormChild) ;
      Modulo:=O Handle Da Package;
  else 
  with MyFormChild do 
    begin 
      Show; 
      BringToFront; 
      WindowState := wsNormal; 
    end; 
end; 


Da certim...[/quote:9e65814a71]

Olá, eu ia postar aqui no forum uma resposta parecida com a sua, neste momento não vou poder mas assim que der eu vou postar como fazer isso na integra, em todos os foruns que eu postei esta mensagem, para que os colegas que ajudaram e os que possam vir a precisar disso tirem proveito dos nossos exemplos. Eu realmente com sua ajuda e com mais umas informaçoes que achei neste site: http://www.delphi3000.com/articles/article_574.asp?SK= consegui arrumar uma funçao que faça o que vc vc flw acima, fico muito feliz pela sua ajuda, vlw mesmo.

[b:9e65814a71]Obrigado mesmo pela força ai, à vc e a Todos que ajudaram.[/b:9e65814a71]

vlw [b:9e65814a71]Marco Salles[/b:9e65814a71]


GOSTEI 0
Márcio

Márcio

23/08/2008

Srs, estou com exatamente o mesmo problema.

Gostaria de verificar com os Srs, como fazem nos casos em que tem um pacote e diversos formulários dentro do mesmo, sendo que existe mais de um formulário aberto ao mesmo tempo (do mesmo pacote, ou de diversos pacotes).

Como vcs fazem para não ocasionar o erro de access violation ao sair do pacote (implementado conforme os posts anteriores)?

Exemplo: Tenho a tela A10 aberta 2x no programa e o usuário fecha uma das janelas, como fazer para descarregar o pacote e não dar violação de acesso?
GOSTEI 0
POSTAR