フォーム無しのコンソール・アプリで FormCloseQuery の処理をするには?

解決


マリン  2006-05-22 18:24:55  No: 21709  IP: [192.*.*.*]

フォーム無しのコンソール・アプリで 
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin

end;
相当の処理をしたいのですが無理でしょうか?
VCL を使うとサイズが大きくなるので・・・。

編集 削除
ママん  2006-05-22 18:34:23  No: 21710  IP: [192.*.*.*]

ちょっと詳しく状況が分かりませんが、
そもそもコンソールアプリにCloseQueryなる状況ってありましたっけ?
コンソールアプリはイベントドリブン形式じゃないですよね…

編集 削除
マリン  2006-05-22 18:42:24  No: 21711  IP: [192.*.*.*]

ママんさん、ありがとうございます。
フォームを作らないで、WM_XXXを受けるのはやっぱり無理なんでしょうかね?
なんとかポーリングできないかと・・・。

編集 削除
ママん  2006-05-22 20:07:07  No: 21712  IP: [192.*.*.*]

あー。
勘違いされてると思いますのでご確認ください。
コンソールアプリはWindowsメッセージのやり取りはできません。
WM_XXXを受け取りたければウィンドウズアプリケーションになります。
また、ウィンドウズアプリケーションは必ずウィンドウを持たなくてはなりませんが、ウィンドウは非表示ができますので、恐らくこちらの方か目的にあっていると思います。

フォームを作らないウィンドウアプリケーションは当然作れます。
しかし、フォームを作るかどうかとVCLを使うかどうかは別問題です。
VCLを使わないウィンドウズアプリケーションの作り方は
以前Halbowさんという方が膨大な資料を残してくれています。
http://homepage2.nifty.com/Mr_XRAY/Halbow/ShousaiIndex.html
ですので、こちらを参照してください。

編集 削除
マリン  2006-05-22 21:55:38  No: 21713  IP: [192.*.*.*]

ママんさん、ありがとうございます。
ちょっと探し回らなければなりせんね。
何をどう見たらいいのか・・な感じで、ちょっと辛いです。

「終了時に処理したい・・」の続きなんです。
で、ちょっと検索してみたところ、

実行ファイルのサイズが随分大きいようですが。 
http://forum.nifty.com/fdelphi/faq/00178.htm

がヒットしたので、ここのソースを参考に、ごりごり作ってみます。
自分の技術力と理解力でできるかな・・?

ここの下部に
・ウィンドウの無いサブEXE
の見出しだけがありましたが、検索してもヒットしませんでした。

フォームそのものは、あってもなくてもいいのですが、
「非表示で VCL でない・・」が一応、目標です。
「あと、タスク・バーに出ない」ことも理想です。どちらかと言うと、サイズより
こちらの要求のほうが強いんですけどね・・・。
一応、上記ページにて、解決にします。
また解からなところが出てくれば、よろしくお願いします。

編集 削除
ママん  2006-05-22 22:48:57  No: 21714  IP: [192.*.*.*]

ぬぅー。Halbowさんの1章ですよー。
サンプル:
  F1で終了
  終了時に終了するか聞く
これで15,360Bです。
まだVCLを二個使ってますが、ここらがDelphiの限界値でしょうか?
C++のほかのコンパイラを使えばもう少し小さくなるかもしれません。
てかDelphiの意味無いッスね。

program Project1;

uses
  windows, messages;

procedure WMCLOSE(hWindow:HWND);
var i:Integer;
begin
  if MessageBox(hWindow,'終わるよ','ミニ',MB_YESNO)= IDYES then
    DestroyWindow(hWindow);
end;

procedure WMHOTKEY(hWindow:HWND; WParam: WPARAM; LParam: LPARAM);
begin
  if WParam=1 then
    SendMessage(hWindow,WM_CLOSE,0,0);
end;

procedure MainDestroy(hWindow: HWND);
begin
  UnregisterHotKey(hWindow, 1);
  PostQuitMessage(0);
end;

//------------------------------------------------------------
//             Main Window Procedure
//------------------------------------------------------------
function MainWndProc(hWindow: HWND; Msg: UINT; WParam: WPARAM;
                     LParam: LPARAM): LRESULT; stdcall; export;
var
  ps: TPaintStruct;
begin
  Result := 0;
  case Msg of
    WM_CLOSE  : WMCLOSE(hWindow);
    WM_Hotkey : WMHOTKEY(hWindow,WParam,LParam);
    WM_DESTROY: MainDestroy(hWindow);
  else
    begin
      result := DefWindowProc( hWindow, Msg, wParam, lParam );
      exit;
    end;
  end;
