質問者
PCによって、Graphics.CopyFronScreenでキャプチャ画像の左上の座標が異なる件

質問
-
Visual C#で開発したソフトウェア製品(VS2005?使用)ですが、お客様のPCで印刷がうまくできないとのクレームが来ました。
Form全体をBMPのキャプチャ画像で取り込み、それを保存したり、印刷したりしますが、BMP自体がForm全体でなく
Formやや左上のWindows画面からはじまり、大きさはFormと同じなのでFormの右下部分はうつっていません。
当方で試したところ、Delのデスクトップ、ノートPC、NECのデスクトップでは正常動作しましたが、東芝のノートPCでは
Formの右下がBMPデータからはみだしています。
どのような原因が考えられるでしょうか。また解決法としてどのような措置があるでしょうか(PCの設定を変える?)。
私としてはFormをキャプチャに取り込む箇所に問題があると考え、該当箇所のプログラムを添付しました。
特に「private void CaptureScreen()」内部が問題だと考えます。
Graphics.FopyFromScreenがPCによって、Form全体を正しく取り込まず、座標が左上にずれてしますようです。
なにかしらアドバイスをお願いいたします。
//印刷関係 private void btnPrint_Click(object sender, EventArgs e) { CaptureScreen(); Thread.Sleep(100); printDocument1.Print(); } private void CaptureScreen() { Graphics myGraphics = this.CreateGraphics(); Size s = this.Size; memoryImage = new Bitmap(s.Width, s.Height, myGraphics); Graphics memoryGraphics = Graphics.FromImage(memoryImage); memoryGraphics.CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, s); } private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e) { e.Graphics.DrawImage(memoryImage, 0, 0); } //データ保存関係 private void menuWrite_Click(object sender, EventArgs e) { CaptureScreen(); saveFileDialog1.InitialDirectory = System.Environment.GetFolderPath(Environment.SpecialFolder.Personal); saveFileDialog1.Filter = "BMP形式(*.Bmp)|*.Bmp|すべてのファイル(*.*)|*.*"; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { string filename = saveFileDialog1.FileName; memoryImage.Save(filename); MessageBox.Show("保存しました"); } }
すべての返信
-
ディスプレイが100%(96dpi)以外の設定になっているからです。
高DPIの対応(DPI aware)が必要です。たとえば150%(144dpi)の環境だったとして、何も対策しないとthis.CreateGraphics().DpiXとDpiYは96を返します。
ディスプレイが144dpi座標系であるのに96dpi座標系でCopyFromScreenするためにずれて取得されているのです。VS2017やVS2019の環境で再ビルドできるのであれば、プロジェクトに新しい項目の追加で、アプリケーションマニフェストのアイテムテンプレートがあるので追加します。
これでプロジェクトのプロパティのアプリケーションリソースでマニフェストを埋めこんでくれる設定になります。そして以下のコメントになっている部分を解除して再ビルドします。
<!-- アプリケーションが DPI 対応であり、それ以上の DPI で Windows によって自動的にスケーリングされないことを 示します。Windows Presentation Foundation (WPF) アプリケーションは自動的に DPI に対応し、オプトインする必要は ありません。さらに、この設定にオプトインする .NET Framework 4.6 を対象とする Windows Forms アプリケーションは、 app.config ファイルで 'EnableWindowsFormsHighDpiAutoResizing' 設定を 'true' に設定する必要があります。--> <!--以下のコメントを解除 --> <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings> </application>
どうしても古い環境でビルドしなければならないのであれば、マニフェストを自分で記述するか、新しいVSで作ったファイルをコピーするなどして、プロジェクトのプロパティに登録してください。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 編集済み gekkaMVP 2020年8月14日 9:51
- 回答の候補に設定 kumo-msftMicrosoft contingent staff, Moderator 2020年8月18日 8:12
-
Formやや左上のWindows画面からはじまり、大きさはFormと同じなのでFormの右下部分はうつっていません。
当方で試したところ、Delのデスクトップ、ノートPC、NECのデスクトップでは正常動作しましたが、東芝のノートPCではFormの右下がBMPデータからはみだしています。
期待動作する PC と、ズレの発生する PC の OS バージョンは同一でしょうか。OS が同じ場合は、デスクトップテーマを合わせてみてください。また、解像度設定(100% か否か)に違いは無いでしょうか?
Windows 10 の場合、フォームの周辺に薄く影を落とすようになっており、this.Size や this.DesktopBounds はその分を含めた領域を指し示します。この影の領域サイズはデスクトップテーマによっても異なるようですが、概ね、7px 程度のズレになるようです。正しいサイズを得るために、DwmGetWindowAttribute を試してみてください。
該当箇所のプログラムを添付しました。
以下、本題からは外れますが:
- UI スレッドで Thread.Sleep を呼び出すことは避けましょう。遅延実行が必要なら、Timer の Tick イベント等を利用してください。
- CreateGraphics や FromImage で生成したリソースが Dispose されていません。using ステートメントを使いましょう。
- 同様に、memoryImage に割り当ててある Bitmap リソースも、本来は使用後に Dispose 処理が必要です。
- 編集済み 魔界の仮面弁士MVP 2020年8月14日 10:00
-
健一太田さん、こんにちは。フォーラムオペレーターのKumoです。
MSDNフォーラムにご投稿くださいましてありがとうございます。
ご質問いただいた件ですが、その後いかがでしょうか。
参考になった投稿には [回答としてマーク] をお願い致します。
設定いただくことで、
他のユーザーもお役に立つ回答を見つけやすくなります。
お手数ですが、ご協力の程どうかよろしくお願いいたします。MSDN/ TechNet Community Support Kumo ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~