#include "ctlspriv.h" #ifdef NEED_WOWGETNOTIFYSIZE_HELPER #include // SEN_* notifications #include // CDN_* notifications // Miscellaneous hackery needed in order to include shlobjp.h #define CCONTROLINFO OAIDL_CONTROLINFO #define LPCCONTROLINFO LPOAIDL_CONTROLINFO #include #include // NMVIEWFOLDER structure #undef CCONTROLINFO #undef LPCCONTROLINFO // // Helper function for WOW on NT. // // WOW needs to know the size of the notify structure associated with a // notification. If a 32-bit window has been subclassed by a 16-bit app, // WOW needs to copy the notify structure into 16-bit space, and then when // the 16-bit guy does a CallWindowProc(), they have to copy it back into // 32-bit space. Without the size information, you fault on the // 32-bit side because the notify structure is incomplete. // // Some notifications have multiple structures associated with them, in // which case you should return the largest possible valid structure. // STDAPI_(UINT) WOWGetNotifySize(UINT code) { switch (code) { // Generic comctl32 notifications case NM_OUTOFMEMORY: return sizeof(NMHDR); // not used case NM_CLICK: return max(max( sizeof(NMHDR), // tab, treeview sizeof(NMCLICK)), // toolbar, statusbar sizeof(NMITEMACTIVATE)); // listview case NM_DBLCLK: return max(max( sizeof(NMHDR), // tab, treeview sizeof(NMCLICK)), // toolbar, statusbar sizeof(NMITEMACTIVATE)); // listview case NM_RETURN: return sizeof(NMHDR); case NM_RCLICK: return max(max( sizeof(NMHDR), // header, listview report mode, treeview sizeof(NMCLICK)), // toolbar, statusbar sizeof(NMITEMACTIVATE)); // listview icon mode case NM_RDBLCLK: return max(max( sizeof(NMHDR), // treeview sizeof(NMCLICK)), // toolbar, statusbar sizeof(NMITEMACTIVATE)); // listview case NM_SETFOCUS: return sizeof(NMHDR); case NM_KILLFOCUS: return sizeof(NMHDR); case NM_STARTWAIT: return sizeof(NMHDR); // not used case NM_ENDWAIT: return sizeof(NMHDR); // not used case NM_BTNCLK: return sizeof(NMHDR); // not used case NM_CUSTOMDRAW: return sizeof(NMCUSTOMDRAW); case NM_HOVER: return sizeof(NMHDR); case NM_NCHITTEST: return sizeof(NMMOUSE); case NM_KEYDOWN: return sizeof(NMKEY); case NM_RELEASEDCAPTURE: return sizeof(NMHDR); case NM_SETCURSOR: return sizeof(NMMOUSE); case NM_CHAR: return sizeof(NMCHAR); case NM_TOOLTIPSCREATED: return sizeof(NMTOOLTIPSCREATED); case NM_LDOWN: return sizeof(NMCLICK); case NM_RDOWN: return sizeof(NMCLICK); // not used // Listview notifications case LVN_ITEMCHANGING: return sizeof(NMLISTVIEW); case LVN_ITEMCHANGED: return sizeof(NMLISTVIEW); case LVN_INSERTITEM: return sizeof(NMLISTVIEW); case LVN_DELETEITEM: return sizeof(NMLISTVIEW); case LVN_DELETEALLITEMS: return sizeof(NMLISTVIEW); case LVN_BEGINLABELEDITA: return sizeof(NMLVDISPINFOA); case LVN_BEGINLABELEDITW: return sizeof(NMLVDISPINFOW); case LVN_ENDLABELEDITA: return sizeof(NMLVDISPINFOA); case LVN_ENDLABELEDITW: return sizeof(NMLVDISPINFOW); case LVN_COLUMNCLICK: return sizeof(NMLISTVIEW); case LVN_BEGINDRAG: return sizeof(NMITEMACTIVATE); case LVN_BEGINRDRAG: return sizeof(NMITEMACTIVATE); // not used case LVN_ENDDRAG: return sizeof(NMITEMACTIVATE); // not used case LVN_ENDRDRAG: return sizeof(NMITEMACTIVATE); // not used case LVN_ODCACHEHINT: return sizeof(NMLVCACHEHINT); case LVN_ODFINDITEMA: return sizeof(NMLVFINDITEMA); case LVN_ODFINDITEMW: return sizeof(NMLVFINDITEMW); case LVN_ITEMACTIVATE: return sizeof(NMITEMACTIVATE); case LVN_ODSTATECHANGED: return sizeof(NMLVODSTATECHANGE); // case LVN_PEN: // Pen Windows slackers case LVN_HOTTRACK: return sizeof(NMLISTVIEW); case LVN_GETDISPINFOA: return sizeof(NMLVDISPINFOA); case LVN_GETDISPINFOW: return sizeof(NMLVDISPINFOW); case LVN_SETDISPINFOA: return sizeof(NMLVDISPINFOA); case LVN_SETDISPINFOW: return sizeof(NMLVDISPINFOW); case LVN_KEYDOWN: return sizeof(NMLVKEYDOWN); case LVN_MARQUEEBEGIN: return sizeof(NMITEMACTIVATE); case LVN_GETINFOTIPA: return sizeof(NMLVGETINFOTIPA); case LVN_GETINFOTIPW: return sizeof(NMLVGETINFOTIPW); case LVN_GETEMPTYTEXTA: return sizeof(NMLVDISPINFOA); case LVN_GETEMPTYTEXTW: return sizeof(NMLVDISPINFOW); case LVN_INCREMENTALSEARCHA:return sizeof(NMLVFINDITEMA); case LVN_INCREMENTALSEARCHW:return sizeof(NMLVFINDITEMW); // Property sheet notifications case PSN_SETACTIVE: return sizeof(PSHNOTIFY); case PSN_KILLACTIVE: return sizeof(PSHNOTIFY); case PSN_APPLY: return sizeof(PSHNOTIFY); case PSN_RESET: return sizeof(PSHNOTIFY); case PSN_HASHELP: return sizeof(PSHNOTIFY); // not used case PSN_HELP: return sizeof(PSHNOTIFY); case PSN_WIZBACK: return sizeof(PSHNOTIFY); case PSN_WIZNEXT: return sizeof(PSHNOTIFY); case PSN_WIZFINISH: return sizeof(PSHNOTIFY); case PSN_QUERYCANCEL: return sizeof(PSHNOTIFY); case PSN_GETOBJECT: return sizeof(NMOBJECTNOTIFY); case PSN_LASTCHANCEAPPLY: return sizeof(PSHNOTIFY); case PSN_TRANSLATEACCELERATOR: return sizeof(PSHNOTIFY); case PSN_QUERYINITIALFOCUS: return sizeof(PSHNOTIFY); // Header notifications case HDN_ITEMCHANGINGA: return sizeof(NMHEADERA); case HDN_ITEMCHANGINGW: return sizeof(NMHEADERW); case HDN_ITEMCHANGEDA: return sizeof(NMHEADERA); case HDN_ITEMCHANGEDW: return sizeof(NMHEADERW); case HDN_ITEMCLICKA: return sizeof(NMHEADERA); case HDN_ITEMCLICKW: return sizeof(NMHEADERW); case HDN_ITEMDBLCLICKA: return sizeof(NMHEADERA); case HDN_ITEMDBLCLICKW: return sizeof(NMHEADERW); case HDN_DIVIDERDBLCLICKA: return sizeof(NMHEADERA); case HDN_DIVIDERDBLCLICKW: return sizeof(NMHEADERW); case HDN_BEGINTRACKA: return sizeof(NMHEADERA); case HDN_BEGINTRACKW: return sizeof(NMHEADERW); case HDN_ENDTRACKA: return sizeof(NMHEADERA); case HDN_ENDTRACKW: return sizeof(NMHEADERW); case HDN_TRACKA: return sizeof(NMHEADERA); case HDN_TRACKW: return sizeof(NMHEADERW); case HDN_GETDISPINFOA: return sizeof(NMHDDISPINFOA); case HDN_GETDISPINFOW: return sizeof(NMHDDISPINFOW); case HDN_BEGINDRAG: return sizeof(NMHEADER); // No strings case HDN_ENDDRAG: return sizeof(NMHEADER); // No strings case HDN_FILTERCHANGE: return sizeof(NMHEADER); // No strings case HDN_FILTERBTNCLICK: return sizeof(NMHDFILTERBTNCLICK); // Treeview notifications case TVN_SELCHANGINGA: return sizeof(NMTREEVIEWA); case TVN_SELCHANGINGW: return sizeof(NMTREEVIEWW); case TVN_SELCHANGEDA: return sizeof(NMTREEVIEWA); case TVN_SELCHANGEDW: return sizeof(NMTREEVIEWW); case TVN_GETDISPINFOA: return sizeof(NMTVDISPINFOA); case TVN_GETDISPINFOW: return sizeof(NMTVDISPINFOW); case TVN_SETDISPINFOA: return sizeof(NMTVDISPINFOA); case TVN_SETDISPINFOW: return sizeof(NMTVDISPINFOW); case TVN_ITEMEXPANDINGA: return sizeof(NMTREEVIEWA); case TVN_ITEMEXPANDINGW: return sizeof(NMTREEVIEWW); case TVN_ITEMEXPANDEDA: return sizeof(NMTREEVIEWA); case TVN_ITEMEXPANDEDW: return sizeof(NMTREEVIEWW); case TVN_BEGINDRAGA: return sizeof(NMTREEVIEWA); case TVN_BEGINDRAGW: return sizeof(NMTREEVIEWW); case TVN_BEGINRDRAGA: return sizeof(NMTREEVIEWA); case TVN_BEGINRDRAGW: return sizeof(NMTREEVIEWW); case TVN_DELETEITEMA: return sizeof(NMTREEVIEWA); case TVN_DELETEITEMW: return sizeof(NMTREEVIEWW); case TVN_BEGINLABELEDITA: return sizeof(NMTVDISPINFOA); case TVN_BEGINLABELEDITW: return sizeof(NMTVDISPINFOW); case TVN_ENDLABELEDITA: return sizeof(NMTVDISPINFOA); case TVN_ENDLABELEDITW: return sizeof(NMTVDISPINFOW); case TVN_KEYDOWN: return sizeof(NMTVKEYDOWN); case TVN_GETINFOTIPA: return sizeof(NMTVGETINFOTIPA); case TVN_GETINFOTIPW: return sizeof(NMTVGETINFOTIPW); case TVN_SINGLEEXPAND: return sizeof(NMTREEVIEW); // No strings // Rundll32 notifications case RDN_TASKINFO: return sizeof(RUNDLL_NOTIFY); // Tooltip notifications case TTN_GETDISPINFOA: return sizeof(NMTTDISPINFOA); case TTN_GETDISPINFOW: return sizeof(NMTTDISPINFOW); case TTN_SHOW: return sizeof(NMTTSHOWINFO); case TTN_POP: return sizeof(NMHDR); // Tab control notifications // WE ARE SUCH HORRIBLE SLACKERS! // // Even though commctrl.h says that the shell reserved range is from // -580 to -589, shsemip.h defines SEN_FIRST as -550, which conflicts // with TCN_KEYDOWN, so now TCN_KEYDOWN and SEN_DDEEXECUTE have the // same value. case TCN_KEYDOWN: return max(sizeof(NMTCKEYDOWN), sizeof(NMVIEWFOLDERW)); case TCN_SELCHANGE: return sizeof(NMHDR); case TCN_SELCHANGING: return sizeof(NMHDR); case TCN_GETOBJECT: return sizeof(NMOBJECTNOTIFY); case TCN_FOCUSCHANGE: return sizeof(NMHDR); // Comdlg32 notifications case CDN_INITDONE: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_SELCHANGE: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_FOLDERCHANGE: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_SHAREVIOLATION: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_HELP: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_FILEOK: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_TYPECHANGE: return max(sizeof(OFNOTIFYA), sizeof(OFNOTIFYW)); case CDN_INCLUDEITEM: return max(sizeof(OFNOTIFYEXA), sizeof(OFNOTIFYEXW)); // Toolbar notifications case TBN_GETBUTTONINFOA: return sizeof(NMTOOLBARA); case TBN_GETBUTTONINFOW: return sizeof(NMTOOLBARW); case TBN_BEGINDRAG: return sizeof(NMTOOLBAR); // No strings case TBN_ENDDRAG: return sizeof(NMTOOLBAR); // No strings case TBN_BEGINADJUST: return sizeof(NMHDR); case TBN_ENDADJUST: return sizeof(NMHDR); case TBN_RESET: return sizeof(NMTBCUSTOMIZEDLG); case TBN_QUERYINSERT: return sizeof(NMTOOLBAR); // No strings case TBN_QUERYDELETE: return sizeof(NMTOOLBAR); // No strings case TBN_TOOLBARCHANGE: return sizeof(NMHDR); case TBN_CUSTHELP: return sizeof(NMHDR); case TBN_DROPDOWN: return sizeof(NMTOOLBAR); // No strings case TBN_CLOSEUP: return sizeof(NMHDR); // not used case TBN_GETOBJECT: return sizeof(NMOBJECTNOTIFY); case TBN_HOTITEMCHANGE: return sizeof(NMTBHOTITEM); case TBN_DRAGOUT: return sizeof(NMTOOLBAR); // No strings case TBN_DELETINGBUTTON: return sizeof(NMTOOLBAR); // No strings case TBN_GETDISPINFOA: return sizeof(NMTBDISPINFOA); case TBN_GETDISPINFOW: return sizeof(NMTBDISPINFOW); case TBN_GETINFOTIPA: return sizeof(NMTBGETINFOTIPA); case TBN_GETINFOTIPW: return sizeof(NMTBGETINFOTIPW); case TBN_RESTORE: return sizeof(NMTBRESTORE); // WE ARE SUCH HORRIBLE SLACKERS! // // The TBN_FIRST/TBN_LAST range reserves 20 notifications for toolbar, // and we overflowed that limit, so now UDN_DELTAPOS and // TBN_SAVE have the same value. case TBN_SAVE: return max(sizeof(NMTBSAVE), sizeof(NMUPDOWN)); case TBN_INITCUSTOMIZE: return sizeof(NMTBCUSTOMIZEDLG); case TBN_WRAPHOTITEM: return sizeof(NMTBWRAPHOTITEM); case TBN_DUPACCELERATOR: return sizeof(NMTBDUPACCELERATOR); case TBN_WRAPACCELERATOR: return sizeof(NMTBWRAPACCELERATOR); case TBN_DRAGOVER: return sizeof(NMTBHOTITEM); case TBN_MAPACCELERATOR: return sizeof(NMCHAR); // Up-down control #if 0 // see comment at TBN_SAVE case UDN_DELTAPOS: return sizeof(NMUPDOWN); #endif // Monthcal control case MCN_SELCHANGE: return sizeof(NMSELCHANGE); case MCN_GETDAYSTATE: return sizeof(NMDAYSTATE); case MCN_SELECT: return sizeof(NMSELECT); // Date/time picker control case DTN_DATETIMECHANGE: return sizeof(NMDATETIMECHANGE); case DTN_USERSTRINGA: return sizeof(NMDATETIMESTRINGA); case DTN_USERSTRINGW: return sizeof(NMDATETIMESTRINGW); case DTN_WMKEYDOWNA: return sizeof(NMDATETIMEWMKEYDOWNA); case DTN_WMKEYDOWNW: return sizeof(NMDATETIMEWMKEYDOWNW); case DTN_FORMATA: return sizeof(NMDATETIMEFORMATA); case DTN_FORMATW: return sizeof(NMDATETIMEFORMATW); case DTN_FORMATQUERYA: return sizeof(NMDATETIMEFORMATQUERYA); case DTN_FORMATQUERYW: return sizeof(NMDATETIMEFORMATQUERYW); case DTN_DROPDOWN: return sizeof(NMHDR); case DTN_CLOSEUP: return sizeof(NMHDR); // Comboex notifications case CBEN_GETDISPINFOA: return sizeof(NMCOMBOBOXEXA); case CBEN_GETDISPINFOW: return sizeof(NMCOMBOBOXEXW); case CBEN_INSERTITEM: return sizeof(NMCOMBOBOXEX); // Random character set case CBEN_DELETEITEM: return sizeof(NMCOMBOBOXEX); // No strings case CBEN_ITEMCHANGED: return sizeof(NMCOMBOBOXEX); // Not used case CBEN_BEGINEDIT: return sizeof(NMHDR); case CBEN_ENDEDITA: return sizeof(NMCBEENDEDITA); case CBEN_ENDEDITW: return sizeof(NMCBEENDEDITW); case CBEN_DRAGBEGINA: return sizeof(NMCBEDRAGBEGINA); case CBEN_DRAGBEGINW: return sizeof(NMCBEDRAGBEGINW); // Rebar notifications case RBN_HEIGHTCHANGE: return sizeof(NMHDR); case RBN_GETOBJECT: return sizeof(NMOBJECTNOTIFY); case RBN_LAYOUTCHANGED: return sizeof(NMHDR); case RBN_AUTOSIZE: return sizeof(NMRBAUTOSIZE); case RBN_BEGINDRAG: return sizeof(NMREBAR); case RBN_DELETINGBAND: return sizeof(NMREBAR); case RBN_DELETEDBAND: return sizeof(NMREBAR); case RBN_CHILDSIZE: return sizeof(NMREBARCHILDSIZE); // IP address control notification case IPN_FIELDCHANGED: return sizeof(NMIPADDRESS); // Status bar notifications case SBN_SIMPLEMODECHANGE: return sizeof(NMHDR); // Pager control notifications case PGN_SCROLL: return sizeof(NMPGSCROLL); case PGN_CALCSIZE: return sizeof(NMPGCALCSIZE); default: break; } // // Categories of notifications we explicitly know nothing about. // if (code >= WMN_LAST && code <= WMN_FIRST) { // Internet Mail and News return 0; } if ((int)code >= 0) { // Application-specific notifications return 0; } // // IF THIS ASSERT FIRES, YOU MUST FIX IT OR YOU WILL BREAK WOW! // AssertMsg(0, TEXT("Notification code %d must be added to WOWGetNotifySize")); return 0; } #endif // NEED_WOWGETNOTIFYSIZE_HELPER LRESULT WINAPI SendNotifyEx(HWND hwndTo, HWND hwndFrom, int code, NMHDR* pnmhdr, BOOL bUnicode) { CCONTROLINFO ci; if (!hwndTo) { if (IsWindow(hwndFrom)) hwndTo = GetParent(hwndFrom); if (!hwndTo) return 0; } ci.hwndParent = hwndTo; ci.hwnd = hwndFrom; ci.bUnicode = BOOLIFY(bUnicode); ci.uiCodePage = CP_ACP; return CCSendNotify(&ci, code, pnmhdr); } void StringBufferAtoW(UINT uiCodePage, LPVOID pvOrgPtr, DWORD dwOrgSize, CHAR **ppszText) { if (pvOrgPtr == *ppszText) { // the pointer has not been changed by the callback... // must convert from A to W in-place if (dwOrgSize) { LPWSTR pszW = ProduceWFromA(uiCodePage, *ppszText); if (pszW) { lstrcpynW((WCHAR *)(*ppszText), pszW, dwOrgSize); // this becomes a W buffer FreeProducedString(pszW); } } } else { // the pointer has been changed out from underneath us, copy // unicode back into the original buffer. ConvertAToWN(uiCodePage, pvOrgPtr, dwOrgSize, *ppszText, -1); *ppszText = pvOrgPtr; } } typedef struct tagTHUNKSTATE { LPVOID ts_pvThunk1; LPVOID ts_pvThunk2; DWORD ts_dwThunkSize; } THUNKSTATE; // // InOutWtoA/InOutAtoW is for thunking INOUT string parameters. // // INOUT parameters always create a hassle. // // We need to save both the original ANSI and the // original UNICODE strings, so that if the app doesn't // change the ANSI string, we leave the original UNICODE // string alone. That way, UNICODE item names don't get // obliterated by the thunk. // // The original buffer is saved in pvThunk1. // We allocate two ANSI buffers. // pvThunk2 contains the original ANSIfied string. // pvThunk2+cchTextMax is the buffer we pass to the app. // On the way back, we compare pvThunk2 with pvThunk2+cchTextMax. // If they are different, then we unthunk the string; otherwise, // we leave the original UNICODE buffer alone. BOOL InOutWtoA(CCONTROLINFO *pci, THUNKSTATE *pts, LPWSTR *ppsz, DWORD cchTextMax) { pts->ts_pvThunk1 = *ppsz; // Save original buffer pts->ts_dwThunkSize = cchTextMax; if (!IsFlagPtr(pts->ts_pvThunk1)) { pts->ts_pvThunk2 = LocalAlloc(LPTR, cchTextMax * 2 * sizeof(char)); if (!ConvertWToAN(pci->uiCodePage, (LPSTR)pts->ts_pvThunk2, pts->ts_dwThunkSize, (LPWSTR)pts->ts_pvThunk1, -1)) { LocalFree(pts->ts_pvThunk2); return 0; } *ppsz = (LPWSTR)((LPSTR)pts->ts_pvThunk2 + cchTextMax); lstrcpyA((LPSTR)*ppsz, pts->ts_pvThunk2); } return TRUE; } void InOutAtoW(CCONTROLINFO *pci, THUNKSTATE *pts, LPSTR *ppsz) { if (!IsFlagPtr(pts->ts_pvThunk1)) { if (!IsFlagPtr(*ppsz) && lstrcmpA(pts->ts_pvThunk2, (LPSTR)*ppsz) != 0) StringBufferAtoW(pci->uiCodePage, pts->ts_pvThunk1, pts->ts_dwThunkSize, ppsz); LocalFree(pts->ts_pvThunk2); } *ppsz = pts->ts_pvThunk1; } LRESULT WINAPI CCSendNotify(CCONTROLINFO * pci, int code, LPNMHDR pnmhdr) { NMHDR nmhdr; LONG_PTR id; THUNKSTATE ts = { 0 }; #define pvThunk1 ts.ts_pvThunk1 #define pvThunk2 ts.ts_pvThunk2 #define dwThunkSize ts.ts_dwThunkSize LRESULT lRet; BOOL bSet = FALSE; HWND hwndParent = pci->hwndParent; DWORD dwParentPid; // -1 means Requery on each notify if ( hwndParent == (HWND)-1 ) { hwndParent = GetParent(pci->hwnd); } // unlikely but it can technically happen -- avoid the rips if ( hwndParent == NULL ) return 0; // // If pci->hwnd is -1, then a WM_NOTIFY is being forwared // from one control to a parent. EG: Tooltips sent // a WM_NOTIFY to toolbar, and toolbar is forwarding it // to the real parent window. // if (pci->hwnd != (HWND) -1) { // // If this is a child then get its ID. We need to go out of our way to // avoid calling GetDlgCtrlID on toplevel windows since it will return // a pseudo-random number (those of you who know what this number is // keep quiet). Anyway it's kinda hard to figure this out in Windows // because of the following: // // - a window can SetWindowLong(GWL_STYLE, WS_CHILD) but this only // does about half the work - hence checking the style is out. // - GetParent will return your OWNER if you are toplevel. // - there is no GetWindow(...GW_HWNDPARENT) to save us. // // Hence we are stuck with calling GetParent and then checking to see // if it lied and gave us the owner instead. Yuck. // id = 0; if (pci->hwnd) { HWND hwndParent = GetParent(pci->hwnd); if (hwndParent && (hwndParent != GetWindow(pci->hwnd, GW_OWNER))) { id = GetDlgCtrlID(pci->hwnd); } } if (!pnmhdr) pnmhdr = &nmhdr; pnmhdr->hwndFrom = pci->hwnd; pnmhdr->idFrom = id; pnmhdr->code = code; } else { id = pnmhdr->idFrom; code = pnmhdr->code; } // OLE in its massively componentized world sometimes creates // a control whose parent belongs to another process. (For example, // when there is a local server embedding.) WM_NOTIFY // messages can't cross process boundaries, so stop the message // from going there lest we fault the recipient. if (!GetWindowThreadProcessId(hwndParent, &dwParentPid) || dwParentPid != GetCurrentProcessId()) { TraceMsg(TF_WARNING, "nf: Not sending WM_NOTIFY %08x across processes", code); return 0; } #ifdef NEED_WOWGETNOTIFYSIZE_HELPER ASSERT(code >= 0 || WOWGetNotifySize(code)); #endif // NEED_WOWGETNOTIFYSIZE_HELPER /* * All the thunking for Notify Messages happens here */ if (!pci->bUnicode) { BOOL fThunked = TRUE; switch( code ) { case LVN_ODFINDITEMW: pnmhdr->code = LVN_ODFINDITEMA; goto ThunkLV_FINDINFO; case LVN_INCREMENTALSEARCHW: pnmhdr->code = LVN_INCREMENTALSEARCHA; goto ThunkLV_FINDINFO; ThunkLV_FINDINFO: { LV_FINDINFO *plvfi; // Hack Alert! This code assumes that all fields of LV_FINDINFOA and // LV_FINDINFOW are exactly the same except for the string pointers. COMPILETIME_ASSERT(sizeof(LV_FINDINFOA) == sizeof(LV_FINDINFOW)); // Since WCHARs are bigger than char, we will just use the // wchar buffer to hold the chars, and not worry about the extra // room at the end. COMPILETIME_ASSERT(sizeof(WCHAR) >= sizeof(char)); plvfi = &((PNM_FINDITEM)pnmhdr)->lvfi; if (plvfi->flags & (LVFI_STRING | LVFI_PARTIAL | LVFI_SUBSTRING)) { pvThunk1 = (PVOID)plvfi->psz; dwThunkSize = lstrlen(pvThunk1) + 1; plvfi->psz = (LPWSTR)ProduceAFromW(pci->uiCodePage, plvfi->psz); } } break; case LVN_GETDISPINFOW: { LV_ITEMW *pitem; pnmhdr->code = LVN_GETDISPINFOA; // Hack Alert! This code assumes that all fields of LV_DISPINFOA and // LV_DISPINFOW are exactly the same except for the string pointers. COMPILETIME_ASSERT(sizeof(LV_DISPINFOA) == sizeof(LV_DISPINFOW)); // Since WCHARs are bigger than char, we will just use the // wchar buffer to hold the chars, and not worry about the extra // room at the end. COMPILETIME_ASSERT(sizeof(WCHAR) >= sizeof(char)); // // Some sleazebag code (shell32.dll) just changes the pszText // pointer to point to the name, so capture the original pointer // so we can detect this and not smash their data. // pitem = &(((LV_DISPINFOW *)pnmhdr)->item); if (!IsFlagPtr(pitem) && (pitem->mask & LVIF_TEXT) && !IsFlagPtr(pitem->pszText)) { pvThunk1 = pitem->pszText; dwThunkSize = pitem->cchTextMax; } break; } // LVN_ENDLABELEDIT uses an INOUT parameter, never explicitly // documented as such, but it just happened to be that way, // and I don't want to take the chance that somebody was relying // on it. case LVN_ENDLABELEDITW: pnmhdr->code = LVN_ENDLABELEDITA; goto ThunkLV_DISPINFO; case LVN_BEGINLABELEDITW: pnmhdr->code = LVN_BEGINLABELEDITA; goto ThunkLV_DISPINFO; case LVN_SETDISPINFOW: pnmhdr->code = LVN_SETDISPINFOA; goto ThunkLV_DISPINFO; case LVN_GETEMPTYTEXTW: pnmhdr->code = LVN_GETEMPTYTEXTA; goto ThunkLV_DISPINFO; ThunkLV_DISPINFO: { LV_ITEMW *pitem; COMPILETIME_ASSERT(sizeof(LV_ITEMA) == sizeof(LV_ITEMW)); pitem = &(((LV_DISPINFOW *)pnmhdr)->item); if (pitem->mask & LVIF_TEXT) { if (!InOutWtoA(pci, &ts, &pitem->pszText, pitem->cchTextMax)) return 0; } break; } case LVN_GETINFOTIPW: { NMLVGETINFOTIPW *pgit = (NMLVGETINFOTIPW *)pnmhdr; COMPILETIME_ASSERT(sizeof(NMLVGETINFOTIPA) == sizeof(NMLVGETINFOTIPW)); pnmhdr->code = LVN_GETINFOTIPA; if (!InOutWtoA(pci, &ts, &pgit->pszText, pgit->cchTextMax)) return 0; } break; case TVN_GETINFOTIPW: { NMTVGETINFOTIPW *pgit = (NMTVGETINFOTIPW *)pnmhdr; pnmhdr->code = TVN_GETINFOTIPA; pvThunk1 = pgit->pszText; dwThunkSize = pgit->cchTextMax; } break; case TBN_GETINFOTIPW: { NMTBGETINFOTIPW *pgit = (NMTBGETINFOTIPW *)pnmhdr; pnmhdr->code = TBN_GETINFOTIPA; pvThunk1 = pgit->pszText; dwThunkSize = pgit->cchTextMax; } break; case TVN_SELCHANGINGW: pnmhdr->code = TVN_SELCHANGINGA; bSet = TRUE; // fall through case TVN_SELCHANGEDW: if (!bSet) { pnmhdr->code = TVN_SELCHANGEDA; bSet = TRUE; } /* * These msgs have a NM_TREEVIEW with both TV_ITEMs filled in * * FALL THROUGH TO TVN_DELETEITEM to thunk itemOld then go on for * the other structure. */ // fall through case TVN_DELETEITEMW: { /* * This message has a NM_TREEVIEW in lParam with itemOld filled in */ LPTV_ITEMW pitem; if (!bSet) { pnmhdr->code = TVN_DELETEITEMA; bSet = TRUE; } pitem = &(((LPNM_TREEVIEWW)pnmhdr)->itemOld); // thunk itemOld if ( (pitem->mask & TVIF_TEXT) && !IsFlagPtr(pitem->pszText)) { pvThunk2 = pitem->pszText; pitem->pszText = (LPWSTR)ProduceAFromW(pci->uiCodePage, pvThunk2); } // if this is deleteitem then we are done if (pnmhdr->code == TVN_DELETEITEMA) break; /* FALL THROUGH TO TVN_ITEMEXPANDING to thunk itemNew */ } // fall through case TVN_ITEMEXPANDINGW: if (!bSet) { pnmhdr->code = TVN_ITEMEXPANDINGA; bSet = TRUE; } // fall through case TVN_ITEMEXPANDEDW: if (!bSet) { pnmhdr->code = TVN_ITEMEXPANDEDA; bSet = TRUE; } // fall through case TVN_BEGINDRAGW: if (!bSet) { pnmhdr->code = TVN_BEGINDRAGA; bSet = TRUE; } // fall through case TVN_BEGINRDRAGW: { /* these msgs have a NM_TREEVIEW with itemNew TV_ITEM filled in */ LPTV_ITEMW pitem; if (!bSet) { pnmhdr->code = TVN_BEGINRDRAGA; } pitem = &(((LPNM_TREEVIEWW)pnmhdr)->itemNew); if ( (pitem->mask & TVIF_TEXT) && !IsFlagPtr(pitem->pszText)) { pvThunk1 = pitem->pszText; pitem->pszText = (LPWSTR)ProduceAFromW(pci->uiCodePage, pvThunk1); } break; } case TVN_SETDISPINFOW: pnmhdr->code = TVN_SETDISPINFOA; goto ThunkTV_DISPINFO; case TVN_BEGINLABELEDITW: pnmhdr->code = TVN_BEGINLABELEDITA; goto ThunkTV_DISPINFO; // TVN_ENDLABELEDIT uses an INOUT parameter, never explicitly // documented as such, but it just happened to be that way, // and I don't want to take the chance that somebody was relying // on it. case TVN_ENDLABELEDITW: pnmhdr->code = TVN_ENDLABELEDITA; goto ThunkTV_DISPINFO; ThunkTV_DISPINFO: { /* * All these messages have a TV_DISPINFO in lParam. */ LPTV_ITEMW pitem; pitem = &(((TV_DISPINFOW *)pnmhdr)->item); if (pitem->mask & TVIF_TEXT) { if (!InOutWtoA(pci, &ts, &pitem->pszText, pitem->cchTextMax)) return 0; } break; } case TVN_GETDISPINFOW: { // All these messages have a TV_DISPINFO in lParam. LPTV_ITEMW pitem; pnmhdr->code = TVN_GETDISPINFOA; pitem = &(((TV_DISPINFOW *)pnmhdr)->item); if ((pitem->mask & TVIF_TEXT) && !IsFlagPtr(pitem->pszText) && pitem->cchTextMax) { pvThunk1 = pitem->pszText; dwThunkSize = pitem->cchTextMax; pvThunk2 = LocalAlloc(LPTR, pitem->cchTextMax * sizeof(char)); pitem->pszText = pvThunk2; pitem->pszText[0] = TEXT('\0'); } break; } case HDN_ITEMCHANGINGW: pnmhdr->code = HDN_ITEMCHANGINGA; bSet = TRUE; // fall through case HDN_ITEMCHANGEDW: if (!bSet) { pnmhdr->code = HDN_ITEMCHANGEDA; bSet = TRUE; } // fall through case HDN_ITEMCLICKW: if (!bSet) { pnmhdr->code = HDN_ITEMCLICKA; bSet = TRUE; } // fall through case HDN_ITEMDBLCLICKW: if (!bSet) { pnmhdr->code = HDN_ITEMDBLCLICKA; bSet = TRUE; } // fall through case HDN_DIVIDERDBLCLICKW: if (!bSet) { pnmhdr->code = HDN_DIVIDERDBLCLICKA; bSet = TRUE; } // fall through case HDN_BEGINTRACKW: if (!bSet) { pnmhdr->code = HDN_BEGINTRACKA; bSet = TRUE; } // fall through case HDN_ENDTRACKW: if (!bSet) { pnmhdr->code = HDN_ENDTRACKA; bSet = TRUE; } // fall through case HDN_TRACKW: { HD_ITEMW *pitem; if (!bSet) { pnmhdr->code = HDN_TRACKA; } pitem = ((HD_NOTIFY *)pnmhdr)->pitem; if ( !IsFlagPtr(pitem) && (pitem->mask & HDI_TEXT) && !IsFlagPtr(pitem->pszText)) { pvThunk1 = pitem->pszText; dwThunkSize = pitem->cchTextMax; pitem->pszText = (LPWSTR)ProduceAFromW(pci->uiCodePage, pvThunk1); } if ( !IsFlagPtr(pitem) && (pitem->mask & HDI_FILTER) && pitem->pvFilter ) { if ( !(pitem->type & HDFT_HASNOVALUE) && ((pitem->type & HDFT_ISMASK)==HDFT_ISSTRING) ) { LPHD_TEXTFILTER ptextFilter = (LPHD_TEXTFILTER)pitem->pvFilter; pvThunk2 = ptextFilter->pszText; dwThunkSize = ptextFilter->cchTextMax; ptextFilter->pszText = (LPWSTR)ProduceAFromW(pci->uiCodePage, pvThunk2); } } break; } case CBEN_ENDEDITW: { LPNMCBEENDEDITW peew = (LPNMCBEENDEDITW) pnmhdr; LPNMCBEENDEDITA peea = LocalAlloc(LPTR, sizeof(NMCBEENDEDITA)); if (!peea) return 0; peea->hdr = peew->hdr; peea->hdr.code = CBEN_ENDEDITA; peea->fChanged = peew->fChanged; peea->iNewSelection = peew->iNewSelection; peea->iWhy = peew->iWhy; ConvertWToAN(pci->uiCodePage, peea->szText, ARRAYSIZE(peea->szText), peew->szText, -1); pvThunk1 = pnmhdr; pnmhdr = &peea->hdr; ASSERT((LPVOID)pnmhdr == (LPVOID)peea); break; } case CBEN_DRAGBEGINW: { LPNMCBEDRAGBEGINW pdbw = (LPNMCBEDRAGBEGINW) pnmhdr; LPNMCBEDRAGBEGINA pdba = LocalAlloc(LPTR, sizeof(NMCBEDRAGBEGINA)); if (!pdba) return 0; pdba->hdr = pdbw->hdr; pdba->hdr.code = CBEN_DRAGBEGINA; pdba->iItemid = pdbw->iItemid; ConvertWToAN(pci->uiCodePage, pdba->szText, ARRAYSIZE(pdba->szText), pdbw->szText, -1); pvThunk1 = pnmhdr; pnmhdr = &pdba->hdr; ASSERT((LPVOID)pnmhdr == (LPVOID)pdba); break; } case CBEN_GETDISPINFOW: { PNMCOMBOBOXEXW pnmcbe = (PNMCOMBOBOXEXW)pnmhdr; pnmhdr->code = CBEN_GETDISPINFOA; if (pnmcbe->ceItem.mask & CBEIF_TEXT && !IsFlagPtr(pnmcbe->ceItem.pszText) && pnmcbe->ceItem.cchTextMax) { pvThunk1 = pnmcbe->ceItem.pszText; dwThunkSize = pnmcbe->ceItem.cchTextMax; pvThunk2 = LocalAlloc(LPTR, pnmcbe->ceItem.cchTextMax * sizeof(char)); pnmcbe->ceItem.pszText = pvThunk2; pnmcbe->ceItem.pszText[0] = TEXT('\0'); } break; } case HDN_GETDISPINFOW: { LPNMHDDISPINFOW pHDDispInfoW; pnmhdr->code = HDN_GETDISPINFOA; pHDDispInfoW = (LPNMHDDISPINFOW) pnmhdr; pvThunk1 = pHDDispInfoW->pszText; dwThunkSize = pHDDispInfoW->cchTextMax; pHDDispInfoW->pszText = LocalAlloc (LPTR, pHDDispInfoW->cchTextMax * sizeof(char)); if (!pHDDispInfoW->pszText) { pHDDispInfoW->pszText = (LPWSTR) pvThunk1; break; } WideCharToMultiByte(pci->uiCodePage, 0, (LPWSTR)pvThunk1, -1, (LPSTR)pHDDispInfoW->pszText, pHDDispInfoW->cchTextMax, NULL, NULL); break; } case TBN_GETBUTTONINFOW: { LPTBNOTIFYW pTBNW; pnmhdr->code = TBN_GETBUTTONINFOA; pTBNW = (LPTBNOTIFYW)pnmhdr; pvThunk1 = pTBNW->pszText; dwThunkSize = pTBNW->cchText; pvThunk2 = LocalAlloc (LPTR, pTBNW->cchText * sizeof(char)); if (!pvThunk2) { break; } pTBNW->pszText = pvThunk2; WideCharToMultiByte(pci->uiCodePage, 0, (LPWSTR)pvThunk1, -1, (LPSTR)pTBNW->pszText, pTBNW->cchText, NULL, NULL); } break; case TTN_NEEDTEXTW: { LPTOOLTIPTEXTA lpTTTA; LPTOOLTIPTEXTW lpTTTW = (LPTOOLTIPTEXTW) pnmhdr; lpTTTA = LocalAlloc(LPTR, sizeof(TOOLTIPTEXTA)); if (!lpTTTA) return 0; lpTTTA->hdr = lpTTTW->hdr; lpTTTA->hdr.code = TTN_NEEDTEXTA; lpTTTA->lpszText = lpTTTA->szText; lpTTTA->hinst = lpTTTW->hinst; lpTTTA->uFlags = lpTTTW->uFlags; lpTTTA->lParam = lpTTTW->lParam; WideCharToMultiByte(pci->uiCodePage, 0, lpTTTW->szText, -1, lpTTTA->szText, ARRAYSIZE(lpTTTA->szText), NULL, NULL); pvThunk1 = pnmhdr; pnmhdr = (NMHDR *)lpTTTA; } break; case DTN_USERSTRINGW: { LPNMDATETIMESTRINGW lpDateTimeString = (LPNMDATETIMESTRINGW) pnmhdr; pnmhdr->code = DTN_USERSTRINGA; pvThunk1 = ProduceAFromW(pci->uiCodePage, lpDateTimeString->pszUserString); lpDateTimeString->pszUserString = (LPWSTR) pvThunk1; } break; case DTN_WMKEYDOWNW: { LPNMDATETIMEWMKEYDOWNW lpDateTimeWMKeyDown = (LPNMDATETIMEWMKEYDOWNW) pnmhdr; pnmhdr->code = DTN_WMKEYDOWNA; pvThunk1 = ProduceAFromW(pci->uiCodePage, lpDateTimeWMKeyDown->pszFormat); lpDateTimeWMKeyDown->pszFormat = (LPWSTR) pvThunk1; } break; case DTN_FORMATQUERYW: { LPNMDATETIMEFORMATQUERYW lpDateTimeFormatQuery = (LPNMDATETIMEFORMATQUERYW) pnmhdr; pnmhdr->code = DTN_FORMATQUERYA; pvThunk1 = ProduceAFromW(pci->uiCodePage, lpDateTimeFormatQuery->pszFormat); lpDateTimeFormatQuery->pszFormat = (LPWSTR) pvThunk1; } break; case DTN_FORMATW: { LPNMDATETIMEFORMATW lpDateTimeFormat = (LPNMDATETIMEFORMATW) pnmhdr; pnmhdr->code = DTN_FORMATA; pvThunk1 = ProduceAFromW(pci->uiCodePage, lpDateTimeFormat->pszFormat); lpDateTimeFormat->pszFormat = (LPWSTR) pvThunk1; } break; default: fThunked = FALSE; break; } #ifdef NEED_WOWGETNOTIFYSIZE_HELPER ASSERT(code >= 0 || WOWGetNotifySize(code)); #endif // NEED_WOWGETNOTIFYSIZE_HELPER lRet = SendMessage(hwndParent, WM_NOTIFY, (WPARAM)id, (LPARAM)pnmhdr); /* * All the thunking for Notify Messages happens here */ if (fThunked) { switch(pnmhdr->code) { case LVN_ODFINDITEMA: case LVN_INCREMENTALSEARCHA: { LV_FINDINFO *plvfi = &((PNM_FINDITEM)pnmhdr)->lvfi; if (pvThunk1) { FreeProducedString((LPWSTR)plvfi->psz); plvfi->psz = pvThunk1; } } break; case LVN_GETDISPINFOA: { LV_ITEMA *pitem = &(((LV_DISPINFOA *)pnmhdr)->item); if (!IsFlagPtr(pitem) && (pitem->mask & LVIF_TEXT) && !IsFlagPtr(pitem->pszText)) { StringBufferAtoW(pci->uiCodePage, pvThunk1, dwThunkSize, &pitem->pszText); } } break; case LVN_ENDLABELEDITA: case LVN_BEGINLABELEDITA: case LVN_SETDISPINFOA: case LVN_GETEMPTYTEXTA: { LV_ITEMA *pitem = &(((LV_DISPINFOA *)pnmhdr)->item); InOutAtoW(pci, &ts, &pitem->pszText); } break; case LVN_GETINFOTIPA: { NMLVGETINFOTIPA *pgit = (NMLVGETINFOTIPA *)pnmhdr; InOutAtoW(pci, &ts, &pgit->pszText); } break; case TVN_GETINFOTIPA: { NMTVGETINFOTIPA *pgit = (NMTVGETINFOTIPA *)pnmhdr; StringBufferAtoW(pci->uiCodePage, pvThunk1, dwThunkSize, &pgit->pszText); } break; case TBN_GETINFOTIPA: { NMTBGETINFOTIPA *pgit = (NMTBGETINFOTIPA *)pnmhdr; StringBufferAtoW(pci->uiCodePage, pvThunk1, dwThunkSize, &pgit->pszText); } break; case TVN_SELCHANGINGA: case TVN_SELCHANGEDA: case TVN_DELETEITEMA: { LPTV_ITEMW pitem; if ( !IsFlagPtr(pvThunk2) ) { pitem = &(((LPNM_TREEVIEWW)pnmhdr)->itemOld); FreeProducedString(pitem->pszText); pitem->pszText = pvThunk2; } // if this is delitem, then we are done if (code == TVN_DELETEITEM) break; /* FALL THROUGH TO TVN_ITEMEXPANDING to unthunk itemNew */ } // fall through case TVN_ITEMEXPANDINGA: case TVN_ITEMEXPANDEDA: case TVN_BEGINDRAGA: case TVN_BEGINRDRAGA: { /* these msgs have a NM_TREEVIEW with itemNew TV_ITEM filled in */ LPTV_ITEMW pitem; if (!IsFlagPtr(pvThunk1)) { pitem = &(((LPNM_TREEVIEWW)pnmhdr)->itemNew); FreeProducedString(pitem->pszText); pitem->pszText = pvThunk1; } break; } case TVN_SETDISPINFOA: case TVN_BEGINLABELEDITA: case TVN_ENDLABELEDITA: { LPTV_ITEMA pitem; pitem = &(((TV_DISPINFOA *)pnmhdr)->item); InOutAtoW(pci, &ts, &pitem->pszText); } break; case TVN_GETDISPINFOA: { /* * This message has a TV_DISPINFO in lParam that wass filled in * during the callback and needs to be unthunked. */ LPTV_ITEMW pitem; pitem = &(((TV_DISPINFOW *)pnmhdr)->item); if (!IsFlagPtr(pvThunk1) && (pitem->mask & TVIF_TEXT) && !IsFlagPtr(pitem->pszText)) { ConvertAToWN(pci->uiCodePage, pvThunk1, dwThunkSize, (LPSTR)pitem->pszText, -1); pitem->pszText = pvThunk1; LocalFree(pvThunk2); } break; } case HDN_ITEMCHANGINGA: case HDN_ITEMCHANGEDA: case HDN_ITEMCLICKA: case HDN_ITEMDBLCLICKA: case HDN_DIVIDERDBLCLICKA: case HDN_BEGINTRACKA: case HDN_ENDTRACKA: case HDN_TRACKA: { HD_ITEMW *pitem; pitem = ((HD_NOTIFY *)pnmhdr)->pitem; if ( !IsFlagPtr(pitem) && (pitem->mask & HDI_TEXT) && !IsFlagPtr(pvThunk1)) { ConvertAToWN(pci->uiCodePage, pvThunk1, dwThunkSize, (LPSTR)(pitem->pszText), -1); FreeProducedString(pitem->pszText); pitem->pszText = pvThunk1; } if ( !IsFlagPtr(pitem) && (pitem->mask & HDI_FILTER) && pitem->pvFilter && pvThunk2 ) { if ( !(pitem->type & HDFT_HASNOVALUE) && ((pitem->type & HDFT_ISMASK)==HDFT_ISSTRING) ) { LPHD_TEXTFILTER ptextFilter = (LPHD_TEXTFILTER)pitem->pvFilter; ConvertAToWN(pci->uiCodePage, pvThunk2, dwThunkSize, (LPSTR)(ptextFilter->pszText), -1); FreeProducedString(ptextFilter->pszText); ptextFilter->pszText = pvThunk2; } } break; } case CBEN_ENDEDITA: { LPNMCBEENDEDITW peew = (LPNMCBEENDEDITW) pvThunk1; LPNMCBEENDEDITA peea = (LPNMCBEENDEDITA) pnmhdr; // Don't unthunk the string since that destroys unicode round-trip // and the client shouldn't be modifying it anyway. // ConvertAToWN(pci->uiCodePage, peew->szText, ARRAYSIZE(peew->szText), // peea->szText, -1); LocalFree(peea); } break; case CBEN_DRAGBEGINA: { LPNMCBEDRAGBEGINW pdbw = (LPNMCBEDRAGBEGINW) pvThunk1; LPNMCBEDRAGBEGINA pdba = (LPNMCBEDRAGBEGINA) pnmhdr; // Don't unthunk the string since that destroys unicode round-trip // and the client shouldn't be modifying it anyway. // ConvertAToWN(pci->uiCodePage, pdbw->szText, ARRAYSIZE(pdbw->szText), // pdba->szText, -1); LocalFree(pdba); } break; case CBEN_GETDISPINFOA: { PNMCOMBOBOXEXW pnmcbeW; pnmcbeW = (PNMCOMBOBOXEXW)pnmhdr; ConvertAToWN(pci->uiCodePage, pvThunk1, dwThunkSize, (LPSTR)(pnmcbeW->ceItem.pszText), -1); if (pvThunk2) LocalFree(pvThunk2); pnmcbeW->ceItem.pszText = pvThunk1; } break; case HDN_GETDISPINFOA: { LPNMHDDISPINFOW pHDDispInfoW; pHDDispInfoW = (LPNMHDDISPINFOW)pnmhdr; ConvertAToWN(pci->uiCodePage, pvThunk1, dwThunkSize, (LPSTR)(pHDDispInfoW->pszText), -1); LocalFree(pHDDispInfoW->pszText); pHDDispInfoW->pszText = pvThunk1; } break; case TBN_GETBUTTONINFOA: { LPTBNOTIFYW pTBNW; pTBNW = (LPTBNOTIFYW)pnmhdr; ConvertAToWN(pci->uiCodePage, pvThunk1, dwThunkSize, (LPSTR)(pTBNW->pszText), -1); pTBNW->pszText = pvThunk1; LocalFree(pvThunk2); } break; case TTN_NEEDTEXTA: { LPTOOLTIPTEXTA lpTTTA = (LPTOOLTIPTEXTA) pnmhdr; LPTOOLTIPTEXTW lpTTTW = (LPTOOLTIPTEXTW) pvThunk1; ThunkToolTipTextAtoW (lpTTTA, lpTTTW, pci->uiCodePage); LocalFree(lpTTTA); } break; case DTN_USERSTRINGA: case DTN_WMKEYDOWNA: case DTN_FORMATQUERYA: { FreeProducedString (pvThunk1); } break; case DTN_FORMATA: { LPNMDATETIMEFORMATA lpDateTimeFormat = (LPNMDATETIMEFORMATA) pnmhdr; FreeProducedString (pvThunk1); // // pszDisplay and szDisplay are special cases. // if (lpDateTimeFormat->pszDisplay && *lpDateTimeFormat->pszDisplay) { // // if pszDisplay still points at szDisplay then thunk // in place. Otherwise allocate memory and copy the // display string. This buffer will be freeded in monthcal.c // if (lpDateTimeFormat->pszDisplay == lpDateTimeFormat->szDisplay) { CHAR szDisplay[64]; lstrcpynA (szDisplay, lpDateTimeFormat->szDisplay, 64); ConvertAToWN (pci->uiCodePage, (LPWSTR)lpDateTimeFormat->szDisplay, 64, szDisplay, -1); } else { lpDateTimeFormat->pszDisplay = (LPSTR) ProduceWFromA (pci->uiCodePage, lpDateTimeFormat->pszDisplay); } } } break; default: /* No thunking needed */ break; } } return lRet; } else return(SendMessage(hwndParent, WM_NOTIFY, (WPARAM)id, (LPARAM)pnmhdr)); #undef pvThunk1 #undef pvThunk2 #undef dwThunkSize } LRESULT WINAPI SendNotify(HWND hwndTo, HWND hwndFrom, int code, NMHDR* pnmhdr) { CCONTROLINFO ci; ci.hwndParent = hwndTo; ci.hwnd = hwndFrom; ci.bUnicode = FALSE; ci.uiCodePage = CP_ACP; // // SendNotify is obsolete. New code should call CCSendNotify // instead. However, if something does call SendNotify, // it will call SendNotifyEx with FALSE as the Unicode parameter, // because it probably is ANSI code. // return CCSendNotify(&ci, code, pnmhdr); } DWORD CICustomDrawNotify(LPCCONTROLINFO lpci, DWORD dwStage, LPNMCUSTOMDRAW lpnmcd) { DWORD dwRet = CDRF_DODEFAULT; // bail if... // this is an item notification, but an item notification wasn't asked for if ((dwStage & CDDS_ITEM) && !(lpci->dwCustom & CDRF_NOTIFYITEMDRAW)) { return dwRet; } lpnmcd->dwDrawStage = dwStage; dwRet = (DWORD) CCSendNotify(lpci, NM_CUSTOMDRAW, &lpnmcd->hdr); // validate the flags if (dwRet & ~CDRF_VALIDFLAGS) return CDRF_DODEFAULT; return dwRet; } // // Too many apps encounter strange behavior when we send out // NM_CUSTOMDRAW messages at times unrelated to painting. // E.g., NetMeeting and MFC recurse back into ListView_RecomputeLabelSize. // CryptUI will fault if it's asked to NM_CUSTOMDRAW before it gets // WM_INITDIALOG. So all this fake customdraw stuff is v5 only. // // And since it is very popular to call back into the control during // the handling of NM_CUSTOMDRAW, we protect against recursing ourselves // to death by blowing off nested fake customdraw messages. DWORD CIFakeCustomDrawNotify(LPCCONTROLINFO lpci, DWORD dwStage, LPNMCUSTOMDRAW lpnmcd) { DWORD dwRet = CDRF_DODEFAULT; if (!lpci->bInFakeCustomDraw) { lpci->bInFakeCustomDraw = TRUE; dwRet = CICustomDrawNotify(lpci, dwStage, lpnmcd); ASSERT(lpci->bInFakeCustomDraw); lpci->bInFakeCustomDraw = FALSE; } return dwRet; } /*---------------------------------------------------------- Purpose: Release the capture and tell the parent we've done so. Returns: Whether the control is still alive. */ BOOL CCReleaseCapture(CCONTROLINFO * pci) { HWND hwndCtl = pci->hwnd; NMHDR nmhdr = {0}; ReleaseCapture(); // Tell the parent we've released the capture CCSendNotify(pci, NM_RELEASEDCAPTURE, &nmhdr); return IsWindow(hwndCtl); } /*---------------------------------------------------------- Purpose: Set the capture. If the hwndSet is NULL, it means the capture is being released, so tell the parent we've done so. Use this function if there's a possibility that the hwnd may be NULL. */ void CCSetCapture(CCONTROLINFO * pci, HWND hwndSet) { SetCapture(hwndSet); if (NULL == hwndSet) { NMHDR nmhdr = {0}; // Tell the parent we've released the capture CCSendNotify(pci, NM_RELEASEDCAPTURE, &nmhdr); } }