end;

//------------------------------------------------------------
//             Main Procedure
//------------------------------------------------------------
var
  wc: TWndClass;
  hWindow: HWND;
  Msg: TMsg;
begin
  wc.lpszClassName   := 'AppClass';
  wc.lpfnWndProc     := @MainWndProc;
  wc.style           := CS_VREDRAW or CS_HREDRAW;
  wc.hInstance       := hInstance;
  wc.hIcon           := LoadIcon(0,IDI_APPLICATION);
  wc.hCursor         := LoadCursor(0,IDC_ARROW);
  wc.hbrBackground   := (COLOR_WINDOW+1);
  wc.lpszMenuName    := nil;
  wc.cbClsExtra      := 0;
  wc.cbWndExtra      := 0;

  RegisterClass(wc);
  hWindow := CreateWindowEx(0,'AppClass','',0,0,0,0,0,0,0,hInstance,nil);
  RegisterHotKey(hWindow, 1, 0, VK_F1);
  //ShowWindow(hWindow,CmdShow);
  //UpDateWindow(hWindow);
//---- Message Loop ---------------------
  while GetMessage(Msg, 0, 0, 0) do begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
  Halt(Msg.wParam);
end.

編集 削除
マリン  2006-05-22 23:35:02  No: 21715  IP: [192.*.*.*]

お手数かけます。
このままコンパイルしてみました。

8.50 KB (8,704 バイト)
16.0 KB (16,384 バイト)

でした。1セクタに収まってる感じですね。あれ、2セクタか(?笑)
ま、それはともかく、これを改造させていただこうと思います。

>C++のほかのコンパイラを使えばもう少し小さくなるかもしれません。
>てかDelphiの意味無いッスね。

う〜む、これだけのためにBC5.5をインストールする気にもなれなくて(苦笑)。
いっそ、VBS / WSH ででもできるといいかなぁとか思うんですけど、
まったく未経験のため、CloseQuery  が取れるのかどうか不安で、
調べまわったあげくできない・・なんてことを少し考えててしまいまして・・。
まぁ、勉強ということではいいんですけどね。今回の場合、ちょっちょっと
イメージしているものを作ってみたくて・・な感じですので、
どちらかと言うとインスタント・メイクなパターンで行けたら・・な感じでした。

いろいろご面倒おかけいたします。m(_._)m

編集 削除
マリン  2006-05-23 06:06:45  No: 21716  IP: [192.*.*.*]

ほぼできました。
一応、お世話になりましたので、報告しておきます。
単に、終了時刻を記録するだけです。
あと、起動時刻も追加予定ですが・・・。

こんなことしなくても、何かのファイルのスタンプを見るだけで
判るのでしょうかね・・・?  と、それを先にPC関係の Q&A で
尋ねるべきでしたね(^^;)。

ま、こんなタイプのマイクロ・アプリを作る
どなたかの参考にでもなれば・・な感じです。
ありがとうございました。


program Project1;

uses
  windows, messages, SysUtils, Dialogs;

procedure WMQUERYENDSESSION(hWindow: HWND);
var  f: TextFile;
begin              //ShowMessage('222222222222');  // 確認用
    AssignFile(f, 'C:\WINDOWS\デスクトップ\test.txt'); // 0 バイトのファイルをメモ帳面とかで
    Append(f);                                     // 作成しておかなければエラーになります。
    Writeln(f, DateTimeToStr(Now));
    Flush(f);
    CloseFile(f);
    DestroyWindow(hWindow);
end;

procedure WMCLOSE(hWindow:HWND);
var i:Integer;
begin
  if MessageBox(hWindow,'終わるよ','ミニ',MB_YESNO)= IDYES then
    DestroyWindow(hWindow);
end;

procedure WMHOTKEY(hWindow:HWND; WParam: WPARAM; LParam: LPARAM);
begin
  if WParam=1 then
    SendMessage(hWindow,WM_CLOSE,0,0);
end;

procedure MainDestroy(hWindow: HWND);
begin             // ShowMessage('00000000');  // 確認用
  UnregisterHotKey(hWindow, 1);
  PostQuitMessage(0);
end;

//------------------------------------------------------------
//             Main Window Procedure
//------------------------------------------------------------
function MainWndProc(hWindow: HWND; Msg: UINT; WParam: WPARAM;
                     LParam: LPARAM): LRESULT; stdcall; export;
