2010-09-03 4 views
5

के अंदर एक विंडो बनाना 2 अलग-अलग परियोजनाओं के बीच एक संदेश भेजने की कोशिश कर रहा है, लेकिन मेरी समस्या यह है कि मैं रिसीवर को टीटीएचड ऑब्जेक्ट के अंदर चलाने की कोशिश कर रहा हूं, लेकिन WNDProc किसी ऑब्जेक्ट के अंदर से काम नहीं करेगा, एक फ़ंक्शन होना चाहिए, क्या टीटीएचड के अंदर एक विंडो बनाने के लिए वैसे भी है जो धागे के अंदर संदेशों को संसाधित कर सकता है?टीटीएचड

यहाँ मैं क्या मतलब

function TDataThread.WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; 
begin 
Result := 0; 
case uMsg of 
    WM_DATA_AVA: MessageBox(0, 'Data Avaibale', 'Test', 0); 
    else Result := DefWindowProc(hwnd, uMsg, wParam, lParam); 
end; 
end; 

Procedure TDataThread.Create(const Title:String); 
begin 
HAppInstance := HInstance; 
with WndClass do 
begin 
    Style := 0; 
    lpfnWndProc := @WindowProc;   //The Error Lies here (Variable Required) 
    cbClsExtra := 0; 
    cbWndExtra := 0; 
    hInstance := HAppInstance; 
    hIcon := 0; 
    hCursor := LoadCursor(0, IDC_ARROW); 
    hbrBackground := COLOR_WINDOW; 
    lpszMenuName := nil; 
    lpszClassName := 'TDataForm'; 
end; 
Windows.RegisterClass(WndClass); 
MainForm := CreateWindow('TDataForm', PAnsiChar(Title), WS_DLGFRAME , XPos, YPos, 698, 517, 0, 0, hInstance, nil); 
end; 

मैं तो मैं FindWindow और FindWindowEx का उपयोग करता है, तो

उत्तर

9

की जरूरत के लिए एक पृष्ठभूमि सूत्र में एक wndproc चल रहा है हो सकता है किसी अन्य अनुप्रयोग से इसके हैंडल प्राप्त कर सकते हैं एक विधि अवश्य होनी चाहिए है Win32 में किया गया, लेकिन इसे व्यापक रूप से एक बुरा विचार माना जाता है।

ऐसा करने के लिए, आपको यह सुनिश्चित करना होगा कि आपके पृष्ठभूमि धागे में एक संदेश प्रेषण लूप है: GetMessage/TranslationMessage/DispatchMessage। आपको यह सुनिश्चित करना होगा कि पृष्ठभूमि थ्रेड में संदेशों को संसाधित करने के लिए आप जिस विंडो हैंडल को पृष्ठभूमि थ्रेड पर बनाते हैं (CreateWindow को पृष्ठभूमि थ्रेड के संदर्भ में कहा जाता है) और इसके सभी बच्चे विंडो भी। और आपको यह सुनिश्चित करना होगा कि आपका बैकग्राउंड थ्रेड इसके संदेश लूप को अक्सर जो कुछ भी कर रहा है उसके अतिरिक्त कॉल करता है (जो कि पृष्ठभूमि थ्रेड का उपयोग करने के उद्देश्य को हरा देता है!)

यदि आपके पृष्ठभूमि धागे में संदेश लूप नहीं है, तो पृष्ठभूमि थ्रेड पर बनाए गए विंडो हैंडल को कभी भी कोई संदेश नहीं मिलेगा, इसलिए कुछ भी नहीं होगा।

