質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

1回答

3959閲覧

アクティブウィンドウが正しくキャプチャされない

ab03

総合スコア5

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Windows

Windowsは、マイクロソフト社が開発したオペレーティングシステムです。当初は、MS-DOSに変わるOSとして開発されました。 GUIを採用し、主にインテル系のCPUを搭載したコンピューターで動作します。Windows系OSのシェアは、90%を超えるといわれています。 パソコン用以外に、POSシステムやスマートフォンなどの携帯端末用、サーバ用のOSもあります。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

0グッド

0クリップ

投稿2017/12/03 05:31

###前提・実現したいこと
Visual Studio 2017を用いてC#でWindowsフォームアプリケーションを作成しています。

以下のサイトの「Win32 APIを使用する方法」を参考にしながらアクティブウィンドウのスクリーンショットを撮り保存する機能を実装しました。

http://dobon.net/vb/dotnet/graphics/screencapture.html

私の理想はこの通りです。
理想SS

しかし保存された画像は私の想像していた画像とは少し違いました。

###発生している問題・エラーメッセージ
保存された画像は以下になります。
テストSS

ウィンドウの左右と下の部分にアクティブウィンドウの後ろ側の壁紙(紫色の部分)が表示されてしまいます。
私はこれを解決する方法を調べたのですがお恥ずかしながら私では解決できませんでした。

###該当のソースコード

C#

1private void screenshot() 2{ 3 Bitmap bmp; 4 bmp = CaptureActiveWindow(); 5 string outputPath = "./" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".png"; 6 bmp.Save(outputPath, System.Drawing.Imaging.ImageFormat.Png); 7 bmp.Dispose(); 8} 9 10private const int SRCCOPY = 13369376; 11private const int CAPTUREBLT = 1073741824; 12 13[DllImport("user32.dll")] 14private static extern IntPtr GetDC(IntPtr hwnd); 15 16[DllImport("gdi32.dll")] 17private static extern int BitBlt(IntPtr hDestDC, 18 int x, 19 int y, 20 int nWidth, 21 int nHeight, 22 IntPtr hSrcDC, 23 int xSrc, 24 int ySrc, 25 int dwRop); 26 27[DllImport("user32.dll")] 28private static extern IntPtr ReleaseDC(IntPtr hwnd, IntPtr hdc); 29[StructLayout(LayoutKind.Sequential)] 30private struct RECT 31{ 32 public int left; 33 public int top; 34 public int right; 35 public int bottom; 36} 37 38[DllImport("user32.dll")] 39private static extern IntPtr GetWindowDC(IntPtr hwnd); 40 41[DllImport("user32.dll")] 42private static extern IntPtr GetForegroundWindow(); 43 44[DllImport("user32.dll")] 45private static extern int GetWindowRect(IntPtr hwnd, 46 ref RECT lpRect); 47 48/// <summary> 49/// アクティブなウィンドウの画像を取得する 50/// </summary> 51/// <returns>アクティブなウィンドウの画像</returns> 52public static Bitmap CaptureActiveWindow() 53{ 54 //アクティブなウィンドウのデバイスコンテキストを取得 55 IntPtr hWnd = GetForegroundWindow(); 56 IntPtr winDC = GetWindowDC(hWnd); 57 //ウィンドウの大きさを取得 58 RECT winRect = new RECT(); 59 GetWindowRect(hWnd, ref winRect); 60 //Bitmapの作成 61 Bitmap bmp = new Bitmap(winRect.right - winRect.left, 62 winRect.bottom - winRect.top); 63 //Graphicsの作成 64 Graphics g = Graphics.FromImage(bmp); 65 //Graphicsのデバイスコンテキストを取得 66 IntPtr hDC = g.GetHdc(); 67 //Bitmapに画像をコピーする 68 int bmpWidth = bmp.Width; 69 int bmpHeight = bmp.Height; 70 BitBlt(hDC, 0, 0, bmpWidth, bmpHeight, 71 winDC, 0, 0, SRCCOPY); 72 //解放 73 g.ReleaseHdc(hDC); 74 g.Dispose(); 75 ReleaseDC(hWnd, winDC); 76 77 return bmp; 78} 79

###試したこと
BitBlt(hDC, 0, 0, bmpWidth, bmpHeight,winDC, 0, 0, SRCCOPY);
で第2、第3引数の値を増加させると画像の左上が、bmpWidthとbmpHeightの値に対して減算すると画像の右下がトリミングされることが最初わかりました。

しかしアクティブウィンドウのサイズによって先に述べた紫色の部分の領域のサイズが変わることに気付き、固定値の加減算では上手く動作しないことがわかりました。

他の方法として最初に上げたリンク先の「.NET Framework 2.0以降で、Graphics.CopyFromScreenメソッドを使用する方法」はアクティブウィンドウのみでは無いため断念。

「Print Screenキーストロークを送信する方法」は画像を取得することが出来ず断念しました。

