喫茶XRAYトップページ

[スレッド全体
]

[306] DllでMainForm以外からのClose返信 削除
2005/6/19 (日) 12:00:39 Syake
こんにちは
暑い日が続いています。
窓を開けていても暑くてたまらないので今年になって初、
クーラーを入れて作業しとりましたら、見事に頭ガンガン
鼻ズルズル(汚ったね)になってしまいました。(^_^;)
皆さん、気をつけましょう。

ところで、DLLFormを作成して遊んどります。
で、SubFormで処理中に、DllForm自体をクローズしようといた。
ところが、これがどうも、上手くいきません。
単体のEXEの様にApplication.Terminateは使えない。
そこで、Unit2からForm1.Close;等とやると、読み込み違反
となってしまう。

どうしたもんでしょうか?



[308] Re:DllでMainForm以外からのClose返信 削除
2005/6/19 (日) 22:36:20 Mr.XRAY
▼ Syakeさん こんにちは

> どうしたもんでしょうか?

マスターにはわかりません.(^^ゞ


[310] Re2:DllでMainForm以外からのClose返信 削除
2005/6/19 (日) 23:00:33 Mr.XRAY
▼ Syakeさん こんにちは

> マスターにはわかりません.(^^ゞ

といいつつ,ちょっとやって見ました.

(1) Formを生成するDLLを作成.FormDll.Dll というのを作成
(2) メインフォームのForm1でDLL内の以下のルーチンを定義して利用
   function FormShow :TForm; stdcall; external 'FormDLL.DLL';
      procedure FormClose; stdcall; external 'FormDLL.DLL';
(3) プロジェクトに新規フォーム追加.Form2とする.
(4) Form1にボタンを2つ追加.
    1つはDLL内のFormShowを実行してDLL内のFormを表示(Showメソッド使用)
(5) 2つ目はForm2を表示.
(6) Form2にボタンを1つ配置.

コードは以下.なおFormShowを実行することにより,DLL内部でフォームを
Createしています.

  public
     AForm : TForm;
 ........
procedure TForm1.Button1Click(Sender: TObject);
begin
     FormShow;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
     Form2.Show;
end;


procedure TForm2.Button1Click(Sender: TObject);
begin
     if Assigned(Form1.AForm) then begin
       Form1.AForm.Close;
       FreeAndNil(Form1.AForm);
     end;
end;

こんなやり方がいいのかどうかは不明.ちょっとわかりにくい説明でゴメン
なさい.




[311] Re3:DllでMainForm以外からのClose返信 削除
2005/6/20 (月) 02:20:44 りおりお
▼ Mr.XRAYさん
> ▼ Syakeさん こんにちは
>
> > マスターにはわかりません.(^^ゞ
>
> といいつつ,ちょっとやって見ました.
>
> (1) Formを生成するDLLを作成.FormDll.Dll というのを作成
> (2) メインフォームのForm1でDLL内の以下のルーチンを定義して利用
>    function FormShow :TForm; stdcall; external 'FormDLL.DLL';


こんばんは。

dll は独立したライブラリです。dll をコンパイルしたときにつかったライブラリ(VCL)
とそれを呼ぶ exe をコンパイルしたときの VCL ライブラリは、共通パッケージを使わない
限り、「同一」ではありません。例え、同じバージョンの Delphi で両方コンパイルしたと
しても、それぞれ別個のライブラリを使用してコンパイルされたことになります。

ですから、一般には dll とそれを呼ぶ exe の間でクラスのインスタンスを渡して
操作することはできません。うまくいったとしても偶然と考えるべきです。

Form を閉じるくらいなら、ウィンドウハンドルを渡すべきです。それなら、
isWindow() で有効なウィンドウであるかどうか確認できますし、DestroyWindow や
WM_SYSCOMMAND で SC_CLOSE を送れば閉じられます。

[312] Re4:DllでMainForm以外からのClose返信 削除
2005/6/20 (月) 07:50:31 Mr.XRAY
▼ りおりおさん

> ですから、一般には dll とそれを呼ぶ exe の間でクラスのインスタンスを渡して
> 操作することはできません。うまくいったとしても偶然と考えるべきです。


ははっ,そうですよね.

> Form を閉じるくらいなら、ウィンドウハンドルを渡すべきです。それなら、
> isWindow() で有効なウィンドウであるかどうか確認できますし、DestroyWindow や
> WM_SYSCOMMAND で SC_CLOSE を送れば閉じられます。


わかりました.初めてFormを表示するDLLを作成してみたのですが,作法を無視
してしまいました.とてもHalbow資料館のような内容を提供しているサイトの
管理者とは思えない(苦笑).

ありがとうごさいました.

[313] Re5:DllでMainForm以外からのClose返信 削除
2005/6/20 (月) 14:31:00 Syake
皆さんこんにちは
色々ありがとうございます。

if IsWindow({DLLFormのWindowハンドル}) then
begin
   SendMessage({DLLFormのWindowハンドル} ,WM_SYSCOMMAND,SC_CLOSE,0);
end;
これは、呼び出し側(EXE)から呼び出し先DllFormを閉じる処理ですね。

どうも、最初の説明が良くなくてすいません<m(__)m>
呼び出し側ではなく、呼び出し先の(DLLForm内)Unit2からDLLForm自体を
閉じようとしています。

DLLFormはForm1(Main)とForm2(Sub)で構成されています。
DLLForm呼出し後、DLLForm内の処理で、Form1(Main)でClose処理では
DLLFormは閉じる事ができますが、Form2(Sub)からでは閉じることが
できないでいます。

これが単体のEXEであれば、Application.Terminateを使うとか、Unit2から
Form1.Close(かまわないですよね?)を記述すれば終了できます。
・・・が、DLLFormでは当然前者は使えず、後者でやってしまえって事で、
記述したら「読み込み違反」となってしまいます。




[314] Re6:DllでMainForm以外からのClose Re5:追加返信 削除
2005/6/20 (月) 14:48:53 Syake
続きです
//=================EXE側
var
  Form1: TForm1;
implementation
   function OpenFrm(var WinHad :THandle):Boolean; stdcall; external 'OpenFrmTest.dll';
   procedure SyncApp(appHnd :THandle); stdcall; external 'OpenFrmTest.dll';
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
  SyncApp(Application.Handle);
  {DllFormが既にオープンされているか}
  if IsWindow(TestfrmWinHnd) = False then //(二重起動防止)
  begin
    if OpenFrmExCCTPrt(TestfrmWinHnd) = False then
    begin
       //;
    end;
 end;
end;

//=================DLLForm側
//----------------------frmMain
var
  frmMain: TfrmMain;
   function OpenFrm(var WinHad :THandle):Boolean;stdcall;
   procedure SyncApp(AppHnd :THandle); stdcall;
implementation
uses UfrmSub;
   function OpenFrm(var WinHad :THandle):Boolean;stdcall;
   begin
      frmMain := TfrmMain.Create(Application);
      frmSub := TfrmSub.Create(Application);
      try
         WinHad := frmMain.WindowHandle;
        frmMain.Show ;
        Result := True;
      except
         frmMain.Free;
        frmSub.Free;
        Result := False;
      end;
   end;
   procedure SyncApp(AppHnd :THandle);stdcall;
   begin
      //Application.Handle := AppHnd;//今回は使わない
   end;
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   frmSub.Free;
   frmMain.Free;
end;

//----------------------FrmSub
procedure TfrmSub.Button1Click(Sender: TObject);
begin
   frmMain.Close;//DLLの読み込み違反
end;

一体、どうすればUnit2から閉じれるのか(^_^;)




[315] Re7:DllでMainForm以外からのClose Re5:追加返信 削除
2005/6/20 (月) 18:15:36 りおりお
こんにちは。

なにも問題なさそうですけど。

--------------- Exe ---------------------

function ShowTestForm(hOwner:HWND):HWND;stdcall;
                               external 'TestDll.dll' name 'ShowTestForm';

function ShowSubForm(hOwner:HWND):HWND;stdcall;
                               external 'TestDll.dll' name 'ShowSubForm';

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowTestForm(Application.Handle);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowSubForm(Application.Handle);
end;

---------------- DLL --------------------

--------------- project

library TestDll;

uses
  Forms,
  Windows,
  Controls,
  DllMain in 'DllMain.pas' {DllForm},
  DllUnit2 in 'DllUnit2.pas' {DllSubForm};

function ShowTestForm(hOwner:HWND):HWND;stdcall;
begin
  //Application.Handle := hOwner;
  DllForm := TDllForm.Create(Application);
  result := DllForm.Handle;
  DllForm.Show;
  //Application.Handle := 0;
end;

function ShowSubForm(hOwner:HWND):HWND;stdcall;
begin
  //Application.Handle := hOwner;
  DllSubForm := TDllSubForm.Create(Application);
  result := DllSubForm.Handle;
  DllSubForm.Show;
  //Application.Handle := 0;
end;

exports
  ShowTestForm,
  ShowSubForm;

begin
end.

[316] Re8:DllでMainForm以外からのClose Re5:追加返信 削除
2005/6/20 (月) 18:16:57 りおりお
つづき

--------------------

unit DllMain;

interface

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

type
  TDllForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  DllForm: TDllForm;

implementation

{$R *.dfm}

procedure TDllForm.Button1Click(Sender: TObject);
begin
  Close;
end;

procedure TDllForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
  if Assigned(DllSubForm) then DllSubForm.Release;
end;

end.

--------------------------

unit DllUnit2;

interface

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

type
  TDllSubForm = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  DllSubForm: TDllSubForm;

implementation

{$R *.dfm}

uses
  DllMain;

procedure TDllSubForm.Button1Click(Sender: TObject);
begin
  DllForm.Close;
end;

end.

--------------------

これで DllSubForm の Button1Click で両方閉じます。

[317] Re9:DllでMainForm以外からのClose Re5:追加返信 削除
2005/6/20 (月) 19:20:14 Syake
▼ りおりおさん
色々ありがとうございます。<m(__)m>

Helpで再確認したところ
「フォームまたはその子フォームのイベントハンドラでは
Free (Delphi) のかわりに Releaseを使う必要があります。
そうしないと,メモリアクセスエラーが発生することがあ
ります。」と親切に書いてありました。
procedure TfrmMain.FormClose(Sender: TObject;
        var Action: TCloseAction);
begin
   //frmMain.Free;
   Action := caFree;
   //frmSub.Free;{これは駄目でした}
  //if Assigned(frmSub) then frmSub.Release;{}
  frmSub.Release;
end;

この場合の Action := caFree と frmMain.Freeは同じ意味
で、動作に支障は無い。
frmMain以外のFormは「Release」としなければならない。
と、言うことでしょうか。

う〜む、では何でFrmMainからCloseしたら何もエラーが
出なかったのだろうか?
「メモリアクセスエラーが発生することがあります。」
だから、たまたまって事で、やはり「Release」て事かな。


[▼次のスレッド]
INCM/CMT
Cyclamen v3.82