هل يمكنني الحصول على صورة نقطية من نافذة تعسفية في عملية تطبيق أخرى؟

StackOverflow https://stackoverflow.com/questions/2835656

سؤال

أحاول أتمتة تطبيق WIN32 من طرف ثالث حيث أرغب في التقاط محتوى الرسومات لنافذة معينة على فترات زمنية محددة. أنا في المراحل المبكرة من هذا ، وأنا أحاول حاليًا استخدام Microsoft UI Automation API عبر C# للقيام بمعظم التفاعل بين تطبيق العميل والتطبيق الخارجي. يمكنني الآن الحصول على التطبيق الخارجي للقيام بما أريد أن أفعله ، لكنني الآن أريد التقاط الرسومات من نافذة محددة يبدو أنها تحكم من طرف ثالث. كيف يمكنني أن أفعل هذا؟ النافذة التي أريد التقاطها هي تلك التي تتميز بها المستطيل الأحمر في هذه الصورة:

I need what's in the red rectangle

لديّ تطبيق هذا النوع من الأعمال ، لكنه يعتمد على أن يكون واجهة المستخدم الخارجي في المقدمة ، وهذا غير مضمون بالنسبة لي ، لذلك أفضل العثور على شيء أكثر عمومية.

var p = Process.Start("c:\myapp.exe");
var mainForm = AutomationElement.FromHandle(p.MainWindowHandle);
// "workspace" below is the window whose content I want to capture.
var workspace = mainForm.FindFirst(TreeScope.Descendents,
                    new PropertyCondition(AutomationElement.ClassNameProperty, "AfxFrameOrView70u"));
var rect = (Rect) workspace.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
using (var bmp = new Bitmap((int)rect.Width, (int)rect.Height))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.CopyFromScreen((int)rect.Left, (int)rect.Top, 0, 0, new Size((int)rect.Width, (int)rect.Height));
        bmp.Save(@"c:\screenshot.png", ImageFormat.Png);
    }
}

يعمل ما سبق بشكل جيد بما فيه الكفاية عندما يكون التطبيق الآلي في الأعلى ، لكنه ينسخ الشاشة بشكل أعمى في المستطيل ، لذلك فإن الكود الخاص بي تحت رحمة كل ما يحدث على الجهاز وقد يغطي نافذة التطبيق.

لقد قرأت بعض الاقتراحات لإرسال WM_PRINT رسالة إلى النافذة. هذه جواب السؤال منذ بضعة أشهر ، بدا الأمر واعداً ، لكن عندما أستخدم هذا الرمز ، أحصل على مستطيل أبيض مع عدم وجود أي من محتويات التحكم الفعلية.

var prop = (int)workspace.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
var hwnd = new IntPtr(prop);
using ( var bmp2 = new Bitmap((int)rect.Width, (int)rect.Height))
{
    using (Graphics g = Graphics.FromImage(bmp2))
    {
        g.FillRectangle(SystemBrushes.Control, 0, 0, (int)rect.Width, (int)rect.Height);
        try
        {
            SendMessage(hwnd, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_OWNED));
        }
        finally
        {
            g.ReleaseHdc();
        }
        bmp2.Save(@"c:\screenshot.bmp");
    }
}

لذا ، أولاً ، هل من الممكن بالنسبة لي أن أحفظ صورة نقطية من محتويات النافذة بشكل موثوق؟ إذا كان الأمر كذلك ، فما هي أفضل طريقة ، وما هو الخطأ في WM_PRINT مع SendMessage محاولة؟

هل كانت مفيدة؟

المحلول

هذا التعديل من PrintWindow مثال API على موقع pinvoke.net يبدو أنه قام بالخدعة.

Bitmap bmp = new Bitmap((int)rect.Width, (int)rect.Height);
Graphics memoryGraphics = Graphics.FromImage(bmp);
IntPtr dc = memoryGraphics.GetHdc();
bool success = PrintWindow(hwnd, dc, 0);
memoryGraphics.ReleaseHdc(dc);
bmp.Save(@"c:\screenshot.bmp");

يعمل هذا إذا كان التطبيق مغطى بنافذة أخرى ، لكنه لا يعمل إذا تم تقليل التطبيق. أعتقد أنني أستطيع العيش مع ذلك.

نصائح أخرى

لا يوجد موثوق بها طريقة للحصول على صورة نقطية من تطبيق مختلف إذا لم يكن هذا التطبيق في المقدمة. ذلك لأن عناصر تحكم التطبيق لا تقدم حتى إذا لم يكن التطبيق مرئيًا ، ولا يتذكر Windows بالضرورة ما هي آخر محتويات التحكم المعروفة بعد أن تفقد هذا التحكم في أعلى الموضع في الترتيب z.

أفضل رهان لك هو نقل التطبيق المستهدف إلى مقدمة الترتيب z في الوقت الذي تحتاج فيه إلى أخذ لقطة الشاشة ، ثم استعادة الترتيب الأصلي بعد التقاط الصورة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top