1

オープンソース ソフトウェアを何年も楽しく使ってきた私は、恩返しをする時が来たと考えました。ドキュメントは通常、多くのプロジェクトの弱点であり、加えて私の C# スキルは、FLOSS の土地の片隅ではあまり求められていないため、チュートリアルなどから始めようと考えています.

書き込みツールの 2 番目のチュートリアルの後、私はすでに次のルーチンに悩まされています。

  1. スクリーンショットを撮ります
  2. 重要な部分を強調表示する
  3. 注釈を付ける
  4. ウェブサイトに追加
  5. 繰り返す

そして、それを自動化できると考えました。

私が探しているのは、現在開いているウィンドウのスクリーンショットを撮り、たとえば、フォーカスされたコントロール (おそらくボタン) の周りに黄色のバーをペイントし、小さなテキスト ボックスをポップアップして、その説明を入力するプログラムだと思います。画像を作成し、最終的にすべてをウェブサイト/データベース/リスト/などに追加します。

さて、私の実際の質問です。すでにそれを行うツールを誰も知らない限り、「外部」ウィンドウのコントロールのサイズと位置にアクセスする方法についていくつかのスターターが必要です。これにより、重要なコントロールの周りにこれらの強調表示バーをどこにペイントするかを計算できます。で保護されたテキストボックスの内容を明らかにできるWindows用のパスワードマスク解除ツールを覚えていますが******、それに関する開いている例は見つかりません.. WinAPIのもの、WindowFromPoint + GetDlgItemなど。Linuxの方が簡単かどうかはわかりませんが、どちらでも問題ありません。プログラミング言語の好みもありません。

4

1 に答える 1

2

私の知る限り、.NET には他のアプリケーションのウィンドウにアクセスするための API がないため、実行するには P/Invoke が必要です。

おそらく、GetForegroundWindowを使用して現在のウィンドウを取得することから始めることができます (スクリーンショットを撮るためにウィンドウを切り替えると、GetForegroundWindow から独自のウィンドウが返されるため、グローバル ホットキーまたはタイマーを使用してそのコードを起動する必要があります)。

編集

あなたの質問に触発されて、日曜日の午後にコーディングを少し行いました。GetForegroundWindow はフォアグラウンド ウィンドウを取得しますが、コントロール レベルは取得しないことがわかりました。しかし、別の便利な関数GetGUIThreadInfoがあり、現在フォーカスされているコントロールとその他の情報を取得できます。GetWindowInfoを使用して、ウィンドウ (トップレベル ウィンドウに含まれるコントロールである可能性があります) に関する情報を取得できます。

これらをまとめると、厄介な P/Invoke 呼び出しをすべて抽象化する Window クラスを作成できます。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;

namespace dr.Stackoverflow.ScreenshotTest
{
    public class Window
    {
        private WINDOWINFO info;
        private readonly IntPtr handle;

        internal Window(IntPtr handle)
        {
            this.handle = handle;
        }

        public int Handle
        {
            get { return handle.ToInt32(); }
        }

        // Note - will not work on controls in other processes.
        public string Text
        {
            get
            {
                int length = GetWindowTextLength(handle);
                if ( length > 0 )
                {
                    StringBuilder buffer = new StringBuilder(length);
                    if (0 < GetWindowText(handle, buffer, length))
                    {
                        return buffer.ToString();
                    }
                }
                return "<unknown>";
            }
        }

        public Rectangle WindowArea
        {
            get
            {
                EnsureWindowInfo();
                return info.rcWindow;
            }
        }

        public override string ToString()
        {
            return String.Format("{0} 0x{1}", Text, handle.ToString("x8"));
        }

        private unsafe void EnsureWindowInfo()
        {
            if (info.cbSize == 0)
            {
                info.cbSize = sizeof (WINDOWINFO);
                if ( !GetWindowInfo(handle, out info) ) 
                    throw new ApplicationException("Unable to get Window Info");

            }
        }

