سؤال

هل يُسمح لي باستخدام DC خارج دورة الطلاء؟ هل مضمون النافذة العاصمة ليكون صالحًا إلى الأبد؟

أحاول معرفة المدة التي يتمتع بها سياق جهاز التحكم الخاص بي (DC).

أعلم أنه يمكنني الاتصال:

GetDC(hWnd);

للحصول على سياق الجهاز من نافذة التحكم الخاصة بي ، ولكن هل هذا مسموح به؟

عندما يرسل لي Windows رسالة WM_Paint ، من المفترض أن أتصل ابدأ/بطانة للاعتراف بشكل صحيح أنني قمت برسمها ، ولإزالة المنطقة غير الصالحة داخليًا:

BeginPaint(hWnd, {out}paintStruct);
try
   //Do my painting
finally
   EndPaint(hWnd, paintStruct);
end;

لكن استدعاء BeginPaint يعيدني أيضًا إلى DC داخل بنية PaintStruct. هذا هو العاصمة التي أنا ينبغي أن ترسم على.

لا يمكنني العثور على أي شيء في الوثائق التي تقول أن العاصمة التي تم إرجاعها بواسطة BeginPaint () هي نفس العاصمة التي سأحصل عليها من GETDC ().

الآن ، خاصة ، في أيام تكوين سطح المكتب ، هل من صالح الطلاء على العاصمة التي أحصل عليها خارج BeginPaint؟

يبدو أن هناك طريقتين يمكنني الحصول على العاصمة للطلاء أثناء دورة الطلاء:

  1. DC = getDC(hwnd) ؛

  2. BeginPaint (& paintstruct) ؛

هناك طريقة ثالثة ، ولكن يبدو أنها خطأ مع بورلاند دلفي الذي أتطور معه.

أثناء wm_paint المعالجة ، تعتقد Delphi أن WPARAM هي العاصمة ، وتستمر في الطلاء عليها. في حين يقول MSDN أن WPARAM لرسالة WM_PAINT غير مستخدمة.

لماذا

هدفي الحقيقي هو محاولة الحفاظ على كائن رسومات GDI+ مستمر مقابل HDC ، حتى أتمكن من استخدام بعض الميزات الأفضل أداءً لـ GDI+ والتي تعتمد على وجود DC مستمر.

أثناء معالجة الرسائل WM_Paint ، أريد رسم صورة GDI+ إلى اللوحة. إصدار Nieve التالي بطيء جدًا:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);
   Graphics g = new Graphics(ps.hdc);
   g.DrawImage(m_someBitmap, 0, 0);
   g.Destroy();
   EndPaint(h_hwnd, ps);
}

يحتوي GDI على صورة نقطية أداء أسرع ، و cachedbitmap. لكن استخدامه دون تفكير لا يعطي أي فائدة أداء:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);

   Graphics g = new Graphics(ps.hdc);
   CachedBitmap bm = new CachedBitmap(m_someBitmap, g);
   g.DrawCachedBitmap(m_bm, 0, 0);
   bm.Destroy();
   g.Destroy();
   EndPaint(h_hwnd, ps);
}

يأتي كسب الأداء من إنشاء CacheDbitMap مرة واحدة ، لذلك على تهيئة البرنامج:

m_graphics = new Graphics(GetDC(m_hwnd));
m_cachedBitmap = new CachedBitmap(b_someBitmap, m_graphcis);

والآن على دورة الطلاء:

WM_PAINT:
{
   PAINTSTRUCT ps;
   BeginPaint(m_hwnd, ps);
   m_graphics.DrawCachedBitmap(m_cachedBitmap, 0, 0);
   EndPaint(h_hwnd, ps);
}        

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

  • مفاتيح المستخدم السريع
  • تمكين/تعطيل التكوين
  • تبديل الموضوع
  • موضوع تعطيل

لا أجد شيئًا في MSDN يضمن استخدام DC نفسه في نافذة معينة طالما وجود النافذة.

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

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

المحلول

