C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ウインドウのスクリーンショットで枠の外を表示しない方法

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■92657 / inTopicNo.1)  ウインドウのスクリーンショットで枠の外を表示しない方法
  
□投稿者/ JJ (1回)-(2019/10/18(Fri) 14:02:22)

分類:[.NET 全般] 

ウインドウのスクリーンショットを保存したいのですが、
以下のようにして、指定したフォームの画像を保存することができます。


Dim bmp As New Bitmap(frm.Width, frm.Height)

Dim g As Graphics = Graphics.FromImage(bmp)

g.CopyFromScreen(New Point(frm.Left, frm.Top), New Point(0, 0), bmp.Size)
g.Dispose()

bmp.Save(FileOutPath_PNG)
bmp.Dispose()


Win7の時はこれで良かったのですが、
Win10の場合、ウインドウの枠幅が狭いせいか、
ウインドウの少し外側の背景まで一緒にイメージが保存されてしまいます。

Altキー+スクリーンショットキーを押すと
ウインドウの中身だけのスクリーンショットを撮ることができるので
何か方法があると思うのですが、
どのようにすれば良いですか?

ちなみに、ウインドウのクライエント領域だけでなく、
タイトルバーも保存したいと考えています。


引用返信 編集キー/
■92661 / inTopicNo.2)  Re[1]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ 魔界の仮面弁士 (2428回)-(2019/10/18(Fri) 14:42:15)
No92657 (JJ さん) に返信
> Win7の時はこれで良かったのですが、
> Win10の場合、ウインドウの枠幅が狭いせいか、
> ウインドウの少し外側の背景まで一緒にイメージが保存されてしまいます。

Windows 10 においては、フォームの有効なサイズを
取得するために、追加の手続きが必要になります。

https://social.msdn.microsoft.com/Forums/ja-JP/24a29e45-afde-448a-9958-d2fb2dfd4183/12501124571254012512124342000612409123902120512363123751239012?forum=vsgeneralja

引用返信 編集キー/
■92672 / inTopicNo.3)  Re[2]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ JJ (2回)-(2019/10/18(Fri) 20:47:45)
DwmGetWindowAttributeのAPIを使えばできるのですね
どうもありがとうございました。

解決済み
引用返信 編集キー/
■92673 / inTopicNo.4)  Re[3]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ JJ (3回)-(2019/10/18(Fri) 21:00:50)
すいません、Win10だとうまくいったのですが、
Win7でやると
DwmGetWindowAttributeで値を取得することができません。
特にエラーは出ないのですが、座標が全て0になってしまいます。
どうすれば良いですか?

コードは以下の通りです。


    Private Enum DWMWINDOWATTRIBUTE
        DWMWA_NCRENDERING_ENABLED = 1
        DWMWA_NCRENDERING_POLICY
        DWMWA_TRANSITIONS_FORCEDISABLED
        DWMWA_ALLOW_NCPAINT
        DWMWA_CAPTION_BUTTON_BOUNDS
        DWMWA_NONCLIENT_RTL_LAYOUT
        DWMWA_FORCE_ICONIC_REPRESENTATION
        DWMWA_FLIP3D_POLICY
        DWMWA_EXTENDED_FRAME_BOUNDS
        DWMWA_LAST
    End Enum
    Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" (
     ByVal hwnd As IntPtr,
     ByVal dwAttribute As DWMWINDOWATTRIBUTE,
     ByRef pvAttribute As RECT,
     ByVal cbAttribute As Int32) As Boolean

    Private Structure RECT
        Property Left As Integer
        Property Top As Integer
        Property Right As Integer
        Property Bottom As Integer
    End Structure




            Dim wombat As RECT
            DwmGetWindowAttribute(frm.Handle, DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, wombat, Marshal.SizeOf(wombat))

            Dim bmp As New Bitmap(wombat.Right - wombat.Left, wombat.Bottom - wombat.Top)

            Dim g As Graphics = Graphics.FromImage(bmp)

            g.CopyFromScreen(New Point(wombat.Left, wombat.Top), New Point(0, 0), bmp.Size)
            g.Dispose()

            bmp.Save(FileOutPath_PNG)
            bmp.Dispose()