अब, आपको ऐसा क्यों नहीं करना चाहिए: विंडोज संदेश संचालित हैं, जिसका अर्थ है कि वे स्वाभाविक रूप से एक सहकारी मल्टीटास्कड प्रेषण प्रणाली हैं। प्रत्येक जीयूआई विंडोज ऐप को कुछ भी करने के लिए मुख्य धागे में एक संदेश लूप होना पड़ता है। वह संदेश लूप मुख्य थ्रेड पर लगभग सभी खिड़कियों का समर्थन करेगा। निष्पादन को अवरुद्ध करने के लिए एक उचित ढंग से कार्यान्वित यूआई मुख्य धागे में कुछ भी नहीं करेगा, इसलिए संदेश लूप हमेशा तैयार और उत्तरदायी होगा।

तो यदि मुख्य धागे पर मौजूदा संदेश लूप अवरुद्ध या ठंडे बिना आपकी सभी विंडो मैसेजिंग आवश्यकताओं को संभालेगा, तो आप पृष्ठभूमि थ्रेड में दूसरा संदेश लूप चलाने की कोशिश करके अपने जीवन को और अधिक जटिल क्यों बनाना चाहते हैं? पृष्ठभूमि धागे का उपयोग करने के लिए कोई फायदा नहीं है।

+1

सुझाव के रूप में, मुख्य थ्रेड संदेश मिलता है और अपने काम का संकेत थ्रेड जब प्रक्रिया के लिए नया डेटा उपलब्ध है। – jachguate

+0

विंडोज़ में सभी थ्रेड बराबर हैं, उनके बारे में "मुख्य" या "पृष्ठभूमि" कुछ भी नहीं है। वे इस बात में भिन्न होते हैं कि उनके पास संदेश लूप है या नहीं, और एक ऐसा है जो पहले प्रक्रिया में बनाया गया था, लेकिन यह अंतर के रूप में इसके बारे में है। COM के साथ इंटरैक्शन के लिए एक थ्रेड की आवश्यकता हो सकती है जिसमें एक संदेश लूप हो, थ्रेड में विंडो के साथ काम करने के लिए उसे एक संदेश लूप होना आवश्यक हो।एक धागा के साथ संवाद करने के लिए एक संदेश लूप भी एक अच्छा तरीका है। वीसीएल के अलावा इसके लिए एक खराब मैच होने के अलावा प्रक्रिया में एकाधिक संदेश लूप के साथ कुछ भी गलत नहीं है। – mghie

+0

तो क्या कोई तरीका नहीं है कि मैं अपने धागे को अन्य प्रक्रियाओं के साथ संवाद कर सकूं? प्रत्येक प्रक्रिया को अपने धागे का जवाब वापस भेजना होगा ताकि यह दिखाया जा सके कि यह डेटा के लिए फ़ाइल को मैप करने के लिए तैयार है! – killercode

4

आपको संदेश प्राप्त करने के लिए विंडो की आवश्यकता नहीं है, निम्न का प्रयास करें।

// Force Message Queue Creation 
    PeekMessage(Msg, 0, WM_USER, WM_USER, PM_NOREMOVE); 

तो सेटअप एक संदेश लूप/पम्प, उदाहरण:

// Run until terminated 
    while not Terminated do 
    begin 

    if GetMessage(@Msg, 0, 0, 0) then 
    begin 
     case Msg.message of 
     WM_DATA_AV: MessageBox(0, 'Data Avaibale', 'Test', 0); 
     else begin 
     TranslateMessage(@Msg); 
     DispatchMessage(@Msg); 
     end; 
    end; 
    end; 
+0

हाँ, लेकिन मैं संदेश भेजने के लिए इस धागे के हैंडल को कैसे जानूंगा? प्रेषक एक अन्य प्रक्रिया से है – killercode

+0

