none
Vista UltimeteをBasci videoモードでした状態で、透明モードを使用するアプリケーションウィンドウをキャプチャする方法 RRS feed

  • 質問

  • アプリケーションのクライアント領域の背景をTransparent Colorに設定して、ウィンドウの下が透けるアプリケーションを作成しています。

    このアプリケーションで、アプリケーション自身を含めた領域をキャプチャしたいと考えています。

     

    .NetFrameworkには、 g.CopyFromScreen(rc.X, rc.Y, 0, 0,rc.Size, CopyPixelOperation.SourceCopy)

    を使うと、簡単に指定領域をキャプチャできました。

     

    これは、WindowsXPでも、Wnidows VistaのAeroモードでも問題ありませんでした。

     

    しかし、Wnidows Vista UltimateのVideoモードをBASICに変更して、実行すると、

    アプリケーション自身が完全に透明になってしまい、ウィンドウの下のイメージだけがキャプチャされます。

     

    このウィンドウ自身も含めてXPやVista Aeroのように、Vista Basicでキャプチャしたいのですが、良い方法はないでしょうか?

     

    ちなみに、DirectXを使ったキャプチャ方式でも、やはり透明色の指定を行ったアプリケーションはキャプチャできないようです。

    私が、DirectXを使ったキャプチャのコードを持っているわけでありませんが、その方式を使っているという、ソフトでも同様にキャプチャできませんでした。

    2008年10月16日 8:51

回答

  •  Reiji さんからの引用
    ご助言ありがとうございます。

    試してみましたが、キャプチャデータは真っ黒となりました。


    おかしいなーと思って自分でも試してみて原因が分かりました.結論から言えば CopyPixelOperation.CaptureBlt の使い方が間違っているのですが,正しく使おうとすると .NET Framework の CopyFromScreen メソッドのバグに遭遇することもわかりました.


    元々 Win32 GDI Function の BitBlt では,CAPTUREBLT はビットフラグとして使うことが想定されています.つまり,


    Code Snippet

    BitBlt( ......, SRCCOPY | CAPTUREBLT);


    のように使います.なので,CopyFromScreen も以下のように使えるものと思っていたのですが,実際試してみると例外が発生します.


    Code Snippet

    g.CopyFromScreen(..., CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);


    おかしいと思って CopyFromScreen のソースコードを確認してみましたが,例外の原因は CopyFromScreen 実装者の理解不足から引数チェックを正しく実装できていないためでした.

    CopyFromScreen の実装者は,CopyPixelOperation をそれぞれ単独で使うと思い込んでいて,上記のようにビットフラグとして組み合わせた場合にわざわざ例外を発生させています.


    さらに調べてみると,本家の MSDN Forums で同じ問題が指摘されていることに気づきました.

    copyFromScreen - no alpha window captured


    上記スレッドで書かれているように,Win32 GDI の BitBlt を P/Invoke し,CopyPixelOperation を組み合わせることで,ご希望の動作が実現できることを確認しました.

    2008年10月21日 1:01

