2008-09-22 10 views
23

में उपयोग के लिए इस पॉइंटर को संग्रहीत करने के लिए सबसे अच्छी विधि WndProc में उपयोग के लिए this सूचक को संग्रहीत करने का सबसे अच्छा/सामान्य तरीका जानने में मुझे रूचि है। मैं कई दृष्टिकोणों के बारे में जानता हूं, लेकिन जैसा कि मैं समझता हूं कि इसमें अपनी खुद की कमी है। मेरे प्रश्न हैं:WndProc

क्या अलग अलग तरीकों से कोड के इस प्रकार का निर्माण करने में देखते हैं:

CWindow::WndProc(UINT msg, WPARAM wParam, LPARAM) 
{ 
    this->DoSomething(); 
} 

मैं,, Thunks के बारे में सोच सकते हैं HashMaps स्थानीय संग्रह और खिड़की उपयोगकर्ता डेटा struct थ्रेड।

इनमें से प्रत्येक दृष्टिकोण के पेशेवर/विपक्ष क्या हैं?

कोड उदाहरण और सिफारिशों के लिए दिए गए अंक।

यह पूरी तरह से जिज्ञासा के लिए है। मैं सिर्फ इतना है कि कैसे काम करता है सोच किया गया है MFC इस्तेमाल करने के बाद और फिर ATL के बारे में सोच करने के लिए मिला है आदि

संपादित करें: जल्द से जल्द जगह मैं वैध खिड़की proc में HWND उपयोग कर सकते हैं क्या है? इसे WM_NCCREATE के रूप में प्रलेखित किया गया है - लेकिन यदि आप वास्तव में प्रयोग करते हैं, तो विंडो पर भेजा जाने वाला पहला संदेश नहीं है।

संपादित करें: एटीएल इस सूचक को एक्सेस करने के लिए एक थंक का उपयोग करता है। एमएफसी HWND एस के हैशटेबल लुकअप का उपयोग करता है।

उत्तर

5

मैंने विंडो के साथ डेटा में पॉइंटर स्टोर करने के लिए सेटप्रॉप/गेटप्रॉप का उपयोग किया है। मुझे यकीन नहीं है कि यह आपके द्वारा उल्लिखित अन्य वस्तुओं तक कैसे ढेर करता है।

+0

मुझे लगता है कि Get/SetProp निश्चित रूप से इसे करने का एक साफ तरीका है। यह Get/SetWindowLong (जो ओ (1) है) की तुलना में धीमी गति से है, लेकिन जब तक खिड़की के लिए "कई" गुण सेट नहीं होते हैं, मुझे संदेह है कि यह एक बाधा होगी। –

7

आपको GetWindowLongPtr()/SetWindowLongPtr() (या बहिष्कृत GetWindowLong()/SetWindowLong()) का उपयोग करना चाहिए। वे तेज़ हैं और ठीक वही करते हैं जो आप करना चाहते हैं। SetWindowLongPtr() पर कॉल करने के लिए एकमात्र मुश्किल हिस्सा यह पता लगा रहा है कि जब आपको पहला विंडो संदेश भेजा जाता है, तो आपको यह करने की ज़रूरत है, जो WM_NCCREATE है।
नमूना कोड के लिए this article और अधिक गहन चर्चा के लिए देखें।

थ्रेड-लोकल स्टोरेज एक बुरा विचार है, क्योंकि आपके पास एक थ्रेड में कई खिड़कियां चल रही हैं।

एक हैश नक्शा भी काम करेगा, लेकिन प्रत्येक विंडो संदेश के लिए हैश फ़ंक्शन की गणना करेगा (और LOT) महंगा हो सकता है।

मुझे यकीन नहीं है कि आप का उपयोग करने का मतलब कैसे है; आप कैसे thunks के आसपास गुजर रहे हैं?

+2

जहां तक ​​thunks जाना है, आप प्रत्येक खिड़की के लिए एक अलग थंक हो सकता है (गतिशील उत्पन्न)। आप खिड़की की प्रक्रिया के रूप में इस थंक को पंजीकृत करेंगे, और इस थंक में ऑब्जेक्ट पॉइंटर पहले से ही होगा। मैं * विश्वास करता हूं * यह एटीएल/डब्ल्यूटीएल दृष्टिकोण है। –

+0

WM_NCCREATE जरूरी पहला संदेश नहीं भेजा गया है, लेकिन पहला जिसके लिए 'इस' जानकारी वाला CREATESTRUCT संदेश के साथ हो सकता है। –

10

अपने कन्स्ट्रक्टर में, कॉलविंडोएक्स को "इस" के साथ lpParam तर्क के रूप में कॉल करें।

SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) ((CREATESTRUCT*)lParam)->lpCreateParams); 
SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER); 

