2012-03-11 179 views
5

我有一個功能:在另一個線程創建窗口(不是主線程)

HWND createMainWindow(P2p_Socket_Machine * toSend){ 

    HWND hMainWnd = CreateWindow( 
     L"Class",/*(LPCWSTR) nameOfConference.c_str()*/L"Chat", WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, 
    CW_USEDEFAULT, 0, 600,400, 
    (HWND)NULL, (HMENU)NULL, 
    /*(HINSTANCE)hlnstance*/NULL, NULL 
    ); 

    if (!hMainWnd) { 
     MessageBox(NULL, L"Cannot create main window", L"Error", MB_OK); 
     return 0; 
    } 

    CreateWindowA("LISTBOX",NULL, WS_CHILD|WS_VISIBLE|WS_BORDER|WS_VSCROLL|LBS_NOTIFY|LBS_MULTIPLESEL,310,30,255,275,hMainWnd,(HMENU)List_Box,NULL,NULL); 

    CreateWindowExA(NULL,"BUTTON", "Refresh", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,310,100,24,hMainWnd,(HMENU)Button_Refresh, NULL ,NULL); 

    CreateWindowExA(NULL,"BUTTON", "Send", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,334,100,24,hMainWnd,(HMENU)Button_Send, NULL ,NULL); 

    CreateWindowExA(NULL,"BUTTON", "New", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,385,354,100,24,hMainWnd,(HMENU)Button_New, NULL ,NULL); 

    CreateWindowA("EDIT",0,WS_BORDER|WS_VISIBLE|WS_CHILD|ES_LEFT|ES_MULTILINE|WS_VSCROLL|WS_DISABLED, 
    10,30,265,275,hMainWnd,(HMENU)Text_Box_Get,NULL,NULL); 

    CreateWindowA("EDIT",0,WS_BORDER|WS_VISIBLE|WS_CHILD|ES_LEFT|ES_MULTILINE|WS_VSCROLL, 
    10,320,265,45,hMainWnd,(HMENU)Text_Box_Send,NULL,NULL); 

    SetWindowLongPtr(hMainWnd,GWLP_USERDATA,(LONG_PTR)toSend); 

    ShowWindow(hMainWnd, SW_SHOW); 
    //UpdateWindow(hMainWnd); 

    return hMainWnd; 

} 

這是我計劃的主要部分:)

int WINAPI WinMain(HINSTANCE hlnstance, HINSTANCE hPrevInstance, LPSTR IpCmdLine, int 
nCmdShow) 
{ 
WNDCLASSEX wc; 
    wc.cbSize = sizeof(wc); 
    wc.style = CS_HREDRAW | CS_VREDRAW; 
    wc.lpfnWndProc = MyFunc; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 
    wc.hInstance = hlnstance; 
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 
    wc.lpszMenuName = NULL; 
    wc.lpszClassName = L"Class"; 
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 
HWND toSend = createMainWindow(P2pSocket); 

//some code 

hThread = CreateThread(NULL, 0, ClientThread, 
      Message2, 0, &dwThreadId); 

     if (hThread == NULL) 
     { 
      cout<<"Create thread filed"; 
      exit(10); 
     } 


    while (GetMessage(&msg, NULL, 0, 0)) { 

     TranslateMessage(&msg); 
     DispatchMessage(&msg); 

    } 

    return msg.wParam; 

當我打電話功能則CreateMainWindow(在我的程序的主要部分 它的作品,因爲它應該,但是當我在我的 線程中運行它(ClientThread)這是行不通的。我讀過我應該只在主線程中創建窗口。這是真的嗎?如果這是真的,從另一個主線程調用此函數最簡單的方法是什麼?


謝謝大家。現在我知道這個問題,但我堅持解決方案。 我的客戶端線程的代碼是:直到接收消息

while(1){ 

    vector<HWND> AllHandlers; 

    pair<string,string> Answer = Pointer->receiveMsgByUdp(); 

    if(!Pointer->isMyLocalAddress(Answer.first)){ 

     int type = messageUdpContentType(Answer.second); 

     switch(type){ 

     case 0 : 

      Pointer->sendMsgToIpUdp(Answer.first,"<?xml version='1.0'?><accepted/>"); 
      AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box); 
      for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++) 
       if(SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str())==LB_ERR) 
        SendMessageA(*j, LB_ADDSTRING, 0, (LPARAM)Answer.first.c_str()); 

      break; 

     case 1 : 
      AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box); 
      for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++) 
       if(SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str())==LB_ERR) 
        SendMessageA(*j, LB_ADDSTRING, 0, (LPARAM)Answer.first.c_str()); 

      break; 

     case 2 : 
      AllHandlers = getAllHandlersOfElementsOnWindowsByIdentityCode(Pointer->getAllHandlers(),List_Box); 
      for(vector<HWND>::iterator j = AllHandlers.begin();j!=AllHandlers.end();j++) 
       if((i = SendMessageA(*j, LB_FINDSTRINGEXACT, 0, (LPARAM)Answer.first.c_str()))!=LB_ERR) 
        SendMessageA(*j,LB_DELETESTRING, 0, (LPARAM)Answer.first.c_str()); 

      break; 

     case 3 : 

      userReply = MessageBoxW(NULL, L"Принять приглашение на конференцию?", 
        L"", MB_YESNO | MB_ICONQUESTION); 
      if (userReply==IDYES){ 

       //todo: Проверка на создание встречи, в которой уже состоишь 
       string nameOfConf = fetchNameOfInviteConf(Answer.second); 
       Pointer->createConference(nameOfConf); 
       HWND toSendTo = createMainWindow(Pointer); 
       Pointer->setHandlerInfo(nameOfConf,toSendTo);    
       Pointer->addNewMemberToConference_ServerType(nameOfConf,Answer.first); 
       string toSend = string("<?xml version='1.0'?><inviteAccepted>") + nameOfConf + string("</inviteAccepted>"); 
       Pointer->sendMsgToIpUdp(Answer.first,toSend); 

      } 
      break; 

     case 4 : 

      string nameOfConf = fetchNameOfInviteAcceptConf(Answer.second); 

      toSend.clear(); 
      Participants.clear(); 
      Participants = Pointer->getCurrentParticipants(nameOfConf); 
      toSend+="<?xml version='1.0'?>"; 
      toSend+="<conference>"; 
      toSend+="<nameOfConference>"; 
      toSend+=nameOfConf; 
      toSend+="</nameOfConference>"; 
      for(vector<string>::iterator i = Participants.begin();i!=Participants.end();i++){ 
       toSend+="<participant>" + *i + "</participant>"; 
      } 
      toSend+="</conference>"; 



      Pointer->addNewMemberToConference_ClientType(nameOfConf,Answer.first); 

      Pointer->sendToIpTcp(Answer.first,toSend); 

      break; 

    } 

