/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS user32.dll * FILE: win32ss/user/user32/windows/message.c * PURPOSE: Messages * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) * UPDATE HISTORY: * 06-06-2001 CSH Created */ #include #include WINE_DEFAULT_DEBUG_CHANNEL(user32); #ifdef __i386__ /* For bad applications which provide bad (non stdcall) WndProc */ extern LRESULT __cdecl CALL_EXTERN_WNDPROC( WNDPROC WndProc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); #else # define CALL_EXTERN_WNDPROC(proc, h, m, w, l) proc(h, m, w, l) #endif /* From wine: */ /* flag for messages that contain pointers */ /* 32 messages per entry, messages 0..31 map to bits 0..31 */ #define SET(msg) (1 << ((msg) & 31)) static const unsigned int message_pointer_flags[] = { /* 0x00 - 0x1f */ SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) | SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE), /* 0x20 - 0x3f */ SET(WM_GETMINMAXINFO) | SET(WM_DRAWITEM) | SET(WM_MEASUREITEM) | SET(WM_DELETEITEM) | SET(WM_COMPAREITEM), /* 0x40 - 0x5f */ SET(WM_WINDOWPOSCHANGING) | SET(WM_WINDOWPOSCHANGED) | SET(WM_COPYDATA) | SET(WM_COPYGLOBALDATA) | SET(WM_HELP), /* 0x60 - 0x7f */ SET(WM_STYLECHANGING) | SET(WM_STYLECHANGED), /* 0x80 - 0x9f */ SET(WM_NCCREATE) | SET(WM_NCCALCSIZE) | SET(WM_GETDLGCODE), /* 0xa0 - 0xbf */ SET(EM_GETSEL) | SET(EM_GETRECT) | SET(EM_SETRECT) | SET(EM_SETRECTNP), /* 0xc0 - 0xdf */ SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETTABSTOPS), /* 0xe0 - 0xff */ SET(SBM_GETRANGE) | SET(SBM_SETSCROLLINFO) | SET(SBM_GETSCROLLINFO) | SET(SBM_GETSCROLLBARINFO), /* 0x100 - 0x11f */ 0, /* 0x120 - 0x13f */ 0, /* 0x140 - 0x15f */ SET(CB_GETEDITSEL) | SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) | SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) | SET(CB_GETDROPPEDCONTROLRECT) | SET(CB_FINDSTRINGEXACT), /* 0x160 - 0x17f */ 0, /* 0x180 - 0x19f */ SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_SELECTSTRING) | SET(LB_DIR) | SET(LB_FINDSTRING) | SET(LB_GETSELITEMS) | SET(LB_SETTABSTOPS) | SET(LB_ADDFILE) | SET(LB_GETITEMRECT), /* 0x1a0 - 0x1bf */ SET(LB_FINDSTRINGEXACT), /* 0x1c0 - 0x1df */ 0, /* 0x1e0 - 0x1ff */ 0, /* 0x200 - 0x21f */ SET(WM_NEXTMENU) | SET(WM_SIZING) | SET(WM_MOVING) | SET(WM_DEVICECHANGE), /* 0x220 - 0x23f */ SET(WM_MDICREATE) | SET(WM_MDIGETACTIVE) | SET(WM_DROPOBJECT) | SET(WM_QUERYDROPOBJECT) | SET(WM_DRAGLOOP) | SET(WM_DRAGSELECT) | SET(WM_DRAGMOVE), /* 0x240 - 0x25f */ 0, /* 0x260 - 0x27f */ 0, /* 0x280 - 0x29f */ 0, /* 0x2a0 - 0x2bf */ 0, /* 0x2c0 - 0x2df */ 0, /* 0x2e0 - 0x2ff */ 0, /* 0x300 - 0x31f */ SET(WM_ASKCBFORMATNAME) }; /* check whether a given message type includes pointers */ static inline int is_pointer_message( UINT message ) { if (message >= 8*sizeof(message_pointer_flags)) return FALSE; return (message_pointer_flags[message / 32] & SET(message)) != 0; } #undef SET /* check whether a combobox expects strings or ids in CB_ADDSTRING/CB_INSERTSTRING */ static BOOL FASTCALL combobox_has_strings( HWND hwnd ) { DWORD style = GetWindowLongA( hwnd, GWL_STYLE ); return (!(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) || (style & CBS_HASSTRINGS)); } /* check whether a listbox expects strings or ids in LB_ADDSTRING/LB_INSERTSTRING */ static BOOL FASTCALL listbox_has_strings( HWND hwnd ) { DWORD style = GetWindowLongA( hwnd, GWL_STYLE ); return (!(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) || (style & LBS_HASSTRINGS)); } /* DDE message exchange * * - Session initialization * Client sends a WM_DDE_INITIATE message, usually a broadcast message. lParam of * this message contains a pair of global atoms, the Application and Topic atoms. * The client must destroy the atoms. * Server window proc handles the WM_DDE_INITIATE message and if the Application * and Topic atoms are recognized sends a WM_DDE_ACK message to the client. lParam * of the reply message contains another pair of global atoms (Application and * Topic again), which must be destroyed by the server. * * - Execute * Client posts a WM_DDE_EXECUTE message to the server window. lParam of that message * is a global memory handle containing the string to execute. After the command has * been executed the server posts a WM_DDE_ACK message to the client, which contains * a packed lParam which in turn contains that global memory handle. The client takes * ownership of both the packed lParam (meaning it needs to call FreeDDElParam() on * it and the global memory handle. * This might work nice and easy in Win3.1, but things are more complicated for NT. * Global memory handles in NT are not really global, they're still local to the * process. So, what happens under the hood is that PostMessage must handle the * WM_DDE_EXECUTE message specially. It will obtain the contents of the global memory * area, repack that into a new structure together with the original memory handle * and pass that off to the win32k. Win32k will marshall that data over to the target * (server) process where it will be unpacked and stored in a newly allocated global * memory area. The handle of that area will then be sent to the window proc, after * storing it together with the "original" (client) handle in a table. * The server will eventually post the WM_DDE_ACK response, containing the global * memory handle it received. PostMessage must then lookup that memory handle (only * valid in the server process) and replace it with the corresponding client memory * handle. To avoid memory leaks, the server-side global memory block must be freed. * Also, the WM_DDE_ACK lParam (a PackDDElParam() result) is unpacked and the * individual components are handed to win32k.sys to post to the client side. Since * the server side app hands over ownership of the packed lParam when it calls * PostMessage(), the packed lParam needs to be freed on the server side too. * When the WM_DDE_ACK message (containing the client-side global memory handle) * arrives at the client side a new lParam is PackDDElParam()'ed and this is handed * to the client side window proc which is expected to free/reuse it. */ /* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle * to the memory handle, we keep track (in the server side) of all pairs of handle * used (the client passes its value and the content of the memory handle), and * the server stored both values (the client, and the local one, created after the * content). When a ACK message is generated, the list of pair is searched for a * matching pair, so that the client memory handle can be returned. */ typedef struct tagDDEPAIR { HGLOBAL ClientMem; HGLOBAL ServerMem; } DDEPAIR, *PDDEPAIR; static PDDEPAIR DdePairs = NULL; static unsigned DdeNumAlloc = 0; static unsigned DdeNumUsed = 0; static CRITICAL_SECTION DdeCrst; BOOL FASTCALL DdeAddPair(HGLOBAL ClientMem, HGLOBAL ServerMem) { unsigned i; EnterCriticalSection(&DdeCrst); /* now remember the pair of hMem on both sides */ if (DdeNumUsed == DdeNumAlloc) { #define GROWBY 4 PDDEPAIR New; if (NULL != DdePairs) { New = HeapReAlloc(GetProcessHeap(), 0, DdePairs, (DdeNumAlloc + GROWBY) * sizeof(DDEPAIR)); } else { New = HeapAlloc(GetProcessHeap(), 0, (DdeNumAlloc + GROWBY) * sizeof(DDEPAIR)); } if (NULL == New) { LeaveCriticalSection(&DdeCrst); return FALSE; } DdePairs = New; /* zero out newly allocated part */ memset(&DdePairs[DdeNumAlloc], 0, GROWBY * sizeof(DDEPAIR)); DdeNumAlloc += GROWBY; #undef GROWBY } for (i = 0; i < DdeNumAlloc; i++) { if (NULL == DdePairs[i].ServerMem) { DdePairs[i].ClientMem = ClientMem; DdePairs[i].ServerMem = ServerMem; DdeNumUsed++; break; } } LeaveCriticalSection(&DdeCrst); return TRUE; } HGLOBAL FASTCALL DdeGetPair(HGLOBAL ServerMem) { unsigned i; HGLOBAL Ret = NULL; EnterCriticalSection(&DdeCrst); for (i = 0; i < DdeNumAlloc; i++) { if (DdePairs[i].ServerMem == ServerMem) { /* free this pair */ DdePairs[i].ServerMem = 0; DdeNumUsed--; Ret = DdePairs[i].ClientMem; break; } } LeaveCriticalSection(&DdeCrst); return Ret; } DWORD FASTCALL get_input_codepage( void ) { DWORD cp; int ret; HKL hkl = GetKeyboardLayout( 0 ); ret = GetLocaleInfoW( LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, (WCHAR *)&cp, sizeof(cp) / sizeof(WCHAR) ); if (!ret) cp = CP_ACP; return cp; } static WPARAM FASTCALL map_wparam_char_WtoA( WPARAM wParam, DWORD len ) { WCHAR wch = wParam; BYTE ch[2]; DWORD cp = get_input_codepage(); len = WideCharToMultiByte( cp, 0, &wch, 1, (LPSTR)ch, len, NULL, NULL ); if (len == 2) return MAKEWPARAM( (ch[0] << 8) | ch[1], HIWORD(wParam) ); else return MAKEWPARAM( ch[0], HIWORD(wParam) ); } /*********************************************************************** * map_wparam_AtoW * * Convert the wparam of an ASCII message to Unicode. */ static WPARAM FASTCALL map_wparam_AtoW( UINT message, WPARAM wparam ) { char ch[2]; WCHAR wch[2]; wch[0] = wch[1] = 0; switch(message) { case WM_CHAR: /* WM_CHAR is magic: a DBCS char can be sent/posted as two consecutive WM_CHAR * messages, in which case the first char is stored, and the conversion * to Unicode only takes place once the second char is sent/posted. */ #if 0 if (mapping != WMCHAR_MAP_NOMAPPING) // NlsMbCodePageTag { PCLIENTINFO pci = GetWin32ClientInfo(); struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data; BYTE low = LOBYTE(wparam); if (HIBYTE(wparam)) { ch[0] = low; ch[1] = HIBYTE(wparam); RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 ); TRACE( "map %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping ); if (data) data->lead_byte[mapping] = 0; } else if (data && data->lead_byte[mapping]) { ch[0] = data->lead_byte[mapping]; ch[1] = low; RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 ); TRACE( "map stored %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping ); data->lead_byte[mapping] = 0; } else if (!IsDBCSLeadByte( low )) { ch[0] = low; RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 1 ); TRACE( "map %02x -> %04x\n", (BYTE)ch[0], wch[0] ); if (data) data->lead_byte[mapping] = 0; } else /* store it and wait for trail byte */ { if (!data) { if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ))) return FALSE; get_user_thread_info()->wmchar_data = data; } TRACE( "storing lead byte %02x mapping %u\n", low, mapping ); data->lead_byte[mapping] = low; return FALSE; } wparam = MAKEWPARAM(wch[0], wch[1]); break; } #endif /* else fall through */ case WM_CHARTOITEM: case EM_SETPASSWORDCHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: ch[0] = LOBYTE(wparam); ch[1] = HIBYTE(wparam); RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 ); wparam = MAKEWPARAM(wch[0], wch[1]); break; case WM_IME_CHAR: ch[0] = HIBYTE(wparam); ch[1] = LOBYTE(wparam); if (ch[0]) RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch, 2 ); else RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch + 1, 1 ); wparam = MAKEWPARAM(wch[0], HIWORD(wparam)); break; } return wparam; } static BOOL FASTCALL MsgiUMToKMMessage(PMSG UMMsg, PMSG KMMsg, BOOL Posted) { *KMMsg = *UMMsg; switch (UMMsg->message) { case WM_COPYDATA: { PCOPYDATASTRUCT pUMCopyData = (PCOPYDATASTRUCT)UMMsg->lParam; PCOPYDATASTRUCT pKMCopyData; pKMCopyData = HeapAlloc(GetProcessHeap(), 0, sizeof(COPYDATASTRUCT) + pUMCopyData->cbData); if (!pKMCopyData) { SetLastError(ERROR_OUTOFMEMORY); return FALSE; } pKMCopyData->dwData = pUMCopyData->dwData; pKMCopyData->cbData = pUMCopyData->cbData; pKMCopyData->lpData = pKMCopyData + 1; RtlCopyMemory(pKMCopyData + 1, pUMCopyData->lpData, pUMCopyData->cbData); KMMsg->lParam = (LPARAM)pKMCopyData; } break; default: break; } return TRUE; } static VOID FASTCALL MsgiUMToKMCleanup(PMSG UMMsg, PMSG KMMsg) { switch (KMMsg->message) { case WM_COPYDATA: HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam); break; default: break; } return; } static BOOL FASTCALL MsgiKMToUMMessage(PMSG KMMsg, PMSG UMMsg) { *UMMsg = *KMMsg; if (KMMsg->lParam == 0) return TRUE; switch (UMMsg->message) { case WM_CREATE: case WM_NCCREATE: { CREATESTRUCTW *Cs = (CREATESTRUCTW *) KMMsg->lParam; PCHAR Class; Cs->lpszName = (LPCWSTR) ((PCHAR) Cs + (DWORD_PTR) Cs->lpszName); Class = (PCHAR) Cs + (DWORD_PTR) Cs->lpszClass; if (L'A' == *((WCHAR *) Class)) { Class += sizeof(WCHAR); Cs->lpszClass = (LPCWSTR)(DWORD_PTR) (*((ATOM *) Class)); } else { ASSERT(L'S' == *((WCHAR *) Class)); Class += sizeof(WCHAR); Cs->lpszClass = (LPCWSTR) Class; } } break; case WM_COPYDATA: { PCOPYDATASTRUCT pKMCopyData = (PCOPYDATASTRUCT)KMMsg->lParam; pKMCopyData->lpData = pKMCopyData + 1; } break; default: break; } return TRUE; } static VOID FASTCALL MsgiKMToUMCleanup(PMSG KMMsg, PMSG UMMsg) { switch (KMMsg->message) { case WM_DDE_EXECUTE: #ifdef TODO HeapFree(GetProcessHeap(), 0, (LPVOID) KMMsg->lParam); GlobalUnlock((HGLOBAL) UMMsg->lParam); #endif break; default: break; } return; } static BOOL FASTCALL MsgiKMToUMReply(PMSG KMMsg, PMSG UMMsg, LRESULT *Result) { MsgiKMToUMCleanup(KMMsg, UMMsg); return TRUE; } // // Ansi to Unicode -> callout // static BOOL FASTCALL MsgiAnsiToUnicodeMessage(HWND hwnd, LPMSG UnicodeMsg, LPMSG AnsiMsg) { UNICODE_STRING UnicodeString; *UnicodeMsg = *AnsiMsg; switch (AnsiMsg->message) { case WM_GETTEXT: case WM_ASKCBFORMATNAME: { LPWSTR Buffer; if (!AnsiMsg->lParam) break; Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, AnsiMsg->wParam * sizeof(WCHAR)); //ERR("WM_GETTEXT A2U Size %d\n",AnsiMsg->wParam); if (!Buffer) return FALSE; UnicodeMsg->lParam = (LPARAM)Buffer; break; } case LB_GETTEXT: { DWORD Size = 1024 * sizeof(WCHAR); if (!AnsiMsg->lParam || !listbox_has_strings( AnsiMsg->hwnd )) break; /*Size = SendMessageW( AnsiMsg->hwnd, LB_GETTEXTLEN, AnsiMsg->wParam, 0 ); if (Size == LB_ERR) { ERR("LB_GETTEXT LB_ERR\n"); Size = sizeof(ULONG_PTR); } Size = (Size + 1) * sizeof(WCHAR);*/ UnicodeMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); if (!UnicodeMsg->lParam) return FALSE; break; } case CB_GETLBTEXT: { DWORD Size = 1024 * sizeof(WCHAR); if (!AnsiMsg->lParam || !combobox_has_strings( AnsiMsg->hwnd )) break; /*Size = SendMessageW( AnsiMsg->hwnd, CB_GETLBTEXTLEN, AnsiMsg->wParam, 0 ); if (Size == LB_ERR) { ERR("CB_GETTEXT LB_ERR\n"); Size = sizeof(ULONG_PTR); } Size = (Size + 1) * sizeof(WCHAR);*/ UnicodeMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); if (!UnicodeMsg->lParam) return FALSE; break; } /* AnsiMsg->lParam is string (0-terminated) */ case WM_SETTEXT: case WM_WININICHANGE: case WM_DEVMODECHANGE: case CB_DIR: case LB_DIR: case LB_ADDFILE: case EM_REPLACESEL: { if (!AnsiMsg->lParam) break; RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam); UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer; break; } case LB_ADDSTRING: case LB_ADDSTRING_LOWER: case LB_ADDSTRING_UPPER: case LB_INSERTSTRING: case LB_INSERTSTRING_UPPER: case LB_INSERTSTRING_LOWER: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_SELECTSTRING: { if (AnsiMsg->lParam && listbox_has_strings(AnsiMsg->hwnd)) { RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam); UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer; } break; } case CB_ADDSTRING: case CB_INSERTSTRING: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_SELECTSTRING: { if (AnsiMsg->lParam && combobox_has_strings(AnsiMsg->hwnd)) { RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)AnsiMsg->lParam); UnicodeMsg->lParam = (LPARAM)UnicodeString.Buffer; } break; } case WM_NCCREATE: case WM_CREATE: { struct s { CREATESTRUCTW cs; /* new structure */ MDICREATESTRUCTW mdi_cs; /* MDI info */ LPCWSTR lpszName; /* allocated Name */ LPCWSTR lpszClass; /* allocated Class */ }; struct s *xs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct s)); if (!xs) { return FALSE; } xs->cs = *(CREATESTRUCTW *)AnsiMsg->lParam; if (!IS_INTRESOURCE(xs->cs.lpszName)) { RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)xs->cs.lpszName); xs->lpszName = xs->cs.lpszName = UnicodeString.Buffer; } if (!IS_ATOM(xs->cs.lpszClass)) { RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)xs->cs.lpszClass); xs->lpszClass = xs->cs.lpszClass = UnicodeString.Buffer; } if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_MDICHILD) { xs->mdi_cs = *(MDICREATESTRUCTW *)xs->cs.lpCreateParams; xs->mdi_cs.szTitle = xs->cs.lpszName; xs->mdi_cs.szClass = xs->cs.lpszClass; xs->cs.lpCreateParams = &xs->mdi_cs; } UnicodeMsg->lParam = (LPARAM)xs; break; } case WM_MDICREATE: { MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)HeapAlloc(GetProcessHeap(), 0, sizeof(*cs)); if (!cs) { return FALSE; } *cs = *(MDICREATESTRUCTW *)AnsiMsg->lParam; if (!IS_ATOM(cs->szClass)) { RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)cs->szClass); cs->szClass = UnicodeString.Buffer; } RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)cs->szTitle); cs->szTitle = UnicodeString.Buffer; UnicodeMsg->lParam = (LPARAM)cs; break; } case WM_GETDLGCODE: if (UnicodeMsg->lParam) { MSG newmsg = *(MSG *)UnicodeMsg->lParam; newmsg.wParam = map_wparam_AtoW( newmsg.message, newmsg.wParam); } break; case WM_CHARTOITEM: case WM_MENUCHAR: case WM_CHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case EM_SETPASSWORDCHAR: case WM_IME_CHAR: UnicodeMsg->wParam = map_wparam_AtoW( AnsiMsg->message, AnsiMsg->wParam ); break; case EM_GETLINE: ERR("FIXME EM_GETLINE A2U\n"); break; } return TRUE; } static BOOL FASTCALL MsgiAnsiToUnicodeCleanup(LPMSG UnicodeMsg, LPMSG AnsiMsg) { UNICODE_STRING UnicodeString; switch (AnsiMsg->message) { case LB_GETTEXT: if (!listbox_has_strings( UnicodeMsg->hwnd )) break; case CB_GETLBTEXT: if (UnicodeMsg->message == CB_GETLBTEXT && !combobox_has_strings( UnicodeMsg->hwnd )) break; case WM_GETTEXT: case WM_ASKCBFORMATNAME: { if (!UnicodeMsg->lParam) break; RtlFreeHeap(GetProcessHeap(), 0, (PVOID) UnicodeMsg->lParam); break; } case WM_SETTEXT: case WM_WININICHANGE: case WM_DEVMODECHANGE: case CB_DIR: case LB_DIR: case LB_ADDFILE: case EM_REPLACESEL: { if (!UnicodeMsg->lParam) break; RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam); RtlFreeUnicodeString(&UnicodeString); break; } case LB_ADDSTRING: case LB_ADDSTRING_LOWER: case LB_ADDSTRING_UPPER: case LB_INSERTSTRING: case LB_INSERTSTRING_UPPER: case LB_INSERTSTRING_LOWER: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_SELECTSTRING: { if (UnicodeMsg->lParam && listbox_has_strings(AnsiMsg->hwnd)) { RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam); RtlFreeUnicodeString(&UnicodeString); } break; } case CB_ADDSTRING: case CB_INSERTSTRING: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_SELECTSTRING: { if (UnicodeMsg->lParam && combobox_has_strings(AnsiMsg->hwnd)) { RtlInitUnicodeString(&UnicodeString, (PCWSTR)UnicodeMsg->lParam); RtlFreeUnicodeString(&UnicodeString); } break; } case WM_NCCREATE: case WM_CREATE: { struct s { CREATESTRUCTW cs; /* new structure */ MDICREATESTRUCTW mdi_cs; /* MDI info */ LPWSTR lpszName; /* allocated Name */ LPWSTR lpszClass; /* allocated Class */ }; struct s *xs = (struct s *)UnicodeMsg->lParam; if (xs->lpszName) { RtlInitUnicodeString(&UnicodeString, (PCWSTR)xs->lpszName); RtlFreeUnicodeString(&UnicodeString); } if (xs->lpszClass) { RtlInitUnicodeString(&UnicodeString, (PCWSTR)xs->lpszClass); RtlFreeUnicodeString(&UnicodeString); } HeapFree(GetProcessHeap(), 0, xs); break; } case WM_MDICREATE: { MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)UnicodeMsg->lParam; RtlInitUnicodeString(&UnicodeString, (PCWSTR)cs->szTitle); RtlFreeUnicodeString(&UnicodeString); if (!IS_ATOM(cs->szClass)) { RtlInitUnicodeString(&UnicodeString, (PCWSTR)cs->szClass); RtlFreeUnicodeString(&UnicodeString); } HeapFree(GetProcessHeap(), 0, cs); break; } } return(TRUE); } /* * callout return -> Unicode Result to Ansi Result */ static BOOL FASTCALL MsgiAnsiToUnicodeReply(LPMSG UnicodeMsg, LPMSG AnsiMsg, LRESULT *Result) { LPWSTR Buffer = (LPWSTR)UnicodeMsg->lParam; LPSTR AnsiBuffer = (LPSTR)AnsiMsg->lParam; switch (AnsiMsg->message) { case WM_GETTEXT: case WM_ASKCBFORMATNAME: { if (UnicodeMsg->wParam) { DWORD len = 0; if (*Result) RtlUnicodeToMultiByteN( AnsiBuffer, UnicodeMsg->wParam - 1, &len, Buffer, strlenW(Buffer) * sizeof(WCHAR)); AnsiBuffer[len] = 0; *Result = len; //ERR("WM_GETTEXT U2A Result %d Size %d\n",*Result,AnsiMsg->wParam); } break; } case LB_GETTEXT: { if (!AnsiBuffer || !listbox_has_strings( UnicodeMsg->hwnd )) break; if (*Result >= 0) { DWORD len; RtlUnicodeToMultiByteN( AnsiBuffer, ~0u, &len, Buffer, (strlenW(Buffer) + 1) * sizeof(WCHAR) ); *Result = len - 1; } break; } case CB_GETLBTEXT: { if (!AnsiBuffer || !combobox_has_strings( UnicodeMsg->hwnd )) break; if (*Result >= 0) { DWORD len; RtlUnicodeToMultiByteN( AnsiBuffer, ~0u, &len, Buffer, (strlenW(Buffer) + 1) * sizeof(WCHAR) ); *Result = len - 1; } break; } } MsgiAnsiToUnicodeCleanup(UnicodeMsg, AnsiMsg); return TRUE; } // // Unicode to Ansi callout -> // static BOOL FASTCALL MsgiUnicodeToAnsiMessage(HWND hwnd, LPMSG AnsiMsg, LPMSG UnicodeMsg) { ANSI_STRING AnsiString; UNICODE_STRING UnicodeString; *AnsiMsg = *UnicodeMsg; switch(UnicodeMsg->message) { case WM_CREATE: case WM_NCCREATE: { MDICREATESTRUCTA *pmdi_cs; CREATESTRUCTA* CsA; CREATESTRUCTW* CsW; ULONG NameSize, ClassSize; NTSTATUS Status; CsW = (CREATESTRUCTW*)(UnicodeMsg->lParam); RtlInitUnicodeString(&UnicodeString, CsW->lpszName); NameSize = RtlUnicodeStringToAnsiSize(&UnicodeString); if (NameSize == 0) { return FALSE; } ClassSize = 0; if (!IS_ATOM(CsW->lpszClass)) { RtlInitUnicodeString(&UnicodeString, CsW->lpszClass); ClassSize = RtlUnicodeStringToAnsiSize(&UnicodeString); if (ClassSize == 0) { return FALSE; } } CsA = RtlAllocateHeap(GetProcessHeap(), 0, sizeof(CREATESTRUCTA) + sizeof(MDICREATESTRUCTA) + NameSize + ClassSize); if (NULL == CsA) { return FALSE; } memcpy(CsA, CsW, sizeof(CREATESTRUCTW)); /* pmdi_cs starts right after CsA */ pmdi_cs = (MDICREATESTRUCTA*)(CsA + 1); RtlInitUnicodeString(&UnicodeString, CsW->lpszName); RtlInitEmptyAnsiString(&AnsiString, (PCHAR)(pmdi_cs + 1), NameSize); Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE); if (! NT_SUCCESS(Status)) { RtlFreeHeap(GetProcessHeap(), 0, CsA); return FALSE; } CsA->lpszName = AnsiString.Buffer; if (!IS_ATOM(CsW->lpszClass)) { RtlInitUnicodeString(&UnicodeString, CsW->lpszClass); RtlInitEmptyAnsiString(&AnsiString, (PCHAR)(pmdi_cs + 1) + NameSize, ClassSize); Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE); if (! NT_SUCCESS(Status)) { RtlFreeHeap(GetProcessHeap(), 0, CsA); return FALSE; } CsA->lpszClass = AnsiString.Buffer; } if (GetWindowLongW(hwnd, GWL_EXSTYLE) & WS_EX_MDICHILD) { *pmdi_cs = *(MDICREATESTRUCTA *)CsW->lpCreateParams; pmdi_cs->szTitle = CsA->lpszName; pmdi_cs->szClass = CsA->lpszClass; CsA->lpCreateParams = pmdi_cs; } AnsiMsg->lParam = (LPARAM)CsA; break; } case WM_GETTEXT: case WM_ASKCBFORMATNAME: { if (!UnicodeMsg->lParam) break; /* Ansi string might contain MBCS chars so we need 2 * the number of chars */ AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, UnicodeMsg->wParam * 2); //ERR("WM_GETTEXT U2A Size %d\n",AnsiMsg->wParam); if (!AnsiMsg->lParam) return FALSE; break; } case LB_GETTEXT: { DWORD Size = 1024; if (!UnicodeMsg->lParam || !listbox_has_strings( UnicodeMsg->hwnd )) break; /*Size = SendMessageA( UnicodeMsg->hwnd, LB_GETTEXTLEN, UnicodeMsg->wParam, 0 ); if (Size == LB_ERR) { ERR("LB_GETTEXT LB_ERR\n"); Size = sizeof(ULONG_PTR); } Size = (Size + 1) * sizeof(WCHAR);*/ AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); if (!AnsiMsg->lParam) return FALSE; break; } case CB_GETLBTEXT: { DWORD Size = 1024; if (!UnicodeMsg->lParam || !combobox_has_strings( UnicodeMsg->hwnd )) break; /*Size = SendMessageA( UnicodeMsg->hwnd, CB_GETLBTEXTLEN, UnicodeMsg->wParam, 0 ); if (Size == LB_ERR) { ERR("CB_GETTEXT LB_ERR\n"); Size = sizeof(ULONG_PTR); } Size = (Size + 1) * sizeof(WCHAR);*/ AnsiMsg->lParam = (LPARAM) RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Size); if (!AnsiMsg->lParam) return FALSE; break; } case WM_SETTEXT: case WM_WININICHANGE: case WM_DEVMODECHANGE: case CB_DIR: case LB_DIR: case LB_ADDFILE: case EM_REPLACESEL: { if (!UnicodeMsg->lParam) break; RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam); if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE))) { return FALSE; } AnsiMsg->lParam = (LPARAM) AnsiString.Buffer; break; } case LB_ADDSTRING: case LB_ADDSTRING_LOWER: case LB_ADDSTRING_UPPER: case LB_INSERTSTRING: case LB_INSERTSTRING_UPPER: case LB_INSERTSTRING_LOWER: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_SELECTSTRING: { if (UnicodeMsg->lParam && listbox_has_strings(AnsiMsg->hwnd)) { RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam); if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE))) { return FALSE; } AnsiMsg->lParam = (LPARAM) AnsiString.Buffer; } break; } case CB_ADDSTRING: case CB_INSERTSTRING: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_SELECTSTRING: { if (UnicodeMsg->lParam && combobox_has_strings(AnsiMsg->hwnd)) { RtlInitUnicodeString(&UnicodeString, (PWSTR) UnicodeMsg->lParam); if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE))) { return FALSE; } AnsiMsg->lParam = (LPARAM) AnsiString.Buffer; } break; } case WM_MDICREATE: { MDICREATESTRUCTA *cs = (MDICREATESTRUCTA *)HeapAlloc(GetProcessHeap(), 0, sizeof(*cs)); if (!cs) { return FALSE; } *cs = *(MDICREATESTRUCTA *)UnicodeMsg->lParam; if (!IS_ATOM(cs->szClass)) { RtlInitUnicodeString(&UnicodeString, (LPCWSTR)cs->szClass); if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE))) { HeapFree(GetProcessHeap(), 0, cs); return FALSE; } cs->szClass = AnsiString.Buffer; } RtlInitUnicodeString(&UnicodeString, (LPCWSTR)cs->szTitle); if (! NT_SUCCESS(RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE))) { if (!IS_ATOM(cs->szClass)) { RtlInitAnsiString(&AnsiString, cs->szClass); RtlFreeAnsiString(&AnsiString); } HeapFree(GetProcessHeap(), 0, cs); return FALSE; } cs->szTitle = AnsiString.Buffer; AnsiMsg->lParam = (LPARAM)cs; break; } case WM_GETDLGCODE: if (UnicodeMsg->lParam) { MSG newmsg = *(MSG *)UnicodeMsg->lParam; switch(newmsg.message) { case WM_CHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: newmsg.wParam = map_wparam_char_WtoA( newmsg.wParam, 1 ); break; case WM_IME_CHAR: newmsg.wParam = map_wparam_char_WtoA( newmsg.wParam, 2 ); break; } } break; case WM_CHAR: { WCHAR wch = UnicodeMsg->wParam; char ch[2]; DWORD cp = get_input_codepage(); DWORD len = WideCharToMultiByte( cp, 0, &wch, 1, ch, 2, NULL, NULL ); AnsiMsg->wParam = (BYTE)ch[0]; if (len == 2) AnsiMsg->wParam = (BYTE)ch[1]; } break; case WM_CHARTOITEM: case WM_MENUCHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case EM_SETPASSWORDCHAR: AnsiMsg->wParam = map_wparam_char_WtoA(UnicodeMsg->wParam,1); break; case WM_IME_CHAR: AnsiMsg->wParam = map_wparam_char_WtoA(UnicodeMsg->wParam,2); break; case EM_GETLINE: ERR("FIXME EM_GETLINE U2A\n"); break; } return TRUE; } static BOOL FASTCALL MsgiUnicodeToAnsiCleanup(LPMSG AnsiMsg, LPMSG UnicodeMsg) { ANSI_STRING AnsiString; switch(UnicodeMsg->message) { case LB_GETTEXT: if (!listbox_has_strings( AnsiMsg->hwnd )) break; case CB_GETLBTEXT: if (AnsiMsg->message == CB_GETLBTEXT && !combobox_has_strings( AnsiMsg->hwnd )) break; case WM_GETTEXT: case WM_ASKCBFORMATNAME: { if (!AnsiMsg->lParam) break; RtlFreeHeap(GetProcessHeap(), 0, (PVOID) AnsiMsg->lParam); break; } case WM_CREATE: case WM_NCCREATE: { CREATESTRUCTA* Cs; Cs = (CREATESTRUCTA*) AnsiMsg->lParam; RtlFreeHeap(GetProcessHeap(), 0, Cs); break; } case WM_SETTEXT: case WM_WININICHANGE: case WM_DEVMODECHANGE: case CB_DIR: case LB_DIR: case LB_ADDFILE: case EM_REPLACESEL: { if (!AnsiMsg->lParam) break; RtlInitAnsiString(&AnsiString, (PSTR) AnsiMsg->lParam); RtlFreeAnsiString(&AnsiString); break; } case LB_ADDSTRING: case LB_ADDSTRING_LOWER: case LB_ADDSTRING_UPPER: case LB_INSERTSTRING: case LB_INSERTSTRING_UPPER: case LB_INSERTSTRING_LOWER: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_SELECTSTRING: { if (AnsiMsg->lParam && listbox_has_strings(AnsiMsg->hwnd)) { RtlInitAnsiString(&AnsiString, (PSTR) AnsiMsg->lParam); RtlFreeAnsiString(&AnsiString); } break; } case CB_ADDSTRING: case CB_INSERTSTRING: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_SELECTSTRING: { if (AnsiMsg->lParam && combobox_has_strings(AnsiMsg->hwnd)) { RtlInitAnsiString(&AnsiString, (PSTR) AnsiMsg->lParam); RtlFreeAnsiString(&AnsiString); } break; } case WM_MDICREATE: { MDICREATESTRUCTA *cs = (MDICREATESTRUCTA *)AnsiMsg->lParam; RtlInitAnsiString(&AnsiString, (PCSTR)cs->szTitle); RtlFreeAnsiString(&AnsiString); if (!IS_ATOM(cs->szClass)) { RtlInitAnsiString(&AnsiString, (PCSTR)cs->szClass); RtlFreeAnsiString(&AnsiString); } HeapFree(GetProcessHeap(), 0, cs); break; } } return TRUE; } /* * callout return -> Ansi Result to Unicode Result */ static BOOL FASTCALL MsgiUnicodeToAnsiReply(LPMSG AnsiMsg, LPMSG UnicodeMsg, LRESULT *Result) { LPSTR Buffer = (LPSTR) AnsiMsg->lParam; LPWSTR UBuffer = (LPWSTR) UnicodeMsg->lParam; switch (UnicodeMsg->message) { case WM_GETTEXT: case WM_ASKCBFORMATNAME: { DWORD len = AnsiMsg->wParam;// * 2; if (len) { if (*Result) { RtlMultiByteToUnicodeN( UBuffer, AnsiMsg->wParam*sizeof(WCHAR), &len, Buffer, strlen(Buffer)+1 ); *Result = len/sizeof(WCHAR) - 1; /* do not count terminating null */ //ERR("WM_GETTEXT U2A Result %d Size %d\n",*Result,AnsiMsg->wParam); } UBuffer[*Result] = 0; } break; } case LB_GETTEXT: { if (!UBuffer || !listbox_has_strings( UnicodeMsg->hwnd )) break; if (*Result >= 0) { DWORD len; RtlMultiByteToUnicodeN( UBuffer, ~0u, &len, Buffer, strlen(Buffer) + 1 ); *Result = len / sizeof(WCHAR) - 1; } break; } case CB_GETLBTEXT: { if (!UBuffer || !combobox_has_strings( UnicodeMsg->hwnd )) break; if (*Result >= 0) { DWORD len; RtlMultiByteToUnicodeN( UBuffer, ~0u, &len, Buffer, strlen(Buffer) + 1 ); *Result = len / sizeof(WCHAR) - 1; } break; } } MsgiUnicodeToAnsiCleanup(AnsiMsg, UnicodeMsg); return TRUE; } LRESULT WINAPI DesktopWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT Result; MSG AnsiMsg, UcMsg; TRACE("Desktop A Class Atom! hWnd 0x%x, Msg %d\n", hwnd, message); AnsiMsg.hwnd = hwnd; AnsiMsg.message = message; AnsiMsg.wParam = wParam; AnsiMsg.lParam = lParam; AnsiMsg.time = 0; AnsiMsg.pt.x = 0; AnsiMsg.pt.y = 0; // Desktop is always Unicode so convert Ansi here. if (!MsgiAnsiToUnicodeMessage(hwnd, &UcMsg, &AnsiMsg)) { return FALSE; } Result = DesktopWndProcW(hwnd, message, UcMsg.wParam, UcMsg.lParam); MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg); return Result; } /* * @implemented */ LPARAM WINAPI GetMessageExtraInfo(VOID) { return NtUserxGetMessageExtraInfo(); } /* * @implemented */ DWORD WINAPI GetMessagePos(VOID) { return NtUserxGetMessagePos(); } /* * @implemented */ LONG WINAPI GetMessageTime(VOID) { return NtUserGetThreadState(THREADSTATE_GETMESSAGETIME); } /* * @implemented */ BOOL WINAPI InSendMessage(VOID) { PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo; if ( pcti ) { if (pcti->CTI_flags & CTI_INSENDMESSAGE) { return TRUE; } } return(NtUserGetThreadState(THREADSTATE_INSENDMESSAGE) != ISMEX_NOSEND); } /* * @implemented */ DWORD WINAPI InSendMessageEx( LPVOID lpReserved) { PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo; if (pcti && !(pcti->CTI_flags & CTI_INSENDMESSAGE)) return ISMEX_NOSEND; else return NtUserGetThreadState(THREADSTATE_INSENDMESSAGE); } /* * @implemented */ BOOL WINAPI ReplyMessage(LRESULT lResult) { return NtUserxReplyMessage(lResult); } /* * @implemented */ LPARAM WINAPI SetMessageExtraInfo( LPARAM lParam) { return NtUserxSetMessageExtraInfo(lParam); } LRESULT FASTCALL IntCallWindowProcW(BOOL IsAnsiProc, WNDPROC WndProc, PWND pWnd, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { MSG AnsiMsg; MSG UnicodeMsg; BOOL Hook = FALSE, MsgOverride = FALSE, Dialog, DlgOverride = FALSE; LRESULT Result = 0, PreResult = 0; DWORD Data = 0; if (WndProc == NULL) { WARN("IntCallWindowsProcW() called with WndProc = NULL!\n"); return FALSE; } if (pWnd) Dialog = (pWnd->fnid == FNID_DIALOG); else Dialog = FALSE; Hook = BeginIfHookedUserApiHook(); if (Hook) { if (Dialog) DlgOverride = IsMsgOverride( Msg, &guah.DlgProcArray); MsgOverride = IsMsgOverride( Msg, &guah.WndProcArray); } if (IsAnsiProc) { UnicodeMsg.hwnd = hWnd; UnicodeMsg.message = Msg; UnicodeMsg.wParam = wParam; UnicodeMsg.lParam = lParam; UnicodeMsg.time = 0; UnicodeMsg.pt.x = 0; UnicodeMsg.pt.y = 0; if (! MsgiUnicodeToAnsiMessage(hWnd, &AnsiMsg, &UnicodeMsg)) { goto Exit; } if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) PreResult = guah.PreWndProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data ); else PreResult = guah.PreDefDlgProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } if (PreResult) goto Exit; if (!Dialog) Result = CALL_EXTERN_WNDPROC(WndProc, AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam); else { _SEH2_TRY { Result = CALL_EXTERN_WNDPROC(WndProc, AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ERR("Exception Dialog Ansi %p Msg %d pti %p Wndpti %p\n",WndProc,Msg,GetW32ThreadInfo(),pWnd->head.pti); } _SEH2_END; } if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) guah.PostWndProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data ); else guah.PostDefDlgProc(AnsiMsg.hwnd, AnsiMsg.message, AnsiMsg.wParam, AnsiMsg.lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } if (! MsgiUnicodeToAnsiReply(&AnsiMsg, &UnicodeMsg, &Result)) { goto Exit; } } else { if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) PreResult = guah.PreWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); else PreResult = guah.PreDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } if (PreResult) goto Exit; if (!Dialog) Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam); else { _SEH2_TRY { Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ERR("Exception Dialog unicode %p Msg %d pti %p Wndpti %p\n",WndProc, Msg,GetW32ThreadInfo(),pWnd->head.pti); } _SEH2_END; } if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) guah.PostWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); else guah.PostDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } } Exit: if (Hook) EndUserApiHook(); return Result; } static LRESULT FASTCALL IntCallWindowProcA(BOOL IsAnsiProc, WNDPROC WndProc, PWND pWnd, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { MSG AnsiMsg; MSG UnicodeMsg; BOOL Hook = FALSE, MsgOverride = FALSE, Dialog, DlgOverride = FALSE; LRESULT Result = 0, PreResult = 0; DWORD Data = 0; TRACE("IntCallWindowProcA: IsAnsiProc : %s, WndProc %p, pWnd %p, hWnd %p, Msg %u, wParam %Iu, lParam %Iu.\n", IsAnsiProc ? "TRUE" : "FALSE", WndProc, pWnd, hWnd, Msg, wParam, lParam); if (WndProc == NULL) { WARN("IntCallWindowsProcA() called with WndProc = NULL!\n"); return FALSE; } if (pWnd) Dialog = (pWnd->fnid == FNID_DIALOG); else Dialog = FALSE; Hook = BeginIfHookedUserApiHook(); if (Hook) { if (Dialog) DlgOverride = IsMsgOverride( Msg, &guah.DlgProcArray); MsgOverride = IsMsgOverride( Msg, &guah.WndProcArray); } if (IsAnsiProc) { if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) PreResult = guah.PreWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); else PreResult = guah.PreDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } if (PreResult) goto Exit; if (!Dialog) Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam); else { _SEH2_TRY { Result = CALL_EXTERN_WNDPROC(WndProc, hWnd, Msg, wParam, lParam); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ERR("Exception Dialog Ansi %p Msg %d pti %p Wndpti %p\n",WndProc,Msg,GetW32ThreadInfo(),pWnd->head.pti); } _SEH2_END; } if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) guah.PostWndProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); else guah.PostDefDlgProc(hWnd, Msg, wParam, lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } } else { AnsiMsg.hwnd = hWnd; AnsiMsg.message = Msg; AnsiMsg.wParam = wParam; AnsiMsg.lParam = lParam; AnsiMsg.time = 0; AnsiMsg.pt.x = 0; AnsiMsg.pt.y = 0; if (! MsgiAnsiToUnicodeMessage(hWnd, &UnicodeMsg, &AnsiMsg)) { goto Exit; } if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) PreResult = guah.PreWndProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data ); else PreResult = guah.PreDefDlgProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } if (PreResult) goto Exit; if (!Dialog) Result = CALL_EXTERN_WNDPROC(WndProc, UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam); else { _SEH2_TRY { Result = CALL_EXTERN_WNDPROC(WndProc, UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ERR("Exception Dialog unicode %p Msg %d pti %p Wndpti %p\n",WndProc, Msg,GetW32ThreadInfo(),pWnd->head.pti); } _SEH2_END; } if (Hook && (MsgOverride || DlgOverride)) { _SEH2_TRY { if (!DlgOverride) guah.PostWndProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data ); else guah.PostDefDlgProc(UnicodeMsg.hwnd, UnicodeMsg.message, UnicodeMsg.wParam, UnicodeMsg.lParam, (ULONG_PTR)&Result, &Data ); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { } _SEH2_END; } if (! MsgiAnsiToUnicodeReply(&UnicodeMsg, &AnsiMsg, &Result)) { goto Exit; } } Exit: if (Hook) EndUserApiHook(); return Result; } static LRESULT WINAPI IntCallMessageProc(IN PWND Wnd, IN HWND hWnd, IN UINT Msg, IN WPARAM wParam, IN LPARAM lParam, IN BOOL Ansi) { WNDPROC WndProc; BOOL IsAnsi; PCLS Class; Class = DesktopPtrToUser(Wnd->pcls); WndProc = NULL; if ( Wnd->head.pti != GetW32ThreadInfo()) { // Must be inside the same thread! SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return 0; } /* This is the message exchange for user32. If there's a need to monitor messages, do it here! */ TRACE("HWND %p, MSG %u, WPARAM %p, LPARAM %p, Ansi %d\n", hWnd, Msg, wParam, lParam, Ansi); // if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON ) if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_FIRST ) { if (Ansi) { if (GETPFNCLIENTW(Class->fnid) == Wnd->lpfnWndProc) WndProc = GETPFNCLIENTA(Class->fnid); } else { if (GETPFNCLIENTA(Class->fnid) == Wnd->lpfnWndProc) WndProc = GETPFNCLIENTW(Class->fnid); } IsAnsi = Ansi; if (!WndProc) { IsAnsi = !Wnd->Unicode; WndProc = Wnd->lpfnWndProc; } } else { IsAnsi = !Wnd->Unicode; WndProc = Wnd->lpfnWndProc; } /* Message caller can be Ansi/Unicode and the receiver can be Unicode/Ansi or the same. */ if (!Ansi) return IntCallWindowProcW(IsAnsi, WndProc, Wnd, hWnd, Msg, wParam, lParam); else return IntCallWindowProcA(IsAnsi, WndProc, Wnd, hWnd, Msg, wParam, lParam); } /* * @implemented */ LRESULT WINAPI CallWindowProcA(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { PWND pWnd; PCALLPROCDATA CallProc; if (lpPrevWndFunc == NULL) { WARN("CallWindowProcA: lpPrevWndFunc == NULL!\n"); return 0; } pWnd = ValidateHwnd(hWnd); if (!IsCallProcHandle(lpPrevWndFunc)) return IntCallWindowProcA(TRUE, lpPrevWndFunc, pWnd, hWnd, Msg, wParam, lParam); else { CallProc = ValidateCallProc((HANDLE)lpPrevWndFunc); if (CallProc != NULL) { return IntCallWindowProcA(!(CallProc->wType & UserGetCPDA2U), CallProc->pfnClientPrevious, pWnd, hWnd, Msg, wParam, lParam); } else { WARN("CallWindowProcA: can not dereference WndProcHandle\n"); return 0; } } } /* * @implemented */ LRESULT WINAPI CallWindowProcW(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { PWND pWnd; PCALLPROCDATA CallProc; /* FIXME - can the first parameter be NULL? */ if (lpPrevWndFunc == NULL) { WARN("CallWindowProcA: lpPrevWndFunc == NULL!\n"); return 0; } pWnd = ValidateHwnd(hWnd); if (!IsCallProcHandle(lpPrevWndFunc)) return IntCallWindowProcW(FALSE, lpPrevWndFunc, pWnd, hWnd, Msg, wParam, lParam); else { CallProc = ValidateCallProc((HANDLE)lpPrevWndFunc); if (CallProc != NULL) { return IntCallWindowProcW(!(CallProc->wType & UserGetCPDA2U), CallProc->pfnClientPrevious, pWnd, hWnd, Msg, wParam, lParam); } else { WARN("CallWindowProcW: can not dereference WndProcHandle\n"); return 0; } } } /* * @implemented */ LRESULT WINAPI DECLSPEC_HOTPATCH DispatchMessageA(CONST MSG *lpmsg) { LRESULT Ret = 0; MSG UnicodeMsg; PWND Wnd; BOOL Hit = FALSE; if ( lpmsg->message & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (lpmsg->hwnd != NULL) { Wnd = ValidateHwnd(lpmsg->hwnd); if (!Wnd) return 0; } else Wnd = NULL; if (is_pointer_message(lpmsg->message)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return 0; } if ((lpmsg->message == WM_TIMER || lpmsg->message == WM_SYSTIMER) && lpmsg->lParam != 0) { WNDPROC WndProc = (WNDPROC)lpmsg->lParam; if ( lpmsg->message == WM_SYSTIMER ) return NtUserDispatchMessage( (PMSG)lpmsg ); if (!NtUserValidateTimerCallback(lpmsg->lParam)) { WARN("Validating Timer Callback failed!\n"); return 0; } _SEH2_TRY // wine does this. Hint: Prevents call to another thread.... { Ret = WndProc(lpmsg->hwnd, lpmsg->message, lpmsg->wParam, GetTickCount()); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Hit = TRUE; } _SEH2_END; } else if (Wnd != NULL) { if ( (lpmsg->message != WM_PAINT) && !(Wnd->state & WNDS_SERVERSIDEWINDOWPROC) ) { Ret = IntCallMessageProc(Wnd, lpmsg->hwnd, lpmsg->message, lpmsg->wParam, lpmsg->lParam, TRUE); } else { if (!MsgiAnsiToUnicodeMessage(lpmsg->hwnd, &UnicodeMsg, (LPMSG)lpmsg)) { return FALSE; } Ret = NtUserDispatchMessage(&UnicodeMsg); if (!MsgiAnsiToUnicodeReply(&UnicodeMsg, (LPMSG)lpmsg, &Ret)) { return FALSE; } } } if (Hit) { WARN("Exception in Timer Callback WndProcA!\n"); } return Ret; } /* * @implemented */ LRESULT WINAPI DECLSPEC_HOTPATCH DispatchMessageW(CONST MSG *lpmsg) { LRESULT Ret = 0; PWND Wnd; BOOL Hit = FALSE; if ( lpmsg->message & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (lpmsg->hwnd != NULL) { Wnd = ValidateHwnd(lpmsg->hwnd); if (!Wnd) return 0; } else Wnd = NULL; if (is_pointer_message(lpmsg->message)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return 0; } if ((lpmsg->message == WM_TIMER || lpmsg->message == WM_SYSTIMER) && lpmsg->lParam != 0) { WNDPROC WndProc = (WNDPROC)lpmsg->lParam; if ( lpmsg->message == WM_SYSTIMER ) return NtUserDispatchMessage( (PMSG) lpmsg ); if (!NtUserValidateTimerCallback(lpmsg->lParam)) { WARN("Validating Timer Callback failed!\n"); return 0; } _SEH2_TRY { Ret = WndProc(lpmsg->hwnd, lpmsg->message, lpmsg->wParam, GetTickCount()); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Hit = TRUE; } _SEH2_END; } else if (Wnd != NULL) { if ( (lpmsg->message != WM_PAINT) && !(Wnd->state & WNDS_SERVERSIDEWINDOWPROC) ) { Ret = IntCallMessageProc(Wnd, lpmsg->hwnd, lpmsg->message, lpmsg->wParam, lpmsg->lParam, FALSE); } else Ret = NtUserDispatchMessage( (PMSG) lpmsg ); } if (Hit) { WARN("Exception in Timer Callback WndProcW!\n"); } return Ret; } static VOID IntConvertMsgToAnsi(LPMSG lpMsg) { CHAR ch[2]; WCHAR wch[2]; switch (lpMsg->message) { case WM_CHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: wch[0] = LOWORD(lpMsg->wParam); wch[1] = HIWORD(lpMsg->wParam); ch[0] = ch[1] = 0; WideCharToMultiByte(CP_THREAD_ACP, 0, wch, 2, ch, 2, NULL, NULL); lpMsg->wParam = MAKEWPARAM(ch[0] | (ch[1] << 8), 0); break; } } /* * @implemented */ BOOL WINAPI DECLSPEC_HOTPATCH GetMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) { BOOL Res; if ( (wMsgFilterMin|wMsgFilterMax) & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } Res = NtUserGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); if (-1 == (int) Res) { return Res; } IntConvertMsgToAnsi(lpMsg); return Res; } /* * @implemented */ BOOL WINAPI DECLSPEC_HOTPATCH GetMessageW(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax) { BOOL Res; if ( (wMsgFilterMin|wMsgFilterMax) & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } Res = NtUserGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax); if (-1 == (int) Res) { return Res; } return Res; } BOOL WINAPI PeekMessageWorker( PMSG pMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) { PCLIENTINFO pci; PCLIENTTHREADINFO pcti; pci = GetWin32ClientInfo(); pcti = pci->pClientThreadInfo; if (!hWnd && pci && pcti) { pci->cSpins++; if ((pci->cSpins >= 100) && (pci->dwTIFlags & TIF_SPINNING)) { // Yield after 100 spin cycles and ready to swap vinyl. if (!(pci->dwTIFlags & TIF_WAITFORINPUTIDLE)) { // Not waiting for idle event. if (!pcti->fsChangeBits && !pcti->fsWakeBits) { // No messages are available. if ((GetTickCount() - pcti->tickLastMsgChecked) > 1000) { // Up the msg read count if over 1 sec. NtUserGetThreadState(THREADSTATE_UPTIMELASTREAD); } pci->cSpins = 0; ZwYieldExecution(); FIXME("seeSpins!\n"); return FALSE; } } } } return NtUserPeekMessage(pMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); } /* * @implemented */ BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) { BOOL Res; Res = PeekMessageWorker(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); if (-1 == (int) Res || !Res) { return FALSE; } IntConvertMsgToAnsi(lpMsg); return Res; } /* * @implemented */ BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageW( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg) { BOOL Res; Res = PeekMessageWorker(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg); if (-1 == (int) Res || !Res) { return FALSE; } return Res; } /* * @implemented */ BOOL WINAPI PostMessageA( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { LRESULT Ret; /* Check for combo box or a list box to send names. */ if (Msg == CB_DIR || Msg == LB_DIR) { /* Set DDL_POSTMSGS, so use the PostMessage function to send messages to the combo/list box. Forces a call like DlgDirListComboBox. */ //wParam |= DDL_POSTMSGS; return NtUserPostMessage(hWnd, Msg, wParam, lParam); } /* No drop files or current Process, just post message. */ if ( (Msg != WM_DROPFILES) || ( NtUserQueryWindow( hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID) == PtrToUint(NtCurrentTeb()->ClientId.UniqueProcess) ) ) { return NtUserPostMessage(hWnd, Msg, wParam, lParam); } /* We have drop files and this is not the same process for this window. */ /* Just incase, check wParam for Global memory handle and send size. */ Ret = SendMessageA( hWnd, WM_COPYGLOBALDATA, (WPARAM)GlobalSize((HGLOBAL)wParam), // Zero if not a handle. (LPARAM)wParam); // Send wParam as lParam. if ( Ret ) return NtUserPostMessage(hWnd, Msg, (WPARAM)Ret, lParam); return FALSE; } /* * @implemented */ BOOL WINAPI PostMessageW( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { LRESULT Ret; /* Check for combo box or a list box to send names. */ if (Msg == CB_DIR || Msg == LB_DIR) { /* Set DDL_POSTMSGS, so use the PostMessage function to send messages to the combo/list box. Forces a call like DlgDirListComboBox. */ //wParam |= DDL_POSTMSGS; return NtUserPostMessage(hWnd, Msg, wParam, lParam); } /* No drop files or current Process, just post message. */ if ( (Msg != WM_DROPFILES) || ( NtUserQueryWindow( hWnd, QUERY_WINDOW_UNIQUE_PROCESS_ID) == PtrToUint(NtCurrentTeb()->ClientId.UniqueProcess) ) ) { return NtUserPostMessage(hWnd, Msg, wParam, lParam); } /* We have drop files and this is not the same process for this window. */ /* Just incase, check wParam for Global memory handle and send size. */ Ret = SendMessageW( hWnd, WM_COPYGLOBALDATA, (WPARAM)GlobalSize((HGLOBAL)wParam), // Zero if not a handle. (LPARAM)wParam); // Send wParam as lParam. if ( Ret ) return NtUserPostMessage(hWnd, Msg, (WPARAM)Ret, lParam); return FALSE; } /* * @implemented */ VOID WINAPI PostQuitMessage( int nExitCode) { NtUserxPostQuitMessage(nExitCode); } /* * @implemented */ BOOL WINAPI PostThreadMessageA( DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam) { return NtUserPostThreadMessage(idThread, Msg, wParam, lParam); } /* * @implemented */ BOOL WINAPI PostThreadMessageW( DWORD idThread, UINT Msg, WPARAM wParam, LPARAM lParam) { return NtUserPostThreadMessage(idThread, Msg, wParam, lParam); } /* * @implemented */ LRESULT WINAPI SendMessageW(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) { MSG UMMsg, KMMsg; LRESULT Result; BOOL Ret; PWND Window; PTHREADINFO ti = GetW32ThreadInfo(); if ( Msg & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (Wnd != HWND_TOPMOST && Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST)) { Window = ValidateHwnd(Wnd); if ( Window != NULL && Window->head.pti == ti && !ISITHOOKED(WH_CALLWNDPROC) && !ISITHOOKED(WH_CALLWNDPROCRET) && !(Window->state & WNDS_SERVERSIDEWINDOWPROC) ) { /* NOTE: We can directly send messages to the window procedure if *all* the following conditions are met: * Window belongs to calling thread * The calling thread is not being hooked for CallWndProc * Not calling a server side proc: Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage */ return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, FALSE); } } UMMsg.hwnd = Wnd; UMMsg.message = Msg; UMMsg.wParam = wParam; UMMsg.lParam = lParam; UMMsg.time = 0; UMMsg.pt.x = 0; UMMsg.pt.y = 0; if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, FALSE)) { return FALSE; } Ret = NtUserMessageCall( Wnd, KMMsg.message, KMMsg.wParam, KMMsg.lParam, (ULONG_PTR)&Result, FNID_SENDMESSAGE, FALSE); if (!Ret) { ERR("SendMessageW Error\n"); } MsgiUMToKMCleanup(&UMMsg, &KMMsg); return Result; } /* * @implemented */ LRESULT WINAPI SendMessageA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) { MSG AnsiMsg, UcMsg, KMMsg; LRESULT Result; BOOL Ret; PWND Window; PTHREADINFO ti = GetW32ThreadInfo(); if ( Msg & ~WM_MAXIMUM ) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (Wnd != HWND_TOPMOST && Wnd != HWND_BROADCAST && (Msg < WM_DDE_FIRST || Msg > WM_DDE_LAST)) { Window = ValidateHwnd(Wnd); if ( Window != NULL && Window->head.pti == ti && !ISITHOOKED(WH_CALLWNDPROC) && !ISITHOOKED(WH_CALLWNDPROCRET) && !(Window->state & WNDS_SERVERSIDEWINDOWPROC) ) { /* NOTE: We can directly send messages to the window procedure if *all* the following conditions are met: * Window belongs to calling thread * The calling thread is not being hooked for CallWndProc * Not calling a server side proc: Desktop, Switch, ScrollBar, Menu, IconTitle, or hWndMessage */ return IntCallMessageProc(Window, Wnd, Msg, wParam, lParam, TRUE); } } AnsiMsg.hwnd = Wnd; AnsiMsg.message = Msg; AnsiMsg.wParam = wParam; AnsiMsg.lParam = lParam; AnsiMsg.time = 0; AnsiMsg.pt.x = 0; AnsiMsg.pt.y = 0; if (!MsgiAnsiToUnicodeMessage(Wnd, &UcMsg, &AnsiMsg)) { return FALSE; } if (!MsgiUMToKMMessage(&UcMsg, &KMMsg, FALSE)) { MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg); return FALSE; } Ret = NtUserMessageCall( Wnd, KMMsg.message, KMMsg.wParam, KMMsg.lParam, (ULONG_PTR)&Result, FNID_SENDMESSAGE, TRUE); if (!Ret) { ERR("SendMessageA Error\n"); } MsgiUMToKMCleanup(&UcMsg, &KMMsg); MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result); return Result; } /* * @implemented */ BOOL WINAPI SendMessageCallbackA( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, SENDASYNCPROC lpCallBack, ULONG_PTR dwData) { BOOL Result; MSG AnsiMsg, UcMsg; CALL_BACK_INFO CallBackInfo; if (is_pointer_message(Msg)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return FALSE; } CallBackInfo.CallBack = lpCallBack; CallBackInfo.Context = dwData; AnsiMsg.hwnd = hWnd; AnsiMsg.message = Msg; AnsiMsg.wParam = wParam; AnsiMsg.lParam = lParam; AnsiMsg.time = 0; AnsiMsg.pt.x = 0; AnsiMsg.pt.y = 0; if (!MsgiAnsiToUnicodeMessage(hWnd, &UcMsg, &AnsiMsg)) { return FALSE; } Result = NtUserMessageCall( hWnd, UcMsg.message, UcMsg.wParam, UcMsg.lParam, (ULONG_PTR)&CallBackInfo, FNID_SENDMESSAGECALLBACK, TRUE); MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg); return Result; } /* * @implemented */ BOOL WINAPI SendMessageCallbackW( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, SENDASYNCPROC lpCallBack, ULONG_PTR dwData) { CALL_BACK_INFO CallBackInfo; if (is_pointer_message(Msg)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return FALSE; } CallBackInfo.CallBack = lpCallBack; CallBackInfo.Context = dwData; return NtUserMessageCall(hWnd, Msg, wParam, lParam, (ULONG_PTR)&CallBackInfo, FNID_SENDMESSAGECALLBACK, FALSE); } /* * @implemented */ LRESULT WINAPI SendMessageTimeoutA( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult) { MSG AnsiMsg, UcMsg; LRESULT Result; DOSENDMESSAGE dsm; if ( Msg & ~WM_MAXIMUM || fuFlags & ~(SMTO_NOTIMEOUTIFNOTHUNG|SMTO_ABORTIFHUNG|SMTO_BLOCK)) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (lpdwResult) *lpdwResult = 0; SPY_EnterMessage(SPY_SENDMESSAGE, hWnd, Msg, wParam, lParam); dsm.uFlags = fuFlags; dsm.uTimeout = uTimeout; dsm.Result = 0; AnsiMsg.hwnd = hWnd; AnsiMsg.message = Msg; AnsiMsg.wParam = wParam; AnsiMsg.lParam = lParam; AnsiMsg.time = 0; AnsiMsg.pt.x = 0; AnsiMsg.pt.y = 0; if (! MsgiAnsiToUnicodeMessage(hWnd, &UcMsg, &AnsiMsg)) { return FALSE; } Result = NtUserMessageCall( hWnd, UcMsg.message, UcMsg.wParam, UcMsg.lParam, (ULONG_PTR)&dsm, FNID_SENDMESSAGEWTOOPTION, TRUE); MsgiAnsiToUnicodeReply(&UcMsg, &AnsiMsg, &Result); if (lpdwResult) *lpdwResult = dsm.Result; SPY_ExitMessage(SPY_RESULT_OK, hWnd, Msg, Result, wParam, lParam); return Result; } /* * @implemented */ LRESULT WINAPI SendMessageTimeoutW( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult) { LRESULT Result; DOSENDMESSAGE dsm; if ( Msg & ~WM_MAXIMUM || fuFlags & ~(SMTO_NOTIMEOUTIFNOTHUNG|SMTO_ABORTIFHUNG|SMTO_BLOCK)) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; } if (lpdwResult) *lpdwResult = 0; SPY_EnterMessage(SPY_SENDMESSAGE, hWnd, Msg, wParam, lParam); dsm.uFlags = fuFlags; dsm.uTimeout = uTimeout; dsm.Result = 0; Result = NtUserMessageCall( hWnd, Msg, wParam, lParam, (ULONG_PTR)&dsm, FNID_SENDMESSAGEWTOOPTION, FALSE); if (lpdwResult) *lpdwResult = dsm.Result; SPY_ExitMessage(SPY_RESULT_OK, hWnd, Msg, Result, wParam, lParam); return Result; } /* * @implemented */ BOOL WINAPI SendNotifyMessageA( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { BOOL Ret; MSG AnsiMsg, UcMsg; if (is_pointer_message(Msg)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return FALSE; } AnsiMsg.hwnd = hWnd; AnsiMsg.message = Msg; AnsiMsg.wParam = wParam; AnsiMsg.lParam = lParam; AnsiMsg.time = 0; AnsiMsg.pt.x = 0; AnsiMsg.pt.y = 0; if (! MsgiAnsiToUnicodeMessage(hWnd, &UcMsg, &AnsiMsg)) { return FALSE; } Ret = SendNotifyMessageW(hWnd, UcMsg.message, UcMsg.wParam, UcMsg.lParam); MsgiAnsiToUnicodeCleanup(&UcMsg, &AnsiMsg); return Ret; } /* * @implemented */ BOOL WINAPI SendNotifyMessageW( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { MSG UMMsg, KMMsg; LRESULT Result; if (is_pointer_message(Msg)) { SetLastError( ERROR_MESSAGE_SYNC_ONLY ); return FALSE; } UMMsg.hwnd = hWnd; UMMsg.message = Msg; UMMsg.wParam = wParam; UMMsg.lParam = lParam; UMMsg.time = 0; UMMsg.pt.x = 0; UMMsg.pt.y = 0; if (! MsgiUMToKMMessage(&UMMsg, &KMMsg, TRUE)) { return FALSE; } Result = NtUserMessageCall( hWnd, KMMsg.message, KMMsg.wParam, KMMsg.lParam, 0, FNID_SENDNOTIFYMESSAGE, FALSE); MsgiUMToKMCleanup(&UMMsg, &KMMsg); return Result; } /* * @implemented */ BOOL WINAPI TranslateMessageEx(CONST MSG *lpMsg, UINT Flags) { switch (lpMsg->message) { case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: return(NtUserTranslateMessage((LPMSG)lpMsg, Flags)); default: if ( lpMsg->message & ~WM_MAXIMUM ) SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } } /* * @implemented */ BOOL WINAPI TranslateMessage(CONST MSG *lpMsg) { BOOL Ret = FALSE; // Ref: msdn ImmGetVirtualKey: // http://msdn.microsoft.com/en-us/library/aa912145.aspx /* if ( (LOWORD(lpMsg->wParam) != VK_PROCESSKEY) || !(Ret = IMM_ImmTranslateMessage( lpMsg->hwnd, lpMsg->message, lpMsg->wParam, lpMsg->lParam)) )*/ { Ret = TranslateMessageEx((LPMSG)lpMsg, 0); } return Ret; } /* * @implemented */ UINT WINAPI RegisterWindowMessageA(LPCSTR lpString) { UNICODE_STRING String; UINT Atom; if (!RtlCreateUnicodeStringFromAsciiz(&String, (PCSZ)lpString)) { return(0); } Atom = NtUserRegisterWindowMessage(&String); RtlFreeUnicodeString(&String); return(Atom); } /* * @implemented */ UINT WINAPI RegisterWindowMessageW(LPCWSTR lpString) { UNICODE_STRING String; RtlInitUnicodeString(&String, lpString); return(NtUserRegisterWindowMessage(&String)); } /* * @implemented */ HWND WINAPI GetCapture(VOID) { return (HWND)NtUserGetThreadState(THREADSTATE_CAPTUREWINDOW); } /* * @implemented */ BOOL WINAPI ReleaseCapture(VOID) { return NtUserxReleaseCapture(); } /* * @implemented */ DWORD WINAPI RealGetQueueStatus(UINT flags) { #define QS_TEMPALLINPUT 255 // ATM, do not support QS_RAWINPUT if (flags & ~(QS_SMRESULT|QS_ALLPOSTMESSAGE|QS_TEMPALLINPUT)) { SetLastError( ERROR_INVALID_FLAGS ); return 0; } return NtUserxGetQueueStatus(flags); } /* * @implemented */ BOOL WINAPI GetInputState(VOID) { PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo; if ((!pcti) || (pcti->fsChangeBits & (QS_KEY|QS_MOUSEBUTTON))) return (BOOL)NtUserGetThreadState(THREADSTATE_GETINPUTSTATE); return FALSE; } NTSTATUS WINAPI User32CallWindowProcFromKernel(PVOID Arguments, ULONG ArgumentLength) { PWINDOWPROC_CALLBACK_ARGUMENTS CallbackArgs; MSG KMMsg, UMMsg; PWND pWnd = NULL; PCLIENTINFO pci = GetWin32ClientInfo(); /* Make sure we don't try to access mem beyond what we were given */ if (ArgumentLength < sizeof(WINDOWPROC_CALLBACK_ARGUMENTS)) { return STATUS_INFO_LENGTH_MISMATCH; } CallbackArgs = (PWINDOWPROC_CALLBACK_ARGUMENTS) Arguments; KMMsg.hwnd = CallbackArgs->Wnd; KMMsg.message = CallbackArgs->Msg; KMMsg.wParam = CallbackArgs->wParam; KMMsg.time = 0; KMMsg.pt.x = 0; KMMsg.pt.y = 0; /* Check if lParam is really a pointer and adjust it if it is */ if (0 <= CallbackArgs->lParamBufferSize) { if (ArgumentLength != sizeof(WINDOWPROC_CALLBACK_ARGUMENTS) + CallbackArgs->lParamBufferSize) { return STATUS_INFO_LENGTH_MISMATCH; } KMMsg.lParam = (LPARAM) ((char *) CallbackArgs + sizeof(WINDOWPROC_CALLBACK_ARGUMENTS)); switch(KMMsg.message) { case WM_CREATE: { TRACE("WM_CREATE CB %p lParam %p\n",CallbackArgs, KMMsg.lParam); break; } case WM_SYSTIMER: { TRACE("WM_SYSTIMER %p\n",KMMsg.hwnd); break; } case WM_SIZING: { PRECT prect = (PRECT) KMMsg.lParam; TRACE("WM_SIZING 1 t %d l %d r %d b %d\n",prect->top,prect->left,prect->right,prect->bottom); break; } default: break; } } else { if (ArgumentLength != sizeof(WINDOWPROC_CALLBACK_ARGUMENTS)) { return STATUS_INFO_LENGTH_MISMATCH; } KMMsg.lParam = CallbackArgs->lParam; } if (WM_NCCALCSIZE == CallbackArgs->Msg && CallbackArgs->wParam) { NCCALCSIZE_PARAMS *Params = (NCCALCSIZE_PARAMS *) KMMsg.lParam; Params->lppos = (PWINDOWPOS) (Params + 1); } if (! MsgiKMToUMMessage(&KMMsg, &UMMsg)) { } if (pci->CallbackWnd.hWnd == UMMsg.hwnd) pWnd = pci->CallbackWnd.pWnd; CallbackArgs->Result = IntCallWindowProcW( CallbackArgs->IsAnsiProc, CallbackArgs->Proc, pWnd, UMMsg.hwnd, UMMsg.message, UMMsg.wParam, UMMsg.lParam); if (! MsgiKMToUMReply(&KMMsg, &UMMsg, &CallbackArgs->Result)) { } if (0 <= CallbackArgs->lParamBufferSize) { switch(KMMsg.message) { case WM_SIZING: { PRECT prect = (PRECT) KMMsg.lParam; TRACE("WM_SIZING 2 t %d l %d r %d b %d\n",prect->top,prect->left,prect->right,prect->bottom); break; } default: break; } } return ZwCallbackReturn(CallbackArgs, ArgumentLength, STATUS_SUCCESS); } /* * @implemented */ BOOL WINAPI SetMessageQueue(int cMessagesMax) { /* Function does nothing on 32 bit windows */ return TRUE; } typedef DWORD (WINAPI * RealGetQueueStatusProc)(UINT flags); typedef DWORD (WINAPI * RealMsgWaitForMultipleObjectsExProc)(DWORD nCount, CONST HANDLE *lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags); typedef BOOL (WINAPI * RealInternalGetMessageProc)(LPMSG,HWND,UINT,UINT,UINT,BOOL); typedef BOOL (WINAPI * RealWaitMessageExProc)(DWORD,UINT); typedef struct _USER_MESSAGE_PUMP_ADDRESSES { DWORD cbSize; RealInternalGetMessageProc NtUserRealInternalGetMessage; RealWaitMessageExProc NtUserRealWaitMessageEx; RealGetQueueStatusProc RealGetQueueStatus; RealMsgWaitForMultipleObjectsExProc RealMsgWaitForMultipleObjectsEx; } USER_MESSAGE_PUMP_ADDRESSES, * PUSER_MESSAGE_PUMP_ADDRESSES; DWORD WINAPI RealMsgWaitForMultipleObjectsEx( DWORD nCount, CONST HANDLE *pHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags); typedef BOOL (WINAPI * MESSAGEPUMPHOOKPROC)(BOOL Unregistering,PUSER_MESSAGE_PUMP_ADDRESSES MessagePumpAddresses); CRITICAL_SECTION gcsMPH; MESSAGEPUMPHOOKPROC gpfnInitMPH; DWORD gcLoadMPH = 0; USER_MESSAGE_PUMP_ADDRESSES gmph = {sizeof(USER_MESSAGE_PUMP_ADDRESSES), NtUserRealInternalGetMessage, NtUserRealWaitMessageEx, RealGetQueueStatus, RealMsgWaitForMultipleObjectsEx }; DWORD gfMessagePumpHook = 0; BOOL WINAPI IsInsideMessagePumpHook() { // FF uses this and polls it when Min/Max PCLIENTTHREADINFO pcti = GetWin32ClientInfo()->pClientThreadInfo; return (gfMessagePumpHook && pcti && (pcti->dwcPumpHook > 0)); } void WINAPI ResetMessagePumpHook(PUSER_MESSAGE_PUMP_ADDRESSES Addresses) { Addresses->cbSize = sizeof(USER_MESSAGE_PUMP_ADDRESSES); Addresses->NtUserRealInternalGetMessage = NtUserRealInternalGetMessage; Addresses->NtUserRealWaitMessageEx = NtUserRealWaitMessageEx; Addresses->RealGetQueueStatus = RealGetQueueStatus; Addresses->RealMsgWaitForMultipleObjectsEx = RealMsgWaitForMultipleObjectsEx; } BOOL WINAPI RegisterMessagePumpHook(MESSAGEPUMPHOOKPROC Hook) { EnterCriticalSection(&gcsMPH); if(!Hook) { SetLastError(ERROR_INVALID_PARAMETER); LeaveCriticalSection(&gcsMPH); return FALSE; } if(!gcLoadMPH) { USER_MESSAGE_PUMP_ADDRESSES Addresses; gpfnInitMPH = Hook; ResetMessagePumpHook(&Addresses); if(!Hook(FALSE, &Addresses) || !Addresses.cbSize) { LeaveCriticalSection(&gcsMPH); return FALSE; } memcpy(&gmph, &Addresses, Addresses.cbSize); } else { if(gpfnInitMPH != Hook) { LeaveCriticalSection(&gcsMPH); return FALSE; } } if(NtUserxInitMessagePump()) { LeaveCriticalSection(&gcsMPH); return FALSE; } if (!gcLoadMPH++) { InterlockedExchange((PLONG)&gfMessagePumpHook, 1); } LeaveCriticalSection(&gcsMPH); return TRUE; } BOOL WINAPI UnregisterMessagePumpHook(VOID) { EnterCriticalSection(&gcsMPH); if(gcLoadMPH > 0) { if(NtUserxUnInitMessagePump()) { gcLoadMPH--; if(!gcLoadMPH) { InterlockedExchange((PLONG)&gfMessagePumpHook, 0); gpfnInitMPH(TRUE, NULL); ResetMessagePumpHook(&gmph); gpfnInitMPH = 0; } LeaveCriticalSection(&gcsMPH); return TRUE; } } LeaveCriticalSection(&gcsMPH); return FALSE; } DWORD WINAPI GetQueueStatus(UINT flags) { return IsInsideMessagePumpHook() ? gmph.RealGetQueueStatus(flags) : RealGetQueueStatus(flags); } /** * @name RealMsgWaitForMultipleObjectsEx * * Wait either for either message arrival or for one of the passed events * to be signalled. * * @param nCount * Number of handles in the pHandles array. * @param pHandles * Handles of events to wait for. * @param dwMilliseconds * Timeout interval. * @param dwWakeMask * Mask specifying on which message events we should wakeup. * @param dwFlags * Wait type (see MWMO_* constants). * * @implemented */ DWORD WINAPI RealMsgWaitForMultipleObjectsEx( DWORD nCount, const HANDLE *pHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags) { LPHANDLE RealHandles; HANDLE MessageQueueHandle; DWORD Result; PCLIENTINFO pci; PCLIENTTHREADINFO pcti; if (dwFlags & ~(MWMO_WAITALL | MWMO_ALERTABLE | MWMO_INPUTAVAILABLE)) { SetLastError(ERROR_INVALID_PARAMETER); return WAIT_FAILED; } pci = GetWin32ClientInfo(); if (!pci) return WAIT_FAILED; pcti = pci->pClientThreadInfo; if (pcti && ( !nCount || !(dwFlags & MWMO_WAITALL) )) { if ( (pcti->fsChangeBits & LOWORD(dwWakeMask)) || ( (dwFlags & MWMO_INPUTAVAILABLE) && (pcti->fsWakeBits & LOWORD(dwWakeMask)) ) ) { //FIXME("Return Chg 0x%x Wake 0x%x Mask 0x%x nCnt %d\n",pcti->fsChangeBits, pcti->fsWakeBits, dwWakeMask, nCount); return nCount; } } MessageQueueHandle = NtUserxMsqSetWakeMask(MAKELONG(dwWakeMask, dwFlags)); if (MessageQueueHandle == NULL) { SetLastError(0); /* ? */ return WAIT_FAILED; } RealHandles = HeapAlloc(GetProcessHeap(), 0, (nCount + 1) * sizeof(HANDLE)); if (RealHandles == NULL) { NtUserxMsqClearWakeMask(); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return WAIT_FAILED; } RtlCopyMemory(RealHandles, pHandles, nCount * sizeof(HANDLE)); RealHandles[nCount] = MessageQueueHandle; //FIXME("1 Chg 0x%x Wake 0x%x Mask 0x%x nCnt %d\n",pcti->fsChangeBits, pcti->fsWakeBits, dwWakeMask, nCount); Result = WaitForMultipleObjectsEx( nCount + 1, RealHandles, dwFlags & MWMO_WAITALL, dwMilliseconds, dwFlags & MWMO_ALERTABLE ); //FIXME("2 Chg 0x%x Wake 0x%x Mask 0x%x nCnt %d\n",pcti->fsChangeBits, pcti->fsWakeBits, dwWakeMask, nCount); HeapFree(GetProcessHeap(), 0, RealHandles); NtUserxMsqClearWakeMask(); // wine hack! MSDN: If dwMilliseconds is zero,,specified objects are not signaled; it always returns immediately. if (!Result && !nCount && !dwMilliseconds) Result = WAIT_TIMEOUT; //FIXME("Result 0X%x\n",Result); return Result; } /* * @implemented */ DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD nCount, CONST HANDLE *lpHandles, DWORD dwMilliseconds, DWORD dwWakeMask, DWORD dwFlags) { return IsInsideMessagePumpHook() ? gmph.RealMsgWaitForMultipleObjectsEx(nCount, lpHandles, dwMilliseconds, dwWakeMask, dwFlags) : RealMsgWaitForMultipleObjectsEx(nCount, lpHandles,dwMilliseconds, dwWakeMask, dwFlags); } /* * @implemented */ DWORD WINAPI MsgWaitForMultipleObjects( DWORD nCount, CONST HANDLE *lpHandles, BOOL fWaitAll, DWORD dwMilliseconds, DWORD dwWakeMask) { return MsgWaitForMultipleObjectsEx(nCount, lpHandles, dwMilliseconds, dwWakeMask, fWaitAll ? MWMO_WAITALL : 0); } BOOL FASTCALL MessageInit(VOID) { InitializeCriticalSection(&DdeCrst); InitializeCriticalSection(&gcsMPH); return TRUE; } VOID FASTCALL MessageCleanup(VOID) { DeleteCriticalSection(&DdeCrst); DeleteCriticalSection(&gcsMPH); } /* * @implemented */ BOOL WINAPI IsDialogMessageA( HWND hwndDlg, LPMSG pmsg ) { MSG msg = *pmsg; msg.wParam = map_wparam_AtoW( msg.message, msg.wParam ); return IsDialogMessageW( hwndDlg, &msg ); } LONG WINAPI IntBroadcastSystemMessage( DWORD dwflags, LPDWORD lpdwRecipients, UINT uiMessage, WPARAM wParam, LPARAM lParam, PBSMINFO pBSMInfo, BOOL Ansi) { BROADCASTPARM parm; DWORD recips = BSM_ALLCOMPONENTS; BOOL ret = -1; // Set to return fail static const DWORD all_flags = ( BSF_QUERY | BSF_IGNORECURRENTTASK | BSF_FLUSHDISK | BSF_NOHANG | BSF_POSTMESSAGE | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG | BSF_ALLOWSFW | BSF_SENDNOTIFYMESSAGE | BSF_RETURNHDESK | BSF_LUID ); if ((dwflags & ~all_flags) || (!pBSMInfo && (dwflags & (BSF_RETURNHDESK|BSF_LUID))) ) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if(uiMessage >= WM_USER && uiMessage < 0xC000) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if (dwflags & BSF_FORCEIFHUNG) dwflags |= BSF_NOHANG; if (dwflags & BSF_QUERY) dwflags &= ~BSF_SENDNOTIFYMESSAGE|BSF_POSTMESSAGE; if (!lpdwRecipients) lpdwRecipients = &recips; if (*lpdwRecipients & ~(BSM_APPLICATIONS|BSM_ALLDESKTOPS|BSM_INSTALLABLEDRIVERS|BSM_NETDRIVER|BSM_VXDS)) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if ( pBSMInfo && (dwflags & BSF_QUERY) ) { if (pBSMInfo->cbSize != sizeof(BSMINFO)) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } } parm.hDesk = NULL; parm.hWnd = NULL; parm.flags = dwflags; parm.recipients = *lpdwRecipients; if (dwflags & BSF_LUID) parm.luid = pBSMInfo->luid; ret = NtUserMessageCall(GetDesktopWindow(), uiMessage, wParam, lParam, (ULONG_PTR)&parm, FNID_BROADCASTSYSTEMMESSAGE, Ansi); if (!ret) { if ( pBSMInfo && (dwflags & BSF_QUERY) ) { pBSMInfo->hdesk = parm.hDesk; pBSMInfo->hwnd = parm.hWnd; } } return ret; } /* * @implemented */ LONG WINAPI BroadcastSystemMessageA( DWORD dwFlags, LPDWORD lpdwRecipients, UINT uiMessage, WPARAM wParam, LPARAM lParam) { return IntBroadcastSystemMessage( dwFlags, lpdwRecipients, uiMessage, wParam, lParam, NULL, TRUE ); } /* * @implemented */ LONG WINAPI BroadcastSystemMessageW( DWORD dwFlags, LPDWORD lpdwRecipients, UINT uiMessage, WPARAM wParam, LPARAM lParam) { return IntBroadcastSystemMessage( dwFlags, lpdwRecipients, uiMessage, wParam, lParam, NULL, FALSE ); } /* * @implemented */ LONG WINAPI BroadcastSystemMessageExA( DWORD dwflags, LPDWORD lpdwRecipients, UINT uiMessage, WPARAM wParam, LPARAM lParam, PBSMINFO pBSMInfo) { return IntBroadcastSystemMessage( dwflags, lpdwRecipients, uiMessage, wParam, lParam , pBSMInfo, TRUE ); } /* * @implemented */ LONG WINAPI BroadcastSystemMessageExW( DWORD dwflags, LPDWORD lpdwRecipients, UINT uiMessage, WPARAM wParam, LPARAM lParam, PBSMINFO pBSMInfo) { return IntBroadcastSystemMessage( dwflags, lpdwRecipients, uiMessage, wParam, lParam , pBSMInfo, FALSE ); }