1480 lines
50 KiB
C
1480 lines
50 KiB
C
#include "ctlspriv.h"
|
|
|
|
#ifdef NEED_WOWGETNOTIFYSIZE_HELPER
|
|
|
|
#include <shsemip.h> // SEN_* notifications
|
|
#include <commdlg.h> // CDN_* notifications
|
|
|
|
// Miscellaneous hackery needed in order to include shlobjp.h
|
|
#define CCONTROLINFO OAIDL_CONTROLINFO
|
|
#define LPCCONTROLINFO LPOAIDL_CONTROLINFO
|
|
#include <shlobj.h>
|
|
#include <shlobjp.h> // 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);
|
|
}
|
|
}
|