फिर, आप निम्न कर सकता है अपने विंडो प्रक्रिया के शीर्ष पर:

फिर, WM_NCCREATE पर, निम्नलिखित कोड फोन

MyWindowClass *wndptr = (MyWindowClass*) GetWindowLongPtr(hwnd, GWL_USERDATA); 

कौन आपको ऐसा करने की अनुमति देता है:

wndptr->DoSomething(); 

बेशक, आप उपरोक्त अपने कार्य की तरह कुछ कॉल करने के लिए उसी तकनीक का उपयोग कर सकते हैं:

wndptr->WndProc(msg, wparam, lparam); 

... जो इसके बाद "इस" पॉइंटर को अपेक्षित रूप से उपयोग कर सकता है।

+1

आपको 'SetWindowPos() 'की आवश्यकता क्यों है? – Timmmm

10

GWL_USERDATA तक पहुँचने के लिए एक अच्छा विचार की तरह ध्वनि हो सकता है SetWindowLongPtr और GetWindowLongPtr उपयोग करते समय, मैं दृढ़ता से इस दृष्टिकोण का उपयोग कर की सिफारिश करेंगे नहीं

यह वास्तव में Zeus संपादक द्वारा उपयोग किया गया है और हाल के वर्षों में इसने दर्द के अलावा कुछ भी नहीं किया है।

मुझे लगता है कि क्या होता है तीसरे पक्ष के विंडोज संदेश Zeus पर भेजे जाते हैं, जिनमें उनके GWL_USERDATA मान सेट भी होते हैं। विशेष रूप से एक एप्लीकेशन एक माइक्रोसॉफ्ट टूल था जिसने किसी भी विंडोज़ एप्लिकेशन में एशियाई पात्रों को दर्ज करने का एक वैकल्पिक तरीका साबित किया (यानी कुछ प्रकार की सॉफ्टवेयर कीबोर्ड उपयोगिता)।

समस्या Zeus हमेशा मान लिया गया GWL_USERDATA डेटा यह द्वारा स्थापित किया गया था और इस सूचक है, जो तब एक दुर्घटना में परिणाम एक के रूप में डेटा का उपयोग करने की कोशिश करता है।

अगर मैं इसे फिर से करना चाहता था, तो अब मुझे क्या पता है, मैं एक कैश हैश लुकअप दृष्टिकोण के लिए जाऊंगा जहां विंडो हैंडल कुंजी के रूप में उपयोग किया जाता है।

+2

यह WNDCLASSEX संरचना में अतिरिक्त विंडो डेटा सेट करके उपचार किया जा सकता है और GWL_USERDATA के बजाय Get/SetWindowLongPtr में उस नए ऑफसेट का उपयोग कर सकता है। –

+1

विंडो * संदेश * GWL_USERDATA नहीं है। * विंडोज * है। अन्य खिड़कियों के लिए संदेशों पर नज़र डालें, और अपनी खिड़की के लिए यह सूचक प्राप्त करने की उम्मीद है। और ध्यान दें कि आपको खिड़की और खिड़की * वर्ग * दोनों का मालिक होना चाहिए: http://blogs.msdn.com/oldnewthing/archive/2005/03/03/384285.aspx – MSalters

+0

समस्या यह है कि विंडोज़ आवेदन को एक विंडो के लिए एक हैंडल के साथ एक संदेश भेजता है जो एप्लिकेशन द्वारा नहीं बनाया गया था। लेकिन यह दृष्टिकोण मानता है कि एप्लिकेशन को भेजे गए प्रत्येक संदेश को एप्लिकेशन द्वारा बनाई गई विंडो के लिए होगा और यह मामला नहीं है। – jussij

2
SetWindowLong()/GetWindowLong() सुरक्षा के संबंध में

, माइक्रोसॉफ्ट के अनुसार:

SetWindowLong समारोह में विफल रहता है, तो खिड़की hWnd पैरामीटर द्वारा निर्दिष्ट के रूप में ही प्रक्रिया से संबंधित नहीं है कॉलिंग धागा

दुर्भाग्य से, 12 अक्टूबर, 2004, विंडोज would not enforce this rule पर एक Security Update की रिहाई, किसी अन्य अनुप्रयोग की GWL_USERDATA स्थापित करने के लिए एक आवेदन की अनुमति देता है जब तक। इसलिए, unpatched सिस्टम पर चल रहे अनुप्रयोग SetWindowLong() पर कॉल के माध्यम से हमला करने के लिए कमजोर हैं।

3

आप GetWindowLongPtr और SetWindowLongPtr का उपयोग कर सकते हैं; विंडो में सूचक संलग्न करने के लिए GWLP_USERDATA का उपयोग करें। हालांकि, अगर आप कस्टम नियंत्रण लिख रहे हैं तो मैं काम पूरा करने के लिए अतिरिक्त विंडो बाइट्स का उपयोग करने का सुझाव दूंगा। विंडो क्लास को पंजीकृत करते समय इस तरह के डेटा के आकार पर wc.cbWndExtra = sizeof(Ctrl*); सेट करें।