すべての返信

  • Windows Vista Basicでは、最初はキャプチャできるようですが、一度明示的にBasic videoモードに強制的に変更すると、キャプチャできなくなります。

     

    Windows Vista BasicのVideoモードはUltimetのBasicモードとは違うように思えてきました。

    Microsoftのサイトを調べてみると、今回の現象の条件が特定できました。

     

    条件:

       Desktop Composition が無効になると、透明な背景を持つApplicationを

           g.CopyFromScreen(rc.X, rc.Y, 0, 0,rc.Size, CopyPixelOperation.SourceCopy)

       でキャプチャできなくなります。

     

     

    上記の関数は、たぶんDirectXベースのキャプチャだと思うのですが、このDirectXベースで、Windows VistaのDesktop Compositionが無効な場合にでも、キャプチャする方法はないものでしょうか?

     

    あるいは、背景を透過色指定しているAP側を改造する方法でもかまいません。

     

    ないにかよい手立てはないでしょうか?

    2008年10月17日 5:49
  • キャプチャできない透過のサンプルコードです。

     

    C#のWindowFormのプロジェクトで生成されるフォームにボタンを1つ配置します。

    そのボタンを押すとキャプチャデータがファイルに生成され、Viewerで表示するようにしています。

     

    現象を確認するには、FormのTransparencyKeyに色を指定すると、キャプチャできなくなります。

    TransparenceyKeyの色とBackColorの色を同じにすると、背景が透明になるウィンドウとなります。

     

    TransparencyKeyが指定される場合、Vistaでは、Desktop Compositionが無効な場合なぜかキャプチャ

    できないという結論です。キャプチャの方法に問題があるのでしょうか?

     

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Drawing.Imaging;
    using System.Drawing.Printing;
    using System.Drawing.Drawing2D;
    using System.Runtime.InteropServices;
    using System.Diagnostics;

     


    namespace SampleAP
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private void button1_Click(object sender, EventArgs e)
            {
                                // スクリーン・キャプチャする範囲を決定
                    Rectangle rc,rcClient;
                    rc = this.Bounds;
                    rcClient = this.ClientRectangle;

                    // Bitmapオブジェクトにスクリーン・キャプチャ
                    Bitmap bmp = new Bitmap(rc.Width, rc.Height,
                        PixelFormat.Format32bppArgb);
                    using (Graphics g = Graphics.FromImage(bmp))
                    {
                        g.CopyFromScreen(rc.X, rc.Y, 0, 0,
                          rc.Size, CopyPixelOperation.SourceCopy);

                        // ビット・ブロック転送方式の切り替え例:
                        //g.FillRectangle(Brushes.LightPink,
                        //  0, 0, rc.Width, rc.Height);
                        //g.CopyFromScreen(rc.X, rc.Y, 0, 0,
                        //  rc.Size, CopyPixelOperation.SourceAnd);

                    }

                    // MyPictureフォルダの取得
                    string myPicture = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);

                    // ファイル名の生成
                    string myFileName = DateTime.Now.ToString("yyyyMMdd-HHmm")
                        + ".bmp";

                    // 保存ファイルの確定
                    string filePath = myPicture + "\\" + myFileName;
                    if (System.IO.File.Exists(filePath))
                    {
                        // 既にファイルが存在する
                        filePath = myPicture + "\\" + DateTime.Now.ToString("yyyyMMdd-HHmm") + "-2.bmp";
                    }

     

                    // ビットマップ画像として保存して表示
                    bmp.Save(filePath, ImageFormat.Bmp);
                    Process.Start(filePath);


            }
        }
    }

     

    2008年10月17日 6:17
  • 以前から,レイヤードウィンドウを含めて確実にキャプチャするには CAPTUREBLT を使用する必要があったかと思います.

     

    CopyPixelOperation 列挙体 の CopyPixelOperation.CaptureBlt はいかがでしょうか?

    2008年10月17日 8:45
  • ご助言ありがとうございます。

    試してみましたが、キャプチャデータは真っ黒となりました。

     

    関連して、CopyPixelOperation列挙体のどの方法でも期待したようにはキャプチャできませんでした。

    ちなみに、そのアプリケーションをALT+PrintScreenでキャプチャすると、背景が不透明でキャプチャされます。

    また、PrintScreenでデスクトップ全体をキャプチャすると、アプリケーションを含めて期待したようにキャプチャできます。

     

    デスクトップ全体をPrintScreenキャプチャできるということは、何か方法があるように思うのですが、良い方法が思い当たりません。

    2008年10月20日 4:29
  •  Reiji さんからの引用
    ご助言ありがとうございます。

    試してみましたが、キャプチャデータは真っ黒となりました。


    おかしいなーと思って自分でも試してみて原因が分かりました.結論から言えば CopyPixelOperation.CaptureBlt の使い方が間違っているのですが,正しく使おうとすると .NET Framework の CopyFromScreen メソッドのバグに遭遇することもわかりました.


    元々 Win32 GDI Function の BitBlt では,CAPTUREBLT はビットフラグとして使うことが想定されています.つまり,


    Code Snippet

    BitBlt( ......, SRCCOPY | CAPTUREBLT);


    のように使います.なので,CopyFromScreen も以下のように使えるものと思っていたのですが,実際試してみると例外が発生します.


    Code Snippet

    g.CopyFromScreen(..., CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);


    おかしいと思って CopyFromScreen のソースコードを確認してみましたが,例外の原因は CopyFromScreen 実装者の理解不足から引数チェックを正しく実装できていないためでした.

    CopyFromScreen の実装者は,CopyPixelOperation をそれぞれ単独で使うと思い込んでいて,上記のようにビットフラグとして組み合わせた場合にわざわざ例外を発生させています.


    さらに調べてみると,本家の MSDN Forums で同じ問題が指摘されていることに気づきました.

    copyFromScreen - no alpha window captured


    上記スレッドで書かれているように,Win32 GDI の BitBlt を P/Invoke し,CopyPixelOperation を組み合わせることで,ご希望の動作が実現できることを確認しました.

    2008年10月21日 1:01
  • NyaRuRu さん

     

    うまくキャプチャできることをこちらでも確認しました。

    Win32 GDIを直接操作するところまで思いが至りませんでした。

     

    本当に助かりました。ありがとうございます。

    2008年10月21日 5:07