引用返信 編集キー/
■92674 / inTopicNo.5)  Re[4]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ Hongliang (903回)-(2019/10/18(Fri) 21:32:53)
Windows APIでは、関数呼び出しの失敗は一般的に例外ではなく返値として返ってきます。

https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/nf-dwmapi-dwmgetwindowattribute
DwmGetWindowAttributeの返値はHRESULTとなっているので、Declare文においてBooleanではなくInt32と返値を定義する必要があります。
また呼び出したときは返値をちゃんと確認して成功かどうかを判断する必要があります。
HRESULTの時の成功を意味する値は基本的にS_OK、数値としては0です。
なお、HRESULTの値は、System.Runtime.InteropServices.Marshal.GetExceptionForHRメソッドで例外オブジェクトに変換できます。

ところで、そのWindows 7は、テーマをWindows 7 ベーシックにしていたりしませんか?
DwmGetWindowAttributeはその名の通り、DWMが有効な環境でないと有効ではないはずです。
https://ja.wikipedia.org/wiki/Desktop_Window_Manager
引用返信 編集キー/
■92675 / inTopicNo.6)  Re[5]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ JJ (4回)-(2019/10/18(Fri) 21:39:59)
ありがとうございます。

そういうことでしたか

仰る通り、Windows 7 ベーシックを使用しています。

Windows 7のPCでもエアロの場合とベーシックの場合があると思いますが、
どちらを使っているか調べるにはどのようにしたら良いですか?

あと、DwmGetWindowAttributeと同じようなAPIで
Windows 7 ベーシックでも使えるAPIってありますか?

普通に.NETの方法で取得しても良いのですが、
それぞれコードを書かないといけなくなる面倒なので、
取得部分だけを分岐させたいのですが。






引用返信 編集キー/
■92676 / inTopicNo.7)  Re[6]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ Hongliang (904回)-(2019/10/18(Fri) 21:46:41)
んー、単純にDwmGetWindowAttributeの呼び出しに失敗したらFormのBoundsを使う、で良いんじゃないですかね。

それから、座標周りの話であれば、ディスプレイのDPI設定(Win7ならコントロールパネル→デスクトップのカスタマイズ→ディスプレイ→テキストやその他の項目の大きさの変更、で設定する値)が異なる環境でそれぞれに想定通り動作するか確認した方が良いです。
引用返信 編集キー/
■92677 / inTopicNo.8)  Re[4]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ 魔界の仮面弁士 (2429回)-(2019/10/18(Fri) 22:02:44)
No92673 (JJ さん) に返信
> Private Declare Function DwmGetWindowAttribute Lib "dwmapi.dll" (
Vista 未満の環境も考慮する場合は、
LoadLibrary/FreeLibrary + LoadLibrary & Marshal.GetDelegateForFunctionPointer で。


> Win7でやると
Aero 無効なデスクトップ テーマが選択されていませんか?


> DwmGetWindowAttributeで値を取得することができません。
> 特にエラーは出ないのですが、
DwmGetWindowAttribute の戻り値は HRESULT ですので、As Boolean で受けてはいけません。
戻り値を As Integer で受けるようにして、

Dim result = DwmGetWindowAttribute(〜〜)
If result <> 0 Then
  'Throw New Win32Exception(result)
End If

のように処理してみてください。


> 座標が全て0になってしまいます。

API の戻り値が非ゼロになっていませんか?
引用返信 編集キー/
■92679 / inTopicNo.9)  Re[7]: ウインドウのスクリーンショットで枠の外を表示しない方法
□投稿者/ JJ (6回)-(2019/10/19(Sat) 12:15:42)
なるほどうまくいきました
ありがとうございます。

解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