आप GetWindowLongPtr और SetWindowLongPtr का उपयोग करके nIndex पैरामीटर 0 पर सेट करके प्राप्त कर सकते हैं और सेट कर सकते हैं। यह विधि अन्य उद्देश्यों के लिए GWLP_USERDATA को सहेज सकती है।

GetProp और SetProp के साथ नुकसान, संपत्ति प्राप्त करने/सेट करने के लिए एक स्ट्रिंग तुलना होगी।

+0

यह 'GWL_USERDATA' के उपयोग से परहेज करके एड्रियन लोपेज़ द्वारा उल्लिखित मुद्दे का उपचार करता है। – legends2k

+1

विस्तार से व्याख्या नहीं करता है या इस समस्या को हल करने के लिए विंडो वर्ग का सही तरीके से उपयोग करने का तरीका दिखाता है। –

0

अतीत में मैं CreateWindowEx की lpParam पैरामीटर का उपयोग किया है:

lpParam [में वैकल्पिक,] टाइप करें: LPVOID

सूचक एक मूल्य CREATESTRUCT के माध्यम से खिड़की को पास करने की को संरचना (lpCreateParams सदस्य) WM_CREATE संदेश के lParam param द्वारा इंगित किया गया। इस संदेश को बनाए जाने से पहले इस फ़ंक्शन को द्वारा बनाई गई विंडो पर भेजा गया है। यदि कोई एमडीआई क्लाइंट विंडो बनाने के लिए CreateWindow पर कोई एप्लिकेशन कॉल करता है, तो lpParam को CLIENTCREATESTRUCT संरचना को इंगित करना चाहिए। यदि कोई एमडीआई क्लाइंट विंडो एमडीआई बाल विंडो बनाने के लिए CreateWindow को कॉल करती है, तो एलपी पैराम को MDICREATESTRUCT संरचना को इंगित करना चाहिए। यदि कोई अतिरिक्त डेटा आवश्यक है तो एलपी पैराम न्यूल हो सकता है।

चाल यहाँ वर्ग उदाहरण संकेत करने के लिए HWND के staticstd::map है। यह संभव है कि std::map::findSetWindowLongPtr विधि से अधिक प्रदर्शनशील हो सकता है। यद्यपि इस विधि का उपयोग कर टेस्ट कोड लिखना निश्चित रूप से आसान है।

बीटीडब्ल्यू यदि आप Win32 संवाद का उपयोग कर रहे हैं तो आपको DialogBoxParam फ़ंक्शन का उपयोग करने की आवश्यकता होगी।

0

मैं सिर्फ CreateWindow कॉल करने से पहले एक thread_local चर निर्धारित करने की अनुशंसा, और अपने WindowProc में इसे पढ़ने के this चर पता लगाने के लिए (मुझे लगता है आप WindowProc पर नियंत्रण है)।

इस तरह आपके पास विंडो पर भेजे गए पहले संदेश पर this/HWND एसोसिएशन होगा।

अन्य दृष्टिकोणों के साथ यहां सुझाव दिया गया है कि आप कुछ संदेशों पर याद करेंगे: WM_CREATE/WM_NCCREATE/WM_GETMINMAXINFO से पहले भेजे गए।

class Window 
{ 
    // ... 
    static thread_local Window* _windowBeingCreated; 
    static thread_local std::unordered_map<HWND, Window*> _hwndMap; 
    // ... 
    HWND _hwnd; 
    // ... 
    // all error checking omitted 
    // ... 
    void Create (HWND parentHWnd, UINT nID, HINSTANCE hinstance) 
    { 
     // ... 
     _windowBeingCreated = this; 
     ::CreateWindow (YourWndClassName, L"", WS_CHILD | WS_VISIBLE, x, y, w, h, parentHWnd, (HMENU) nID, hinstance, NULL); 
    } 

    static LRESULT CALLBACK Window::WindowProcStatic (HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 
    { 
     Window* _this; 
     if (_windowBeingCreated != nullptr) 
     { 
      _hwndMap[hwnd] = _windowBeingCreated; 
      _windowBeingCreated->_hwnd = hwnd; 
      _this = _windowBeingCreated; 
      windowBeingCreated = NULL; 
     } 
     else 
     { 
      auto existing = _hwndMap.find (hwnd); 
      _this = existing->second; 
     } 

     return _this->WindowProc (msg, wparam, lparam); 
    } 

    LRESULT Window::WindowProc (UINT msg, WPARAM wparam, LPARAM lparam) 
    { 
     switch (msg) 
     { 
      // .... 
संबंधित मुद्दे