何卒よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

画像を見るに、Getting a window rectangle without the drop shadow - Articles and information on C# and .NET development topics • Cyotekのスクリーンショット結果に似ているように見えますね。
サイトの方によると、OSのバージョンを調べて、Vista以降ならDwmGetWindowAttributeを使ってDWMWA_EXTENDED_FRAME_BOUNDSを取得することでドロップシャドウなしの矩形領域を得られるそうですが、それを試してみるのはいかがでしょう。

[追記]
残念ですね、うまくいきませんでしたか...
2012年の投稿ですが、Bug in DwmGetWindowAttribute DWMWA_EXTENDED_FRAME_BOUNDS
に寄せられた回答に、Windows 8ではDWMWA_EXTENDED_FRAME_BOUNDSとGetWindowRectが同じになるという報告がありました。バージョンによって挙動がころころ変わるんでしょうかね?

ご参考までに、私の環境の場合(Windows 10 バージョン1703)ですと、下記のようにしたところ、いい感じにドロップシャドウなしの画像が得られました。
ネイティブメソッド関連を別クラスにまとめたためメソッド等の頭にNativeMethodsが付いていますが、お気になさらないでください。
winDCの原点はGetWindowRectで得られる左上座標っぽかったので、DWMWA_EXTENDED_FRAME_BOUNDSで得た左上座標との差をBitBltのソース座標としましたが、それ以外は特にご質問者さんと違ったことはしていないかと思います。

C#

1 public static Bitmap CaptureActiveWindow() 2 { 3 //アクティブなウィンドウのデバイスコンテキストを取得 4 IntPtr hWnd = NativeMethods.GetForegroundWindow(); 5 IntPtr winDC = NativeMethods.GetWindowDC(hWnd); 6 //ウィンドウの大きさを取得 7 NativeMethods.RECT winRect = new NativeMethods.RECT(); 8 NativeMethods.DwmGetWindowAttribute( 9 hWnd, 10 NativeMethods.DWMWA_EXTENDED_FRAME_BOUNDS, 11 out var bounds, 12 Marshal.SizeOf(typeof(NativeMethods.RECT))); 13 NativeMethods.GetWindowRect(hWnd, ref winRect); 14 //Bitmapの作成 15 var offsetX = bounds.left - winRect.left; 16 var offsetY = bounds.top - winRect.top; 17 Bitmap bmp = new Bitmap(bounds.right - bounds.left, 18 bounds.bottom - bounds.top); 19 //Graphicsの作成 20 Graphics g = Graphics.FromImage(bmp); 21 //Graphicsのデバイスコンテキストを取得 22 IntPtr hDC = g.GetHdc(); 23 //Bitmapに画像をコピーする 24 int bmpWidth = bmp.Width; 25 int bmpHeight = bmp.Height; 26 Console.WriteLine(winRect); 27 NativeMethods.BitBlt(hDC, 0, 0, bmpWidth, bmpHeight, 28 winDC, offsetX, offsetY, NativeMethods.SRCCOPY); 29 //解放 30 g.ReleaseHdc(hDC); 31 g.Dispose(); 32 NativeMethods.ReleaseDC(hWnd, winDC); 33 34 return bmp; 35 }

投稿2017/12/03 06:58

編集2017/12/03 22:38
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ab03

2017/12/03 10:33

ご回答ありがとうございます。 1時間ほど試してみてるのですが上手く短径領域を得ることが出来ません。 もう少し頑張らせて頂きます。
ab03

2017/12/03 13:46 編集

大変申し訳ないです。どうしても出来ません。 環境はWindows10です。 IntPtr hWnd = GetForegroundWindow(); RECT winRect = new RECT(); DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out winRect, Marshal.SizeOf(winRect)); Bitmap bmp = new Bitmap(winRect.right - winRect.left, winRect.bottom - winRect.top); で生成しているはずなのですが値が変わらず… 今日は一旦諦めてみます。 解答ありがとうございました。
ab03

2017/12/05 14:11

追記のご回答ありがとうございます! Bongo様のメソッドをコピペしたところ動作致しました… 今から原因を究明したいと思います。 取り急ぎ動作した旨をご報告させていただきます。ありがとうございました!
ab03

2017/12/05 14:31

原因がわかりました。 DwmGetWindowAttribute( hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, out winRect, // ←ここが原因! Marshal.SizeOf(winRect) ); DwmGetWindowAttributeでせっかく影無しの座標を取得したにも関わらずその後 GetWindowRect(hWnd, ref winRect); でwinRectを上書きしていたというとても恥ずかしいミスでした。 プログラム自体は初めてでは無いのですがC#(.NET)は初めてだったのでずっと「私がC#についてまだ理解しきれていないんだ!」と思い込んでいてこのような単純なミスを見逃しておりました… 煮詰めすぎず頭を一度リフレッシュする必要性を知り、大変良い勉強になりました。 重ねてですがお付き合いくださり本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問