لماذا تفشل رسالة TVM_GETITEM على وجهات نظر شجرة comctl32.ocx أو mscomctl.ocx؟

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

سؤال

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

std::string getTreeViewItemText( HWND treeView, HTREEITEM item )
{
    DWORD pid;
    ::GetWindowThreadProcessId( treeView, &pid );

    HANDLE proc = ::OpenProcess( PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, pid );
    if ( !proc )
        // handle error

    TVITEM tvi;
    ZeroMemory( &tvi, sizeof(tvi) );

    LPVOID tvi_ = ::VirtualAllocEx( proc, NULL, sizeof(tvi), MEM_COMMIT, PAGE_READWRITE);
    if ( !tvi_ )
        // handle error

    TCHAR buffer[100] = { 'X' };

    LPVOID txt_ = ::VirtualAllocEx( proc, NULL, sizeof(buffer), MEM_COMMIT, PAGE_READWRITE );
    if ( !txt_ )
        // handle error

    tvi.mask = TVIF_TEXT | TVIF_HANDLE;
    tvi.pszText =  (LPTSTR)txt_;
    tvi.cchTextMax = sizeof(buffer) / sizeof(buffer[0] );
    tvi.hItem = item;

    if ( !::WriteProcessMemory( proc, tvi_, &tvi, sizeof(tvi), NULL ) )
        // handle error

    if ( !::SendMessage( treeView, TVM_GETITEM, 0, (LPARAM)tvi_ ) )
        // handle error

    if ( !::ReadProcessMemory( proc, (LPCVOID)txt_, buffer, sizeof( buffer ), NULL ) )
        // handle error

    ::VirtualFreeEx( proc, tvi_, 0, MEM_RELEASE );

    ::VirtualFreeEx( proc, txt_, 0, MEM_RELEASE );

    ::CloseHandle( proc );

    return buffer;
}

يعمل هذا الرمز بشكل جيد للغاية مع طرق عرض الأشجار العادية التي تحصل عليها عند تمرير WC_TREEVIEW اسم الفصل إلى CreateWindow. وبعد ومع ذلك، لاحظت أنه لا يعمل مع الأشجار الأحدث كما توفره MS Common Controls V5 (COMCTL32.OCX) أو MS Common Controls v6 (mscomctl.ocx). في تلك الحالات، يكون النص المرتجع فارغا دائما (المخزن المؤقت هو كل الأصفار). لقد لاحظت أيضا أن دعوة SendMessage تعود صفر (وبالتالي التعامل مع الأخطاء الذي يشير إليه // handle error تعليقات أعلاه الركلات في). من غير الواضح بالنسبة لي ما إذا كان هذا يشير حقا إلى خطأ، في أي حال، يتم ملء المخزن المؤقت بجميع الأصفار.

يبدو أن جميع رسائل عرض الأشجار الأخرى (مثل TVM_GetitemRect) تعمل بشكل جيد تماما.

هل يعرف أي شخص لماذا هذا؟ حاولت اللعب مع علم يونيكود (لاحظت ذلك TVM_GETITEM هو إما محددة ل TVM_GETITEMA أو TVM_GETITEMW) ولكن هذا لا يبدو للمساعدة.

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

المحلول

حسنا، دعونا نقدمها لقطة أخرى.

Newer TreeViews نتوقع TVITEMEX بدلاً من TVITEM, ، وبما أن لا يوجد معتاد cbSize الحقل، عنصر التحكم غير قادر على معرفة الإصدار الذي يتلقاه ويفترض TVITEMEX. وبعد ربما هناك مشكلة مع TreeView غير قادر على الوصول إلى آخر أعضاء TVITEMEX لأنه لا يتم تخصيص الذاكرة. جرب استخدام TVITEMEX أو تخصيص ذاكرة أكثر قليلا ل TVITEM من المطلوبة فعلا.

أيضا النظر في ذلك

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

لذلك قد تحتاج إلى القراءة من قطعة واحدة مختلفة من ذاكرة العملية.

وعلى المخزن المؤقت صفرية لأن VirtualalloCex يعيد تعيين الذاكرة.

وكمنتج آخر وربما عديمة الفائدة، حاول استخدام MEM_RESERVE|MEM_COMMIT بدلا من مجرد MEM_COMMIT.

نصائح أخرى

لا يعمل الكود كما هو متوقع إذا تم تجميعه مع Unicode المحدد، لكن العملية البعيدة ليست (أو الطريقة الأخرى الأخرى). يجب عليك الاتصال iswindowunicode. أولا في treeView التعامل مع التحقق مما إذا كان الجانب البعيد يتوقع رسائل يونيكود.

هناك حاجة إلى ذلك منذ التنظيم القياسي في اتجاهين أي sendmessage لا يكفي في هذه الحالة: عليك أن ترسل رسالتين مختلفتين تماما اعتمادا على ما إذا كان الجانب البعيد هو نافذة Unicode أم لا. إذا كان Unicode، استخدم SendMessageW مع TVM_GETITEMW. إذا كان ذلك ansi، فاستخدم sendmessagea مع tvm_getitema.

ينطبق هذا على جميع عناصر التحكم الشائعة، ولكن ليس إلى المجموعة الأساسية من عناصر التحكم (التي تستخدم رسائل النافذة <1024).

أعتقد أيضا أن الكود سوف ينكسر إذا تم تجميعه في ثنائي 64 بت، ولكن العملية البعيدة هي 32 بت (أو الطريقة الأخرى الأخرى). وذلك لأن التعليمات البرمجية هي محلية (قل: 64bit) TVITEM في العملية البعيدة، ثم تتوقع العملية عن بعد TOREAD كما هو متوقع أثناء التعامل مع رسالة TVM_GETITEM (A | W). ومع ذلك، قد يكون حجم الهيكل مختلفا (بسبب أحجام المؤشر المختلفة).

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

ينطبق هذا بنفس القدر على الإصدارات السابقة من Windows BTW.

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