Outlookのメールアイコンが出てたらメッセージを表示したい Notifire(3)

まずは、右下の通知領域を「掴む」ことから始めます。
Delphiで作ったプログラムでできてるんですが、Lazarusの使い心地というべきか、usesひとつとっても、どこでAPIを宣言されてるかわからなさすぎるので、それも含めてやってみます。
また、Delphi版はマウスカーソルの下にあるウィンドウについて情報を取得する方式だったんですが、今回は監視する形なので、いちいち勝手にカーソルを動かすわけにも行かないので、クラス名から芋づる式にたどり着くことになります。

で、FindWindowを使います。こういうAPIをシュッと調べるのに、以前使ってたパソコンだとMSDNの何かAPIのヘルプのようなのをインストールしてたんですけど、改めて今のパソコンに最新のものをダウンロードしようとしても見当たりません。
いったい何をインストールしたんだったのか。

まずはFindWindowについて
FindWindow 関数:[2012/10/02]
指定された文字列と一致するクラス名とウィンドウ名を持つトップレベルウィンドウ( 親を持たないウィンドウ)のハンドルを返します。ですって。

これの仲間に
FindWindowEx 関数:[2012/10/02]
この関数は、子ウィンドウを検索します。ですって。

よく読んで見ると、hwndParentにNULLを指定すると、デスクトップウィンドウが親ウインドウとして使われ...と書いてあります。今回の場合、通知領域はデスクトップにあるので、こっちを使ってよさそうです。

クラスの階層はすでに調べたのでわかっています。
Shell_TrayWnd
-TrayNotifyWnd
--SysPager
---ToolbarWindow32
です。

なので(といっても試行錯誤の結果)
procedure TForm1.Button1Click(Sender: TObject);
var
LName: String;
LWnd: HWND;
begin
LName := 'Shell_TrayWnd';
LWnd := FindWindowEx(0, 0, PChar(LName), nil);
Memo1.Lines.Add(Format('%d:%s', [LWnd, LName]));
if (LWnd <> 0) then
begin
LName := 'TrayNotifyWnd';
LWnd := FindWindowEx(LWnd, 0, PChar(LName), nil);
Memo1.Lines.Add(Format('%d:%s', [LWnd, LName]));
if (LWnd <> 0) then
begin
LName := 'SysPager';
LWnd := FindWindowEx(LWnd, 0, PChar(LName), nil);
Memo1.Lines.Add(Format('%d:%s', [LWnd, LName]));
if (LWnd <> 0) then
begin
LName := 'ToolbarWindow32';
LWnd := FindWindowEx(LWnd, 0, PChar(LName), nil);
Memo1.Lines.Add(Format('%d:%s', [LWnd, LName]));
if (LWnd <> 0) then
begin
//画像を表示する予定
//UpdateImage(LWnd);
end
else
Memo1.Lines.Add(SysErrorMessageUTF8(GetLastError()));
end
else
Memo1.Lines.Add(SysErrorMessageUTF8(GetLastError()));
end
else
Memo1.Lines.Add(SysErrorMessageUTF8(GetLastError()));
end
else
Memo1.Lines.Add(SysErrorMessageUTF8(GetLastError()));
end;

となりました。単純に一番上から見つかればその次その次その次と調べてるだけです。で失敗したらエラーを出す。もう少し何とかならんか感がすごいです。

話ずれてきますけど、こんな感じにしてみます。
LWnd := TraceWindow(0, 'Shell_TrayWnd,TrayNotifyWnd,SysPager');

「辿る」が「trace」らしいので、ウィンドウの階層をたどっていく意味を込めてます。込めるのは自由です。
戻り値が0だった時(ウィンドウが見つからなかった時)にGetLastErrorを呼べば、ちゃんと最後のFindWindowExのエラーを戻してくれるだろうと信じて、エラーの内容を戻すようなvar引数はなしで。

行けたか?
// ex) LWnd := TraceWindow(0, 'Shell_TrayWnd,TrayNotifyWnd');
function TraceWindow(const aWnd: HWND; const CsNames: String): HWND;
var
LBuf: TStringList;
LName: String;
begin
Result := aWnd;
LBuf := TStringList.Create;
try
LBuf.CommaText := Trim(CsNames);
while (LBuf.Count > 0) do
begin
LName := LBuf[0]; LBuf.Delete(0);
Result := FindWindowEx(Result, 0, PChar(LName), nil);
end;
finally
FreeAndNil(LBuf);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
LWnd: HWND;
begin
LWnd := TraceWindow(0, 'Shell_TrayWnd,TrayNotifyWnd,SysPager,ToolbarWindow32');
if (LWnd <> 0) then
begin
Memo1.Lines.Add(Format('%d:ToolbarWindow32(のハズ)', [LWnd]));
UpdateImage(LWnd);
end
else
Memo1.Lines.Add(SysErrorMessageUTF8(GetLastError()));
end;

繰り返しがなくなってちょっとすっきりです。

さてウィンドウハンドルはとれたので、通知領域の画像をキャプチャしてみます。
ウィンドウハンドルはわかっているので
GetWindowDC 関数:[2012/10/02]
ReleaseDC 関数:[2012/10/02]
このふたつはセットで。

めんどくさいのは、デバイスコンテキスト(未だに雰囲気不明)を戻されても、画面とかTCanvasにどうやって表示するのよ、です。

それはこのへんで。
Torry's Delphi Pages:[2012/10/02]

サンプルはスクリーン全体なのでそのままじゃ使えません。今回は特定のウィンドウ限定で取得したいので、ウィンドウの描画サイズを取得します。
ウィンドウハンドルはわかっているので
GetWindowInfo 関数:[2012/10/02]
WINDOWINFO structure:[2012/10/02]
なぜかGetWindowInfoの説明頁に構造体へのリンクがないので、雰囲気の違う頁をつないでますけど、この構造体の中のrcWindowがそれです。

で、フォームにTImageをおいてる前提で
uses
JwaWinUser;
procedure TForm1.UpdateImage(const WH: HWND);
var
LCanvas: TCanvas;
LWI: TWindowInfo;
LRect: TRect;
begin
LCanvas := TCanvas.Create;
LCanvas.Handle := GetWindowDC(WH);
try
if GetWindowInfo(WH, LWI) then
begin
LRect := LWI.rcWindow;
OffsetRect(LRect, - LRect.Left, - LRect.Top);
Image1.Picture.Bitmap.Width := LRect.Right - LRect.Left;
Image1.Picture.Bitmap.Height := LRect.Bottom - LRect.Top;
Image1.Picture.Bitmap.Canvas.CopyRect(LRect, LCanvas, LRect);
end;
finally
ReleaseDC(WH, LCanvas.Handle);
FreeAndNil(LCanvas);
end;
end;

簡単に書いてますけど、毎回描画領域に悩みます。今回も何度となく真っ黒にしか表示されず嫌になりかけました。取得した通知領域の矩形の左端をゼロゼロにすることで解決しましたけど、行き当たりばったりなので時間がかかってしまいました。あとJwaWinUserを見つけるの、大変でした。

Button1クリックで、通知領域のウィンドウハンドル(この場合は65650)をとってきて、それを使ってウィンドウのイメージを取得してます。


目的見失いかけ。続く。
関連記事
スポンサーサイト