DOBON.NET DOBON.NETプログラミング掲示板過去ログ

Graphics.FromHwnd(0)はなぜNG?

環境/言語:[WindowsXP,VB2005]
分類:[.NET]

2006/03/22(Wed) 22:42:45 編集(投稿者)
2006/03/22(Wed) 22:23:30 編集(投稿者)

いつもお世話になります。

デスクトップに直接描画したいのですが,

<DllImport("user32.dll")> _
Shared Function GetDC( _
ByVal hWnd As IntPtr) _
As IntPtr
End Function

Dim g As Graphics = Graphics.FromHdc(GetDC(IntPtr.Zero))

はOKなのですが,

Dim g As Graphics = Graphics.FromHwnd(IntPtr.Zero)

はNGなのです。(エラーにはならないが描画できない)

上のやり方でやればよいだけなのですが,下のやり方でなぜできないのか
気になって質問いたしました。
ご存じの方がいらっしゃいましたらご教授ください。
いろいろ試して見たのですが,次のコードも私にはよさそうに見えるのですがNGです。

    <DllImport("user32.dll")> _
    Shared Function GetDC( _
        ByVal hWnd As IntPtr) _
        As IntPtr
    End Function

    <DllImport("user32.dll")> _
    Shared Function GetDesktopWindow() As IntPtr
    End Function

    Dim g As Graphics = Graphics.FromHdc(GetDC(GetDesktopWindow))

次のもNGです。

    Dim g As Graphics = Graphics.FromHwnd(GetDesktopWindow)

う〜ん。何がいけないんでしょう。
> 上のやり方でやればよいだけなのですが,下のやり方でなぜできないのか
> 気になって質問いたしました。

デスクトップウィンドウのウィンドウハンドルの値は NULL ではないからです。

一部の API は NULL を渡すとデスクトップウィンドウのハンドルを渡したのと「同じ」動作をしますが、だからと言って「デスクトップウィンドウのウィンドウハンドル値が NULL である」わけではありません。
2006/03/22(Wed) 23:10:07 編集(投稿者)

渋木宏明(ひどり)様,ご返答ありがとうございます。

> デスクトップウィンドウのウィンドウハンドルの値は NULL ではないからです。
>
> 一部の API は NULL を渡すとデスクトップウィンドウのハンドルを渡したのと
>「同じ」動作をしますが、だからと言って「デスクトップウィンドウのウィンド
>ウハンドル値が NULL である」わけではありません。

確か大昔のCマガだったかInsideWindowsだったかにデスクトップのウィンドウハンドルは"0"という記事があったような記憶があり,ずっとそれを信じていました...

GetDesktopWindowという関数が存在する理由が今までわからなかったのですが,謎が解け,すっきりしました!
確かにDebug.Print(GetDesktopWindow.ToString)とすると決して0にはならないので,今まで不思議に思っていました。

すると,No.14969に書いたようなコードが比較的正しいものに近いと思われますが,動作しません。
なぜなのでしょう......
2006/03/23(Thu) 00:00:14 編集(投稿者)

> すると,No.14969に書いたようなコードが比較的正しいものに近いと思われますが,動作しません。
> なぜなのでしょう......

「動作しない」というのは、どんな現象を指してるでしょう?
たとえば、例外が throw されたりしますか?

デスクトップウィンドウの hwnd も、hdc もちゃんと取得できているはずです。
なんせ「デスクトップ」ですから、通常はすべてのウィンドウの最背面に存在します。

さらに IE4 以降のデスクトップ統合により、Windows 3.1 から継承されている本来のデスクトップウィンドウは、シェルの広げているいわゆる「デスクトップ」として見えるウィンドウによって隠されていたと思います。
(Spy++ で確認してみましたが、キャプションが "Program Manager" になってますね)
渋木宏明(ひどり)様,ご返答ありがとうございます。

> 「動作しない」というのは、どんな現象を指してるでしょう?
> たとえば、例外が throw されたりしますか?

例外はスローされません。
エラーらしきものは何も報告されません。
何も起こりません....

g.DrawLine(Pens.Black, 0, 0, 100, 100)
などとしても描画もされません。

Dim g As Graphics = Graphics.FromHdc(GetDC(IntPtr.Zero))
とした場合には黒い線が画面左上に引かれます。

ヒントをいただいた,デスクトップ統合とシェルのウィンドウについて調べてみます。
SPY++で調べたところ,デスクトップは次のようなウィンドウで構成されていることがわかりました。

+ 00010014 "#32769(デスクトップ)
|
+-+ 0001009C 'Program Manager' Progman
  |
  +-+ 00100A6 "SHELLDLL_DefView
    |
    +- 000100A8 'FolderView' SysListView32

GetDesktopWindow関数は00010014のデスクトップを返します。
Graphics.FromHwnd()に上のウィンドウ4つを渡して試したところ,
000100A8のときにデスクトップに描画することが出来ました。

ただ,GetDC(IntPtr.Zero)のときには「全てのウィンドウの上」に描画される
ようになるのに対し,FromHwnd(New Intptr(&H000100A8))のときには
「全てのウィンドウの下」に描画されるようです。

うまく説明できませんが,GetDCはウィンドウの重なり等は無視で,とにかく
デスクトップに描画されます。
FolderViewに描画した場合はあくまでFolderViewに描かれ,上にウィンドウが
重なっているとそこには描かれないのです。

GetDC(0)で得られるハンドルはデスクトップのウィンドウのデバイスコンテキスト
ハンドルなのではなくて,ディスプレイに直接描画するようなちょっと特殊な
ハンドルのように思います。
2006/03/23(Thu) 09:42:41 編集(投稿者)
2006/03/23(Thu) 00:59:09 編集(投稿者)

GetDCのヘルプをよ〜く読んで考えた,私の結論は,

・Window98・Window2000以降ではGetDC(IntPtr.Zero)でプライマリモニタの
 デバイスコンテキストのハンドルが取得できる。
・しかし,このプライマリモニタのデバイスコンテキストのハンドルというのは
 デスクトップウィンドウのデバイスコンテキストのハンドルとは異なったもの
 である。
・デスクトップウィンドウのデバイスコンテキストのハンドルに描画をしても
 表示されない。

・よって,画面に落書きをするようなアプリケーションではGetDC(IntPtr.Zero)
 を使うことになる。

です。
頭の中がだいぶすっきりしました。

渋木宏明(ひどり)様,何度もご返答いただきましてありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板