هناك استثناءات ، ولكن بشكل عام ، قد تحصل على DC مختلف في كل مرة تتصل بها GetDC أو BeginPaint. وبالتالي ، يجب ألا تحاول حفظ الحالة في العاصمة. (إذا كان يجب عليك القيام بذلك من أجل الأداء ، فهناك DCs خاصة يمكنك إنشاؤها لفئة من النوافذ أو مثيل نافذة معين ، لكن هذا لا يبدو أن هذا ما تحتاجه حقًا أو تريده.)

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

هناك رسائل Windows تخبرك عندما يتغير وضع الرسومات ، مثل WM_DISPLAYCHANGE و WM_PALETTECHANGED. يمكنك الاستماع إليها ، وإعادة إنشاء صورة نقطية محزنة مؤقتًا. نظرًا لأن هذه أحداث نادرة ، فلن تضطر إلى القلق بشأن تأثير أداء إعادة إنشاء صورة نقطية مؤقتة في تلك المرحلة.

يمكنك أيضًا الحصول على إشعارات لأشياء مثل تغييرات الموضوع. هؤلاء لا يغيرون وضع الرسومات-فهي مفهوم مستوى أعلى-لذلك يجب أن تظل نقطات النقط مؤقتًا متوافقًا مع أي DC تحصل عليها. ولكن إذا كنت ترغب في تغيير صورة نقطية عندما يتغير السمة ، فيمكنك الاستماع إليه WM_THEMECHANGED أيضًا.

نصائح أخرى

الطريقة الوحيدة التي أعرفها بذلك (أو لا) تفعل ما تبحث عنه هي إنشاء النافذة مع CS_OWNDC نمط الفصل.

ما يفعله هو تخصيص سياق جهاز فريد لكل نافذة في الفصل.

تعديل

من مقالة MSDN المرتبطة:

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

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

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

Windows 95/98/ME: على الرغم من أن نمط CS_OWNDC مناسب ، استخدمه بعناية ، لأن كل سياق جهاز يستخدم جزءًا كبيرًا من كومة GDI 64K.

ربما يوضح هذا المثال استخدام CS_OWNDC بشكل أفضل:

#include <windows.h>

static TCHAR ClassName[] = TEXT("BitmapWindow");
static TCHAR WindowTitle[] = TEXT("Bitmap Window");

