/*++ Copyright (c) 2000-2001, Microsoft Corporation All rights reserved. Module Name: usermsgs.c APIs found in this file: GodotDoCallback GodotTransmitMessage GodotReceiveMessage GodotDispatchMessage Helper functions MapCatureMessage TransmitHelper This function does not currently handle ANSI caller to a UNICODE window. All other calls are handled properly. CONSIDER: To fully implement the capture window stuff, we would need to support these callback functions: capErrorCallback capStatusCallback Revision History: 6 Feb 2001 v-michka Created. --*/ #include "precomp.h" // Internal MFC messages #define WM_SETMESSAGESTRING 0x0362 // wParam = nIDS (or 0), // lParam = lpszOther (or NULL) // Must dynamically link to "BroadcastSystemMessage" because it // does not exist as "BroadcastSystemMessageA" on all platforms. typedef BOOL (__stdcall *PFNbsma) (DWORD, LPDWORD, UINT, WPARAM, LPARAM); static PFNbsma s_pfnBSMA; // forward declares LRESULT TransmitHelper(MESSAGETYPES mt, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, WNDPROC lpPrevWndFunc, SENDASYNCPROC lpCallBack, ULONG_PTR dwData, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult); /*------------------------------- MapCaptureMessage Simple function that maps one message type to another (A->W and W->A) for video captures -------------------------------*/ UINT MapCaptureMessage(UINT uMsg) { if(uMsg >= WM_CAP_UNICODE_START) return(uMsg - WM_CAP_UNICODE_START); else return(uMsg + WM_CAP_UNICODE_START); } /*------------------------------- GodotDoCallback Our global wrapper around callback functions; all callbacks that need random conversions done go through this proc. Note that all callers will be Unicode windows so we do not have to check for this here. -------------------------------*/ LRESULT GodotDoCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn, BOOL fUniSrc, FAUXPROCTYPE fpt) { LRESULT RetVal = 0; BOOL fUniDst; fUniDst = (! DoesProcExpectAnsi(hWnd, lpfn, fpt)); if(!fUniDst && !fUniSrc) { // Call through via CallWindowProcA with no conversion // Note that this is the only place in this entire function where we use // CallWindowProc, since either the user or the system is expecting ANSI. // Note that on Win9x, the base wndproc can be a thunked function sitting // in USER.EXE; only CallWindowProcA can manage that sort of detail. return(CallWindowProcA(lpfn, hWnd, uMsg, wParam, lParam)); } else if(fUniDst && fUniSrc) { // Pure Unicode: Call directly, no conversion needed // Note that we do not currently use this!!! return((* lpfn)(hWnd, uMsg, wParam, lParam)); } else if(!fUniDst && fUniSrc) { // We need to convert from Unicode to ANSI, so use // our own GodotTransmitMessage to do the work. return(GodotTransmitMessage(mtCallWindowProc, hWnd, uMsg, wParam, lParam, lpfn, 0, 0, 0, 0, 0)); } else // (fUniDst && !fUniSrc) { switch(uMsg) { case WM_CHAR: case EM_SETPASSWORDCHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: if(FDBCS_CPG(g_acp)) { WPARAM wParamW = 0; static CHAR s_ch[3] = "\0"; // We have to go through all this nonsense because DBCS characters // arrive one byte at a time. Most of this code is never used because // DBCS chars OUGHT to be handled by WM_IME_CHAR below. if(!s_ch[0]) { // No lead byte already waiting for trail byte s_ch[0] = *(char *)wParam; if(IsDBCSLeadByteEx(g_acp, *(char *)wParam)) { // This is a lead byte. Save it and wait for trail byte RetVal = FALSE; } // Not a DBCS character. Convert to Unicode. MultiByteToWideChar(g_acp, 0, s_ch, 1, (WCHAR *)&wParamW, 1); // Reset to indicate no Lead byte waiting s_ch[0] = 0 ; RetVal = TRUE; } else { // Have lead byte, wParam should contain the trail byte s_ch[1] = *(char *)wParam; // Convert both bytes into one Unicode character MultiByteToWideChar(g_acp, 0, s_ch, 2, (WCHAR *)&wParamW, 1); // Reset to non-waiting state s_ch[0] = 0; RetVal = TRUE; } break; } // Not a DBCS system, so fall through here case WM_IME_CHAR: case WM_IME_COMPOSITION: { WPARAM wParamW = 0; MultiByteToWideChar(g_acp, 0, (CHAR *)&wParam, g_mcs, (WCHAR *)&wParamW, 1); RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParam); WideCharToMultiByte(g_acp, 0, (WCHAR *)&wParamW, 1, (CHAR *)&wParam, g_mcs, NULL, NULL); break; } case WM_CHARTOITEM: { // Mask off the hiword bits, convert, then stick the hiword bits back on. WPARAM wParamW = 0; WPARAM wpT = wParam & 0xFFFF; MultiByteToWideChar(g_acp, 0, (CHAR *)&wpT, g_mcs, (WCHAR *)&wParamW, 1); RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParam); WideCharToMultiByte(g_acp, 0, (WCHAR *)&wParamW, 1, (CHAR *)&wpT, g_mcs, NULL, NULL); wParam = MAKELONG(LOWORD(wpT),HIWORD(wParam)); break; } case (WM_USER + 25): // might be WM_CAP_FILE_SAVEDIBA case (WM_USER + 23): // might be WM_CAP_FILE_SAVEASA case (WM_USER + 66): // might be WM_CAP_SET_MCI_DEVICEA case (WM_USER + 80): // might be WM_CAP_PAL_OPENA case (WM_USER + 81): // might be WM_CAP_PAL_SAVEA case (WM_USER + 20): // might be WM_CAP_FILE_SET_CAPTURE_FILEA if(!IsCaptureWindow(hWnd)) { // The numbers are right, but its not a capture window, so // do not convert. Instead, just pass as is. RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } else { // No memory? If the alloc fails, we eat the results. LPARAM lParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, MapCaptureMessage(uMsg), wParam, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); } break; case CB_ADDSTRING: case CB_DIR: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_INSERTSTRING: case CB_SELECTSTRING: { LONG styl = GetWindowLongA(hWnd, GWL_STYLE); if(((styl & CBS_OWNERDRAWFIXED) || (styl & CBS_OWNERDRAWVARIABLE)) && (!(styl & CBS_HASSTRINGS))) { // Owner draw combo box which does not have strings stored // (See Windows Bugs # 356304 for details here) RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } else { // No memory? If the alloc fails, we eat the results. LPARAM lParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); } } break; case LB_ADDSTRING: case LB_ADDFILE: case LB_DIR: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_INSERTSTRING: case LB_SELECTSTRING: { LONG styl = GetWindowLongA(hWnd, GWL_STYLE); if(((styl & LBS_OWNERDRAWFIXED) || (styl & LBS_OWNERDRAWVARIABLE)) && (!(styl & LBS_HASSTRINGS))) { // Owner draw listbox which does not have strings stored // (See Windows Bugs # 356304 for details here) RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } else { // No memory? If the alloc fails, we eat the results. LPARAM lParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); } break; } case EM_REPLACESEL: case WM_SETTEXT: case WM_DEVMODECHANGE: case WM_SETTINGCHANGE: case WM_SETMESSAGESTRING: // MFC internal msg { // No memory? If the alloc fails, we eat the results. LPARAM lParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); break; } case WM_DDE_EXECUTE: // wParam is the client window hWnd, lParam is the command LPTSTR. // Only convert lParam if both client and server windows are Unicode if(GetUnicodeWindowProp((HWND)hWnd) && GetUnicodeWindowProp((HWND)wParam)) { // No memory? If the alloc fails, we eat the results. LPARAM lParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); } else RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); break; case EM_GETLINE: { // lParam is a pointer to the buffer that receives a copy of the line. Before // sending the message, set the first word of this buffer to the size, in TCHARs, // of the buffer. For ANSI text, this is the number of bytes; for Unicode text, // this is the numer of characters. The size in the first word is overwritten by // the copied line. size_t cchlParam = (WORD)lParam + 1; LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(cchlParam * sizeof(WCHAR)); if(RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW)) { RetVal = WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, RetVal + 1, (LPSTR)lParam, cchlParam, NULL, NULL); if(RetVal) RetVal--; } else { if((LPSTR)lParam) *((LPSTR)lParam) = '\0'; } if(lParamW) GodotHeapFree((LPWSTR)lParamW); } case LB_GETTEXT: { // lParam is a pointer to the buffer that will receive the string; it is type // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient // space for the string and a terminating null character. An LB_GETTEXTLEN message // can be sent before the LB_GETTEXT message to retrieve the length, in TCHARs, of // the string. size_t cchlParam = SendMessageA(hWnd, LB_GETTEXTLEN, wParam, 0) + 1; LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(cchlParam * sizeof(WCHAR)); if(RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW)) { RetVal = WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, RetVal + 1, (LPSTR)lParam, cchlParam, NULL, NULL); if(RetVal) RetVal--; } else { if((LPSTR)lParam) *((LPSTR)lParam) = '\0'; } if(lParamW) GodotHeapFree((LPWSTR)lParamW); break; } case CB_GETLBTEXT: { // lParam is a pointer to the buffer that will receive the string; it is type // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient // space for the string and a terminating null character. An CB_GETLBTEXTLEN message // can be sent before the CB_GETLBTEXT message to retrieve the length, in TCHARs, of // the string. size_t cchlParam = SendMessageA(hWnd, CB_GETLBTEXTLEN, wParam, 0) + 1; LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(cchlParam * sizeof(WCHAR)); if((RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW)) && lParamW) { RetVal = WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, RetVal + 1, (LPSTR)lParam, cchlParam, NULL, NULL); if(RetVal) RetVal--; } else { if((LPSTR)lParam) *((LPSTR)lParam) = '\0'; } if(lParamW) GodotHeapFree((LPWSTR)lParamW); break; } case (WM_USER + 67): // might be WM_CAP_GET_MCI_DEVICEA case (WM_USER + 12): // might be WM_CAP_DRIVER_GET_NAMEA case (WM_USER + 13): // might be WM_CAP_DRIVER_GET_VERSIONA case (WM_USER + 21): // might be WM_CAP_FILE_GET_CAPTURE_FILEA if(!IsCaptureWindow(hWnd)) { // The numbers are right, but its not a capture window, so // do not convert. Instead, just pass as is. RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); break; } // If we are still here, then it is a capture message. // So lets map it and fall through. uMsg = MapCaptureMessage(uMsg); case WM_GETTEXT: case WM_ASKCBFORMATNAME: { // wParam specifies the buffer size of the string lParam (includes the terminating null). LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc((UINT)wParam * sizeof(WCHAR)); if(RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW)) { RetVal = WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, RetVal + 1, (LPSTR)lParam, (UINT)wParam, NULL, NULL); if(RetVal) RetVal--; } else { if((LPSTR)lParam) *((LPSTR)lParam) = '\0'; } if(lParamW) GodotHeapFree((LPWSTR)lParamW); break; } case (WM_USER + 1): if(IsFontDialog(hWnd)) { // This is a WM_CHOOSEFONT_GETLOGFONT msg LPARAM lParamW = (LPARAM)(LPLOGFONTW)GodotHeapAlloc(sizeof(LOGFONTW)); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); LogFontAfromW((LPLOGFONTA)lParam, (LPLOGFONTW)lParamW); if(lParamW) GodotHeapFree((LPWSTR)lParamW); } else { // This would be one of the common control msgs we do not handle RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); break; } break; case (WM_USER + 100): if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_GETSPEC msg LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(wParam * sizeof(WCHAR)); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, wParam, (LPSTR)lParam, wParam, NULL, NULL); RetVal = lstrlenA( (LPSTR)lParam); if(lParamW) GodotHeapFree((LPWSTR)lParamW); } else { RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; case (WM_USER + 101): if(IsFontDialog(hWnd)) { // This is a WM_CHOOSEFONT_SETLOGFONT msg LPARAM lParamW = (LPARAM)(LPLOGFONTW)GodotHeapAlloc(sizeof(LOGFONTW)); LogFontWfromA((LPLOGFONTW)lParamW, (LPLOGFONTA)lParam); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); if(lParamW) GodotHeapFree((LPLOGFONTW)lParamW); } else if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_GETFILEPATH msg LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(wParam * sizeof(WCHAR)); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, wParam, (LPSTR)lParam, wParam, NULL, NULL); RetVal = lstrlenA( (LPSTR)lParam); if(lParamW) GodotHeapFree((LPWSTR)lParamW); } else { RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; case (WM_USER + 102): if(IsFontDialog(hWnd)) { // This is a WM_CHOOSEFONT_SETFLAGS msg // The docs claim that lParam has a CHOOSEFONT struct but the code shows // that it only has the Flags in it, so pass it as is RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } else if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_GETFOLDERPATH // lParam is a buffer for the path of the open folder LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(wParam * sizeof(WCHAR)); RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); WideCharToMultiByte(g_acp, 0, (LPWSTR)lParamW, wParam, (LPSTR)lParam, wParam, NULL, NULL); RetVal = lstrlenA( (LPSTR)lParam); if(lParamW) GodotHeapFree((LPWSTR)lParamW); } else { RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; case (WM_USER + 104): if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_SETCONTROLTEXT message // lParam is the control text (wParam is the control ID) // No memory? If the alloc fails, we eat the results. LPARAM lParamW; WPARAM wParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); wParamW = gwcslen((LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); } else { RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; case (WM_USER + 106): if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_SETDEFEXT message // lParam is the extension // No memory? If the alloc fails, we eat the results. LPARAM lParamW; WPARAM wParamW; ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW); wParamW = gwcslen((LPWSTR)lParamW); RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParamW); if(ar == arAlloc) GodotHeapFree((LPWSTR)lParamW); } else { RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; case WM_CREATE: case WM_NCCREATE: { LPCREATESTRUCTA lpcsA = (LPCREATESTRUCTA)lParam; CREATESTRUCTW cs; ALLOCRETURN arName = arNoAlloc; ALLOCRETURN arClass = arNoAlloc; ZeroMemory(&cs, sizeof(CREATESTRUCTW)); cs.lpCreateParams = lpcsA->lpCreateParams; cs.hInstance = lpcsA->hInstance; cs.hMenu = lpcsA->hMenu; cs.hwndParent = lpcsA->hwndParent; cs.cy = lpcsA->cy; cs.cx = lpcsA->cx; cs.y = lpcsA->y; cs.x = lpcsA->x; cs.style = lpcsA->style; cs.dwExStyle = lpcsA->dwExStyle; arName = GodotToUnicodeOnHeap(lpcsA->lpszName, &(LPWSTR)(cs.lpszName)); arClass = GodotToUnicodeOnHeap(lpcsA->lpszClass, &(LPWSTR)(cs.lpszClass)); RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&cs); // Free up strings if we allocated any if(arName==arAlloc) GodotHeapFree((LPWSTR)(cs.lpszName)); if(arClass==arAlloc) GodotHeapFree((LPWSTR)(cs.lpszClass)); break; } case WM_MDICREATE: { // wParam is not used, lParam is a pointer to an MDICREATESTRUCT structure containing // information that the system uses to create the MDI child window. LPMDICREATESTRUCTA lpmcsiA = (LPMDICREATESTRUCTA)lParam; MDICREATESTRUCTW mcsi; ALLOCRETURN arTitle = arNoAlloc; ALLOCRETURN arClass = arNoAlloc; ZeroMemory(&mcsi, sizeof(MDICREATESTRUCTW)); mcsi.hOwner = lpmcsiA->hOwner; mcsi.x = lpmcsiA->x; mcsi.y = lpmcsiA->y; mcsi.cx = lpmcsiA->cx; mcsi.cy = lpmcsiA->cy; mcsi.style = lpmcsiA->style; mcsi.lParam = lpmcsiA->lParam; arTitle = GodotToUnicodeOnHeap(lpmcsiA->szTitle, &(LPWSTR)(mcsi.szTitle)); arClass = GodotToUnicodeOnHeap(lpmcsiA->szClass, &(LPWSTR)(mcsi.szClass)); RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&mcsi); // Free up strings if we allocated any if(arTitle==arAlloc) GodotHeapFree((LPWSTR)(mcsi.szTitle)); if(arClass==arAlloc) GodotHeapFree((LPWSTR)(mcsi.szClass)); break; } case WM_DEVICECHANGE: { switch(wParam) { case DBT_CUSTOMEVENT: case DBT_DEVICEARRIVAL: case DBT_DEVICEQUERYREMOVE: case DBT_DEVICEQUERYREMOVEFAILED: case DBT_DEVICEREMOVECOMPLETE: case DBT_DEVICEREMOVEPENDING: case DBT_DEVICETYPESPECIFIC: { // lParam contains info about the device. We interrogate it as if it were // a PDEV_BROADCAST_HDR in order to find out what it really is, then convert // as needed if (((PDEV_BROADCAST_HDR)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { PDEV_BROADCAST_DEVICEINTERFACE_A pdbdia = (PDEV_BROADCAST_DEVICEINTERFACE_A)lParam; DEV_BROADCAST_DEVICEINTERFACE_W dbdi; ALLOCRETURN arName = arNoAlloc; ZeroMemory(&dbdi, sizeof(DEV_BROADCAST_DEVICEINTERFACE_W)); dbdi.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W); dbdi.dbcc_devicetype = pdbdia->dbcc_devicetype; dbdi.dbcc_reserved = pdbdia->dbcc_reserved; dbdi.dbcc_classguid = pdbdia->dbcc_classguid; arName = GodotToUnicodeOnHeap(pdbdia->dbcc_name, *(LPWSTR**)(dbdi.dbcc_name)); RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&dbdi); if(arName==arAlloc) GodotHeapFree(dbdi.dbcc_name); } else if(((PDEV_BROADCAST_HDR)lParam)->dbch_devicetype == DBT_DEVTYP_PORT) { PDEV_BROADCAST_PORT_A pdbpa = (PDEV_BROADCAST_PORT_A)lParam; DEV_BROADCAST_PORT_W dbp; ALLOCRETURN arName = arNoAlloc; ZeroMemory(&dbp, sizeof(DEV_BROADCAST_PORT_W)); dbp.dbcp_size = sizeof(DEV_BROADCAST_PORT_W); dbp.dbcp_devicetype = pdbpa->dbcp_devicetype; dbp.dbcp_reserved = pdbpa->dbcp_reserved; arName = GodotToUnicodeOnHeap(pdbpa->dbcp_name, *(LPWSTR**)(dbp.dbcp_name)); RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&dbp); if(arName==arAlloc) GodotHeapFree(dbp.dbcp_name); } else { // No changes needed! There are no strings in the other structures. RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; } case DBT_USERDEFINED: // No UNICODE string in this one, so fall through default: RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); break; } break; } default: { // Lets get our registered messages, if we haven't yet. if(!msgHELPMSGSTRING) msgHELPMSGSTRING = RegisterWindowMessage(HELPMSGSTRINGA); if(!msgFINDMSGSTRING) msgFINDMSGSTRING = RegisterWindowMessage(FINDMSGSTRINGA); if((uMsg == msgHELPMSGSTRING) && (((LPOPENFILENAMEA)lParam)->lStructSize == OPENFILENAME_SIZE_VERSION_400A)) { WCHAR drive[_MAX_DRIVE]; WCHAR dir[_MAX_DIR]; WCHAR file[_MAX_FNAME]; LPOPENFILENAMEA lpofnA = (LPOPENFILENAMEA)lParam; OPENFILENAMEW ofn; ALLOCRETURN arCustomFilter = arNoAlloc; ALLOCRETURN arFile = arNoAlloc; ALLOCRETURN arFileTitle = arNoAlloc; // lParam is an LPOPENFILENAMEA to be converted. Copy all the // members that the user might expect. ZeroMemory(&ofn, OPENFILENAME_SIZE_VERSION_400W); ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400W; arCustomFilter = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter, lpofnA->nMaxCustFilter, &ofn.lpstrCustomFilter, g_acp); ofn.nMaxCustFilter = gwcslen(ofn.lpstrCustomFilter); ofn.nFilterIndex = lpofnA->nFilterIndex; ofn.nMaxFile = lpofnA->nMaxFile * sizeof(WCHAR); arFile = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile, lpofnA->nMaxFile, &ofn.lpstrFile, g_acp); ofn.nMaxFile = gwcslen(ofn.lpstrFile); arFileTitle = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle, lpofnA->nMaxFileTitle, &ofn.lpstrFileTitle, g_acp); ofn.nMaxFileTitle = gwcslen(ofn.lpstrFileTitle); ofn.Flags = lpofnA->Flags; // nFileOffset and nFileExtension are to provide info about the // file name and extension location in lpstrFile, but there is // no reasonable way to get it from the return so we just recalc gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL); ofn.nFileOffset = (gwcslen(drive) + gwcslen(dir)); ofn.nFileExtension = ofn.nFileOffset + gwcslen(file); RetVal = (*lpfn)(hWnd, uMsg, wParam, (LPARAM)&ofn); // Free up some memory if we allocated any if(arCustomFilter==arAlloc) GodotHeapFree(ofn.lpstrCustomFilter); if(arFile==arAlloc) GodotHeapFree(ofn.lpstrFile); if(arFileTitle==arAlloc) GodotHeapFree(ofn.lpstrFileTitle); } else if(((uMsg == msgFINDMSGSTRING) || (uMsg == msgHELPMSGSTRING)) && ((((LPFINDREPLACEW)lParam)->lStructSize) == sizeof(FINDREPLACEA))) { LPFINDREPLACEW lpfr = (LPFINDREPLACEW)lParam; // lParam is an LPFINDREPLACEW that we passed on through. RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); if((lpfr->Flags & FR_DIALOGTERM) && ((lpfr->lpfnHook == &FRHookProcFind) || (lpfr->lpfnHook == &FRHookProcReplace))) { // Now handle our cleanup. I do not think this should // be needed, but it can't hurt to do it just in case. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); // They are destroying the dialog, so unhook ourselves // and clean up the dialog (if we have not done it yet). if(lpfr->lpfnHook == &FRHookProcFind) { // Find dialog, not yet cleaned up lpfr->lpfnHook = lpgti->pfnFindText; if(lpfr->lpfnHook == NULL) lpfr->Flags &= ~FR_ENABLEHOOK; } else if(lpfr->lpfnHook == &FRHookProcReplace) { // Replace dialog, not yet cleaned up lpfr->lpfnHook = lpgti->pfnReplaceText; if(lpfr->lpfnHook == NULL) lpfr->Flags &= ~FR_ENABLEHOOK; } } } else if((uMsg == msgHELPMSGSTRING) && ((LPCHOOSEFONTA)lParam)->lStructSize == sizeof(CHOOSEFONTA)) { LPCHOOSEFONTA lpcfA = (LPCHOOSEFONTA)lParam; CHOOSEFONTW cf; LPARAM lParamW; ALLOCRETURN ar = arNoAlloc; // lParam is an LPCHOOSEFONTA to be converted. Copy all the // members that the user might expect. ZeroMemory(&cf, sizeof(CHOOSEFONTW)); cf.lStructSize = sizeof(CHOOSEFONTW); cf.hDC = lpcfA->hDC; LogFontWfromA(cf.lpLogFont, lpcfA->lpLogFont); cf.iPointSize = lpcfA->iPointSize; cf.Flags = lpcfA->Flags; cf.rgbColors = lpcfA->rgbColors; cf.lCustData = lpcfA->lCustData; cf.nFontType = lpcfA->nFontType; cf.nSizeMin = lpcfA->nSizeMin; cf.nSizeMax = lpcfA->nSizeMax; ar = GodotToUnicodeOnHeap(lpcfA->lpszStyle, &(cf.lpszStyle)); lParamW = (LPARAM)&cf; RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW); if(ar==arAlloc) GodotHeapFree((LPWSTR)cf.lpszStyle); } else { // No translation needed, as far as we know. RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam); } break; } } return(RetVal); } } /*------------------------------- GodotTransmitMessage Our global wrapper for sending out messages (SendMessage, et. al.). Its fundamental purpose: 1) Convert back to Ansi, as expected 2) Call the function as specified by mt 3) Convert to Unicode, as expected -------------------------------*/ LRESULT GodotTransmitMessage( MESSAGETYPES mt, // Type of message function HWND hWnd, // handle to window - overloaded for thread id in PostThreadMessage UINT Msg, // message WPARAM wParamW, // first message parameter LPARAM lParamW, // second message parameter WNDPROC lpPrevWndFunc, // pointer to previous procedure - overloaded for multiple hook procs SENDASYNCPROC lpCallBack, // callback function ULONG_PTR dwData, // application-defined value - overloaded for DefFrameProc as second hWnd UINT fuFlags, // send options -- overloaded for BroadcastSystemMessages as dwFlags UINT uTimeout, // time-out duration PDWORD_PTR lpdwResult // retval for synch. -- overloaded BroadcastSystemMessages lpdwRecipients ) { LRESULT retval = 0; WPARAM wParam = 0; LPARAM lParam = 0; size_t cchlParam; // Some flags we will need for our message handling BOOL fUnicodeProc = (! DoesProcExpectAnsi(hWnd, lpPrevWndFunc, fptUnknown)); /* fUnicodeProc == Does the wndproc being called expect Unicode messages? */ if((!fUnicodeProc) && (mt==mtCallWindowProcA) || (fUnicodeProc) && ((mt==mtCallWindowProc))) { // The wndproc either expects ANSI and the caller has used one of the ANSI // functions or it expects Unicode and they have used CallWindowProcW. In // these cases, we do no conversions here retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } else { switch(Msg) { case WM_CHAR: case EM_SETPASSWORDCHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: // All these messages require the wParamW to be converted from Unicode wParam = 0; WideCharToMultiByte(g_acp, 0, (WCHAR *)&wParamW, 1, (char *)&wParam, g_mcs, NULL, NULL); if(FDBCS_CPG(g_acp)) { if(!wParam) { retval = TransmitHelper(mt, hWnd, Msg, wParam, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); break; } else if(IsDBCSLeadByte(*(char *)(LOBYTE(wParam)))) { // Ok, its a DBCS code page and wParam contains a DBCS character. // we must send two WM_CHAR messages, one with each byte in it char sz[2]; sz[0] = *(char *)LOBYTE(wParam); sz[1] = *(char *)HIBYTE(wParam); retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)&sz[0], lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(retval==0) { // The first byte was handled, so send the second byte retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)&sz[1], lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } MultiByteToWideChar(g_acp, 0, (char *)&sz[0], g_mcs, (WCHAR *)&wParamW, 1); break; } } /* if(FDBCS_CPG(g_acp) && (IsDBCSLeadByte((LOBYTE(wParam))))) { // Ok, its a DBCS code page and wParam contains a DBCS character. // we must send two WM_CHAR messages, one with each byte in it retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)LOBYTE(wParam), lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(retval==0) { // The first byte was handled, so send the second byte retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)HIBYTE(wParam), lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } break; } */ // Not a DBCS code page, or at least not a DBCS character, so we just fall through now. case WM_IME_CHAR: case WM_IME_COMPOSITION: retval = TransmitHelper(mt, hWnd, Msg, wParam, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); MultiByteToWideChar(g_acp, 0, (char *)&wParam, g_mcs, (WCHAR *)&wParamW, 1); break; case WM_CHARTOITEM: { // Mask off the hiword bits, convert, then stick the hiword bits back on. WPARAM wpT = wParamW & 0xFFFF; WideCharToMultiByte(g_acp, 0, (WCHAR *)&wpT, 1, (char *)&wParam, g_mcs, NULL, NULL); retval = TransmitHelper(mt, hWnd, Msg, wParam, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); MultiByteToWideChar(g_acp, 0, (char *)&wParam, g_mcs, (WCHAR *)&wpT, 1); wParamW = MAKELONG(LOWORD(wpT),HIWORD(wParamW)); break; } case (WM_USER + 125): // might be WM_CAP_FILE_SAVEDIBW: case (WM_USER + 123): // might be WM_CAP_FILE_SAVEASW: case (WM_USER + 166): // might be WM_CAP_SET_MCI_DEVICEW: case (WM_USER + 180): // might be WM_CAP_PAL_OPENW: case (WM_USER + 181): // might be WM_CAP_PAL_SAVEW: case (WM_USER + 120): // might be WM_CAP_FILE_SET_CAPTURE_FILEW: if(!IsCaptureWindow(hWnd)) { // The numbers are right, but its not a capture window, so // do not convert. Instead, just pass as is. retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } else { // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); if(ar != arFailed) { Msg = MapCaptureMessage(Msg); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); } break; case CB_ADDSTRING: case CB_DIR: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_INSERTSTRING: case CB_SELECTSTRING: { LONG styl = GetWindowLongA(hWnd, GWL_STYLE); if(((styl & CBS_OWNERDRAWFIXED) || (styl & CBS_OWNERDRAWVARIABLE)) && (!(styl & CBS_HASSTRINGS))) { // Owner draw combo box which does not have strings stored // (See Windows Bugs # 356304 for details here) retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } else { // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); } break; } case LB_ADDFILE: case LB_ADDSTRING: case LB_DIR: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_INSERTSTRING: case LB_SELECTSTRING: { LONG styl = GetWindowLongA(hWnd, GWL_STYLE); if(((styl & LBS_OWNERDRAWFIXED) || (styl & LBS_OWNERDRAWVARIABLE)) && (!(styl & LBS_HASSTRINGS))) { // Owner draw listbox which does not have strings stored // (See Windows Bugs # 356304 for details here) retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } else { // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); } break; } case EM_REPLACESEL: case WM_SETTEXT: case WM_DEVMODECHANGE: case WM_SETTINGCHANGE: case WM_SETMESSAGESTRING: // MFC internal msg { // All these messages require a string in lParam to be converted from Unicode // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); break; } case WM_DDE_EXECUTE: // wParam is the client window hWnd, lParam is the command LPTSTR. // Only convert lParam if both client and server windows are Unicode if(GetUnicodeWindowProp((HWND)wParamW)) { // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); } else retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); break; case EM_GETLINE: // lParam is a pointer to the buffer that receives a copy of the line. Before // sending the message, set the first word of this buffer to the size, in TCHARs, // of the buffer. For ANSI text, this is the number of bytes; for Unicode text, // this is the numer of characters. The size in the first word is overwritten by // the copied line. cchlParam = (WORD)lParamW; lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(retval) { retval = MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, retval + 1, (LPWSTR)lParamW, cchlParam); if(retval) retval--; } else { if((LPWSTR)lParamW) *((LPWSTR)lParamW) = L'\0'; } if(lParam) GodotHeapFree((LPSTR)lParam); break; case LB_GETTEXT: // lParam is a pointer to the buffer that will receive the string; it is type // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient // space for the string and a terminating null character. An LB_GETTEXTLEN message // can be sent before the LB_GETTEXT message to retrieve the length, in TCHARs, of // the string. cchlParam = SendMessageA(hWnd, LB_GETTEXTLEN, wParamW, 0) + 1; lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(retval) { retval = MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, retval + 1, (LPWSTR)lParamW, cchlParam); if(retval) retval--; } else { if((LPWSTR)lParamW) *((LPWSTR)lParamW) = L'\0'; } if(lParam) GodotHeapFree((LPSTR)lParam); break; case LB_GETTEXTLEN: retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(FDBCS_CPG(g_acp)) { // In the DBCS case, LB_GETTEXTLEN returns number of bytes, but // we need to get the number of characters for the Unicode case. LPSTR lpsz = GodotHeapAlloc(retval + 1); if(lpsz) { cchlParam = TransmitHelper(mt, hWnd, LB_GETTEXT, (WPARAM)(retval + 1), (LPARAM)lpsz, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(cchlParam > 0) { LPWSTR lpwz = GodotHeapAlloc((cchlParam + 1) * sizeof(WCHAR)); size_t cch = (cchlParam + 1) * sizeof(WCHAR); retval = MultiByteToWideChar(g_acp, 0, lpsz, cchlParam, lpwz, cch); if(lpwz) GodotHeapFree(lpwz); } GodotHeapFree(lpsz); } } break; case CB_GETLBTEXT: // lParam is a pointer to the buffer that will receive the string; it is type // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient // space for the string and a terminating null character. An CB_GETLBTEXTLEN message // can be sent before the CB_GETLBTEXT message to retrieve the length, in TCHARs, of // the string. cchlParam = SendMessageA(hWnd, CB_GETLBTEXTLEN, wParamW, 0) + 1; lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(retval) { retval = MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, retval + 1, (LPWSTR)lParamW, cchlParam); if(retval) retval--; } else { if((LPWSTR)lParamW) *((LPWSTR)lParamW) = L'\0'; } if(lParam) GodotHeapFree((LPSTR)lParam); break; case CB_GETLBTEXTLEN: retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(FDBCS_CPG(g_acp)) { // In the DBCS case, CB_GETLBTEXTLEN returns number of bytes, but // we need to get the number of characters for the Unicode case. LPSTR lpsz = GodotHeapAlloc(retval + 1); if(lpsz) { cchlParam = TransmitHelper(mt, hWnd, CB_GETLBTEXT, (WPARAM)(retval + 1), (LPARAM)lpsz, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(cchlParam > 0) { LPWSTR lpwz = GodotHeapAlloc((cchlParam + 1) * sizeof(WCHAR)); if(lpwz) { size_t cch = (cchlParam + 1) * sizeof(WCHAR); retval = MultiByteToWideChar(g_acp, 0, lpsz, cchlParam, lpwz, cch); GodotHeapFree(lpwz); } } GodotHeapFree(lpsz); } } break; case (WM_USER + 167): // might be WM_CAP_GET_MCI_DEVICEW case (WM_USER + 112): // might be WM_CAP_DRIVER_GET_NAMEW: case (WM_USER + 113): // might be WM_CAP_DRIVER_GET_VERSIONW: case (WM_USER + 121): // might be WM_CAP_FILE_GET_CAPTURE_FILEW: if(!IsCaptureWindow(hWnd)) { // The numbers are right, but its not a capture window, so // do not convert. Instead, just pass as is. retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); break; } // If we are still here, then it is a capture message. So lets map // it and faill through. Msg = MapCaptureMessage(Msg); case WM_GETTEXT: // wParam specifies the size of the buffer in the string in lParam cchlParam = (size_t)wParamW; lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(retval) { retval = MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, retval + 1, (LPWSTR)lParamW, cchlParam); if(retval) retval--; } else { if((LPWSTR)lParamW) *((LPWSTR)lParamW) = L'\0'; } if(lParam) GodotHeapFree((LPSTR)lParam); break; case WM_GETTEXTLENGTH: retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(FDBCS_CPG(g_acp)) { // In the DBCS case, WM_GETTEXTLENGTH returns number of bytes, but // we need to get the number of characters for the Unicode case. // If any of the allocs fail, we will live with the less than perfect // result we have in hand LPSTR lpsz = GodotHeapAlloc(retval + 1); if(lpsz) { cchlParam = TransmitHelper(mt, hWnd, WM_GETTEXT, (WPARAM)(retval + 1), (LPARAM)lpsz, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(cchlParam > 0) { LPWSTR lpwz = GodotHeapAlloc((cchlParam + 1) * sizeof(WCHAR)); if(lpwz) { size_t cch = (cchlParam + 1) * sizeof(WCHAR); retval = MultiByteToWideChar(g_acp, 0, lpsz, cchlParam, lpwz, cch); GodotHeapFree(lpwz); } } GodotHeapFree(lpsz); } } break; case (WM_USER + 1): if(IsFontDialog(hWnd)) { // This is a WM_CHOOSEFONT_GETLOGFONT msg LOGFONTA lfa; lParam = (LPARAM)&lfa; retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); LogFontWfromA((LPLOGFONTW)lParamW, (LPLOGFONTA)lParam); } else { // This would be one of the common control msgs we do not handle retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); break; } break; case (WM_USER + 100): if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_GETSPEC msg wParam = wParamW * g_mcs; (LPSTR)lParam = (LPSTR)GodotHeapAlloc(wParam); retval = TransmitHelper(mt, hWnd, Msg, wParam, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, wParam, (LPWSTR)lParamW, wParamW); retval = gwcslen((LPWSTR)lParamW); if(lParam) GodotHeapFree((LPSTR)lParam); } else { retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } break; case (WM_USER + 101): if(IsFontDialog(hWnd)) { // This is a WM_CHOOSEFONT_SETLOGFONT msg LOGFONTA lfa; lParam = (LPARAM)&lfa; LogFontAfromW((LPLOGFONTA)lParam, (LPLOGFONTW)lParamW); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } else if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_GETFILEPATH msg wParam = wParamW * g_mcs; (LPSTR)lParam = (LPSTR)GodotHeapAlloc(wParam); retval = TransmitHelper(mt, hWnd, Msg, wParam, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, wParam, (LPWSTR)lParamW, wParamW); retval = gwcslen((LPWSTR)lParamW); if(lParam) GodotHeapFree((LPSTR)lParam); } else { retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } break; case (WM_USER + 102): if(IsFontDialog(hWnd)) { // This is a WM_CHOOSEFONT_SETFLAGS msg // The docs claim that lParam has a CHOOSEFONT struct but the code shows // that it only has the Flags in it retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } else if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_GETFOLDERPATH // lParam is a buffer for the path of the open folder wParam = wParamW * g_mcs; (LPSTR)lParam = (LPSTR)GodotHeapAlloc(wParam); retval = TransmitHelper(mt, hWnd, Msg, wParam, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, wParam, (LPWSTR)lParamW, wParamW); retval = gwcslen((LPWSTR)lParamW); if(lParam) GodotHeapFree((LPSTR)lParam); } else { retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } break; case (WM_USER + 104): if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_SETCONTROLTEXT message // lParam is the control text (wParam is the control ID) // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); } else { retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } break; case (WM_USER + 106): if(IsNewFileOpenDialog(hWnd)) { // This is a CDM_SETDEFEXT message // lParam is the extension // No memory? If the alloc fails, we eat the results. ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam); retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar == arAlloc) GodotHeapFree((LPSTR)lParam); } else { retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); } break; case EM_GETPASSWORDCHAR: { // All these messages require that the (single char) retval be converted to Unicode // CONSIDER: is this always single character? LRESULT retvalA = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); MultiByteToWideChar(g_acp, 0, (char *)&retvalA, g_mcs, (WCHAR *)&retval, sizeof(WCHAR)); break; } case WM_CREATE: case WM_NCCREATE: { LPCREATESTRUCTW lpcs = (LPCREATESTRUCTW)lParamW; CREATESTRUCTA csA; ALLOCRETURN arClass = arNoAlloc; ALLOCRETURN arName = arNoAlloc; ZeroMemory(&csA, sizeof(CREATESTRUCTA)); csA.lpCreateParams = lpcs->lpCreateParams; csA.hInstance = lpcs->hInstance; csA.hMenu = lpcs->hMenu; csA.hwndParent = lpcs->hwndParent; csA.cy = lpcs->cy; csA.cx = lpcs->cx; csA.y = lpcs->y; csA.x = lpcs->x; csA.style = lpcs->style; csA.dwExStyle = lpcs->dwExStyle; arClass = GodotToAcpOnHeap(lpcs->lpszClass, &(LPSTR)(csA.lpszClass)); arName = GodotToAcpOnHeap(lpcs->lpszName, &(LPSTR)(csA.lpszName)); lParam = (LPARAM)&csA; retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(arClass==arAlloc) GodotHeapFree((LPSTR)csA.lpszClass); if(arName==arAlloc) GodotHeapFree((LPSTR)csA.lpszName); break; } case WM_MDICREATE: // wParam is not used, lParam is a pointer to an MDICREATESTRUCT structure containing // information that the system uses to create the MDI child window. { LPMDICREATESTRUCTW lpmcsi = (LPMDICREATESTRUCTW)lParamW; MDICREATESTRUCTA mcsiA; ALLOCRETURN arClass = arNoAlloc; ALLOCRETURN arTitle = arNoAlloc; ZeroMemory(&mcsiA, sizeof(MDICREATESTRUCTA)); mcsiA.hOwner = lpmcsi->hOwner; mcsiA.x = lpmcsi->x; mcsiA.y = lpmcsi->y; mcsiA.cx = lpmcsi->cx; mcsiA.cy = lpmcsi->cy; mcsiA.style = lpmcsi->style; mcsiA.lParam = lpmcsi->lParam; arClass = GodotToAcpOnHeap(lpmcsi->szClass, &(LPSTR)(mcsiA.szClass)); arTitle = GodotToAcpOnHeap(lpmcsi->szTitle, &(LPSTR)(mcsiA.szTitle)); retval = TransmitHelper(mt, hWnd, Msg, wParamW, (LPARAM)&mcsiA, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(arClass==arAlloc) GodotHeapFree((LPSTR)mcsiA.szClass); if(arTitle==arAlloc) GodotHeapFree((LPSTR)mcsiA.szTitle); } break; default: // Lets get our registered messages, if we haven't yet. if(!msgHELPMSGSTRING) msgHELPMSGSTRING = RegisterWindowMessage(HELPMSGSTRINGA); if(!msgFINDMSGSTRING) msgFINDMSGSTRING = RegisterWindowMessage(FINDMSGSTRINGA); if(((Msg == msgFINDMSGSTRING) || (Msg == msgHELPMSGSTRING)) && ((((LPFINDREPLACEW)lParamW)->lStructSize) == sizeof(FINDREPLACEA))) { LPFINDREPLACEW lpfr; // No translation needed retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); lpfr = (LPFINDREPLACEW)lParamW; if((lpfr->Flags & FR_DIALOGTERM) && ((lpfr->lpfnHook == &FRHookProcFind) || (lpfr->lpfnHook == &FRHookProcReplace))) { // Now handle our cleanup. I do not think this should // be needed, but it can't hurt to do it just in case. LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); // They are destroying the dialog, so unhook ourselves // and clean up the dialog (if we have not done it yet). if(lpfr->lpfnHook == &FRHookProcFind) { // Find dialog, not yet cleaned up lpfr->lpfnHook = lpgti->pfnFindText; if(lpfr->lpfnHook == NULL) lpfr->Flags &= ~FR_ENABLEHOOK; } else if(lpfr->lpfnHook == &FRHookProcReplace) { // Replace dialog, not yet cleaned up lpfr->lpfnHook = lpgti->pfnReplaceText; if(lpfr->lpfnHook == NULL) lpfr->Flags &= ~FR_ENABLEHOOK; } } } else if((Msg == msgHELPMSGSTRING) && ((LPCHOOSEFONTA)lParamW)->lStructSize == sizeof(CHOOSEFONTA)) { LPCHOOSEFONTW lpcf = (LPCHOOSEFONTW)lParamW; CHOOSEFONTA cfA; ALLOCRETURN ar = arNoAlloc; // lParam is an LPCHOOSEFONTW to be converted. Copy all the // members that the user might expect. ZeroMemory(&cfA, sizeof(CHOOSEFONTA)); cfA.lStructSize = sizeof(CHOOSEFONTA); cfA.hDC = lpcf->hDC; LogFontAfromW(cfA.lpLogFont, lpcf->lpLogFont); cfA.iPointSize = lpcf->iPointSize; cfA.Flags = lpcf->Flags; cfA.rgbColors = lpcf->rgbColors; cfA.lCustData = lpcf->lCustData; ar = GodotToAcpOnHeap(lpcf->lpszStyle, &(cfA.lpszStyle)); cfA.nFontType = lpcf->nFontType; cfA.nSizeMin = lpcf->nSizeMin; cfA.nSizeMax = lpcf->nSizeMax; retval = TransmitHelper(mt, hWnd, Msg, wParamW, (LPARAM)&cfA, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); if(ar==arAlloc) GodotHeapFree(cfA.lpszStyle); break; } else { // No translation needed retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, lpCallBack, dwData, fuFlags, uTimeout, lpdwResult); break; } } } return (retval); } /*------------------------------- TransmitHelper Our helper function that handles the proper one of the twelve functions that call GodotTransmitMessage -------------------------------*/ LRESULT TransmitHelper( MESSAGETYPES mt, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, WNDPROC lpPrevWndFunc, SENDASYNCPROC lpCallBack, ULONG_PTR dwData, UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult ) { LRESULT RetVal; switch(mt) { case mtSendMessage: { // We have to special case the WM_MDICREATE case, because it creates a window and we // thus have to do the hook, etc. Note that currently it is only done in SendMessage, // not in any other call -- this matches the docs, and we cannot risk hitting a // recursive situation here. if(Msg==WM_MDICREATE) { LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE); if(lpgti) INIT_WINDOW_SNIFF(lpgti->hHook); RetVal=SendMessageA(hWnd, Msg, wParam, lParam); if(lpgti) TERM_WINDOW_SNIFF(lpgti->hHook); } else { RetVal=SendMessageA(hWnd, Msg, wParam, lParam); } break; } case mtSendMessageCallback: RetVal=(LRESULT)SendMessageCallbackA(hWnd, Msg, wParam, lParam, lpCallBack, dwData); break; case mtSendMessageTimeout: RetVal=SendMessageTimeoutA(hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult); break; case mtSendNotifyMessage: RetVal=(LRESULT)SendNotifyMessageA(hWnd, Msg, wParam, lParam); break; case mtPostMessage: RetVal=(LRESULT)PostMessageA(hWnd, Msg, wParam, lParam); break; case mtPostThreadMessage: // hWnd is overloaded in this case to be the thread ID RetVal=(LRESULT)PostThreadMessageA((DWORD)hWnd, Msg, wParam, lParam); break; case mtCallWindowProc: case mtCallWindowProcA: { WNDPROC lpfn = WndprocFromFauxWndproc(hWnd, lpPrevWndFunc, fptUnknown); // If the wndproc was not a "faux" one or if the wndproc // is expecting ANSI (or both!), then use CallWindowProcA, // since that is what the wndproc would expect. Otherwise, // use Unicode and call the function directly. if((lpfn==lpPrevWndFunc) || (DoesProcExpectAnsi(hWnd, lpfn, fptUnknown))) { if(lpfn) RetVal=((CallWindowProcA)(lpfn, hWnd, Msg, wParam, lParam)); } else { if(lpfn) RetVal=((* lpfn)(hWnd, Msg, wParam, lParam)); } break; } case mtDefWindowProc: RetVal=DefWindowProcA(hWnd, Msg, wParam, lParam); break; case mtDefDlgProc: RetVal=DefDlgProcA(hWnd, Msg, wParam, lParam); break; case mtDefFrameProc: // dwData is overload in this case to ve the second hWnd param RetVal=DefFrameProcA(hWnd, (HWND)dwData, Msg, wParam, lParam); break; case mtDefMDIChildProc: RetVal=DefMDIChildProcA(hWnd, Msg, wParam, lParam); break; case mtBroadcastSystemMessage: if (s_pfnBSMA == NULL) { s_pfnBSMA = (PFNbsma)GetProcAddress(GetUserHandle(), "BroadcastSystemMessageA"); if (s_pfnBSMA == NULL) s_pfnBSMA = (PFNbsma)GetProcAddress(GetUserHandle(), "BroadcastSystemMessage"); } if (s_pfnBSMA) // fuFlags is overloaded here as the dwFlags broadcast options // lpdwResult is overloaded here as the passed in lpdwRecipients RetVal=(s_pfnBSMA((DWORD)fuFlags, lpdwResult, Msg, wParam, lParam)); else { // Should be impossible!!! SetLastError(ERROR_CALL_NOT_IMPLEMENTED); RetVal=(-1); } break; default: // Should also be impossible!!! RetVal=(0); break; } return(RetVal); } /*------------------------------- GodotReceiveMessage Our global wrapper for getting messages (GetMessage, et. al.). Does all precall and postcall conversions needed for any string values that are used -------------------------------*/ BOOL GodotReceiveMessage(MESSAGETYPES mt, LPMSG lpMsg, HWND hWnd, UINT wMin, UINT wMax, UINT wRemoveMsg) { BOOL retval; MSG MsgA; // call stuff switch(mt) { case mtGetMessage: retval = (GetMessageA)(&MsgA, hWnd, wMin, wMax); break; case mtPeekMessage: retval = (PeekMessageA)(&MsgA, hWnd, wMin, wMax, wRemoveMsg); break; } // Copy some defaults (we will override for specific messages, as needed) lpMsg->wParam = MsgA.wParam; lpMsg->lParam = MsgA.lParam; lpMsg->hwnd = MsgA.hwnd; lpMsg->message = MsgA.message; lpMsg->time = MsgA.time; lpMsg->pt.x = MsgA.pt.x; lpMsg->pt.y = MsgA.pt.y; // The caller is always expecting Unicode here, so we must ALWAYS convert. switch(MsgA.message) { case EM_SETPASSWORDCHAR: case WM_CHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: case WM_IME_CHAR: case WM_IME_COMPOSITION: // All these messages require the wParam to be converted to Unicode MultiByteToWideChar(g_acp, 0, (char *)&(MsgA.wParam), g_mcs, (WCHAR *)&(lpMsg->wParam), 1); break; case WM_CHARTOITEM: { // Mask off the hiword bits, convert, then stick the hiword bits back on. WPARAM wpT = MsgA.wParam & 0xFFFF; MultiByteToWideChar(g_acp, 0, (char *)&(wpT), g_mcs, (WCHAR *)&(lpMsg->wParam), 1); lpMsg->wParam = MAKELONG(LOWORD(wpT),HIWORD(MsgA.wParam)); break; } case CB_ADDSTRING: case CB_DIR: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_INSERTSTRING: case CB_SELECTSTRING: { LONG styl = GetWindowLongA(hWnd, GWL_STYLE); if(!(((styl & CBS_OWNERDRAWFIXED) || (styl & CBS_OWNERDRAWVARIABLE)) && (!(styl & CBS_HASSTRINGS)))) { // Owner draw combo box which does not have strings stored // (See Windows Bugs # 356304 for details here) if(FSTRING_VALID((LPSTR)(MsgA.lParam))) { MultiByteToWideChar(g_acp, 0, (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), (LPWSTR)(lpMsg->lParam), MsgA.wParam); } } } break; case LB_ADDSTRING: case LB_ADDFILE: case LB_DIR: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_INSERTSTRING: case LB_SELECTSTRING: { LONG styl = GetWindowLongA(hWnd, GWL_STYLE); if(!(((styl & LBS_OWNERDRAWFIXED) || (styl & LBS_OWNERDRAWVARIABLE)) && (!(styl & LBS_HASSTRINGS)))) { // Owner draw listbox which does not have strings stored // (See Windows Bugs # 356304 for details here) if(FSTRING_VALID((LPSTR)(MsgA.lParam))) { MultiByteToWideChar(g_acp, 0, (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), (LPWSTR)(lpMsg->lParam), MsgA.wParam); } } } break; case EM_REPLACESEL: case WM_SETTEXT: case WM_DEVMODECHANGE: case WM_SETTINGCHANGE: case WM_SETMESSAGESTRING: // MFC internal msg if(FSTRING_VALID((LPSTR)(MsgA.lParam))) { // All these messages require the lParam to be converted to Unicode // CONSIDER: Is that string buffer size right? MultiByteToWideChar(g_acp, 0, (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), (LPWSTR)(lpMsg->lParam), MsgA.wParam); } case WM_DDE_EXECUTE: // wParam is the client window hWnd, lParam is the command LPTSTR. // Only convert lParam if both client and server windows are Unicode if(GetUnicodeWindowProp((HWND)lpMsg->wParam)) { MultiByteToWideChar(g_acp, 0, (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), (LPWSTR)(lpMsg->lParam), MsgA.wParam); } break; } // Get out of here return (retval); } /*------------------------------- GodotDispatchMessage Handles all the message dispatch functions -------------------------------*/ LRESULT GodotDispatchMessage(MESSAGETYPES mt, HWND hDlg, HACCEL hAccTable, LPMSG lpMsg) { // Begin locals LRESULT RetVal; ALLOCRETURN ar = arNoAlloc; MSG MsgA; // We will override some of these params later, as needed MsgA.wParam = lpMsg->wParam; MsgA.lParam = lpMsg->lParam; MsgA.hwnd = lpMsg->hwnd; MsgA.message = lpMsg->message; MsgA.time = lpMsg->time; MsgA.pt = lpMsg->pt; // The caller is always passing Unicode here, so we must ALWAYS convert. switch(lpMsg->message) { case EM_SETPASSWORDCHAR: case WM_CHAR: case WM_DEADCHAR: case WM_SYSCHAR: case WM_SYSDEADCHAR: case WM_MENUCHAR: case WM_IME_CHAR: case WM_IME_COMPOSITION: // All these messages require the wParam to be converted from Unicode WideCharToMultiByte(g_acp, 0, (WCHAR *)&(lpMsg->wParam), 1, (CHAR *)&(MsgA.wParam), g_mcs, NULL, NULL); break; case WM_CHARTOITEM: { // Mask off the hiword bits, convert, then stick the hiword bits back on. WPARAM wpT = lpMsg->wParam & 0xFFFF; WideCharToMultiByte(g_acp, 0, (WCHAR *)&(wpT), 1, (CHAR *)&(MsgA.wParam), g_mcs, NULL, NULL); MsgA.wParam = MAKELONG(LOWORD(wpT),HIWORD(lpMsg->wParam)); break; } case CB_ADDSTRING: case LB_ADDFILE: case CB_DIR: case CB_FINDSTRING: case CB_FINDSTRINGEXACT: case CB_INSERTSTRING: case CB_SELECTSTRING: { LONG styl = GetWindowLongA(hDlg, GWL_STYLE); if(!(((styl & CBS_OWNERDRAWFIXED) || (styl & CBS_OWNERDRAWVARIABLE)) && (!(styl & CBS_HASSTRINGS)))) { // Owner draw combobox which does not have strings stored // (See Windows Bugs # 356304 for details here) ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam)); } break; } case LB_ADDSTRING: case LB_DIR: case LB_FINDSTRING: case LB_FINDSTRINGEXACT: case LB_INSERTSTRING: case LB_SELECTSTRING: { LONG styl = GetWindowLongA(hDlg, GWL_STYLE); if(!(((styl & LBS_OWNERDRAWFIXED) || (styl & LBS_OWNERDRAWVARIABLE)) && (!(styl & LBS_HASSTRINGS)))) { // Owner draw listbox which does not have strings stored // (See Windows Bugs # 356304 for details here) ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam)); } break; } case EM_REPLACESEL: case WM_SETTEXT: case WM_DEVMODECHANGE: case WM_SETTINGCHANGE: case WM_SETMESSAGESTRING: // MFC internal msg // All these messages require the lParam to be converted from Unicode // It is a full string, so lets treat it as such. ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam)); break; case WM_DDE_EXECUTE: // wParam is the client window hWnd, lParam is the command LPTSTR. // Only convert lParam if both client and server windows are Unicode if(GetUnicodeWindowProp((HWND)lpMsg->wParam)) { ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam)); } break; } switch(mt) { case mtDispatchMessage: RetVal=DispatchMessageA(&MsgA); break; case mtIsDialogMessage: RetVal=(LRESULT)IsDialogMessageA(hDlg, &MsgA); break; case mtTranslateAccelerator: RetVal = (LRESULT)TranslateAcceleratorA(hDlg, hAccTable, &MsgA); break; } // If we used some heap memory then free it now if(ar == arAlloc) GodotHeapFree((LPSTR)(MsgA.lParam)); return(RetVal); }