var
  ps: TPaintStruct;
begin
  Result := 0;
  case Msg of
    WM_CLOSE  : WMCLOSE(hWindow);
    WM_Hotkey : WMHOTKEY(hWindow,WParam,LParam);
    WM_DESTROY: MainDestroy(hWindow);
    WM_QUERYENDSESSION: WMQUERYENDSESSION(hWindow);
  else
    begin
      result := DefWindowProc( hWindow, Msg, wParam, lParam );
      exit;
    end;
  end;
end;

//------------------------------------------------------------
//             Main Procedure
//------------------------------------------------------------
var
  wc: TWndClass;
  hWindow: HWND;
  Msg: TMsg;
begin
  wc.lpszClassName   := 'AppClass';
  wc.lpfnWndProc     := @MainWndProc;
  wc.style           := CS_VREDRAW or CS_HREDRAW;
  wc.hInstance       := hInstance;
  wc.hIcon           := LoadIcon(0,IDI_APPLICATION);
  wc.hCursor         := LoadCursor(0,IDC_ARROW);
  wc.hbrBackground   := (COLOR_WINDOW+1);
  wc.lpszMenuName    := nil;
  wc.cbClsExtra      := 0;
  wc.cbWndExtra      := 0;

  RegisterClass(wc);
  hWindow := CreateWindowEx(0,'AppClass','',0,0,0,0,0,0,0,hInstance,nil);
  RegisterHotKey(hWindow, 1, 0, VK_F12); // F1 はヘルプ系 に良く使うので F12 に変更
  //ShowWindow(hWindow,CmdShow);
  //UpDateWindow(hWindow);
//---- Message Loop ---------------------
  while GetMessage(Msg, 0, 0, 0) do begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
  Halt(Msg.wParam);
end.

編集 削除
final  2006-05-23 08:44:28  No: 21717  IP: [192.*.*.*]

すでに解決していますが・・・
上記のソースをみるとFormCloseQuery相当の処理が必要ですか?
単にアプリ終了時に処理をしたいのであれば finalization節に書けばいいのでは?
コンソールアプリの場合はプロジェクトファイルには書けませんので別のユニットに例えば
----------------------------------------
unit u_last;

interface

implementation

uses SysUtils;

procedure do_final;
var
  f: TextFile;
begin
  AssignFile(f, 'test.txt');
{$I-}
  Append(f);
  if IOResult <> 0 then
    Rewrite(F);
{$I+}
  Writeln(f, DateTimeToStr(Now));
  Flush(f);
  CloseFile(f);
end;

initialization

finalization
  do_final;

end.
----------------------------------------
だけでよいような。
意味が違っていたらごめんんさい

編集 削除
マリン  2006-05-23 11:37:59  No: 21718  IP: [192.*.*.*]

finalさん、ヒントありがとうございます。
うぅ、これってどうやればコンパイルが通るんでしょうか?
「TForm1 が無い」になるばかりで・・。

提案ソース見る限り、アプリ起動して即、DateTimeToStr(Now));  して
で、即アプリ終了と言う意味に読めましたが・・・。

あるいは、何かごく普通(簡易エディタとか)のアプリで
「本アプリは操作終了時刻を記録する機能があります」を
想定されているのでしょうか?

それだとすると制作意図ではないです。別に隠したいワケでもないのですが、
タスク・バーとかに出ると鬱陶しいので、できれば影でコソっと動いて欲しい、
そんなタイプのものです。

前者だとすると、PC終了時に意図的に自分でランさせるか、

(*)
Windows 2000
Windows XP Professional
Windows Server 2003

これらのOSで「グループポリシー」使うしかないですね。
OS選択の制限がきついので、ちょっと辛いですが、常駐型に・・なってしまいました。

このアプリの目的は、「(PC起動時刻と)PC終了時刻を自動で記録したい」
単にそれだけです。自分一人しかこのPC触らないので、
「昨日、何時ごろ終わったんだっけ?」とか、運用実態の統計/傾向的なものを
取るしか意味ありませんが、家族が勝手にPCを使って困る・・・なんて場合にも
多少役立つ可能性はあります。そんな意図のものです。

この程度のものに、VCL …って何んだかなぁ・・で、
メッセージ・ループをごりごりと・・・そんな感じです。
この部分は何かまた別の応用のための練習も半分兼ねてます。