        public static Window GetForeground()
        {
            IntPtr handle = GetForegroundWindow();
            if (handle == IntPtr.Zero)
                return null;

            return new Window(handle);
        }

        public unsafe static Window GetFocus()
        {
            IntPtr foreground = GetForegroundWindow();
            int procId;
            int tId = GetWindowThreadProcessId(foreground, out procId);
            if (0 != tId)
            {
                GUITHREADINFO threadInfo = new GUITHREADINFO() {cbSize = sizeof (GUITHREADINFO)};
                if ( GetGUIThreadInfo(tId, out threadInfo) )
                {
                    return new Window(threadInfo.hwndFocus);
                }
            }
            return null;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct WINDOWINFO
        {
            public int cbSize;
            public RECT rcWindow;
            public RECT rcClient;
            public int dwStyle;
            public int dwExStyle;
            public int dwWindowStatus;
            public uint cxWindowBorders;
            public uint cyWindowBorders;
            public int atomWindowType;
            public int wCreatorVersion;
        } 

        [StructLayout(LayoutKind.Sequential)]
        private struct GUITHREADINFO
        {

            public int cbSize;
            public int flags;
            public IntPtr hwndActive;
            public IntPtr hwndFocus;
            public IntPtr hwndCapture;
            public IntPtr hwndMenuOwner;
            public IntPtr hwndMoveSize;
            public IntPtr hwndCaret;
            public RECT rcCaret;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;

            public static implicit operator Rectangle(RECT rhs)
            {
                return new Rectangle(rhs.left, rhs.top, rhs.right - rhs.left, rhs.bottom - rhs.top);
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetWindowInfo(IntPtr hwnd, out WINDOWINFO pwi);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern bool GetGUIThreadInfo(int threadId, out GUITHREADINFO threadInfo);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);
    }
}

次に、それを使用してサンプル プログラムを作成します。

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;

namespace dr.Stackoverflow.ScreenshotTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Sleeping for 3 seconds (switch to a window of interest)");
            Thread.Sleep(3000);

            Window currentWindow = Window.GetForeground();
            Window focusedWindow = Window.GetFocus();       
            if ( currentWindow != null )
            {
                Console.WriteLine("Foreground window");
                Console.WriteLine(currentWindow.Text);
                Console.WriteLine(currentWindow.WindowArea);
            }
            if (focusedWindow != null)
            {
                Console.WriteLine("\tFocused window");
                Console.WriteLine("\t{0}", focusedWindow.WindowArea);
            }

            if (focusedWindow !=null && currentWindow != null && focusedWindow.Handle != currentWindow.Handle)
            {
                Console.WriteLine("\nTaking a screenshot");
                Rectangle screenshotArea = currentWindow.WindowArea;
                Bitmap bm = new Bitmap(currentWindow.WindowArea.Width,currentWindow.WindowArea.Height);
                using(Graphics g = Graphics.FromImage(bm))
                {
                    g.CopyFromScreen(screenshotArea.Left,screenshotArea.Top, 0,0, new Size(screenshotArea.Width,screenshotArea.Height));
                    Rectangle focusBox = focusedWindow.WindowArea;
                    focusBox.Offset(screenshotArea.Left * -1, screenshotArea.Top * -1);
                    focusBox.Inflate(5,5);
                    g.DrawRectangle(Pens.Red,focusBox);
                }
                bm.Save("D:\\screenshot.png", ImageFormat.Png);
            }
        }
    }
}

これにより、現在のフォアグラウンド ウィンドウのスクリーンショットが作成され、現在フォーカスされているコントロールが強調表示された赤いボックスが表示されます。これはサンプル コードであり、最小限のエラー チェックを備えていることに注意してください :-) 実行するときは、Alt-Tab キーを押して目的のウィンドウに移動し、プログラムが終了するまでそこにとどまります。

ただし、いくつかの制限があります。私が発見した最も重要なことは、このアプローチは WPF アプリケーションでは機能しないということです。これは、個々のコントロールが他の Windows プログラムのように Windows ではないためです。

于 2010-05-23T14:40:58.400 に答える