アプリのキャンバスハンドル取得方法

解決


おらくるる  2008-10-31 00:08:59  No: 32411

あるアプリのキャンバスのハンドルを取得し、TEXTOUTで文字を出力したいのですがFindWindow(nil,'アプリのタイトル');ではアプリのハンドルまでで、キャンバスのハンドルは取得できなくて困っております
何か良い方法はないでしょうか?


monaa  2008-10-31 00:35:56  No: 32412

TCanvasはDelphi製のアプリケーションのみに存在します
msdnに行けば分かると思いますが、
BOOL TextOut(
  HDC hdc,           // デバイスコンテキストのハンドル
  int nXStart,       // 開始位置(基準点)の x 座標
  int nYStart,       // 開始位置(基準点)の y 座標
  LPCTSTR lpString,  // 文字列
  int cbString       // 文字数
);
おっしゃっているのはデバイスコンテキストのハンドルですよね?
ならば
ウィンドウハンドルからデバイスコンテキストのハンドルを取得する方法でしょう。
そこまでわかれば、ネットに転がっています。頑張ってください。
調べた結果をここにレポートしてくれれば同じようにここに来た人の為になるかもしれません。


おらくるる  2008-11-01 22:09:00  No: 32413

>>monaaさん
返信ありがとうございます、以下調べた結果です

var
  hw: HWND;
  DC: HDC;
begin
  hw := FindWindow(nil,'無題 - メモ帳');
  if hw = 0 then exit;
    DC := GetWindowDC(hw);    // タイトルバーも含める場合
  if DC <> 0 then
  begin
    TextOut(dc,100,100,'test',4); 
  end;
  ReleaseDC(hw, DC);
end;

これでtestと描画できるのですが、描画してる場所に別窓を重ねると消えてしまいます、調べると再描画しないといけないみたいで・・
WndProcのWM_PRINTで再描画すればいけるみたいですが、WndProcがいまいち理解できていなくて作業が止まっております
わかりやすいサイト等を教えていただけませんか?


monaa  2008-11-02 01:01:24  No: 32414

そうですね、別アプリケーションのWindowは基本的に他人様ですからね。
アプリケーションによってはWM_PRINT無しで再描画を行う物もあるので万能な描画方法は無いと考えてください。
WM_PRINTをフックしたいのであればSetWindowsHookExを使用します。
これはなかなか敷居が高いですよ。
見た目を気にしないのであれば、Timerで連続描画させればいいと思います。
勝手にオブジェクトを置いてしまうという手もあります
procedure TForm1.Button1Click(Sender: TObject);
var
  hw:HWND;
begin
  ShellExecute(Handle,'open','c:\windows\Notepad.exe','','',SW_SHOWDEFAULT);
  sleep(1000);
  hw := FindWindow(nil,'無題 - メモ帳');
  Windows.SetParent(Button1.Handle,hw)
end;


Mr.XRAY  2008-11-02 04:22:16  No: 32415

Canvasというか,デバイスコンテキストへの描画には注意が必要です.
例えば以下のコードを実行し,
このForm1の上に他のウィンドウを表示し,再度前面に表示すると,
「埼玉県」という文字は消えてしまいます.

procedure TForm1.Button1Click(Sender: TObject);
begin
    Form1.Canvas.Font.Size :=50;
    Form1.Canvas.TextOut(10,20,'埼玉県');

    Image1.Canvas.Font.Size :=50;
    Image1.Canvas.TextOut(20,20,'千葉県');
end;

これを常に表示するようにするには,描画が必要になった時点で,
WM_PAINTなどのメッセージを処理して描画します.

自身のスレッド内のプロセスであれば,WindowProc: TWndMethod;をフックして
行うこともできますが,他のアプリの場合は,この方法が利用できません.
他のアプリの場合は,グローバルなフックを使用して,メッセージを処理します.
ただ,このフックは,DLL内にないと,他のアプリのメッセージを捕らえることが
できません.

これは,結構面倒です.
必要であれば,フックとかフック関数,メッセージなどで調べてください.

質問内容とは関係ありせんが,質問の際は,開発環境を書くようにした方が
いいですね.Vistaも出て,Delphi2009も発売され,環境によって動作が
違い場合が,これからはもっと顕著になるでしょう.

もっとも,初心者の場合は,質問は仕方はこれでいいのか,緊張して,
いろいろ調べて発言しますが,
ベテランになると,どうしてもうっかりということがよくあります(笑).


Mr.XRAY  2008-11-02 04:31:02  No: 32416

>DLL内にないと,他のアプリのメッセージを捕らえることが

失礼.DLLにしなくてもマウスやキーのメッセージであればフックする
ことが可能です.


ケセランパサラン  2008-11-02 06:16:22  No: 32417

>>monaaさん
Windows.SetParent(Button1.Handle,hw)
SetParentでボタンではなくラベルははれるのでしょうか?
Handleがないので無理ですかね?


monaa  2008-11-03 03:51:09  No: 32418

えっと、こういうハッキング的な技術は汎用法はありません。
対象アプリケーションの挙動を確認してそれに合わせた挙動を見つけなくてはなりません。
Labelに何故ハンドルがないのかは自分で調べてください、
確かにVCLがよくできていてEditやButtonと同列に並べているので間違いやすいと言えば間違えやすいです。Windowsで親子関係を作れるのはWindowオブジェクトのみです。(ハンドルを持ってるやつね)
私なりにメモ帳を調べたところ、”メモ帳の場合”は以下のようにすればオブジェクトの配置が可能です。
親EditコントロールがWS_CLIPCHILDRENを持っていなかったので追加します。

くどいようですがこういうプログラミングは相手のアプリケーション次第です。どんなアプリケーションに対してどのようなアプローチをするかは私は詮索しませんが、汎用法などありません。目的のアプリにラベルが張れるかどうかはあなた以外知ることはできません。

procedure TForm1.Button1Click(Sender: TObject);
var
  hw:HWND;
  i:Integer;
begin
  ShellExecute(Handle,'open','c:\windows\Notepad.exe','','',SW_SHOWDEFAULT);
  sleep(1000);
  hw := FindWindow('Notepad',nil);
  hw := FindWindowEx(hw,0,'Edit',nil);
  i:= GetWindowLong(hw,GWL_STYLE );
  SetWindowLong(hw,GWL_STYLE,i or WS_CLIPCHILDREN);
  Windows.SetParent(Panel1.Handle,hw);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Caption:='set';
  Panel1.ParentBackground:=False;
end;


おらくるる  2008-11-03 22:35:48  No: 32419

>>monaaさん
いろいろありがとうございました、無事理想的なことができましたので解決させていただきます


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加