フォーム無しのコンソール・アプリで
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
end;
相当の処理をしたいのですが無理でしょうか?
VCL を使うとサイズが大きくなるので・・・。
ちょっと詳しく状況が分かりませんが、
そもそもコンソールアプリにCloseQueryなる状況ってありましたっけ?
コンソールアプリはイベントドリブン形式じゃないですよね…
ママんさん、ありがとうございます。
フォームを作らないで、WM_XXXを受けるのはやっぱり無理なんでしょうかね?
なんとかポーリングできないかと・・・。
あー。
勘違いされてると思いますのでご確認ください。
コンソールアプリはWindowsメッセージのやり取りはできません。
WM_XXXを受け取りたければウィンドウズアプリケーションになります。
また、ウィンドウズアプリケーションは必ずウィンドウを持たなくてはなりませんが、ウィンドウは非表示ができますので、恐らくこちらの方か目的にあっていると思います。
フォームを作らないウィンドウアプリケーションは当然作れます。
しかし、フォームを作るかどうかとVCLを使うかどうかは別問題です。
VCLを使わないウィンドウズアプリケーションの作り方は
以前Halbowさんという方が膨大な資料を残してくれています。
http://homepage2.nifty.com/Mr_XRAY/Halbow/ShousaiIndex.html
ですので、こちらを参照してください。
ママんさん、ありがとうございます。
ちょっと探し回らなければなりせんね。
何をどう見たらいいのか・・な感じで、ちょっと辛いです。
「終了時に処理したい・・」の続きなんです。
で、ちょっと検索してみたところ、
実行ファイルのサイズが随分大きいようですが。
http://forum.nifty.com/fdelphi/faq/00178.htm
がヒットしたので、ここのソースを参考に、ごりごり作ってみます。
自分の技術力と理解力でできるかな・・?
ここの下部に
・ウィンドウの無いサブEXE
の見出しだけがありましたが、検索してもヒットしませんでした。
フォームそのものは、あってもなくてもいいのですが、
「非表示で VCL でない・・」が一応、目標です。
「あと、タスク・バーに出ない」ことも理想です。どちらかと言うと、サイズより
こちらの要求のほうが強いんですけどね・・・。
一応、上記ページにて、解決にします。
また解からなところが出てくれば、よろしくお願いします。
ぬぅー。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.
お手数かけます。
このままコンパイルしてみました。
8.50 KB (8,704 バイト)
16.0 KB (16,384 バイト)
でした。1セクタに収まってる感じですね。あれ、2セクタか(?笑)
ま、それはともかく、これを改造させていただこうと思います。
>C++のほかのコンパイラを使えばもう少し小さくなるかもしれません。
>てかDelphiの意味無いッスね。
う〜む、これだけのためにBC5.5をインストールする気にもなれなくて(苦笑)。
いっそ、VBS / WSH ででもできるといいかなぁとか思うんですけど、
まったく未経験のため、CloseQuery が取れるのかどうか不安で、
調べまわったあげくできない・・なんてことを少し考えててしまいまして・・。
まぁ、勉強ということではいいんですけどね。今回の場合、ちょっちょっと
イメージしているものを作ってみたくて・・な感じですので、
どちらかと言うとインスタント・メイクなパターンで行けたら・・な感じでした。
いろいろご面倒おかけいたします。m(_._)m
ほぼできました。
一応、お世話になりましたので、報告しておきます。
単に、終了時刻を記録するだけです。
あと、起動時刻も追加予定ですが・・・。
こんなことしなくても、何かのファイルのスタンプを見るだけで
判るのでしょうかね・・・? と、それを先に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.
すでに解決していますが・・・
上記のソースをみると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.
----------------------------------------
だけでよいような。
意味が違っていたらごめんんさい
finalさん、ヒントありがとうございます。
うぅ、これってどうやればコンパイルが通るんでしょうか?
「TForm1 が無い」になるばかりで・・。
提案ソース見る限り、アプリ起動して即、DateTimeToStr(Now)); して
で、即アプリ終了と言う意味に読めましたが・・・。
あるいは、何かごく普通(簡易エディタとか)のアプリで
「本アプリは操作終了時刻を記録する機能があります」を
想定されているのでしょうか?
それだとすると制作意図ではないです。別に隠したいワケでもないのですが、
タスク・バーとかに出ると鬱陶しいので、できれば影でコソっと動いて欲しい、
そんなタイプのものです。
前者だとすると、PC終了時に意図的に自分でランさせるか、
(*)
Windows 2000
Windows XP Professional
Windows Server 2003
これらのOSで「グループポリシー」使うしかないですね。
OS選択の制限がきついので、ちょっと辛いですが、常駐型に・・なってしまいました。
このアプリの目的は、「(PC起動時刻と)PC終了時刻を自動で記録したい」
単にそれだけです。自分一人しかこのPC触らないので、
「昨日、何時ごろ終わったんだっけ?」とか、運用実態の統計/傾向的なものを
取るしか意味ありませんが、家族が勝手にPCを使って困る・・・なんて場合にも
多少役立つ可能性はあります。そんな意図のものです。
この程度のものに、VCL …って何んだかなぁ・・で、
メッセージ・ループをごりごりと・・・そんな感じです。
この部分は何かまた別の応用のための練習も半分兼ねてます。
・・・この程度しかレス書けません。すみません。
>上記のソースをみるとFormCloseQuery相当の処理が必要ですか?
の部分、最初、ママんさんにサンプルいただいた時はなかったので、
WM_QUERYENDSESSION: WMQUERYENDSESSION(hWindow);
を追加して、その機能を実現しました。
WM_CLOSEQUERY があるものだと思い、Messages.pas を探し回りましが、
無くて、これでできそうなので・・・。
WM_QUERYENDSESSION と FormCloseQuery と等価なのかどうかは判りません。
コンソールアプリと書いてあったもので・・・失礼しました。
上の説明を見ると常駐ということはコンソールアプリではないですね。
Winアプリでも他の方が書かれているようにVCLを一切使わずに小さいアプリを書くことは可能です。
>コンソールアプリ
混乱の原因になってすみません。
「逆スタートアップ?」さえなんとかなれば、コンソールアプリで行けそうなので
思い込みのまま、書いてしまいました。
でも、機能は WinApp より COM >> date.dat にかなり近いかと(^^;)
BAT でDataTime 取れたかなぁ? とちょっと今思いますが、いずれにしても
一般向けのOSでも「PC終了処理」で何か操作できる機能が欲しいとなぁと
思ったりしています。
メールでの質問のレスのために検索していたら,このページがヒットしました.
ついでですので書き込みしておきます.
残念ながら,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.
動作確認と参考記事です.
Windows [Version 10.0.19042] + Delphi XE5(UP2) Pro VCL-32
[ HandlerRoutine callback function ]
https://docs.microsoft.com/en-us/windows/console/handlerroutine
ツイート | ![]() |