HDC m_hDC;
HWND m_hWnd;

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static PAINTSTRUCT ps;

    switch (msg)
    {
    case WM_PAINT:
        {
            BeginPaint(hWnd, &ps);

            if (ps.hdc == m_hDC)
                MessageBox(NULL, L"ps.hdc == m_hDC", WindowTitle, MB_OK);
            else
                MessageBox(NULL, L"ps.hdc != m_hDC", WindowTitle, MB_OK);

            if (ps.hdc == GetDC(hWnd))
                MessageBox(NULL, L"ps.hdc == GetDC(hWnd)", WindowTitle, MB_OK);
            else
                MessageBox(NULL, L"ps.hdc != GetDC(hWnd)", WindowTitle, MB_OK);

            RECT r;
            SetRect(&r, 10, 10, 50, 50);
            FillRect(m_hDC, &r, (HBRUSH) GetStockObject( BLACK_BRUSH ));

            EndPaint(hWnd, &ps);
            return 0;
        }
    case WM_DESTROY:
        {
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{   
    WNDCLASSEX wcex;

    wcex.cbClsExtra = 0;
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.cbWndExtra = 0;
    wcex.hbrBackground = (HBRUSH) GetStockObject( WHITE_BRUSH );
    wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
    wcex.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wcex.hIconSm = NULL;
    wcex.hInstance = hInstance;
    wcex.lpfnWndProc = WndProc;
    wcex.lpszClassName = ClassName;
    wcex.lpszMenuName = NULL;
    wcex.style = CS_OWNDC;

    if (!RegisterClassEx(&wcex))
        return 0;

    DWORD dwExStyle = 0;
    DWORD dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE;

    m_hWnd = CreateWindowEx(dwExStyle, ClassName, WindowTitle, dwStyle, 0, 0, 300, 300, NULL, NULL, hInstance, NULL);

    if (!m_hWnd)
        return 0;

    m_hDC = GetDC(m_hWnd);

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

علامة CS_OWNDC ليس أن يتم الخلط بينه وبين علم CS_CLASSDC الذي:

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

إذا فشل كل شيء آخر فقط إعادة البناء cachedbitmap.

عندما تقوم ببناء كائن CacheDbitMap ، يجب عليك تمرير عنوان كائن الرسومات إلى المُنشئ. إذا كانت الشاشة المرتبطة بكائن الرسومات قد تم تغيير عمقها بت بعد إنشاء صورة نقطية مخزنة مؤقتًا ، فسوف تفشل طريقة DrawCacheDbitMap ، ويجب عليك إعادة بناء صورة نقطية مخزنة مؤقتًا. بدلاً من ذلك ، يمكنك ربط رسالة تغيير الإخطار وإعادة بناء صورة نقطية مخبأة في ذلك الوقت.

أنا لا أقول أن cs_owndc هو الحل الأمثل ، لكنه هو خطوة واحدة نحو حل أفضل.

تعديل

يبدو أن برنامج العينة يحتفظ بنفس التيار المستمر أثناء دقة الشاشة / تغيير عمق البت مع علامة CS_OWNDC ، ومع ذلك ، عندما تمت إزالة هذا العلامة ، كانت العاصمة مختلفة (النافذة 7 64 بت Ultimate) (ينبغي اعمل بنفس الشيء على إصدارات Overn OS ... على الرغم من أنه لن يضر لاختبار).

EDIT2

هذا المثال لا يستدعي getUpdaterect للتحقق مما إذا كان هناك حاجة إلى رسم النافذة خلال WM_Paint. هذا خطأ.

يمكنك الرسم على أي نافذة العاصمة ترضيك. كلاهما صالح. لا تحتوي النافذة على DC واحدة فقط يمكن أن تمثلها في وقت واحد. لذلك في كل مرة تقوم فيها بالاتصال بـ GetDC - وتبدأ في داخليًا ، ستحصل على DC فريدة من نوعها ، ومع ذلك يمثل منطقة العرض نفسها. تم إصدارها للتو (أو بطيئة) عندما تنتهي من ذلك. في أيام Windows 3.1 ، كانت سياقات الأجهزة محدودة ، أو مورد نظام مكلف للغاية ، لذلك تم تشجيع التطبيقات على عدم التمسك بها أبدًا ، ولكن لاستردادها من ذاكرة التخزين المؤقت GETDC. في الوقت الحاضر ، من المقبول تمامًا إنشاء DC عند إنشاء النافذة ، وتخزينه في حياة النافذة.

"المشكلة" الوحيدة هي ، عند التعامل WM_PAINT, ، سيتم قص العاصمة التي تم إرجاعها بواسطة BeginPaint إلى المستقيم غير الصالح ، ولن يتم حفظها.


ومع ذلك ، لا أفهم ما تحاول تحقيقه مع Gdiplus. عادة ، إذا تم تحديد كائن ... في العاصمة لفترة طويلة من الزمن ، فإن العاصمة هي ذاكرة العاصمة ، وليس نافذة العاصمة.


في كل مرة يتم استدعاء GETDC ، ستحصل على HDC جديد يمثل سياق جهاز متميز مع حالته الخاصة. لذلك ، لن تؤثر الكائنات وألوان الخلفية وأوضاع النص وما إلى ذلك. لن يؤثر ذلك على حالة DC أخرى تم استردادها بواسطة مكالمة مختلفة إلى GetDC أو BeginPaint.

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

ومع ذلك ، من الحكمة أن نراقب على الأقل من أجل WM_DisplayChange ، وإطلاق أي DCs مخزنة مؤقتًا ونقطات نقطية للأجهزة ، وإعادة إنشائها.

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