पोस्ट थ्रेड मैसेज (http://msdn.microsoft.com/en-us/library/ms644946(VS.85).aspx) का उपयोग करें, यह विंडो हैंडल के बजाय थ्रेडआईड लेता है। – Remko

+1

लेकिन फिर आपको प्राप्त करने वाले एप की समस्या है जो प्राप्त करने वाले थ्रेड की आईडी का पता लगाने की आवश्यकता है। खिड़की का उपयोग करना उस खोज को आसान बनाता है। –

7

एक खिड़की बनाना धागा में (एक बार) एक संदेश कतार, उदाहरण के निर्माण के लिए मजबूर करने के PeekMessage को एक फोन करना टीटीएचड के अंदर ठीक काम करता है, बशर्ते टीटीएचड एक संदेश लूप लागू करता है, और CreateWindow() को उसी थ्रेड संदर्भ के अंदर संदेश लूप के रूप में बुलाया जाता है। दूसरे शब्दों में, आप CreateWindow() TThread के निष्पादित() विधि के अंदर से, जैसे कॉल करना होगा नहीं इसके निर्माता अंदर से:

type 
    TDataThread = class(TThread) 
    private 
    FTitle: String; 
    FWnd: HWND; 
    FWndClass: WNDCLASS; 
    FRegistered: boolean; 
    class function WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; static; 
    protected 
    procedure Execute; override; 
    procedure DoTerminate; override; 
    public 
    constructor Create(const Title:String); reintroduce; 
    end; 

constructor TDataThread.Create(const Title: String); 
begin 
    inherited Create(False); 
    FTitle := Title; 
    with FWndClass do 
    begin 
    Style := 0; 
    lpfnWndProc := @WindowProc; 
    cbClsExtra := 0; 
    cbWndExtra := 0; 
    hInstance := HInstance; 
    hIcon := 0; 
    hCursor := LoadCursor(0, IDC_ARROW); 
    hbrBackground := COLOR_WINDOW; 
    lpszMenuName := nil; 
    lpszClassName := 'TDataForm'; 
    end; 
end; 

procedure TDataThread.Execute; 
var 
    Msg: TMsg; 
begin 
    FRegistered := Windows.RegisterClass(FWndClass) <> 0; 
    if not FRegistered then Exit; 
    FWnd := CreateWindow(FWndClass.lpszClassName, PChar(FTitle), WS_DLGFRAME, XPos, YPos, 698, 517, 0, 0, HInstance, nil); 
    if FWnd = 0 then Exit; 
    while GetMessage(Msg, FWnd, 0, 0) > 0 do 
    begin 
    TranslateMessage(msg); 
    DispatchMessage(msg) 
    end; 
end; 

procedure TDataThread.DoTerminate; 
begin 
    if FWnd <> 0 then DestroyWindow(FWnd); 
    if FRegistered then Windows.UnregisterClass(FWndClass.lpszClassName, HInstance); 
    inherited; 
end; 

function TDataThread.WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall; 
begin 
    Result := 0; 
    case uMsg of 
    WM_DATA_AVA: 
     MessageBox(0, 'Data Available', 'Test', 0); 
    else 
    Result := DefWindowProc(hwnd, uMsg, wParam, lParam); 
    end; 
end; 
+0

+1, यह dthorpe के उत्तर से महत्वपूर्ण तकनीकी जानकारी है, जो चेतावनी में थोड़ा दफनाया गया था। सदस्य 'FWndClass' रखने की कोई आवश्यकता नहीं है, हालांकि, सब कुछ' निष्पादन() 'में डालें,' DoTerminate() 'से छुटकारा पाएं, और चीजें स्पष्ट होंगी। यदि दोनों वर्ग का नाम और विंडो शीर्षक कन्स्ट्रक्टर के पैरामीटर थे तो यह एक अच्छा सहायक बेस क्लास के लिए तैयार होगा। – mghie

+0

मैं DoTerminate() का उपयोग करना पसंद करता हूं क्योंकि यह थ्रेड को स्वयं के बाद सफाई करने की इजाजत देता है चाहे निष्पादित (निष्कासित) स्पष्ट रूप से बाहर निकलता है या बिना किसी अपवाद के कारण होता है।कोशिश करें/पूरे निष्पादन() कोड को छोड़कर मेरे लिए थोड़ा बदसूरत है। –

+0

कृत्रिम रूप से विभिन्न तरीकों से डेटा संरचना का सेटअप, उपयोग और विनाश करना बहुत खराब है। उदाहरण के लिए आपका कोड खुशी से 'UnregisterClass()' को कॉल करेगा, भले ही 'RegisterClass() 'विफल हो गया हो। – mghie

0
TTestLoopThread = class(TThread) 
     private 
     FWinHandle: HWND; 
     procedure DeallocateHWnd(Wnd: HWND); 
     protected 
     procedure Execute; override; 
     procedure WndProc(var msg: TMessage); 
     public 
     constructor Create; 
     destructor Destroy; override; 
     end; 

    implementation 

    var 
     WM_SHUTDOWN_THREADS: Cardinal; 

    procedure TForm1.FormCreate(Sender: TObject); 
    begin 
     WM_SHUTDOWN_THREADS := RegisterWindowMessage('TVS_Threads'); 
    end; 

    procedure TForm1.Button1Click(Sender: TObject); 
    begin 
     TTestLoopThread.Create; 
    end; 

    procedure TForm1.Button2Click(Sender: TObject); 
    begin 
     SendMessage(wnd_broadcast, WM_SHUTDOWN_THREADS, 0, 0); 
    end; 

    { TTestLoopThread } 

    constructor TTestLoopThread.Create; 
    begin 
     inherited Create(False); 
    end; 

    destructor TTestLoopThread.Destroy; 
    begin 
     inherited; 
    end; 

    procedure TTestLoopThread.DeallocateHWnd(Wnd: HWND); 
    var 
     Instance: Pointer; 
    begin 
     Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC)); 
     if Instance <> @DefWindowProc then 
     // make sure we restore the old, original windows procedure before leaving 
     SetWindowLong(Wnd, GWL_WNDPROC, Longint(@DefWindowProc)); 
     FreeObjectInstance(Instance); 
     DestroyWindow(Wnd); 
    end; 

    procedure TTestLoopThread.Execute; 
    var 
     Msg: TMsg; 
    begin 
     FreeOnTerminate := True; 
     FWinHandle := AllocateHWND(WndProc); //Inside Thread 
     try 
     while GetMessage(Msg, 0, 0, 0) do 
     begin 
     TranslateMessage(Msg); 
     DispatchMessage(Msg); 
     end; 
     finally 
     DeallocateHWND(FWinHandle); 
     end; 
    end; 

    procedure TTestLoopThread.WndProc(var msg: TMessage); 
    begin 
     if Msg.Msg = WM_SHUTDOWN_THREADS then 
     begin 
     Form1.Memo1.Lines.Add('Thread ' + IntToStr(ThreadID) + ' shutting down.'); 
     PostMessage(FWinHandle, WM_QUIT, 0, 0); 
     end 
     else 
     Msg.Result := DefWindowProc(FWinHandle, Msg.Msg, Msg.wParam, Msg.lParam); 
    end; 
+0

'AlocateHWND()', 'DeallocateHWND()', 'MakeObjectInstance() ',' FreeObjectInstance() '- ये फ़ंक्शन थ्रेड-सुरक्षित नहीं हैं, क्योंकि वे वैश्विक संसाधनों का उपयोग करते हैं जो थ्रेड में समवर्ती पहुंच से सुरक्षित नहीं हैं। मुख्य धागा इन कार्यों का काफी व्यापक उपयोग करता है, इसलिए असुरक्षित कार्यकर्ता थ्रेड जो उनका उपयोग भी करते हैं, वास्तव में उन्हें गड़बड़ कर सकते हैं। ऐसा कहा जा रहा है कि थर्ड-पार्टी कस्टम कार्यान्वयन थ्रेड-सुरक्षित हैं। अन्यथा, उनका बिल्कुल उपयोग न करें, और केवल Win32 API फ़ंक्शन कॉल का उपयोग करें ('CreateWindow()', 'SetWindowLong() ') जो वर्कर थ्रेड में ठीक काम करता है। –