功能receiveMsgByUdp()停止該線程。我對缺乏知識表示歉意,但我可以使用什麼功能或其他的東西來解決這個問題。我應該重寫我的方法receiveMsgByUdp()是異步的,或者如何調用函數createMainWindow()在主線程上運行?關於最後一個變體:我怎樣才能在純winapi中做到這一點,我找不到任何簡單的例子。有人可以提供代碼片段。再次感謝)

+0

感謝每一位。我設法讓這個程序起作用。我使用了David Heffernan的方法。而這個環節上的信息http://stackoverflow.com/questions/7060686/how-do-i-sendmessage-to-a-window-created-on-another-thread – knightOfSpring 2012-03-11 15:53:36

回答

6

您確實可以創造比主UI線程其他線程的窗口。但是,這些窗口將與創建它們的線程相關聯,並且您將需要在創建窗口的每個線程中運行消息泵。

因此,雖然你可以做你問什麼,Win32的是真正設計的具有親和力的同一線程進程中的所有窗口的工作。創建多個UI線程確實沒有什麼可以獲得的。你所做的所有事情都會讓你的生活變得異常複雜。

+0

是的,這很複雜。但在我的情況下,我找不到另一種解決方案。在非主線程套接字接收消息中,當我收到特殊消息時,我需要創建窗口。我不知道如何在主線程中創建它。我是winapi的新手,所以也許你會告訴我如何設法做這種伎倆? – knightOfSpring 2012-03-11 12:12:35

+0

這是一種常見的情況。你所做的是發送或發佈消息到主線程並獲得主線程來創建窗口。所有的GUI框架都提供了支持這種情況的機制。 – 2012-03-11 12:15:41

+0

但是,如果我需要在純winapi中做到這一點?它會非常複雜嗎? – knightOfSpring 2012-03-11 12:24:48

3

您可以在「非主要」線程上創建窗口,但請注意這些窗口已附加到創建線程,並且您需要確保在那裏實現一個消息循環並保持發送消息發佈在隊列中。如果你不這樣做,你的窗戶將凍結。

參見:

系統不會自動創建用於每個 螺紋的消息隊列。相反,系統僅爲執行需要消息隊列的操作的線程 創建消息隊列。 如果線程 創建一個或多個窗口,則必須提供消息循環;這個 消息循環從線程的消息隊列中檢索消息並且將它們分派到適當的窗口過程。

+0

因此,所有我需要做的就是添加一個代碼到我的非主線程來發送消息。但是有一個問題:在這個線程中,我使用套接字,我接收消息,並且一些函數阻止我的循環,直到套接字接收到消息。我認爲解決這個問題的唯一方法是在我的非主線程中創建另一個線程來分散窗口中的消息。你怎麼看? – knightOfSpring 2012-03-11 12:03:44

+0

@Roman在我看來,建議多個UI線程在這裏是最好的解決方案將是不好的建議。你怎麼看?你是否同意我的觀點? – 2012-03-11 12:16:27

+0

您可以選擇使用API​​來處理套接字,所有您需要的都是避免阻止執行在API中花費很長時間的調用。 – 2012-03-11 12:18:20

3

如果你在另一個線程創建一個窗口,你也將需要實現該線程的消息循環,因爲排隊的消息發佈到線程的消息隊列該公司擁有的窗口。

相關問題