・・・この程度しかレス書けません。すみません。

編集 削除
マリン  2006-05-23 11:49:27  No: 21719  IP: [192.*.*.*]

>上記のソースをみるとFormCloseQuery相当の処理が必要ですか?

の部分、最初、ママんさんにサンプルいただいた時はなかったので、
    WM_QUERYENDSESSION: WMQUERYENDSESSION(hWindow);
を追加して、その機能を実現しました。

WM_CLOSEQUERY があるものだと思い、Messages.pas を探し回りましが、
無くて、これでできそうなので・・・。

WM_QUERYENDSESSION  と FormCloseQuery  と等価なのかどうかは判りません。

編集 削除
final  2006-05-23 12:35:43  No: 21720  IP: [192.*.*.*]

コンソールアプリと書いてあったもので・・・失礼しました。
上の説明を見ると常駐ということはコンソールアプリではないですね。
Winアプリでも他の方が書かれているようにVCLを一切使わずに小さいアプリを書くことは可能です。

編集 削除
マリン  2006-05-23 12:56:01  No: 21721  IP: [192.*.*.*]

>コンソールアプリ
混乱の原因になってすみません。

「逆スタートアップ?」さえなんとかなれば、コンソールアプリで行けそうなので
思い込みのまま、書いてしまいました。
でも、機能は WinApp より COM >> date.dat にかなり近いかと(^^;)
BAT でDataTime 取れたかなぁ?  とちょっと今思いますが、いずれにしても
一般向けのOSでも「PC終了処理」で何か操作できる機能が欲しいとなぁと
思ったりしています。

編集 削除
Mr.XRAY  2022-03-13 10:59:06  No: 150110  IP: [192.*.*.*]

メールでの質問のレスのために検索していたら,このページがヒットしました.
ついでですので書き込みしておきます.
残念ながら,CanClose 相当の処理はできませんが,参考になるかと思います.

[Ctrl] + [C] または [Alt] + [F4} あるいは [×] ボタンで閉じる時にダイアログを表示します.
ダイアログは自動的に閉じます.[OK] ボタンをクリックする必要はありません. 


program Project1;
{$APPTYPE CONSOLE}

uses
  Winapi.Windows,
  Winapi.Messages,
  Vcl.Dialogs,
  Vcl.Forms;
               var
  FhConsole : HWND;

//-----------------------------------------------------------------------------
//  SetConsoleCtrlHandler のコールバック関数
//  以下のイベントが使用可能
//  処理の変更 (閉じる処理の阻止等) はできない
//
//  CTRL_C_EVENT        : [Ctrl] + [C] の信号を受信
//  CTRL_BREAK_EVENT    : [Ctrl] + [Break] の信号を受信
//  CTRL_CLOSE_EVENT    : コンソールウィンドウを閉じる時の信号
//  CTRL_LOGOFF_EVENT   : ユーザーがログオフする時の信号
//  CTRL_SHUTDOWN_EVENT : システムのシャットダウン時の信号
//-----------------------------------------------------------------------------
function ConsoleEventProc(CtrlType: DWORD): Bool; stdcall;
var
  LText : string;
begin
  Result := True;
  LText := '';

  case CtrlType of
  CTRL_C_EVENT:
    begin
      LText := '[Ctrl] + [C] のキー操作がありました.';
    end;
  CTRL_CLOSE_EVENT:
    begin
      LText := 'ウィンドウが閉じようとしています';
    end;
  else
  end;

  if LText <> '' then begin
    MessageBox(0, PChar(LText), '情報', MB_ICONINFORMATION);
  end;
end;
//-----------------------------------------------------------------------------

begin
  Writeln('コンソールアプリのイベント');

  // コンソールウィンドウのハンドルを取得
  FhConsole := FindWindow(nil, PChar(Application.ExeName));

  // イベントハンドラの設定
  SetConsoleCtrlHandler(@ConsoleEventProc, True);
  SetConsoleMode(FhConsole, ENABLE_PROCESSED_INPUT);

  Readln;
  // ダイアログを見られるようにするためのタイムラグ
  Sleep(3000);
end.

編集 削除
Mr.XRAY  2022-03-13 11:15:09  No: 150111  IP: [192.*.*.*]

動作確認と参考記事です.

Windows [Version 10.0.19042] + Delphi XE5(UP2) Pro VCL-32

[ HandlerRoutine callback function ]
https://docs.microsoft.com/en-us/windows/console/handlerroutine

編集 削除