--- /dev/null
+/*
+ * Top level resource file for browseui stuff
+ *
+ * Copyright 2009 Andrew Hill
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#define WIN32_NO_STATUS
+#define _INC_WINDOWS
+#define COM_NO_WINDOWS_H
+#include <shlobj.h>
+
+#include "resource.h"
+
+#include "version.rc"
+
+LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
+
+/* REGISTRY */
+IDR_ADDRESSBAND REGISTRY "res/addressband.rgs"
+IDR_ADDRESSEDITBOX REGISTRY "res/addresseditbox.rgs"
+IDR_ACLMULTI REGISTRY "res/autocompletecontainer.rgs"
+IDR_BANDPROXY REGISTRY "res/bandproxy.rgs"
+IDR_BANDSITE REGISTRY "res/rebarbandsite.rgs"
+IDR_BANDSITEMENU REGISTRY "res/bandsitemenu.rgs"
+IDR_BRANDBAND REGISTRY "res/brandband.rgs"
+IDR_COMMONBROWSER REGISTRY "res/commonbrowser.rgs"
+IDR_GLOBALFOLDERSETTINGS REGISTRY "res/globalfoldersettings.rgs"
+IDR_INTERNETTOOLBAR REGISTRY "res/internettoolbar.rgs"
+IDR_REGTREEOPTIONS REGISTRY "res/regtreeoptions.rgs"
+
+/*
+ * Everything specific to any language goes
+ * in one of the specific files.
+ */
+//#include "lang/ca-ES.rc"
+//#include "lang/cs-CZ.rc"
+//#include "lang/da-DK.rc"
+//#include "lang/el-GR.rc"
+//#include "lang/en-GB.rc"
+//#include "lang/fi-FI.rc"
+//#include "lang/fr-FR.rc"
+//#include "lang/he-IL.rc"
+//#include "lang/hu-HU.rc"
+//#include "lang/it-IT.rc"
+//#include "lang/ja-JP.rc"
+//#include "lang/ko-KR.rc"
+//#include "lang/nl-NL.rc"
+//#include "lang/no-NO.rc"
+//#include "lang/pt-BR.rc"
+//#include "lang/pt-PT.rc"
+//#include "lang/ru-RU.rc"
+//#include "lang/sl-SI.rc"
+//#include "lang/sk-SK.rc"
+//#include "lang/sv-SE.rc"
+//#include "lang/zh-CN.rc"
+//#include "lang/zh-TW.rc"
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+#ifdef LANGUAGE_BG_BG
+ #include "lang/bg-BG.rc"
+#endif
+#ifdef LANGUAGE_DE_DE
+ #include "lang/de-DE.rc"
+#endif
+#ifdef LANGUAGE_EN_US
+ #include "lang/en-US.rc"
+#endif
+#ifdef LANGUAGE_ES_ES
+ #include "lang/es-ES.rc"
+#endif
+#ifdef LANGUAGE_HE_IL
+ #include "lang/he-IL.rc"
+#endif
+#ifdef LANGUAGE_IT_IT
+ #include "lang/it-IT.rc"
+#endif
+#ifdef LANGUAGE_PL_PL
+ #include "lang/pl-PL.rc"
+#endif
+#ifdef LANGUAGE_RO_RO
+ #include "lang/ro-RO.rc"
+#endif
++#ifdef LANGUAGE_RU_RU
++ #include "lang/ru-RU.rc"
++#endif
+#ifdef LANGUAGE_TR_TR
+ #include "lang/tr-TR.rc"
+#endif
+#ifdef LANGUAGE_SQ_AL
+ #include "lang/sq-AL.rc"
+#endif
+#ifdef LANGUAGE_UK_UA
+ #include "lang/uk-UA.rc"
+#endif
--- /dev/null
+/*
+ * PROJECT: shell32
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: dll/win32/shell32/shv_item_new.c
+ * PURPOSE: provides default context menu implementation
+ * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
+ */
+
+/*
+TODO:
+ The code in NotifyShellViewWindow to deliver commands to the view is broken. It is an excellent
+ example of the wrong way to do it.
+*/
+
+#include "precomp.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dmenu);
+
+typedef struct _DynamicShellEntry_
+{
+ UINT iIdCmdFirst;
+ UINT NumIds;
+ CLSID ClassID;
+ IContextMenu *pCM;
+ struct _DynamicShellEntry_ *pNext;
+} DynamicShellEntry, *PDynamicShellEntry;
+
+typedef struct _StaticShellEntry_
+{
+ LPWSTR szVerb;
+ LPWSTR szClass;
+ struct _StaticShellEntry_ *pNext;
+} StaticShellEntry, *PStaticShellEntry;
+
+class CDefaultContextMenu :
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IContextMenu2
+{
+ private:
+ DEFCONTEXTMENU m_Dcm;
+ IDataObject *m_pDataObj;
+ LPCITEMIDLIST m_pidlFolder;
+ DWORD m_bGroupPolicyActive;
+ PDynamicShellEntry m_pDynamicEntries; /* first dynamic shell extension entry */
+ UINT m_iIdSHEFirst; /* first used id */
+ UINT m_iIdSHELast; /* last used id */
+ PStaticShellEntry m_pStaticEntries; /* first static shell extension entry */
+ UINT m_iIdSCMFirst; /* first static used id */
+ UINT m_iIdSCMLast; /* last static used id */
+
+ void AddStaticEntry(LPCWSTR pwszVerb, LPCWSTR pwszClass);
+ void AddStaticEntryForKey(HKEY hKey, LPCWSTR pwszClass);
+ void AddStaticEntryForFileClass(LPCWSTR pwszExt);
+ BOOL IsShellExtensionAlreadyLoaded(const CLSID *pclsid);
+ HRESULT LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid);
+ BOOL EnumerateDynamicContextHandlerForKey(HKEY hRootKey);
+ UINT InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast);
+ UINT BuildBackgroundContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
+ UINT AddStaticContextMenusToMenu(HMENU hMenu, UINT IndexMenu);
+ UINT BuildShellItemContextMenu(HMENU hMenu, UINT iIdCmdFirst, UINT iIdCmdLast, UINT uFlags);
+ HRESULT DoPaste(LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink);
+ HRESULT DoOpenOrExplore(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoCreateLink(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoRefresh(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoDelete(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoCopyOrCut(LPCMINVOKECOMMANDINFO lpcmi, BOOL bCopy);
+ HRESULT DoRename(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoProperties(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoFormat(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoDynamicShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
+ HRESULT DoStaticShellExtensions(LPCMINVOKECOMMANDINFO lpcmi);
+ DWORD BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry);
+ HRESULT TryToBrowse(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags);
+ HRESULT InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry);
+
+ public:
+ CDefaultContextMenu();
+ ~CDefaultContextMenu();
+ HRESULT WINAPI Initialize(const DEFCONTEXTMENU *pdcm);
+
+ // IContextMenu
+ virtual HRESULT WINAPI QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
+ virtual HRESULT WINAPI InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi);
+ virtual HRESULT WINAPI GetCommandString(UINT_PTR idCommand, UINT uFlags, UINT *lpReserved, LPSTR lpszName, UINT uMaxNameLen);
+
+ // IContextMenu2
+ virtual HRESULT WINAPI HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ BEGIN_COM_MAP(CDefaultContextMenu)
+ COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
+ COM_INTERFACE_ENTRY_IID(IID_IContextMenu2, IContextMenu2)
+ END_COM_MAP()
+};
+
+CDefaultContextMenu::CDefaultContextMenu()
+{
+ memset(&m_Dcm, 0, sizeof(m_Dcm));
+ m_pDataObj = NULL;
+ m_pidlFolder = NULL;
+ m_bGroupPolicyActive = 0;
+ m_pDynamicEntries = NULL;
+ m_iIdSHEFirst = 0;
+ m_iIdSHELast = 0;
+ m_pStaticEntries = NULL;
+ m_iIdSCMFirst = 0;
+ m_iIdSCMLast = 0;
+}
+
+CDefaultContextMenu::~CDefaultContextMenu()
+{
+ /* Free dynamic shell extension entries */
+ PDynamicShellEntry pDynamicEntry = m_pDynamicEntries, pNextDynamic;
+ while (pDynamicEntry)
+ {
+ pNextDynamic = pDynamicEntry->pNext;
+ pDynamicEntry->pCM->Release();
+ HeapFree(GetProcessHeap(), 0, pDynamicEntry);
+ pDynamicEntry = pNextDynamic;
+ }
+
+ /* Free static shell extension entries */
+ PStaticShellEntry pStaticEntry = m_pStaticEntries, pNextStatic;
+ while (pStaticEntry)
+ {
+ pNextStatic = pStaticEntry->pNext;
+ HeapFree(GetProcessHeap(), 0, pStaticEntry->szClass);
+ HeapFree(GetProcessHeap(), 0, pStaticEntry->szVerb);
+ HeapFree(GetProcessHeap(), 0, pStaticEntry);
+ pStaticEntry = pNextStatic;
+ }
+
+ if (m_pidlFolder)
+ ILFree((_ITEMIDLIST*)m_pidlFolder);
+ if (m_pDataObj)
+ m_pDataObj->Release();
+}
+
+HRESULT WINAPI CDefaultContextMenu::Initialize(const DEFCONTEXTMENU *pdcm)
+{
+ IDataObject *pDataObj;
+
+ TRACE("cidl %u\n", pdcm->cidl);
+ if (SUCCEEDED(SHCreateDataObject(pdcm->pidlFolder, pdcm->cidl, pdcm->apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
+ m_pDataObj = pDataObj;
+
+ if (!pdcm->cidl)
+ {
+ /* Init pidlFolder only if it is background context menu. See IShellExtInit::Initialize */
+ if (pdcm->pidlFolder)
+ m_pidlFolder = ILClone(pdcm->pidlFolder);
+ else
+ {
+ IPersistFolder2 *pf = NULL;
+ if (SUCCEEDED(pdcm->psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
+ {
+ if (FAILED(pf->GetCurFolder((_ITEMIDLIST**)&m_pidlFolder)))
+ ERR("GetCurFolder failed\n");
+ pf->Release();
+ }
+ }
+ TRACE("pidlFolder %p\n", m_pidlFolder);
+ }
+
+ CopyMemory(&m_Dcm, pdcm, sizeof(DEFCONTEXTMENU));
+ return S_OK;
+}
+
+void
+CDefaultContextMenu::AddStaticEntry(const WCHAR *szVerb, const WCHAR *szClass)
+{
+ PStaticShellEntry pEntry = m_pStaticEntries, pLastEntry = NULL;
+ while(pEntry)
+ {
+ if (!wcsicmp(pEntry->szVerb, szVerb))
+ {
+ /* entry already exists */
+ return;
+ }
+ pLastEntry = pEntry;
+ pEntry = pEntry->pNext;
+ }
+
+ TRACE("adding verb %s szClass %s\n", debugstr_w(szVerb), debugstr_w(szClass));
+
+ pEntry = (StaticShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(StaticShellEntry));
+ if (pEntry)
+ {
+ pEntry->pNext = NULL;
+ pEntry->szVerb = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szVerb) + 1) * sizeof(WCHAR));
+ if (pEntry->szVerb)
+ wcscpy(pEntry->szVerb, szVerb);
+ pEntry->szClass = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (wcslen(szClass) + 1) * sizeof(WCHAR));
+ if (pEntry->szClass)
+ wcscpy(pEntry->szClass, szClass);
+ }
+
+ if (!wcsicmp(szVerb, L"open"))
+ {
+ /* open verb is always inserted in front */
+ pEntry->pNext = m_pStaticEntries;
+ m_pStaticEntries = pEntry;
+ }
+ else if (pLastEntry)
+ pLastEntry->pNext = pEntry;
+ else
+ m_pStaticEntries = pEntry;
+}
+
+void
+CDefaultContextMenu::AddStaticEntryForKey(HKEY hKey, const WCHAR *pwszClass)
+{
+ WCHAR wszName[40];
+ DWORD cchName, dwIndex = 0;
+
+ TRACE("AddStaticEntryForKey %x %ls\n", hKey, pwszClass);
+
+ while(TRUE)
+ {
+ cchName = _countof(wszName);
+ if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
+ break;
+
+ AddStaticEntry(wszName, pwszClass);
+ }
+}
+
+void
+CDefaultContextMenu::AddStaticEntryForFileClass(const WCHAR * szExt)
+{
+ WCHAR szBuffer[100];
+ HKEY hKey;
+ LONG result;
+ DWORD dwBuffer;
+ UINT Length;
+ static WCHAR szShell[] = L"\\shell";
+ static WCHAR szShellAssoc[] = L"SystemFileAssociations\\";
+
+ TRACE("AddStaticEntryForFileClass entered with %s\n", debugstr_w(szExt));
+
+ Length = wcslen(szExt);
+ if (Length + (sizeof(szShell) / sizeof(WCHAR)) + 1 < sizeof(szBuffer) / sizeof(WCHAR))
+ {
+ wcscpy(szBuffer, szExt);
+ wcscpy(&szBuffer[Length], szShell);
+ result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
+ if (result == ERROR_SUCCESS)
+ {
+ szBuffer[Length] = 0;
+ AddStaticEntryForKey(hKey, szExt);
+ RegCloseKey(hKey);
+ }
+ }
+
+ dwBuffer = sizeof(szBuffer);
+ result = RegGetValueW(HKEY_CLASSES_ROOT, szExt, NULL, RRF_RT_REG_SZ, NULL, (LPBYTE)szBuffer, &dwBuffer);
+ if (result == ERROR_SUCCESS)
+ {
+ Length = wcslen(szBuffer);
+ if (Length + (sizeof(szShell) / sizeof(WCHAR)) + 1 < sizeof(szBuffer) / sizeof(WCHAR))
+ {
+ wcscpy(&szBuffer[Length], szShell);
+ TRACE("szBuffer %s\n", debugstr_w(szBuffer));
+
+ result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
+ if (result == ERROR_SUCCESS)
+ {
+ szBuffer[Length] = 0;
+ AddStaticEntryForKey(hKey, szBuffer);
+ RegCloseKey(hKey);
+ }
+ }
+ }
+
+ wcscpy(szBuffer, szShellAssoc);
+ dwBuffer = sizeof(szBuffer) - sizeof(szShellAssoc) - sizeof(WCHAR);
+ result = RegGetValueW(HKEY_CLASSES_ROOT, szExt, L"PerceivedType", RRF_RT_REG_SZ, NULL, (LPBYTE)&szBuffer[_countof(szShellAssoc) - 1], &dwBuffer);
+ if (result == ERROR_SUCCESS)
+ {
+ Length = wcslen(&szBuffer[_countof(szShellAssoc)]) + _countof(szShellAssoc);
+ wcscat(szBuffer, L"\\shell");
+ TRACE("szBuffer %s\n", debugstr_w(szBuffer));
+
+ result = RegOpenKeyExW(HKEY_CLASSES_ROOT, szBuffer, 0, KEY_READ | KEY_QUERY_VALUE, &hKey);
+ if (result == ERROR_SUCCESS)
+ {
+ szBuffer[Length] = 0;
+ AddStaticEntryForKey(hKey, szBuffer);
+ RegCloseKey(hKey);
+ }
+ }
+}
+
+static
+BOOL
+HasClipboardData()
+{
+ BOOL bRet = FALSE;
+ IDataObject *pDataObj;
+
+ if(SUCCEEDED(OleGetClipboard(&pDataObj)))
+ {
+ STGMEDIUM medium;
+ FORMATETC formatetc;
+
+ TRACE("pDataObj=%p\n", pDataObj);
+
+ /* Set the FORMATETC structure*/
+ InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_SHELLIDLIST), TYMED_HGLOBAL);
+ if(SUCCEEDED(pDataObj->GetData(&formatetc, &medium)))
+ {
+ bRet = TRUE;
+ ReleaseStgMedium(&medium);
+ }
+
+ pDataObj->Release();
+ }
+
+ return bRet;
+}
+
+static
+VOID
+DisablePasteOptions(HMENU hMenu)
+{
+ MENUITEMINFOW mii;
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE;
+ mii.fState = MFS_DISABLED;
+
+ TRACE("result %d\n", SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERT, FALSE, &mii));
+ TRACE("result %d\n", SetMenuItemInfoW(hMenu, FCIDM_SHVIEW_INSERTLINK, FALSE, &mii));
+}
+
+BOOL
+CDefaultContextMenu::IsShellExtensionAlreadyLoaded(const CLSID *pclsid)
+{
+ PDynamicShellEntry pEntry = m_pDynamicEntries;
+
+ while (pEntry)
+ {
+ if (!memcmp(&pEntry->ClassID, pclsid, sizeof(CLSID)))
+ return TRUE;
+ pEntry = pEntry->pNext;
+ }
+
+ return FALSE;
+}
+
+HRESULT
+CDefaultContextMenu::LoadDynamicContextMenuHandler(HKEY hKey, const CLSID *pclsid)
+{
+ HRESULT hr;
+
+ TRACE("LoadDynamicContextMenuHandler entered with This %p hKey %p pclsid %s\n", this, hKey, wine_dbgstr_guid(pclsid));
+
+ if (IsShellExtensionAlreadyLoaded(pclsid))
+ return S_OK;
+
+ IContextMenu *pcm;
+ hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
+ if (hr != S_OK)
+ {
+ ERR("SHCoCreateInstance failed %x\n", GetLastError());
+ return hr;
+ }
+
+ IShellExtInit *pExtInit;
+ hr = pcm->QueryInterface(IID_PPV_ARG(IShellExtInit, &pExtInit));
+ if (hr != S_OK)
+ {
+ ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
+ pcm->Release();
+ return hr;
+ }
+
+ hr = pExtInit->Initialize(m_pidlFolder, m_pDataObj, hKey);
+ pExtInit->Release();
+ if (hr != S_OK)
+ {
+ TRACE("Failed to initialize shell extension error %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
+ pcm->Release();
+ return hr;
+ }
+
+ PDynamicShellEntry pEntry = (DynamicShellEntry *)HeapAlloc(GetProcessHeap(), 0, sizeof(DynamicShellEntry));
+ if (!pEntry)
+ {
+ pcm->Release();
+ return E_OUTOFMEMORY;
+ }
+
+ pEntry->iIdCmdFirst = 0;
+ pEntry->pNext = NULL;
+ pEntry->NumIds = 0;
+ pEntry->pCM = pcm;
+ memcpy(&pEntry->ClassID, pclsid, sizeof(CLSID));
+
+ if (m_pDynamicEntries)
+ {
+ PDynamicShellEntry pLastEntry = m_pDynamicEntries;
+
+ while (pLastEntry->pNext)
+ pLastEntry = pLastEntry->pNext;
+
+ pLastEntry->pNext = pEntry;
+ }
+ else
+ m_pDynamicEntries = pEntry;
+
+ return S_OK;
+}
+
+BOOL
+CDefaultContextMenu::EnumerateDynamicContextHandlerForKey(HKEY hRootKey)
+{
+
+ WCHAR wszName[MAX_PATH], wszBuf[MAX_PATH], *pwszClsid;
+ DWORD cchName;
+ HRESULT hr;
+ HKEY hKey;
+
+ if (RegOpenKeyExW(hRootKey, L"shellex\\ContextMenuHandlers", 0, KEY_READ, &hKey) != ERROR_SUCCESS)
+ {
+ TRACE("RegOpenKeyExW failed\n");
+ return FALSE;
+ }
+
+ DWORD dwIndex = 0;
+ while (TRUE)
+ {
+ cchName = _countof(wszName);
+ if (RegEnumKeyExW(hKey, dwIndex++, wszName, &cchName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
+ break;
+
+ /* Key name or key value is CLSID */
+ CLSID clsid;
+ hr = CLSIDFromString(wszName, &clsid);
+ if (hr == S_OK)
+ pwszClsid = wszName;
+ else
+ {
+ DWORD cchBuf = _countof(wszBuf);
+ if (RegGetValueW(hKey, wszName, NULL, RRF_RT_REG_SZ, NULL, wszBuf, &cchBuf) == ERROR_SUCCESS)
+ hr = CLSIDFromString(wszBuf, &clsid);
+ pwszClsid = wszBuf;
+ }
+ if (SUCCEEDED(hr))
+ {
+ if (m_bGroupPolicyActive)
+ {
+ if (RegGetValueW(HKEY_LOCAL_MACHINE,
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
+ pwszClsid,
+ RRF_RT_REG_SZ,
+ NULL,
+ NULL,
+ NULL) == ERROR_SUCCESS)
+ {
+ LoadDynamicContextMenuHandler(hKey, &clsid);
+ }
+ }
+ else
+ LoadDynamicContextMenuHandler(hKey, &clsid);
+ }
+ }
+
+ RegCloseKey(hKey);
+ return TRUE;
+}
+
+UINT
+CDefaultContextMenu::InsertMenuItemsOfDynamicContextMenuExtension(HMENU hMenu, UINT IndexMenu, UINT idCmdFirst, UINT idCmdLast)
+{
+ if (!m_pDynamicEntries)
+ {
+ m_iIdSHEFirst = 0;
+ m_iIdSHELast = 0;
+ return IndexMenu;
+ }
+
+ PDynamicShellEntry pEntry = m_pDynamicEntries;
+ idCmdFirst = 0x5000;
+ idCmdLast = 0x6000;
+ m_iIdSHEFirst = idCmdFirst;
+ do
+ {
+ HRESULT hr = pEntry->pCM->QueryContextMenu(hMenu, IndexMenu++, idCmdFirst, idCmdLast, CMF_NORMAL);
+ if (SUCCEEDED(hr))
+ {
+ pEntry->iIdCmdFirst = idCmdFirst;
+ pEntry->NumIds = LOWORD(hr);
+ IndexMenu += pEntry->NumIds;
+ idCmdFirst += pEntry->NumIds + 0x10;
+ }
+ TRACE("pEntry %p hr %x contextmenu %p cmdfirst %x num ids %x\n", pEntry, hr, pEntry->pCM, pEntry->iIdCmdFirst, pEntry->NumIds);
+ pEntry = pEntry->pNext;
+ } while (pEntry);
+
+ m_iIdSHELast = idCmdFirst;
+ TRACE("SH_LoadContextMenuHandlers first %x last %x\n", m_iIdSHEFirst, m_iIdSHELast);
+ return IndexMenu;
+}
+
+UINT
+CDefaultContextMenu::BuildBackgroundContextMenu(
+ HMENU hMenu,
+ UINT iIdCmdFirst,
+ UINT iIdCmdLast,
+ UINT uFlags)
+{
+ UINT IndexMenu = 0;
+ HMENU hSubMenu;
+
+ TRACE("BuildBackgroundContextMenu entered\n");
+
+ if (!_ILIsDesktop(m_pidlFolder))
+ {
+ WCHAR wszBuf[MAX_PATH];
+
+ /* view option is only available in browsing mode */
+ hSubMenu = LoadMenuW(shell32_hInstance, L"MENU_001");
+ if (hSubMenu && LoadStringW(shell32_hInstance, FCIDM_SHVIEW_VIEW, wszBuf, _countof(wszBuf)))
+ {
+ TRACE("wszBuf %s\n", debugstr_w(wszBuf));
+
+ MENUITEMINFOW mii;
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_SUBMENU | MIIM_ID;
+ mii.fType = MFT_STRING;
+ mii.wID = iIdCmdFirst++;
+ mii.dwTypeData = wszBuf;
+ mii.cch = wcslen(mii.dwTypeData);
+ mii.fState = MFS_ENABLED;
+ mii.hSubMenu = hSubMenu;
+ InsertMenuItemW(hMenu, IndexMenu++, TRUE, &mii);
+ DestroyMenu(hSubMenu);
+ }
+ }
+
+ hSubMenu = LoadMenuW(shell32_hInstance, L"MENU_002");
+ if (hSubMenu)
+ {
+ /* merge general background context menu in */
+ iIdCmdFirst = Shell_MergeMenus(hMenu, GetSubMenu(hSubMenu, 0), IndexMenu, 0, 0xFFFF, MM_DONTREMOVESEPS | MM_SUBMENUSHAVEIDS) + 1;
+ DestroyMenu(hSubMenu);
+ }
+
+ if (!HasClipboardData())
+ {
+ TRACE("disabling paste options\n");
+ DisablePasteOptions(hMenu);
+ }
+
+ /* Directory is progid of filesystem folders only */
+ LPITEMIDLIST pidlFolderLast = ILFindLastID(m_pidlFolder);
+ if (_ILIsDesktop(pidlFolderLast) || _ILIsDrive(pidlFolderLast) || _ILIsFolder(pidlFolderLast))
+ {
+ /* Load context menu handlers */
+ TRACE("Add background handlers: %p\n", m_pidlFolder);
+ HKEY hKey;
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Directory\\Background", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+
+ if (InsertMenuItemsOfDynamicContextMenuExtension(hMenu, GetMenuItemCount(hMenu) - 1, iIdCmdFirst, iIdCmdLast))
+ {
+ /* seperate dynamic context menu items */
+ _InsertMenuItemW(hMenu, GetMenuItemCount(hMenu) - 1, TRUE, -1, MFT_SEPARATOR, NULL, MFS_ENABLED);
+ }
+ }
+
+ return iIdCmdLast;
+}
+
+UINT
+CDefaultContextMenu::AddStaticContextMenusToMenu(
+ HMENU hMenu,
+ UINT IndexMenu)
+{
+ MENUITEMINFOW mii;
+ UINT idResource;
+ WCHAR wszVerb[40];
+ UINT fState;
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA;
+ mii.fType = MFT_STRING;
+ mii.wID = 0x4000;
+ mii.dwTypeData = NULL;
+ m_iIdSCMFirst = mii.wID;
+
+ PStaticShellEntry pEntry = m_pStaticEntries;
+
+ while (pEntry)
+ {
+ fState = MFS_ENABLED;
+ mii.dwTypeData = NULL;
+
++ /* set first entry as default */
++ if (pEntry == m_pStaticEntries)
++ fState |= MFS_DEFAULT;
++
+ if (!wcsicmp(pEntry->szVerb, L"open"))
+ {
++ /* override default when open verb is found */
+ fState |= MFS_DEFAULT;
+ idResource = IDS_OPEN_VERB;
+ }
+ else if (!wcsicmp(pEntry->szVerb, L"explore"))
+ idResource = IDS_EXPLORE_VERB;
+ else if (!wcsicmp(pEntry->szVerb, L"runas"))
+ idResource = IDS_RUNAS_VERB;
+ else if (!wcsicmp(pEntry->szVerb, L"edit"))
+ idResource = IDS_EDIT_VERB;
+ else if (!wcsicmp(pEntry->szVerb, L"find"))
+ idResource = IDS_FIND_VERB;
+ else if (!wcsicmp(pEntry->szVerb, L"print"))
+ idResource = IDS_PRINT_VERB;
+ else if (!wcsicmp(pEntry->szVerb, L"printto"))
+ {
+ pEntry = pEntry->pNext;
+ continue;
+ }
+ else
+ idResource = 0;
+
+ /* By default use verb for menu item name */
+ mii.dwTypeData = pEntry->szVerb;
+
+ if (idResource > 0)
+ {
+ if (LoadStringW(shell32_hInstance, idResource, wszVerb, _countof(wszVerb)))
+ mii.dwTypeData = wszVerb; /* use translated verb */
+ else
+ ERR("Failed to load string\n");
+ }
+ else
+ {
+ WCHAR wszKey[256];
+ HRESULT hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"%s\\shell\\%s", pEntry->szClass, pEntry->szVerb);
+
+ if (SUCCEEDED(hr))
+ {
+ DWORD cbVerb = sizeof(wszVerb);
+
+ if (RegGetValueW(HKEY_CLASSES_ROOT, wszKey, NULL, RRF_RT_REG_SZ, NULL, wszVerb, &cbVerb) == ERROR_SUCCESS)
+ mii.dwTypeData = wszVerb; /* use description for the menu entry */
+ }
+ }
+
+ mii.cch = wcslen(mii.dwTypeData);
+ mii.fState = fState;
+ InsertMenuItemW(hMenu, IndexMenu++, TRUE, &mii);
+
+ mii.wID++;
+ pEntry = pEntry->pNext;
+ }
+
+ m_iIdSCMLast = mii.wID - 1;
+ return IndexMenu;
+}
+
+void WINAPI _InsertMenuItemW(
+ HMENU hMenu,
+ UINT indexMenu,
+ BOOL fByPosition,
+ UINT wID,
+ UINT fType,
+ LPCWSTR dwTypeData,
+ UINT fState)
+{
+ MENUITEMINFOW mii;
+ WCHAR wszText[100];
+
+ ZeroMemory(&mii, sizeof(mii));
+ mii.cbSize = sizeof(mii);
+ if (fType == MFT_SEPARATOR)
+ mii.fMask = MIIM_ID | MIIM_TYPE;
+ else if (fType == MFT_STRING)
+ {
+ mii.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE;
+ if ((ULONG_PTR)HIWORD((ULONG_PTR)dwTypeData) == 0)
+ {
+ if (LoadStringW(shell32_hInstance, LOWORD((ULONG_PTR)dwTypeData), wszText, _countof(wszText)))
+ mii.dwTypeData = wszText;
+ else
+ {
+ ERR("failed to load string %p\n", dwTypeData);
+ return;
+ }
+ }
+ else
+ mii.dwTypeData = (LPWSTR)dwTypeData;
+ mii.fState = fState;
+ }
+
+ mii.wID = wID;
+ mii.fType = fType;
+ InsertMenuItemW(hMenu, indexMenu, fByPosition, &mii);
+}
+
+UINT
+CDefaultContextMenu::BuildShellItemContextMenu(
+ HMENU hMenu,
+ UINT iIdCmdFirst,
+ UINT iIdCmdLast,
+ UINT uFlags)
+{
+ HKEY hKey;
+ HRESULT hr;
+
+ TRACE("BuildShellItemContextMenu entered\n");
+ ASSERT(m_Dcm.cidl >= 1);
+
+ STRRET strFile;
+ hr = m_Dcm.psf->GetDisplayNameOf(m_Dcm.apidl[0], SHGDN_FORPARSING, &strFile);
+ if (hr == S_OK)
+ {
+ WCHAR wszPath[MAX_PATH];
+ hr = StrRetToBufW(&strFile, m_Dcm.apidl[0], wszPath, _countof(wszPath));
+ if (hr == S_OK)
+ {
+ LPCWSTR pwszExt = PathFindExtensionW(wszPath);
+ if (pwszExt[0])
+ {
+ /* enumerate dynamic/static for a given file class */
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, pwszExt, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ /* add static verbs */
+ AddStaticEntryForFileClass(pwszExt);
+
+ /* load dynamic extensions from file extension key */
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+
+ WCHAR wszTemp[40];
+ DWORD dwSize = sizeof(wszTemp);
+ if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszTemp, &dwSize) == ERROR_SUCCESS)
+ {
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszTemp, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ /* add static verbs from progid key */
+ AddStaticEntryForFileClass(wszTemp);
+
+ /* load dynamic extensions from progid key */
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+ }
+ }
+
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"*", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ /* load default extensions */
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+ }
+ }
+ else
+ ERR("GetDisplayNameOf failed: %x\n", hr);
+
+ GUID *pGuid = _ILGetGUIDPointer(m_Dcm.apidl[0]);
+ if (pGuid)
+ {
+ LPOLESTR pwszCLSID;
+ WCHAR buffer[60];
+
+ wcscpy(buffer, L"CLSID\\");
+ hr = StringFromCLSID(*pGuid, &pwszCLSID);
+ if (hr == S_OK)
+ {
+ wcscpy(&buffer[6], pwszCLSID);
+ TRACE("buffer %s\n", debugstr_w(buffer));
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, buffer, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ EnumerateDynamicContextHandlerForKey(hKey);
+ AddStaticEntryForFileClass(buffer);
+ RegCloseKey(hKey);
+ }
+ CoTaskMemFree(pwszCLSID);
+ }
+ }
+
+ if (_ILIsDrive(m_Dcm.apidl[0]))
+ {
+ AddStaticEntryForFileClass(L"Drive");
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Drive", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+
+ }
+
+ /* add static actions */
+ SFGAOF rfg = SFGAO_BROWSABLE | SFGAO_CANCOPY | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_HASPROPSHEET | SFGAO_FILESYSTEM | SFGAO_FOLDER;
+ hr = m_Dcm.psf->GetAttributesOf(m_Dcm.cidl, m_Dcm.apidl, &rfg);
+ if (FAILED(hr))
+ {
+ ERR("GetAttributesOf failed: %x\n", hr);
+ rfg = 0;
+ }
+
+ if (rfg & SFGAO_FOLDER)
+ {
+ /* add the default verbs open / explore */
+ AddStaticEntryForFileClass(L"Folder");
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Folder", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+
+ /* Directory is only loaded for real filesystem directories */
+ if (_ILIsFolder(m_Dcm.apidl[0]))
+ {
+ AddStaticEntryForFileClass(L"Directory");
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Directory", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+ }
+ }
+
+ /* AllFilesystemObjects class is loaded only for files and directories */
+ if (_ILIsFolder(m_Dcm.apidl[0]) || _ILIsValue(m_Dcm.apidl[0]))
+ {
+ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, L"AllFilesystemObjects", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
+ {
+ /* sendto service is registered here */
+ EnumerateDynamicContextHandlerForKey(hKey);
+ RegCloseKey(hKey);
+ }
+ }
+
+ /* add static context menu handlers */
+ UINT IndexMenu = AddStaticContextMenusToMenu(hMenu, 0);
+
+ /* now process dynamic context menu handlers */
+ BOOL bAddSep = FALSE;
+ IndexMenu = InsertMenuItemsOfDynamicContextMenuExtension(hMenu, IndexMenu, iIdCmdFirst, iIdCmdLast);
+ TRACE("IndexMenu %d\n", IndexMenu);
+
+ if (_ILIsDrive(m_Dcm.apidl[0]))
+ {
+ /* The 'Format' option must be always available,
+ * thus it is not registered as a static shell extension */
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0x7ABC, MFT_STRING, MAKEINTRESOURCEW(IDS_FORMATDRIVE), MFS_ENABLED);
+ bAddSep = TRUE;
+ }
+
+ BOOL bClipboardData = (HasClipboardData() && (rfg & SFGAO_FILESYSTEM));
+ if (rfg & (SFGAO_CANCOPY | SFGAO_CANMOVE) || bClipboardData)
+ {
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ if (rfg & SFGAO_CANMOVE)
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CUT, MFT_STRING, MAKEINTRESOURCEW(IDS_CUT), MFS_ENABLED);
+ if (rfg & SFGAO_CANCOPY)
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, MAKEINTRESOURCEW(IDS_COPY), MFS_ENABLED);
+ if (bClipboardData)
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_INSERT, MFT_STRING, MAKEINTRESOURCEW(IDS_INSERT), MFS_ENABLED);
+
+ bAddSep = TRUE;
+ }
+
+ if (rfg & SFGAO_CANLINK)
+ {
+ bAddSep = FALSE;
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_CREATELINK, MFT_STRING, MAKEINTRESOURCEW(IDS_CREATELINK), MFS_ENABLED);
+ }
+
+ if (rfg & SFGAO_CANDELETE)
+ {
+ if (bAddSep)
+ {
+ bAddSep = FALSE;
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ }
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_DELETE, MFT_STRING, MAKEINTRESOURCEW(IDS_DELETE), MFS_ENABLED);
+ }
+
+ if (rfg & SFGAO_CANRENAME)
+ {
+ if (bAddSep)
+ {
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ }
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, MAKEINTRESOURCEW(IDS_RENAME), MFS_ENABLED);
+ bAddSep = TRUE;
+ }
+
+ if (rfg & SFGAO_HASPROPSHEET)
+ {
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0);
+ _InsertMenuItemW(hMenu, IndexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
+ }
+
+ return iIdCmdLast;
+}
+
+HRESULT
+WINAPI
+CDefaultContextMenu::QueryContextMenu(
+ HMENU hMenu,
+ UINT IndexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags)
+{
+ if (m_Dcm.cidl)
+ idCmdFirst = BuildShellItemContextMenu(hMenu, idCmdFirst, idCmdLast, uFlags);
+ else
+ idCmdFirst = BuildBackgroundContextMenu(hMenu, idCmdFirst, idCmdLast, uFlags);
+
+ return S_OK;
+}
+
+static
+HRESULT
+NotifyShellViewWindow(LPCMINVOKECOMMANDINFO lpcmi, BOOL bRefresh)
+{
+ /* Note: CWM_GETISHELLBROWSER returns not referenced object */
+ LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
+ if (!lpSB)
+ return E_FAIL;
+
+ LPSHELLVIEW lpSV = NULL;
+ if (FAILED(lpSB->QueryActiveShellView(&lpSV)))
+ return E_FAIL;
+
+ HWND hwndSV = NULL;
+ if (SUCCEEDED(lpSV->GetWindow(&hwndSV)))
+ SendMessageW(hwndSV, WM_COMMAND, MAKEWPARAM(LOWORD(lpcmi->lpVerb), 0), 0);
+
+ lpSV->Release();
+ return S_OK;
+}
+
+HRESULT
+CDefaultContextMenu::DoRefresh(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ CComPtr<IPersistFolder2> ppf2 = NULL;
+ LPITEMIDLIST pidl;
+ HRESULT hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
+ if (SUCCEEDED(hr))
+ {
+ hr = ppf2->GetCurFolder(&pidl);
+ if (SUCCEEDED(hr))
+ {
+ SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST, pidl, NULL);
+ ILFree(pidl);
+ }
+ ppf2->Release();
+ }
+ return hr;
+}
+
+HRESULT
+CDefaultContextMenu::DoPaste(
+ LPCMINVOKECOMMANDINFO lpcmi, BOOL bLink)
+{
+ HRESULT hr;
+
+ CComPtr<IDataObject> pda;
+ hr = OleGetClipboard(&pda);
+ if (FAILED(hr))
+ return hr;
+
+ CComPtr<IShellFolder> psfDesktop;
+ CComPtr<IShellFolder> psfTarget = NULL;
+
+ hr = SHGetDesktopFolder(&psfDesktop);
+ if (FAILED(hr))
+ return hr;
+
+ /* Find target folder */
+ if (m_Dcm.cidl)
+ {
+ hr = m_Dcm.psf->BindToObject(m_Dcm.apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
+ }
+ else
+ {
+ CComPtr<IPersistFolder2> ppf2 = NULL;
+ LPITEMIDLIST pidl;
+
+ /* cidl is zero due to explorer view */
+ hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
+ if (SUCCEEDED(hr))
+ {
+ hr = ppf2->GetCurFolder(&pidl);
+ if (SUCCEEDED(hr))
+ {
+ if (_ILIsDesktop(pidl))
+ {
+ /* use desktop shellfolder */
+ psfTarget = psfDesktop;
+ }
+ else
+ {
+ /* retrieve target desktop folder */
+ hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
+ }
+ TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget.p, _ILIsDesktop(pidl));
+ ILFree(pidl);
+ }
+ }
+ }
+
+ if (FAILED(hr))
+ {
+ ERR("no IShellFolder\n");
+ return hr;
+ }
+
+ FORMATETC formatetc2;
+ STGMEDIUM medium2;
+ InitFormatEtc(formatetc2, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
+
+ DWORD dwKey= 0;
+
+ if (SUCCEEDED(pda->GetData(&formatetc2, &medium2)))
+ {
+ DWORD * pdwFlag = (DWORD*)GlobalLock(medium2.hGlobal);
+ if (pdwFlag)
+ {
+ if (*pdwFlag == DROPEFFECT_COPY)
+ dwKey = MK_CONTROL;
+ else
+ dwKey = MK_SHIFT;
+ }
+ else {
+ ERR("No drop effect obtained");
+ }
+ GlobalUnlock(medium2.hGlobal);
+ }
+
+ if (bLink)
+ {
+ dwKey = MK_CONTROL|MK_SHIFT;
+ }
+
+ IDropTarget *pdrop;
+ hr = psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pdrop));
+ if (FAILED(hr))
+ {
+ ERR("Error getting IDropTarget interface\n");
+ return hr;
+ }
+
+ SHSimulateDrop(pdrop, pda, dwKey, NULL, NULL);
+
+ TRACE("CP result %x\n", hr);
+ return S_OK;
+}
+
+HRESULT
+CDefaultContextMenu::DoOpenOrExplore(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ UNIMPLEMENTED;
+ return E_FAIL;
+}
+
+HRESULT
+CDefaultContextMenu::DoCreateLink(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ LPDATAOBJECT pDataObj;
+ IDropTarget *pDT;
+ HRESULT hr;
+ CComPtr<IPersistFolder2> ppf2 = NULL;
+ LPITEMIDLIST pidl;
+ CComPtr<IShellFolder> psfDesktop;
+ CComPtr<IShellFolder> psfTarget = NULL;
+
+ hr = SHGetDesktopFolder(&psfDesktop);
+ if (FAILED(hr))
+ return hr;
+
+ if (SUCCEEDED(hr = SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
+ {
+ hr = m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &ppf2));
+ if (SUCCEEDED(hr))
+ {
+ hr = ppf2->GetCurFolder(&pidl);
+ if (SUCCEEDED(hr))
+ {
+ if (_ILIsDesktop(pidl))
+ {
+ /* use desktop shellfolder */
+ psfTarget = psfDesktop;
+ }
+ else
+ {
+ /* retrieve target desktop folder */
+ hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTarget));
+ }
+ TRACE("psfTarget %x %p, Desktop %u\n", hr, psfTarget.p, _ILIsDesktop(pidl));
+ ILFree(pidl);
+ }
+ }
+
+ }
+
+ if (FAILED(hr))
+ {
+ ERR("no IShellFolder\n");
+ return hr;
+ }
+
+ psfTarget->QueryInterface(IID_PPV_ARG(IDropTarget, &pDT));
+ if (FAILED(hr))
+ {
+ ERR("no IDropTarget Interface\n");
+ return hr;
+ }
+ SHSimulateDrop(pDT, pDataObj, MK_CONTROL|MK_SHIFT, NULL, NULL);
+
+ return S_OK;
+}
+
+HRESULT CDefaultContextMenu::DoDelete(LPCMINVOKECOMMANDINFO lpcmi) {
+ TRACE("(%p) Deleting\n", this);
+
+ LPDATAOBJECT pDataObj;
+
+ if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
+ {
+ pDataObj->AddRef();
+ SHCreateThread(DoDeleteThreadProc, pDataObj, NULL, NULL);
+ pDataObj->Release();
+ }
+ else
+ return E_FAIL;
+ return S_OK;
+
+}
+
+HRESULT
+CDefaultContextMenu::DoCopyOrCut(
+ LPCMINVOKECOMMANDINFO lpcmi,
+ BOOL bCopy)
+{
+ LPDATAOBJECT pDataObj;
+ HRESULT hr;
+
+ if (SUCCEEDED(SHCreateDataObject(m_Dcm.pidlFolder, m_Dcm.cidl, m_Dcm.apidl, NULL, IID_PPV_ARG(IDataObject, &pDataObj))))
+ {
+ if (!bCopy)
+ {
+ FORMATETC formatetc;
+ STGMEDIUM medium;
+ InitFormatEtc(formatetc, RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECT), TYMED_HGLOBAL);
+ pDataObj->GetData(&formatetc, &medium);
+ DWORD * pdwFlag = (DWORD*)GlobalLock(medium.hGlobal);
+ if (pdwFlag)
+ *pdwFlag = DROPEFFECT_MOVE;
+ GlobalUnlock(medium.hGlobal);
+ pDataObj->SetData(&formatetc, &medium, TRUE);
+ }
+
+ hr = OleSetClipboard(pDataObj);
+ pDataObj->Release();
+ return hr;
+ }
+
+ /* Note: CWM_GETISHELLBROWSER returns not referenced object */
+ LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
+ if (!lpSB)
+ {
+ ERR("failed to get shellbrowser\n");
+ return E_FAIL;
+ }
+
+ LPSHELLVIEW lpSV;
+ hr = lpSB->QueryActiveShellView(&lpSV);
+ if (FAILED(hr))
+ {
+ ERR("failed to query the active shellview\n");
+ return hr;
+ }
+
+ hr = lpSV->GetItemObject(SVGIO_SELECTION, IID_PPV_ARG(IDataObject, &pDataObj));
+ if (SUCCEEDED(hr))
+ {
+ hr = OleSetClipboard(pDataObj);
+ if (FAILED(hr))
+ ERR("OleSetClipboard failed");
+ pDataObj->Release();
+ } else
+ ERR("failed to get item object\n");
+
+ lpSV->Release();
+ return hr;
+}
+
+HRESULT
+CDefaultContextMenu::DoRename(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ /* get the active IShellView. Note: CWM_GETISHELLBROWSER returns not referenced object */
+ LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
+ if (!lpSB)
+ {
+ ERR("CWM_GETISHELLBROWSER failed\n");
+ return E_FAIL;
+ }
+
+ /* is the treeview focused */
+ HWND hwnd;
+ if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwnd)))
+ {
+ HTREEITEM hItem = TreeView_GetSelection(hwnd);
+ if (hItem)
+ (void)TreeView_EditLabel(hwnd, hItem);
+ }
+
+ LPSHELLVIEW lpSV;
+ HRESULT hr = lpSB->QueryActiveShellView(&lpSV);
+ if (FAILED(hr))
+ {
+ ERR("CWM_GETISHELLBROWSER failed\n");
+ return hr;
+ }
+
+ lpSV->SelectItem(m_Dcm.apidl[0],
+ SVSI_DESELECTOTHERS | SVSI_EDIT | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT);
+ lpSV->Release();
+ return S_OK;
+}
+
+HRESULT
+CDefaultContextMenu::DoProperties(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ HRESULT hr = S_OK;
+ const ITEMIDLIST *pidlParent = m_Dcm.pidlFolder, *pidlChild;
+
+ if (!pidlParent)
+ {
+ IPersistFolder2 *pf;
+
+ /* pidlFolder is optional */
+ if (SUCCEEDED(m_Dcm.psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &pf))))
+ {
+ pf->GetCurFolder((_ITEMIDLIST**)&pidlParent);
+ pf->Release();
+ }
+ }
+
+ if (m_Dcm.cidl > 0)
+ pidlChild = m_Dcm.apidl[0];
+ else
+ {
+ /* Set pidlChild to last pidl of current folder */
+ if (pidlParent == m_Dcm.pidlFolder)
+ pidlParent = (ITEMIDLIST*)ILClone(pidlParent);
+
+ pidlChild = (ITEMIDLIST*)ILClone(ILFindLastID(pidlParent));
+ ILRemoveLastID((ITEMIDLIST*)pidlParent);
+ }
+
+ if (_ILIsMyComputer(pidlChild))
+ {
+ if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl", NULL, NULL, SW_SHOWNORMAL))
+ hr = E_FAIL;
+ }
+ else if (_ILIsDesktop(pidlChild))
+ {
+ if (32 >= (UINT)ShellExecuteW(lpcmi->hwnd, L"open", L"rundll32.exe shell32.dll,Control_RunDLL desk.cpl", NULL, NULL, SW_SHOWNORMAL))
+ hr = E_FAIL;
+ }
+ else if (_ILIsDrive(pidlChild))
+ {
+ WCHAR wszBuf[MAX_PATH];
+ ILGetDisplayName(pidlChild, wszBuf);
+ if (!SH_ShowDriveProperties(wszBuf, pidlParent, &pidlChild))
+ hr = E_FAIL;
+ }
+ else if (_ILIsNetHood(pidlChild))
+ {
+ // FIXME path!
+ if (32 >= (UINT)ShellExecuteW(NULL, L"open", L"explorer.exe",
+ L"::{7007ACC7-3202-11D1-AAD2-00805FC1270E}",
+ NULL, SW_SHOWDEFAULT))
+ hr = E_FAIL;
+ }
+ else if (_ILIsBitBucket(pidlChild))
+ {
+ /* FIXME: detect the drive path of bitbucket if appropiate */
+ if(!SH_ShowRecycleBinProperties(L'C'))
+ hr = E_FAIL;
+ }
+ else
+ {
+ if (m_Dcm.cidl > 1)
+ WARN("SHMultiFileProperties is not yet implemented\n");
+
+ STRRET strFile;
+ hr = m_Dcm.psf->GetDisplayNameOf(pidlChild, SHGDN_FORPARSING, &strFile);
+ if (SUCCEEDED(hr))
+ {
+ WCHAR wszBuf[MAX_PATH];
+ hr = StrRetToBufW(&strFile, pidlChild, wszBuf, _countof(wszBuf));
+ if (SUCCEEDED(hr))
+ hr = SH_ShowPropertiesDialog(wszBuf, pidlParent, &pidlChild);
+ else
+ ERR("StrRetToBufW failed\n");
+ }
+ else
+ ERR("IShellFolder_GetDisplayNameOf failed for apidl\n");
+ }
+
+ /* Free allocated PIDLs */
+ if (pidlParent != m_Dcm.pidlFolder)
+ ILFree((ITEMIDLIST*)pidlParent);
+ if (m_Dcm.cidl < 1 || pidlChild != m_Dcm.apidl[0])
+ ILFree((ITEMIDLIST*)pidlChild);
+
+ return hr;
+}
+
+HRESULT
+CDefaultContextMenu::DoFormat(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ char szDrive[8] = {0};
+
+ if (!_ILGetDrive(m_Dcm.apidl[0], szDrive, sizeof(szDrive)))
+ {
+ ERR("pidl is not a drive\n");
+ return E_FAIL;
+ }
+
+ SHFormatDrive(lpcmi->hwnd, szDrive[0] - 'A', SHFMT_ID_DEFAULT, 0);
+ return S_OK;
+}
+
+HRESULT
+CDefaultContextMenu::DoDynamicShellExtensions(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ UINT idCmd = LOWORD(lpcmi->lpVerb);
+ PDynamicShellEntry pEntry = m_pDynamicEntries;
+
+ TRACE("verb %p first %x last %x", lpcmi->lpVerb, m_iIdSHEFirst, m_iIdSHELast);
+
+ while(pEntry && idCmd > pEntry->iIdCmdFirst + pEntry->NumIds)
+ pEntry = pEntry->pNext;
+
+ if (!pEntry)
+ return E_FAIL;
+
+ if (idCmd >= pEntry->iIdCmdFirst && idCmd <= pEntry->iIdCmdFirst + pEntry->NumIds)
+ {
+ /* invoke the dynamic context menu */
+ lpcmi->lpVerb = MAKEINTRESOURCEA(idCmd - pEntry->iIdCmdFirst);
+ return pEntry->pCM->InvokeCommand(lpcmi);
+ }
+
+ return E_FAIL;
+}
+
+DWORD
+CDefaultContextMenu::BrowserFlagsFromVerb(LPCMINVOKECOMMANDINFO lpcmi, PStaticShellEntry pEntry)
+{
+ LPSHELLBROWSER lpSB;
+ HWND hwndTree;
+ LPCWSTR FlagsName;
+ WCHAR wszKey[256];
+ HRESULT hr;
+ DWORD wFlags;
+ DWORD cbVerb;
+
+ /* Get a pointer to the shell browser */
+ lpSB = (LPSHELLBROWSER)SendMessageA(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
+ if (lpSB == NULL)
+ return 0;
+
+ /* See if we are in Explore or Browse mode. If the browser's tree is present, we are in Explore mode.*/
+ if (SUCCEEDED(lpSB->GetControlWindow(FCW_TREE, &hwndTree)) && hwndTree)
+ FlagsName = L"ExplorerFlags";
+ else
+ FlagsName = L"BrowserFlags";
+
+ /* Try to get the flag from the verb */
+ hr = StringCbPrintfW(wszKey, sizeof(wszKey), L"%s\\shell\\%s", pEntry->szClass, pEntry->szVerb);
+ if (!SUCCEEDED(hr))
+ return 0;
+
+ cbVerb = sizeof(wFlags);
+ if (RegGetValueW(HKEY_CLASSES_ROOT, wszKey, FlagsName, RRF_RT_REG_DWORD, NULL, &wFlags, &cbVerb) == ERROR_SUCCESS)
+ {
+ return wFlags;
+ }
+
+ return 0;
+}
+
+HRESULT
+CDefaultContextMenu::TryToBrowse(
+ LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, DWORD wFlags)
+{
+ LPSHELLBROWSER lpSB = (LPSHELLBROWSER)SendMessageW(lpcmi->hwnd, CWM_GETISHELLBROWSER, 0, 0);
+ HRESULT hr;
+
+ if (lpSB == NULL)
+ return E_FAIL;
+
+ hr = lpSB->BrowseObject(ILCombine(m_Dcm.pidlFolder, pidl), wFlags);
+
+ return hr;
+}
+
+HRESULT
+CDefaultContextMenu::InvokePidl(LPCMINVOKECOMMANDINFO lpcmi, LPCITEMIDLIST pidl, PStaticShellEntry pEntry)
+{
+ LPITEMIDLIST pidlFull = ILCombine(m_Dcm.pidlFolder, pidl);
+ if (pidlFull == NULL)
+ {
+ return E_FAIL;
+ }
+
+ WCHAR wszPath[MAX_PATH];
+ BOOL bHasPath = SHGetPathFromIDListW(pidlFull, wszPath);
+
+ WCHAR wszDir[MAX_PATH];
+ if(bHasPath)
+ {
+ wcscpy(wszDir, wszPath);
+ PathRemoveFileSpec(wszDir);
+ }
+ else
+ {
+ SHGetPathFromIDListW(m_Dcm.pidlFolder, wszDir);
+ }
+
+ HKEY hkeyClass;
+ RegOpenKeyExW(HKEY_CLASSES_ROOT, pEntry->szClass, 0, KEY_READ, &hkeyClass);
+
+ SHELLEXECUTEINFOW sei;
+ ZeroMemory(&sei, sizeof(sei));
+ sei.cbSize = sizeof(sei);
+ sei.hwnd = lpcmi->hwnd;
+ sei.nShow = SW_SHOWNORMAL;
+ sei.lpVerb = pEntry->szVerb;
+ sei.lpDirectory = wszDir;
+ sei.lpIDList = pidlFull;
+ sei.hkeyClass = hkeyClass;
+ sei.fMask = SEE_MASK_CLASSKEY | SEE_MASK_IDLIST;
+ if (bHasPath)
+ {
+ sei.lpFile = wszPath;
+ }
+
+ ShellExecuteExW(&sei);
+
+ RegCloseKey(hkeyClass);
+
+ ILFree(pidlFull);
+
+ return S_OK;
+}
+
+HRESULT
+CDefaultContextMenu::DoStaticShellExtensions(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ PStaticShellEntry pEntry = m_pStaticEntries;
+ INT iCmd = LOWORD(lpcmi->lpVerb) - m_iIdSCMFirst;
+ HRESULT hr;
+ UINT i;
+
+ while (pEntry && (iCmd--) > 0)
+ pEntry = pEntry->pNext;
+
+ if (iCmd > 0)
+ return E_FAIL;
+
+ /* Get the browse flags to see if we need to browse */
+ DWORD wFlags = BrowserFlagsFromVerb(lpcmi, pEntry);
+ BOOL bBrowsed = FALSE;
+
+ for (i=0; i < m_Dcm.cidl; i++)
+ {
+ /* Check if we need to browse */
+ if (wFlags > 0)
+ {
+ /* In xp if we have browsed, we don't open any more folders .
+ * In win7 we browse to the first folder we find and
+ * open new windows fo for each of the rest of the folders */
+ if (bBrowsed)
+ continue;
+
+ hr = TryToBrowse(lpcmi, m_Dcm.apidl[i], wFlags);
+ if (SUCCEEDED(hr))
+ {
+ bBrowsed = TRUE;
+ continue;
+ }
+ }
+
+ InvokePidl(lpcmi, m_Dcm.apidl[i], pEntry);
+ }
+
+ return S_OK;
+}
+
+HRESULT
+WINAPI
+CDefaultContextMenu::InvokeCommand(
+ LPCMINVOKECOMMANDINFO lpcmi)
+{
+ switch(LOWORD(lpcmi->lpVerb))
+ {
+ case FCIDM_SHVIEW_BIGICON:
+ case FCIDM_SHVIEW_SMALLICON:
+ case FCIDM_SHVIEW_LISTVIEW:
+ case FCIDM_SHVIEW_REPORTVIEW:
+ case 0x30: /* FIX IDS in resource files */
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case FCIDM_SHVIEW_AUTOARRANGE:
+ case FCIDM_SHVIEW_SNAPTOGRID:
+ return NotifyShellViewWindow(lpcmi, FALSE);
+ case FCIDM_SHVIEW_REFRESH:
+ return DoRefresh(lpcmi);
+ case FCIDM_SHVIEW_INSERT:
+ return DoPaste(lpcmi, FALSE);
+ case FCIDM_SHVIEW_INSERTLINK:
+ return DoPaste(lpcmi, TRUE);
+ case FCIDM_SHVIEW_OPEN:
+ case FCIDM_SHVIEW_EXPLORE:
+ return DoOpenOrExplore(lpcmi);
+ case FCIDM_SHVIEW_COPY:
+ case FCIDM_SHVIEW_CUT:
+ return DoCopyOrCut(lpcmi, LOWORD(lpcmi->lpVerb) == FCIDM_SHVIEW_COPY);
+ case FCIDM_SHVIEW_CREATELINK:
+ return DoCreateLink(lpcmi);
+ case FCIDM_SHVIEW_DELETE:
+ return DoDelete(lpcmi);
+ case FCIDM_SHVIEW_RENAME:
+ return DoRename(lpcmi);
+ case FCIDM_SHVIEW_PROPERTIES:
+ return DoProperties(lpcmi);
+ case 0x7ABC:
+ return DoFormat(lpcmi);
+ }
+
+ if (m_iIdSHEFirst && m_iIdSHELast)
+ {
+ if (LOWORD(lpcmi->lpVerb) >= m_iIdSHEFirst && LOWORD(lpcmi->lpVerb) <= m_iIdSHELast)
+ return DoDynamicShellExtensions(lpcmi);
+ }
+
+ if (m_iIdSCMFirst && m_iIdSCMLast)
+ {
+ if (LOWORD(lpcmi->lpVerb) >= m_iIdSCMFirst && LOWORD(lpcmi->lpVerb) <= m_iIdSCMLast)
+ return DoStaticShellExtensions(lpcmi);
+ }
+
+ FIXME("Unhandled Verb %xl\n", LOWORD(lpcmi->lpVerb));
+ return E_UNEXPECTED;
+}
+
+HRESULT
+WINAPI
+CDefaultContextMenu::GetCommandString(
+ UINT_PTR idCommand,
+ UINT uFlags,
+ UINT* lpReserved,
+ LPSTR lpszName,
+ UINT uMaxNameLen)
+{
+ return S_OK;
+}
+
+HRESULT
+WINAPI
+CDefaultContextMenu::HandleMenuMsg(
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ return S_OK;
+}
+
+static
+HRESULT
+IDefaultContextMenu_Constructor(
+ const DEFCONTEXTMENU *pdcm,
+ REFIID riid,
+ void **ppv)
+{
+ if (ppv == NULL)
+ return E_POINTER;
+ *ppv = NULL;
+
+ CComObject<CDefaultContextMenu> *pCM;
+ HRESULT hr = CComObject<CDefaultContextMenu>::CreateInstance(&pCM);
+ if (FAILED(hr))
+ return hr;
+ pCM->AddRef(); // CreateInstance returns object with 0 ref count */
+
+ CComPtr<IUnknown> pResult;
+ hr = pCM->QueryInterface(riid, (void **)&pResult);
+ if (FAILED(hr))
+ {
+ pCM->Release();
+ return hr;
+ }
+
+ hr = pCM->Initialize(pdcm);
+ if (FAILED(hr))
+ {
+ pCM->Release();
+ return hr;
+ }
+
+ *ppv = pResult.Detach();
+ pCM->Release();
+ TRACE("This(%p) cidl %u\n", *ppv, pdcm->cidl);
+ return S_OK;
+}
+
+/*************************************************************************
+ * SHCreateDefaultContextMenu [SHELL32.325] Vista API
+ *
+ */
+
+HRESULT
+WINAPI
+SHCreateDefaultContextMenu(
+ const DEFCONTEXTMENU *pdcm,
+ REFIID riid,
+ void **ppv)
+{
+ *ppv = NULL;
+ HRESULT hr = IDefaultContextMenu_Constructor(pdcm, riid, ppv);
+ if (FAILED(hr))
+ ERR("IDefaultContextMenu_Constructor failed: %x\n", hr);
+ TRACE("pcm %p hr %x\n", pdcm, hr);
+ return hr;
+}
+
+/*************************************************************************
+ * CDefFolderMenu_Create2 [SHELL32.701]
+ *
+ */
+
+HRESULT
+WINAPI
+CDefFolderMenu_Create2(
+ LPCITEMIDLIST pidlFolder,
+ HWND hwnd,
+ UINT cidl,
+ LPCITEMIDLIST *apidl,
+ IShellFolder *psf,
+ LPFNDFMCALLBACK lpfn,
+ UINT nKeys,
+ const HKEY *ahkeyClsKeys,
+ IContextMenu **ppcm)
+{
+ DEFCONTEXTMENU pdcm;
+ pdcm.hwnd = hwnd;
+ pdcm.pcmcb = NULL;
+ pdcm.pidlFolder = pidlFolder;
+ pdcm.psf = psf;
+ pdcm.cidl = cidl;
+ pdcm.apidl = apidl;
+ pdcm.punkAssociationInfo = NULL;
+ pdcm.cKeys = nKeys;
+ pdcm.aKeys = ahkeyClsKeys;
+
+ HRESULT hr = SHCreateDefaultContextMenu(&pdcm, IID_PPV_ARG(IContextMenu, ppcm));
+ return hr;
+}
+
--- /dev/null
- INT_PTR CALLBACK AboutDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
+/*
+ * Shell basics
+ *
+ * Copyright 1998 Marcus Meissner
+ * Copyright 1998 Juergen Schmied (jsch) * <juergen.schmied@metronet.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "precomp.h"
+
+#include "shell32_version.h"
+#include <reactos/version.h>
+
+WINE_DEFAULT_DEBUG_CHANNEL(shell);
+
+const char * const SHELL_Authors[] = { "Copyright 1993-"COPYRIGHT_YEAR" WINE team", "Copyright 1998-"COPYRIGHT_YEAR" ReactOS Team", 0 };
+
+#define MORE_DEBUG 1
+/*************************************************************************
+ * CommandLineToArgvW [SHELL32.@]
+ *
+ * We must interpret the quotes in the command line to rebuild the argv
+ * array correctly:
+ * - arguments are separated by spaces or tabs
+ * - quotes serve as optional argument delimiters
+ * '"a b"' -> 'a b'
+ * - escaped quotes must be converted back to '"'
+ * '\"' -> '"'
+ * - consecutive backslashes preceding a quote see their number halved with
+ * the remainder escaping the quote:
+ * 2n backslashes + quote -> n backslashes + quote as an argument delimiter
+ * 2n+1 backslashes + quote -> n backslashes + literal quote
+ * - backslashes that are not followed by a quote are copied literally:
+ * 'a\b' -> 'a\b'
+ * 'a\\b' -> 'a\\b'
+ * - in quoted strings, consecutive quotes see their number divided by three
+ * with the remainder modulo 3 deciding whether to close the string or not.
+ * Note that the opening quote must be counted in the consecutive quotes,
+ * that's the (1+) below:
+ * (1+) 3n quotes -> n quotes
+ * (1+) 3n+1 quotes -> n quotes plus closes the quoted string
+ * (1+) 3n+2 quotes -> n+1 quotes plus closes the quoted string
+ * - in unquoted strings, the first quote opens the quoted string and the
+ * remaining consecutive quotes follow the above rule.
+ */
+LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCmdline, int* numargs)
+{
+ DWORD argc;
+ LPWSTR *argv;
+ LPCWSTR s;
+ LPWSTR d;
+ LPWSTR cmdline;
+ int qcount,bcount;
+
+ if(!numargs)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ if (*lpCmdline==0)
+ {
+ /* Return the path to the executable */
+ DWORD len, deslen=MAX_PATH, size;
+
+ size = sizeof(LPWSTR) + deslen*sizeof(WCHAR) + sizeof(LPWSTR);
+ for (;;)
+ {
+ if (!(argv = (LPWSTR *)LocalAlloc(LMEM_FIXED, size))) return NULL;
+ len = GetModuleFileNameW(0, (LPWSTR)(argv+1), deslen);
+ if (!len)
+ {
+ LocalFree(argv);
+ return NULL;
+ }
+ if (len < deslen) break;
+ deslen*=2;
+ size = sizeof(LPWSTR) + deslen*sizeof(WCHAR) + sizeof(LPWSTR);
+ LocalFree( argv );
+ }
+ argv[0]=(LPWSTR)(argv+1);
+ *numargs=1;
+
+ return argv;
+ }
+
+ /* --- First count the arguments */
+ argc=1;
+ s=lpCmdline;
+ /* The first argument, the executable path, follows special rules */
+ if (*s=='"')
+ {
+ /* The executable path ends at the next quote, no matter what */
+ s++;
+ while (*s)
+ if (*s++=='"')
+ break;
+ }
+ else
+ {
+ /* The executable path ends at the next space, no matter what */
+ while (*s && *s!=' ' && *s!='\t')
+ s++;
+ }
+ /* skip to the first argument, if any */
+ while (*s==' ' || *s=='\t')
+ s++;
+ if (*s)
+ argc++;
+
+ /* Analyze the remaining arguments */
+ qcount=bcount=0;
+ while (*s)
+ {
+ if ((*s==' ' || *s=='\t') && qcount==0)
+ {
+ /* skip to the next argument and count it if any */
+ while (*s==' ' || *s=='\t')
+ s++;
+ if (*s)
+ argc++;
+ bcount=0;
+ }
+ else if (*s=='\\')
+ {
+ /* '\', count them */
+ bcount++;
+ s++;
+ }
+ else if (*s=='"')
+ {
+ /* '"' */
+ if ((bcount & 1)==0)
+ qcount++; /* unescaped '"' */
+ s++;
+ bcount=0;
+ /* consecutive quotes, see comment in copying code below */
+ while (*s=='"')
+ {
+ qcount++;
+ s++;
+ }
+ qcount=qcount % 3;
+ if (qcount==2)
+ qcount=0;
+ }
+ else
+ {
+ /* a regular character */
+ bcount=0;
+ s++;
+ }
+ }
+
+ /* Allocate in a single lump, the string array, and the strings that go
+ * with it. This way the caller can make a single LocalFree() call to free
+ * both, as per MSDN.
+ */
+ argv=(LPWSTR *)LocalAlloc(LMEM_FIXED, argc*sizeof(LPWSTR)+(strlenW(lpCmdline)+1)*sizeof(WCHAR));
+ if (!argv)
+ return NULL;
+ cmdline=(LPWSTR)(argv+argc);
+ strcpyW(cmdline, lpCmdline);
+
+ /* --- Then split and copy the arguments */
+ argv[0]=d=cmdline;
+ argc=1;
+ /* The first argument, the executable path, follows special rules */
+ if (*d=='"')
+ {
+ /* The executable path ends at the next quote, no matter what */
+ s=d+1;
+ while (*s)
+ {
+ if (*s=='"')
+ {
+ s++;
+ break;
+ }
+ *d++=*s++;
+ }
+ }
+ else
+ {
+ /* The executable path ends at the next space, no matter what */
+ while (*d && *d!=' ' && *d!='\t')
+ d++;
+ s=d;
+ if (*s)
+ s++;
+ }
+ /* close the executable path */
+ *d++=0;
+ /* skip to the first argument and initialize it if any */
+ while (*s==' ' || *s=='\t')
+ s++;
+ if (!*s)
+ {
+ /* There are no parameters so we are all done */
+ *numargs=argc;
+ return argv;
+ }
+
+ /* Split and copy the remaining arguments */
+ argv[argc++]=d;
+ qcount=bcount=0;
+ while (*s)
+ {
+ if ((*s==' ' || *s=='\t') && qcount==0)
+ {
+ /* close the argument */
+ *d++=0;
+ bcount=0;
+
+ /* skip to the next one and initialize it if any */
+ do {
+ s++;
+ } while (*s==' ' || *s=='\t');
+ if (*s)
+ argv[argc++]=d;
+ }
+ else if (*s=='\\')
+ {
+ *d++=*s++;
+ bcount++;
+ }
+ else if (*s=='"')
+ {
+ if ((bcount & 1)==0)
+ {
+ /* Preceded by an even number of '\', this is half that
+ * number of '\', plus a quote which we erase.
+ */
+ d-=bcount/2;
+ qcount++;
+ }
+ else
+ {
+ /* Preceded by an odd number of '\', this is half that
+ * number of '\' followed by a '"'
+ */
+ d=d-bcount/2-1;
+ *d++='"';
+ }
+ s++;
+ bcount=0;
+ /* Now count the number of consecutive quotes. Note that qcount
+ * already takes into account the opening quote if any, as well as
+ * the quote that lead us here.
+ */
+ while (*s=='"')
+ {
+ if (++qcount==3)
+ {
+ *d++='"';
+ qcount=0;
+ }
+ s++;
+ }
+ if (qcount==2)
+ qcount=0;
+ }
+ else
+ {
+ /* a regular character */
+ *d++=*s++;
+ bcount=0;
+ }
+ }
+ *d='\0';
+ *numargs=argc;
+
+ return argv;
+}
+
+static DWORD shgfi_get_exe_type(LPCWSTR szFullPath)
+{
+ BOOL status = FALSE;
+ HANDLE hfile;
+ DWORD BinaryType;
+ IMAGE_DOS_HEADER mz_header;
+ IMAGE_NT_HEADERS nt;
+ DWORD len;
+ char magic[4];
+
+ status = GetBinaryTypeW (szFullPath, &BinaryType);
+ if (!status)
+ return 0;
+ if (BinaryType == SCS_DOS_BINARY || BinaryType == SCS_PIF_BINARY)
+ return 0x4d5a;
+
+ hfile = CreateFileW( szFullPath, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, 0, 0 );
+ if ( hfile == INVALID_HANDLE_VALUE )
+ return 0;
+
+ /*
+ * The next section is adapted from MODULE_GetBinaryType, as we need
+ * to examine the image header to get OS and version information. We
+ * know from calling GetBinaryTypeA that the image is valid and either
+ * an NE or PE, so much error handling can be omitted.
+ * Seek to the start of the file and read the header information.
+ */
+
+ SetFilePointer( hfile, 0, NULL, SEEK_SET );
+ ReadFile( hfile, &mz_header, sizeof(mz_header), &len, NULL );
+
+ SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET );
+ ReadFile( hfile, magic, sizeof(magic), &len, NULL );
+
+ if ( *(DWORD*)magic == IMAGE_NT_SIGNATURE )
+ {
+ SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET );
+ ReadFile( hfile, &nt, sizeof(nt), &len, NULL );
+ CloseHandle( hfile );
+
+ /* DLL files are not executable and should return 0 */
+ if (nt.FileHeader.Characteristics & IMAGE_FILE_DLL)
+ return 0;
+
+ if (nt.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI)
+ {
+ return IMAGE_NT_SIGNATURE |
+ (nt.OptionalHeader.MajorSubsystemVersion << 24) |
+ (nt.OptionalHeader.MinorSubsystemVersion << 16);
+ }
+ return IMAGE_NT_SIGNATURE;
+ }
+ else if ( *(WORD*)magic == IMAGE_OS2_SIGNATURE )
+ {
+ IMAGE_OS2_HEADER ne;
+ SetFilePointer( hfile, mz_header.e_lfanew, NULL, SEEK_SET );
+ ReadFile( hfile, &ne, sizeof(ne), &len, NULL );
+ CloseHandle( hfile );
+
+ if (ne.ne_exetyp == 2)
+ return IMAGE_OS2_SIGNATURE | (ne.ne_expver << 16);
+ return 0;
+ }
+ CloseHandle( hfile );
+ return 0;
+}
+
+/*************************************************************************
+ * SHELL_IsShortcut [internal]
+ *
+ * Decide if an item id list points to a shell shortcut
+ */
+BOOL SHELL_IsShortcut(LPCITEMIDLIST pidlLast)
+{
+ char szTemp[MAX_PATH];
+ HKEY keyCls;
+ BOOL ret = FALSE;
+
+ if (_ILGetExtension(pidlLast, szTemp, MAX_PATH) &&
+ HCR_MapTypeToValueA(szTemp, szTemp, MAX_PATH, TRUE))
+ {
+ if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CLASSES_ROOT, szTemp, 0, KEY_QUERY_VALUE, &keyCls))
+ {
+ if (ERROR_SUCCESS == RegQueryValueExA(keyCls, "IsShortcut", NULL, NULL, NULL, NULL))
+ ret = TRUE;
+
+ RegCloseKey(keyCls);
+ }
+ }
+
+ return ret;
+}
+
+#define SHGFI_KNOWN_FLAGS \
+ (SHGFI_SMALLICON | SHGFI_OPENICON | SHGFI_SHELLICONSIZE | SHGFI_PIDL | \
+ SHGFI_USEFILEATTRIBUTES | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX | \
+ SHGFI_ICON | SHGFI_DISPLAYNAME | SHGFI_TYPENAME | SHGFI_ATTRIBUTES | \
+ SHGFI_ICONLOCATION | SHGFI_EXETYPE | SHGFI_SYSICONINDEX | \
+ SHGFI_LINKOVERLAY | SHGFI_SELECTED | SHGFI_ATTR_SPECIFIED)
+
+/*************************************************************************
+ * SHGetFileInfoW [SHELL32.@]
+ *
+ */
+DWORD_PTR WINAPI SHGetFileInfoW(LPCWSTR path,DWORD dwFileAttributes,
+ SHFILEINFOW *psfi, UINT sizeofpsfi, UINT flags )
+{
+ WCHAR szLocation[MAX_PATH], szFullPath[MAX_PATH];
+ int iIndex;
+ DWORD_PTR ret = TRUE;
+ DWORD dwAttributes = 0;
+ CComPtr<IShellFolder> psfParent;
+ CComPtr<IExtractIconW> pei;
+ LPITEMIDLIST pidlLast = NULL, pidl = NULL;
+ HRESULT hr = S_OK;
+ BOOL IconNotYetLoaded=TRUE;
+ UINT uGilFlags = 0;
+
+ TRACE("%s fattr=0x%x sfi=%p(attr=0x%08x) size=0x%x flags=0x%x\n",
+ (flags & SHGFI_PIDL)? "pidl" : debugstr_w(path), dwFileAttributes,
+ psfi, psfi->dwAttributes, sizeofpsfi, flags);
+
+ if (!path)
+ return FALSE;
+
+ /* windows initializes these values regardless of the flags */
+ if (psfi != NULL)
+ {
+ psfi->szDisplayName[0] = '\0';
+ psfi->szTypeName[0] = '\0';
+ psfi->iIcon = 0;
+ }
+
+ if (!(flags & SHGFI_PIDL))
+ {
+ /* SHGetFileInfo should work with absolute and relative paths */
+ if (PathIsRelativeW(path))
+ {
+ GetCurrentDirectoryW(MAX_PATH, szLocation);
+ PathCombineW(szFullPath, szLocation, path);
+ }
+ else
+ {
+ lstrcpynW(szFullPath, path, MAX_PATH);
+ }
+ }
+
+ if (flags & SHGFI_EXETYPE)
+ {
+ if (flags != SHGFI_EXETYPE)
+ return 0;
+ return shgfi_get_exe_type(szFullPath);
+ }
+
+ /*
+ * psfi is NULL normally to query EXE type. If it is NULL, none of the
+ * below makes sense anyway. Windows allows this and just returns FALSE
+ */
+ if (psfi == NULL)
+ return FALSE;
+
+ /*
+ * translate the path into a pidl only when SHGFI_USEFILEATTRIBUTES
+ * is not specified.
+ * The pidl functions fail on not existing file names
+ */
+
+ if (flags & SHGFI_PIDL)
+ {
+ pidl = ILClone((LPCITEMIDLIST)path);
+ }
+ else if (!(flags & SHGFI_USEFILEATTRIBUTES))
+ {
+ hr = SHILCreateFromPathW(szFullPath, &pidl, &dwAttributes);
+ }
+
+ if ((flags & SHGFI_PIDL) || !(flags & SHGFI_USEFILEATTRIBUTES))
+ {
+ /* get the parent shellfolder */
+ if (pidl)
+ {
+ hr = SHBindToParent( pidl, IID_PPV_ARG(IShellFolder, &psfParent),
+ (LPCITEMIDLIST*)&pidlLast );
+ if (SUCCEEDED(hr))
+ pidlLast = ILClone(pidlLast);
+ ILFree(pidl);
+ }
+ else
+ {
+ ERR("pidl is null!\n");
+ return FALSE;
+ }
+ }
+
+ /* get the attributes of the child */
+ if (SUCCEEDED(hr) && (flags & SHGFI_ATTRIBUTES))
+ {
+ if (!(flags & SHGFI_ATTR_SPECIFIED))
+ {
+ psfi->dwAttributes = 0xffffffff;
+ }
+ if (psfParent != NULL)
+ psfParent->GetAttributesOf(1, (LPCITEMIDLIST*)&pidlLast,
+ &(psfi->dwAttributes) );
+ }
+
+ /* get the displayname */
+ if (SUCCEEDED(hr) && (flags & SHGFI_DISPLAYNAME))
+ {
+ if (flags & SHGFI_USEFILEATTRIBUTES)
+ {
+ wcscpy (psfi->szDisplayName, PathFindFileNameW(szFullPath));
+ }
+ else
+ {
+ STRRET str;
+ hr = psfParent->GetDisplayNameOf(pidlLast,
+ SHGDN_INFOLDER, &str);
+ StrRetToStrNW (psfi->szDisplayName, MAX_PATH, &str, pidlLast);
+ }
+ }
+
+ /* get the type name */
+ if (SUCCEEDED(hr) && (flags & SHGFI_TYPENAME))
+ {
+ static const WCHAR szFile[] = { 'F','i','l','e',0 };
+ static const WCHAR szDashFile[] = { '-','f','i','l','e',0 };
+
+ if (!(flags & SHGFI_USEFILEATTRIBUTES))
+ {
+ char ftype[80];
+
+ _ILGetFileType(pidlLast, ftype, 80);
+ MultiByteToWideChar(CP_ACP, 0, ftype, -1, psfi->szTypeName, 80 );
+ }
+ else
+ {
+ if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ wcscat (psfi->szTypeName, szFile);
+ else
+ {
+ WCHAR sTemp[64];
+
+ wcscpy(sTemp,PathFindExtensionW(szFullPath));
+ if (!( HCR_MapTypeToValueW(sTemp, sTemp, 64, TRUE) &&
+ HCR_MapTypeToValueW(sTemp, psfi->szTypeName, 80, FALSE )))
+ {
+ lstrcpynW (psfi->szTypeName, sTemp, 64);
+ wcscat (psfi->szTypeName, szDashFile);
+ }
+ }
+ }
+ }
+
+ /* ### icons ###*/
+ if (flags & SHGFI_OPENICON)
+ uGilFlags |= GIL_OPENICON;
+
+ if (flags & SHGFI_LINKOVERLAY)
+ uGilFlags |= GIL_FORSHORTCUT;
+ else if ((flags&SHGFI_ADDOVERLAYS) ||
+ (flags&(SHGFI_ICON|SHGFI_SMALLICON))==SHGFI_ICON)
+ {
+ if (SHELL_IsShortcut(pidlLast))
+ uGilFlags |= GIL_FORSHORTCUT;
+ }
+
+ if (flags & SHGFI_OVERLAYINDEX)
+ FIXME("SHGFI_OVERLAYINDEX unhandled\n");
+
+ if (flags & SHGFI_SELECTED)
+ FIXME("set icon to selected, stub\n");
+
+ if (flags & SHGFI_SHELLICONSIZE)
+ FIXME("set icon to shell size, stub\n");
+
+ /* get the iconlocation */
+ if (SUCCEEDED(hr) && (flags & SHGFI_ICONLOCATION ))
+ {
+ UINT uFlags;
+
+ if (flags & SHGFI_USEFILEATTRIBUTES)
+ {
+ if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ {
+ wcscpy(psfi->szDisplayName, swShell32Name);
+ psfi->iIcon = -IDI_SHELL_FOLDER;
+ }
+ else
+ {
+ WCHAR* szExt;
+ static const WCHAR p1W[] = {'%','1',0};
+ WCHAR sTemp [MAX_PATH];
+
+ szExt = PathFindExtensionW(szFullPath);
+ TRACE("szExt=%s\n", debugstr_w(szExt));
+ if ( szExt &&
+ HCR_MapTypeToValueW(szExt, sTemp, MAX_PATH, TRUE) &&
+ HCR_GetIconW(sTemp, sTemp, NULL, MAX_PATH, &psfi->iIcon))
+ {
+ if (lstrcmpW(p1W, sTemp))
+ wcscpy(psfi->szDisplayName, sTemp);
+ else
+ {
+ /* the icon is in the file */
+ wcscpy(psfi->szDisplayName, szFullPath);
+ }
+ }
+ else
+ ret = FALSE;
+ }
+ }
+ else
+ {
+ hr = psfParent->GetUIObjectOf(0, 1,
+ (LPCITEMIDLIST*)&pidlLast, IID_NULL_PPV_ARG(IExtractIconW, &pei));
+ if (SUCCEEDED(hr))
+ {
+ hr = pei->GetIconLocation(uGilFlags,
+ szLocation, MAX_PATH, &iIndex, &uFlags);
+
+ if (uFlags & GIL_NOTFILENAME)
+ ret = FALSE;
+ else
+ {
+ wcscpy (psfi->szDisplayName, szLocation);
+ psfi->iIcon = iIndex;
+ }
+ }
+ }
+ }
+
+ /* get icon index (or load icon)*/
+ if (SUCCEEDED(hr) && (flags & (SHGFI_ICON | SHGFI_SYSICONINDEX)))
+ {
+ if (flags & SHGFI_USEFILEATTRIBUTES && !(flags & SHGFI_PIDL))
+ {
+ WCHAR sTemp [MAX_PATH];
+ WCHAR * szExt;
+ int icon_idx=0;
+
+ lstrcpynW(sTemp, szFullPath, MAX_PATH);
+
+ if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ psfi->iIcon = SIC_GetIconIndex(swShell32Name, -IDI_SHELL_FOLDER, 0);
+ else
+ {
+ static const WCHAR p1W[] = {'%','1',0};
+
+ psfi->iIcon = 0;
+ szExt = PathFindExtensionW(sTemp);
+ if ( szExt &&
+ HCR_MapTypeToValueW(szExt, sTemp, MAX_PATH, TRUE) &&
+ HCR_GetIconW(sTemp, sTemp, NULL, MAX_PATH, &icon_idx))
+ {
+ if (!lstrcmpW(p1W,sTemp)) /* icon is in the file */
+ wcscpy(sTemp, szFullPath);
+
+ if (flags & SHGFI_SYSICONINDEX)
+ {
+ psfi->iIcon = SIC_GetIconIndex(sTemp,icon_idx,0);
+ if (psfi->iIcon == -1)
+ psfi->iIcon = 0;
+ }
+ else
+ {
+ UINT ret;
+ if (flags & SHGFI_SMALLICON)
+ ret = PrivateExtractIconsW( sTemp,icon_idx,
+ GetSystemMetrics( SM_CXSMICON ),
+ GetSystemMetrics( SM_CYSMICON ),
+ &psfi->hIcon, 0, 1, 0);
+ else
+ ret = PrivateExtractIconsW( sTemp, icon_idx,
+ GetSystemMetrics( SM_CXICON),
+ GetSystemMetrics( SM_CYICON),
+ &psfi->hIcon, 0, 1, 0);
+
+ if (ret != 0 && ret != 0xFFFFFFFF)
+ {
+ IconNotYetLoaded=FALSE;
+ psfi->iIcon = icon_idx;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if (!(PidlToSicIndex(psfParent, pidlLast, !(flags & SHGFI_SMALLICON),
+ uGilFlags, &(psfi->iIcon))))
+ {
+ ret = FALSE;
+ }
+ }
+ if (ret && (flags & SHGFI_SYSICONINDEX))
+ {
+ if (flags & SHGFI_SMALLICON)
+ ret = (DWORD_PTR) ShellSmallIconList;
+ else
+ ret = (DWORD_PTR) ShellBigIconList;
+ }
+ }
+
+ /* icon handle */
+ if (SUCCEEDED(hr) && (flags & SHGFI_ICON) && IconNotYetLoaded)
+ {
+ if (flags & SHGFI_SMALLICON)
+ psfi->hIcon = ImageList_GetIcon( ShellSmallIconList, psfi->iIcon, ILD_NORMAL);
+ else
+ psfi->hIcon = ImageList_GetIcon( ShellBigIconList, psfi->iIcon, ILD_NORMAL);
+ }
+
+ if (flags & ~SHGFI_KNOWN_FLAGS)
+ FIXME("unknown flags %08x\n", flags & ~SHGFI_KNOWN_FLAGS);
+
+ if (hr != S_OK)
+ ret = FALSE;
+
+ SHFree(pidlLast);
+
+#ifdef MORE_DEBUG
+ TRACE ("icon=%p index=0x%08x attr=0x%08x name=%s type=%s ret=0x%08lx\n",
+ psfi->hIcon, psfi->iIcon, psfi->dwAttributes,
+ debugstr_w(psfi->szDisplayName), debugstr_w(psfi->szTypeName), ret);
+#endif
+
+ return ret;
+}
+
+/*************************************************************************
+ * SHGetFileInfoA [SHELL32.@]
+ *
+ * Note:
+ * MSVBVM60.__vbaNew2 expects this function to return a value in range
+ * 1 .. 0x7fff when the function succeeds and flags does not contain
+ * SHGFI_EXETYPE or SHGFI_SYSICONINDEX (see bug 7701)
+ */
+DWORD_PTR WINAPI SHGetFileInfoA(LPCSTR path,DWORD dwFileAttributes,
+ SHFILEINFOA *psfi, UINT sizeofpsfi,
+ UINT flags )
+{
+ INT len;
+ LPWSTR temppath = NULL;
+ LPCWSTR pathW;
+ DWORD ret;
+ SHFILEINFOW temppsfi;
+
+ if (flags & SHGFI_PIDL)
+ {
+ /* path contains a pidl */
+ pathW = (LPCWSTR)path;
+ }
+ else
+ {
+ len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
+ temppath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, path, -1, temppath, len);
+ pathW = temppath;
+ }
+
+ if (psfi && (flags & SHGFI_ATTR_SPECIFIED))
+ temppsfi.dwAttributes=psfi->dwAttributes;
+
+ if (psfi == NULL)
+ ret = SHGetFileInfoW(pathW, dwFileAttributes, NULL, sizeof(temppsfi), flags);
+ else
+ ret = SHGetFileInfoW(pathW, dwFileAttributes, &temppsfi, sizeof(temppsfi), flags);
+
+ if (psfi)
+ {
+ if(flags & SHGFI_ICON)
+ psfi->hIcon=temppsfi.hIcon;
+ if(flags & (SHGFI_SYSICONINDEX|SHGFI_ICON|SHGFI_ICONLOCATION))
+ psfi->iIcon=temppsfi.iIcon;
+ if(flags & SHGFI_ATTRIBUTES)
+ psfi->dwAttributes=temppsfi.dwAttributes;
+ if(flags & (SHGFI_DISPLAYNAME|SHGFI_ICONLOCATION))
+ {
+ WideCharToMultiByte(CP_ACP, 0, temppsfi.szDisplayName, -1,
+ psfi->szDisplayName, sizeof(psfi->szDisplayName), NULL, NULL);
+ }
+ if(flags & SHGFI_TYPENAME)
+ {
+ WideCharToMultiByte(CP_ACP, 0, temppsfi.szTypeName, -1,
+ psfi->szTypeName, sizeof(psfi->szTypeName), NULL, NULL);
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, temppath);
+
+ return ret;
+}
+
+/*************************************************************************
+ * DuplicateIcon [SHELL32.@]
+ */
+EXTERN_C HICON WINAPI DuplicateIcon( HINSTANCE hInstance, HICON hIcon)
+{
+ ICONINFO IconInfo;
+ HICON hDupIcon = 0;
+
+ TRACE("%p %p\n", hInstance, hIcon);
+
+ if (GetIconInfo(hIcon, &IconInfo))
+ {
+ hDupIcon = CreateIconIndirect(&IconInfo);
+
+ /* clean up hbmMask and hbmColor */
+ DeleteObject(IconInfo.hbmMask);
+ DeleteObject(IconInfo.hbmColor);
+ }
+
+ return hDupIcon;
+}
+
+/*************************************************************************
+ * ExtractIconA [SHELL32.@]
+ */
+HICON WINAPI ExtractIconA(HINSTANCE hInstance, LPCSTR lpszFile, UINT nIconIndex)
+{
+ HICON ret;
+ INT len = MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, NULL, 0);
+ LPWSTR lpwstrFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+
+ TRACE("%p %s %d\n", hInstance, lpszFile, nIconIndex);
+
+ MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, lpwstrFile, len);
+ ret = ExtractIconW(hInstance, lpwstrFile, nIconIndex);
+ HeapFree(GetProcessHeap(), 0, lpwstrFile);
+
+ return ret;
+}
+
+/*************************************************************************
+ * ExtractIconW [SHELL32.@]
+ */
+HICON WINAPI ExtractIconW(HINSTANCE hInstance, LPCWSTR lpszFile, UINT nIconIndex)
+{
+ HICON hIcon = NULL;
+ UINT ret;
+ UINT cx = GetSystemMetrics(SM_CXICON), cy = GetSystemMetrics(SM_CYICON);
+
+ TRACE("%p %s %d\n", hInstance, debugstr_w(lpszFile), nIconIndex);
+
+ if (nIconIndex == 0xFFFFFFFF)
+ {
+ ret = PrivateExtractIconsW(lpszFile, 0, cx, cy, NULL, NULL, 0, LR_DEFAULTCOLOR);
+ if (ret != 0xFFFFFFFF && ret)
+ return (HICON)(UINT_PTR)ret;
+ return NULL;
+ }
+ else
+ ret = PrivateExtractIconsW(lpszFile, nIconIndex, cx, cy, &hIcon, NULL, 1, LR_DEFAULTCOLOR);
+
+ if (ret == 0xFFFFFFFF)
+ return (HICON)1;
+ else if (ret > 0 && hIcon)
+ return hIcon;
+
+ return NULL;
+}
+
+/*************************************************************************
+ * Printer_LoadIconsW [SHELL32.205]
+ */
+EXTERN_C VOID WINAPI Printer_LoadIconsW(LPCWSTR wsPrinterName, HICON * pLargeIcon, HICON * pSmallIcon)
+{
+ INT iconindex=IDI_SHELL_PRINTERS_FOLDER;
+
+ TRACE("(%s, %p, %p)\n", debugstr_w(wsPrinterName), pLargeIcon, pSmallIcon);
+
+ /* We should check if wsPrinterName is
+ 1. the Default Printer or not
+ 2. connected or not
+ 3. a Local Printer or a Network-Printer
+ and use different Icons
+ */
+ if((wsPrinterName != NULL) && (wsPrinterName[0] != 0))
+ {
+ FIXME("(select Icon by PrinterName %s not implemented)\n", debugstr_w(wsPrinterName));
+ }
+
+ if(pLargeIcon != NULL)
+ *pLargeIcon = (HICON)LoadImageW(shell32_hInstance,
+ (LPCWSTR) MAKEINTRESOURCE(iconindex), IMAGE_ICON,
+ 0, 0, LR_DEFAULTCOLOR|LR_DEFAULTSIZE);
+
+ if(pSmallIcon != NULL)
+ *pSmallIcon = (HICON)LoadImageW(shell32_hInstance,
+ (LPCWSTR) MAKEINTRESOURCE(iconindex), IMAGE_ICON,
+ 16, 16, LR_DEFAULTCOLOR);
+}
+
+/*************************************************************************
+ * Printers_RegisterWindowW [SHELL32.213]
+ * used by "printui.dll":
+ * find the Window of the given Type for the specific Printer and
+ * return the already existent hwnd or open a new window
+ */
+EXTERN_C BOOL WINAPI Printers_RegisterWindowW(LPCWSTR wsPrinter, DWORD dwType,
+ HANDLE * phClassPidl, HWND * phwnd)
+{
+ FIXME("(%s, %x, %p (%p), %p (%p)) stub!\n", debugstr_w(wsPrinter), dwType,
+ phClassPidl, (phClassPidl != NULL) ? *(phClassPidl) : NULL,
+ phwnd, (phwnd != NULL) ? *(phwnd) : NULL);
+
+ return FALSE;
+}
+
+/*************************************************************************
+ * Printers_UnregisterWindow [SHELL32.214]
+ */
+EXTERN_C VOID WINAPI Printers_UnregisterWindow(HANDLE hClassPidl, HWND hwnd)
+{
+ FIXME("(%p, %p) stub!\n", hClassPidl, hwnd);
+}
+
+/*************************************************************************/
+
+typedef struct
+{
+ LPCWSTR szApp;
+ LPCWSTR szOtherStuff;
+ HICON hIcon;
+} ABOUT_INFO;
+
+#define DROP_FIELD_TOP (-15)
+#define DROP_FIELD_HEIGHT 15
+
+/*************************************************************************
+ * SHAppBarMessage [SHELL32.@]
+ */
+UINT_PTR WINAPI SHAppBarMessage(DWORD msg, PAPPBARDATA data)
+{
+ int width=data->rc.right - data->rc.left;
+ int height=data->rc.bottom - data->rc.top;
+ RECT rec=data->rc;
+
+ TRACE("msg=%d, data={cb=%d, hwnd=%p, callback=%x, edge=%d, rc=%s, lparam=%lx}\n",
+ msg, data->cbSize, data->hWnd, data->uCallbackMessage, data->uEdge,
+ wine_dbgstr_rect(&data->rc), data->lParam);
+
+ switch (msg)
+ {
+ case ABM_GETSTATE:
+ return ABS_ALWAYSONTOP | ABS_AUTOHIDE;
+
+ case ABM_GETTASKBARPOS:
+ GetWindowRect(data->hWnd, &rec);
+ data->rc=rec;
+ return TRUE;
+
+ case ABM_ACTIVATE:
+ SetActiveWindow(data->hWnd);
+ return TRUE;
+
+ case ABM_GETAUTOHIDEBAR:
+ return 0; /* pretend there is no autohide bar */
+
+ case ABM_NEW:
+ /* cbSize, hWnd, and uCallbackMessage are used. All other ignored */
+ SetWindowPos(data->hWnd,HWND_TOP,0,0,0,0,SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
+ return TRUE;
+
+ case ABM_QUERYPOS:
+ GetWindowRect(data->hWnd, &(data->rc));
+ return TRUE;
+
+ case ABM_REMOVE:
+ FIXME("ABM_REMOVE broken\n");
+ /* FIXME: this is wrong; should it be DestroyWindow instead? */
+ /*CloseHandle(data->hWnd);*/
+ return TRUE;
+
+ case ABM_SETAUTOHIDEBAR:
+ SetWindowPos(data->hWnd,HWND_TOP,rec.left+1000,rec.top,
+ width,height,SWP_SHOWWINDOW);
+ return TRUE;
+
+ case ABM_SETPOS:
+ data->uEdge=(ABE_RIGHT | ABE_LEFT);
+ SetWindowPos(data->hWnd,HWND_TOP,data->rc.left,data->rc.top,
+ width,height,SWP_SHOWWINDOW);
+ return TRUE;
+
+ case ABM_WINDOWPOSCHANGED:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*************************************************************************
+ * SHHelpShortcuts_RunDLLA [SHELL32.@]
+ *
+ */
+EXTERN_C DWORD WINAPI SHHelpShortcuts_RunDLLA(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3, DWORD dwArg4)
+{
+ FIXME("(%x, %x, %x, %x) stub!\n", dwArg1, dwArg2, dwArg3, dwArg4);
+ return 0;
+}
+
+/*************************************************************************
+ * SHHelpShortcuts_RunDLLA [SHELL32.@]
+ *
+ */
+EXTERN_C DWORD WINAPI SHHelpShortcuts_RunDLLW(DWORD dwArg1, DWORD dwArg2, DWORD dwArg3, DWORD dwArg4)
+{
+ FIXME("(%x, %x, %x, %x) stub!\n", dwArg1, dwArg2, dwArg3, dwArg4);
+ return 0;
+}
+
+/*************************************************************************
+ * SHLoadInProc [SHELL32.@]
+ * Create an instance of specified object class from within
+ * the shell process and release it immediately
+ */
+EXTERN_C HRESULT WINAPI SHLoadInProc (REFCLSID rclsid)
+{
+ CComPtr<IUnknown> ptr;
+
+ TRACE("%s\n", debugstr_guid(&rclsid));
+
+ CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&ptr);
+ if (ptr)
+ return S_OK;
+ return DISP_E_MEMBERNOTFOUND;
+}
+
+static VOID SetRegTextData(HWND hWnd, HKEY hKey, LPCWSTR Value, UINT uID)
+{
+ DWORD dwBufferSize;
+ DWORD dwType;
+ LPWSTR lpBuffer;
+
+ if( RegQueryValueExW(hKey, Value, NULL, &dwType, NULL, &dwBufferSize) == ERROR_SUCCESS )
+ {
+ if(dwType == REG_SZ)
+ {
+ lpBuffer = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
+
+ if(lpBuffer)
+ {
+ if( RegQueryValueExW(hKey, Value, NULL, &dwType, (LPBYTE)lpBuffer, &dwBufferSize) == ERROR_SUCCESS )
+ {
+ SetDlgItemTextW(hWnd, uID, lpBuffer);
+ }
+
+ HeapFree(GetProcessHeap(), 0, lpBuffer);
+ }
+ }
+ }
+}
+
+INT_PTR CALLBACK AboutAuthorsDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ const char* const *pstr = SHELL_Authors;
+
+ // Add the authors to the list
+ SendDlgItemMessageW( hWnd, IDC_ABOUT_AUTHORS_LISTBOX, WM_SETREDRAW, FALSE, 0 );
+
+ while (*pstr)
+ {
+ WCHAR name[64];
+
+ /* authors list is in utf-8 format */
+ MultiByteToWideChar( CP_UTF8, 0, *pstr, -1, name, sizeof(name)/sizeof(WCHAR) );
+ SendDlgItemMessageW( hWnd, IDC_ABOUT_AUTHORS_LISTBOX, LB_ADDSTRING, (WPARAM)-1, (LPARAM)name );
+ pstr++;
+ }
+
+ SendDlgItemMessageW( hWnd, IDC_ABOUT_AUTHORS_LISTBOX, WM_SETREDRAW, TRUE, 0 );
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+/*************************************************************************
+ * AboutDlgProc (internal)
+ */
++static INT_PTR CALLBACK AboutDlgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ static DWORD cxLogoBmp;
+ static DWORD cyLogoBmp;
+ static HBITMAP hLogoBmp;
+ static HWND hWndAuthors;
+
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ ABOUT_INFO *info = (ABOUT_INFO *)lParam;
+
+ if (info)
+ {
+ const WCHAR szRegKey[] = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
+ HKEY hRegKey;
+ MEMORYSTATUSEX MemStat;
+ WCHAR szAppTitle[512];
+ WCHAR szAppTitleTemplate[512];
+ WCHAR szAuthorsText[20];
+
+ // Preload the ROS bitmap
+ hLogoBmp = (HBITMAP)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDB_REACTOS), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
+
+ if(hLogoBmp)
+ {
+ BITMAP bmpLogo;
+
+ GetObject( hLogoBmp, sizeof(BITMAP), &bmpLogo );
+
+ cxLogoBmp = bmpLogo.bmWidth;
+ cyLogoBmp = bmpLogo.bmHeight;
+ }
+
+ // Set App-specific stuff (icon, app name, szOtherStuff string)
+ SendDlgItemMessageW(hWnd, IDC_ABOUT_ICON, STM_SETICON, (WPARAM)info->hIcon, 0);
+
+ GetWindowTextW( hWnd, szAppTitleTemplate, sizeof(szAppTitleTemplate) / sizeof(WCHAR) );
+ swprintf( szAppTitle, szAppTitleTemplate, info->szApp );
+ SetWindowTextW( hWnd, szAppTitle );
+
+ SetDlgItemTextW( hWnd, IDC_ABOUT_APPNAME, info->szApp );
+ SetDlgItemTextW( hWnd, IDC_ABOUT_OTHERSTUFF, info->szOtherStuff );
+
+ // Set the registered user and organization name
+ if(RegOpenKeyExW( HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_QUERY_VALUE, &hRegKey ) == ERROR_SUCCESS)
+ {
+ SetRegTextData( hWnd, hRegKey, L"RegisteredOwner", IDC_ABOUT_REG_USERNAME );
+ SetRegTextData( hWnd, hRegKey, L"RegisteredOrganization", IDC_ABOUT_REG_ORGNAME );
+
+ RegCloseKey( hRegKey );
+ }
+
+ // Set the value for the installed physical memory
+ MemStat.dwLength = sizeof(MemStat);
+ if( GlobalMemoryStatusEx(&MemStat) )
+ {
+ WCHAR szBuf[12];
+
+ if (MemStat.ullTotalPhys > 1024 * 1024 * 1024)
+ {
+ double dTotalPhys;
+ WCHAR szDecimalSeparator[4];
+ WCHAR szUnits[3];
+
+ // We're dealing with GBs or more
+ MemStat.ullTotalPhys /= 1024 * 1024;
+
+ if (MemStat.ullTotalPhys > 1024 * 1024)
+ {
+ // We're dealing with TBs or more
+ MemStat.ullTotalPhys /= 1024;
+
+ if (MemStat.ullTotalPhys > 1024 * 1024)
+ {
+ // We're dealing with PBs or more
+ MemStat.ullTotalPhys /= 1024;
+
+ dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
+ wcscpy( szUnits, L"PB" );
+ }
+ else
+ {
+ dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
+ wcscpy( szUnits, L"TB" );
+ }
+ }
+ else
+ {
+ dTotalPhys = (double)MemStat.ullTotalPhys / 1024;
+ wcscpy( szUnits, L"GB" );
+ }
+
+ // We need the decimal point of the current locale to display the RAM size correctly
+ if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
+ szDecimalSeparator,
+ sizeof(szDecimalSeparator) / sizeof(WCHAR)) > 0)
+ {
+ UCHAR uDecimals;
+ UINT uIntegral;
+
+ uIntegral = (UINT)dTotalPhys;
+ uDecimals = (UCHAR)((UINT)(dTotalPhys * 100) - uIntegral * 100);
+
+ // Display the RAM size with 2 decimals
+ swprintf(szBuf, L"%u%s%02u %s", uIntegral, szDecimalSeparator, uDecimals, szUnits);
+ }
+ }
+ else
+ {
+ // We're dealing with MBs, don't show any decimals
+ swprintf( szBuf, L"%u MB", (UINT)MemStat.ullTotalPhys / 1024 / 1024 );
+ }
+
+ SetDlgItemTextW( hWnd, IDC_ABOUT_PHYSMEM, szBuf);
+ }
+
+ // Add the Authors dialog
+ hWndAuthors = CreateDialogW( shell32_hInstance, MAKEINTRESOURCEW(IDD_ABOUT_AUTHORS), hWnd, AboutAuthorsDlgProc );
+ LoadStringW( shell32_hInstance, IDS_SHELL_ABOUT_AUTHORS, szAuthorsText, sizeof(szAuthorsText) / sizeof(WCHAR) );
+ SetDlgItemTextW( hWnd, IDC_ABOUT_AUTHORS, szAuthorsText );
+ }
+
+ return TRUE;
+ }
+
+ case WM_PAINT:
+ {
+ if(hLogoBmp)
+ {
+ PAINTSTRUCT ps;
+ HDC hdc;
+ HDC hdcMem;
+
+ hdc = BeginPaint(hWnd, &ps);
+ hdcMem = CreateCompatibleDC(hdc);
+
+ if(hdcMem)
+ {
+ SelectObject(hdcMem, hLogoBmp);
+ BitBlt(hdc, 0, 0, cxLogoBmp, cyLogoBmp, hdcMem, 0, 0, SRCCOPY);
+
+ DeleteDC(hdcMem);
+ }
+
+ EndPaint(hWnd, &ps);
+ }
+ }; break;
+
+ case WM_COMMAND:
+ {
+ switch(wParam)
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hWnd, TRUE);
+ return TRUE;
+
+ case IDC_ABOUT_AUTHORS:
+ {
+ static BOOL bShowingAuthors = FALSE;
+ WCHAR szAuthorsText[20];
+
+ if(bShowingAuthors)
+ {
+ LoadStringW( shell32_hInstance, IDS_SHELL_ABOUT_AUTHORS, szAuthorsText, sizeof(szAuthorsText) / sizeof(WCHAR) );
+ ShowWindow( hWndAuthors, SW_HIDE );
+ }
+ else
+ {
+ LoadStringW( shell32_hInstance, IDS_SHELL_ABOUT_BACK, szAuthorsText, sizeof(szAuthorsText) / sizeof(WCHAR) );
+ ShowWindow( hWndAuthors, SW_SHOW );
+ }
+
+ SetDlgItemTextW( hWnd, IDC_ABOUT_AUTHORS, szAuthorsText );
+ bShowingAuthors = !bShowingAuthors;
+ return TRUE;
+ }
+ }
+ }; break;
+
+ case WM_CLOSE:
+ EndDialog(hWnd, TRUE);
+ break;
+ }
+
+ return FALSE;
+}
+
+
+/*************************************************************************
+ * ShellAboutA [SHELL32.288]
+ */
+BOOL WINAPI ShellAboutA( HWND hWnd, LPCSTR szApp, LPCSTR szOtherStuff, HICON hIcon )
+{
+ BOOL ret;
+ LPWSTR appW = NULL, otherW = NULL;
+ int len;
+
+ if (szApp)
+ {
+ len = MultiByteToWideChar(CP_ACP, 0, szApp, -1, NULL, 0);
+ appW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, szApp, -1, appW, len);
+ }
+ if (szOtherStuff)
+ {
+ len = MultiByteToWideChar(CP_ACP, 0, szOtherStuff, -1, NULL, 0);
+ otherW = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, szOtherStuff, -1, otherW, len);
+ }
+
+ ret = ShellAboutW(hWnd, appW, otherW, hIcon);
+
+ HeapFree(GetProcessHeap(), 0, otherW);
+ HeapFree(GetProcessHeap(), 0, appW);
+ return ret;
+}
+
+
+/*************************************************************************
+ * ShellAboutW [SHELL32.289]
+ */
+BOOL WINAPI ShellAboutW( HWND hWnd, LPCWSTR szApp, LPCWSTR szOtherStuff,
+ HICON hIcon )
+{
+ ABOUT_INFO info;
+ HRSRC hRes;
+ DLGTEMPLATE *DlgTemplate;
+ BOOL bRet;
+
+ TRACE("\n");
+
+ // DialogBoxIndirectParamW will be called with the hInstance of the calling application, so we have to preload the dialog template
+ hRes = FindResourceW(shell32_hInstance, MAKEINTRESOURCEW(IDD_ABOUT), (LPWSTR)RT_DIALOG);
+ if(!hRes)
+ return FALSE;
+
+ DlgTemplate = (DLGTEMPLATE *)LoadResource(shell32_hInstance, hRes);
+ if(!DlgTemplate)
+ return FALSE;
+
+ info.szApp = szApp;
+ info.szOtherStuff = szOtherStuff;
+ info.hIcon = hIcon ? hIcon : LoadIconW( 0, (LPWSTR)IDI_WINLOGO );
+
+ bRet = DialogBoxIndirectParamW((HINSTANCE)GetWindowLongPtrW( hWnd, GWLP_HINSTANCE ),
+ DlgTemplate, hWnd, AboutDlgProc, (LPARAM)&info );
+ return bRet;
+}
+
+/*************************************************************************
+ * FreeIconList (SHELL32.@)
+ */
+EXTERN_C void WINAPI FreeIconList( DWORD dw )
+{
+ FIXME("%x: stub\n",dw);
+}
+
+/*************************************************************************
+ * SHLoadNonloadedIconOverlayIdentifiers (SHELL32.@)
+ */
+EXTERN_C HRESULT WINAPI SHLoadNonloadedIconOverlayIdentifiers(VOID)
+{
+ FIXME("stub\n");
+ return S_OK;
+}
+
+class CShell32Module : public CComModule
+{
+public:
+};
+
+
+BEGIN_OBJECT_MAP(ObjectMap)
+ OBJECT_ENTRY(CLSID_ShellFSFolder, CFSFolder)
+ OBJECT_ENTRY(CLSID_MyComputer, CDrivesFolder)
+ OBJECT_ENTRY(CLSID_ShellDesktop, CDesktopFolder)
+ OBJECT_ENTRY(CLSID_ShellItem, CShellItem)
+ OBJECT_ENTRY(CLSID_ShellLink, CShellLink)
+ OBJECT_ENTRY(CLSID_DragDropHelper, CDropTargetHelper)
+ OBJECT_ENTRY(CLSID_ControlPanel, CControlPanelFolder)
+ OBJECT_ENTRY(CLSID_AutoComplete, CAutoComplete)
+ OBJECT_ENTRY(CLSID_MyDocuments, CMyDocsFolder)
+ OBJECT_ENTRY(CLSID_NetworkPlaces, CNetFolder)
+ OBJECT_ENTRY(CLSID_FontsFolderShortcut, CFontsFolder)
+ OBJECT_ENTRY(CLSID_Printers, CPrinterFolder)
+ OBJECT_ENTRY(CLSID_AdminFolderShortcut, CAdminToolsFolder)
+ OBJECT_ENTRY(CLSID_RecycleBin, CRecycleBin)
+ OBJECT_ENTRY(CLSID_OpenWithMenu, COpenWithMenu)
+ OBJECT_ENTRY(CLSID_NewMenu, CNewMenu)
+ OBJECT_ENTRY(CLSID_StartMenu, CStartMenu)
+ OBJECT_ENTRY(CLSID_MenuBandSite, CMenuBandSite)
+ OBJECT_ENTRY(CLSID_MenuBand, CMenuBand)
+ OBJECT_ENTRY(CLSID_MenuDeskBar, CMenuDeskBar)
+ OBJECT_ENTRY(CLSID_ExeDropHandler, CExeDropHandler)
+END_OBJECT_MAP()
+
+CShell32Module gModule;
+
+
+/***********************************************************************
+ * DllGetVersion [SHELL32.@]
+ *
+ * Retrieves version information of the 'SHELL32.DLL'
+ *
+ * PARAMS
+ * pdvi [O] pointer to version information structure.
+ *
+ * RETURNS
+ * Success: S_OK
+ * Failure: E_INVALIDARG
+ *
+ * NOTES
+ * Returns version of a shell32.dll from IE4.01 SP1.
+ */
+
+STDAPI DllGetVersion(DLLVERSIONINFO *pdvi)
+{
+ /* FIXME: shouldn't these values come from the version resource? */
+ if (pdvi->cbSize == sizeof(DLLVERSIONINFO) ||
+ pdvi->cbSize == sizeof(DLLVERSIONINFO2))
+ {
+ pdvi->dwMajorVersion = WINE_FILEVERSION_MAJOR;
+ pdvi->dwMinorVersion = WINE_FILEVERSION_MINOR;
+ pdvi->dwBuildNumber = WINE_FILEVERSION_BUILD;
+ pdvi->dwPlatformID = WINE_FILEVERSION_PLATFORMID;
+ if (pdvi->cbSize == sizeof(DLLVERSIONINFO2))
+ {
+ DLLVERSIONINFO2 *pdvi2 = (DLLVERSIONINFO2 *)pdvi;
+
+ pdvi2->dwFlags = 0;
+ pdvi2->ullVersion = MAKEDLLVERULL(WINE_FILEVERSION_MAJOR,
+ WINE_FILEVERSION_MINOR,
+ WINE_FILEVERSION_BUILD,
+ WINE_FILEVERSION_PLATFORMID);
+ }
+ TRACE("%u.%u.%u.%u\n",
+ pdvi->dwMajorVersion, pdvi->dwMinorVersion,
+ pdvi->dwBuildNumber, pdvi->dwPlatformID);
+ return S_OK;
+ }
+ else
+ {
+ WARN("wrong DLLVERSIONINFO size from app\n");
+ return E_INVALIDARG;
+ }
+}
+
+/*************************************************************************
+ * global variables of the shell32.dll
+ * all are once per process
+ *
+ */
+HINSTANCE shell32_hInstance;
+HIMAGELIST ShellSmallIconList = 0;
+HIMAGELIST ShellBigIconList = 0;
+
+void *operator new (size_t, void *buf)
+{
+ return buf;
+}
+
+/*************************************************************************
+ * SHELL32 DllMain
+ *
+ * NOTES
+ * calling oleinitialize here breaks sone apps.
+ */
+STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID fImpLoad)
+{
+ TRACE("%p 0x%x %p\n", hInstance, dwReason, fImpLoad);
+ if (dwReason == DLL_PROCESS_ATTACH)
+ {
+ /* HACK - the global constructors don't run, so I placement new them here */
+ new (&gModule) CShell32Module;
+ new (&_AtlWinModule) CAtlWinModule;
+ new (&_AtlBaseModule) CAtlBaseModule;
+ new (&_AtlComModule) CAtlComModule;
+
+ shell32_hInstance = hInstance;
+ gModule.Init(ObjectMap, hInstance, NULL);
+
+ DisableThreadLibraryCalls (hInstance);
+
+ /* get full path to this DLL for IExtractIconW_fnGetIconLocation() */
+ GetModuleFileNameW(hInstance, swShell32Name, MAX_PATH);
+ swShell32Name[MAX_PATH - 1] = '\0';
+
+ /* Initialize comctl32 */
+ INITCOMMONCONTROLSEX InitCtrls;
+ InitCtrls.dwSize = sizeof(INITCOMMONCONTROLSEX);
+ InitCtrls.dwICC = ICC_WIN95_CLASSES | ICC_DATE_CLASSES | ICC_USEREX_CLASSES;
+ InitCommonControlsEx(&InitCtrls);
+
+ SIC_Initialize();
+ InitChangeNotifications();
+ InitIconOverlays();
+ }
+ else if (dwReason == DLL_PROCESS_DETACH)
+ {
+ shell32_hInstance = NULL;
+ SIC_Destroy();
+ FreeChangeNotifications();
+ gModule.Term();
+ }
+ return TRUE;
+}
+
+/***********************************************************************
+ * DllCanUnloadNow (SHELL32.@)
+ */
+STDAPI DllCanUnloadNow()
+{
+ return gModule.DllCanUnloadNow();
+}
+
+/*************************************************************************
+ * DllGetClassObject [SHELL32.@]
+ * SHDllGetClassObject [SHELL32.128]
+ */
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
+{
+ HRESULT hResult;
+
+ TRACE("CLSID:%s,IID:%s\n", shdebugstr_guid(&rclsid), shdebugstr_guid(&riid));
+
+ hResult = gModule.DllGetClassObject(rclsid, riid, ppv);
+ TRACE("-- pointer to class factory: %p\n", *ppv);
+ return hResult;
+}
+
+/***********************************************************************
+ * DllRegisterServer (SHELL32.@)
+ */
+STDAPI DllRegisterServer()
+{
+ HRESULT hr;
+
+ hr = gModule.DllRegisterServer(FALSE);
+ if (FAILED(hr))
+ return hr;
+
+ hr = gModule.UpdateRegistryFromResource(IDR_FOLDEROPTIONS, TRUE, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ hr = SHELL_RegisterShellFolders();
+ if (FAILED(hr))
+ return hr;
+
+ return S_OK;
+}
+
+/***********************************************************************
+ * DllUnregisterServer (SHELL32.@)
+ */
+STDAPI DllUnregisterServer()
+{
+ HRESULT hr;
+
+ hr = gModule.DllUnregisterServer(FALSE);
+ if (FAILED(hr))
+ return hr;
+
+ hr = gModule.UpdateRegistryFromResource(IDR_FOLDEROPTIONS, FALSE, NULL);
+ if (FAILED(hr))
+ return hr;
+
+ return S_OK;
+}
+
+/*************************************************************************
+ * DllInstall [SHELL32.@]
+ *
+ * PARAMETERS
+ *
+ * BOOL bInstall - TRUE for install, FALSE for uninstall
+ * LPCWSTR pszCmdLine - command line (unused by shell32?)
+ */
+
+HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
+{
+ FIXME("%s %s: stub\n", bInstall ? "TRUE":"FALSE", debugstr_w(cmdline));
+ return S_OK; /* indicate success */
+}
--- /dev/null
- *res = '\0';
+/*
+ * Shell Library Functions
+ *
+ * Copyright 1998 Marcus Meissner
+ * Copyright 2002 Eric Pouech
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "precomp.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(exec);
+
+static const WCHAR wszOpen[] = L"open";
+static const WCHAR wszExe[] = L".exe";
+
+#define SEE_MASK_CLASSALL (SEE_MASK_CLASSNAME | SEE_MASK_CLASSKEY)
+
++typedef UINT_PTR (*SHELL_ExecuteW32)(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
++ const SHELLEXECUTEINFOW *sei, LPSHELLEXECUTEINFOW sei_out);
++
+static void ParseNoTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
+{
+ bool firstCharQuote = false;
+ bool quotes_opened = false;
+ bool backslash_encountered = false;
+
+ for (int curArg = 0; curArg <= argNum && *args; ++curArg)
+ {
+ firstCharQuote = false;
+ if (*args == '"')
+ {
+ quotes_opened = true;
+ firstCharQuote = true;
+ args++;
+ }
+
+ while(*args)
+ {
+ if (*args == '\\')
+ {
+ // if we found a backslash then flip the variable
+ backslash_encountered = !backslash_encountered;
+ }
+ else if (*args == '"')
+ {
+ if (quotes_opened)
+ {
+ if (*(args + 1) != '"')
+ {
+ quotes_opened = false;
+ args++;
+ break;
+ }
+ else
+ {
+ args++;
+ }
+ }
+ else
+ {
+ quotes_opened = true;
+ }
+
+ backslash_encountered = false;
+ }
+ else
+ {
+ backslash_encountered = false;
+ if (*args == ' ' && !firstCharQuote)
+ break;
+ }
+
+ if (curArg == argNum)
+ {
+ used++;
+ if (used < len)
+ *res++ = *args;
+ }
+
+ args++;
+ }
+
+ while(*args == ' ')
+ ++args;
+ }
+}
+
+static void ParseTildeEffect(PWSTR &res, LPCWSTR &args, DWORD &len, DWORD &used, int argNum)
+{
+ bool quotes_opened = false;
+ bool backslash_encountered = false;
+
+ for (int curArg = 0; curArg <= argNum && *args; ++curArg)
+ {
+ while(*args)
+ {
+ if (*args == '\\')
+ {
+ // if we found a backslash then flip the variable
+ backslash_encountered = !backslash_encountered;
+ }
+ else if (*args == '"')
+ {
+ if (quotes_opened)
+ {
+ if (*(args + 1) != '"')
+ {
+ quotes_opened = false;
+ }
+ else
+ {
+ args++;
+ }
+ }
+ else
+ {
+ quotes_opened = true;
+ }
+
+ backslash_encountered = false;
+ }
+ else
+ {
+ backslash_encountered = false;
+ if (*args == ' ' && !quotes_opened && curArg != argNum)
+ break;
+ }
+
+ if (curArg == argNum)
+ {
+ used++;
+ if (used < len)
+ *res++ = *args;
+ }
+
+ args++;
+ }
+ }
+}
+
+/***********************************************************************
+ * SHELL_ArgifyW [Internal]
+ *
+ * this function is supposed to expand the escape sequences found in the registry
+ * some diving reported that the following were used:
+ * + %1, %2... seem to report to parameter of index N in ShellExecute pmts
+ * %1 file
+ * %2 printer
+ * %3 driver
+ * %4 port
+ * %I address of a global item ID (explorer switch /idlist)
+ * %L seems to be %1 as long filename followed by the 8+3 variation
+ * %S ???
+ * %* all following parameters (see batfile)
+ *
+ * The way we parse the command line arguments was determined through extensive
+ * testing and can be summed up by the following rules"
+ *
+ * - %2
+ * - if first letter is " break on first non literal " and include any white spaces
+ * - if first letter is NOT " break on first " or white space
+ * - if " is opened any pair of consecutive " results in ONE literal "
+ *
+ * - %~2
+ * - use rules from here http://www.autohotkey.net/~deleyd/parameters/parameters.htm
+ */
+
+static BOOL SHELL_ArgifyW(WCHAR* out, DWORD len, const WCHAR* fmt, const WCHAR* lpFile, LPITEMIDLIST pidl, LPCWSTR args, DWORD* out_len)
+{
+ WCHAR xlpFile[1024];
+ BOOL done = FALSE;
+ BOOL found_p1 = FALSE;
+ PWSTR res = out;
+ PCWSTR cmd;
+ DWORD used = 0;
+ bool tildeEffect = false;
+
+ TRACE("Before parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
+ debugstr_w(lpFile), pidl, args);
+
+ while (*fmt)
+ {
+ if (*fmt == '%')
+ {
+ switch (*++fmt)
+ {
+ case '\0':
+ case '%':
+ {
+ used++;
+ if (used < len)
+ *res++ = '%';
+ };
+ break;
+
+ case '*':
+ {
+ if (args)
+ {
+ if (*fmt == '*')
+ {
+ used++;
+ while(*args)
+ {
+ used++;
+ if (used < len)
+ *res++ = *args++;
+ else
+ args++;
+ }
+ used++;
+ break;
+ }
+ }
+ };
+ break;
+
+ case '~':
+
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ //case '0':
+ {
+ if (*fmt == '~')
+ {
+ fmt++;
+ tildeEffect = true;
+ }
+
+ if (args)
+ {
+ if (tildeEffect)
+ {
+ ParseTildeEffect(res, args, len, used, *fmt - '2');
+ tildeEffect = false;
+ }
+ else
+ {
+ ParseNoTildeEffect(res, args, len, used, *fmt - '2');
+ }
+ }
+ };
+ break;
+
+ case '1':
+ if (!done || (*fmt == '1'))
+ {
+ /*FIXME Is the call to SearchPathW() really needed? We already have separated out the parameter string in args. */
+ if (SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile) / sizeof(WCHAR), xlpFile, NULL))
+ cmd = xlpFile;
+ else
+ cmd = lpFile;
+
+ used += wcslen(cmd);
+ if (used < len)
+ {
+ wcscpy(res, cmd);
+ res += wcslen(cmd);
+ }
+ }
+ found_p1 = TRUE;
+ break;
+
+ /*
+ * IE uses this a lot for activating things such as windows media
+ * player. This is not verified to be fully correct but it appears
+ * to work just fine.
+ */
+ case 'l':
+ case 'L':
+ if (lpFile)
+ {
+ used += wcslen(lpFile);
+ if (used < len)
+ {
+ wcscpy(res, lpFile);
+ res += wcslen(lpFile);
+ }
+ }
+ found_p1 = TRUE;
+ break;
+
+ case 'i':
+ case 'I':
+ if (pidl)
+ {
+ DWORD chars = 0;
+ /* %p should not exceed 8, maybe 16 when looking forward to 64bit.
+ * allowing a buffer of 100 should more than exceed all needs */
+ WCHAR buf[100];
+ LPVOID pv;
+ HGLOBAL hmem = SHAllocShared(pidl, ILGetSize(pidl), 0);
+ pv = SHLockShared(hmem, 0);
+ chars = swprintf(buf, L":%p", pv);
+
+ if (chars >= sizeof(buf) / sizeof(WCHAR))
+ ERR("pidl format buffer too small!\n");
+
+ used += chars;
+
+ if (used < len)
+ {
+ wcscpy(res, buf);
+ res += chars;
+ }
+ SHUnlockShared(pv);
+ }
+ found_p1 = TRUE;
+ break;
+
+ default:
+ /*
+ * Check if this is an env-variable here...
+ */
+
+ /* Make sure that we have at least one more %.*/
+ if (strchrW(fmt, '%'))
+ {
+ WCHAR tmpBuffer[1024];
+ PWSTR tmpB = tmpBuffer;
+ WCHAR tmpEnvBuff[MAX_PATH];
+ DWORD envRet;
+
+ while (*fmt != '%')
+ *tmpB++ = *fmt++;
+ *tmpB++ = 0;
+
+ TRACE("Checking %s to be an env-var\n", debugstr_w(tmpBuffer));
+
+ envRet = GetEnvironmentVariableW(tmpBuffer, tmpEnvBuff, MAX_PATH);
+ if (envRet == 0 || envRet > MAX_PATH)
+ {
+ used += wcslen(tmpBuffer);
+ if (used < len)
+ {
+ wcscpy( res, tmpBuffer );
+ res += wcslen(tmpBuffer);
+ }
+ }
+ else
+ {
+ used += wcslen(tmpEnvBuff);
+ if (used < len)
+ {
+ wcscpy( res, tmpEnvBuff );
+ res += wcslen(tmpEnvBuff);
+ }
+ }
+ }
+ done = TRUE;
+ break;
+ }
+ /* Don't skip past terminator (catch a single '%' at the end) */
+ if (*fmt != '\0')
+ {
+ fmt++;
+ }
+ }
+ else
+ {
+ used ++;
+ if (used < len)
+ *res++ = *fmt++;
+ else
+ fmt++;
+ }
+ }
+
- if (psei->fMask & SEE_MASK_NO_CONSOLE)
++ used++;
++ if (res - out < static_cast<int>(len))
++ *res = '\0';
++ else
++ out[len-1] = '\0';
++
+ TRACE("used %i of %i space\n", used, len);
+ if (out_len)
+ *out_len = used;
+
+ TRACE("After parsing: %p, %d, %s, %s, %p, %p\n", out, len, debugstr_w(fmt),
+ debugstr_w(lpFile), pidl, args);
+
+ return found_p1;
+}
+
+static HRESULT SHELL_GetPathFromIDListForExecuteW(LPCITEMIDLIST pidl, LPWSTR pszPath, UINT uOutSize)
+{
+ STRRET strret;
+ IShellFolder *desktop;
+
+ HRESULT hr = SHGetDesktopFolder(&desktop);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = desktop->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret);
+
+ if (SUCCEEDED(hr))
+ StrRetToStrNW(pszPath, uOutSize, &strret, pidl);
+
+ desktop->Release();
+ }
+
+ return hr;
+}
+
+/*************************************************************************
+ * SHELL_ExecuteW [Internal]
+ *
+ */
+static UINT_PTR SHELL_ExecuteW(const WCHAR *lpCmd, WCHAR *env, BOOL shWait,
+ const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
+{
+ STARTUPINFOW startup;
+ PROCESS_INFORMATION info;
+ UINT_PTR retval = SE_ERR_NOASSOC;
+ UINT gcdret = 0;
+ WCHAR curdir[MAX_PATH];
+ DWORD dwCreationFlags;
+ const WCHAR *lpDirectory = NULL;
+
+ TRACE("Execute %s from directory %s\n", debugstr_w(lpCmd), debugstr_w(psei->lpDirectory));
+
+ /* make sure we don't fail the CreateProcess if the calling app passes in
+ * a bad working directory */
+ if (psei->lpDirectory && psei->lpDirectory[0])
+ {
+ DWORD attr = GetFileAttributesW(psei->lpDirectory);
+ if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_DIRECTORY)
+ lpDirectory = psei->lpDirectory;
+ }
+
+ /* ShellExecute specifies the command from psei->lpDirectory
+ * if present. Not from the current dir as CreateProcess does */
+ if (lpDirectory)
+ if ((gcdret = GetCurrentDirectoryW( MAX_PATH, curdir)))
+ if (!SetCurrentDirectoryW( lpDirectory))
+ ERR("cannot set directory %s\n", debugstr_w(lpDirectory));
+
+ ZeroMemory(&startup, sizeof(STARTUPINFOW));
+ startup.cb = sizeof(STARTUPINFOW);
+ startup.dwFlags = STARTF_USESHOWWINDOW;
+ startup.wShowWindow = psei->nShow;
+ dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
- static UINT SHELL_FindExecutableByOperation(LPCWSTR lpOperation, LPWSTR key, LPWSTR filetype, LPWSTR command, LONG commandlen)
++ if (!(psei->fMask & SEE_MASK_NO_CONSOLE))
+ dwCreationFlags |= CREATE_NEW_CONSOLE;
+ startup.lpTitle = (LPWSTR)(psei->fMask & (SEE_MASK_HASLINKNAME | SEE_MASK_HASTITLE) ? psei->lpClass : NULL);
+
+ if (psei->fMask & SEE_MASK_HASLINKNAME)
+ startup.dwFlags |= STARTF_TITLEISLINKNAME;
+
+ if (CreateProcessW(NULL, (LPWSTR)lpCmd, NULL, NULL, FALSE, dwCreationFlags, env,
+ lpDirectory, &startup, &info))
+ {
+ /* Give 30 seconds to the app to come up, if desired. Probably only needed
+ when starting app immediately before making a DDE connection. */
+ if (shWait)
+ if (WaitForInputIdle(info.hProcess, 30000) == WAIT_FAILED)
+ WARN("WaitForInputIdle failed: Error %d\n", GetLastError() );
+ retval = 33;
+
+ if (psei->fMask & SEE_MASK_NOCLOSEPROCESS)
+ psei_out->hProcess = info.hProcess;
+ else
+ CloseHandle( info.hProcess );
+ CloseHandle( info.hThread );
+ }
+ else if ((retval = GetLastError()) >= 32)
+ {
+ WARN("CreateProcess returned error %ld\n", retval);
+ retval = ERROR_BAD_FORMAT;
+ }
+
+ TRACE("returning %lu\n", retval);
+
+ psei_out->hInstApp = (HINSTANCE)retval;
+
+ if (gcdret)
+ if (!SetCurrentDirectoryW(curdir))
+ ERR("cannot return to directory %s\n", debugstr_w(curdir));
+
+ return retval;
+}
+
+
+/***********************************************************************
+ * SHELL_BuildEnvW [Internal]
+ *
+ * Build the environment for the new process, adding the specified
+ * path to the PATH variable. Returned pointer must be freed by caller.
+ */
+static LPWSTR SHELL_BuildEnvW( const WCHAR *path )
+{
+ static const WCHAR wPath[] = L"PATH=";
+ WCHAR *strings, *new_env;
+ WCHAR *p, *p2;
+ int total = wcslen(path) + 1;
+ BOOL got_path = FALSE;
+
+ if (!(strings = GetEnvironmentStringsW())) return NULL;
+ p = strings;
+ while (*p)
+ {
+ int len = wcslen(p) + 1;
+ if (!_wcsnicmp( p, wPath, 5 )) got_path = TRUE;
+ total += len;
+ p += len;
+ }
+ if (!got_path) total += 5; /* we need to create PATH */
+ total++; /* terminating null */
+
+ if (!(new_env = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, total * sizeof(WCHAR))))
+ {
+ FreeEnvironmentStringsW(strings);
+ return NULL;
+ }
+ p = strings;
+ p2 = new_env;
+ while (*p)
+ {
+ int len = wcslen(p) + 1;
+ memcpy(p2, p, len * sizeof(WCHAR));
+ if (!_wcsnicmp( p, wPath, 5 ))
+ {
+ p2[len - 1] = ';';
+ wcscpy( p2 + len, path );
+ p2 += wcslen(path) + 1;
+ }
+ p += len;
+ p2 += len;
+ }
+ if (!got_path)
+ {
+ wcscpy(p2, wPath);
+ wcscat(p2, path);
+ p2 += wcslen(p2) + 1;
+ }
+ *p2 = 0;
+ FreeEnvironmentStringsW(strings);
+ return new_env;
+}
+
+
+/***********************************************************************
+ * SHELL_TryAppPathW [Internal]
+ *
+ * Helper function for SHELL_FindExecutable
+ * @param lpResult - pointer to a buffer of size MAX_PATH
+ * On entry: szName is a filename (probably without path separators).
+ * On exit: if szName found in "App Path", place full path in lpResult, and return true
+ */
+static BOOL SHELL_TryAppPathW( LPCWSTR szName, LPWSTR lpResult, WCHAR **env)
+{
+ HKEY hkApp = 0;
+ WCHAR buffer[1024];
+ LONG len;
+ LONG res;
+ BOOL found = FALSE;
+
+ if (env) *env = NULL;
+ wcscpy(buffer, L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\");
+ wcscat(buffer, szName);
+ res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buffer, 0, KEY_READ, &hkApp);
+ if (res) goto end;
+
+ len = MAX_PATH * sizeof(WCHAR);
+ res = RegQueryValueW(hkApp, NULL, lpResult, &len);
+ if (res) goto end;
+ found = TRUE;
+
+ if (env)
+ {
+ DWORD count = sizeof(buffer);
+ if (!RegQueryValueExW(hkApp, L"Path", NULL, NULL, (LPBYTE)buffer, &count) && buffer[0])
+ *env = SHELL_BuildEnvW(buffer);
+ }
+
+end:
+ if (hkApp) RegCloseKey(hkApp);
+ return found;
+}
+
- if (RegOpenKeyExW(HKEY_CLASSES_ROOT, filetype, 0, 0x02000000, &hkeyClass))
++/*************************************************************************
++ * SHELL_FindExecutableByVerb [Internal]
++ *
++ * called from SHELL_FindExecutable or SHELL_execute_class
++ * in/out:
++ * classname a buffer, big enough, to get the key name to do actually the
++ * command "WordPad.Document.1\\shell\\open\\command"
++ * passed as "WordPad.Document.1"
++ * in:
++ * lpVerb the operation on it (open)
++ * commandlen the size of command buffer (in bytes)
++ * out:
++ * command a buffer, to store the command to do the
++ * operation on the file
++ * key a buffer, big enough, to get the key name to do actually the
++ * command "WordPad.Document.1\\shell\\open\\command"
++ * Can be NULL
++ */
++static UINT SHELL_FindExecutableByVerb(LPCWSTR lpVerb, LPWSTR key, LPWSTR classname, LPWSTR command, LONG commandlen)
+{
+ static const WCHAR wCommand[] = L"\\command";
+ HKEY hkeyClass;
+ WCHAR verb[MAX_PATH];
+
- if (!HCR_GetDefaultVerbW(hkeyClass, lpOperation, verb, sizeof(verb) / sizeof(verb[0])))
++ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass))
+ return SE_ERR_NOASSOC;
- wcscat(filetype, L"\\shell\\");
- wcscat(filetype, verb);
- wcscat(filetype, wCommand);
++ if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, sizeof(verb) / sizeof(verb[0])))
+ return SE_ERR_NOASSOC;
+ RegCloseKey(hkeyClass);
+
+ /* Looking for ...buffer\shell\<verb>\command */
- if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, command,
++ wcscat(classname, L"\\shell\\");
++ wcscat(classname, verb);
++ wcscat(classname, wCommand);
+
- if (key) wcscpy(key, filetype);
++ if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, command,
+ &commandlen) == ERROR_SUCCESS)
+ {
+ commandlen /= sizeof(WCHAR);
- tmp = strstrW(filetype, wCommand);
++ if (key) wcscpy(key, classname);
+#if 0
+ LPWSTR tmp;
+ WCHAR param[256];
+ LONG paramlen = sizeof(param);
+ static const WCHAR wSpace[] = {' ', 0};
+
+ /* FIXME: it seems all Windows version don't behave the same here.
+ * the doc states that this ddeexec information can be found after
+ * the exec names.
+ * on Win98, it doesn't appear, but I think it does on Win2k
+ */
+ /* Get the parameters needed by the application
+ from the associated ddeexec key */
- wcscat(filetype, wDdeexec);
- if (RegQueryValueW(HKEY_CLASSES_ROOT, filetype, param,
++ tmp = strstrW(classname, wCommand);
+ tmp[0] = '\0';
- * lpOperation the operation on it (open)
++ wcscat(classname, wDdeexec);
++ if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, param,
+ ¶mlen) == ERROR_SUCCESS)
+ {
+ paramlen /= sizeof(WCHAR);
+ wcscat(command, wSpace);
+ wcscat(command, param);
+ commandlen += paramlen;
+ }
+#endif
+
+ command[commandlen] = '\0';
+
+ return 33; /* FIXME see SHELL_FindExecutable() */
+ }
+
+ return SE_ERR_NOASSOC;
+}
+
+/*************************************************************************
+ * SHELL_FindExecutable [Internal]
+ *
+ * Utility for code sharing between FindExecutable and ShellExecute
+ * in:
+ * lpFile the name of a file
- static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpOperation,
++ * lpVerb the operation on it (open)
+ * out:
+ * lpResult a buffer, big enough :-(, to store the command to do the
+ * operation on the file
+ * key a buffer, big enough, to get the key name to do actually the
+ * command (it'll be used afterwards for more information
+ * on the operation)
+ */
- WCHAR filetype[256]; /* registry name for this filetype */
- LONG filetypelen = sizeof(filetype); /* length of above */
++static UINT SHELL_FindExecutable(LPCWSTR lpPath, LPCWSTR lpFile, LPCWSTR lpVerb,
+ LPWSTR lpResult, DWORD resultLen, LPWSTR key, WCHAR **env, LPITEMIDLIST pidl, LPCWSTR args)
+{
+ WCHAR *extension = NULL; /* pointer to file extension */
- /* Hey, isn't this value ignored? Why make this call? Shouldn't we return here? --dank*/
++ WCHAR classname[256]; /* registry name for this file type */
++ LONG classnamelen = sizeof(classname); /* length of above */
+ WCHAR command[1024]; /* command from registry */
+ WCHAR wBuffer[256]; /* Used to GetProfileString */
+ UINT retval = SE_ERR_NOASSOC;
+ WCHAR *tok; /* token pointer */
+ WCHAR xlpFile[256]; /* result of SearchPath */
+ DWORD attribs; /* file attributes */
+
+ TRACE("%s\n", debugstr_w(lpFile));
+
+ if (!lpResult)
+ return ERROR_INVALID_PARAMETER;
+
+ xlpFile[0] = '\0';
+ lpResult[0] = '\0'; /* Start off with an empty return string */
+ if (key) *key = '\0';
+
+ /* trap NULL parameters on entry */
+ if (!lpFile)
+ {
+ WARN("(lpFile=%s,lpResult=%s): NULL parameter\n",
+ debugstr_w(lpFile), debugstr_w(lpResult));
+ return ERROR_FILE_NOT_FOUND; /* File not found. Close enough, I guess. */
+ }
+
+ if (SHELL_TryAppPathW( lpFile, lpResult, env ))
+ {
+ TRACE("found %s via App Paths\n", debugstr_w(lpResult));
+ return 33;
+ }
+
+ if (SearchPathW(lpPath, lpFile, wszExe, sizeof(xlpFile) / sizeof(WCHAR), xlpFile, NULL))
+ {
+ TRACE("SearchPathW returned non-zero\n");
+ lpFile = xlpFile;
- wcscpy(filetype, L"Folder");
- filetypelen = 6; /* strlen("Folder") */
++ /* The file was found in the application-supplied default directory (or the system search path) */
+ }
+ else if (lpPath && SearchPathW(NULL, lpFile, wszExe, sizeof(xlpFile)/sizeof(WCHAR), xlpFile, NULL))
+ {
+ TRACE("SearchPathW returned non-zero\n");
+ lpFile = xlpFile;
+ /* The file was found in one of the directories in the system-wide search path */
+ }
+
+ attribs = GetFileAttributesW(lpFile);
+ if (attribs != INVALID_FILE_ATTRIBUTES && (attribs & FILE_ATTRIBUTE_DIRECTORY))
+ {
- /* 2. Registry, HKEY_CLASS_ROOT\<filetype>\shell\open\command */
++ wcscpy(classname, L"Folder");
+ }
+ else
+ {
+ /* Did we get something? Anything? */
+ if (xlpFile[0] == 0)
+ {
+ TRACE("Returning SE_ERR_FNF\n");
+ return SE_ERR_FNF;
+ }
+ /* First thing we need is the file's extension */
+ extension = wcsrchr(xlpFile, '.'); /* Assume last "." is the one; */
+ /* File->Run in progman uses */
+ /* .\FILE.EXE :( */
+ TRACE("xlpFile=%s,extension=%s\n", debugstr_w(xlpFile), debugstr_w(extension));
+
+ if (extension == NULL || extension[1] == 0)
+ {
+ WARN("Returning SE_ERR_NOASSOC\n");
+ return SE_ERR_NOASSOC;
+ }
+
+ /* Three places to check: */
+ /* 1. win.ini, [windows], programs (NB no leading '.') */
- if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, filetype,
- &filetypelen) == ERROR_SUCCESS)
++ /* 2. Registry, HKEY_CLASS_ROOT\<classname>\shell\open\command */
+ /* 3. win.ini, [extensions], extension (NB no leading '.' */
+ /* All I know of the order is that registry is checked before */
+ /* extensions; however, it'd make sense to check the programs */
+ /* section first, so that's what happens here. */
+
+ /* See if it's a program - if GetProfileString fails, we skip this
+ * section. Actually, if GetProfileString fails, we've probably
+ * got a lot more to worry about than running a program... */
+ if (GetProfileStringW(L"windows", L"programs", L"exe pif bat cmd com", wBuffer, sizeof(wBuffer) / sizeof(WCHAR)) > 0)
+ {
+ CharLowerW(wBuffer);
+ tok = wBuffer;
+ while (*tok)
+ {
+ WCHAR *p = tok;
+ while (*p && *p != ' ' && *p != '\t') p++;
+ if (*p)
+ {
+ *p++ = 0;
+ while (*p == ' ' || *p == '\t') p++;
+ }
+
+ if (wcsicmp(tok, &extension[1]) == 0) /* have to skip the leading "." */
+ {
+ wcscpy(lpResult, xlpFile);
+ /* Need to perhaps check that the file has a path
+ * attached */
+ TRACE("found %s\n", debugstr_w(lpResult));
+ return 33;
+ /* Greater than 32 to indicate success */
+ }
+ tok = p;
+ }
+ }
+
+ /* Check registry */
- filetypelen /= sizeof(WCHAR);
- if (filetypelen == sizeof(filetype) / sizeof(WCHAR))
- filetypelen--;
++ if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, classname,
++ &classnamelen) == ERROR_SUCCESS)
+ {
- filetype[filetypelen] = '\0';
- TRACE("File type: %s\n", debugstr_w(filetype));
++ classnamelen /= sizeof(WCHAR);
++ if (classnamelen == sizeof(classname) / sizeof(WCHAR))
++ classnamelen--;
+
- *filetype = '\0';
- filetypelen = 0;
++ classname[classnamelen] = '\0';
++ TRACE("File type: %s\n", debugstr_w(classname));
+ }
+ else
+ {
- if (*filetype)
++ *classname = '\0';
+ }
+ }
+
- /* pass the operation string to SHELL_FindExecutableByOperation() */
- filetype[filetypelen] = '\0';
- retval = SHELL_FindExecutableByOperation(lpOperation, key, filetype, command, sizeof(command));
++ if (*classname)
+ {
- WCHAR app[256], topic[256], ifexec[256], res[256];
++ /* pass the verb string to SHELL_FindExecutableByVerb() */
++ retval = SHELL_FindExecutableByVerb(lpVerb, key, classname, command, sizeof(command));
+
+ if (retval > 32)
+ {
+ DWORD finishedLen;
+ SHELL_ArgifyW(lpResult, resultLen, command, xlpFile, pidl, args, &finishedLen);
+ if (finishedLen > resultLen)
+ ERR("Argify buffer not large enough.. truncated\n");
+ /* Remove double quotation marks and command line arguments */
+ if (*lpResult == '"')
+ {
+ WCHAR *p = lpResult;
+ while (*(p + 1) != '"')
+ {
+ *p = *(p + 1);
+ p++;
+ }
+ *p = '\0';
+ }
+ else
+ {
+ /* Truncate on first space */
+ WCHAR *p = lpResult;
+ while (*p != ' ' && *p != '\0')
+ p++;
+ *p = '\0';
+ }
+ }
+ }
+ else /* Check win.ini */
+ {
+ /* Toss the leading dot */
+ extension++;
+ if (GetProfileStringW(L"extensions", extension, L"", command, sizeof(command) / sizeof(WCHAR)) > 0)
+ {
+ if (wcslen(command) != 0)
+ {
+ wcscpy(lpResult, command);
+ tok = wcschr(lpResult, '^'); /* should be ^.extension? */
+ if (tok != NULL)
+ {
+ tok[0] = '\0';
+ wcscat(lpResult, xlpFile); /* what if no dir in xlpFile? */
+ tok = wcschr(command, '^'); /* see above */
+ if ((tok != NULL) && (wcslen(tok) > 5))
+ {
+ wcscat(lpResult, &tok[5]);
+ }
+ }
+ retval = 33; /* FIXME - see above */
+ }
+ }
+ }
+
+ TRACE("returning %s\n", debugstr_w(lpResult));
+ return retval;
+}
+
+/******************************************************************
+ * dde_cb
+ *
+ * callback for the DDE connection. not really useful
+ */
+static HDDEDATA CALLBACK dde_cb(UINT uType, UINT uFmt, HCONV hConv,
+ HSZ hsz1, HSZ hsz2, HDDEDATA hData,
+ ULONG_PTR dwData1, ULONG_PTR dwData2)
+{
+ TRACE("dde_cb: %04x, %04x, %p, %p, %p, %p, %08lx, %08lx\n",
+ uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2);
+ return NULL;
+}
+
+/******************************************************************
+ * dde_connect
+ *
+ * ShellExecute helper. Used to do an operation with a DDE connection
+ *
+ * Handles both the direct connection (try #1), and if it fails,
+ * launching an application and trying (#2) to connect to it
+ *
+ */
+static unsigned dde_connect(const WCHAR* key, const WCHAR* start, WCHAR* ddeexec,
+ const WCHAR* lpFile, WCHAR *env,
+ LPCWSTR szCommandline, LPITEMIDLIST pidl, SHELL_ExecuteW32 execfunc,
+ const SHELLEXECUTEINFOW *psei, LPSHELLEXECUTEINFOW psei_out)
+{
+ WCHAR regkey[256];
+ WCHAR * endkey = regkey + wcslen(key);
- DWORD resultLen;
++ WCHAR app[256], topic[256], ifexec[256], static_res[256];
++ WCHAR * dynamic_res=NULL;
++ WCHAR * res;
+ LONG applen, topiclen, ifexeclen;
+ WCHAR * exec;
+ DWORD ddeInst = 0;
+ DWORD tid;
- wcscpy(endkey, L"\\application");
++ DWORD resultLen, endkeyLen;
+ HSZ hszApp, hszTopic;
+ HCONV hConv;
+ HDDEDATA hDdeData;
+ unsigned ret = SE_ERR_NOASSOC;
+ BOOL unicode = !(GetVersion() & 0x80000000);
+
++ if (strlenW(key) + 1 > sizeof(regkey) / sizeof(regkey[0]))
++ {
++ FIXME("input parameter %s larger than buffer\n", debugstr_w(key));
++ return 2;
++ }
+ wcscpy(regkey, key);
- LPWSTR p, space;
- for (p = (LPWSTR)start; (space = const_cast<LPWSTR>(strchrW(p, ' '))); p = space + 1)
++ static const WCHAR wApplication[] = L"\\application";
++ endkeyLen = sizeof(regkey) / sizeof(regkey[0]) - (endkey - regkey);
++ if (strlenW(wApplication) + 1 > endkeyLen)
++ {
++ FIXME("endkey %s overruns buffer\n", debugstr_w(wApplication));
++ return 2;
++ }
++ wcscpy(endkey, wApplication);
+ applen = sizeof(app);
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, app, &applen) != ERROR_SUCCESS)
+ {
+ WCHAR command[1024], fullpath[MAX_PATH];
+ static const WCHAR wSo[] = L".so";
+ DWORD sizeSo = sizeof(wSo) / sizeof(WCHAR);
+ LPWSTR ptr = NULL;
+ DWORD ret = 0;
+
+ /* Get application command from start string and find filename of application */
+ if (*start == '"')
+ {
++ if (strlenW(start + 1) + 1 > sizeof(command) / sizeof(command[0]))
++ {
++ FIXME("size of input parameter %s larger than buffer\n",
++ debugstr_w(start + 1));
++ return 2;
++ }
+ wcscpy(command, start + 1);
+ if ((ptr = wcschr(command, '"')))
+ * ptr = 0;
+ ret = SearchPathW(NULL, command, wszExe, sizeof(fullpath) / sizeof(WCHAR), fullpath, &ptr);
+ }
+ else
+ {
-
- wcscpy(endkey, L"\\topic");
++ LPCWSTR p;
++ LPWSTR space;
++ for (p = start; (space = const_cast<LPWSTR>(strchrW(p, ' '))); p = space + 1)
+ {
+ int idx = space - start;
+ memcpy(command, start, idx * sizeof(WCHAR));
+ command[idx] = '\0';
+ if ((ret = SearchPathW(NULL, command, wszExe, sizeof(fullpath) / sizeof(WCHAR), fullpath, &ptr)))
+ break;
+ }
+ if (!ret)
+ ret = SearchPathW(NULL, start, wszExe, sizeof(fullpath) / sizeof(WCHAR), fullpath, &ptr);
+ }
+
+ if (!ret)
+ {
+ ERR("Unable to find application path for command %s\n", debugstr_w(start));
+ return ERROR_ACCESS_DENIED;
+ }
++ if (strlenW(ptr) + 1 > sizeof(app) / sizeof(app[0]))
++ {
++ FIXME("size of found path %s larger than buffer\n", debugstr_w(ptr));
++ return 2;
++ }
+ wcscpy(app, ptr);
+
+ /* Remove extensions (including .so) */
+ ptr = app + wcslen(app) - (sizeSo - 1);
+ if (wcslen(app) >= sizeSo &&
+ !wcscmp(ptr, wSo))
+ *ptr = 0;
+
+ ptr = const_cast<LPWSTR>(strrchrW(app, '.'));
+ assert(ptr);
+ *ptr = 0;
+ }
- strcpyW(endkey, L"\\ifexec");
++
++ static const WCHAR wTopic[] = L"\\topic";
++ if (strlenW(wTopic) + 1 > endkeyLen)
++ {
++ FIXME("endkey %s overruns buffer\n", debugstr_w(wTopic));
++ return 2;
++ }
++ wcscpy(endkey, wTopic);
+ topiclen = sizeof(topic);
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, topic, &topiclen) != ERROR_SUCCESS)
+ {
+ wcscpy(topic, L"System");
+ }
+
+ if (unicode)
+ {
+ if (DdeInitializeW(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
+ return 2;
+ }
+ else
+ {
+ if (DdeInitializeA(&ddeInst, dde_cb, APPCMD_CLIENTONLY, 0L) != DMLERR_NO_ERROR)
+ return 2;
+ }
+
+ hszApp = DdeCreateStringHandleW(ddeInst, app, CP_WINUNICODE);
+ hszTopic = DdeCreateStringHandleW(ddeInst, topic, CP_WINUNICODE);
+
+ hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
+ exec = ddeexec;
+ if (!hConv)
+ {
+ TRACE("Launching %s\n", debugstr_w(start));
+ ret = execfunc(start, env, TRUE, psei, psei_out);
+ if (ret <= 32)
+ {
+ TRACE("Couldn't launch\n");
+ goto error;
+ }
+ hConv = DdeConnect(ddeInst, hszApp, hszTopic, NULL);
+ if (!hConv)
+ {
+ TRACE("Couldn't connect. ret=%d\n", ret);
+ DdeUninitialize(ddeInst);
+ SetLastError(ERROR_DDE_FAIL);
+ return 30; /* whatever */
+ }
- SHELL_ArgifyW(res, sizeof(res) / sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen);
- if (resultLen > sizeof(res) / sizeof(WCHAR))
- ERR("Argify buffer not large enough, truncated\n");
++ static const WCHAR wIfexec[] = L"\\ifexec";
++ if (strlenW(wIfexec) + 1 > endkeyLen)
++ {
++ FIXME("endkey %s overruns buffer\n", debugstr_w(wIfexec));
++ return 2;
++ }
++ strcpyW(endkey, wIfexec);
+ ifexeclen = sizeof(ifexec);
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, regkey, ifexec, &ifexeclen) == ERROR_SUCCESS)
+ {
+ exec = ifexec;
+ }
+ }
+
- ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI));
++ SHELL_ArgifyW(static_res, sizeof(static_res)/sizeof(WCHAR), exec, lpFile, pidl, szCommandline, &resultLen);
++ if (resultLen > sizeof(static_res)/sizeof(WCHAR))
++ {
++ res = dynamic_res = static_cast<WCHAR *>(HeapAlloc(GetProcessHeap(), 0, resultLen * sizeof(WCHAR)));
++ SHELL_ArgifyW(dynamic_res, resultLen, exec, lpFile, pidl, szCommandline, NULL);
++ }
++ else
++ res = static_res;
+ TRACE("%s %s => %s\n", debugstr_w(exec), debugstr_w(lpFile), debugstr_w(res));
+
+ /* It's documented in the KB 330337 that IE has a bug and returns
+ * error DMLERR_NOTPROCESSED on XTYP_EXECUTE request.
+ */
+ if (unicode)
+ hDdeData = DdeClientTransaction((LPBYTE)res, (strlenW(res) + 1) * sizeof(WCHAR), hConv, 0L, 0, XTYP_EXECUTE, 30000, &tid);
+ else
+ {
+ DWORD lenA = WideCharToMultiByte(CP_ACP, 0, res, -1, NULL, 0, NULL, NULL);
+ char *resA = (LPSTR)HeapAlloc(GetProcessHeap(), 0, lenA);
+ WideCharToMultiByte(CP_ACP, 0, res, -1, resA, lenA, NULL, NULL);
+ hDdeData = DdeClientTransaction( (LPBYTE)resA, lenA, hConv, 0L, 0,
+ XTYP_EXECUTE, 10000, &tid );
+ HeapFree(GetProcessHeap(), 0, resA);
+ }
+ if (hDdeData)
+ DdeFreeDataHandle(hDdeData);
+ else
+ WARN("DdeClientTransaction failed with error %04x\n", DdeGetLastError(ddeInst));
+ ret = 33;
+
++ HeapFree(GetProcessHeap(), 0, dynamic_res);
++
+ DdeDisconnect(hConv);
+
+error:
+ DdeUninitialize(ddeInst);
+
+ return ret;
+}
+
+/*************************************************************************
+ * execute_from_key [Internal]
+ */
+static UINT_PTR execute_from_key(LPCWSTR key, LPCWSTR lpFile, WCHAR *env,
+ LPCWSTR szCommandline, LPCWSTR executable_name,
+ SHELL_ExecuteW32 execfunc,
+ LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out)
+{
+ WCHAR cmd[256], param[1024], ddeexec[256];
+ DWORD cmdlen = sizeof(cmd), ddeexeclen = sizeof(ddeexec);
+ UINT_PTR retval = SE_ERR_NOASSOC;
+ DWORD resultLen;
+ LPWSTR tmp;
+
+ TRACE("%s %s %s %s %s\n", debugstr_w(key), debugstr_w(lpFile), debugstr_w(env),
+ debugstr_w(szCommandline), debugstr_w(executable_name));
+
+ cmd[0] = '\0';
+ param[0] = '\0';
+
+ /* Get the application from the registry */
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, key, cmd, (LONG *)&cmdlen) == ERROR_SUCCESS)
+ {
+ TRACE("got cmd: %s\n", debugstr_w(cmd));
+
+ /* Is there a replace() function anywhere? */
+ cmdlen /= sizeof(WCHAR);
+ if (cmdlen >= sizeof(cmd) / sizeof(WCHAR))
+ cmdlen = sizeof(cmd) / sizeof(WCHAR) - 1;
+ cmd[cmdlen] = '\0';
+ SHELL_ArgifyW(param, sizeof(param) / sizeof(WCHAR), cmd, lpFile, (LPITEMIDLIST)psei->lpIDList, szCommandline, &resultLen);
+ if (resultLen > sizeof(param) / sizeof(WCHAR))
+ ERR("Argify buffer not large enough, truncating\n");
+ }
+
+ /* Get the parameters needed by the application
+ from the associated ddeexec key */
+ tmp = const_cast<LPWSTR>(strstrW(key, L"command"));
+ assert(tmp);
+ wcscpy(tmp, L"ddeexec");
+
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, key, ddeexec, (LONG *)&ddeexeclen) == ERROR_SUCCESS)
+ {
+ TRACE("Got ddeexec %s => %s\n", debugstr_w(key), debugstr_w(ddeexec));
+ if (!param[0]) strcpyW(param, executable_name);
+ retval = dde_connect(key, param, ddeexec, lpFile, env, szCommandline, (LPITEMIDLIST)psei->lpIDList, execfunc, psei, psei_out);
+ }
+ else if (param[0])
+ {
+ TRACE("executing: %s\n", debugstr_w(param));
+ retval = execfunc(param, env, FALSE, psei, psei_out);
+ }
+ else
+ WARN("Nothing appropriate found for %s\n", debugstr_w(key));
+
+ return retval;
+}
+
+/*************************************************************************
+ * FindExecutableA [SHELL32.@]
+ */
+HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
+{
+ HINSTANCE retval;
+ WCHAR *wFile = NULL, *wDirectory = NULL;
+ WCHAR wResult[MAX_PATH];
+
+ if (lpFile) __SHCloneStrAtoW(&wFile, lpFile);
+ if (lpDirectory) __SHCloneStrAtoW(&wDirectory, lpDirectory);
+
+ retval = FindExecutableW(wFile, wDirectory, wResult);
+ WideCharToMultiByte(CP_ACP, 0, wResult, -1, lpResult, MAX_PATH, NULL, NULL);
+ SHFree(wFile);
+ SHFree(wDirectory);
+
+ TRACE("returning %s\n", lpResult);
+ return retval;
+}
+
+/*************************************************************************
+ * FindExecutableW [SHELL32.@]
+ *
+ * This function returns the executable associated with the specified file
+ * for the default verb.
+ *
+ * PARAMS
+ * lpFile [I] The file to find the association for. This must refer to
+ * an existing file otherwise FindExecutable fails and returns
+ * SE_ERR_FNF.
+ * lpResult [O] Points to a buffer into which the executable path is
+ * copied. This parameter must not be NULL otherwise
+ * FindExecutable() segfaults. The buffer must be of size at
+ * least MAX_PATH characters.
+ *
+ * RETURNS
+ * A value greater than 32 on success, less than or equal to 32 otherwise.
+ * See the SE_ERR_* constants.
+ *
+ * NOTES
+ * On Windows XP and 2003, FindExecutable() seems to first convert the
+ * filename into 8.3 format, thus taking into account only the first three
+ * characters of the extension, and expects to find an association for those.
+ * However other Windows versions behave sanely.
+ */
+HINSTANCE WINAPI FindExecutableW(LPCWSTR lpFile, LPCWSTR lpDirectory, LPWSTR lpResult)
+{
+ UINT_PTR retval = SE_ERR_NOASSOC;
+ WCHAR old_dir[1024];
+
+ TRACE("File %s, Dir %s\n", debugstr_w(lpFile), debugstr_w(lpDirectory));
+
+ lpResult[0] = '\0'; /* Start off with an empty return string */
+ if (lpFile == NULL)
+ return (HINSTANCE)SE_ERR_FNF;
+
+ if (lpDirectory)
+ {
+ GetCurrentDirectoryW(sizeof(old_dir) / sizeof(WCHAR), old_dir);
+ SetCurrentDirectoryW(lpDirectory);
+ }
+
+ retval = SHELL_FindExecutable(lpDirectory, lpFile, wszOpen, lpResult, MAX_PATH, NULL, NULL, NULL, NULL);
+
+ TRACE("returning %s\n", debugstr_w(lpResult));
+ if (lpDirectory)
+ SetCurrentDirectoryW(old_dir);
+ return (HINSTANCE)retval;
+}
+
+/* FIXME: is this already implemented somewhere else? */
+static HKEY ShellExecute_GetClassKey(const SHELLEXECUTEINFOW *sei)
+{
+ LPCWSTR ext = NULL, lpClass = NULL;
+ LPWSTR cls = NULL;
+ DWORD type = 0, sz = 0;
+ HKEY hkey = 0;
+ LONG r;
+
+ if (sei->fMask & SEE_MASK_CLASSALL)
+ return sei->hkeyClass;
+
+ if (sei->fMask & SEE_MASK_CLASSNAME)
+ lpClass = sei->lpClass;
+ else
+ {
+ ext = PathFindExtensionW(sei->lpFile);
+ TRACE("ext = %s\n", debugstr_w(ext));
+ if (!ext)
+ return hkey;
+
+ r = RegOpenKeyW(HKEY_CLASSES_ROOT, ext, &hkey);
+ if (r != ERROR_SUCCESS)
+ return hkey;
+
+ r = RegQueryValueExW(hkey, NULL, 0, &type, NULL, &sz);
+ if (r == ERROR_SUCCESS && type == REG_SZ)
+ {
+ sz += sizeof (WCHAR);
+ cls = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sz);
+ cls[0] = 0;
+ RegQueryValueExW(hkey, NULL, 0, &type, (LPBYTE) cls, &sz);
+ }
+
+ RegCloseKey( hkey );
+ lpClass = cls;
+ }
+
+ TRACE("class = %s\n", debugstr_w(lpClass));
+
+ hkey = 0;
+ if (lpClass)
+ RegOpenKeyW( HKEY_CLASSES_ROOT, lpClass, &hkey);
+
+ HeapFree(GetProcessHeap(), 0, cls);
+
+ return hkey;
+}
+
+static IDataObject *shellex_get_dataobj( LPSHELLEXECUTEINFOW sei )
+{
+ LPCITEMIDLIST pidllast = NULL;
+ IDataObject *dataobj = NULL;
+ IShellFolder *shf = NULL;
+ LPITEMIDLIST pidl = NULL;
+ HRESULT r;
+
+ if (sei->fMask & SEE_MASK_CLASSALL)
+ pidl = (LPITEMIDLIST)sei->lpIDList;
+ else
+ {
+ WCHAR fullpath[MAX_PATH];
+ BOOL ret;
+
+ fullpath[0] = 0;
+ ret = GetFullPathNameW(sei->lpFile, MAX_PATH, fullpath, NULL);
+ if (!ret)
+ goto end;
+
+ pidl = ILCreateFromPathW(fullpath);
+ }
+
+ r = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &shf), &pidllast);
+ if (FAILED(r))
+ goto end;
+
+ shf->GetUIObjectOf(NULL, 1, &pidllast, IID_NULL_PPV_ARG(IDataObject, &dataobj));
+
+end:
+ if (pidl != sei->lpIDList)
+ ILFree(pidl);
+ if (shf)
+ shf->Release();
+ return dataobj;
+}
+
+static HRESULT shellex_run_context_menu_default(IShellExtInit *obj,
+ LPSHELLEXECUTEINFOW sei)
+{
+ IContextMenu *cm = NULL;
+ CMINVOKECOMMANDINFOEX ici;
+ MENUITEMINFOW info;
+ WCHAR string[0x80];
+ INT i, n, def = -1;
+ HMENU hmenu = 0;
+ HRESULT r;
+
+ TRACE("%p %p\n", obj, sei);
+
+ r = obj->QueryInterface(IID_PPV_ARG(IContextMenu, &cm));
+ if (FAILED(r))
+ return r;
+
+ hmenu = CreateMenu();
+ if (!hmenu)
+ goto end;
+
+ /* the number of the last menu added is returned in r */
+ r = cm->QueryContextMenu(hmenu, 0, 0x20, 0x7fff, CMF_DEFAULTONLY);
+ if (FAILED(r))
+ goto end;
+
+ n = GetMenuItemCount(hmenu);
+ for (i = 0; i < n; i++)
+ {
+ memset(&info, 0, sizeof(info));
+ info.cbSize = sizeof info;
+ info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
+ info.dwTypeData = string;
+ info.cch = sizeof string;
+ string[0] = 0;
+ GetMenuItemInfoW(hmenu, i, TRUE, &info);
+
+ TRACE("menu %d %s %08x %08lx %08x %08x\n", i, debugstr_w(string),
+ info.fState, info.dwItemData, info.fType, info.wID);
+ if ((!sei->lpVerb && (info.fState & MFS_DEFAULT)) ||
+ (sei->lpVerb && !lstrcmpiW(sei->lpVerb, string)))
+ {
+ def = i;
+ break;
+ }
+ }
+
+ r = E_FAIL;
+ if (def == -1)
+ goto end;
+
+ memset(&ici, 0, sizeof ici);
+ ici.cbSize = sizeof ici;
- WCHAR execCmd[1024], wcmd[1024];
++ ici.fMask = CMIC_MASK_UNICODE | (sei->fMask & (SEE_MASK_NO_CONSOLE | SEE_MASK_NOASYNC | SEE_MASK_ASYNCOK | SEE_MASK_FLAG_NO_UI));
+ ici.nShow = sei->nShow;
+ ici.lpVerb = MAKEINTRESOURCEA(def);
+ ici.hwnd = sei->hwnd;
+ ici.lpParametersW = sei->lpParameters;
+
+ r = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
+
+ TRACE("invoke command returned %08x\n", r);
+
+end:
+ if (hmenu)
+ DestroyMenu( hmenu );
+ if (cm)
+ cm->Release();
+ return r;
+}
+
+static HRESULT shellex_load_object_and_run(HKEY hkey, LPCGUID guid, LPSHELLEXECUTEINFOW sei)
+{
+ IDataObject *dataobj = NULL;
+ IObjectWithSite *ows = NULL;
+ IShellExtInit *obj = NULL;
+ HRESULT r;
+
+ TRACE("%p %s %p\n", hkey, debugstr_guid(guid), sei);
+
+ r = CoInitialize(NULL);
+ if (FAILED(r))
+ goto end;
+
+ r = CoCreateInstance(*guid, NULL, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARG(IShellExtInit, &obj));
+ if (FAILED(r))
+ {
+ ERR("failed %08x\n", r);
+ goto end;
+ }
+
+ dataobj = shellex_get_dataobj(sei);
+ if (!dataobj)
+ {
+ ERR("failed to get data object\n");
+ goto end;
+ }
+
+ r = obj->Initialize(NULL, dataobj, hkey);
+ if (FAILED(r))
+ goto end;
+
+ r = obj->QueryInterface(IID_PPV_ARG(IObjectWithSite, &ows));
+ if (FAILED(r))
+ goto end;
+
+ ows->SetSite(NULL);
+
+ r = shellex_run_context_menu_default(obj, sei);
+
+end:
+ if (ows)
+ ows->Release();
+ if (dataobj)
+ dataobj->Release();
+ if (obj)
+ obj->Release();
+ CoUninitialize();
+ return r;
+}
+
+
+/*************************************************************************
+ * ShellExecute_FromContextMenu [Internal]
+ */
+static LONG ShellExecute_FromContextMenu( LPSHELLEXECUTEINFOW sei )
+{
+ HKEY hkey, hkeycm = 0;
+ WCHAR szguid[39];
+ HRESULT hr;
+ GUID guid;
+ DWORD i;
+ LONG r;
+
+ TRACE("%s\n", debugstr_w(sei->lpFile));
+
+ hkey = ShellExecute_GetClassKey(sei);
+ if (!hkey)
+ return ERROR_FUNCTION_FAILED;
+
+ r = RegOpenKeyW(hkey, L"shellex\\ContextMenuHandlers", &hkeycm);
+ if (r == ERROR_SUCCESS)
+ {
+ i = 0;
+ while (1)
+ {
+ r = RegEnumKeyW(hkeycm, i++, szguid, sizeof(szguid) / sizeof(szguid[0]));
+ if (r != ERROR_SUCCESS)
+ break;
+
+ hr = CLSIDFromString(szguid, &guid);
+ if (SUCCEEDED(hr))
+ {
+ /* stop at the first one that succeeds in running */
+ hr = shellex_load_object_and_run(hkey, &guid, sei);
+ if (SUCCEEDED(hr))
+ break;
+ }
+ }
+ RegCloseKey(hkeycm);
+ }
+
+ if (hkey != sei->hkeyClass)
+ RegCloseKey(hkey);
+ return r;
+}
+
++static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc);
++
+static UINT_PTR SHELL_execute_class(LPCWSTR wszApplicationName, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc)
+{
-
- HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL,
- (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass : NULL,
- psei->lpVerb,
- execCmd, sizeof(execCmd));
-
- /* FIXME: get the extension of lpFile, check if it fits to the lpClass */
- TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName));
-
- wcmd[0] = '\0';
- done = SHELL_ArgifyW(wcmd, sizeof(wcmd) / sizeof(WCHAR), execCmd, wszApplicationName, (LPITEMIDLIST)psei->lpIDList, NULL, &resultLen);
- if (!done && wszApplicationName[0])
++ WCHAR execCmd[1024], classname[1024];
+ /* launch a document by fileclass like 'WordPad.Document.1' */
+ /* the Commandline contains 'c:\Path\wordpad.exe "%1"' */
+ /* FIXME: wcmd should not be of a fixed size. Fixed to 1024, MAX_PATH is way too short! */
+ ULONG cmask = (psei->fMask & SEE_MASK_CLASSALL);
+ DWORD resultLen;
+ BOOL done;
- strcatW(wcmd, L" ");
- strcatW(wcmd, wszApplicationName);
++ UINT_PTR rslt;
++
++ /* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */
++ if (cmask != SEE_MASK_CLASSNAME)
+ {
- if (resultLen > sizeof(wcmd) / sizeof(WCHAR))
- ERR("Argify buffer not large enough... truncating\n");
- return execfunc(wcmd, NULL, FALSE, psei, psei_out);
++ WCHAR wcmd[1024];
++ HCR_GetExecuteCommandW((cmask == SEE_MASK_CLASSKEY) ? psei->hkeyClass : NULL,
++ (cmask == SEE_MASK_CLASSNAME) ? psei->lpClass : NULL,
++ psei->lpVerb,
++ execCmd, sizeof(execCmd));
++
++ /* FIXME: get the extension of lpFile, check if it fits to the lpClass */
++ TRACE("SEE_MASK_CLASSNAME->%s, doc->%s\n", debugstr_w(execCmd), debugstr_w(wszApplicationName));
++
++ wcmd[0] = '\0';
++ done = SHELL_ArgifyW(wcmd, sizeof(wcmd) / sizeof(WCHAR), execCmd, wszApplicationName, (LPITEMIDLIST)psei->lpIDList, NULL, &resultLen);
++ if (!done && wszApplicationName[0])
++ {
++ strcatW(wcmd, L" ");
++ if (*wszApplicationName != '"')
++ {
++ strcatW(wcmd, L"\"");
++ strcatW(wcmd, wszApplicationName);
++ strcatW(wcmd, L"\"");
++ }
++ else
++ strcatW(wcmd, wszApplicationName);
++ }
++ if (resultLen > sizeof(wcmd) / sizeof(WCHAR))
++ ERR("Argify buffer not large enough... truncating\n");
++ return execfunc(wcmd, NULL, FALSE, psei, psei_out);
+ }
- if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, sizeof(buffer)))) {
++
++ strcpyW(classname, psei->lpClass);
++ rslt = SHELL_FindExecutableByVerb(psei->lpVerb, NULL, classname, execCmd, sizeof(execCmd));
++
++ TRACE("SHELL_FindExecutableByVerb returned %u (%s, %s)\n", (unsigned int)rslt, debugstr_w(classname), debugstr_w(execCmd));
++ if (33 > rslt)
++ return rslt;
++ rslt = SHELL_quote_and_execute( execCmd, L"", classname,
++ wszApplicationName, NULL, psei,
++ psei_out, execfunc );
++ return rslt;
++
+}
+
+static BOOL SHELL_translate_idlist(LPSHELLEXECUTEINFOW sei, LPWSTR wszParameters, DWORD parametersLen, LPWSTR wszApplicationName, DWORD dwApplicationNameLen)
+{
+ static const WCHAR wExplorer[] = L"explorer.exe";
+ WCHAR buffer[MAX_PATH];
+ BOOL appKnownSingular = FALSE;
+
+ /* last chance to translate IDList: now also allow CLSID paths */
- static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR lpstrProtocol, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc)
++ if (SUCCEEDED(SHELL_GetPathFromIDListForExecuteW((LPCITEMIDLIST)sei->lpIDList, buffer, sizeof(buffer)/sizeof(WCHAR)))) {
+ if (buffer[0] == ':' && buffer[1] == ':') {
+ /* open shell folder for the specified class GUID */
+ if (strlenW(buffer) + 1 > parametersLen)
+ ERR("parameters len exceeds buffer size (%i > %i), truncating\n",
+ lstrlenW(buffer) + 1, parametersLen);
+ lstrcpynW(wszParameters, buffer, parametersLen);
+ if (strlenW(wExplorer) > dwApplicationNameLen)
+ ERR("application len exceeds buffer size (%i > %i), truncating\n",
+ lstrlenW(wExplorer) + 1, dwApplicationNameLen);
+ lstrcpynW(wszApplicationName, wExplorer, dwApplicationNameLen);
+ appKnownSingular = TRUE;
+
+ sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
+ } else {
+ WCHAR target[MAX_PATH];
+ DWORD attribs;
+ DWORD resultLen;
+ /* Check if we're executing a directory and if so use the
+ handler for the Folder class */
+ strcpyW(target, buffer);
+ attribs = GetFileAttributesW(buffer);
+ if (attribs != INVALID_FILE_ATTRIBUTES &&
+ (attribs & FILE_ATTRIBUTE_DIRECTORY) &&
+ HCR_GetExecuteCommandW(0, L"Folder",
+ sei->lpVerb,
+ buffer, sizeof(buffer))) {
+ SHELL_ArgifyW(wszApplicationName, dwApplicationNameLen,
+ buffer, target, (LPITEMIDLIST)sei->lpIDList, NULL, &resultLen);
+ if (resultLen > dwApplicationNameLen)
+ ERR("Argify buffer not large enough... truncating\n");
+ appKnownSingular = FALSE;
+ }
+ sei->fMask &= ~SEE_MASK_INVOKEIDLIST;
+ }
+ }
+ return appKnownSingular;
+}
+
- TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(lpstrProtocol));
++static UINT_PTR SHELL_quote_and_execute(LPCWSTR wcmd, LPCWSTR wszParameters, LPCWSTR wszKeyname, LPCWSTR wszApplicationName, LPWSTR env, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc)
+{
+ UINT_PTR retval;
+ DWORD len;
+ WCHAR *wszQuotedCmd;
+
+ /* Length of quotes plus length of command plus NULL terminator */
+ len = 2 + lstrlenW(wcmd) + 1;
+ if (wszParameters[0])
+ {
+ /* Length of space plus length of parameters */
+ len += 1 + lstrlenW(wszParameters);
+ }
+ wszQuotedCmd = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ /* Must quote to handle case where cmd contains spaces,
+ * else security hole if malicious user creates executable file "C:\\Program"
+ */
+ strcpyW(wszQuotedCmd, L"\"");
+ strcatW(wszQuotedCmd, wcmd);
+ strcatW(wszQuotedCmd, L"\"");
+ if (wszParameters[0])
+ {
+ strcatW(wszQuotedCmd, L" ");
+ strcatW(wszQuotedCmd, wszParameters);
+ }
+
- if (*lpstrProtocol)
- retval = execute_from_key(lpstrProtocol, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out);
++ TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname));
+
- static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc)
++ if (*wszKeyname)
++ retval = execute_from_key(wszKeyname, wszApplicationName, env, psei->lpParameters, wcmd, execfunc, psei, psei_out);
+ else
+ retval = execfunc(wszQuotedCmd, env, FALSE, psei, psei_out);
+ HeapFree(GetProcessHeap(), 0, wszQuotedCmd);
+ return retval;
+}
+
- /* Looking for ...protocol\shell\lpOperation\command */
++static UINT_PTR SHELL_execute_url(LPCWSTR lpFile, LPCWSTR wcmd, LPSHELLEXECUTEINFOW psei, LPSHELLEXECUTEINFOW psei_out, SHELL_ExecuteW32 execfunc)
+{
+ static const WCHAR wShell[] = L"\\shell\\";
+ static const WCHAR wCommand[] = L"\\command";
+ UINT_PTR retval;
+ WCHAR *lpstrProtocol;
+ LPCWSTR lpstrRes;
+ INT iSize;
+ DWORD len;
+
+ lpstrRes = strchrW(lpFile, ':');
+ if (lpstrRes)
+ iSize = lpstrRes - lpFile;
+ else
+ iSize = strlenW(lpFile);
+
+ TRACE("Got URL: %s\n", debugstr_w(lpFile));
- if (psei->lpVerb)
++ /* Looking for ...<protocol>\shell\<lpVerb>\command */
+ len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1;
- strcatW(lpstrProtocol, psei->lpVerb ? psei->lpVerb : wszOpen);
++ if (psei->lpVerb && *psei->lpVerb)
+ len += lstrlenW(psei->lpVerb);
+ else
+ len += lstrlenW(wszOpen);
+ lpstrProtocol = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ memcpy(lpstrProtocol, lpFile, iSize * sizeof(WCHAR));
+ lpstrProtocol[iSize] = '\0';
+ strcatW(lpstrProtocol, wShell);
- /* Remove File Protocol from lpFile */
- /* In the case file://path/file */
- if (!strncmpiW(lpFile, wFile, iSize))
- {
- lpFile += iSize;
- while (*lpFile == ':') lpFile++;
- }
++ strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb : wszOpen);
+ strcatW(lpstrProtocol, wCommand);
+
- void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename)
+ retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters,
+ wcmd, execfunc, psei, psei_out);
+ HeapFree(GetProcessHeap(), 0, lpstrProtocol);
+ return retval;
+}
+
- BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
++static void do_error_dialog(UINT_PTR retval, HWND hwnd, WCHAR* filename)
+{
+ WCHAR msg[2048];
+ DWORD_PTR msgArguments[3] = { (DWORD_PTR)filename, 0, 0 };
+ DWORD error_code;
+
+ error_code = GetLastError();
+
+ if (retval == SE_ERR_NOASSOC)
+ LoadStringW(shell32_hInstance, IDS_SHLEXEC_NOASSOC, msg, sizeof(msg) / sizeof(WCHAR));
+ else
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ NULL,
+ error_code,
+ LANG_USER_DEFAULT,
+ msg,
+ sizeof(msg) / sizeof(WCHAR),
+ (va_list*)msgArguments);
+
+ MessageBoxW(hwnd, msg, NULL, MB_ICONERROR);
+}
+
+/*************************************************************************
+ * SHELL_execute [Internal]
+ */
- WCHAR lpstrProtocol[256];
++static BOOL SHELL_execute(LPSHELLEXECUTEINFOW sei, SHELL_ExecuteW32 execfunc)
+{
+ static const DWORD unsupportedFlags =
+ SEE_MASK_INVOKEIDLIST | SEE_MASK_ICON | SEE_MASK_HOTKEY |
+ SEE_MASK_CONNECTNETDRV | SEE_MASK_FLAG_DDEWAIT |
+ SEE_MASK_UNICODE | SEE_MASK_ASYNCOK | SEE_MASK_HMONITOR;
+
+ WCHAR parametersBuffer[1024], dirBuffer[MAX_PATH], wcmdBuffer[1024];
+ WCHAR *wszApplicationName, *wszParameters, *wszDir, *wcmd;
+ DWORD dwApplicationNameLen = MAX_PATH + 2;
+ DWORD parametersLen = sizeof(parametersBuffer) / sizeof(WCHAR);
+ DWORD dirLen = sizeof(dirBuffer) / sizeof(WCHAR);
+ DWORD wcmdLen = sizeof(wcmdBuffer) / sizeof(WCHAR);
+ DWORD len;
+ SHELLEXECUTEINFOW sei_tmp; /* modifiable copy of SHELLEXECUTEINFO struct */
+ WCHAR wfileName[MAX_PATH];
+ WCHAR *env;
- /* expand environment strings */
- len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0);
- if (len > 0)
++ WCHAR wszKeyname[256];
+ LPCWSTR lpFile;
+ UINT_PTR retval = SE_ERR_NOASSOC;
+ BOOL appKnownSingular = FALSE;
+
+ /* make a local copy of the LPSHELLEXECUTEINFO structure and work with this from now on */
+ sei_tmp = *sei;
+
+ TRACE("mask=0x%08x hwnd=%p verb=%s file=%s parm=%s dir=%s show=0x%08x class=%s\n",
+ sei_tmp.fMask, sei_tmp.hwnd, debugstr_w(sei_tmp.lpVerb),
+ debugstr_w(sei_tmp.lpFile), debugstr_w(sei_tmp.lpParameters),
+ debugstr_w(sei_tmp.lpDirectory), sei_tmp.nShow,
+ ((sei_tmp.fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME) ?
+ debugstr_w(sei_tmp.lpClass) : "not used");
+
+ sei->hProcess = NULL;
+
+ /* make copies of all path/command strings */
+ if (!sei_tmp.lpFile)
+ {
+ wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR));
+ *wszApplicationName = '\0';
+ }
+ else if (*sei_tmp.lpFile == '\"')
+ {
+ DWORD l = strlenW(sei_tmp.lpFile + 1);
+ if(l >= dwApplicationNameLen)
+ dwApplicationNameLen = l + 1;
+
+ wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR));
+ memcpy(wszApplicationName, sei_tmp.lpFile + 1, (l + 1)*sizeof(WCHAR));
+
+ if (wszApplicationName[l-1] == L'\"')
+ wszApplicationName[l-1] = L'\0';
+ appKnownSingular = TRUE;
+
+ TRACE("wszApplicationName=%s\n", debugstr_w(wszApplicationName));
+ }
+ else
+ {
+ DWORD l = strlenW(sei_tmp.lpFile) + 1;
+ if(l > dwApplicationNameLen) dwApplicationNameLen = l + 1;
+ wszApplicationName = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, dwApplicationNameLen * sizeof(WCHAR));
+ memcpy(wszApplicationName, sei_tmp.lpFile, l * sizeof(WCHAR));
+ }
+
+ wszParameters = parametersBuffer;
+ if (sei_tmp.lpParameters)
+ {
+ len = lstrlenW(sei_tmp.lpParameters) + 1;
+ if (len > parametersLen)
+ {
+ wszParameters = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ parametersLen = len;
+ }
+ strcpyW(wszParameters, sei_tmp.lpParameters);
+ }
+ else
+ *wszParameters = L'\0';
+
+ wszDir = dirBuffer;
+ if (sei_tmp.lpDirectory)
+ {
+ len = lstrlenW(sei_tmp.lpDirectory) + 1;
+ if (len > dirLen)
+ {
+ wszDir = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ dirLen = len;
+ }
+ strcpyW(wszDir, sei_tmp.lpDirectory);
+ }
+ else
+ *wszDir = L'\0';
+
+ /* adjust string pointers to point to the new buffers */
+ sei_tmp.lpFile = wszApplicationName;
+ sei_tmp.lpParameters = wszParameters;
+ sei_tmp.lpDirectory = wszDir;
+
+ if (sei_tmp.fMask & unsupportedFlags)
+ {
+ FIXME("flags ignored: 0x%08x\n", sei_tmp.fMask & unsupportedFlags);
+ }
+
+ /* process the IDList */
+ if (sei_tmp.fMask & SEE_MASK_IDLIST)
+ {
+ IShellExecuteHookW* pSEH;
+
+ HRESULT hr = SHBindToParent((LPCITEMIDLIST)sei_tmp.lpIDList, IID_PPV_ARG(IShellExecuteHookW, &pSEH), NULL);
+
+ if (SUCCEEDED(hr))
+ {
+ hr = pSEH->Execute(&sei_tmp);
+
+ pSEH->Release();
+
+ if (hr == S_OK)
+ {
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
+ if (wszParameters != parametersBuffer)
+ HeapFree(GetProcessHeap(), 0, wszParameters);
+ if (wszDir != dirBuffer)
+ HeapFree(GetProcessHeap(), 0, wszDir);
+ return TRUE;
+ }
+ }
+
+ SHGetPathFromIDListW((LPCITEMIDLIST)sei_tmp.lpIDList, wszApplicationName);
+ appKnownSingular = TRUE;
+ TRACE("-- idlist=%p (%s)\n", sei_tmp.lpIDList, debugstr_w(wszApplicationName));
+ }
+
+ if (ERROR_SUCCESS == ShellExecute_FromContextMenu(&sei_tmp))
+ {
+ sei->hInstApp = (HINSTANCE) 33;
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
+ if (wszParameters != parametersBuffer)
+ HeapFree(GetProcessHeap(), 0, wszParameters);
+ if (wszDir != dirBuffer)
+ HeapFree(GetProcessHeap(), 0, wszDir);
+ return TRUE;
+ }
+
+ if (sei_tmp.fMask & SEE_MASK_CLASSALL)
+ {
+ retval = SHELL_execute_class(wszApplicationName, &sei_tmp, sei, execfunc);
+ if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
+ {
+ OPENASINFO Info;
+
+ //FIXME
+ // need full path
+
+ Info.pcszFile = wszApplicationName;
+ Info.pcszClass = NULL;
+ Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
+
+ //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK)
+ do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName);
+ }
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
+ if (wszParameters != parametersBuffer)
+ HeapFree(GetProcessHeap(), 0, wszParameters);
+ if (wszDir != dirBuffer)
+ HeapFree(GetProcessHeap(), 0, wszDir);
+ return retval > 32;
+ }
+
+ /* Has the IDList not yet been translated? */
+ if (sei_tmp.fMask & SEE_MASK_IDLIST)
+ {
+ appKnownSingular = SHELL_translate_idlist( &sei_tmp, wszParameters,
+ parametersLen,
+ wszApplicationName,
+ dwApplicationNameLen );
+ }
+
- buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
++ /* convert file URLs */
++ if (UrlIsFileUrlW(sei_tmp.lpFile))
+ {
+ LPWSTR buf;
- ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1);
++ DWORD size;
++
++ size = MAX_PATH;
++ buf = static_cast<LPWSTR>(HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)));
++ if (!buf || FAILED(PathCreateFromUrlW(sei_tmp.lpFile, buf, &size, 0)))
++ {
++ HeapFree(GetProcessHeap(), 0, buf);
++ return SE_ERR_OOM;
++ }
+
- dwApplicationNameLen = len + 1;
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
- /* appKnownSingular unmodified */
-
++ dwApplicationNameLen = lstrlenW(buf) + 1;
+ wszApplicationName = buf;
-
- if (*sei_tmp.lpParameters)
+ sei_tmp.lpFile = wszApplicationName;
+ }
- len = ExpandEnvironmentStringsW(sei_tmp.lpParameters, NULL, 0);
++ else /* or expand environment strings (not both!) */
+ {
- len++;
- buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
- ExpandEnvironmentStringsW(sei_tmp.lpParameters, buf, len);
- if (wszParameters != parametersBuffer)
- HeapFree(GetProcessHeap(), 0, wszParameters);
- wszParameters = buf;
- parametersLen = len;
- sei_tmp.lpParameters = wszParameters;
++ len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0);
+ if (len > 0)
+ {
+ LPWSTR buf;
- lstrcpynW(wfileName, sei_tmp.lpFile, sizeof(wfileName) / sizeof(WCHAR));
++ buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
++
++ ExpandEnvironmentStringsW(sei_tmp.lpFile, buf, len + 1);
++ HeapFree(GetProcessHeap(), 0, wszApplicationName);
++ dwApplicationNameLen = len + 1;
++ wszApplicationName = buf;
++ /* appKnownSingular unmodified */
++
++ sei_tmp.lpFile = wszApplicationName;
+ }
+ }
+
+ if (*sei_tmp.lpDirectory)
+ {
+ len = ExpandEnvironmentStringsW(sei_tmp.lpDirectory, NULL, 0);
+ if (len > 0)
+ {
+ LPWSTR buf;
+ len++;
+ buf = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ ExpandEnvironmentStringsW(sei_tmp.lpDirectory, buf, len);
+ if (wszDir != dirBuffer)
+ HeapFree(GetProcessHeap(), 0, wszDir);
+ wszDir = buf;
+ sei_tmp.lpDirectory = wszDir;
+ }
+ }
+
+ /* Else, try to execute the filename */
+ TRACE("execute: %s,%s,%s\n", debugstr_w(wszApplicationName), debugstr_w(wszParameters), debugstr_w(wszDir));
+
+ /* separate out command line arguments from executable file name */
+ if (!*sei_tmp.lpParameters && !appKnownSingular)
+ {
+ /* If the executable path is quoted, handle the rest of the command line as parameters. */
+ if (sei_tmp.lpFile[0] == L'"')
+ {
+ LPWSTR src = wszApplicationName/*sei_tmp.lpFile*/ + 1;
+ LPWSTR dst = wfileName;
+ LPWSTR end;
+
+ /* copy the unquoted executable path to 'wfileName' */
+ while(*src && *src != L'"')
+ *dst++ = *src++;
+
+ *dst = L'\0';
+
+ if (*src == L'"')
+ {
+ end = ++src;
+
+ while(isspace(*src))
+ ++src;
+ }
+ else
+ end = src;
+
+ /* copy the parameter string to 'wszParameters' */
+ strcpyW(wszParameters, src);
+
+ /* terminate previous command string after the quote character */
+ *end = L'\0';
++ lpFile = wfileName;
+ }
+ else
+ {
+ /* If the executable name is not quoted, we have to use this search loop here,
+ that in CreateProcess() is not sufficient because it does not handle shell links. */
+ WCHAR buffer[MAX_PATH], xlpFile[MAX_PATH];
+ LPWSTR space, s;
+
+ LPWSTR beg = wszApplicationName/*sei_tmp.lpFile*/;
+ for(s = beg; (space = const_cast<LPWSTR>(strchrW(s, L' '))); s = space + 1)
+ {
+ int idx = space - sei_tmp.lpFile;
+ memcpy(buffer, sei_tmp.lpFile, idx * sizeof(WCHAR));
+ buffer[idx] = '\0';
+
+ /*FIXME This finds directory paths if the targeted file name contains spaces. */
+ if (SearchPathW(*sei_tmp.lpDirectory ? sei_tmp.lpDirectory : NULL, buffer, wszExe, sizeof(xlpFile) / sizeof(xlpFile[0]), xlpFile, NULL))
+ {
+ /* separate out command from parameter string */
+ LPCWSTR p = space + 1;
+
+ while(isspaceW(*p))
+ ++p;
+
+ strcpyW(wszParameters, p);
+ *space = L'\0';
+
+ break;
+ }
+ }
+
- lstrcpynW(wfileName, sei_tmp.lpFile, sizeof(wfileName) / sizeof(WCHAR));
-
- lpFile = wfileName;
++ lpFile = sei_tmp.lpFile;
+ }
+ }
+ else
- retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, lpstrProtocol, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters);
++ lpFile = sei_tmp.lpFile;
+
+ wcmd = wcmdBuffer;
+ len = lstrlenW(wszApplicationName) + 3;
+ if (sei_tmp.lpParameters[0])
+ len += 1 + lstrlenW(wszParameters);
+ if (len > wcmdLen)
+ {
+ wcmd = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ wcmdLen = len;
+ }
+ swprintf(wcmd, L"\"%s\"", wszApplicationName);
+ if (sei_tmp.lpParameters[0])
+ {
+ strcatW(wcmd, L" ");
+ strcatW(wcmd, wszParameters);
+ }
+
+ retval = execfunc(wcmd, NULL, FALSE, &sei_tmp, sei);
+ if (retval > 32)
+ {
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
+ if (wszParameters != parametersBuffer)
+ HeapFree(GetProcessHeap(), 0, wszParameters);
+ if (wszDir != dirBuffer)
+ HeapFree(GetProcessHeap(), 0, wszDir);
+ if (wcmd != wcmdBuffer)
+ HeapFree(GetProcessHeap(), 0, wcmd);
+ return TRUE;
+ }
+
+ /* Else, try to find the executable */
+ wcmd[0] = L'\0';
- retval = SHELL_quote_and_execute(wcmd, wszParameters, lpstrProtocol,
++ retval = SHELL_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters);
+ if (retval > 32) /* Found */
+ {
- lpstrProtocol,
++ retval = SHELL_quote_and_execute(wcmd, wszParameters, wszKeyname,
+ wszApplicationName, env, &sei_tmp,
+ sei, execfunc);
+ HeapFree(GetProcessHeap(), 0, env);
+ }
+ else if (PathIsDirectoryW(lpFile))
+ {
+ WCHAR wExec[MAX_PATH];
+ WCHAR * lpQuotedFile = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (strlenW(lpFile) + 3));
+
+ if (lpQuotedFile)
+ {
+ retval = SHELL_FindExecutable(sei_tmp.lpDirectory, L"explorer",
+ wszOpen, wExec, MAX_PATH,
+ NULL, &env, NULL, NULL);
+ if (retval > 32)
+ {
+ swprintf(lpQuotedFile, L"\"%s\"", lpFile);
+ retval = SHELL_quote_and_execute(wExec, lpQuotedFile,
- retval = SHELL_execute_url(lpFile, L"file", wcmd, &sei_tmp, sei, execfunc );
++ wszKeyname,
+ wszApplicationName, env,
+ &sei_tmp, sei, execfunc);
+ HeapFree(GetProcessHeap(), 0, env);
+ }
+ HeapFree(GetProcessHeap(), 0, lpQuotedFile);
+ }
+ else
+ retval = 0; /* Out of memory */
+ }
+ else if (PathIsURLW(lpFile)) /* File not found, check for URL */
+ {
- /* if so, append lpFile http:// and call ShellExecute */
++ retval = SHELL_execute_url(lpFile, wcmd, &sei_tmp, sei, execfunc );
+ }
+ /* Check if file specified is in the form www.??????.*** */
+ else if (!strncmpiW(lpFile, L"www", 3))
+ {
- HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile,
++ /* if so, prefix lpFile with http:// and call ShellExecute */
+ WCHAR lpstrTmpFile[256];
+ strcpyW(lpstrTmpFile, L"http://");
+ strcatW(lpstrTmpFile, lpFile);
+ retval = (UINT_PTR)ShellExecuteW(sei_tmp.hwnd, sei_tmp.lpVerb, lpstrTmpFile, NULL, NULL, 0);
+ }
+
+ TRACE("retval %lu\n", retval);
+
+ if (retval <= 32 && !(sei_tmp.fMask & SEE_MASK_FLAG_NO_UI))
+ {
+ OPENASINFO Info;
+
+ //FIXME
+ // need full path
+
+ Info.pcszFile = wszApplicationName;
+ Info.pcszClass = NULL;
+ Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_EXEC;
+
+ //if (SHOpenWithDialog(sei_tmp.hwnd, &Info) != S_OK)
+ do_error_dialog(retval, sei_tmp.hwnd, wszApplicationName);
+ }
+
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
+ if (wszParameters != parametersBuffer)
+ HeapFree(GetProcessHeap(), 0, wszParameters);
+ if (wszDir != dirBuffer)
+ HeapFree(GetProcessHeap(), 0, wszDir);
+ if (wcmd != wcmdBuffer)
+ HeapFree(GetProcessHeap(), 0, wcmd);
+
+ sei->hInstApp = (HINSTANCE)(retval > 32 ? 33 : retval);
+
+ return retval > 32;
+}
+
+/*************************************************************************
+ * ShellExecuteA [SHELL32.290]
+ */
- hWnd, debugstr_a(lpOperation), debugstr_a(lpFile),
++HINSTANCE WINAPI ShellExecuteA(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile,
+ LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd)
+{
+ SHELLEXECUTEINFOA sei;
+
+ TRACE("%p,%s,%s,%s,%s,%d\n",
- sei.lpVerb = lpOperation;
++ hWnd, debugstr_a(lpVerb), debugstr_a(lpFile),
+ debugstr_a(lpParameters), debugstr_a(lpDirectory), iShowCmd);
+
+ sei.cbSize = sizeof(sei);
+ sei.fMask = SEE_MASK_FLAG_NO_UI;
+ sei.hwnd = hWnd;
- * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpOperation,
++ sei.lpVerb = lpVerb;
+ sei.lpFile = lpFile;
+ sei.lpParameters = lpParameters;
+ sei.lpDirectory = lpDirectory;
+ sei.nShow = iShowCmd;
+ sei.lpIDList = 0;
+ sei.lpClass = 0;
+ sei.hkeyClass = 0;
+ sei.dwHotKey = 0;
+ sei.hProcess = 0;
+
+ ShellExecuteExA(&sei);
+ return sei.hInstApp;
+}
+
+/*************************************************************************
+ * ShellExecuteExA [SHELL32.292]
+ *
+ */
+BOOL
+WINAPI
+DECLSPEC_HOTPATCH
+ShellExecuteExA(LPSHELLEXECUTEINFOA sei)
+{
+ SHELLEXECUTEINFOW seiW;
+ BOOL ret;
+ WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL, *wClass = NULL;
+
+ TRACE("%p\n", sei);
+
+ memcpy(&seiW, sei, sizeof(SHELLEXECUTEINFOW));
+
+ if (sei->lpVerb)
+ seiW.lpVerb = __SHCloneStrAtoW(&wVerb, sei->lpVerb);
+
+ if (sei->lpFile)
+ seiW.lpFile = __SHCloneStrAtoW(&wFile, sei->lpFile);
+
+ if (sei->lpParameters)
+ seiW.lpParameters = __SHCloneStrAtoW(&wParameters, sei->lpParameters);
+
+ if (sei->lpDirectory)
+ seiW.lpDirectory = __SHCloneStrAtoW(&wDirectory, sei->lpDirectory);
+
+ if ((sei->fMask & SEE_MASK_CLASSALL) == SEE_MASK_CLASSNAME && sei->lpClass)
+ seiW.lpClass = __SHCloneStrAtoW(&wClass, sei->lpClass);
+ else
+ seiW.lpClass = NULL;
+
+ ret = SHELL_execute(&seiW, SHELL_ExecuteW);
+
+ sei->hInstApp = seiW.hInstApp;
+
+ if (sei->fMask & SEE_MASK_NOCLOSEPROCESS)
+ sei->hProcess = seiW.hProcess;
+
+ SHFree(wVerb);
+ SHFree(wFile);
+ SHFree(wParameters);
+ SHFree(wDirectory);
+ SHFree(wClass);
+
+ return ret;
+}
+
+/*************************************************************************
+ * ShellExecuteExW [SHELL32.293]
+ *
+ */
+BOOL
+WINAPI
+DECLSPEC_HOTPATCH
+ShellExecuteExW(LPSHELLEXECUTEINFOW sei)
+{
+ return SHELL_execute(sei, SHELL_ExecuteW);
+}
+
+/*************************************************************************
+ * ShellExecuteW [SHELL32.294]
+ * from shellapi.h
- HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile,
++ * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb,
+ * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
+ */
- sei.lpVerb = lpOperation;
++HINSTANCE WINAPI ShellExecuteW(HWND hwnd, LPCWSTR lpVerb, LPCWSTR lpFile,
+ LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd)
+{
+ SHELLEXECUTEINFOW sei;
+
+ TRACE("\n");
+ sei.cbSize = sizeof(sei);
+ sei.fMask = SEE_MASK_FLAG_NO_UI;
+ sei.hwnd = hwnd;
- EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpOperation, LPCSTR lpFile,
++ sei.lpVerb = lpVerb;
+ sei.lpFile = lpFile;
+ sei.lpParameters = lpParameters;
+ sei.lpDirectory = lpDirectory;
+ sei.nShow = nShowCmd;
+ sei.lpIDList = 0;
+ sei.lpClass = 0;
+ sei.hkeyClass = 0;
+ sei.dwHotKey = 0;
+ sei.hProcess = 0;
+
+ SHELL_execute(&sei, SHELL_ExecuteW);
+ return sei.hInstApp;
+}
+
+/*************************************************************************
+ * WOWShellExecute [SHELL32.@]
+ *
+ * FIXME: the callback function most likely doesn't work the same way on Windows.
+ */
- seiW.lpVerb = lpOperation ? __SHCloneStrAtoW(&wVerb, lpOperation) : NULL;
++EXTERN_C HINSTANCE WINAPI WOWShellExecute(HWND hWnd, LPCSTR lpVerb, LPCSTR lpFile,
+ LPCSTR lpParameters, LPCSTR lpDirectory, INT iShowCmd, void *callback)
+{
+ SHELLEXECUTEINFOW seiW;
+ WCHAR *wVerb = NULL, *wFile = NULL, *wParameters = NULL, *wDirectory = NULL;
+ HANDLE hProcess = 0;
+
++ seiW.lpVerb = lpVerb ? __SHCloneStrAtoW(&wVerb, lpVerb) : NULL;
+ seiW.lpFile = lpFile ? __SHCloneStrAtoW(&wFile, lpFile) : NULL;
+ seiW.lpParameters = lpParameters ? __SHCloneStrAtoW(&wParameters, lpParameters) : NULL;
+ seiW.lpDirectory = lpDirectory ? __SHCloneStrAtoW(&wDirectory, lpDirectory) : NULL;
+
+ seiW.cbSize = sizeof(seiW);
+ seiW.fMask = 0;
+ seiW.hwnd = hWnd;
+ seiW.nShow = iShowCmd;
+ seiW.lpIDList = 0;
+ seiW.lpClass = 0;
+ seiW.hkeyClass = 0;
+ seiW.dwHotKey = 0;
+ seiW.hProcess = hProcess;
+
+ SHELL_execute(&seiW, (SHELL_ExecuteW32)callback);
+
+ SHFree(wVerb);
+ SHFree(wFile);
+ SHFree(wParameters);
+ SHFree(wDirectory);
+ return seiW.hInstApp;
+}
+
+/*************************************************************************
+ * OpenAs_RunDLLA [SHELL32.@]
+ */
+EXTERN_C void WINAPI OpenAs_RunDLLA(HWND hwnd, HINSTANCE hinst, LPCSTR cmdline, int cmdshow)
+{
+ FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_a(cmdline), cmdshow);
+}
+
+/*************************************************************************
+ * OpenAs_RunDLLW [SHELL32.@]
+ */
+EXTERN_C void WINAPI OpenAs_RunDLLW(HWND hwnd, HINSTANCE hinst, LPCWSTR cmdline, int cmdshow)
+{
+ FIXME("%p, %p, %s, %d\n", hwnd, hinst, debugstr_w(cmdline), cmdshow);
+}
--- /dev/null
- hNewMenu = co_IntLoadSysMenuTemplate();
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS Win32k subsystem
+ * PURPOSE: Windows
+ * FILE: subsystems/win32/win32k/ntuser/window.c
+ * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ */
+
+#include <win32k.h>
+DBG_DEFAULT_CHANNEL(UserWnd);
+
+INT gNestedWindowLimit = 50;
+
+/* HELPER FUNCTIONS ***********************************************************/
+
+BOOL FASTCALL UserUpdateUiState(PWND Wnd, WPARAM wParam)
+{
+ WORD Action = LOWORD(wParam);
+ WORD Flags = HIWORD(wParam);
+
+ if (Flags & ~(UISF_HIDEFOCUS | UISF_HIDEACCEL | UISF_ACTIVE))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ switch (Action)
+ {
+ case UIS_INITIALIZE:
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+
+ case UIS_SET:
+ if (Flags & UISF_HIDEFOCUS)
+ Wnd->HideFocus = TRUE;
+ if (Flags & UISF_HIDEACCEL)
+ Wnd->HideAccel = TRUE;
+ break;
+
+ case UIS_CLEAR:
+ if (Flags & UISF_HIDEFOCUS)
+ Wnd->HideFocus = FALSE;
+ if (Flags & UISF_HIDEACCEL)
+ Wnd->HideAccel = FALSE;
+ break;
+ }
+
+ return TRUE;
+}
+
+PWND FASTCALL IntGetWindowObject(HWND hWnd)
+{
+ PWND Window;
+
+ if (!hWnd) return NULL;
+
+ Window = UserGetWindowObject(hWnd);
+ if (Window)
+ Window->head.cLockObj++;
+
+ return Window;
+}
+
+PWND FASTCALL VerifyWnd(PWND pWnd)
+{
+ HWND hWnd;
+ UINT State, State2;
+
+ if (!pWnd) return NULL;
+
+ _SEH2_TRY
+ {
+ hWnd = UserHMGetHandle(pWnd);
+ State = pWnd->state;
+ State2 = pWnd->state2;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ _SEH2_YIELD(return NULL);
+ }
+ _SEH2_END
+
+ if ( UserObjectInDestroy(hWnd) ||
+ State & WNDS_DESTROYED ||
+ State2 & WNDS2_INDESTROY )
+ return NULL;
+
+ return pWnd;
+}
+
+PWND FASTCALL ValidateHwndNoErr(HWND hWnd)
+{
+ if (hWnd) return (PWND)UserGetObjectNoErr(gHandleTable, hWnd, TYPE_WINDOW);
+ return NULL;
+}
+
+/* Temp HACK */
+PWND FASTCALL UserGetWindowObject(HWND hWnd)
+{
+ PWND Window;
+
+ if (!hWnd)
+ {
+ EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
+ return NULL;
+ }
+
+ Window = (PWND)UserGetObject(gHandleTable, hWnd, TYPE_WINDOW);
+ if (!Window || 0 != (Window->state & WNDS_DESTROYED))
+ {
+ EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
+ return NULL;
+ }
+
+ return Window;
+}
+
+ULONG FASTCALL
+IntSetStyle( PWND pwnd, ULONG set_bits, ULONG clear_bits )
+{
+ ULONG styleOld, styleNew;
+ styleOld = pwnd->style;
+ styleNew = (pwnd->style | set_bits) & ~clear_bits;
+ if (styleNew == styleOld) return styleNew;
+ pwnd->style = styleNew;
+ if ((styleOld ^ styleNew) & WS_VISIBLE) // State Change.
+ {
+ if (styleOld & WS_VISIBLE) pwnd->head.pti->cVisWindows--;
+ if (styleNew & WS_VISIBLE) pwnd->head.pti->cVisWindows++;
+ DceResetActiveDCEs( pwnd );
+ }
+ return styleOld;
+}
+
+/*
+ * IntIsWindow
+ *
+ * The function determines whether the specified window handle identifies
+ * an existing window.
+ *
+ * Parameters
+ * hWnd
+ * Handle to the window to test.
+ *
+ * Return Value
+ * If the window handle identifies an existing window, the return value
+ * is TRUE. If the window handle does not identify an existing window,
+ * the return value is FALSE.
+ */
+
+BOOL FASTCALL
+IntIsWindow(HWND hWnd)
+{
+ PWND Window;
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL FASTCALL
+IntIsWindowVisible(PWND Wnd)
+{
+ PWND Temp = Wnd;
+ for (;;)
+ {
+ if (!Temp) return TRUE;
+ if (!(Temp->style & WS_VISIBLE)) break;
+ if (Temp->style & WS_MINIMIZE && Temp != Wnd) break;
+ if (Temp->fnid == FNID_DESKTOP) return TRUE;
+ Temp = Temp->spwndParent;
+ }
+ return FALSE;
+}
+
+PWND FASTCALL
+IntGetParent(PWND Wnd)
+{
+ if (Wnd->style & WS_POPUP)
+ {
+ return Wnd->spwndOwner;
+ }
+ else if (Wnd->style & WS_CHILD)
+ {
+ return Wnd->spwndParent;
+ }
+
+ return NULL;
+}
+
+BOOL
+FASTCALL
+IntEnableWindow( HWND hWnd, BOOL bEnable )
+{
+ BOOL Update;
+ PWND pWnd;
+ UINT bIsDisabled;
+
+ if(!(pWnd = UserGetWindowObject(hWnd)))
+ {
+ return FALSE;
+ }
+
+ /* check if updating is needed */
+ bIsDisabled = !!(pWnd->style & WS_DISABLED);
+ Update = bIsDisabled;
+
+ if (bEnable)
+ {
+ IntSetStyle( pWnd, 0, WS_DISABLED );
+ }
+ else
+ {
+ Update = !bIsDisabled;
+
+ co_IntSendMessage( hWnd, WM_CANCELMODE, 0, 0);
+
+ /* Remove keyboard focus from that window if it had focus */
+ if (hWnd == IntGetThreadFocusWindow())
+ {
+ TRACE("IntEnableWindow SF NULL\n");
+ co_UserSetFocus(NULL);
+ }
+ IntSetStyle( pWnd, WS_DISABLED, 0 );
+ }
+
+ if (Update)
+ {
+ IntNotifyWinEvent(EVENT_OBJECT_STATECHANGE, pWnd, OBJID_WINDOW, CHILDID_SELF, 0);
+ co_IntSendMessage(hWnd, WM_ENABLE, (LPARAM)bEnable, 0);
+ }
+ // Return nonzero if it was disabled, or zero if it wasn't:
+ return bIsDisabled;
+}
+
+/*
+ * IntWinListChildren
+ *
+ * Compile a list of all child window handles from given window.
+ *
+ * Remarks
+ * This function is similar to Wine WIN_ListChildren. The caller
+ * must free the returned list with ExFreePool.
+ */
+
+HWND* FASTCALL
+IntWinListChildren(PWND Window)
+{
+ PWND Child;
+ HWND *List;
+ UINT Index, NumChildren = 0;
+
+ if (!Window) return NULL;
+
+ for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
+ ++NumChildren;
+
+ List = ExAllocatePoolWithTag(PagedPool, (NumChildren + 1) * sizeof(HWND), USERTAG_WINDOWLIST);
+ if(!List)
+ {
+ ERR("Failed to allocate memory for children array\n");
+ EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return NULL;
+ }
+ for (Child = Window->spwndChild, Index = 0;
+ Child != NULL;
+ Child = Child->spwndNext, ++Index)
+ List[Index] = Child->head.h;
+ List[Index] = NULL;
+
+ return List;
+}
+
+PWND FASTCALL
+IntGetNonChildAncestor(PWND pWnd)
+{
+ while(pWnd && (pWnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
+ pWnd = pWnd->spwndParent;
+ return pWnd;
+}
+
+BOOL FASTCALL
+IntIsTopLevelWindow(PWND pWnd)
+{
+ if ( pWnd->spwndParent &&
+ pWnd->spwndParent == co_GetDesktopWindow(pWnd) ) return TRUE;
+ return FALSE;
+}
+
+BOOL FASTCALL
+IntValidateOwnerDepth(PWND Wnd, PWND Owner)
+{
+ INT Depth = 1;
+ for (;;)
+ {
+ if ( !Owner ) return gNestedWindowLimit >= Depth;
+ if (Owner == Wnd) break;
+ Owner = Owner->spwndOwner;
+ Depth++;
+ }
+ return FALSE;
+}
+
+HWND FASTCALL
+IntGetWindow(HWND hWnd,
+ UINT uCmd)
+{
+ PWND Wnd, FoundWnd;
+ HWND Ret = NULL;
+
+ Wnd = ValidateHwndNoErr(hWnd);
+ if (!Wnd)
+ return NULL;
+
+ FoundWnd = NULL;
+ switch (uCmd)
+ {
+ case GW_OWNER:
+ if (Wnd->spwndOwner != NULL)
+ FoundWnd = Wnd->spwndOwner;
+ break;
+
+ case GW_HWNDFIRST:
+ if(Wnd->spwndParent != NULL)
+ {
+ FoundWnd = Wnd->spwndParent;
+ if (FoundWnd->spwndChild != NULL)
+ FoundWnd = FoundWnd->spwndChild;
+ }
+ break;
+ case GW_HWNDNEXT:
+ if (Wnd->spwndNext != NULL)
+ FoundWnd = Wnd->spwndNext;
+ break;
+
+ case GW_HWNDPREV:
+ if (Wnd->spwndPrev != NULL)
+ FoundWnd = Wnd->spwndPrev;
+ break;
+
+ case GW_CHILD:
+ if (Wnd->spwndChild != NULL)
+ FoundWnd = Wnd->spwndChild;
+ break;
+
+ case GW_HWNDLAST:
+ FoundWnd = Wnd;
+ while ( FoundWnd->spwndNext != NULL)
+ FoundWnd = FoundWnd->spwndNext;
+ break;
+
+ default:
+ Wnd = NULL;
+ break;
+ }
+
+ if (FoundWnd != NULL)
+ Ret = UserHMGetHandle(FoundWnd);
+ return Ret;
+}
+
+/***********************************************************************
+ * IntSendDestroyMsg
+ */
+static void IntSendDestroyMsg(HWND hWnd)
+{
+
+ PWND Window;
+#if 0 /* FIXME */
+
+ GUITHREADINFO info;
+
+ if (GetGUIThreadInfo(GetCurrentThreadId(), &info))
+ {
+ if (hWnd == info.hwndCaret)
+ {
+ DestroyCaret();
+ }
+ }
+#endif
+
+ Window = UserGetWindowObject(hWnd);
+ if (Window)
+ {
+// USER_REFERENCE_ENTRY Ref;
+// UserRefObjectCo(Window, &Ref);
+
+ if (!Window->spwndOwner && !IntGetParent(Window))
+ {
+ co_IntShellHookNotify(HSHELL_WINDOWDESTROYED, (WPARAM) hWnd, 0);
+ }
+
+// UserDerefObjectCo(Window);
+ }
+
+ /* The window could already be destroyed here */
+
+ /*
+ * Send the WM_DESTROY to the window.
+ */
+
+ co_IntSendMessage(hWnd, WM_DESTROY, 0, 0);
+
+ /*
+ * This WM_DESTROY message can trigger re-entrant calls to DestroyWindow
+ * make sure that the window still exists when we come back.
+ */
+#if 0 /* FIXME */
+
+ if (IsWindow(Wnd))
+ {
+ HWND* pWndArray;
+ int i;
+
+ if (!(pWndArray = WIN_ListChildren( hwnd )))
+ return;
+
+ /* start from the end (FIXME: is this needed?) */
+ for (i = 0; pWndArray[i]; i++)
+ ;
+
+ while (--i >= 0)
+ {
+ if (IsWindow( pWndArray[i] ))
+ WIN_SendDestroyMsg( pWndArray[i] );
+ }
+ HeapFree(GetProcessHeap(), 0, pWndArray);
+ }
+ else
+ {
+ TRACE("destroyed itself while in WM_DESTROY!\n");
+ }
+#endif
+}
+
+static VOID
+UserFreeWindowInfo(PTHREADINFO ti, PWND Wnd)
+{
+ PCLIENTINFO ClientInfo = GetWin32ClientInfo();
+
+ if (!Wnd) return;
+
+ if (ClientInfo->CallbackWnd.pWnd == DesktopHeapAddressToUser(Wnd))
+ {
+ ClientInfo->CallbackWnd.hWnd = NULL;
+ ClientInfo->CallbackWnd.pWnd = NULL;
+ }
+
+ if (Wnd->strName.Buffer != NULL)
+ {
+ Wnd->strName.Length = 0;
+ Wnd->strName.MaximumLength = 0;
+ DesktopHeapFree(Wnd->head.rpdesk,
+ Wnd->strName.Buffer);
+ Wnd->strName.Buffer = NULL;
+ }
+
+// DesktopHeapFree(Wnd->head.rpdesk, Wnd);
+// WindowObject->hWnd = NULL;
+}
+
+/***********************************************************************
+ * IntDestroyWindow
+ *
+ * Destroy storage associated to a window. "Internals" p.358
+ *
+ * This is the "functional" DestroyWindows function ei. all stuff
+ * done in CreateWindow is undone here and not in DestroyWindow:-P
+
+ */
+static LRESULT co_UserFreeWindow(PWND Window,
+ PPROCESSINFO ProcessData,
+ PTHREADINFO ThreadData,
+ BOOLEAN SendMessages)
+{
+ HWND *Children;
+ HWND *ChildHandle;
+ PWND Child;
+ PMENU Menu;
+ BOOLEAN BelongsToThreadData;
+
+ ASSERT(Window);
+
+ if(Window->state2 & WNDS2_INDESTROY)
+ {
+ TRACE("Tried to call IntDestroyWindow() twice\n");
+ return 0;
+ }
+ Window->state2 |= WNDS2_INDESTROY;
+ Window->style &= ~WS_VISIBLE;
+ Window->head.pti->cVisWindows--;
+
+ IntNotifyWinEvent(EVENT_OBJECT_DESTROY, Window, OBJID_WINDOW, CHILDID_SELF, 0);
+
+ /* remove the window already at this point from the thread window list so we
+ don't get into trouble when destroying the thread windows while we're still
+ in IntDestroyWindow() */
+ RemoveEntryList(&Window->ThreadListEntry);
+
+ BelongsToThreadData = IntWndBelongsToThread(Window, ThreadData);
+
+ IntDeRegisterShellHookWindow(Window->head.h);
+
+ if(SendMessages)
+ {
+ /* Send destroy messages */
+ IntSendDestroyMsg(Window->head.h);
+ }
+
+ /* free child windows */
+ Children = IntWinListChildren(Window);
+ if (Children)
+ {
+ for (ChildHandle = Children; *ChildHandle; ++ChildHandle)
+ {
+ if ((Child = IntGetWindowObject(*ChildHandle)))
+ {
+ if(!IntWndBelongsToThread(Child, ThreadData))
+ {
+ /* send WM_DESTROY messages to windows not belonging to the same thread */
+ IntSendDestroyMsg(Child->head.h);
+ }
+ else
+ co_UserFreeWindow(Child, ProcessData, ThreadData, SendMessages);
+
+ UserDereferenceObject(Child);
+ }
+ }
+ ExFreePoolWithTag(Children, USERTAG_WINDOWLIST);
+ }
+
+ if(SendMessages)
+ {
+ /*
+ * Clear the update region to make sure no WM_PAINT messages will be
+ * generated for this window while processing the WM_NCDESTROY.
+ */
+ co_UserRedrawWindow(Window, NULL, 0,
+ RDW_VALIDATE | RDW_NOFRAME | RDW_NOERASE |
+ RDW_NOINTERNALPAINT | RDW_NOCHILDREN);
+ if(BelongsToThreadData)
+ co_IntSendMessage(Window->head.h, WM_NCDESTROY, 0, 0);
+ }
+
+ DestroyTimersForWindow(ThreadData, Window);
+
+ /* Unregister hot keys */
+ UnregisterWindowHotKeys (Window);
+
+ /* flush the message queue */
+ MsqRemoveWindowMessagesFromQueue(Window);
+
+ /* from now on no messages can be sent to this window anymore */
+ Window->state |= WNDS_DESTROYED;
+ Window->fnid |= FNID_FREED;
+
+ /* don't remove the WINDOWSTATUS_DESTROYING bit */
+
+ /* reset shell window handles */
+ if(ThreadData->rpdesk)
+ {
+ if (Window->head.h == ThreadData->rpdesk->rpwinstaParent->ShellWindow)
+ ThreadData->rpdesk->rpwinstaParent->ShellWindow = NULL;
+
+ if (Window->head.h == ThreadData->rpdesk->rpwinstaParent->ShellListView)
+ ThreadData->rpdesk->rpwinstaParent->ShellListView = NULL;
+ }
+
+ /* FIXME: do we need to fake QS_MOUSEMOVE wakebit? */
+
+#if 0 /* FIXME */
+
+ WinPosCheckInternalPos(Window->head.h);
+ if (Window->head.h == GetCapture())
+ {
+ ReleaseCapture();
+ }
+
+ /* free resources associated with the window */
+ TIMER_RemoveWindowTimers(Window->head.h);
+#endif
+
+ if ( ((Window->style & (WS_CHILD|WS_POPUP)) != WS_CHILD) &&
+ Window->IDMenu &&
+ (Menu = UserGetMenuObject((HMENU)Window->IDMenu)))
+ {
+ IntDestroyMenuObject(Menu, TRUE, TRUE);
+ Window->IDMenu = 0;
+ }
+
+ if(Window->SystemMenu
+ && (Menu = UserGetMenuObject(Window->SystemMenu)))
+ {
+ IntDestroyMenuObject(Menu, TRUE, TRUE);
+ Window->SystemMenu = (HMENU)0;
+ }
+
+ DceFreeWindowDCE(Window); /* Always do this to catch orphaned DCs */
+#if 0 /* FIXME */
+
+ WINPROC_FreeProc(Window->winproc, WIN_PROC_WINDOW);
+ CLASS_RemoveWindow(Window->Class);
+#endif
+
+ IntUnlinkWindow(Window);
+
+ if (Window->PropListItems)
+ {
+ IntRemoveWindowProp(Window);
+ TRACE("Window->PropListItems %d\n",Window->PropListItems);
+ ASSERT(Window->PropListItems==0);
+ }
+
+ UserReferenceObject(Window);
+ UserDeleteObject(Window->head.h, TYPE_WINDOW);
+
+ IntDestroyScrollBars(Window);
+
+ /* dereference the class */
+ IntDereferenceClass(Window->pcls,
+ Window->head.pti->pDeskInfo,
+ Window->head.pti->ppi);
+ Window->pcls = NULL;
+
+ if(Window->hrgnClip)
+ {
+ IntGdiSetRegionOwner(Window->hrgnClip, GDI_OBJ_HMGR_POWNED);
+ GreDeleteObject(Window->hrgnClip);
+ Window->hrgnClip = NULL;
+ }
+ Window->head.pti->cWindows--;
+
+// ASSERT(Window != NULL);
+ UserFreeWindowInfo(Window->head.pti, Window);
+
+ UserDereferenceObject(Window);
+
+ UserClipboardFreeWindow(Window);
+
+ return 0;
+}
+
+//
+// Same as User32:IntGetWndProc.
+//
+WNDPROC FASTCALL
+IntGetWindowProc(PWND pWnd,
+ BOOL Ansi)
+{
+ INT i;
+ PCLS Class;
+ WNDPROC gcpd, Ret = 0;
+
+ ASSERT(UserIsEnteredExclusive() == TRUE);
+
+ Class = pWnd->pcls;
+
+ if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
+ {
+ for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
+ {
+ if (GETPFNSERVER(i) == pWnd->lpfnWndProc)
+ {
+ if (Ansi)
+ Ret = GETPFNCLIENTA(i);
+ else
+ Ret = GETPFNCLIENTW(i);
+ }
+ }
+ return Ret;
+ }
+
+ if (Class->fnid == FNID_EDIT)
+ Ret = pWnd->lpfnWndProc;
+ else
+ {
+ Ret = pWnd->lpfnWndProc;
+
+ if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
+ {
+ if (Ansi)
+ {
+ if (GETPFNCLIENTW(Class->fnid) == pWnd->lpfnWndProc)
+ Ret = GETPFNCLIENTA(Class->fnid);
+ }
+ else
+ {
+ if (GETPFNCLIENTA(Class->fnid) == pWnd->lpfnWndProc)
+ Ret = GETPFNCLIENTW(Class->fnid);
+ }
+ }
+ if ( Ret != pWnd->lpfnWndProc)
+ return Ret;
+ }
+ if ( Ansi == !!(pWnd->state & WNDS_ANSIWINDOWPROC) )
+ return Ret;
+
+ gcpd = (WNDPROC)UserGetCPD(
+ pWnd,
+ (Ansi ? UserGetCPDA2U : UserGetCPDU2A )|UserGetCPDWindow,
+ (ULONG_PTR)Ret);
+
+ return (gcpd ? gcpd : Ret);
+}
+
+static WNDPROC
+IntSetWindowProc(PWND pWnd,
+ WNDPROC NewWndProc,
+ BOOL Ansi)
+{
+ INT i;
+ PCALLPROCDATA CallProc;
+ PCLS Class;
+ WNDPROC Ret, chWndProc = NULL;
+
+ // Retrieve previous window proc.
+ Ret = IntGetWindowProc(pWnd, Ansi);
+
+ Class = pWnd->pcls;
+
+ if (IsCallProcHandle(NewWndProc))
+ {
+ CallProc = UserGetObject(gHandleTable, NewWndProc, TYPE_CALLPROC);
+ if (CallProc)
+ { // Reset new WndProc.
+ NewWndProc = CallProc->pfnClientPrevious;
+ // Reset Ansi from CallProc handle. This is expected with wine "deftest".
+ Ansi = !!(CallProc->wType & UserGetCPDU2A);
+ }
+ }
+ // Switch from Client Side call to Server Side call if match. Ref: "deftest".
+ for ( i = FNID_FIRST; i <= FNID_SWITCH; i++)
+ {
+ if (GETPFNCLIENTW(i) == NewWndProc)
+ {
+ chWndProc = GETPFNSERVER(i);
+ break;
+ }
+ if (GETPFNCLIENTA(i) == NewWndProc)
+ {
+ chWndProc = GETPFNSERVER(i);
+ break;
+ }
+ }
+ // If match, set/reset to Server Side and clear ansi.
+ if (chWndProc)
+ {
+ pWnd->lpfnWndProc = chWndProc;
+ pWnd->Unicode = TRUE;
+ pWnd->state &= ~WNDS_ANSIWINDOWPROC;
+ pWnd->state |= WNDS_SERVERSIDEWINDOWPROC;
+ }
+ else
+ {
+ pWnd->Unicode = !Ansi;
+ // Handle the state change in here.
+ if (Ansi)
+ pWnd->state |= WNDS_ANSIWINDOWPROC;
+ else
+ pWnd->state &= ~WNDS_ANSIWINDOWPROC;
+
+ if (pWnd->state & WNDS_SERVERSIDEWINDOWPROC)
+ pWnd->state &= ~WNDS_SERVERSIDEWINDOWPROC;
+
+ if (!NewWndProc) NewWndProc = pWnd->lpfnWndProc;
+
+ if (Class->fnid <= FNID_GHOST && Class->fnid >= FNID_BUTTON)
+ {
+ if (Ansi)
+ {
+ if (GETPFNCLIENTW(Class->fnid) == NewWndProc)
+ chWndProc = GETPFNCLIENTA(Class->fnid);
+ }
+ else
+ {
+ if (GETPFNCLIENTA(Class->fnid) == NewWndProc)
+ chWndProc = GETPFNCLIENTW(Class->fnid);
+ }
+ }
+ // Now set the new window proc.
+ pWnd->lpfnWndProc = (chWndProc ? chWndProc : NewWndProc);
+ }
+ return Ret;
+}
+
+static BOOL FASTCALL
+IntSetMenu(
+ PWND Wnd,
+ HMENU Menu,
+ BOOL *Changed)
+{
+ PMENU OldMenu, NewMenu = NULL;
+
+ if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
+ {
+ ERR("SetMenu: Invalid handle 0x%p!\n",UserHMGetHandle(Wnd));
+ EngSetLastError(ERROR_INVALID_WINDOW_HANDLE);
+ return FALSE;
+ }
+
+ *Changed = (Wnd->IDMenu != (UINT) Menu);
+ if (! *Changed)
+ {
+ return TRUE;
+ }
+
+ if (Wnd->IDMenu)
+ {
+ OldMenu = IntGetMenuObject((HMENU) Wnd->IDMenu);
+ ASSERT(NULL == OldMenu || OldMenu->hWnd == Wnd->head.h);
+ }
+ else
+ {
+ OldMenu = NULL;
+ }
+
+ if (NULL != Menu)
+ {
+ NewMenu = IntGetMenuObject(Menu);
+ if (NULL == NewMenu)
+ {
+ if (NULL != OldMenu)
+ {
+ IntReleaseMenuObject(OldMenu);
+ }
+ EngSetLastError(ERROR_INVALID_MENU_HANDLE);
+ return FALSE;
+ }
+ if (NULL != NewMenu->hWnd)
+ {
+ /* Can't use the same menu for two windows */
+ if (NULL != OldMenu)
+ {
+ IntReleaseMenuObject(OldMenu);
+ }
+ EngSetLastError(ERROR_INVALID_MENU_HANDLE);
+ return FALSE;
+ }
+
+ }
+
+ Wnd->IDMenu = (UINT) Menu;
+ if (NULL != NewMenu)
+ {
+ NewMenu->hWnd = Wnd->head.h;
+ IntReleaseMenuObject(NewMenu);
+ }
+ if (NULL != OldMenu)
+ {
+ OldMenu->hWnd = NULL;
+ IntReleaseMenuObject(OldMenu);
+ }
+
+ return TRUE;
+}
+
+
+/* INTERNAL ******************************************************************/
+
+
+VOID FASTCALL
+co_DestroyThreadWindows(struct _ETHREAD *Thread)
+{
+ PTHREADINFO WThread;
+ PLIST_ENTRY Current;
+ PWND Wnd;
+ USER_REFERENCE_ENTRY Ref;
+ WThread = (PTHREADINFO)Thread->Tcb.Win32Thread;
+
+ while (!IsListEmpty(&WThread->WindowListHead))
+ {
+ Current = WThread->WindowListHead.Flink;
+ Wnd = CONTAINING_RECORD(Current, WND, ThreadListEntry);
+
+ TRACE("thread cleanup: while destroy wnds, wnd=%p\n", Wnd);
+
+ /* Window removes itself from the list */
+
+ /*
+ * FIXME: It is critical that the window removes itself! If now, we will loop
+ * here forever...
+ */
+
+ //ASSERT(co_UserDestroyWindow(Wnd));
+
+ UserRefObjectCo(Wnd, &Ref); // FIXME: Temp HACK??
+ if (!co_UserDestroyWindow(Wnd))
+ {
+ ERR("Unable to destroy window %p at thread cleanup... This is _VERY_ bad!\n", Wnd);
+ }
+ UserDerefObjectCo(Wnd); // FIXME: Temp HACK??
+ }
+}
+
+PMENU FASTCALL
+IntGetSystemMenu(PWND Window, BOOL bRevert, BOOL RetMenu)
+{
+ PMENU Menu, NewMenu = NULL, SysMenu = NULL, ret = NULL;
+ PTHREADINFO W32Thread;
+ HMENU hNewMenu, hSysMenu;
++ ROSMENUITEMINFO ItemInfoSet = {0};
+ ROSMENUITEMINFO ItemInfo = {0};
++ UNICODE_STRING MenuName;
+
+ if(bRevert)
+ {
+ W32Thread = PsGetCurrentThreadWin32Thread();
+
+ if(!W32Thread->rpdesk)
+ return NULL;
+
+ if(Window->SystemMenu)
+ {
+ Menu = UserGetMenuObject(Window->SystemMenu);
+ if(Menu)
+ {
+ IntDestroyMenuObject(Menu, TRUE, TRUE);
+ Window->SystemMenu = (HMENU)0;
+ }
+ }
+
+ if(W32Thread->rpdesk->rpwinstaParent->SystemMenuTemplate)
+ {
+ /* Clone system menu */
+ Menu = UserGetMenuObject(W32Thread->rpdesk->rpwinstaParent->SystemMenuTemplate);
+ if(!Menu)
+ return NULL;
+
+ NewMenu = IntCloneMenu(Menu);
+ if(NewMenu)
+ { // Use spmenuSys
+ Window->SystemMenu = NewMenu->head.h;
+ NewMenu->fFlags |= MNF_SYSDESKMN;
+ NewMenu->hWnd = Window->head.h;
+ ret = NewMenu;
+ //IntReleaseMenuObject(NewMenu);
+ }
+ }
+ else
+ {
+ hSysMenu = UserCreateMenu(FALSE);
+ if (NULL == hSysMenu)
+ {
+ return NULL;
+ }
+ SysMenu = IntGetMenuObject(hSysMenu);
+ if (NULL == SysMenu)
+ {
+ UserDestroyMenu(hSysMenu);
+ return NULL;
+ }
+ SysMenu->fFlags |= MNF_SYSDESKMN;
+ SysMenu->hWnd = Window->head.h;
- //hNewMenu = co_IntCallLoadMenu( NULL, L"SYSMENUMDI");
- // else
- //hNewMenu = co_IntCallLoadMenu( NULL, L"SYSMENU");
- // Do the rest in here.
++ //hNewMenu = co_IntLoadSysMenuTemplate();
+ //if ( Window->ExStyle & WS_EX_MDICHILD )
++ //{
++ // RtlInitUnicodeString( &MenuName, L"SYSMENUMDI");
++ // hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
++ //}
++ //else
++ {
++ RtlInitUnicodeString( &MenuName, L"SYSMENU");
++ hNewMenu = co_IntCallLoadMenu( hModClient, &MenuName);
++ //ERR("%wZ\n",&MenuName);
++ }
+ if(!hNewMenu)
+ {
++ ERR("No Menu!!\n");
+ IntReleaseMenuObject(SysMenu);
+ UserDestroyMenu(hSysMenu);
+ return NULL;
+ }
+ Menu = IntGetMenuObject(hNewMenu);
+ if(!Menu)
+ {
+ IntReleaseMenuObject(SysMenu);
+ UserDestroyMenu(hSysMenu);
+ return NULL;
+ }
+
++ // Do the rest in here.
++
++ Menu->fFlags |= MNS_CHECKORBMP | MNF_SYSDESKMN | MNF_POPUP;
++
++ ItemInfoSet.cbSize = sizeof( MENUITEMINFOW);
++ ItemInfoSet.fMask = MIIM_BITMAP;
++ ItemInfoSet.hbmpItem = HBMMENU_POPUP_CLOSE;
++ IntMenuItemInfo(Menu, SC_CLOSE, FALSE, &ItemInfoSet, TRUE, NULL);
++ ItemInfoSet.hbmpItem = HBMMENU_POPUP_RESTORE;
++ IntMenuItemInfo(Menu, SC_RESTORE, FALSE, &ItemInfoSet, TRUE, NULL);
++ ItemInfoSet.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
++ IntMenuItemInfo(Menu, SC_MAXIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
++ ItemInfoSet.hbmpItem = HBMMENU_POPUP_MINIMIZE;
++ IntMenuItemInfo(Menu, SC_MINIMIZE, FALSE, &ItemInfoSet, TRUE, NULL);
++
+ NewMenu = IntCloneMenu(Menu);
+ if(NewMenu)
+ {
+ NewMenu->fFlags |= MNF_SYSDESKMN | MNF_POPUP;
+ // Do not set MNS_CHECKORBMP it breaks menus, also original code destroyed the style anyway.
+ IntReleaseMenuObject(NewMenu);
+ UserSetMenuDefaultItem(NewMenu, SC_CLOSE, FALSE);
+
+ if (Window->pcls->style & CS_NOCLOSE)
+ IntRemoveMenuItem(NewMenu, SC_CLOSE, MF_BYCOMMAND, TRUE);
+
+ ItemInfo.cbSize = sizeof(MENUITEMINFOW);
+ ItemInfo.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_SUBMENU;
+ ItemInfo.fType = 0;
+ ItemInfo.fState = MFS_ENABLED;
+ ItemInfo.dwTypeData = NULL;
+ ItemInfo.cch = 0;
+ ItemInfo.hSubMenu = NewMenu->head.h;
+ IntInsertMenuItem(SysMenu, (UINT) -1, TRUE, &ItemInfo, NULL);
+
+ Window->SystemMenu = SysMenu->head.h;
+
+ ret = SysMenu;
+ }
+ IntDestroyMenuObject(Menu, FALSE, TRUE);
+ }
+ if(RetMenu)
+ return ret;
+ else
+ return NULL;
+ }
+ else
+ {
+ if(Window->SystemMenu)
+ return IntGetMenuObject((HMENU)Window->SystemMenu);
+ else
+ return NULL;
+ }
+}
+
+
+BOOL FASTCALL
+IntIsChildWindow(PWND Parent, PWND BaseWindow)
+{
+ PWND Window;
+
+ Window = BaseWindow;
+ while (Window && ((Window->style & (WS_POPUP|WS_CHILD)) == WS_CHILD))
+ {
+ if (Window == Parent)
+ {
+ return(TRUE);
+ }
+
+ Window = Window->spwndParent;
+ }
+
+ return(FALSE);
+}
+
+/*
+ Link the window into siblings list
+ children and parent are kept in place.
+*/
+VOID FASTCALL
+IntLinkWindow(
+ PWND Wnd,
+ PWND WndInsertAfter /* set to NULL if top sibling */
+)
+{
+ if ((Wnd->spwndPrev = WndInsertAfter))
+ {
+ /* link after WndInsertAfter */
+ if ((Wnd->spwndNext = WndInsertAfter->spwndNext))
+ Wnd->spwndNext->spwndPrev = Wnd;
+
+ Wnd->spwndPrev->spwndNext = Wnd;
+ }
+ else
+ {
+ /* link at top */
+ if ((Wnd->spwndNext = Wnd->spwndParent->spwndChild))
+ Wnd->spwndNext->spwndPrev = Wnd;
+
+ Wnd->spwndParent->spwndChild = Wnd;
+ }
+}
+
+/*
+ Note: Wnd->spwndParent can be null if it is the desktop.
+*/
+VOID FASTCALL IntLinkHwnd(PWND Wnd, HWND hWndPrev)
+{
+ if (hWndPrev == HWND_NOTOPMOST)
+ {
+ if (!(Wnd->ExStyle & WS_EX_TOPMOST) &&
+ (Wnd->ExStyle2 & WS_EX2_LINKED)) return; /* nothing to do */
+ Wnd->ExStyle &= ~WS_EX_TOPMOST;
+ hWndPrev = HWND_TOP; /* fallback to the HWND_TOP case */
+ }
+
+ IntUnlinkWindow(Wnd); /* unlink it from the previous location */
+
+ if (hWndPrev == HWND_BOTTOM)
+ {
+ /* Link in the bottom of the list */
+ PWND WndInsertAfter;
+
+ WndInsertAfter = Wnd->spwndParent->spwndChild;
+ while( WndInsertAfter && WndInsertAfter->spwndNext)
+ WndInsertAfter = WndInsertAfter->spwndNext;
+
+ IntLinkWindow(Wnd, WndInsertAfter);
+ Wnd->ExStyle &= ~WS_EX_TOPMOST;
+ }
+ else if (hWndPrev == HWND_TOPMOST)
+ {
+ /* Link in the top of the list */
+ IntLinkWindow(Wnd, NULL);
+
+ Wnd->ExStyle |= WS_EX_TOPMOST;
+ }
+ else if (hWndPrev == HWND_TOP)
+ {
+ /* Link it after the last topmost window */
+ PWND WndInsertBefore;
+
+ WndInsertBefore = Wnd->spwndParent->spwndChild;
+
+ if (!(Wnd->ExStyle & WS_EX_TOPMOST)) /* put it above the first non-topmost window */
+ {
+ while (WndInsertBefore != NULL && WndInsertBefore->spwndNext != NULL)
+ {
+ if (!(WndInsertBefore->ExStyle & WS_EX_TOPMOST)) break;
+ if (WndInsertBefore == Wnd->spwndOwner) /* keep it above owner */
+ {
+ Wnd->ExStyle |= WS_EX_TOPMOST;
+ break;
+ }
+ WndInsertBefore = WndInsertBefore->spwndNext;
+ }
+ }
+
+ IntLinkWindow(Wnd, WndInsertBefore ? WndInsertBefore->spwndPrev : NULL);
+ }
+ else
+ {
+ /* Link it after hWndPrev */
+ PWND WndInsertAfter;
+
+ WndInsertAfter = UserGetWindowObject(hWndPrev);
+ /* Are we called with an erroneous handle */
+ if(WndInsertAfter == NULL)
+ {
+ /* Link in a default position */
+ IntLinkHwnd(Wnd, HWND_TOP);
+ return;
+ }
+
+ IntLinkWindow(Wnd, WndInsertAfter);
+
+ /* Fix the WS_EX_TOPMOST flag */
+ if (!(WndInsertAfter->ExStyle & WS_EX_TOPMOST))
+ {
+ Wnd->ExStyle &= ~WS_EX_TOPMOST;
+ }
+ else
+ {
+ if(WndInsertAfter->spwndNext &&
+ WndInsertAfter->spwndNext->ExStyle & WS_EX_TOPMOST)
+ {
+ Wnd->ExStyle |= WS_EX_TOPMOST;
+ }
+ }
+ }
+ Wnd->ExStyle2 |= WS_EX2_LINKED;
+}
+
+VOID FASTCALL
+IntProcessOwnerSwap(PWND Wnd, PWND WndNewOwner, PWND WndOldOwner)
+{
+ if (WndOldOwner)
+ {
+ if (Wnd->head.pti != WndOldOwner->head.pti)
+ {
+ if (!WndNewOwner ||
+ Wnd->head.pti == WndNewOwner->head.pti ||
+ WndOldOwner->head.pti != WndNewOwner->head.pti )
+ {
+ //ERR("ProcessOwnerSwap Old out.\n");
+ UserAttachThreadInput(Wnd->head.pti, WndOldOwner->head.pti, FALSE);
+ }
+ }
+ }
+ if (WndNewOwner)
+ {
+ if (Wnd->head.pti != WndNewOwner->head.pti)
+ {
+ if (!WndOldOwner ||
+ WndOldOwner->head.pti != WndNewOwner->head.pti )
+ {
+ //ERR("ProcessOwnerSwap New in.\n");
+ UserAttachThreadInput(Wnd->head.pti, WndNewOwner->head.pti, TRUE);
+ }
+ }
+ }
+ // FIXME: System Tray checks.
+}
+
+HWND FASTCALL
+IntSetOwner(HWND hWnd, HWND hWndNewOwner)
+{
+ PWND Wnd, WndOldOwner, WndNewOwner;
+ HWND ret;
+
+ Wnd = IntGetWindowObject(hWnd);
+ if(!Wnd)
+ return NULL;
+
+ WndOldOwner = Wnd->spwndOwner;
+
+ ret = WndOldOwner ? UserHMGetHandle(WndOldOwner) : 0;
+ WndNewOwner = UserGetWindowObject(hWndNewOwner);
+
+ if (!WndNewOwner && hWndNewOwner)
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ ret = NULL;
+ goto Error;
+ }
+
+ /* if parent belongs to a different thread and the window isn't */
+ /* top-level, attach the two threads */
+ IntProcessOwnerSwap(Wnd, WndNewOwner, WndOldOwner);
+
+ if (IntValidateOwnerDepth(Wnd, WndNewOwner))
+ {
+ if (WndNewOwner)
+ {
+ Wnd->spwndOwner= WndNewOwner;
+ }
+ else
+ {
+ Wnd->spwndOwner = NULL;
+ }
+ }
+ else
+ {
+ IntProcessOwnerSwap(Wnd, WndOldOwner, WndNewOwner);
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ ret = NULL;
+ }
+Error:
+ UserDereferenceObject(Wnd);
+ return ret;
+}
+
+PWND FASTCALL
+co_IntSetParent(PWND Wnd, PWND WndNewParent)
+{
+ PWND WndOldParent, pWndExam;
+ BOOL WasVisible;
+ POINT pt;
+ int swFlags = SWP_NOSIZE|SWP_NOZORDER;
+
+ ASSERT(Wnd);
+ ASSERT(WndNewParent);
+ ASSERT_REFS_CO(Wnd);
+ ASSERT_REFS_CO(WndNewParent);
+
+ if (Wnd == Wnd->head.rpdesk->spwndMessage)
+ {
+ EngSetLastError(ERROR_ACCESS_DENIED);
+ return( NULL);
+ }
+
+ /* Some applications try to set a child as a parent */
+ if (IntIsChildWindow(Wnd, WndNewParent))
+ {
+ TRACE("IntSetParent try to set a child as a parent.\n");
+ EngSetLastError( ERROR_INVALID_PARAMETER );
+ return NULL;
+ }
+
+ pWndExam = WndNewParent; // Load parent Window to examine.
+ // Now test for set parent to parent hit.
+ while (pWndExam)
+ {
+ if (Wnd == pWndExam)
+ {
+ TRACE("IntSetParent Failed Test for set parent to parent!\n");
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+ pWndExam = pWndExam->spwndParent;
+ }
+
+ /*
+ * Windows hides the window first, then shows it again
+ * including the WM_SHOWWINDOW messages and all
+ */
+ WasVisible = co_WinPosShowWindow(Wnd, SW_HIDE);
+
+ /* Window must belong to current process */
+ if (Wnd->head.pti->ppi != PsGetCurrentProcessWin32Process())
+ {
+ ERR("IntSetParent Window must belong to current process!\n");
+ return NULL;
+ }
+
+ WndOldParent = Wnd->spwndParent;
+
+ if ( WndOldParent &&
+ WndOldParent->ExStyle & WS_EX_LAYOUTRTL)
+ pt.x = Wnd->rcWindow.right;
+ else
+ pt.x = Wnd->rcWindow.left;
+ pt.y = Wnd->rcWindow.top;
+
+ IntScreenToClient(WndOldParent, &pt);
+
+ if (WndOldParent) UserReferenceObject(WndOldParent); /* Caller must deref */
+
+ if (WndNewParent != WndOldParent)
+ {
+ /* Unlink the window from the siblings list */
+ IntUnlinkWindow(Wnd);
+ Wnd->ExStyle2 &= ~WS_EX2_LINKED;
+
+ /* Set the new parent */
+ Wnd->spwndParent = WndNewParent;
+
+ if ( Wnd->style & WS_CHILD &&
+ Wnd->spwndOwner &&
+ Wnd->spwndOwner->ExStyle & WS_EX_TOPMOST )
+ {
+ ERR("SetParent Top Most from Pop up!\n");
+ Wnd->ExStyle |= WS_EX_TOPMOST;
+ }
+
+ /* Link the window with its new siblings */
+ IntLinkHwnd( Wnd,
+ ((0 == (Wnd->ExStyle & WS_EX_TOPMOST) &&
+ WndNewParent == UserGetDesktopWindow() ) ? HWND_TOP : HWND_TOPMOST ) );
+
+ }
+
+ if ( WndNewParent == co_GetDesktopWindow(Wnd) &&
+ !(Wnd->style & WS_CLIPSIBLINGS) )
+ {
+ Wnd->style |= WS_CLIPSIBLINGS;
+ DceResetActiveDCEs(Wnd);
+ }
+
+ /* if parent belongs to a different thread and the window isn't */
+ /* top-level, attach the two threads */
+ if ((Wnd->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
+ {
+ if ( Wnd->spwndParent != co_GetDesktopWindow(Wnd))
+ {
+ if (Wnd->head.pti != WndOldParent->head.pti)
+ {
+ //ERR("SetParent Old out.\n");
+ UserAttachThreadInput(Wnd->head.pti, WndOldParent->head.pti, FALSE);
+ }
+ }
+ if ( WndNewParent != co_GetDesktopWindow(Wnd))
+ {
+ if (Wnd->head.pti != WndNewParent->head.pti)
+ {
+ //ERR("SetParent New in.\n");
+ UserAttachThreadInput(Wnd->head.pti, WndNewParent->head.pti, TRUE);
+ }
+ }
+ }
+
+ if (WndOldParent == UserGetMessageWindow() || WndNewParent == UserGetMessageWindow())
+ swFlags |= SWP_NOACTIVATE;
+
+ IntNotifyWinEvent(EVENT_OBJECT_PARENTCHANGE, Wnd ,OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI);
+ /*
+ * SetParent additionally needs to make hwnd the top window
+ * in the z-order and send the expected WM_WINDOWPOSCHANGING and
+ * WM_WINDOWPOSCHANGED notification messages.
+ */
+ //ERR("IntSetParent SetWindowPos 1\n");
+ co_WinPosSetWindowPos( Wnd,
+ (0 == (Wnd->ExStyle & WS_EX_TOPMOST) ? HWND_TOP : HWND_TOPMOST),
+ pt.x, pt.y, 0, 0, swFlags);
+ //ERR("IntSetParent SetWindowPos 2 X %d Y %d\n",pt.x, pt.y);
+ if (WasVisible) co_WinPosShowWindow(Wnd, SW_SHOWNORMAL);
+
+ return WndOldParent;
+}
+
+HWND FASTCALL
+co_UserSetParent(HWND hWndChild, HWND hWndNewParent)
+{
+ PWND Wnd = NULL, WndParent = NULL, WndOldParent;
+ HWND hWndOldParent = NULL;
+ USER_REFERENCE_ENTRY Ref, ParentRef;
+
+ if (IntIsBroadcastHwnd(hWndChild) || IntIsBroadcastHwnd(hWndNewParent))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ return( NULL);
+ }
+
+ if (hWndChild == IntGetDesktopWindow())
+ {
+ ERR("UserSetParent Access Denied!\n");
+ EngSetLastError(ERROR_ACCESS_DENIED);
+ return( NULL);
+ }
+
+ if (hWndNewParent)
+ {
+ if (!(WndParent = UserGetWindowObject(hWndNewParent)))
+ {
+ ERR("UserSetParent Bad New Parent!\n");
+ return( NULL);
+ }
+ }
+ else
+ {
+ if (!(WndParent = UserGetWindowObject(IntGetDesktopWindow())))
+ {
+ return( NULL);
+ }
+ }
+
+ if (!(Wnd = UserGetWindowObject(hWndChild)))
+ {
+ ERR("UserSetParent Bad Child!\n");
+ return( NULL);
+ }
+
+ UserRefObjectCo(Wnd, &Ref);
+ UserRefObjectCo(WndParent, &ParentRef);
+ //ERR("Enter co_IntSetParent\n");
+ WndOldParent = co_IntSetParent(Wnd, WndParent);
+ //ERR("Leave co_IntSetParent\n");
+ UserDerefObjectCo(WndParent);
+ UserDerefObjectCo(Wnd);
+
+ if (WndOldParent)
+ {
+ hWndOldParent = WndOldParent->head.h;
+ UserDereferenceObject(WndOldParent);
+ }
+
+ return( hWndOldParent);
+}
+
+BOOL FASTCALL
+IntSetSystemMenu(PWND Window, PMENU Menu)
+{
+ PMENU OldMenu;
+ if(Window->SystemMenu)
+ {
+ OldMenu = IntGetMenuObject(Window->SystemMenu);
+ if(OldMenu)
+ {
+ OldMenu->fFlags &= ~ MNF_SYSDESKMN;
+ IntReleaseMenuObject(OldMenu);
+ }
+ }
+
+ if(Menu)
+ {
+ /* FIXME: Check window style, propably return FALSE? */
+ Window->SystemMenu = Menu->head.h;
+ Menu->fFlags |= MNF_SYSDESKMN;
+ }
+ else // Use spmenuSys too!
+ Window->SystemMenu = (HMENU)0;
+
+ return TRUE;
+}
+
+/* Unlink the window from siblings. children and parent are kept in place. */
+VOID FASTCALL
+IntUnlinkWindow(PWND Wnd)
+{
+ if (Wnd->spwndNext)
+ Wnd->spwndNext->spwndPrev = Wnd->spwndPrev;
+
+ if (Wnd->spwndPrev)
+ Wnd->spwndPrev->spwndNext = Wnd->spwndNext;
+
+ if (Wnd->spwndParent && Wnd->spwndParent->spwndChild == Wnd)
+ Wnd->spwndParent->spwndChild = Wnd->spwndNext;
+
+ Wnd->spwndPrev = Wnd->spwndNext = NULL;
+}
+
+/* FUNCTIONS *****************************************************************/
+
+/*
+ * As best as I can figure, this function is used by EnumWindows,
+ * EnumChildWindows, EnumDesktopWindows, & EnumThreadWindows.
+ *
+ * It's supposed to build a list of HWNDs to return to the caller.
+ * We can figure out what kind of list by what parameters are
+ * passed to us.
+ */
+/*
+ * @implemented
+ */
+NTSTATUS
+APIENTRY
+NtUserBuildHwndList(
+ HDESK hDesktop,
+ HWND hwndParent,
+ BOOLEAN bChildren,
+ ULONG dwThreadId,
+ ULONG lParam,
+ HWND* pWnd,
+ ULONG* pBufSize)
+{
+ NTSTATUS Status;
+ ULONG dwCount = 0;
+
+ if (pBufSize == 0)
+ return ERROR_INVALID_PARAMETER;
+
+ if (hwndParent || !dwThreadId)
+ {
+ PDESKTOP Desktop;
+ PWND Parent, Window;
+
+ if(!hwndParent)
+ {
+ if(hDesktop == NULL && !(Desktop = IntGetActiveDesktop()))
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if(hDesktop)
+ {
+ Status = IntValidateDesktopHandle(hDesktop,
+ UserMode,
+ 0,
+ &Desktop);
+ if(!NT_SUCCESS(Status))
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+ }
+ hwndParent = Desktop->DesktopWindow;
+ }
+ else
+ {
+ hDesktop = 0;
+ }
+
+ if((Parent = UserGetWindowObject(hwndParent)) &&
+ (Window = Parent->spwndChild))
+ {
+ BOOL bGoDown = TRUE;
+
+ Status = STATUS_SUCCESS;
+ while(TRUE)
+ {
+ if (bGoDown)
+ {
+ if(dwCount++ < *pBufSize && pWnd)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pWnd, sizeof(HWND), 1);
+ *pWnd = Window->head.h;
+ pWnd++;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ break;
+ }
+ }
+ if (Window->spwndChild && bChildren)
+ {
+ Window = Window->spwndChild;
+ continue;
+ }
+ bGoDown = FALSE;
+ }
+ if (Window->spwndNext)
+ {
+ Window = Window->spwndNext;
+ bGoDown = TRUE;
+ continue;
+ }
+ Window = Window->spwndParent;
+ if (Window == Parent)
+ {
+ break;
+ }
+ }
+ }
+
+ if(hDesktop)
+ {
+ ObDereferenceObject(Desktop);
+ }
+ }
+ else // Build EnumThreadWindows list!
+ {
+ PETHREAD Thread;
+ PTHREADINFO W32Thread;
+ PLIST_ENTRY Current;
+ PWND Window;
+
+ Status = PsLookupThreadByThreadId((HANDLE)dwThreadId, &Thread);
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("Thread Id is not valid!\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+ if (!(W32Thread = (PTHREADINFO)Thread->Tcb.Win32Thread))
+ {
+ ObDereferenceObject(Thread);
+ ERR("Thread is not initialized!\n");
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ Current = W32Thread->WindowListHead.Flink;
+ while (Current != &(W32Thread->WindowListHead))
+ {
+ Window = CONTAINING_RECORD(Current, WND, ThreadListEntry);
+ ASSERT(Window);
+
+ if (dwCount < *pBufSize && pWnd)
+ {
+ _SEH2_TRY
+ {
+ ProbeForWrite(pWnd, sizeof(HWND), 1);
+ *pWnd = Window->head.h;
+ pWnd++;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("Failure to build window list!\n");
+ SetLastNtError(Status);
+ break;
+ }
+ }
+ dwCount++;
+ Current = Window->ThreadListEntry.Flink;
+ }
+
+ ObDereferenceObject(Thread);
+ }
+
+ *pBufSize = dwCount;
+ return STATUS_SUCCESS;
+}
+
+static void IntSendParentNotify( PWND pWindow, UINT msg )
+{
+ if ( (pWindow->style & (WS_CHILD | WS_POPUP)) == WS_CHILD &&
+ !(pWindow->style & WS_EX_NOPARENTNOTIFY))
+ {
+ if (pWindow->spwndParent && pWindow->spwndParent != UserGetDesktopWindow())
+ {
+ USER_REFERENCE_ENTRY Ref;
+ UserRefObjectCo(pWindow->spwndParent, &Ref);
+ co_IntSendMessage( pWindow->spwndParent->head.h,
+ WM_PARENTNOTIFY,
+ MAKEWPARAM( msg, pWindow->IDMenu),
+ (LPARAM)pWindow->head.h );
+ UserDerefObjectCo(pWindow->spwndParent);
+ }
+ }
+}
+
+void FASTCALL
+IntFixWindowCoordinates(CREATESTRUCTW* Cs, PWND ParentWindow, DWORD* dwShowMode)
+{
+#define IS_DEFAULT(x) ((x) == CW_USEDEFAULT || (x) == (SHORT)0x8000)
+
+ /* default positioning for overlapped windows */
+ if(!(Cs->style & (WS_POPUP | WS_CHILD)))
+ {
+ PMONITOR pMonitor;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParams;
+
+ pMonitor = UserGetPrimaryMonitor();
+
+ /* Check if we don't have a monitor attached yet */
+ if(pMonitor == NULL)
+ {
+ Cs->x = Cs->y = 0;
+ Cs->cx = 800;
+ Cs->cy = 600;
+ return;
+ }
+
+ ProcessParams = PsGetCurrentProcess()->Peb->ProcessParameters;
+
+ if (IS_DEFAULT(Cs->x))
+ {
+ if (!IS_DEFAULT(Cs->y)) *dwShowMode = Cs->y;
+
+ if(ProcessParams->WindowFlags & STARTF_USEPOSITION)
+ {
+ Cs->x = ProcessParams->StartingX;
+ Cs->y = ProcessParams->StartingY;
+ }
+ else
+ {
+ Cs->x = pMonitor->cWndStack * (UserGetSystemMetrics(SM_CXSIZE) + UserGetSystemMetrics(SM_CXFRAME));
+ Cs->y = pMonitor->cWndStack * (UserGetSystemMetrics(SM_CYSIZE) + UserGetSystemMetrics(SM_CYFRAME));
+ if (Cs->x > ((pMonitor->rcWork.right - pMonitor->rcWork.left) / 4) ||
+ Cs->y > ((pMonitor->rcWork.bottom - pMonitor->rcWork.top) / 4))
+ {
+ /* reset counter and position */
+ Cs->x = 0;
+ Cs->y = 0;
+ pMonitor->cWndStack = 0;
+ }
+ pMonitor->cWndStack++;
+ }
+ }
+
+ if (IS_DEFAULT(Cs->cx))
+ {
+ if (ProcessParams->WindowFlags & STARTF_USEPOSITION)
+ {
+ Cs->cx = ProcessParams->CountX;
+ Cs->cy = ProcessParams->CountY;
+ }
+ else
+ {
+ Cs->cx = (pMonitor->rcWork.right - pMonitor->rcWork.left) * 3 / 4;
+ Cs->cy = (pMonitor->rcWork.bottom - pMonitor->rcWork.top) * 3 / 4;
+ }
+ }
+ /* neither x nor cx are default. Check the y values .
+ * In the trace we see Outlook and Outlook Express using
+ * cy set to CW_USEDEFAULT when opening the address book.
+ */
+ else if (IS_DEFAULT(Cs->cy))
+ {
+ TRACE("Strange use of CW_USEDEFAULT in nHeight\n");
+ Cs->cy = (pMonitor->rcWork.bottom - pMonitor->rcWork.top) * 3 / 4;
+ }
+ }
+ else
+ {
+ /* if CW_USEDEFAULT is set for non-overlapped windows, both values are set to zero */
+ if(IS_DEFAULT(Cs->x))
+ {
+ Cs->x = 0;
+ Cs->y = 0;
+ }
+ if(IS_DEFAULT(Cs->cx))
+ {
+ Cs->cx = 0;
+ Cs->cy = 0;
+ }
+ }
+
+#undef IS_DEFAULT
+}
+
+/* Allocates and initializes a window */
+PWND FASTCALL IntCreateWindow(CREATESTRUCTW* Cs,
+ PLARGE_STRING WindowName,
+ PCLS Class,
+ PWND ParentWindow,
+ PWND OwnerWindow,
+ PVOID acbiBuffer,
+ PDESKTOP pdeskCreated)
+{
+ PWND pWnd = NULL;
+ HWND hWnd;
+ PTHREADINFO pti = NULL;
+ PMENU SystemMenu;
+ BOOL MenuChanged;
+ BOOL bUnicodeWindow;
+
+ pti = pdeskCreated ? gptiDesktopThread : GetW32ThreadInfo();
+
+ if (!(Cs->dwExStyle & WS_EX_LAYOUTRTL))
+ { // Need both here for wine win.c test_CreateWindow.
+ //if (Cs->hwndParent && ParentWindow)
+ if (ParentWindow) // It breaks more tests..... WIP.
+ {
+ if ( (Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD &&
+ ParentWindow->ExStyle & WS_EX_LAYOUTRTL &&
+ !(ParentWindow->ExStyle & WS_EX_NOINHERITLAYOUT) )
+ Cs->dwExStyle |= WS_EX_LAYOUTRTL;
+ }
+ else
+ { /*
+ * Note from MSDN <http://msdn.microsoft.com/en-us/library/aa913269.aspx>:
+ *
+ * Dialog boxes and message boxes do not inherit layout, so you must
+ * set the layout explicitly.
+ */
+ if ( Class->fnid != FNID_DIALOG )
+ {
+ if (pti->ppi->dwLayout & LAYOUT_RTL)
+ {
+ Cs->dwExStyle |= WS_EX_LAYOUTRTL;
+ }
+ }
+ }
+ }
+
+ /* Automatically add WS_EX_WINDOWEDGE */
+ if ((Cs->dwExStyle & WS_EX_DLGMODALFRAME) ||
+ ((!(Cs->dwExStyle & WS_EX_STATICEDGE)) &&
+ (Cs->style & (WS_DLGFRAME | WS_THICKFRAME))))
+ Cs->dwExStyle |= WS_EX_WINDOWEDGE;
+ else
+ Cs->dwExStyle &= ~WS_EX_WINDOWEDGE;
+
+ /* Is it a unicode window? */
+ bUnicodeWindow =!(Cs->dwExStyle & WS_EX_SETANSICREATOR);
+ Cs->dwExStyle &= ~WS_EX_SETANSICREATOR;
+
+ /* Allocate the new window */
+ pWnd = (PWND) UserCreateObject( gHandleTable,
+ pdeskCreated ? pdeskCreated : pti->rpdesk,
+ pti,
+ (PHANDLE)&hWnd,
+ TYPE_WINDOW,
+ sizeof(WND) + Class->cbwndExtra);
+
+ if (!pWnd)
+ {
+ goto AllocError;
+ }
+
+ TRACE("Created window object with handle %p\n", hWnd);
+
+ if (pdeskCreated && pdeskCreated->DesktopWindow == NULL )
+ { /* HACK: Helper for win32csr/desktopbg.c */
+ /* If there is no desktop window yet, we must be creating it */
+ TRACE("CreateWindow setting desktop.\n");
+ pdeskCreated->DesktopWindow = hWnd;
+ pdeskCreated->pDeskInfo->spwnd = pWnd;
+ }
+
+ /*
+ * Fill out the structure describing it.
+ */
+ /* Remember, pWnd->head is setup in object.c ... */
+ pWnd->spwndParent = ParentWindow;
+ pWnd->spwndOwner = OwnerWindow;
+ pWnd->fnid = 0;
+ pWnd->spwndLastActive = pWnd;
+ pWnd->state2 |= WNDS2_WIN40COMPAT; // FIXME!!!
+ pWnd->pcls = Class;
+ pWnd->hModule = Cs->hInstance;
+ pWnd->style = Cs->style & ~WS_VISIBLE;
+ pWnd->ExStyle = Cs->dwExStyle;
+ pWnd->cbwndExtra = pWnd->pcls->cbwndExtra;
+ pWnd->pActCtx = acbiBuffer;
+ pWnd->InternalPos.MaxPos.x = pWnd->InternalPos.MaxPos.y = -1;
+ pWnd->InternalPos.IconPos.x = pWnd->InternalPos.IconPos.y = -1;
+
+ if (pWnd->spwndParent != NULL && Cs->hwndParent != 0)
+ {
+ pWnd->HideFocus = pWnd->spwndParent->HideFocus;
+ pWnd->HideAccel = pWnd->spwndParent->HideAccel;
+ }
+
+ pWnd->head.pti->cWindows++;
+
+ if (Class->hIcon && !Class->hIconSm)
+ {
+ Class->hIconSmIntern = co_IntCopyImage( Class->hIcon, IMAGE_ICON,
+ UserGetSystemMetrics( SM_CXSMICON ),
+ UserGetSystemMetrics( SM_CYSMICON ), 0 );
+ TRACE("IntCreateWindow hIconSmIntern %p\n",Class->hIconSmIntern);
+ Class->CSF_flags |= CSF_CACHEDSMICON;
+ }
+
+ if (pWnd->pcls->CSF_flags & CSF_SERVERSIDEPROC)
+ pWnd->state |= WNDS_SERVERSIDEWINDOWPROC;
+
+ /* BugBoy Comments: Comment below say that System classes are always created
+ as UNICODE. In windows, creating a window with the ANSI version of CreateWindow
+ sets the window to ansi as verified by testing with IsUnicodeWindow API.
+
+ No where can I see in code or through testing does the window change back
+ to ANSI after being created as UNICODE in ROS. I didnt do more testing to
+ see what problems this would cause. */
+
+ // Set WndProc from Class.
+ pWnd->lpfnWndProc = pWnd->pcls->lpfnWndProc;
+
+ // GetWindowProc, test for non server side default classes and set WndProc.
+ if ( pWnd->pcls->fnid <= FNID_GHOST && pWnd->pcls->fnid >= FNID_BUTTON )
+ {
+ if (bUnicodeWindow)
+ {
+ if (GETPFNCLIENTA(pWnd->pcls->fnid) == pWnd->lpfnWndProc)
+ pWnd->lpfnWndProc = GETPFNCLIENTW(pWnd->pcls->fnid);
+ }
+ else
+ {
+ if (GETPFNCLIENTW(pWnd->pcls->fnid) == pWnd->lpfnWndProc)
+ pWnd->lpfnWndProc = GETPFNCLIENTA(pWnd->pcls->fnid);
+ }
+ }
+
+ // If not an Unicode caller, set Ansi creator bit.
+ if (!bUnicodeWindow) pWnd->state |= WNDS_ANSICREATOR;
+
+ // Clone Class Ansi/Unicode proc type.
+ if (pWnd->pcls->CSF_flags & CSF_ANSIPROC)
+ {
+ pWnd->state |= WNDS_ANSIWINDOWPROC;
+ pWnd->Unicode = FALSE;
+ }
+ else
+ { /*
+ * It seems there can be both an Ansi creator and Unicode Class Window
+ * WndProc, unless the following overriding conditions occur:
+ */
+ if ( !bUnicodeWindow &&
+ ( Class->atomClassName == gpsi->atomSysClass[ICLS_BUTTON] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_COMBOBOX] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_COMBOLBOX] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_DIALOG] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_EDIT] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_IME] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_LISTBOX] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_MDICLIENT] ||
+ Class->atomClassName == gpsi->atomSysClass[ICLS_STATIC] ) )
+ { // Override Class and set the window Ansi WndProc.
+ pWnd->state |= WNDS_ANSIWINDOWPROC;
+ pWnd->Unicode = FALSE;
+ }
+ else
+ { // Set the window Unicode WndProc.
+ pWnd->state &= ~WNDS_ANSIWINDOWPROC;
+ pWnd->Unicode = TRUE;
+ }
+ }
+
+ /* BugBoy Comments: if the window being created is a edit control, ATOM 0xCxxx,
+ then my testing shows that windows (2k and XP) creates a CallProc for it immediately
+ Dont understand why it does this. */
+ if (Class->atomClassName == gpsi->atomSysClass[ICLS_EDIT])
+ {
+ PCALLPROCDATA CallProc;
+ CallProc = CreateCallProc(NULL, pWnd->lpfnWndProc, pWnd->Unicode , pWnd->head.pti->ppi);
+
+ if (!CallProc)
+ {
+ EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ ERR("Warning: Unable to create CallProc for edit control. Control may not operate correctly! hwnd %p\n", hWnd);
+ }
+ else
+ {
+ UserAddCallProcToClass(pWnd->pcls, CallProc);
+ }
+ }
+
+ InitializeListHead(&pWnd->PropListHead);
+
+ if ( WindowName->Buffer != NULL && WindowName->Length > 0 )
+ {
+ pWnd->strName.Buffer = DesktopHeapAlloc(pWnd->head.rpdesk,
+ WindowName->Length + sizeof(UNICODE_NULL));
+ if (pWnd->strName.Buffer == NULL)
+ {
+ goto AllocError;
+ }
+
+ RtlCopyMemory(pWnd->strName.Buffer, WindowName->Buffer, WindowName->Length);
+ pWnd->strName.Buffer[WindowName->Length / sizeof(WCHAR)] = L'\0';
+ pWnd->strName.Length = WindowName->Length;
+ pWnd->strName.MaximumLength = WindowName->Length + sizeof(UNICODE_NULL);
+ }
+
+ /* Correct the window style. */
+ if ((pWnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
+ {
+ pWnd->style |= WS_CLIPSIBLINGS;
+ if (!(pWnd->style & WS_POPUP))
+ {
+ pWnd->style |= WS_CAPTION;
+ }
+ }
+
+ /* WS_EX_WINDOWEDGE depends on some other styles */
+ if (pWnd->ExStyle & WS_EX_DLGMODALFRAME)
+ pWnd->ExStyle |= WS_EX_WINDOWEDGE;
+ else if (pWnd->style & (WS_DLGFRAME | WS_THICKFRAME))
+ {
+ if (!((pWnd->ExStyle & WS_EX_STATICEDGE) &&
+ (pWnd->style & (WS_CHILD | WS_POPUP))))
+ pWnd->ExStyle |= WS_EX_WINDOWEDGE;
+ }
+ else
+ pWnd->ExStyle &= ~WS_EX_WINDOWEDGE;
+
+ if (!(pWnd->style & (WS_CHILD | WS_POPUP)))
+ pWnd->state |= WNDS_SENDSIZEMOVEMSGS;
+
+ /* Create system menu */
+ if ((Cs->style & WS_SYSMENU)) // && (dwStyle & WS_CAPTION) == WS_CAPTION)
+ {
+ SystemMenu = IntGetSystemMenu(pWnd, TRUE, TRUE);
+ if(SystemMenu)
+ { // spmenuSys
+ pWnd->SystemMenu = SystemMenu->head.h;
+ IntReleaseMenuObject(SystemMenu);
+ }
+ }
+
+ /* Set the window menu */
+ if ((Cs->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)
+ {
+ if (Cs->hMenu)
+ IntSetMenu(pWnd, Cs->hMenu, &MenuChanged);
+ else if (pWnd->pcls->lpszMenuName) // Take it from the parent.
+ {
+ UNICODE_STRING MenuName;
+ HMENU hMenu;
+
+ if (IS_INTRESOURCE(pWnd->pcls->lpszMenuName))
+ {
+ MenuName.Length = 0;
+ MenuName.MaximumLength = 0;
+ MenuName.Buffer = pWnd->pcls->lpszMenuName;
+ }
+ else
+ {
+ RtlInitUnicodeString( &MenuName, pWnd->pcls->lpszMenuName);
+ }
+ hMenu = co_IntCallLoadMenu( pWnd->pcls->hModule, &MenuName);
+ if (hMenu) IntSetMenu(pWnd, hMenu, &MenuChanged);
+ }
+ }
+ else // Not a child
+ pWnd->IDMenu = (UINT) Cs->hMenu;
+
+
+ if ( ParentWindow &&
+ ParentWindow != ParentWindow->head.rpdesk->spwndMessage &&
+ ParentWindow != ParentWindow->head.rpdesk->pDeskInfo->spwnd )
+ {
+ PWND Owner = IntGetNonChildAncestor(ParentWindow);
+
+ if (!IntValidateOwnerDepth(pWnd, Owner))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ goto Error;
+ }
+ if ( pWnd->spwndOwner &&
+ pWnd->spwndOwner->ExStyle & WS_EX_TOPMOST )
+ {
+ pWnd->ExStyle |= WS_EX_TOPMOST;
+ }
+ if ( pWnd->spwndOwner &&
+ Class->atomClassName != gpsi->atomSysClass[ICLS_IME] &&
+ pti != pWnd->spwndOwner->head.pti)
+ {
+ //ERR("CreateWindow Owner in.\n");
+ UserAttachThreadInput(pti, pWnd->spwndOwner->head.pti, TRUE);
+ }
+ }
+
+ /* Insert the window into the thread's window list. */
+ InsertTailList (&pti->WindowListHead, &pWnd->ThreadListEntry);
+
+ /* Handle "CS_CLASSDC", it is tested first. */
+ if ( (pWnd->pcls->style & CS_CLASSDC) && !(pWnd->pcls->pdce) )
+ { /* One DCE per class to have CLASS. */
+ pWnd->pcls->pdce = DceAllocDCE( pWnd, DCE_CLASS_DC );
+ }
+ else if ( pWnd->pcls->style & CS_OWNDC)
+ { /* Allocate a DCE for this window. */
+ DceAllocDCE(pWnd, DCE_WINDOW_DC);
+ }
+
+ return pWnd;
+
+AllocError:
+ ERR("IntCreateWindow Allocation Error.\n");
+ SetLastNtError(STATUS_INSUFFICIENT_RESOURCES);
+Error:
+ if(pWnd)
+ UserDereferenceObject(pWnd);
+ return NULL;
+}
+
+/*
+ * @implemented
+ */
+PWND FASTCALL
+co_UserCreateWindowEx(CREATESTRUCTW* Cs,
+ PUNICODE_STRING ClassName,
+ PLARGE_STRING WindowName,
+ PVOID acbiBuffer)
+{
+ ULONG style;
+ PWND Window = NULL, ParentWindow = NULL, OwnerWindow;
+ HWND hWnd, hWndParent, hWndOwner, hwndInsertAfter;
+ PWINSTATION_OBJECT WinSta;
+ PCLS Class = NULL;
+ SIZE Size;
+ POINT MaxSize, MaxPos, MinTrack, MaxTrack;
+ CBT_CREATEWNDW * pCbtCreate;
+ LRESULT Result;
+ USER_REFERENCE_ENTRY ParentRef, Ref;
+ PTHREADINFO pti;
+ DWORD dwShowMode = SW_SHOW;
+ CREATESTRUCTW *pCsw = NULL;
+ PVOID pszClass = NULL, pszName = NULL;
+ PWND ret = NULL;
+
+ /* Get the current window station and reference it */
+ pti = GetW32ThreadInfo();
+ if (pti == NULL || pti->rpdesk == NULL)
+ {
+ ERR("Thread is not attached to a desktop! Cannot create window!\n");
+ return NULL; // There is nothing to cleanup.
+ }
+ WinSta = pti->rpdesk->rpwinstaParent;
+ ObReferenceObjectByPointer(WinSta, KernelMode, ExWindowStationObjectType, 0);
+
+ pCsw = NULL;
+ pCbtCreate = NULL;
+
+ /* Get the class and reference it */
+ Class = IntGetAndReferenceClass(ClassName, Cs->hInstance, FALSE);
+ if(!Class)
+ {
+ ERR("Failed to find class %wZ\n", ClassName);
+ goto cleanup;
+ }
+
+ /* Now find the parent and the owner window */
+ hWndParent = pti->rpdesk->pDeskInfo->spwnd->head.h;
+ hWndOwner = NULL;
+
+ if (Cs->hwndParent == HWND_MESSAGE)
+ {
+ Cs->hwndParent = hWndParent = pti->rpdesk->spwndMessage->head.h;
+ }
+ else if (Cs->hwndParent)
+ {
+ if ((Cs->style & (WS_CHILD|WS_POPUP)) != WS_CHILD)
+ hWndOwner = Cs->hwndParent;
+ else
+ hWndParent = Cs->hwndParent;
+ }
+ else if ((Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
+ {
+ ERR("Cannot create a child window without a parrent!\n");
+ EngSetLastError(ERROR_TLW_WITH_WSCHILD);
+ goto cleanup; /* WS_CHILD needs a parent, but WS_POPUP doesn't */
+ }
+
+ ParentWindow = hWndParent ? UserGetWindowObject(hWndParent): NULL;
+ OwnerWindow = hWndOwner ? UserGetWindowObject(hWndOwner): NULL;
+
+ /* FIXME: Is this correct? */
+ if(OwnerWindow)
+ OwnerWindow = UserGetAncestor(OwnerWindow, GA_ROOT);
+
+ /* Fix the position and the size of the window */
+ if (ParentWindow)
+ {
+ UserRefObjectCo(ParentWindow, &ParentRef);
+ IntFixWindowCoordinates(Cs, ParentWindow, &dwShowMode);
+ }
+
+ /* Allocate and initialize the new window */
+ Window = IntCreateWindow(Cs,
+ WindowName,
+ Class,
+ ParentWindow,
+ OwnerWindow,
+ acbiBuffer,
+ NULL);
+ if(!Window)
+ {
+ ERR("IntCreateWindow failed!\n");
+ goto cleanup;
+ }
+
+ hWnd = UserHMGetHandle(Window);
+ hwndInsertAfter = HWND_TOP;
+
+ UserRefObjectCo(Window, &Ref);
+ UserDereferenceObject(Window);
+ ObDereferenceObject(WinSta);
+
+ //// Check for a hook to eliminate overhead. ////
+ if ( ISITHOOKED(WH_CBT) || (pti->rpdesk->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_CBT)) )
+ {
+ // Allocate the calling structures Justin Case this goes Global.
+ pCsw = ExAllocatePoolWithTag(NonPagedPool, sizeof(CREATESTRUCTW), TAG_HOOK);
+ pCbtCreate = ExAllocatePoolWithTag(NonPagedPool, sizeof(CBT_CREATEWNDW), TAG_HOOK);
+ if (!pCsw || !pCbtCreate)
+ {
+ ERR("UserHeapAlloc() failed!\n");
+ goto cleanup;
+ }
+
+ /* Fill the new CREATESTRUCTW */
+ RtlCopyMemory(pCsw, Cs, sizeof(CREATESTRUCTW));
+ pCsw->style = Window->style; /* HCBT_CREATEWND needs the real window style */
+
+ // Based on the assumption this is from "unicode source" user32, ReactOS, answer is yes.
+ if (!IS_ATOM(ClassName->Buffer))
+ {
+ if (Window->state & WNDS_ANSICREATOR)
+ {
+ ANSI_STRING AnsiString;
+ AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(ClassName)+sizeof(CHAR);
+ pszClass = UserHeapAlloc(AnsiString.MaximumLength);
+ if (!pszClass)
+ {
+ ERR("UserHeapAlloc() failed!\n");
+ goto cleanup;
+ }
+ RtlZeroMemory(pszClass, AnsiString.MaximumLength);
+ AnsiString.Buffer = (PCHAR)pszClass;
+ RtlUnicodeStringToAnsiString(&AnsiString, ClassName, FALSE);
+ }
+ else
+ {
+ UNICODE_STRING UnicodeString;
+ UnicodeString.MaximumLength = ClassName->Length + sizeof(UNICODE_NULL);
+ pszClass = UserHeapAlloc(UnicodeString.MaximumLength);
+ if (!pszClass)
+ {
+ ERR("UserHeapAlloc() failed!\n");
+ goto cleanup;
+ }
+ RtlZeroMemory(pszClass, UnicodeString.MaximumLength);
+ UnicodeString.Buffer = (PWSTR)pszClass;
+ RtlCopyUnicodeString(&UnicodeString, ClassName);
+ }
+ pCsw->lpszClass = UserHeapAddressToUser(pszClass);
+ }
+ if (WindowName->Length)
+ {
+ UNICODE_STRING Name;
+ Name.Buffer = WindowName->Buffer;
+ Name.Length = (USHORT)min(WindowName->Length, MAXUSHORT); // FIXME: LARGE_STRING truncated
+ Name.MaximumLength = (USHORT)min(WindowName->MaximumLength, MAXUSHORT);
+
+ if (Window->state & WNDS_ANSICREATOR)
+ {
+ ANSI_STRING AnsiString;
+ AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&Name) + sizeof(CHAR);
+ pszName = UserHeapAlloc(AnsiString.MaximumLength);
+ if (!pszName)
+ {
+ ERR("UserHeapAlloc() failed!\n");
+ goto cleanup;
+ }
+ RtlZeroMemory(pszName, AnsiString.MaximumLength);
+ AnsiString.Buffer = (PCHAR)pszName;
+ RtlUnicodeStringToAnsiString(&AnsiString, &Name, FALSE);
+ }
+ else
+ {
+ UNICODE_STRING UnicodeString;
+ UnicodeString.MaximumLength = Name.Length + sizeof(UNICODE_NULL);
+ pszName = UserHeapAlloc(UnicodeString.MaximumLength);
+ if (!pszName)
+ {
+ ERR("UserHeapAlloc() failed!\n");
+ goto cleanup;
+ }
+ RtlZeroMemory(pszName, UnicodeString.MaximumLength);
+ UnicodeString.Buffer = (PWSTR)pszName;
+ RtlCopyUnicodeString(&UnicodeString, &Name);
+ }
+ pCsw->lpszName = UserHeapAddressToUser(pszName);
+ }
+
+ pCbtCreate->lpcs = pCsw;
+ pCbtCreate->hwndInsertAfter = hwndInsertAfter;
+
+ //// Call the WH_CBT hook ////
+ Result = co_HOOK_CallHooks(WH_CBT, HCBT_CREATEWND, (WPARAM) hWnd, (LPARAM) pCbtCreate);
+ if (Result != 0)
+ {
+ ERR("WH_CBT HCBT_CREATEWND hook failed! 0x%x\n", Result);
+ goto cleanup;
+ }
+ // Write back changes.
+ Cs->cx = pCsw->cx;
+ Cs->cy = pCsw->cy;
+ Cs->x = pCsw->x;
+ Cs->y = pCsw->y;
+ hwndInsertAfter = pCbtCreate->hwndInsertAfter;
+ }
+
+ /* NCCREATE and WM_NCCALCSIZE need the original values */
+ Cs->lpszName = (LPCWSTR) WindowName;
+ Cs->lpszClass = (LPCWSTR) ClassName;
+
+ if ((Cs->style & (WS_CHILD|WS_POPUP)) == WS_CHILD)
+ {
+ if (ParentWindow != co_GetDesktopWindow(Window))
+ {
+ Cs->x += ParentWindow->rcClient.left;
+ Cs->y += ParentWindow->rcClient.top;
+ }
+ }
+
+ /* Send the WM_GETMINMAXINFO message */
+ Size.cx = Cs->cx;
+ Size.cy = Cs->cy;
+
+ if ((Cs->style & WS_THICKFRAME) || !(Cs->style & (WS_POPUP | WS_CHILD)))
+ {
+ co_WinPosGetMinMaxInfo(Window, &MaxSize, &MaxPos, &MinTrack, &MaxTrack);
+ if (Size.cx > MaxTrack.x) Size.cx = MaxTrack.x;
+ if (Size.cy > MaxTrack.y) Size.cy = MaxTrack.y;
+ if (Size.cx < MinTrack.x) Size.cx = MinTrack.x;
+ if (Size.cy < MinTrack.y) Size.cy = MinTrack.y;
+ }
+
+ Window->rcWindow.left = Cs->x;
+ Window->rcWindow.top = Cs->y;
+ Window->rcWindow.right = Cs->x + Size.cx;
+ Window->rcWindow.bottom = Cs->y + Size.cy;
+/*
+ if (0 != (Window->style & WS_CHILD) && ParentWindow)
+ {
+// ERR("co_UserCreateWindowEx(): Offset rcWindow\n");
+ RECTL_vOffsetRect(&Window->rcWindow,
+ ParentWindow->rcClient.left,
+ ParentWindow->rcClient.top);
+ }
+*/
+ /* correct child window coordinates if mirroring on parent is enabled */
+ if (ParentWindow != NULL)
+ {
+ if ( ((Cs->style & WS_CHILD) == WS_CHILD) &&
+ ((ParentWindow->ExStyle & WS_EX_LAYOUTRTL) == WS_EX_LAYOUTRTL))
+ {
+ Window->rcWindow.right = ParentWindow->rcClient.right - (Window->rcWindow.left - ParentWindow->rcClient.left);
+ Window->rcWindow.left = Window->rcWindow.right - Size.cx;
+ }
+ }
+
+ Window->rcClient = Window->rcWindow;
+
+ /* Link the window */
+ if (NULL != ParentWindow)
+ {
+ /* Link the window into the siblings list */
+ if ((Cs->style & (WS_CHILD|WS_MAXIMIZE)) == WS_CHILD)
+ IntLinkHwnd(Window, HWND_BOTTOM);
+ else
+ IntLinkHwnd(Window, hwndInsertAfter);
+ }
+
+ if (!(Window->state2 & WNDS2_WIN31COMPAT))
+ {
+ if (Class->style & CS_PARENTDC && !(ParentWindow->style & WS_CLIPCHILDREN))
+ Window->style &= ~(WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
+ }
+
+ if ((Window->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
+ {
+ if ( !IntIsTopLevelWindow(Window) )
+ {
+ if (pti != Window->spwndParent->head.pti)
+ {
+ //ERR("CreateWindow Parent in.\n");
+ UserAttachThreadInput(pti, Window->spwndParent->head.pti, TRUE);
+ }
+ }
+ }
+
+ /* Send the NCCREATE message */
+ Result = co_IntSendMessage(UserHMGetHandle(Window), WM_NCCREATE, 0, (LPARAM) Cs);
+ if (!Result)
+ {
+ ERR("co_UserCreateWindowEx(): NCCREATE message failed\n");
+ goto cleanup;
+ }
+
+ /* Send the WM_NCCALCSIZE message */
+ {
+ // RECT rc;
+ MaxPos.x = Window->rcWindow.left;
+ MaxPos.y = Window->rcWindow.top;
+
+ Result = co_WinPosGetNonClientSize(Window, &Window->rcWindow, &Window->rcClient);
+ //rc = Window->rcWindow;
+ //Result = co_IntSendMessageNoWait(Window->head.h, WM_NCCALCSIZE, FALSE, (LPARAM)&rc);
+ //Window->rcClient = rc;
+
+ RECTL_vOffsetRect(&Window->rcWindow, MaxPos.x - Window->rcWindow.left,
+ MaxPos.y - Window->rcWindow.top);
+ }
+
+ /* Send the WM_CREATE message. */
+ Result = co_IntSendMessage(UserHMGetHandle(Window), WM_CREATE, 0, (LPARAM) Cs);
+ if (Result == (LRESULT)-1)
+ {
+ ERR("co_UserCreateWindowEx(): WM_CREATE message failed\n");
+ goto cleanup;
+ }
+
+ /* Send the EVENT_OBJECT_CREATE event */
+ IntNotifyWinEvent(EVENT_OBJECT_CREATE, Window, OBJID_WINDOW, CHILDID_SELF, 0);
+
+ /* By setting the flag below it can be examined to determine if the window
+ was created successfully and a valid pwnd was passed back to caller since
+ from here the function has to succeed. */
+ Window->state2 |= WNDS2_WMCREATEMSGPROCESSED;
+
+ /* Send the WM_SIZE and WM_MOVE messages. */
+ if (!(Window->state & WNDS_SENDSIZEMOVEMSGS))
+ {
+ co_WinPosSendSizeMove(Window);
+ }
+
+ /* Show or maybe minimize or maximize the window. */
+
+ style = IntSetStyle( Window, 0, WS_MAXIMIZE | WS_MINIMIZE );
+ if (style & (WS_MINIMIZE | WS_MAXIMIZE))
+ {
+ RECTL NewPos;
+ UINT SwFlag = (style & WS_MINIMIZE) ? SW_MINIMIZE : SW_MAXIMIZE;
+
+ SwFlag = co_WinPosMinMaximize(Window, SwFlag, &NewPos);
+ SwFlag |= SWP_NOZORDER|SWP_FRAMECHANGED; /* Frame always gets changed */
+ if (!(style & WS_VISIBLE) || (style & WS_CHILD) || UserGetActiveWindow()) SwFlag |= SWP_NOACTIVATE;
+ co_WinPosSetWindowPos(Window, 0, NewPos.left, NewPos.top,
+ NewPos.right, NewPos.bottom, SwFlag);
+ }
+
+ /* Send the WM_PARENTNOTIFY message */
+ IntSendParentNotify(Window, WM_CREATE);
+
+ /* Notify the shell that a new window was created */
+ if (Window->spwndParent == UserGetDesktopWindow() &&
+ Window->spwndOwner == NULL &&
+ (Window->style & WS_VISIBLE) &&
+ (!(Window->ExStyle & WS_EX_TOOLWINDOW) ||
+ (Window->ExStyle & WS_EX_APPWINDOW)))
+ {
+ co_IntShellHookNotify(HSHELL_WINDOWCREATED, (WPARAM)hWnd, 0);
+ }
+
+ /* Initialize and show the window's scrollbars */
+ if (Window->style & WS_VSCROLL)
+ {
+ co_UserShowScrollBar(Window, SB_VERT, FALSE, TRUE);
+ }
+ if (Window->style & WS_HSCROLL)
+ {
+ co_UserShowScrollBar(Window, SB_HORZ, TRUE, FALSE);
+ }
+
+ /* Show the new window */
+ if (Cs->style & WS_VISIBLE)
+ {
+ if (Window->style & WS_MAXIMIZE)
+ dwShowMode = SW_SHOW;
+ else if (Window->style & WS_MINIMIZE)
+ dwShowMode = SW_SHOWMINIMIZED;
+
+ co_WinPosShowWindow(Window, dwShowMode);
+
+ if (Window->ExStyle & WS_EX_MDICHILD)
+ {
+ ASSERT(ParentWindow);
+ if(!ParentWindow)
+ goto cleanup;
+ co_IntSendMessage(UserHMGetHandle(ParentWindow), WM_MDIREFRESHMENU, 0, 0);
+ /* ShowWindow won't activate child windows */
+ co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
+ }
+ }
+
+ TRACE("co_UserCreateWindowEx(): Created window %p\n", hWnd);
+ ret = Window;
+
+cleanup:
+ if (!ret)
+ {
+ TRACE("co_UserCreateWindowEx(): Error Created window!\n");
+ /* If the window was created, the class will be dereferenced by co_UserDestroyWindow */
+ if (Window)
+ co_UserDestroyWindow(Window);
+ else if (Class)
+ IntDereferenceClass(Class, pti->pDeskInfo, pti->ppi);
+ }
+
+ if (pCsw) ExFreePoolWithTag(pCsw, TAG_HOOK);
+ if (pCbtCreate) ExFreePoolWithTag(pCbtCreate, TAG_HOOK);
+ if (pszName) UserHeapFree(pszName);
+ if (pszClass) UserHeapFree(pszClass);
+
+ if (Window)
+ {
+ UserDerefObjectCo(Window);
+ }
+ if (ParentWindow) UserDerefObjectCo(ParentWindow);
+
+ return ret;
+}
+
+NTSTATUS
+NTAPI
+ProbeAndCaptureLargeString(
+ OUT PLARGE_STRING plstrSafe,
+ IN PLARGE_STRING plstrUnsafe)
+{
+ LARGE_STRING lstrTemp;
+ PVOID pvBuffer = NULL;
+
+ _SEH2_TRY
+ {
+ /* Probe and copy the string */
+ ProbeForRead(plstrUnsafe, sizeof(LARGE_STRING), sizeof(ULONG));
+ lstrTemp = *plstrUnsafe;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Fail */
+ _SEH2_YIELD(return _SEH2_GetExceptionCode();)
+ }
+ _SEH2_END
+
+ if (lstrTemp.Length != 0)
+ {
+ /* Allocate a buffer from paged pool */
+ pvBuffer = ExAllocatePoolWithTag(PagedPool, lstrTemp.Length, TAG_STRING);
+ if (!pvBuffer)
+ {
+ return STATUS_NO_MEMORY;
+ }
+
+ _SEH2_TRY
+ {
+ /* Probe and copy the buffer */
+ ProbeForRead(lstrTemp.Buffer, lstrTemp.Length, sizeof(WCHAR));
+ RtlCopyMemory(pvBuffer, lstrTemp.Buffer, lstrTemp.Length);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ /* Cleanup and fail */
+ ExFreePoolWithTag(pvBuffer, TAG_STRING);
+ _SEH2_YIELD(return _SEH2_GetExceptionCode();)
+ }
+ _SEH2_END
+ }
+
+ /* Set the output string */
+ plstrSafe->Buffer = pvBuffer;
+ plstrSafe->Length = lstrTemp.Length;
+ plstrSafe->MaximumLength = lstrTemp.Length;
+
+ return STATUS_SUCCESS;
+}
+
+/**
+ * \todo Allow passing plstrClassName as ANSI.
+ */
+HWND
+NTAPI
+NtUserCreateWindowEx(
+ DWORD dwExStyle,
+ PLARGE_STRING plstrClassName,
+ PLARGE_STRING plstrClsVersion,
+ PLARGE_STRING plstrWindowName,
+ DWORD dwStyle,
+ int x,
+ int y,
+ int nWidth,
+ int nHeight,
+ HWND hWndParent,
+ HMENU hMenu,
+ HINSTANCE hInstance,
+ LPVOID lpParam,
+ DWORD dwFlags,
+ PVOID acbiBuffer)
+{
+ NTSTATUS Status;
+ LARGE_STRING lstrWindowName;
+ LARGE_STRING lstrClassName;
+ UNICODE_STRING ustrClassName;
+ CREATESTRUCTW Cs;
+ HWND hwnd = NULL;
+ PWND pwnd;
+
+ lstrWindowName.Buffer = NULL;
+ lstrClassName.Buffer = NULL;
+
+ ASSERT(plstrWindowName);
+
+ if ( (dwStyle & (WS_POPUP|WS_CHILD)) != WS_CHILD)
+ {
+ /* check hMenu is valid handle */
+ if (hMenu && !ValidateHandle(hMenu, TYPE_MENU))
+ {
+ /* error is set in ValidateHandle */
+ return NULL;
+ }
+ }
+
+ /* Copy the window name to kernel mode */
+ Status = ProbeAndCaptureLargeString(&lstrWindowName, plstrWindowName);
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("NtUserCreateWindowEx: failed to capture plstrWindowName\n");
+ SetLastNtError(Status);
+ return NULL;
+ }
+
+ plstrWindowName = &lstrWindowName;
+
+ /* Check if the class is an atom */
+ if (IS_ATOM(plstrClassName))
+ {
+ /* It is, pass the atom in the UNICODE_STRING */
+ ustrClassName.Buffer = (PVOID)plstrClassName;
+ ustrClassName.Length = 0;
+ ustrClassName.MaximumLength = 0;
+ }
+ else
+ {
+ /* It's not, capture the class name */
+ Status = ProbeAndCaptureLargeString(&lstrClassName, plstrClassName);
+ if (!NT_SUCCESS(Status))
+ {
+ ERR("NtUserCreateWindowEx: failed to capture plstrClassName\n");
+ /* Set last error, cleanup and return */
+ SetLastNtError(Status);
+ goto cleanup;
+ }
+
+ /* We pass it on as a UNICODE_STRING */
+ ustrClassName.Buffer = lstrClassName.Buffer;
+ ustrClassName.Length = (USHORT)min(lstrClassName.Length, MAXUSHORT); // FIXME: LARGE_STRING truncated
+ ustrClassName.MaximumLength = (USHORT)min(lstrClassName.MaximumLength, MAXUSHORT);
+ }
+
+ /* Fill the CREATESTRUCTW */
+ /* we will keep here the original parameters */
+ Cs.style = dwStyle;
+ Cs.lpCreateParams = lpParam;
+ Cs.hInstance = hInstance;
+ Cs.hMenu = hMenu;
+ Cs.hwndParent = hWndParent;
+ Cs.cx = nWidth;
+ Cs.cy = nHeight;
+ Cs.x = x;
+ Cs.y = y;
+ Cs.lpszName = (LPCWSTR) plstrWindowName->Buffer;
+ if (IS_ATOM(plstrClassName))
+ Cs.lpszClass = (LPCWSTR) plstrClassName;
+ else
+ Cs.lpszClass = (LPCWSTR) plstrClassName->Buffer;
+ Cs.dwExStyle = dwExStyle;
+
+ UserEnterExclusive();
+
+ /* Call the internal function */
+ pwnd = co_UserCreateWindowEx(&Cs, &ustrClassName, plstrWindowName, acbiBuffer);
+
+ if(!pwnd)
+ {
+ ERR("co_UserCreateWindowEx failed!\n");
+ }
+ hwnd = pwnd ? UserHMGetHandle(pwnd) : NULL;
+
+ UserLeave();
+
+cleanup:
+ if (lstrWindowName.Buffer)
+ {
+ ExFreePoolWithTag(lstrWindowName.Buffer, TAG_STRING);
+ }
+ if (lstrClassName.Buffer)
+ {
+ ExFreePoolWithTag(lstrClassName.Buffer, TAG_STRING);
+ }
+
+ return hwnd;
+}
+
+
+BOOLEAN FASTCALL co_UserDestroyWindow(PWND Window)
+{
+ HWND hWnd;
+ PWND pwndTemp;
+ PTHREADINFO ti;
+ MSG msg;
+
+ ASSERT_REFS_CO(Window); // FIXME: Temp HACK?
+
+ hWnd = Window->head.h;
+ ti = PsGetCurrentThreadWin32Thread();
+
+ TRACE("co_UserDestroyWindow \n");
+
+ /* Check for owner thread */
+ if ( Window->head.pti != PsGetCurrentThreadWin32Thread())
+ {
+ /* Check if we are destroying the desktop window */
+ if (! ((Window->head.rpdesk->dwDTFlags & DF_DESTROYED) && Window == Window->head.rpdesk->pDeskInfo->spwnd))
+ {
+ EngSetLastError(ERROR_ACCESS_DENIED);
+ return FALSE;
+ }
+ }
+
+ /* If window was created successfully and it is hooked */
+ if ((Window->state2 & WNDS2_WMCREATEMSGPROCESSED))
+ {
+ if (co_HOOK_CallHooks(WH_CBT, HCBT_DESTROYWND, (WPARAM) hWnd, 0))
+ {
+ ERR("Destroy Window WH_CBT Call Hook return!\n");
+ return FALSE;
+ }
+ }
+
+ if (Window->pcls->atomClassName != gpsi->atomSysClass[ICLS_IME])
+ {
+ if ((Window->style & (WS_POPUP|WS_CHILD)) != WS_CHILD)
+ {
+ if (Window->spwndOwner)
+ {
+ //ERR("DestroyWindow Owner out.\n");
+ UserAttachThreadInput(Window->head.pti, Window->spwndOwner->head.pti, FALSE);
+ }
+ }
+ }
+
+ /* Inform the parent */
+ if (Window->style & WS_CHILD)
+ {
+ IntSendParentNotify(Window, WM_DESTROY);
+ }
+
+ /* Look whether the focus is within the tree of windows we will
+ * be destroying.
+ */
+ if (!co_WinPosShowWindow(Window, SW_HIDE))
+ { // Rule #1.
+ if (ti->MessageQueue->spwndActive == Window && ti->MessageQueue == IntGetFocusMessageQueue())
+ {
+ co_WinPosActivateOtherWindow(Window);
+ }
+ }
+
+ // Adjust last active.
+ if ((pwndTemp = Window->spwndOwner))
+ {
+ while (pwndTemp->spwndOwner)
+ pwndTemp = pwndTemp->spwndOwner;
+
+ if (pwndTemp->spwndLastActive == Window)
+ pwndTemp->spwndLastActive = Window->spwndOwner;
+ }
+
+ if (Window->spwndParent && IntIsWindow(Window->head.h))
+ {
+ if ((Window->style & (WS_POPUP | WS_CHILD)) == WS_CHILD)
+ {
+ if (!IntIsTopLevelWindow(Window))
+ {
+ //ERR("DestroyWindow Parent out.\n");
+ UserAttachThreadInput(Window->head.pti, Window->spwndParent->head.pti, FALSE);
+ }
+ }
+ }
+
+ if (Window->head.pti->MessageQueue->spwndActive == Window)
+ Window->head.pti->MessageQueue->spwndActive = NULL;
+ if (Window->head.pti->MessageQueue->spwndFocus == Window)
+ Window->head.pti->MessageQueue->spwndFocus = NULL;
+ if (Window->head.pti->MessageQueue->spwndActivePrev == Window)
+ Window->head.pti->MessageQueue->spwndActivePrev = NULL;
+ if (Window->head.pti->MessageQueue->spwndCapture == Window)
+ Window->head.pti->MessageQueue->spwndCapture = NULL;
+
+ /*
+ * Check if this window is the Shell's Desktop Window. If so set hShellWindow to NULL
+ */
+
+ if ((ti != NULL) && (ti->pDeskInfo != NULL))
+ {
+ if (ti->pDeskInfo->hShellWindow == hWnd)
+ {
+ ERR("Destroying the ShellWindow!\n");
+ ti->pDeskInfo->hShellWindow = NULL;
+ }
+ }
+
+ IntEngWindowChanged(Window, WOC_DELETE);
+
+ if (!IntIsWindow(Window->head.h))
+ {
+ return TRUE;
+ }
+
+ /* Recursively destroy owned windows */
+
+ if (! (Window->style & WS_CHILD))
+ {
+ for (;;)
+ {
+ BOOL GotOne = FALSE;
+ HWND *Children;
+ HWND *ChildHandle;
+ PWND Child, Desktop;
+
+ Desktop = IntIsDesktopWindow(Window) ? Window :
+ UserGetWindowObject(IntGetDesktopWindow());
+ Children = IntWinListChildren(Desktop);
+
+ if (Children)
+ {
+ for (ChildHandle = Children; *ChildHandle; ++ChildHandle)
+ {
+ Child = UserGetWindowObject(*ChildHandle);
+ if (Child == NULL)
+ continue;
+ if (Child->spwndOwner != Window)
+ {
+ continue;
+ }
+
+ if (IntWndBelongsToThread(Child, PsGetCurrentThreadWin32Thread()))
+ {
+ USER_REFERENCE_ENTRY ChildRef;
+ UserRefObjectCo(Child, &ChildRef); // Temp HACK?
+ co_UserDestroyWindow(Child);
+ UserDerefObjectCo(Child); // Temp HACK?
+
+ GotOne = TRUE;
+ continue;
+ }
+
+ if (Child->spwndOwner != NULL)
+ {
+ Child->spwndOwner = NULL;
+ }
+
+ }
+ ExFreePoolWithTag(Children, USERTAG_WINDOWLIST);
+ }
+ if (! GotOne)
+ {
+ break;
+ }
+ }
+ }
+
+ /* Generate mouse move message for the next window */
+ msg.message = WM_MOUSEMOVE;
+ msg.wParam = UserGetMouseButtonsState();
+ msg.lParam = MAKELPARAM(gpsi->ptCursor.x, gpsi->ptCursor.y);
+ msg.pt = gpsi->ptCursor;
+ co_MsqInsertMouseMessage(&msg, 0, 0, TRUE);
+
+ if (!IntIsWindow(Window->head.h))
+ {
+ return TRUE;
+ }
+
+ /* Destroy the window storage */
+ co_UserFreeWindow(Window, PsGetCurrentProcessWin32Process(), PsGetCurrentThreadWin32Thread(), TRUE);
+
+ return TRUE;
+}
+
+
+/*
+ * @implemented
+ */
+BOOLEAN APIENTRY
+NtUserDestroyWindow(HWND Wnd)
+{
+ PWND Window;
+ DECLARE_RETURN(BOOLEAN);
+ BOOLEAN ret;
+ USER_REFERENCE_ENTRY Ref;
+
+ TRACE("Enter NtUserDestroyWindow\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(Wnd)))
+ {
+ RETURN(FALSE);
+ }
+
+ UserRefObjectCo(Window, &Ref); // FIXME: Dunno if win should be reffed during destroy...
+ ret = co_UserDestroyWindow(Window);
+ UserDerefObjectCo(Window); // FIXME: Dunno if win should be reffed during destroy...
+
+ RETURN(ret);
+
+CLEANUP:
+ TRACE("Leave NtUserDestroyWindow, ret=%u\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+static HWND FASTCALL
+IntFindWindow(PWND Parent,
+ PWND ChildAfter,
+ RTL_ATOM ClassAtom,
+ PUNICODE_STRING WindowName)
+{
+ BOOL CheckWindowName;
+ HWND *List, *phWnd;
+ HWND Ret = NULL;
+ UNICODE_STRING CurrentWindowName;
+
+ ASSERT(Parent);
+
+ CheckWindowName = WindowName->Buffer != 0;
+
+ if((List = IntWinListChildren(Parent)))
+ {
+ phWnd = List;
+ if(ChildAfter)
+ {
+ /* skip handles before and including ChildAfter */
+ while(*phWnd && (*(phWnd++) != ChildAfter->head.h))
+ ;
+ }
+
+ /* search children */
+ while(*phWnd)
+ {
+ PWND Child;
+ if(!(Child = UserGetWindowObject(*(phWnd++))))
+ {
+ continue;
+ }
+
+ /* Do not send WM_GETTEXT messages in the kernel mode version!
+ The user mode version however calls GetWindowText() which will
+ send WM_GETTEXT messages to windows belonging to its processes */
+ if (!ClassAtom || Child->pcls->atomClassName == ClassAtom)
+ {
+ // FIXME: LARGE_STRING truncated
+ CurrentWindowName.Buffer = Child->strName.Buffer;
+ CurrentWindowName.Length = (USHORT)min(Child->strName.Length, MAXUSHORT);
+ CurrentWindowName.MaximumLength = (USHORT)min(Child->strName.MaximumLength, MAXUSHORT);
+ if(!CheckWindowName ||
+ (Child->strName.Length < 0xFFFF &&
+ !RtlCompareUnicodeString(WindowName, &CurrentWindowName, TRUE)))
+ {
+ Ret = Child->head.h;
+ break;
+ }
+ }
+ }
+ ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
+ }
+
+ return Ret;
+}
+
+/*
+ * FUNCTION:
+ * Searches a window's children for a window with the specified
+ * class and name
+ * ARGUMENTS:
+ * hwndParent = The window whose childs are to be searched.
+ * NULL = desktop
+ * HWND_MESSAGE = message-only windows
+ *
+ * hwndChildAfter = Search starts after this child window.
+ * NULL = start from beginning
+ *
+ * ucClassName = Class name to search for
+ * Reguired parameter.
+ *
+ * ucWindowName = Window name
+ * ->Buffer == NULL = don't care
+ *
+ * RETURNS:
+ * The HWND of the window if it was found, otherwise NULL
+ */
+/*
+ * @implemented
+ */
+HWND APIENTRY
+NtUserFindWindowEx(HWND hwndParent,
+ HWND hwndChildAfter,
+ PUNICODE_STRING ucClassName,
+ PUNICODE_STRING ucWindowName,
+ DWORD dwUnknown)
+{
+ PWND Parent, ChildAfter;
+ UNICODE_STRING ClassName = {0}, WindowName = {0};
+ HWND Desktop, Ret = NULL;
+ BOOL DoMessageWnd = FALSE;
+ RTL_ATOM ClassAtom = (RTL_ATOM)0;
+ DECLARE_RETURN(HWND);
+
+ TRACE("Enter NtUserFindWindowEx\n");
+ UserEnterShared();
+
+ if (ucClassName != NULL || ucWindowName != NULL)
+ {
+ _SEH2_TRY
+ {
+ if (ucClassName != NULL)
+ {
+ ClassName = ProbeForReadUnicodeString(ucClassName);
+ if (ClassName.Length != 0)
+ {
+ ProbeForRead(ClassName.Buffer,
+ ClassName.Length,
+ sizeof(WCHAR));
+ }
+ else if (!IS_ATOM(ClassName.Buffer))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ _SEH2_LEAVE;
+ }
+
+ if (!IntGetAtomFromStringOrAtom(&ClassName,
+ &ClassAtom))
+ {
+ _SEH2_LEAVE;
+ }
+ }
+
+ if (ucWindowName != NULL)
+ {
+ WindowName = ProbeForReadUnicodeString(ucWindowName);
+ if (WindowName.Length != 0)
+ {
+ ProbeForRead(WindowName.Buffer,
+ WindowName.Length,
+ sizeof(WCHAR));
+ }
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ _SEH2_YIELD(RETURN(NULL));
+ }
+ _SEH2_END;
+
+ if (ucClassName != NULL)
+ {
+ if (ClassName.Length == 0 && ClassName.Buffer != NULL &&
+ !IS_ATOM(ClassName.Buffer))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ RETURN(NULL);
+ }
+ else if (ClassAtom == (RTL_ATOM)0)
+ {
+ /* LastError code was set by IntGetAtomFromStringOrAtom */
+ RETURN(NULL);
+ }
+ }
+ }
+
+ Desktop = IntGetCurrentThreadDesktopWindow();
+
+ if(hwndParent == NULL)
+ {
+ hwndParent = Desktop;
+ DoMessageWnd = TRUE;
+ }
+ else if(hwndParent == HWND_MESSAGE)
+ {
+ hwndParent = IntGetMessageWindow();
+ }
+
+ if(!(Parent = UserGetWindowObject(hwndParent)))
+ {
+ RETURN( NULL);
+ }
+
+ ChildAfter = NULL;
+ if(hwndChildAfter && !(ChildAfter = UserGetWindowObject(hwndChildAfter)))
+ {
+ RETURN( NULL);
+ }
+
+ _SEH2_TRY
+ {
+ if(Parent->head.h == Desktop)
+ {
+ HWND *List, *phWnd;
+ PWND TopLevelWindow;
+ BOOLEAN CheckWindowName;
+ BOOLEAN WindowMatches;
+ BOOLEAN ClassMatches;
+
+ /* windows searches through all top-level windows if the parent is the desktop
+ window */
+
+ if((List = IntWinListChildren(Parent)))
+ {
+ phWnd = List;
+
+ if(ChildAfter)
+ {
+ /* skip handles before and including ChildAfter */
+ while(*phWnd && (*(phWnd++) != ChildAfter->head.h))
+ ;
+ }
+
+ CheckWindowName = WindowName.Buffer != 0;
+
+ /* search children */
+ while(*phWnd)
+ {
+ UNICODE_STRING ustr;
+
+ if(!(TopLevelWindow = UserGetWindowObject(*(phWnd++))))
+ {
+ continue;
+ }
+
+ /* Do not send WM_GETTEXT messages in the kernel mode version!
+ The user mode version however calls GetWindowText() which will
+ send WM_GETTEXT messages to windows belonging to its processes */
+ ustr.Buffer = TopLevelWindow->strName.Buffer;
+ ustr.Length = (USHORT)min(TopLevelWindow->strName.Length, MAXUSHORT); // FIXME:LARGE_STRING truncated
+ ustr.MaximumLength = (USHORT)min(TopLevelWindow->strName.MaximumLength, MAXUSHORT);
+ WindowMatches = !CheckWindowName ||
+ (TopLevelWindow->strName.Length < 0xFFFF &&
+ !RtlCompareUnicodeString(&WindowName, &ustr, TRUE));
+ ClassMatches = (ClassAtom == (RTL_ATOM)0) ||
+ ClassAtom == TopLevelWindow->pcls->atomClassName;
+
+ if (WindowMatches && ClassMatches)
+ {
+ Ret = TopLevelWindow->head.h;
+ break;
+ }
+
+ if (IntFindWindow(TopLevelWindow, NULL, ClassAtom, &WindowName))
+ {
+ /* window returns the handle of the top-level window, in case it found
+ the child window */
+ Ret = TopLevelWindow->head.h;
+ break;
+ }
+
+ }
+ ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
+ }
+ }
+ else
+ {
+ ERR("FindWindowEx: Not Desktop Parent!\n");
+ Ret = IntFindWindow(Parent, ChildAfter, ClassAtom, &WindowName);
+ }
+
+ if (Ret == NULL && DoMessageWnd)
+ {
+ PWND MsgWindows;
+
+ if((MsgWindows = UserGetWindowObject(IntGetMessageWindow())))
+ {
+ Ret = IntFindWindow(MsgWindows, ChildAfter, ClassAtom, &WindowName);
+ }
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ Ret = NULL;
+ }
+ _SEH2_END;
+
+ RETURN( Ret);
+
+CLEANUP:
+ TRACE("Leave NtUserFindWindowEx, ret %p\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+PWND FASTCALL UserGetAncestor(PWND Wnd, UINT Type)
+{
+ PWND WndAncestor, Parent;
+
+ if (Wnd->head.h == IntGetDesktopWindow())
+ {
+ return NULL;
+ }
+
+ switch (Type)
+ {
+ case GA_PARENT:
+ {
+ WndAncestor = Wnd->spwndParent;
+ break;
+ }
+
+ case GA_ROOT:
+ {
+ WndAncestor = Wnd;
+ Parent = NULL;
+
+ for(;;)
+ {
+ if(!(Parent = WndAncestor->spwndParent))
+ {
+ break;
+ }
+ if(IntIsDesktopWindow(Parent))
+ {
+ break;
+ }
+
+ WndAncestor = Parent;
+ }
+ break;
+ }
+
+ case GA_ROOTOWNER:
+ {
+ WndAncestor = Wnd;
+
+ for (;;)
+ {
+ Parent = IntGetParent(WndAncestor);
+
+ if (!Parent)
+ {
+ break;
+ }
+
+ WndAncestor = Parent;
+ }
+ break;
+ }
+
+ default:
+ {
+ return NULL;
+ }
+ }
+
+ return WndAncestor;
+}
+
+/*
+ * @implemented
+ */
+HWND APIENTRY
+NtUserGetAncestor(HWND hWnd, UINT Type)
+{
+ PWND Window, Ancestor;
+ DECLARE_RETURN(HWND);
+
+ TRACE("Enter NtUserGetAncestor\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(NULL);
+ }
+
+ Ancestor = UserGetAncestor(Window, Type);
+ /* faxme: can UserGetAncestor ever return NULL for a valid window? */
+
+ RETURN(Ancestor ? Ancestor->head.h : NULL);
+
+CLEANUP:
+ TRACE("Leave NtUserGetAncestor, ret=%p\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+////
+//// ReactOS work around! Keep it the sames as in Combo.c and Controls.h
+////
+/* combo state struct */
+typedef struct
+{
+ HWND self;
+ HWND owner;
+ UINT dwStyle;
+ HWND hWndEdit;
+ HWND hWndLBox;
+ UINT wState;
+ HFONT hFont;
+ RECT textRect;
+ RECT buttonRect;
+ RECT droppedRect;
+ INT droppedIndex;
+ INT fixedOwnerDrawHeight;
+ INT droppedWidth; /* last two are not used unless set */
+ INT editHeight; /* explicitly */
+ LONG UIState;
+} HEADCOMBO,*LPHEADCOMBO;
+
+// Window Extra data container.
+typedef struct _WND2CBOX
+{
+ WND;
+ LPHEADCOMBO pCBox;
+} WND2CBOX, *PWND2CBOX;
+
+#define CBF_BUTTONDOWN 0x0002
+////
+////
+////
+BOOL
+APIENTRY
+NtUserGetComboBoxInfo(
+ HWND hWnd,
+ PCOMBOBOXINFO pcbi)
+{
+ PWND Wnd;
+ PPROCESSINFO ppi;
+ BOOL NotSameppi = FALSE;
+ BOOL Ret = TRUE;
+ DECLARE_RETURN(BOOL);
+
+ TRACE("Enter NtUserGetComboBoxInfo\n");
+ UserEnterShared();
+
+ if (!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE );
+ }
+ _SEH2_TRY
+ {
+ if(pcbi)
+ {
+ ProbeForWrite(pcbi,
+ sizeof(COMBOBOXINFO),
+ 1);
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ SetLastNtError(_SEH2_GetExceptionCode());
+ _SEH2_YIELD(RETURN(FALSE));
+ }
+ _SEH2_END;
+
+ if (pcbi->cbSize < sizeof(COMBOBOXINFO))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ RETURN(FALSE);
+ }
+
+ // Pass the user pointer, it was already probed.
+ if ((Wnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_COMBOBOX]) && Wnd->fnid != FNID_COMBOBOX)
+ {
+ RETURN( (BOOL) co_IntSendMessage( Wnd->head.h, CB_GETCOMBOBOXINFO, 0, (LPARAM)pcbi));
+ }
+
+ ppi = PsGetCurrentProcessWin32Process();
+ NotSameppi = ppi != Wnd->head.pti->ppi;
+ if (NotSameppi)
+ {
+ KeAttachProcess(&Wnd->head.pti->ppi->peProcess->Pcb);
+ }
+
+ _SEH2_TRY
+ {
+ LPHEADCOMBO lphc = ((PWND2CBOX)Wnd)->pCBox;
+ pcbi->rcItem = lphc->textRect;
+ pcbi->rcButton = lphc->buttonRect;
+ pcbi->stateButton = 0;
+ if (lphc->wState & CBF_BUTTONDOWN)
+ pcbi->stateButton |= STATE_SYSTEM_PRESSED;
+ if (RECTL_bIsEmptyRect(&lphc->buttonRect))
+ pcbi->stateButton |= STATE_SYSTEM_INVISIBLE;
+ pcbi->hwndCombo = lphc->self;
+ pcbi->hwndItem = lphc->hWndEdit;
+ pcbi->hwndList = lphc->hWndLBox;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Ret = FALSE;
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ RETURN( Ret);
+
+CLEANUP:
+ if (NotSameppi) KeDetachProcess();
+ TRACE("Leave NtUserGetComboBoxInfo, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+////
+//// ReactOS work around! Keep it the sames as in Listbox.c
+////
+/* Listbox structure */
+typedef struct
+{
+ HWND self; /* Our own window handle */
+ HWND owner; /* Owner window to send notifications to */
+ UINT style; /* Window style */
+ INT width; /* Window width */
+ INT height; /* Window height */
+ VOID *items; /* Array of items */
+ INT nb_items; /* Number of items */
+ INT top_item; /* Top visible item */
+ INT selected_item; /* Selected item */
+ INT focus_item; /* Item that has the focus */
+ INT anchor_item; /* Anchor item for extended selection */
+ INT item_height; /* Default item height */
+ INT page_size; /* Items per listbox page */
+ INT column_width; /* Column width for multi-column listboxes */
+} LB_DESCR;
+
+// Window Extra data container.
+typedef struct _WND2LB
+{
+ WND;
+ LB_DESCR * pLBiv;
+} WND2LB, *PWND2LB;
+////
+////
+////
+DWORD
+APIENTRY
+NtUserGetListBoxInfo(
+ HWND hWnd)
+{
+ PWND Wnd;
+ PPROCESSINFO ppi;
+ BOOL NotSameppi = FALSE;
+ DWORD Ret = 0;
+ DECLARE_RETURN(DWORD);
+
+ TRACE("Enter NtUserGetListBoxInfo\n");
+ UserEnterShared();
+
+ if (!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0 );
+ }
+
+ if ((Wnd->pcls->atomClassName != gpsi->atomSysClass[ICLS_LISTBOX]) && Wnd->fnid != FNID_LISTBOX)
+ {
+ RETURN( (DWORD) co_IntSendMessage( Wnd->head.h, LB_GETLISTBOXINFO, 0, 0 ));
+ }
+
+ // wine lisbox:test_GetListBoxInfo lb_getlistboxinfo = 0, should not send a message!
+ ppi = PsGetCurrentProcessWin32Process();
+ NotSameppi = ppi != Wnd->head.pti->ppi;
+ if (NotSameppi)
+ {
+ KeAttachProcess(&Wnd->head.pti->ppi->peProcess->Pcb);
+ }
+
+ _SEH2_TRY
+ {
+ LB_DESCR *descr = ((PWND2LB)Wnd)->pLBiv;
+ // See Controls ListBox.c:LB_GETLISTBOXINFO must match...
+ if (descr->style & LBS_MULTICOLUMN) //// ReactOS
+ Ret = descr->page_size * descr->column_width;
+ else
+ Ret = descr->page_size;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Ret = 0;
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ RETURN( Ret);
+
+CLEANUP:
+ if (NotSameppi) KeDetachProcess();
+ TRACE("Leave NtUserGetListBoxInfo, ret=%lu\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserSetParent
+ *
+ * The NtUserSetParent function changes the parent window of the specified
+ * child window.
+ *
+ * Remarks
+ * The new parent window and the child window must belong to the same
+ * application. If the window identified by the hWndChild parameter is
+ * visible, the system performs the appropriate redrawing and repainting.
+ * For compatibility reasons, NtUserSetParent does not modify the WS_CHILD
+ * or WS_POPUP window styles of the window whose parent is being changed.
+ *
+ * Status
+ * @implemented
+ */
+
+HWND APIENTRY
+NtUserSetParent(HWND hWndChild, HWND hWndNewParent)
+{
+ DECLARE_RETURN(HWND);
+
+ TRACE("Enter NtUserSetParent\n");
+ UserEnterExclusive();
+
+ /*
+ Check Parent first from user space, set it here.
+ */
+ if (!hWndNewParent)
+ {
+ hWndNewParent = IntGetDesktopWindow();
+ }
+ else if (hWndNewParent == HWND_MESSAGE)
+ {
+ hWndNewParent = IntGetMessageWindow();
+ }
+
+ RETURN( co_UserSetParent(hWndChild, hWndNewParent));
+
+CLEANUP:
+ TRACE("Leave NtUserSetParent, ret=%p\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * UserGetShellWindow
+ *
+ * Returns a handle to shell window that was set by NtUserSetShellWindowEx.
+ *
+ * Status
+ * @implemented
+ */
+HWND FASTCALL UserGetShellWindow(VOID)
+{
+ PWINSTATION_OBJECT WinStaObject;
+ HWND Ret;
+
+ NTSTATUS Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+ KernelMode,
+ 0,
+ &WinStaObject);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ return( (HWND)0);
+ }
+
+ Ret = (HWND)WinStaObject->ShellWindow;
+
+ ObDereferenceObject(WinStaObject);
+ return( Ret);
+}
+
+/*
+ * NtUserSetShellWindowEx
+ *
+ * This is undocumented function to set global shell window. The global
+ * shell window has special handling of window position.
+ *
+ * Status
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetShellWindowEx(HWND hwndShell, HWND hwndListView)
+{
+ PWINSTATION_OBJECT WinStaObject;
+ PWND WndShell, WndListView;
+ DECLARE_RETURN(BOOL);
+ USER_REFERENCE_ENTRY Ref;
+ NTSTATUS Status;
+ PTHREADINFO ti;
+
+ TRACE("Enter NtUserSetShellWindowEx\n");
+ UserEnterExclusive();
+
+ if (!(WndShell = UserGetWindowObject(hwndShell)))
+ {
+ RETURN(FALSE);
+ }
+
+ if(!(WndListView = UserGetWindowObject(hwndListView)))
+ {
+ RETURN(FALSE);
+ }
+
+ Status = IntValidateWindowStationHandle(PsGetCurrentProcess()->Win32WindowStation,
+ KernelMode,
+ 0,
+ &WinStaObject);
+
+ if (!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( FALSE);
+ }
+
+ /*
+ * Test if we are permitted to change the shell window.
+ */
+ if (WinStaObject->ShellWindow)
+ {
+ ObDereferenceObject(WinStaObject);
+ RETURN( FALSE);
+ }
+
+ /*
+ * Move shell window into background.
+ */
+ if (hwndListView && hwndListView != hwndShell)
+ {
+ /*
+ * Disabled for now to get Explorer working.
+ * -- Filip, 01/nov/2003
+ */
+#if 0
+ co_WinPosSetWindowPos(WndListView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
+#endif
+
+ if (WndListView->ExStyle & WS_EX_TOPMOST)
+ {
+ ObDereferenceObject(WinStaObject);
+ RETURN( FALSE);
+ }
+ }
+
+ if (WndShell->ExStyle & WS_EX_TOPMOST)
+ {
+ ObDereferenceObject(WinStaObject);
+ RETURN( FALSE);
+ }
+
+ UserRefObjectCo(WndShell, &Ref);
+ WndShell->state2 |= WNDS2_BOTTOMMOST;
+ co_WinPosSetWindowPos(WndShell, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
+
+ WinStaObject->ShellWindow = hwndShell;
+ WinStaObject->ShellListView = hwndListView;
+
+ ti = GetW32ThreadInfo();
+ if (ti->pDeskInfo)
+ {
+ ti->pDeskInfo->hShellWindow = hwndShell;
+ ti->pDeskInfo->spwndShell = WndShell;
+ ti->pDeskInfo->ppiShellProcess = ti->ppi;
+ }
+
+ UserRegisterHotKey(WndShell, SC_TASKLIST, MOD_CONTROL, VK_ESCAPE);
+
+ UserDerefObjectCo(WndShell);
+
+ ObDereferenceObject(WinStaObject);
+ RETURN( TRUE);
+
+CLEANUP:
+ TRACE("Leave NtUserSetShellWindowEx, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserGetSystemMenu
+ *
+ * The NtUserGetSystemMenu function allows the application to access the
+ * window menu (also known as the system menu or the control menu) for
+ * copying and modifying.
+ *
+ * Parameters
+ * hWnd
+ * Handle to the window that will own a copy of the window menu.
+ * bRevert
+ * Specifies the action to be taken. If this parameter is FALSE,
+ * NtUserGetSystemMenu returns a handle to the copy of the window menu
+ * currently in use. The copy is initially identical to the window menu
+ * but it can be modified.
+ * If this parameter is TRUE, GetSystemMenu resets the window menu back
+ * to the default state. The previous window menu, if any, is destroyed.
+ *
+ * Return Value
+ * If the bRevert parameter is FALSE, the return value is a handle to a
+ * copy of the window menu. If the bRevert parameter is TRUE, the return
+ * value is NULL.
+ *
+ * Status
+ * @implemented
+ */
+
+HMENU APIENTRY
+NtUserGetSystemMenu(HWND hWnd, BOOL bRevert)
+{
+ PWND Window;
+ PMENU Menu;
+ DECLARE_RETURN(HMENU);
+
+ TRACE("Enter NtUserGetSystemMenu\n");
+ UserEnterShared();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN(NULL);
+ }
+
+ if (!(Menu = IntGetSystemMenu(Window, bRevert, FALSE)))
+ {
+ RETURN(NULL);
+ }
+
+ RETURN(Menu->head.h);
+
+CLEANUP:
+ TRACE("Leave NtUserGetSystemMenu, ret=%p\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserSetSystemMenu
+ *
+ * Status
+ * @implemented
+ */
+
+BOOL APIENTRY
+NtUserSetSystemMenu(HWND hWnd, HMENU hMenu)
+{
+ BOOL Result = FALSE;
+ PWND Window;
+ PMENU Menu;
+ DECLARE_RETURN(BOOL);
+
+ TRACE("Enter NtUserSetSystemMenu\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+
+ if (hMenu)
+ {
+ /*
+ * Assign new menu handle.
+ */
+ if (!(Menu = UserGetMenuObject(hMenu)))
+ {
+ RETURN( FALSE);
+ }
+
+ Result = IntSetSystemMenu(Window, Menu);
+ }
+
+ RETURN( Result);
+
+CLEANUP:
+ TRACE("Leave NtUserSetSystemMenu, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+// Fixes wine Win test_window_styles and todo tests...
+static BOOL FASTCALL
+IntCheckFrameEdge(ULONG Style, ULONG ExStyle)
+{
+ if (ExStyle & WS_EX_DLGMODALFRAME)
+ return TRUE;
+ else if (!(ExStyle & WS_EX_STATICEDGE) && (Style & (WS_DLGFRAME | WS_THICKFRAME)))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+LONG FASTCALL
+co_UserSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi)
+{
+ PWND Window, Parent;
+ PWINSTATION_OBJECT WindowStation;
+ LONG OldValue;
+ STYLESTRUCT Style;
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ return( 0);
+ }
+
+ if ((INT)Index >= 0)
+ {
+ if ((Index + sizeof(LONG)) > Window->cbwndExtra)
+ {
+ EngSetLastError(ERROR_INVALID_INDEX);
+ return( 0);
+ }
+
+ OldValue = *((LONG *)((PCHAR)(Window + 1) + Index));
+/*
+ if ( Index == DWLP_DLGPROC && Wnd->state & WNDS_DIALOGWINDOW)
+ {
+ OldValue = (LONG)IntSetWindowProc( Wnd,
+ (WNDPROC)NewValue,
+ Ansi);
+ if (!OldValue) return 0;
+ }
+*/
+ *((LONG *)((PCHAR)(Window + 1) + Index)) = NewValue;
+ }
+ else
+ {
+ switch (Index)
+ {
+ case GWL_EXSTYLE:
+ OldValue = (LONG) Window->ExStyle;
+ Style.styleOld = OldValue;
+ Style.styleNew = NewValue;
+
+ co_IntSendMessage(hWnd, WM_STYLECHANGING, GWL_EXSTYLE, (LPARAM) &Style);
+
+ /*
+ * Remove extended window style bit WS_EX_TOPMOST for shell windows.
+ */
+ WindowStation = Window->head.pti->rpdesk->rpwinstaParent;
+ if(WindowStation)
+ {
+ if (hWnd == WindowStation->ShellWindow || hWnd == WindowStation->ShellListView)
+ Style.styleNew &= ~WS_EX_TOPMOST;
+ }
+ /* WS_EX_WINDOWEDGE depends on some other styles */
+ if (IntCheckFrameEdge(Window->style, NewValue))
+ Style.styleNew |= WS_EX_WINDOWEDGE;
+ else
+ Style.styleNew &= ~WS_EX_WINDOWEDGE;
+
+ Window->ExStyle = (DWORD)Style.styleNew;
+ co_IntSendMessage(hWnd, WM_STYLECHANGED, GWL_EXSTYLE, (LPARAM) &Style);
+ break;
+
+ case GWL_STYLE:
+ OldValue = (LONG) Window->style;
+ Style.styleOld = OldValue;
+ Style.styleNew = NewValue;
+ co_IntSendMessage(hWnd, WM_STYLECHANGING, GWL_STYLE, (LPARAM) &Style);
+
+ /* WS_CLIPSIBLINGS can't be reset on top-level windows */
+ if (Window->spwndParent == UserGetDesktopWindow()) Style.styleNew |= WS_CLIPSIBLINGS;
+ /* Fixes wine FIXME: changing WS_DLGFRAME | WS_THICKFRAME is supposed to change WS_EX_WINDOWEDGE too */
+ if (IntCheckFrameEdge(NewValue, Window->ExStyle))
+ Window->ExStyle |= WS_EX_WINDOWEDGE;
+ else
+ Window->ExStyle &= ~WS_EX_WINDOWEDGE;
+
+ if ((Style.styleOld ^ Style.styleNew) & WS_VISIBLE)
+ {
+ if (Style.styleOld & WS_VISIBLE) Window->head.pti->cVisWindows--;
+ if (Style.styleNew & WS_VISIBLE) Window->head.pti->cVisWindows++;
+ DceResetActiveDCEs( Window );
+ }
+ Window->style = (DWORD)Style.styleNew;
+ co_IntSendMessage(hWnd, WM_STYLECHANGED, GWL_STYLE, (LPARAM) &Style);
+ break;
+
+ case GWL_WNDPROC:
+ {
+ if ( Window->head.pti->ppi != PsGetCurrentProcessWin32Process() ||
+ Window->fnid & FNID_FREED)
+ {
+ EngSetLastError(ERROR_ACCESS_DENIED);
+ return( 0);
+ }
+ OldValue = (LONG)IntSetWindowProc(Window,
+ (WNDPROC)NewValue,
+ Ansi);
+ break;
+ }
+
+ case GWL_HINSTANCE:
+ OldValue = (LONG) Window->hModule;
+ Window->hModule = (HINSTANCE) NewValue;
+ break;
+
+ case GWL_HWNDPARENT:
+ Parent = Window->spwndParent;
+ if (Parent && (Parent->head.h == IntGetDesktopWindow()))
+ OldValue = (LONG) IntSetOwner(Window->head.h, (HWND) NewValue);
+ else
+ OldValue = (LONG) co_UserSetParent(Window->head.h, (HWND) NewValue);
+ break;
+
+ case GWL_ID:
+ OldValue = (LONG) Window->IDMenu;
+ Window->IDMenu = (UINT) NewValue;
+ break;
+
+ case GWL_USERDATA:
+ OldValue = Window->dwUserData;
+ Window->dwUserData = NewValue;
+ break;
+
+ default:
+ ERR("NtUserSetWindowLong(): Unsupported index %lu\n", Index);
+ EngSetLastError(ERROR_INVALID_INDEX);
+ OldValue = 0;
+ break;
+ }
+ }
+
+ return( OldValue);
+}
+
+/*
+ * NtUserSetWindowLong
+ *
+ * The NtUserSetWindowLong function changes an attribute of the specified
+ * window. The function also sets the 32-bit (long) value at the specified
+ * offset into the extra window memory.
+ *
+ * Status
+ * @implemented
+ */
+
+LONG APIENTRY
+NtUserSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi)
+{
+ DECLARE_RETURN(LONG);
+
+ TRACE("Enter NtUserSetWindowLong\n");
+ UserEnterExclusive();
+
+ if (hWnd == IntGetDesktopWindow())
+ {
+ EngSetLastError(STATUS_ACCESS_DENIED);
+ RETURN( 0);
+ }
+
+ RETURN( co_UserSetWindowLong(hWnd, Index, NewValue, Ansi));
+
+CLEANUP:
+ TRACE("Leave NtUserSetWindowLong, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserSetWindowWord
+ *
+ * Legacy function similar to NtUserSetWindowLong.
+ *
+ * Status
+ * @implemented
+ */
+
+WORD APIENTRY
+NtUserSetWindowWord(HWND hWnd, INT Index, WORD NewValue)
+{
+ PWND Window;
+ WORD OldValue;
+ DECLARE_RETURN(WORD);
+
+ TRACE("Enter NtUserSetWindowWord\n");
+ UserEnterExclusive();
+
+ if (hWnd == IntGetDesktopWindow())
+ {
+ EngSetLastError(STATUS_ACCESS_DENIED);
+ RETURN( 0);
+ }
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0);
+ }
+
+ switch (Index)
+ {
+ case GWL_ID:
+ case GWL_HINSTANCE:
+ case GWL_HWNDPARENT:
+ RETURN( (WORD)co_UserSetWindowLong(UserHMGetHandle(Window), Index, (UINT)NewValue, TRUE));
+ default:
+ if (Index < 0)
+ {
+ EngSetLastError(ERROR_INVALID_INDEX);
+ RETURN( 0);
+ }
+ }
+
+ if ((ULONG)Index > (Window->cbwndExtra - sizeof(WORD)))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ RETURN( 0);
+ }
+
+ OldValue = *((WORD *)((PCHAR)(Window + 1) + Index));
+ *((WORD *)((PCHAR)(Window + 1) + Index)) = NewValue;
+
+ RETURN( OldValue);
+
+CLEANUP:
+ TRACE("Leave NtUserSetWindowWord, ret=%u\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ QueryWindow based on KJK::Hyperion and James Tabor.
+
+ 0 = QWUniqueProcessId
+ 1 = QWUniqueThreadId
+ 2 = QWActiveWindow
+ 3 = QWFocusWindow
+ 4 = QWIsHung Implements IsHungAppWindow found
+ by KJK::Hyperion.
+
+ 9 = QWKillWindow When I called this with hWnd ==
+ DesktopWindow, it shutdown the system
+ and rebooted.
+*/
+/*
+ * @implemented
+ */
+DWORD APIENTRY
+NtUserQueryWindow(HWND hWnd, DWORD Index)
+{
+/* Console Leader Process CID Window offsets */
+#define GWLP_CONSOLE_LEADER_PID 0
+#define GWLP_CONSOLE_LEADER_TID 4
+
+ PWND pWnd;
+ DWORD Result;
+ DECLARE_RETURN(UINT);
+
+ TRACE("Enter NtUserQueryWindow\n");
+ UserEnterShared();
+
+ if (!(pWnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0);
+ }
+
+ switch(Index)
+ {
+ case QUERY_WINDOW_UNIQUE_PROCESS_ID:
+ {
+ if ( (pWnd->head.pti->TIF_flags & TIF_CSRSSTHREAD) &&
+ (pWnd->pcls->atomClassName == gaGuiConsoleWndClass) )
+ {
+ // IntGetWindowLong(offset == GWLP_CONSOLE_LEADER_PID)
+ Result = (DWORD)(*((LONG_PTR*)((PCHAR)(pWnd + 1) + GWLP_CONSOLE_LEADER_PID)));
+ }
+ else
+ {
+ Result = (DWORD)IntGetWndProcessId(pWnd);
+ }
+ break;
+ }
+
+ case QUERY_WINDOW_UNIQUE_THREAD_ID:
+ {
+ if ( (pWnd->head.pti->TIF_flags & TIF_CSRSSTHREAD) &&
+ (pWnd->pcls->atomClassName == gaGuiConsoleWndClass) )
+ {
+ // IntGetWindowLong(offset == GWLP_CONSOLE_LEADER_TID)
+ Result = (DWORD)(*((LONG_PTR*)((PCHAR)(pWnd + 1) + GWLP_CONSOLE_LEADER_TID)));
+ }
+ else
+ {
+ Result = (DWORD)IntGetWndThreadId(pWnd);
+ }
+ break;
+ }
+
+ case QUERY_WINDOW_ACTIVE:
+ Result = (DWORD)(pWnd->head.pti->MessageQueue->spwndActive ? UserHMGetHandle(pWnd->head.pti->MessageQueue->spwndActive) : 0);
+ break;
+
+ case QUERY_WINDOW_FOCUS:
+ Result = (DWORD)(pWnd->head.pti->MessageQueue->spwndFocus ? UserHMGetHandle(pWnd->head.pti->MessageQueue->spwndFocus) : 0);
+ break;
+
+ case QUERY_WINDOW_ISHUNG:
+ Result = (DWORD)MsqIsHung(pWnd->head.pti);
+ break;
+
+ case QUERY_WINDOW_REAL_ID:
+ Result = (DWORD)pWnd->head.pti->pEThread->Cid.UniqueProcess;
+ break;
+
+ case QUERY_WINDOW_FOREGROUND:
+ Result = (pWnd->head.pti->MessageQueue == gpqForeground);
+ break;
+
+ default:
+ Result = (DWORD)NULL;
+ break;
+ }
+
+ RETURN( Result);
+
+CLEANUP:
+ TRACE("Leave NtUserQueryWindow, ret=%u\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+UINT APIENTRY
+NtUserRegisterWindowMessage(PUNICODE_STRING MessageNameUnsafe)
+{
+ UNICODE_STRING SafeMessageName;
+ NTSTATUS Status;
+ UINT Ret;
+ DECLARE_RETURN(UINT);
+
+ TRACE("Enter NtUserRegisterWindowMessage\n");
+ UserEnterExclusive();
+
+ if(MessageNameUnsafe == NULL)
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ RETURN( 0);
+ }
+
+ Status = IntSafeCopyUnicodeStringTerminateNULL(&SafeMessageName, MessageNameUnsafe);
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( 0);
+ }
+
+ Ret = (UINT)IntAddAtom(SafeMessageName.Buffer);
+ if (SafeMessageName.Buffer)
+ ExFreePoolWithTag(SafeMessageName.Buffer, TAG_STRING);
+ RETURN( Ret);
+
+CLEANUP:
+ TRACE("Leave NtUserRegisterWindowMessage, ret=%u\n", _ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetMenu(
+ HWND hWnd,
+ HMENU Menu,
+ BOOL Repaint)
+{
+ PWND Window;
+ BOOL Changed;
+ DECLARE_RETURN(BOOL);
+
+ TRACE("Enter NtUserSetMenu\n");
+ UserEnterExclusive();
+
+ if (!(Window = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+
+ if (! IntSetMenu(Window, Menu, &Changed))
+ {
+ RETURN( FALSE);
+ }
+
+ if (Changed && Repaint)
+ {
+ USER_REFERENCE_ENTRY Ref;
+
+ UserRefObjectCo(Window, &Ref);
+ co_WinPosSetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ UserDerefObjectCo(Window);
+ }
+
+ RETURN( TRUE);
+
+CLEANUP:
+ TRACE("Leave NtUserSetMenu, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserSetWindowFNID(HWND hWnd,
+ WORD fnID)
+{
+ PWND Wnd;
+ DECLARE_RETURN(BOOL);
+
+ TRACE("Enter NtUserSetWindowFNID\n");
+ UserEnterExclusive();
+
+ if (!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( FALSE);
+ }
+
+ if (Wnd->head.pti->ppi != PsGetCurrentProcessWin32Process())
+ {
+ EngSetLastError(ERROR_ACCESS_DENIED);
+ RETURN( FALSE);
+ }
+
+ // From user land we only set these.
+ if (fnID != FNID_DESTROY)
+ { // Hacked so we can mark desktop~!
+ if ( (/*(fnID < FNID_BUTTON)*/ (fnID < FNID_FIRST) && (fnID > FNID_GHOST)) ||
+ Wnd->fnid != 0 )
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ RETURN( FALSE);
+ }
+ }
+
+ Wnd->fnid |= fnID;
+ RETURN( TRUE);
+
+CLEANUP:
+ TRACE("Leave NtUserSetWindowFNID\n");
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ * NtUserDefSetText
+ *
+ * Undocumented function that is called from DefWindowProc to set
+ * window text.
+ *
+ * Status
+ * @implemented
+ */
+BOOL APIENTRY
+NtUserDefSetText(HWND hWnd, PLARGE_STRING WindowText)
+{
+ PWND Wnd;
+ LARGE_STRING SafeText;
+ UNICODE_STRING UnicodeString;
+ BOOL Ret = TRUE;
+
+ TRACE("Enter NtUserDefSetText\n");
+
+ if (WindowText != NULL)
+ {
+ _SEH2_TRY
+ {
+ SafeText = ProbeForReadLargeString(WindowText);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Ret = FALSE;
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+
+ if (!Ret)
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+ UserEnterExclusive();
+
+ if(!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ UserLeave();
+ return FALSE;
+ }
+
+ // ReactOS uses Unicode and not mixed. Up/Down converting will take time.
+ // Brought to you by: The Wine Project! Dysfunctional Thought Processes!
+ // Now we know what the bAnsi is for.
+ RtlInitUnicodeString(&UnicodeString, NULL);
+ if (SafeText.Buffer)
+ {
+ _SEH2_TRY
+ {
+ if (SafeText.bAnsi)
+ ProbeForRead(SafeText.Buffer, SafeText.Length, sizeof(CHAR));
+ else
+ ProbeForRead(SafeText.Buffer, SafeText.Length, sizeof(WCHAR));
+ Ret = RtlLargeStringToUnicodeString(&UnicodeString, &SafeText);
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Ret = FALSE;
+ SetLastNtError(_SEH2_GetExceptionCode());
+ }
+ _SEH2_END;
+ if (!Ret) goto Exit;
+ }
+
+ if (UnicodeString.Length != 0)
+ {
+ if (Wnd->strName.MaximumLength > 0 &&
+ UnicodeString.Length <= Wnd->strName.MaximumLength - sizeof(UNICODE_NULL))
+ {
+ ASSERT(Wnd->strName.Buffer != NULL);
+
+ Wnd->strName.Length = UnicodeString.Length;
+ Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
+ RtlCopyMemory(Wnd->strName.Buffer,
+ UnicodeString.Buffer,
+ UnicodeString.Length);
+ }
+ else
+ {
+ PWCHAR buf;
+ Wnd->strName.MaximumLength = Wnd->strName.Length = 0;
+ buf = Wnd->strName.Buffer;
+ Wnd->strName.Buffer = NULL;
+ if (buf != NULL)
+ {
+ DesktopHeapFree(Wnd->head.rpdesk, buf);
+ }
+
+ Wnd->strName.Buffer = DesktopHeapAlloc(Wnd->head.rpdesk,
+ UnicodeString.Length + sizeof(UNICODE_NULL));
+ if (Wnd->strName.Buffer != NULL)
+ {
+ Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0';
+ RtlCopyMemory(Wnd->strName.Buffer,
+ UnicodeString.Buffer,
+ UnicodeString.Length);
+ Wnd->strName.MaximumLength = UnicodeString.Length + sizeof(UNICODE_NULL);
+ Wnd->strName.Length = UnicodeString.Length;
+ }
+ else
+ {
+ EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ Ret = FALSE;
+ goto Exit;
+ }
+ }
+ }
+ else
+ {
+ Wnd->strName.Length = 0;
+ if (Wnd->strName.Buffer != NULL)
+ Wnd->strName.Buffer[0] = L'\0';
+ }
+
+ // FIXME: HAX! Windows does not do this in here!
+ // In User32, these are called after: NotifyWinEvent EVENT_OBJECT_NAMECHANGE than
+ // RepaintButton, StaticRepaint, NtUserCallHwndLock HWNDLOCK_ROUTINE_REDRAWFRAMEANDHOOK, etc.
+ /* Send shell notifications */
+ if (!Wnd->spwndOwner && !IntGetParent(Wnd))
+ {
+ co_IntShellHookNotify(HSHELL_REDRAW, (WPARAM) hWnd, FALSE); // FIXME Flashing?
+ }
+
+ Ret = TRUE;
+Exit:
+ if (UnicodeString.Buffer) RtlFreeUnicodeString(&UnicodeString);
+ TRACE("Leave NtUserDefSetText, ret=%i\n", Ret);
+ UserLeave();
+ return Ret;
+}
+
+/*
+ * NtUserInternalGetWindowText
+ *
+ * Status
+ * @implemented
+ */
+
+INT APIENTRY
+NtUserInternalGetWindowText(HWND hWnd, LPWSTR lpString, INT nMaxCount)
+{
+ PWND Wnd;
+ NTSTATUS Status;
+ INT Result;
+ DECLARE_RETURN(INT);
+
+ TRACE("Enter NtUserInternalGetWindowText\n");
+ UserEnterShared();
+
+ if(lpString && (nMaxCount <= 1))
+ {
+ EngSetLastError(ERROR_INVALID_PARAMETER);
+ RETURN( 0);
+ }
+
+ if(!(Wnd = UserGetWindowObject(hWnd)))
+ {
+ RETURN( 0);
+ }
+
+ Result = Wnd->strName.Length / sizeof(WCHAR);
+ if(lpString)
+ {
+ const WCHAR Terminator = L'\0';
+ INT Copy;
+ WCHAR *Buffer = (WCHAR*)lpString;
+
+ Copy = min(nMaxCount - 1, Result);
+ if(Copy > 0)
+ {
+ Status = MmCopyToCaller(Buffer, Wnd->strName.Buffer, Copy * sizeof(WCHAR));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( 0);
+ }
+ Buffer += Copy;
+ }
+
+ Status = MmCopyToCaller(Buffer, &Terminator, sizeof(WCHAR));
+ if(!NT_SUCCESS(Status))
+ {
+ SetLastNtError(Status);
+ RETURN( 0);
+ }
+
+ Result = Copy;
+ }
+
+ RETURN( Result);
+
+CLEANUP:
+ TRACE("Leave NtUserInternalGetWindowText, ret=%i\n",_ret_);
+ UserLeave();
+ END_CLEANUP;
+}
+
+/*
+ API Call
+*/
+BOOL
+FASTCALL
+IntShowOwnedPopups(PWND OwnerWnd, BOOL fShow )
+{
+ int count = 0;
+ PWND pWnd;
+ HWND *win_array;
+
+// ASSERT(OwnerWnd);
+
+ TRACE("Enter ShowOwnedPopups Show: %s\n", (fShow ? "TRUE" : "FALSE"));
+ win_array = IntWinListChildren(OwnerWnd);
+
+ if (!win_array)
+ return TRUE;
+
+ while (win_array[count])
+ count++;
+ while (--count >= 0)
+ {
+ if (!(pWnd = ValidateHwndNoErr( win_array[count] )))
+ continue;
+ if (pWnd->spwndOwner != OwnerWnd)
+ continue;
+
+ if (fShow)
+ {
+ if (pWnd->state & WNDS_HIDDENPOPUP)
+ {
+ /* In Windows, ShowOwnedPopups(TRUE) generates
+ * WM_SHOWWINDOW messages with SW_PARENTOPENING,
+ * regardless of the state of the owner
+ */
+ co_IntSendMessage(win_array[count], WM_SHOWWINDOW, SW_SHOWNORMAL, SW_PARENTOPENING);
+ continue;
+ }
+ }
+ else
+ {
+ if (pWnd->style & WS_VISIBLE)
+ {
+ /* In Windows, ShowOwnedPopups(FALSE) generates
+ * WM_SHOWWINDOW messages with SW_PARENTCLOSING,
+ * regardless of the state of the owner
+ */
+ co_IntSendMessage(win_array[count], WM_SHOWWINDOW, SW_HIDE, SW_PARENTCLOSING);
+ continue;
+ }
+ }
+
+ }
+ ExFreePoolWithTag(win_array, USERTAG_WINDOWLIST);
+ TRACE("Leave ShowOwnedPopups\n");
+ return TRUE;
+}
+
+/* EOF */
--- /dev/null
- ItemInfo->lpstr = pItem->lpstr.Buffer;
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS user32.dll
+ * FILE: user32/windows/menu.c
+ * PURPOSE: Menus
+ *
+ * PROGRAMMERS: Casper S. Hornstrup
+ * James Tabor
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include <user32.h>
+#include <wine/debug.h>
+
+LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active);
+BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj);
+LRESULT DefWndNCHitTest(HWND hWnd, POINT Point);
+void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect);
+
+WINE_DEFAULT_DEBUG_CHANNEL(menu);
+
+/* internal popup menu window messages */
+
+#define MM_SETMENUHANDLE (WM_USER + 0)
+#define MM_GETMENUHANDLE (WM_USER + 1)
+
+/* internal flags for menu tracking */
+
+#define TF_ENDMENU 0x10000
+#define TF_SUSPENDPOPUP 0x20000
+#define TF_SKIPREMOVE 0x40000
+
+#define ITEM_PREV -1
+#define ITEM_NEXT 1
+
+/* Internal MenuTrackMenu() flags */
+#define TPM_INTERNAL 0xF0000000
+#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */
+#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */
+
++ /* Space between 2 columns */
++#define MENU_COL_SPACE 4
++
+/* top and bottom margins for popup menus */
+#define MENU_TOP_MARGIN 3
+#define MENU_BOTTOM_MARGIN 2
+
+#define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR)
+
+#define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK)
+
+#define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP)
+
+#define MENUITEMINFO_TYPE_MASK \
+ (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \
+ MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \
+ MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ )
+
+#define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU)
+
+#define STATE_MASK (~TYPE_MASK)
+
+#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT))
+
+#define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT)
+
+/* macro to test that flags do not indicate bitmap, ownerdraw or separator */
+#define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags))
+#define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1))
+
+#define IS_SYSTEM_MENU(MenuInfo) \
+ (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN))
+
+#define IS_SYSTEM_POPUP(MenuInfo) \
+ (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN))
+
+#define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags))
+
+/* Use global popup window because there's no way 2 menus can
+ * be tracked at the same time. */
+static HWND top_popup;
+static HMENU top_popup_hmenu;
+
+/* Flag set by EndMenu() to force an exit from menu tracking */
+static BOOL fEndMenu = FALSE;
+
+#define MENU_ITEM_HBMP_SPACE (5)
+#define MENU_BAR_ITEMS_SPACE (12)
+#define SEPARATOR_HEIGHT (5)
+#define MENU_TAB_SPACE (8)
+
+typedef struct
+{
+ UINT TrackFlags;
+ HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/
+ HMENU TopMenu; /* initial menu */
+ HWND OwnerWnd; /* where notifications are sent */
+ POINT Pt;
+} MTRACKER;
+
+
+/*********************************************************************
+ * PopupMenu class descriptor
+ */
+const struct builtin_class_descr POPUPMENU_builtin_class =
+{
+ WC_MENU, /* name */
+ CS_SAVEBITS | CS_DBLCLKS, /* style */
+ (WNDPROC) NULL, /* FIXME - procA */
+ (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */
+ sizeof(MENUINFO *), /* extra */
+ (LPCWSTR) IDC_ARROW, /* cursor */
+ (HBRUSH)(COLOR_MENU + 1) /* brush */
+};
+
+#ifndef GET_WORD
+#define GET_WORD(ptr) (*(WORD *)(ptr))
+#endif
+#ifndef GET_DWORD
+#define GET_DWORD(ptr) (*(DWORD *)(ptr))
+#endif
+
+HFONT hMenuFont = NULL;
+HFONT hMenuFontBold = NULL;
+
+/* Dimension of the menu bitmaps */
+static HBITMAP BmpSysMenu = NULL;
+
+static SIZE MenuCharSize;
+
+
+/***********************************************************************
+ * MENU_GetMenu
+ *
+ * Validate the given menu handle and returns the menu structure pointer.
+ */
+FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu)
+{
+ return ValidateHandleNoErr(hMenu, TYPE_MENU);
+}
+
+/***********************************************************************
+ * get_win_sys_menu
+ *
+ * Get the system menu of a window
+ */
+static HMENU get_win_sys_menu( HWND hwnd )
+{
+ HMENU ret = 0;
+ WND *win = ValidateHwnd( hwnd );
+ if (win)
+ {
+ ret = win->SystemMenu;
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * MENU_FindItem
+ *
+ * Find a menu item. Return a pointer on the item, and modifies *hmenu
+ * in case the item was in a sub-menu.
+ */
+ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags )
+{
+ MENU *menu;
+ ITEM *fallback = NULL;
+ UINT fallback_pos = 0;
+ UINT i;
+ PITEM pItem;
+
+ if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL;
+ if (wFlags & MF_BYPOSITION)
+ {
+ if (*nPos >= menu->cItems) return NULL;
+ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ if (pItem) pItem = &pItem[*nPos];
+ return pItem;
+ }
+ else
+ {
+ PITEM item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ for (i = 0; item, i < menu->cItems; i++, item++)
+ {
+ if (item->spSubMenu)
+ {
+ PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
+ HMENU hsubmenu = UserHMGetHandle(pSubMenu);
+ ITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags );
+ if (subitem)
+ {
+ *hmenu = hsubmenu;
+ return subitem;
+ }
+ else if (item->wID == *nPos)
+ {
+ /* fallback to this item if nothing else found */
+ fallback_pos = i;
+ fallback = item;
+ }
+ }
+ else if (item->wID == *nPos)
+ {
+ *nPos = i;
+ return item;
+ }
+ }
+ }
+
+ if (fallback)
+ *nPos = fallback_pos;
+
+ return fallback;
+}
+
+#define MAX_GOINTOSUBMENU (0x10)
+UINT FASTCALL
+IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc)
+{
+ UINT i = 0;
+ PITEM Item = Menu->rgItems ? DesktopPtrToUser(Menu->rgItems) : NULL;
+
+ /* empty menu */
+ if (!Item) return -1;
+
+ while ( !( Item->fState & MFS_DEFAULT ) )
+ {
+ i++; Item++;
+ if (i >= Menu->cItems ) return -1;
+ }
+
+ /* default: don't return disabled items */
+ if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (Item->fState & MFS_DISABLED )) return -1;
+
+ /* search rekursiv when needed */
+ if ( (Item->fType & MF_POPUP) && (gmdiFlags & GMDI_GOINTOPOPUPS) && Item->spSubMenu)
+ {
+ UINT ret;
+ (*gismc)++;
+ ret = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc );
+ (*gismc)--;
+ if ( -1 != ret ) return ret;
+
+ /* when item not found in submenu, return the popup item */
+ }
+ return ( fByPos ) ? i : Item->wID;
+}
+
+static BOOL GetMenuItemInfo_common ( HMENU hmenu,
+ UINT item,
+ BOOL bypos,
+ LPMENUITEMINFOW lpmii,
+ BOOL unicode)
+{
+ ITEM *pItem = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0);
+
+ //debug_print_menuitem("GetMenuItemInfo_common: ", pItem, "");
+
+ if (!pItem)
+ {
+ SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
+ return FALSE;
+ }
+
+ if( lpmii->fMask & MIIM_TYPE)
+ {
+ if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP))
+ {
+ ERR("invalid combination of fMask bits used\n");
+ /* this does not happen on Win9x/ME */
+ SetLastError( ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
+ if( pItem->hbmp) lpmii->fType |= MFT_BITMAP;
+ lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */
+ if( lpmii->fType & MFT_BITMAP)
+ {
+ lpmii->dwTypeData = (LPWSTR) pItem->hbmp;
+ lpmii->cch = 0;
+ }
+ else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR))
+ {
+ /* this does not happen on Win9x/ME */
+ lpmii->dwTypeData = 0;
+ lpmii->cch = 0;
+ }
+ }
+
+ /* copy the text string */
+ if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING)))
+ {
+ if( !pItem->Xlpstr )
+ { // Very strange this fixes a wine test with a crash.
+ if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) )
+ {
+ lpmii->cch = 0;
+ if( unicode)
+ *((WCHAR *)lpmii->dwTypeData) = 0;
+ else
+ *((CHAR *)lpmii->dwTypeData) = 0;
+ }
+ }
+ else
+ {
+ int len;
+ LPWSTR text = DesktopPtrToUser(pItem->Xlpstr);
+ if (unicode)
+ {
+ len = strlenW(text);
+ if(lpmii->dwTypeData && lpmii->cch)
+ lstrcpynW(lpmii->dwTypeData, text, lpmii->cch);
+ }
+ else
+ {
+ len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1;
+ if(lpmii->dwTypeData && lpmii->cch)
+ if (!WideCharToMultiByte( CP_ACP, 0, text, -1,
+ (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL ))
+ ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0;
+ }
+ /* if we've copied a substring we return its length */
+ if(lpmii->dwTypeData && lpmii->cch)
+ if (lpmii->cch <= len + 1)
+ lpmii->cch--;
+ else
+ lpmii->cch = len;
+ else
+ {
+ /* return length of string */
+ /* not on Win9x/ME if fType & MFT_BITMAP */
+ lpmii->cch = len;
+ }
+ }
+ }
+
+ if (lpmii->fMask & MIIM_FTYPE)
+ lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK;
+
+ if (lpmii->fMask & MIIM_BITMAP)
+ lpmii->hbmpItem = pItem->hbmp;
+
+ if (lpmii->fMask & MIIM_STATE)
+ lpmii->fState = pItem->fState & MENUITEMINFO_STATE_MASK;
+
+ if (lpmii->fMask & MIIM_ID)
+ lpmii->wID = pItem->wID;
+
+ if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu )
+ {
+ PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
+ HMENU hSubMenu = UserHMGetHandle(pSubMenu);
+ lpmii->hSubMenu = hSubMenu;
+ }
+ else
+ {
+ /* hSubMenu is always cleared
+ * (not on Win9x/ME ) */
+ lpmii->hSubMenu = 0;
+ }
+
+ if (lpmii->fMask & MIIM_CHECKMARKS)
+ {
+ lpmii->hbmpChecked = pItem->hbmpChecked;
+ lpmii->hbmpUnchecked = pItem->hbmpUnchecked;
+ }
+ if (lpmii->fMask & MIIM_DATA)
+ lpmii->dwItemData = pItem->dwItemData;
+
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuGetRosMenuInfo
+ *
+ * Get full information about menu
+ */
+static BOOL FASTCALL
+MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu)
+{
+ PMENU pMenu;
+ if (!(pMenu = ValidateHandleNoErr(Menu, TYPE_MENU))) return FALSE;
+
+ MenuInfo->hbrBack = pMenu->hbrBack;
+ MenuInfo->dwContextHelpID = pMenu->dwContextHelpId;
+ MenuInfo->cyMax = pMenu->cyMax;
+ MenuInfo->dwMenuData = pMenu->dwMenuData;
+ MenuInfo->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
+
+ MenuInfo->cItems = pMenu->cItems;
+
+ MenuInfo->iItem = pMenu->iItem;
+ MenuInfo->cxMenu = pMenu->cxMenu;
+ MenuInfo->cyMenu = pMenu->cyMenu;
+ MenuInfo->spwndNotify = pMenu->spwndNotify;
+ MenuInfo->cxTextAlign = pMenu->cxTextAlign;
+ MenuInfo->iTop = pMenu->iMaxTop;
+ MenuInfo->iMaxTop = pMenu->iMaxTop;
+ MenuInfo->dwArrowsOn = pMenu->dwArrowsOn;
+
+ MenuInfo->fFlags = pMenu->fFlags;
+ MenuInfo->Self = pMenu->head.h;
+ MenuInfo->TimeToHide = pMenu->TimeToHide;
+ MenuInfo->Wnd = pMenu->hWnd;
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuSetRosMenuInfo
+ *
+ * Set full information about menu
+ */
+static BOOL FASTCALL
+MenuSetRosMenuInfo(PROSMENUINFO MenuInfo)
+{
+ MenuInfo->cbSize = sizeof(ROSMENUINFO);
+ MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE;
+
+ return NtUserThunkedMenuInfo(MenuInfo->Self, (LPCMENUINFO)MenuInfo);
+}
+
+/***********************************************************************
+ * MenuInitRosMenuItemInfo
+ *
+ * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo
+ */
+static VOID FASTCALL
+MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
+{
+ ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO));
+ ItemInfo->cbSize = sizeof(ROSMENUITEMINFO);
+}
+
+/***********************************************************************
+ * MenuGetRosMenuItemInfo
+ *
+ * Get full information about a menu item
+ */
+static BOOL FASTCALL
+MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
+{
+ PITEM pItem;
+ UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */
+
+ if (ItemInfo->dwTypeData != NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
+ }
+
+ ItemInfo->dwTypeData = NULL;
+
+ if (!(pItem = MENU_FindItem(&Menu, &Index, MF_BYPOSITION)))
+ {
+ ItemInfo->fType = 0;
+ return FALSE;
+ }
+
+ ItemInfo->fType = pItem->fType;
+ ItemInfo->hbmpItem = pItem->hbmp;
+ ItemInfo->hbmpChecked = pItem->hbmpChecked;
+ ItemInfo->hbmpUnchecked = pItem->hbmpUnchecked;
+ ItemInfo->dwItemData = pItem->dwItemData;
+ ItemInfo->wID = pItem->wID;
+ ItemInfo->fState = pItem->fState;
+
+ if (pItem->spSubMenu)
+ {
+ PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
+ HMENU hSubMenu = UserHMGetHandle(pSubMenu);
+ ItemInfo->hSubMenu = hSubMenu;
+ }
+ else
+ ItemInfo->hSubMenu = NULL;
+
+ if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING)
+ {
+ LPWSTR lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL;
+ if (lpstr)
+ {
+ ItemInfo->cch = pItem->lpstr.Length / sizeof(WCHAR);
+ ItemInfo->cch++;
+ ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0, ItemInfo->cch * sizeof(WCHAR));
+ if (ItemInfo->dwTypeData == NULL)
+ {
+ return FALSE;
+ }
+ RtlCopyMemory(ItemInfo->dwTypeData, lpstr, min(ItemInfo->cch * sizeof(WCHAR), pItem->lpstr.MaximumLength));
+ }
+ else
+ {
+ ItemInfo->cch = 0;
+ }
+ }
+
+ ItemInfo->Rect.left = pItem->xItem;
+ ItemInfo->Rect.top = pItem->yItem;
+ ItemInfo->Rect.right = pItem->cxItem; // Do this for now......
+ ItemInfo->Rect.bottom = pItem->cyItem;
+ ItemInfo->dxTab = pItem->dxTab;
- if (hmenu)
- {
- MENU *menu = MENU_GetMenu( hmenu );
- ITEM *item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
++ ItemInfo->lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL;
+ ItemInfo->maxBmpSize.cx = pItem->cxBmp;
+ ItemInfo->maxBmpSize.cy = pItem->cyBmp;
+
+ ItemInfo->fMask = Save_Mask;
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuSetRosMenuItemInfo
+ *
+ * Set selected information about a menu item, need to set the mask bits.
+ */
+static BOOL FASTCALL
+MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo)
+{
+ BOOL Ret;
+
+ if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING &&
+ ItemInfo->dwTypeData != NULL)
+ {
+ ItemInfo->cch = strlenW(ItemInfo->dwTypeData);
+ }
+ if (ItemInfo->hSubMenu)
+ {
+ if (!IsMenu(ItemInfo->hSubMenu)) ItemInfo->hSubMenu = NULL;
+ }
+ Ret = NtUserThunkedMenuItemInfo(Menu, Index, TRUE, FALSE, (LPMENUITEMINFOW)ItemInfo, NULL);
+ return Ret;
+}
+
+/***********************************************************************
+ * MenuCleanupRosMenuItemInfo
+ *
+ * Cleanup after use of MenuGet/SetRosMenuItemInfo
+ */
+static VOID FASTCALL
+MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo)
+{
+ if (ItemInfo->dwTypeData != NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData);
+ ItemInfo->dwTypeData = NULL;
+ }
+}
+
+/***********************************************************************
+ * MenuInitSysMenuPopup
+ *
+ * Grey the appropriate items in System menu.
+ */
+void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest )
+{
+ BOOL gray;
+ UINT DefItem;
+ #if 0
+ MENUITEMINFOW mii;
+ #endif
+
+ gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE));
+ EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) );
+ gray = ((style & WS_MAXIMIZE) != 0);
+ EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) );
+ gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE);
+ EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
+ gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE);
+ EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) );
+ gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE));
+ EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) );
+ gray = (clsStyle & CS_NOCLOSE) != 0;
+
+ /* The menu item must keep its state if it's disabled */
+ if(gray)
+ EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED);
+
+ /* Set default menu item */
+ if(style & WS_MINIMIZE) DefItem = SC_RESTORE;
+ else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE);
+ else DefItem = SC_CLOSE;
+#if 0
+ mii.cbSize = sizeof(MENUITEMINFOW);
+ mii.fMask |= MIIM_STATE;
+ if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) &&
+ (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE;
+#endif
+ SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND);
+}
+
+/******************************************************************************
+ *
+ * UINT MenuGetStartOfNextColumn(
+ * PROSMENUINFO MenuInfo)
+ *
+ *****************************************************************************/
+
+static UINT MENU_GetStartOfNextColumn(
+ HMENU hMenu )
+{
+ MENU *menu = MENU_GetMenu(hMenu);
+ PITEM pItem;
+ UINT i;
+
+ if(!menu)
+ return NO_SELECTED_ITEM;
+
+ i = menu->iItem + 1;
+ if( i == NO_SELECTED_ITEM )
+ return i;
+
+ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ if (!pItem) return NO_SELECTED_ITEM;
+ for( ; i < menu->cItems; ++i ) {
+ if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
+ return i;
+ }
+
+ return NO_SELECTED_ITEM;
+}
+
+/******************************************************************************
+ *
+ * UINT MenuGetStartOfPrevColumn(
+ * PROSMENUINFO MenuInfo)
+ *
+ *****************************************************************************/
+static UINT MENU_GetStartOfPrevColumn(
+ HMENU hMenu )
+{
+ MENU *menu = MENU_GetMenu(hMenu);
+ UINT i;
+ PITEM pItem;
+
+ if( !menu )
+ return NO_SELECTED_ITEM;
+
+ if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM )
+ return NO_SELECTED_ITEM;
+
+ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ if (!pItem) return NO_SELECTED_ITEM;
+
+ /* Find the start of the column */
+
+ for(i = menu->iItem; i != 0 &&
+ !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK));
+ --i); /* empty */
+
+ if(i == 0)
+ return NO_SELECTED_ITEM;
+
+ for(--i; i != 0; --i) {
+ if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))
+ break;
+ }
+
+ TRACE("ret %d.\n", i );
+
+ return i;
+}
+
+/***********************************************************************
+ * MenuLoadBitmaps
+ *
+ * Load the arrow bitmap. We can't do this from MenuInit since user32
+ * can also be used (and thus initialized) from text-mode.
+ */
+static void FASTCALL
+MenuLoadBitmaps(VOID)
+{
+ /* Load system buttons bitmaps */
+ if (NULL == BmpSysMenu)
+ {
+ BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
+ }
+}
+/////////// Make gpsi OBMI via callback //////////////
+/***********************************************************************
+ * get_arrow_bitmap
+ */
+HBITMAP get_arrow_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_down_arrow_bitmap DFCS_MENUARROWDOWN
+ */
+HBITMAP get_down_arrow_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_down_arrow_inactive_bitmap DFCS_MENUARROWDOWN | DFCS_INACTIVE
+ */
+HBITMAP get_down_arrow_inactive_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_up_arrow_bitmap DFCS_MENUARROWUP
+ */
+HBITMAP get_up_arrow_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW));
+ return arrow_bitmap;
+}
+
+/***********************************************************************
+ * get_up_arrow_inactive_bitmap DFCS_MENUARROWUP | DFCS_INACTIVE
+ */
+static HBITMAP get_up_arrow_inactive_bitmap(void)
+{
+ static HBITMAP arrow_bitmap;
+
+ if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI));
+ return arrow_bitmap;
+}
+////////////////
+/***********************************************************************
+ * MenuFindSubMenu
+ *
+ * Find a Sub menu. Return the position of the submenu, and modifies
+ * *hmenu in case it is found in another sub-menu.
+ * If the submenu cannot be found, NO_SELECTED_ITEM is returned.
+ */
+static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget )
+{
+ PMENU menu, pSubMenu;
+ HMENU hSubMenu;
+ UINT i;
+ PITEM item;
+
+ if (((*hmenu)==(HMENU)0xffff) ||(!(menu = MENU_GetMenu(*hmenu))))
+ return NO_SELECTED_ITEM;
+
+ item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ for (i = 0; i < menu->cItems; i++, item++)
+ {
+ if (!item->spSubMenu)
+ continue;
+ else
+ {
+ pSubMenu = DesktopPtrToUser(item->spSubMenu);
+ hSubMenu = UserHMGetHandle(pSubMenu);
+ if (hSubMenu == hSubTarget)
+ {
+ return i;
+ }
+ else
+ {
+ HMENU hsubmenu = hSubMenu;
+ UINT pos = MenuFindSubMenu( &hsubmenu, hSubTarget );
+ if (pos != NO_SELECTED_ITEM)
+ {
+ *hmenu = hsubmenu;
+ return pos;
+ }
+ }
+ }
+ }
+ return NO_SELECTED_ITEM;
+}
+
+/***********************************************************************
+ * MENU_AdjustMenuItemRect
+ *
+ * Adjust menu item rectangle according to scrolling state.
+ */
+static void
+MENU_AdjustMenuItemRect(PROSMENUINFO menu, LPRECT rect)
+{
+ if (menu->dwArrowsOn)
+ {
+ UINT arrow_bitmap_height;
+ BITMAP bmp;
+
+ GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp);
+ arrow_bitmap_height = bmp.bmHeight;
+ rect->top += arrow_bitmap_height - menu->iTop;
+ rect->bottom += arrow_bitmap_height - menu->iTop;
+ }
+}
+
+/***********************************************************************
+ * MenuDrawPopupGlyph
+ *
+ * Draws popup magic glyphs (can be found in system menu).
+ */
+static void FASTCALL
+MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite)
+{
+ LOGFONTW lf;
+ HFONT hFont, hOldFont;
+ COLORREF clrsave;
+ INT bkmode;
+ TCHAR symbol;
+ switch (popupMagic)
+ {
+ case (INT_PTR) HBMMENU_POPUP_RESTORE:
+ symbol = '2';
+ break;
+ case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
+ symbol = '0';
+ break;
+ case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
+ symbol = '1';
+ break;
+ case (INT_PTR) HBMMENU_POPUP_CLOSE:
+ symbol = 'r';
+ break;
+ default:
+ ERR("Invalid popup magic bitmap %d\n", (int)popupMagic);
+ return;
+ }
+ ZeroMemory(&lf, sizeof(LOGFONTW));
+ InflateRect(r, -2, -2);
+ lf.lfHeight = r->bottom - r->top;
+ lf.lfWidth = 0;
+ lf.lfWeight = FW_NORMAL;
+ lf.lfCharSet = DEFAULT_CHARSET;
+ lstrcpy(lf.lfFaceName, TEXT("Marlett"));
+ hFont = CreateFontIndirect(&lf);
+ /* save font and text color */
+ hOldFont = SelectObject(dc, hFont);
+ clrsave = GetTextColor(dc);
+ bkmode = GetBkMode(dc);
+ /* set color and drawing mode */
+ SetBkMode(dc, TRANSPARENT);
+ if (inactive)
+ {
+ /* draw shadow */
+ if (!hilite)
+ {
+ SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ TextOut(dc, r->left + 1, r->top + 1, &symbol, 1);
+ }
+ }
+ SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)));
+ /* draw selected symbol */
+ TextOut(dc, r->left, r->top, &symbol, 1);
+ /* restore previous settings */
+ SetTextColor(dc, clrsave);
+ SelectObject(dc, hOldFont);
+ SetBkMode(dc, bkmode);
+ DeleteObject(hFont);
+}
+
+/***********************************************************************
+ * MenuFindItemByKey
+ *
+ * Find the menu item selected by a key press.
+ * Return item id, -1 if none, -2 if we should close the menu.
+ */
+static UINT FASTCALL MENU_FindItemByKey(HWND WndOwner, HMENU hmenu,
+ WCHAR Key, BOOL ForceMenuChar)
+{
+ LRESULT MenuChar;
+ WORD Flags = 0;
+
+ TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, hmenu );
+
+ if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(WndOwner), 0);
- if( !ForceMenuChar )
- {
- UINT i;
- BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
++ if (hmenu)
++ {
++ MENU *menu = MENU_GetMenu( hmenu );
++ ITEM *item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+
- for (i = 0; i < menu->cItems; i++, item++)
- {
- LPWSTR text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
- if( text)
- {
- const WCHAR *p = text - 2;
- do
- {
- const WCHAR *q = p + 2;
- p = strchrW (q, '&');
- if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
- }
- while (p != NULL && p [1] == '&');
- if (p && (toupperW(p[1]) == toupperW(Key))) return i;
- }
- }
- }
++ if ( !ForceMenuChar )
++ {
++ UINT i;
++ BOOL cjk = GetSystemMetrics( SM_DBCSENABLED );
+
- Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0;
- Flags |= menu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0;
++ for (i = 0; i < menu->cItems; i++, item++)
++ {
++ LPWSTR text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
++ if( text)
++ {
++ const WCHAR *p = text - 2;
++ do
++ {
++ const WCHAR *q = p + 2;
++ p = strchrW (q, '&');
++ if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */
++ }
++ while (p != NULL && p [1] == '&');
++ if (p && (toupperW(p[1]) == toupperW(Key))) return i;
++ }
++ }
++ }
+
- MenuChar = SendMessageW(WndOwner, WM_MENUCHAR,
++ Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0;
++ Flags |= menu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0;
+
- if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
- if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
- }
++ MenuChar = SendMessageW( WndOwner, WM_MENUCHAR,
+ MAKEWPARAM(Key, Flags), (LPARAM) hmenu);
- txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
++ if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar);
++ if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2);
++ }
+ return (UINT)(-1);
+}
+
+/***********************************************************************
+ * MenuGetBitmapItemSize
+ *
+ * Get the size of a bitmap item.
+ */
+static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner)
+{
+ BITMAP bm;
+ HBITMAP bmp = lpitem->hbmpItem;
+
+ size->cx = size->cy = 0;
+
+ /* check if there is a magic menu item associated with this item */
+ if (IS_MAGIC_BITMAP(bmp))
+ {
+ switch((INT_PTR) bmp)
+ {
+ case (INT_PTR)HBMMENU_CALLBACK:
+ {
+ MEASUREITEMSTRUCT measItem;
+ measItem.CtlType = ODT_MENU;
+ measItem.CtlID = 0;
+ measItem.itemID = lpitem->wID;
+ measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left;
+ measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top;
+ measItem.itemData = lpitem->dwItemData;
+ SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem);
+ size->cx = measItem.itemWidth;
+ size->cy = measItem.itemHeight;
+ return;
+ }
+ break;
+
+ case (INT_PTR) HBMMENU_SYSTEM:
+ if (0 != lpitem->dwItemData)
+ {
+ bmp = (HBITMAP) lpitem->dwItemData;
+ break;
+ }
+ /* fall through */
+ case (INT_PTR) HBMMENU_MBAR_RESTORE:
+ case (INT_PTR) HBMMENU_MBAR_MINIMIZE:
+ case (INT_PTR) HBMMENU_MBAR_CLOSE:
+ case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D:
+ case (INT_PTR) HBMMENU_MBAR_CLOSE_D:
+ case (INT_PTR) HBMMENU_POPUP_CLOSE:
+ case (INT_PTR) HBMMENU_POPUP_RESTORE:
+ case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
+ case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
+ /* FIXME: Why we need to subtract these magic values? */
+ /* to make them smaller than the menu bar? */
+ size->cx = GetSystemMetrics(SM_CXSIZE) - 2;
+ size->cy = GetSystemMetrics(SM_CYSIZE) - 4;
+ return;
+ }
+ }
+
+ if (GetObjectW(bmp, sizeof(BITMAP), &bm))
+ {
+ size->cx = bm.bmWidth;
+ size->cy = bm.bmHeight;
+ }
+}
+
+/***********************************************************************
+ * MenuDrawBitmapItem
+ *
+ * Draw a bitmap item.
+ */
+static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect,
+ PROSMENUINFO MenuInfo, HWND WndOwner, UINT odaction, BOOL MenuBar)
+{
+ BITMAP bm;
+ DWORD rop;
+ HDC hdcMem;
+ HBITMAP bmp;
+ int w = rect->right - rect->left;
+ int h = rect->bottom - rect->top;
+ int bmp_xoffset = 0;
+ int left, top;
+ HBITMAP hbmToDraw = lpitem->hbmpItem;
+ bmp = hbmToDraw;
+
+ /* Check if there is a magic menu item associated with this item */
+ if (IS_MAGIC_BITMAP(hbmToDraw))
+ {
+ UINT flags = 0;
+ RECT r;
+
+ r = *rect;
+ switch ((INT_PTR)hbmToDraw)
+ {
+ case (INT_PTR)HBMMENU_SYSTEM:
+ if (lpitem->dwTypeData)
+ {
+ bmp = (HBITMAP)lpitem->dwTypeData;
+ if (!GetObjectW( bmp, sizeof(bm), &bm )) return;
+ }
+ else
+ {
+ if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE));
+ bmp = BmpSysMenu;
+ if (! GetObjectW(bmp, sizeof(bm), &bm)) return;
+ /* only use right half of the bitmap */
+ bmp_xoffset = bm.bmWidth / 2;
+ bm.bmWidth -= bmp_xoffset;
+ }
+ goto got_bitmap;
+ case (INT_PTR)HBMMENU_MBAR_RESTORE:
+ flags = DFCS_CAPTIONRESTORE;
+ break;
+ case (INT_PTR)HBMMENU_MBAR_MINIMIZE:
+ r.right += 1;
+ flags = DFCS_CAPTIONMIN;
+ break;
+ case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D:
+ r.right += 1;
+ flags = DFCS_CAPTIONMIN | DFCS_INACTIVE;
+ break;
+ case (INT_PTR)HBMMENU_MBAR_CLOSE:
+ flags = DFCS_CAPTIONCLOSE;
+ break;
+ case (INT_PTR)HBMMENU_MBAR_CLOSE_D:
+ flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE;
+ break;
+ case (INT_PTR)HBMMENU_CALLBACK:
+ {
+ DRAWITEMSTRUCT drawItem;
+ POINT origorg;
+ drawItem.CtlType = ODT_MENU;
+ drawItem.CtlID = 0;
+ drawItem.itemID = lpitem->wID;
+ drawItem.itemAction = odaction;
+ drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0;
+ drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0;
+ drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0;
+ drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0;
+ drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0;
+ //drawItem.itemState |= (!(MenuInfo->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0;
+ //drawItem.itemState |= (MenuInfo->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0;
+ drawItem.hwndItem = (HWND)MenuInfo->Self;
+ drawItem.hDC = hdc;
+ drawItem.rcItem = *rect;
+ drawItem.itemData = lpitem->dwItemData;
+ /* some applications make this assumption on the DC's origin */
+ SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg);
+ OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top);
+ SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem);
+ SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
+ return;
+ }
+ break;
+
+ case (INT_PTR) HBMMENU_POPUP_CLOSE:
+ case (INT_PTR) HBMMENU_POPUP_RESTORE:
+ case (INT_PTR) HBMMENU_POPUP_MAXIMIZE:
+ case (INT_PTR) HBMMENU_POPUP_MINIMIZE:
+ MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE);
+ return;
+ }
+ InflateRect(&r, -1, -1);
+ if (0 != (lpitem->fState & MF_HILITE))
+ {
+ flags |= DFCS_PUSHED;
+ }
+ DrawFrameControl(hdc, &r, DFC_CAPTION, flags);
+ return;
+ }
+
+ if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return;
+
+ got_bitmap:
+ hdcMem = CreateCompatibleDC( hdc );
+ SelectObject( hdcMem, bmp );
+
+ /* handle fontsize > bitmap_height */
+ top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top;
+ left=rect->left;
+ rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY;
+ if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem)
+ SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
+ BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop );
+ DeleteDC( hdcMem );
+}
+
+/***********************************************************************
+ * MenuCalcItemSize
+ *
+ * Calculate the size of the menu item and store it in lpitem->rect.
+ */
+static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner,
+ INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp)
+{
+ WCHAR *p;
+ UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
+ UINT arrow_bitmap_width;
+ BITMAP bm;
+ INT itemheight = 0;
+
+ TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY);
+
+ GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm );
+ arrow_bitmap_width = bm.bmWidth;
+
+ MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy );
+
+ SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY );
+
+ if (lpitem->fType & MF_OWNERDRAW)
+ {
+ MEASUREITEMSTRUCT mis;
+ mis.CtlType = ODT_MENU;
+ mis.CtlID = 0;
+ mis.itemID = lpitem->wID;
+ mis.itemData = lpitem->dwItemData;
+ mis.itemHeight = HIWORD( GetDialogBaseUnits());
+ mis.itemWidth = 0;
+ SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis );
+ /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average
+ * width of a menufont character to the width of an owner-drawn menu.
+ */
+ lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx;
+ if (menuBar) {
+ /* under at least win95 you seem to be given a standard
+ height for the menu and the height value is ignored */
+ lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE);
+ } else
+ lpitem->Rect.bottom += mis.itemHeight;
+ // Or this,
+ //Item->cxBmp = mis.itemWidth;
+ //Item->cyBmp = mis.itemHeight;
+ TRACE("id=%04lx size=%dx%d\n",
+ lpitem->wID, lpitem->Rect.right-lpitem->Rect.left,
+ lpitem->Rect.bottom-lpitem->Rect.top);
+ return;
+ }
+
+ if (lpitem->fType & MF_SEPARATOR)
+ {
+ lpitem->Rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT;
+ if( !menuBar)
+ lpitem->Rect.right += arrow_bitmap_width/*check_bitmap_width*/ + MenuCharSize.cx;
+ return;
+ }
+
+ lpitem->dxTab = 0;
+
+ if (lpitem->hbmpItem)
+ {
+ SIZE size;
+
+ if (!menuBar) {
+ MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
+ /* Keep the size of the bitmap in callback mode to be able
+ * to draw it correctly */
+ lpitem->maxBmpSize = size;
+ MenuInfo->cxTextAlign = max(MenuInfo->cxTextAlign, size.cx);
+ MenuSetRosMenuInfo(MenuInfo);
+ lpitem->Rect.right += size.cx + 2;
+ itemheight = size.cy + 2;
+
+ if( !(MenuInfo->dwStyle & MNS_NOCHECK))
+ lpitem->Rect.right += 2 * check_bitmap_width;
+ lpitem->Rect.right += 4 + MenuCharSize.cx;
+ lpitem->dxTab = lpitem->Rect.right;
+ lpitem->Rect.right += arrow_bitmap_width;//check_bitmap_width;
+ } else /* hbmpItem & MenuBar */ {
+ MenuGetBitmapItemSize(lpitem, &size, hwndOwner );
+ lpitem->Rect.right += size.cx;
+ if( lpitem->lpstr) lpitem->Rect.right += 2;
+ itemheight = size.cy;
+
+ /* Special case: Minimize button doesn't have a space behind it. */
+ if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE ||
+ lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D)
+ lpitem->Rect.right -= 1;
+ }
+ }
+ else if (!menuBar) {
+ if( !(MenuInfo->dwStyle & MNS_NOCHECK))
+ lpitem->Rect.right += check_bitmap_width;
+ lpitem->Rect.right += 4 + MenuCharSize.cx;
+ lpitem->dxTab = lpitem->Rect.right;
+ lpitem->Rect.right += check_bitmap_width;
+ }
+
+ /* it must be a text item - unless it's the system menu */
+ if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) {
+ HFONT hfontOld = NULL;
+ RECT rc = lpitem->Rect;
+ LONG txtheight, txtwidth;
+
+ if ( lpitem->fState & MFS_DEFAULT ) {
+ hfontOld = SelectObject( hdc, hMenuFontBold );
+ }
+ if (menuBar) {
- if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) {
++ txtheight = DrawTextW( hdc, lpitem->lpstr, -1, &rc,
+ DT_SINGLELINE|DT_CALCRECT);
+ lpitem->Rect.right += rc.right - rc.left;
+ itemheight = max( max( itemheight, txtheight),
+ GetSystemMetrics( SM_CYMENU) - 1);
+ lpitem->Rect.right += 2 * MenuCharSize.cx;
+ } else {
- int n = (int)( p - lpitem->dwTypeData);
++ if ((p = strchrW( lpitem->lpstr, '\t' )) != NULL) {
+ RECT tmprc = rc;
+ LONG tmpheight;
- txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc,
++ int n = (int)( p - lpitem->lpstr);
+ /* Item contains a tab (only meaningful in popup menus) */
+ /* get text size before the tab */
- txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc,
++ txtheight = DrawTextW( hdc, lpitem->lpstr, n, &rc,
+ DT_SINGLELINE|DT_CALCRECT);
+ txtwidth = rc.right - rc.left;
+ p += 1; /* advance past the Tab */
+ /* get text size after the tab */
+ tmpheight = DrawTextW( hdc, p, -1, &tmprc,
+ DT_SINGLELINE|DT_CALCRECT);
+ lpitem->dxTab += txtwidth;
+ txtheight = max( txtheight, tmpheight);
+ txtwidth += MenuCharSize.cx + /* space for the tab */
+ tmprc.right - tmprc.left; /* space for the short cut */
+ } else {
- orgY = 2;
++ txtheight = DrawTextW( hdc, lpitem->lpstr, -1, &rc,
+ DT_SINGLELINE|DT_CALCRECT);
+ txtwidth = rc.right - rc.left;
+ lpitem->dxTab += txtwidth;
+ }
+ lpitem->Rect.right += 2 + txtwidth;
+ itemheight = max( itemheight,
+ max( txtheight + 2, MenuCharSize.cy + 4));
+ }
+ if (hfontOld) SelectObject (hdc, hfontOld);
+ } else if( menuBar) {
+ itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1);
+ }
+ lpitem->Rect.bottom += itemheight;
+ TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom);
+}
+
+/***********************************************************************
+ * MENU_GetMaxPopupHeight
+ */
+static UINT
+MENU_GetMaxPopupHeight(PROSMENUINFO lppop)
+{
+ if (lppop->cyMax)
+ return lppop->cyMax;
+ return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER);
+}
+
+/***********************************************************************
+ * MenuPopupMenuCalcSize
+ *
+ * Calculate the size of a popup menu.
+ */
+static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner)
+{
+ ROSMENUITEMINFO lpitem;
+ HDC hdc;
+ int start, i;
+ int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight;
+ BOOL textandbmp = FALSE;
+
+ MenuInfo->cxMenu = MenuInfo->cyMenu = 0;
+ if (MenuInfo->cItems == 0)
+ {
+ MenuSetRosMenuInfo(MenuInfo);
+ return;
+ }
+
+ hdc = GetDC(NULL);
+ SelectObject( hdc, hMenuFont );
+
+ start = 0;
+ maxX = 2 + 1;
+
+ MenuInfo->cxTextAlign = 0;
+
+ MenuInitRosMenuItemInfo(&lpitem);
++ //MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
+ while (start < MenuInfo->cItems)
+ {
++ //lpitem = &lppop->items[start];
+ orgX = maxX;
-
++ //if( lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))
++ // orgX += MENU_COL_SPACE;
++ orgY = 2;//MENU_TOP_MARGIN;
+
+ maxTab = maxTabWidth = 0;
- for (i = start; i < MenuInfo->cItems; i++)
+ /* Parse items until column break or end of menu */
- if( lpitem.lpstr && lpitem.hbmpItem) textandbmp = TRUE;
++ for (i = start; i < MenuInfo->cItems; i++)//, lpitem++)
+ {
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
+ {
+ MenuCleanupRosMenuItemInfo(&lpitem);
+ MenuSetRosMenuInfo(MenuInfo);
+ return;
+ }
+ if (i != start &&
+ (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
+
- // Not sure here,, The patch from wine removes this.
- // if ((lpitem.fType & MF_MENUBARBREAK) != 0)
- // {
- // OrgX++;
- // }
+
+ MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE, textandbmp);
+ if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem))
+ {
+ MenuCleanupRosMenuItemInfo(&lpitem);
+ MenuSetRosMenuInfo(MenuInfo);
+ return;
+ }
- if ((lpitem.lpstr) && lpitem.dxTab )
++
+ maxX = max(maxX, lpitem.Rect.right);
+ orgY = lpitem.Rect.bottom;
- }
++ if (IS_STRING_ITEM(lpitem.fType) && lpitem.dxTab )
+ {
+ maxTab = max( maxTab, lpitem.dxTab );
+ maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab);
+ }
- maxX = max( maxX, maxTab + maxTabWidth );
- while (start < i)
- {
- if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
- {
- lpitem.Rect.right = maxX;
- if ((lpitem.lpstr) && 0 != lpitem.dxTab)
- {
- lpitem.dxTab = maxTab;
- }
- MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
- }
- start++;
- }
- MenuInfo->cyMenu = max(MenuInfo->cyMenu, orgY);
++ if( lpitem.lpstr && lpitem.hbmpItem) textandbmp = TRUE;
++ }
+
+ /* Finish the column (set all items to the largest width found) */
- HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
++ maxX = max( maxX, maxTab + maxTabWidth );
++ while (start < i)
++ {
++ if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem))
++ {
++ lpitem.Rect.right = maxX;
++ if (IS_STRING_ITEM(lpitem.fType) && lpitem.dxTab)
++ {
++ lpitem.dxTab = maxTab;
++ }
++ MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem);
++ }
++ start++;
++ }
++ MenuInfo->cyMenu = max(MenuInfo->cyMenu, orgY);
+ }
+
+ MenuInfo->cxMenu = maxX;
+ /* if none of the items have both text and bitmap then
+ * the text and bitmaps are all aligned on the left. If there is at
+ * least one item with both text and bitmap then bitmaps are
+ * on the left and texts left aligned with the right hand side
+ * of the bitmaps */
+ if( !textandbmp) MenuInfo->cxTextAlign = 0;
+
+ /* space for 3d border */
+ MenuInfo->cyMenu += MENU_BOTTOM_MARGIN;
+ MenuInfo->cxMenu += 2;
+
+ /* Adjust popup height if it exceeds maximum */
+ maxHeight = MENU_GetMaxPopupHeight(MenuInfo);
+ MenuInfo->iMaxTop = MenuInfo->cyMenu - MENU_TOP_MARGIN;
+ if (MenuInfo->cyMenu >= maxHeight)
+ {
+ MenuInfo->cyMenu = maxHeight;
+ MenuInfo->dwArrowsOn = 1;
+ }
+ else
+ {
+ MenuInfo->dwArrowsOn = 0;
+ }
+
+ MenuCleanupRosMenuItemInfo(&lpitem);
+ MenuSetRosMenuInfo(MenuInfo);
+ ReleaseDC( 0, hdc );
+}
+
+/***********************************************************************
+ * MenuMenuBarCalcSize
+ *
+ * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap
+ * height is off by 1 pixel which causes lengthy window relocations when
+ * active document window is maximized/restored.
+ *
+ * Calculate the size of the menu bar.
+ */
+static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect,
+ PROSMENUINFO MenuInfo, HWND hwndOwner )
+{
+ ROSMENUITEMINFO ItemInfo;
+ int start, i, orgX, orgY, maxY, helpPos;
+
+ if ((lprect == NULL) || (MenuInfo == NULL)) return;
+ if (MenuInfo->cItems == 0) return;
+ TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect));
+ MenuInfo->cxMenu = lprect->right - lprect->left;
+ MenuInfo->cyMenu = 0;
+ maxY = lprect->top + 1;
+ start = 0;
+ helpPos = -1;
+
+ MenuInfo->cxTextAlign = 0;
+
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ while (start < MenuInfo->cItems)
+ {
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
+ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
+ }
+ orgX = lprect->left;
+ orgY = maxY;
+
+ /* Parse items until line break or end of menu */
+ for (i = start; i < MenuInfo->cItems; i++)
+ {
+ if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i;
+ if ((i != start) &&
+ (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break;
+
+ TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY);
+ MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE, FALSE);
+ if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo))
+ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
+ }
+
+ if (ItemInfo.Rect.right > lprect->right)
+ {
+ if (i != start) break;
+ else ItemInfo.Rect.right = lprect->right;
+ }
+ maxY = max( maxY, ItemInfo.Rect.bottom );
+ orgX = ItemInfo.Rect.right;
+ if (i + 1 < MenuInfo->cItems)
+ {
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo))
+ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
+ }
+ }
+ }
+
+/* FIXME: Is this really needed? */ /*NO! it is not needed, why make the
- {
- if (i < helpPos)
- {
- break; /* done */
- }
- if (ItemInfo.Rect.top != orgY)
- {
- break; /* Other line */
- }
- if (orgX <= ItemInfo.Rect.right)
- {
- break; /* Too far right already */
- }
++ HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */
+#if 0
+ /* Finish the line (set all items to the largest height found) */
+ while (start < i)
+ {
+ if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo))
+ {
+ ItemInfo.Rect.bottom = maxY;
+ MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo);
+ }
+ start++;
+ }
+#else
+ start = i; /* This works! */
+#endif
+ }
+
+ lprect->bottom = maxY;
+ MenuInfo->cyMenu = lprect->bottom - lprect->top;
+ MenuSetRosMenuInfo(MenuInfo);
+
+ if (helpPos != -1)
+ {
+ /* Flush right all items between the MF_RIGHTJUSTIFY and */
+ /* the last item (if several lines, only move the last line) */
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->cItems - 1, &ItemInfo))
+ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
+ }
+ orgY = ItemInfo.Rect.top;
+ orgX = lprect->right;
+ for (i = MenuInfo->cItems - 1; helpPos <= i; i--)
- {
++ {
++ if (i < helpPos) break; /* done */
++ if (ItemInfo.Rect.top != orgY) break; /* Other line */
++ if (orgX <= ItemInfo.Rect.right) break; /* Too far right already */
+ ItemInfo.Rect.left += orgX - ItemInfo.Rect.right;
+ ItemInfo.Rect.right = orgX;
+ orgX = ItemInfo.Rect.left;
+ MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo);
+ if (helpPos + 1 <= i &&
+ ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo))
- }
- }
++ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
-
++ }
++ }
+ }
+
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+}
+
+/***********************************************************************
+ * MENU_DrawScrollArrows
+ *
+ * Draw scroll arrows.
+ */
+static void
+MENU_DrawScrollArrows(PROSMENUINFO lppop, HDC hdc)
+{
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HBITMAP hOrigBitmap;
+ UINT arrow_bitmap_width, arrow_bitmap_height;
+ BITMAP bmp;
+ RECT rect;
+
+ GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
+ arrow_bitmap_width = bmp.bmWidth;
+ arrow_bitmap_height = bmp.bmHeight;
+
- RECT rectTemp;
+ if (lppop->iTop)
+ hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap());
+ else
+ hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap());
+ rect.left = 0;
+ rect.top = 0;
+ rect.right = lppop->cxMenu;
+ rect.bottom = arrow_bitmap_height;
+ FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
+ BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, 0,
+ arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
+ rect.top = lppop->cyMenu - arrow_bitmap_height;
+ rect.bottom = lppop->cyMenu;
+ FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU));
+ if (lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))
+ SelectObject(hdcMem, get_down_arrow_bitmap());
+ else
+ SelectObject(hdcMem, get_down_arrow_inactive_bitmap());
+ BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2,
+ lppop->cyMenu - arrow_bitmap_height,
+ arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY);
+ SelectObject(hdcMem, hOrigBitmap);
+ DeleteDC(hdcMem);
+}
+
++/***********************************************************************
++ * draw_popup_arrow
++ *
++ * Draws the popup-menu arrow.
++ */
++static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width,
++ UINT arrow_bitmap_height)
++{
++ HDC hdcMem = CreateCompatibleDC( hdc );
++ HBITMAP hOrigBitmap;
++
++ hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() );
++ BitBlt( hdc, rect.right - arrow_bitmap_width - 1,
++ (rect.top + rect.bottom - arrow_bitmap_height) / 2,
++ arrow_bitmap_width, arrow_bitmap_height,
++ hdcMem, 0, 0, SRCCOPY );
++ SelectObject( hdcMem, hOrigBitmap );
++ DeleteDC( hdcMem );
++}
++
+/***********************************************************************
+ * MenuDrawMenuItem
+ *
+ * Draw a single menu item.
+ */
+static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc,
+ PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction)
+{
+ RECT rect;
+ PWCHAR Text;
+ BOOL flat_menu = FALSE;
+ int bkgnd;
++ UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0;
+ PWND Wnd = ValidateHwndNoErr(hWnd);
+
+ if (!Wnd)
+ return;
+
++ if (!menuBar) {
++ BITMAP bmp;
++ GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp );
++ arrow_bitmap_width = bmp.bmWidth;
++ arrow_bitmap_height = bmp.bmHeight;
++ }
++
+ if (lpitem->fType & MF_SYSMENU)
+ {
+ if ( (Wnd->style & WS_MINIMIZE))
+ {
+ UserGetInsideRectNC(Wnd, &rect);
+ UserDrawSysMenuButton(hWnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT));
+ }
+ return;
+ }
+
+ SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
+ bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU;
+
+ /* Setup colors */
+
+ if (lpitem->fState & MF_HILITE)
+ {
+ if(menuBar && !flat_menu) {
+ SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT));
+ SetBkColor(hdc, GetSysColor(COLOR_MENU));
+ } else {
+ if (lpitem->fState & MF_GRAYED)
+ SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
+ else
+ SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
+ }
+ }
+ else
+ {
+ if (lpitem->fState & MF_GRAYED)
+ SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) );
+ else
+ SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) );
+ SetBkColor( hdc, GetSysColor( bkgnd ) );
+ }
+
+ TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect));
+ rect = lpitem->Rect;
+ MENU_AdjustMenuItemRect(MenuInfo, &rect);
+
+ if (lpitem->fType & MF_OWNERDRAW)
+ {
+ /*
+ ** Experimentation under Windows reveals that an owner-drawn
+ ** menu is given the rectangle which includes the space it requested
+ ** in its response to WM_MEASUREITEM _plus_ width for a checkmark
+ ** and a popup-menu arrow. This is the value of lpitem->rect.
+ ** Windows will leave all drawing to the application except for
+ ** the popup-menu arrow. Windows always draws that itself, after
+ ** the menu owner has finished drawing.
+ */
+ DRAWITEMSTRUCT dis;
+
+ dis.CtlType = ODT_MENU;
+ dis.CtlID = 0;
+ dis.itemID = lpitem->wID;
+ dis.itemData = (DWORD)lpitem->dwItemData;
+ dis.itemState = 0;
+ if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED;
+ if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT;
+ if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED;
+ if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED;
+ if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED;
+ //if (!(MenuInfo->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL;
+ //if (MenuInfo->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE;
+ dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */
+ dis.hwndItem = (HWND) MenuInfo->Self;
+ dis.hDC = hdc;
+ dis.rcItem = rect;
+ TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, "
+ "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd,
+ dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem,
+ dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right,
+ dis.rcItem.bottom);
+ SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis);
+ /* Draw the popup-menu arrow */
+ if (lpitem->hSubMenu)
+ {
- rc.left -= 3;
++ /* RECT rectTemp;
+ CopyRect(&rectTemp, &rect);
+ rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
+ DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
++ */
++ draw_popup_arrow( hdc, rect, arrow_bitmap_width, arrow_bitmap_height);
+ }
+ return;
+ }
+
+ if (menuBar && (lpitem->fType & MF_SEPARATOR)) return;
+
+ if (lpitem->fState & MF_HILITE)
+ {
+ if (flat_menu)
+ {
+ InflateRect (&rect, -1, -1);
+ FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT));
+ InflateRect (&rect, 1, 1);
+ FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+ }
+ else
+ {
+ if(menuBar)
+ DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT);
+ else
+ FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+ }
+ }
+ else
+ FillRect( hdc, &rect, GetSysColorBrush(bkgnd) );
+
+ SetBkMode( hdc, TRANSPARENT );
+
+ /* vertical separator */
+ if (!menuBar && (lpitem->fType & MF_MENUBARBREAK))
+ {
+ HPEN oldPen;
+ RECT rc = rect;
+
- if ( lpitem->hbmpItem )
++ rc.left -= 3;//MENU_COL_SPACE / 2 + 1;
+ rc.top = 3;
+ rc.bottom = Height - 3;
+ if (flat_menu)
+ {
+ oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
+ SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW));
+ MoveToEx( hdc, rc.left, rc.top, NULL );
+ LineTo( hdc, rc.left, rc.bottom );
+ SelectObject( hdc, oldPen );
+ }
+ else
+ DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT);
+ }
+
+ /* horizontal separator */
+ if (lpitem->fType & MF_SEPARATOR)
+ {
+ HPEN oldPen;
+ RECT rc = rect;
+
+ rc.left++;
+ rc.right--;
+ rc.top += SEPARATOR_HEIGHT / 2;
+ if (flat_menu)
+ {
+ oldPen = SelectObject( hdc, GetStockObject(DC_PEN) );
+ SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW));
+ MoveToEx( hdc, rc.left, rc.top, NULL );
+ LineTo( hdc, rc.right, rc.top );
+ SelectObject( hdc, oldPen );
+ }
+ else
+ DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP);
+ return;
+ }
+
+#if 0
+ /* helper lines for debugging */
+ /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */
+ FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH));
+ SelectObject(hdc, GetStockObject(DC_PEN));
+ SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME));
+ MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL);
+ LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2);
+#endif
+
+ if (!menuBar)
+ {
+ HBITMAP bm;
+ INT y = rect.top + rect.bottom;
+ RECT rc = rect;
+ BOOL checked = FALSE;
+ UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK );
+ UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK );
+ /* Draw the check mark
+ *
+ * FIXME:
+ * Custom checkmark bitmaps are monochrome but not always 1bpp.
+ */
+ if( !(MenuInfo->dwStyle & MNS_NOCHECK)) {
+ bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked :
+ lpitem->hbmpUnchecked;
+ if (bm) /* we have a custom bitmap */
+ {
+ HDC hdcMem = CreateCompatibleDC( hdc );
+
+ SelectObject( hdcMem, bm );
+ BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2,
+ check_bitmap_width, check_bitmap_height,
+ hdcMem, 0, 0, SRCCOPY );
+ DeleteDC( hdcMem );
+ checked = TRUE;
+ }
+ else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */
+ {
+ RECT r;
++ //HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL );
++ //HDC hdcMem = CreateCompatibleDC( hdc );
++ //SelectObject( hdcMem, bm );
++ //SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height);
+ CopyRect(&r, &rect);
+ r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK);
+ DrawFrameControl( hdc, &r, DFC_MENU,
+ (lpitem->fType & MFT_RADIOCHECK) ?
+ DFCS_MENUBULLET : DFCS_MENUCHECK);
++ //BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom, hdcMem, 0, 0, SRCCOPY );
++ //DeleteDC( hdcMem );
++ //DeleteObject( bm );
+ checked = TRUE;
+ }
+ }
- RECT rectTemp;
++ if ( lpitem->hbmpItem )//&& !( checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
+ {
+ RECT bmpRect;
+ CopyRect(&bmpRect, &rect);
+ if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK))
+ bmpRect.left += check_bitmap_width + 2;
+ if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP)))
+ {
++ //POINT origorg;
+ bmpRect.right = bmpRect.left + lpitem->maxBmpSize.cx;
++ /* some applications make this assumption on the DC's origin */
++ //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
+ MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo, WndOwner, odaction, menuBar);
++ //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
+ }
+ }
+ /* Draw the popup-menu arrow */
+ if (lpitem->hSubMenu)
+ {
- rect.right -= check_bitmap_width;
++ /* RECT rectTemp;
+ CopyRect(&rectTemp, &rect);
+ rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK);
+ DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW);
++ */
++ draw_popup_arrow( hdc, rect, arrow_bitmap_width, arrow_bitmap_height);
+ }
+ rect.left += 4;
+ if( !(MenuInfo->dwStyle & MNS_NOCHECK))
+ rect.left += check_bitmap_width;
-
++ rect.right -= arrow_bitmap_width;//check_bitmap_width;
+ }
+ else if( lpitem->hbmpItem)
+ { /* Draw the bitmap */
++ //POINT origorg;
++
++ //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg);
+ MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo, WndOwner, odaction, menuBar);
++ //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL);
+ }
+
+ /* process text if present */
+ if (lpitem->lpstr)
+ {
+ register int i = 0;
+ HFONT hfontOld = 0;
- Text = (PWCHAR) lpitem->dwTypeData;
+ UINT uFormat = menuBar ?
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE :
+ DT_LEFT | DT_VCENTER | DT_SINGLELINE;
+
+ if((MenuInfo->dwStyle & MNS_CHECKORBMP))
+ rect.left += max(0, (int)(MenuInfo->cxTextAlign - GetSystemMetrics(SM_CXMENUCHECK)));
+ else
+ rect.left += MenuInfo->cxTextAlign;
+
+ if ( lpitem->fState & MFS_DEFAULT )
+ {
+ hfontOld = SelectObject(hdc, hMenuFontBold);
+ }
+
+ if (menuBar) {
+ rect.left += MENU_BAR_ITEMS_SPACE / 2;
+ rect.right -= MENU_BAR_ITEMS_SPACE / 2;
+ }
+
- /* draw menu items */
++ Text = lpitem->lpstr;
+ if(Text)
+ {
+ for (i = 0; L'\0' != Text[i]; i++)
+ if (Text[i] == L'\t' || Text[i] == L'\b')
+ break;
+ }
+
+ if(lpitem->fState & MF_GRAYED)
+ {
+ if (!(lpitem->fState & MF_HILITE) )
+ {
+ ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
+ SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
+ DrawTextW( hdc, Text, i, &rect, uFormat );
+ --rect.left; --rect.top; --rect.right; --rect.bottom;
+ }
+ SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
+ }
+
+ DrawTextW( hdc, Text, i, &rect, uFormat);
+
+ /* paint the shortcut text */
+ if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */
+ {
+ if (L'\t' == Text[i])
+ {
+ rect.left = lpitem->dxTab;
+ uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE;
+ }
+ else
+ {
+ rect.right = lpitem->dxTab;
+ uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE;
+ }
+
+ if (lpitem->fState & MF_GRAYED)
+ {
+ if (!(lpitem->fState & MF_HILITE) )
+ {
+ ++rect.left; ++rect.top; ++rect.right; ++rect.bottom;
+ SetTextColor(hdc, RGB(0xff, 0xff, 0xff));
+ DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat);
+ --rect.left; --rect.top; --rect.right; --rect.bottom;
+ }
+ SetTextColor(hdc, RGB(0x80, 0x80, 0x80));
+ }
+ DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat );
+ }
+
+ if (hfontOld)
+ SelectObject (hdc, hfontOld);
+ }
+}
+
+/***********************************************************************
+ * MenuDrawPopupMenu
+ *
+ * Paint a popup menu.
+ */
+static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu )
+{
+ HBRUSH hPrevBrush = 0;
+ RECT rect;
+
+ TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu);
+
+ GetClientRect( hwnd, &rect );
+
+ if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) ))
+ && (SelectObject( hdc, hMenuFont)))
+ {
+ HPEN hPrevPen;
+
+ Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom );
+
+ hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) );
+ if ( hPrevPen )
+ {
+ BOOL flat_menu = FALSE;
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+
+ SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
+ if (flat_menu)
+ FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW));
+ else
+ DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT);
+
- MenuInfo.cyMenu, FALSE, ODA_DRAWENTIRE);
+ //TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle);
++ /* draw menu items */
+ if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.cItems)
+ {
+ UINT u;
+ MenuInitRosMenuItemInfo(&ItemInfo);
+
+ for (u = 0; u < MenuInfo.cItems; u++)
+ {
+ if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo))
+ {
+ HWND WndOwner = MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL;
+ MenuDrawMenuItem(hwnd, &MenuInfo, WndOwner, hdc, &ItemInfo,
-
++ MenuInfo.cyMenu, FALSE, ODA_DRAWENTIRE);
+ }
+ }
- //MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc);
+ /* draw scroll arrows */
+ if (MenuInfo.dwArrowsOn)
+ MENU_DrawScrollArrows(&MenuInfo, hdc);
+
+ MenuSetRosMenuInfo(&MenuInfo);
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ }
+ } else
+ {
+ SelectObject( hdc, hPrevBrush );
+ }
+ }
+}
+
+/***********************************************************************
+ * MenuDrawMenuBar
+ *
+ * Paint a menu bar. Returns the height of the menu bar.
+ * called from [windows/nonclient.c]
+ */
+UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd,
+ BOOL suppress_draw)
+{
+ ROSMENUINFO lppop;
+ HFONT hfontOld = 0;
+ HMENU hMenu = GetMenu(hwnd);
+
+ if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL)
+ {
+ return GetSystemMetrics(SM_CYMENU);
+ }
+
+ if (suppress_draw)
+ {
+ hfontOld = SelectObject(hDC, hMenuFont);
+
+ MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd);
+
+ lprect->bottom = lprect->top + lppop.cyMenu;
+
+ if (hfontOld) SelectObject( hDC, hfontOld);
+ return lppop.cyMenu;
+ }
+ else
+ return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL);
+}
+
+/***********************************************************************
+ * MENU_InitPopup
+ *
+ * Popup menu initialization before WM_ENTERMENULOOP.
+ */
+static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags )
+{
+ MENU *menu;
+ DWORD ex_style = WS_EX_TOOLWINDOW;
+ ROSMENUINFO MenuInfo;
+
+ TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu);
+
+ if (!(menu = MENU_GetMenu( hmenu ))) return FALSE;
+
+ /* store the owner for DrawItem */
+ if (!IsWindow( hwndOwner ))
+ {
+ SetLastError( ERROR_INVALID_WINDOW_HANDLE );
+ return FALSE;
+ }
+ MenuGetRosMenuInfo(&MenuInfo, menu->head.h);
+ //menu->hwndOwner = hwndOwner;
+ MenuInfo.spwndNotify = ValidateHwndNoErr( hwndOwner );
+
+ if (flags & TPM_LAYOUTRTL)
+ ex_style = WS_EX_LAYOUTRTL;
+
+ /* NOTE: In Windows, top menu popup is not owned. */
+ //menu->hWnd = CreateWindowExW( ex_style, WC_MENU, NULL,
+ MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL,
+ WS_POPUP, 0, 0, 0, 0,
+ hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE),
+ (LPVOID)hmenu );
+ MenuSetRosMenuInfo(&MenuInfo);
+ if( !menu->hWnd ) return FALSE;
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuShowPopup
+ *
+ * Display a popup menu.
+ */
+static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags,
+ INT x, INT y, INT xanchor, INT yanchor )
+{
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+ UINT width, height;
+ POINT pt;
+ HMONITOR monitor;
+ MONITORINFO info;
+
+ TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n",
+ hwndOwner, hmenu, id, x, y, xanchor, yanchor);
+
+ if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE;
+ if (MenuInfo.iItem != NO_SELECTED_ITEM)
+ {
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo))
+ {
+ ItemInfo.fMask |= MIIM_STATE;
+ ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
+ MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo);
+ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ MenuInfo.iItem = NO_SELECTED_ITEM;
+ }
+
+ //menu->dwArrowsOn = 0;
+ MenuInfo.dwArrowsOn = 0;
+ MenuSetRosMenuInfo(&MenuInfo);
+ MenuPopupMenuCalcSize(&MenuInfo, hwndOwner);
+
+ /* adjust popup menu pos so that it fits within the desktop */
+
+ width = MenuInfo.cxMenu + GetSystemMetrics(SM_CXBORDER);
+ height = MenuInfo.cyMenu + GetSystemMetrics(SM_CYBORDER);
+
+ /* FIXME: should use item rect */
+ pt.x = x;
+ pt.y = y;
+ monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST );
+ info.cbSize = sizeof(info);
+ GetMonitorInfoW( monitor, &info );
+
+ if (flags & TPM_LAYOUTRTL)
+ flags ^= TPM_RIGHTALIGN;
+
+ if( flags & TPM_RIGHTALIGN ) x -= width;
+ if( flags & TPM_CENTERALIGN ) x -= width / 2;
+
+ if( flags & TPM_BOTTOMALIGN ) y -= height;
+ if( flags & TPM_VCENTERALIGN ) y -= height / 2;
+
+ if( x + width > info.rcMonitor.right)
+ {
+ if( xanchor && x >= width - xanchor )
+ x -= width - xanchor;
+
+ if( x + width > info.rcMonitor.right)
+ x = info.rcMonitor.right - width;
+ }
+ if( x < info.rcMonitor.left ) x = info.rcMonitor.left;
+
+ if( y + height > info.rcMonitor.bottom)
+ {
+ if( yanchor && y >= height + yanchor )
+ y -= height + yanchor;
+
+ if( y + height > info.rcMonitor.bottom)
+ y = info.rcMonitor.bottom - height;
+ }
+ if( y < info.rcMonitor.top ) y = info.rcMonitor.top;
+
+ if (!top_popup) {
+ top_popup = MenuInfo.Wnd;
+ top_popup_hmenu = hmenu;
+ }
+ /* Display the window */
+
+ SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, x, y, width, height,
+ SWP_SHOWWINDOW | SWP_NOACTIVATE);
+ UpdateWindow( MenuInfo.Wnd );
+
+ IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
+
+ return TRUE;
+}
+
+/***********************************************************************
+ * MENU_EnsureMenuItemVisible
+ */
+void
+MENU_EnsureMenuItemVisible(PROSMENUINFO lppop, PROSMENUITEMINFO item, HDC hdc)
+{
+ if (lppop->dwArrowsOn)
+ {
+ //ITEM *item = &lppop->items[wIndex];
+ UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop);
+ UINT nOldPos = lppop->iTop;
+ RECT rc;
+ UINT arrow_bitmap_height;
+ BITMAP bmp;
+
+ GetClientRect(lppop->Wnd, &rc);
+
+ GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp);
+ arrow_bitmap_height = bmp.bmHeight;
+
+ rc.top += arrow_bitmap_height;
+ rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN;
+
+ nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height;
+ if (item->Rect.bottom > lppop->iTop + nMaxHeight)
+ {
+ lppop->iTop = item->Rect.bottom - nMaxHeight;
+ ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
+ MENU_DrawScrollArrows(lppop, hdc);
+ }
+ else if (item->Rect.top - MENU_TOP_MARGIN < lppop->iTop)
+ {
+ lppop->iTop = item->Rect.top - MENU_TOP_MARGIN;
+ ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc);
+ MENU_DrawScrollArrows(lppop, hdc);
+ }
+ }
+}
+
+/***********************************************************************
+ * MenuSelectItem
+ */
+static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex,
+ BOOL sendMenuSelect, HMENU topmenu)
+{
+ ROSMENUITEMINFO ItemInfo;
+ ROSMENUINFO TopMenuInfo;
+ HDC hdc;
+
+ TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect);
+
+ if (!hmenu || !hmenu->cItems || !hmenu->Wnd) return;
+ if (hmenu->iItem == wIndex) return;
+ if (hmenu->fFlags & MNF_POPUP) hdc = GetDC(hmenu->Wnd);
+ else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW);
+ if (!top_popup) {
+ top_popup = hmenu->Wnd;
+ top_popup_hmenu = hmenu->Self;
+ }
+
+ SelectObject( hdc, hMenuFont );
+
+ MenuInitRosMenuItemInfo(&ItemInfo);
+
+ /* Clear previous highlighted item */
+ if (hmenu->iItem != NO_SELECTED_ITEM)
+ {
+ if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
+ {
+ ItemInfo.fMask |= MIIM_STATE;
+ ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT);
+ MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
+ }
- &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
- ODA_SELECT);
+ MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo,
+ hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP),
+ ODA_SELECT);
+ }
+
+ /* Highlight new item (if any) */
+ hmenu->iItem = wIndex;
+ MenuSetRosMenuInfo(hmenu);
+ if (hmenu->iItem != NO_SELECTED_ITEM)
+ {
+ if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo))
+ {
+ if (!(ItemInfo.fType & MF_SEPARATOR))
+ {
+ ItemInfo.fMask |= MIIM_STATE;
+ ItemInfo.fState |= MF_HILITE;
+ MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo);
++ MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc);
+ MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc,
- if (resinfo & 1) /* Pop-up? */
- {
++ &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP), ODA_SELECT);
+ }
+ if (sendMenuSelect)
+ {
+ WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID,
+ ItemInfo.fType | ItemInfo.fState |
+ (ItemInfo.hSubMenu ? MF_POPUP : 0) |
+ (hmenu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
+
+ SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self);
+ }
+ }
+ }
+ else if (sendMenuSelect)
+ {
+ if(topmenu)
+ {
+ int pos;
+ pos = MenuFindSubMenu(&topmenu, hmenu->Self);
+ if (pos != NO_SELECTED_ITEM)
+ {
+ if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu)
+ && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo))
+ {
+ WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState |
+ (ItemInfo.hSubMenu ? MF_POPUP : 0) |
+ (TopMenuInfo.fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) );
+
+ SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu);
+ }
+ }
+ }
+ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ ReleaseDC(hmenu->Wnd, hdc);
+}
+
+/***********************************************************************
+ * MenuMoveSelection
+ *
+ * Moves currently selected item according to the Offset parameter.
+ * If there is no selection then it should select the last item if
+ * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT.
+ */
+static void FASTCALL
+MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset)
+{
+ INT i;
+ ROSMENUITEMINFO ItemInfo;
+ INT OrigPos;
+
+ TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset);
+
+ /* Prevent looping */
+ if (0 == MenuInfo->cItems || 0 == Offset)
+ return;
+ else if (Offset < -1)
+ Offset = -1;
+ else if (Offset > 1)
+ Offset = 1;
+
+ MenuInitRosMenuItemInfo(&ItemInfo);
+
+ OrigPos = MenuInfo->iItem;
+ if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */
+ {
+ OrigPos = 0;
+ i = -1;
+ }
+ else
+ {
+ i = MenuInfo->iItem;
+ }
+
+ do
+ {
+ /* Step */
+ i += Offset;
+ /* Clip and wrap around */
+ if (i < 0)
+ {
+ i = MenuInfo->cItems - 1;
+ }
+ else if (i >= MenuInfo->cItems)
+ {
+ i = 0;
+ }
+ /* If this is a good candidate; */
+ if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) &&
+ 0 == (ItemInfo.fType & MF_SEPARATOR))
+ {
+ MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL);
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
+ }
+ } while (i != OrigPos);
+
+ /* Not found */
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+}
+
+#if 0
+LRESULT WINAPI
+PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
+{
+#ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
+ PWND pWnd;
+ PPOPUPMENU pPopupMenu;
+
+ pWnd = ValidateHwndNoErr(Wnd);
+ if (pWnd)
+ {
+ if (!pWnd->fnid)
+ {
+ if (Message != WM_NCCREATE)
+ {
+ return DefWindowProcW(Wnd, Message, wParam, lParam);
+ }
+ NtUserSetWindowFNID(Wnd, FNID_MENU);
+ pPopupMenu = HeapAlloc( GetProcessHeap(), 0, sizeof(POPUPMENU) );
+ pPopupMenu->spwndPopupMenu = pWnd;
+ SetWindowLongPtrW(Wnd, 0, (LONG_PTR)pPopupMenu);
+ }
+ else
+ {
+ if (pWnd->fnid != FNID_MENU)
+ {
+ ERR("Wrong window class for Menu!\n");
+ return 0;
+ }
+ pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu;
+ }
+ }
+#endif
+
+ TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
+
+ switch(Message)
+ {
+ case WM_CREATE:
+ {
+ CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
+ pPopupMenu->spmenu = ValidateHandle(cs->lpCreateParams, TYPE_MENU);
+ return 0;
+ }
+
+ case WM_MOUSEACTIVATE: /* We don't want to be activated */
+ return MA_NOACTIVATE;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ BeginPaint(Wnd, &ps);
+ MenuDrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu->head.h);
+ EndPaint(Wnd, &ps);
+ return 0;
+ }
+
+ case WM_PRINTCLIENT:
+ {
+ MenuDrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu->head.h);
+ return 0;
+ }
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_DESTROY:
+ /* zero out global pointer in case resident popup window was destroyed. */
+ if (Wnd == top_popup)
+ {
+ top_popup = NULL;
+ top_popup_hmenu = NULL;
+ }
+ break;
+
+ case WM_NCDESTROY:
+ {
+ HeapFree( GetProcessHeap(), 0, pPopupMenu );
+ SetWindowLongPtrW(Wnd, 0, 0);
+ NtUserSetWindowFNID(Wnd, FNID_DESTROY);
+ break;
+ }
+
+ case WM_SHOWWINDOW:
+ if (0 != wParam)
+ {
+ if (!pPopupMenu || !pPopupMenu->spmenu)
+ {
+ OutputDebugStringA("no menu to display\n");
+ }
+ }
+ /*else
+ {
+ pPopupMenu->spmenu = NULL; ///// WTF?
+ }*/
+ break;
+
+ case MM_SETMENUHANDLE:
+ {
+ PMENU pmenu = ValidateHandle((HMENU)wParam, TYPE_MENU);
+ if (!pmenu)
+ {
+ ERR("Bad Menu Handle\n");
+ break;
+ }
+ pPopupMenu->spmenu = pmenu;
+ break;
+ }
+
+ case MM_GETMENUHANDLE:
+ case MN_GETHMENU:
+ return (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? pPopupMenu->spmenu->head.h : NULL) : NULL);
+
+ default:
+ return DefWindowProcW(Wnd, Message, wParam, lParam);
+ }
+
+ return 0;
+}
+#endif
+
+LRESULT WINAPI
+PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
+{
+#ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
+ PWND pWnd;
+
+ pWnd = ValidateHwnd(Wnd);
+ if (pWnd)
+ {
+ if (!pWnd->fnid)
+ {
+ if (Message != WM_NCCREATE)
+ {
+ return DefWindowProcW(Wnd, Message, wParam, lParam);
+ }
+ NtUserSetWindowFNID(Wnd, FNID_MENU);
+ }
+ else
+ {
+ if (pWnd->fnid != FNID_MENU)
+ {
+ ERR("Wrong window class for Menu!\n");
+ return 0;
+ }
+ }
+ }
+#endif
+
+ TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
+
+ switch(Message)
+ {
+ case WM_CREATE:
+ {
+ CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam;
+ SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams);
+ return 0;
+ }
+
+ case WM_MOUSEACTIVATE: /* We don't want to be activated */
+ return MA_NOACTIVATE;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT ps;
+ BeginPaint(Wnd, &ps);
+ MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0));
+ EndPaint(Wnd, &ps);
+ return 0;
+ }
+
+ case WM_PRINTCLIENT:
+ {
+ MenuDrawPopupMenu( Wnd, (HDC)wParam,
+ (HMENU)GetWindowLongPtrW( Wnd, 0 ) );
+ return 0;
+ }
+
+ case WM_ERASEBKGND:
+ return 1;
+
+ case WM_DESTROY:
+ /* zero out global pointer in case resident popup window was destroyed. */
+ if (Wnd == top_popup)
+ {
+ top_popup = NULL;
+ top_popup_hmenu = NULL;
+ }
+ break;
+
+#ifdef __REACTOS__
+ case WM_NCDESTROY:
+ NtUserSetWindowFNID(Wnd, FNID_DESTROY);
+ break;
+#endif
+
+ case WM_SHOWWINDOW:
+ if (0 != wParam)
+ {
+ if (0 == GetWindowLongPtrW(Wnd, 0))
+ {
+ OutputDebugStringA("no menu to display\n");
+ }
+ }
+ else
+ {
+ SetWindowLongPtrW(Wnd, 0, 0);
+ }
+ break;
+
+ case MM_SETMENUHANDLE:
+ SetWindowLongPtrW(Wnd, 0, wParam);
+ break;
+
+ case MM_GETMENUHANDLE:
+ case MN_GETHMENU:
+ return GetWindowLongPtrW(Wnd, 0);
+
+ default:
+ return DefWindowProcW(Wnd, Message, wParam, lParam);
+ }
+
+ return 0;
+}
+
+//
+// This breaks some test results. Should handle A2U if called!
+//
+LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam)
+{
+ PWND pWnd;
+
+ pWnd = ValidateHwnd(Wnd);
+ if (pWnd && !pWnd->fnid && Message != WM_NCCREATE)
+ {
+ return DefWindowProcA(Wnd, Message, wParam, lParam);
+ }
+ TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam);
+
+ switch(Message)
+ {
+ case WM_NCCREATE:
+ case WM_CREATE:
+ case WM_MOUSEACTIVATE:
+ case WM_PAINT:
+ case WM_PRINTCLIENT:
+ case WM_ERASEBKGND:
+ case WM_DESTROY:
+ case WM_NCDESTROY:
+ case WM_SHOWWINDOW:
+ case MM_SETMENUHANDLE:
+ case MM_GETMENUHANDLE:
+ case MN_GETHMENU:
+ return PopupMenuWndProcW(Wnd, Message, wParam, lParam);
+
+ default:
+ return DefWindowProcA(Wnd, Message, wParam, lParam);
+ }
+ return 0;
+}
+
+/**********************************************************************
+ * MENU_ParseResource
+ *
+ * Parse a standard menu resource and add items to the menu.
+ * Return a pointer to the end of the resource.
+ *
+ * NOTE: flags is equivalent to the mtOption field
+ */
+static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu)
+{
+ WORD flags, id = 0;
+ HMENU hSubMenu;
+ LPCWSTR str;
+ BOOL end = FALSE;
+
+ do
+ {
+ flags = GET_WORD(res);
+
+ /* remove MF_END flag before passing it to AppendMenu()! */
+ end = (flags & MF_END);
+ if(end) flags ^= MF_END;
+
+ res += sizeof(WORD);
+ if(!(flags & MF_POPUP))
+ {
+ id = GET_WORD(res);
+ res += sizeof(WORD);
+ }
+ str = (LPCWSTR)res;
+ res += (strlenW(str) + 1) * sizeof(WCHAR);
+
+ if (flags & MF_POPUP)
+ {
+ hSubMenu = CreatePopupMenu();
+ if(!hSubMenu) return NULL;
+ if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL;
+ AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str);
+ }
+ else /* Not a popup */
+ {
+ AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL);
+ }
+ } while(!end);
+ return res;
+}
+
+/**********************************************************************
+ * MENUEX_ParseResource
+ *
+ * Parse an extended menu resource and add items to the menu.
+ * Return a pointer to the end of the resource.
+ */
+static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu)
+{
+ WORD resinfo;
+ do
+ {
+ MENUITEMINFOW mii;
+
+ mii.cbSize = sizeof(mii);
+ mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE;
+ mii.fType = GET_DWORD(res);
+ res += sizeof(DWORD);
+ mii.fState = GET_DWORD(res);
+ res += sizeof(DWORD);
+ mii.wID = GET_DWORD(res);
+ res += sizeof(DWORD);
+ resinfo = GET_WORD(res);
+ res += sizeof(WORD);
+ /* Align the text on a word boundary. */
+ res += (~((UINT_PTR)res - 1)) & 1;
+ mii.dwTypeData = (LPWSTR)res;
+ mii.cch = strlenW(mii.dwTypeData);
+ res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR);
+ /* Align the following fields on a dword boundary. */
+ res += (~((UINT_PTR)res - 1)) & 3;
+
+ TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n",
+ mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData);
+
- if (NO_SELECTED_ITEM == MenuInfo->iItem)
- {
- return MenuInfo->Self;
- }
++ if (resinfo & 1) /* Pop-up? */
++ {
+ /* DWORD helpid = GET_DWORD(res); FIXME: use this. */
+ res += sizeof(DWORD);
+ mii.hSubMenu = CreatePopupMenu();
+ if (!mii.hSubMenu)
+ {
+ ERR("CreatePopupMenu failed\n");
+ return NULL;
+ }
+
+ if (!(res = MENUEX_ParseResource(res, mii.hSubMenu)))
+ {
+ ERR("MENUEX_ParseResource failed\n");
+ DestroyMenu(mii.hSubMenu);
+ return NULL;
+ }
+ mii.fMask |= MIIM_SUBMENU;
+ }
+ else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR))
+ {
++ WARN("Converting NULL menu item %04x, type %04x to SEPARATOR\n",
++ mii.wID, mii.fType);
+ mii.fType |= MF_SEPARATOR;
+ }
+ InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii);
+ } while (!(resinfo & MF_END));
+ return res;
+}
+
+
+/***********************************************************************
+ * DrawMenuBarTemp (USER32.@)
+ *
+ * UNDOCUMENTED !!
+ *
+ * called by W98SE desk.cpl Control Panel Applet
+ *
+ * Not 100% sure about the param names, but close.
+ *
+ * @implemented
+ */
+DWORD WINAPI
+DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font)
+{
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+ UINT i;
+ HFONT FontOld = NULL;
+ BOOL flat_menu = FALSE;
+
+ SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0);
+
+ if (NULL == Menu)
+ {
+ Menu = GetMenu(Wnd);
+ }
+
+ if (NULL == Font)
+ {
+ Font = hMenuFont;
+ }
+
+ if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu))
+ {
+ return GetSystemMetrics(SM_CYMENU);
+ }
+
+ TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font);
+
+ FontOld = SelectObject(DC, Font);
+
+ if (0 == MenuInfo.cyMenu)
+ {
+ MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd);
+ }
+
+ Rect->bottom = Rect->top + MenuInfo.cyMenu;
+
+ FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU));
+
+ SelectObject(DC, GetStockObject(DC_PEN));
+ SetDCPenColor(DC, GetSysColor(COLOR_3DFACE));
+ MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL);
+ LineTo(DC, Rect->right, Rect->bottom - 1);
+
+ if (0 == MenuInfo.cItems)
+ {
+ SelectObject(DC, FontOld);
+ return GetSystemMetrics(SM_CYMENU);
+ }
+
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ for (i = 0; i < MenuInfo.cItems; i++)
+ {
+ if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo))
+ {
+ MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo,
+ MenuInfo.cyMenu, TRUE, ODA_DRAWENTIRE);
+ }
+ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+
+ SelectObject(DC, FontOld);
+
+ return MenuInfo.cyMenu;
+}
+
+
+/***********************************************************************
+ * MenuShowSubPopup
+ *
+ * Display the sub-menu of the selected item of this menu.
+ * Return the handle of the submenu, or menu if no submenu to display.
+ */
+static HMENU FASTCALL
+MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags)
+{
+ RECT Rect;
+ ROSMENUITEMINFO ItemInfo;
+ ROSMENUINFO SubMenuInfo;
+ HDC Dc;
+ HMENU Ret;
+
+ TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst);
+
- {
++ if (MenuInfo->iItem == NO_SELECTED_ITEM) return MenuInfo->Self;
+
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
- }
- if (0 == (ItemInfo.hSubMenu) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
- {
++ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return MenuInfo->Self;
- }
++ }
++
++ //item = &menu->rgItems[menu->iItem];
++ if (!(ItemInfo.hSubMenu) || (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)))
++ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return MenuInfo->Self;
- if (0 == (Flags & TPM_NONOTIFY))
- {
++ }
+
+ /* message must be sent before using item,
+ because nearly everything may be changed by the application ! */
+
+ /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
- }
++ if (!(Flags & TPM_NONOTIFY))
++ {
+ SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu,
+ MAKELPARAM(MenuInfo->iItem, IS_SYSTEM_MENU(MenuInfo)));
- {
++ }
+
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
- }
++ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return MenuInfo->Self;
- if (0 == (ItemInfo.fState & MF_HILITE))
- {
- if (0 != (MenuInfo->fFlags & MNF_POPUP))
- {
- Dc = GetDC(MenuInfo->Wnd);
- }
- else
- {
- Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
- }
++ }
++
++ //item = &menu->rgItems[menu->iItem];
+ Rect = ItemInfo.Rect;
+
+ /* correct item if modified as a reaction to WM_INITMENUPOPUP message */
- }
++ if (!(ItemInfo.fState & MF_HILITE))
++ {
++ if (MenuInfo->fFlags & MNF_POPUP) Dc = GetDC(MenuInfo->Wnd);
++ else Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW);
+
+ SelectObject(Dc, hMenuFont);
+ ItemInfo.fMask |= MIIM_STATE;
+ ItemInfo.fState |= MF_HILITE;
+ MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
+ MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->cyMenu,
+ !(MenuInfo->fFlags & MNF_POPUP), ODA_DRAWENTIRE);
+ ReleaseDC(MenuInfo->Wnd, Dc);
- if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left
- && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right)
- {
++ }
+
- }
++ if (!ItemInfo.Rect.top && !ItemInfo.Rect.left && !ItemInfo.Rect.bottom && !ItemInfo.Rect.right)
+ ItemInfo.Rect = Rect;
- ERR("Right click on window bar and Draw system menu!\n");
- MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
+
+ ItemInfo.fMask |= MIIM_STATE;
+ ItemInfo.fState |= MF_MOUSESELECT;
+ MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
+
+ if (IS_SYSTEM_MENU(MenuInfo))
+ {
- if (Flags & TPM_LAYOUTRTL) Rect.left;
++ MenuInitSysMenuPopup(ItemInfo.hSubMenu,
++ GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE),
+ GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU);
- if (0 != (MenuInfo->fFlags & MNF_POPUP))
++
+ NcGetSysPopupPos(MenuInfo->Wnd, &Rect);
++ if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right;
+ Rect.top = Rect.bottom;
+ Rect.right = GetSystemMetrics(SM_CXSIZE);
+ Rect.bottom = GetSystemMetrics(SM_CYSIZE);
+ }
+ else
+ {
+ GetWindowRect(MenuInfo->Wnd, &Rect);
- Rect.left += ItemInfo.Rect.right- GetSystemMetrics(SM_CXBORDER);
++ if (MenuInfo->fFlags & MNF_POPUP)
+ {
+ RECT rc = ItemInfo.Rect;
+
+ MENU_AdjustMenuItemRect(MenuInfo, &rc);
+
++ /* The first item in the popup menu has to be at the
++ same y position as the focused menu item */
+ if(Flags & TPM_LAYOUTRTL)
+ Rect.left += GetSystemMetrics(SM_CXBORDER);
+ else
- if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->iItem)
++ Rect.left += rc.right /*ItemInfo.Rect.right*/ - GetSystemMetrics(SM_CXBORDER);
+ Rect.top += rc.top - MENU_TOP_MARGIN;//3;
+ Rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER);
+ Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/
+ - GetSystemMetrics(SM_CYBORDER);
+ }
+ else
+ {
+ if(Flags & TPM_LAYOUTRTL)
+ Rect.left += Rect.right - ItemInfo.Rect.left;
+ else
+ Rect.left += ItemInfo.Rect.left;
+ Rect.top += ItemInfo.Rect.bottom;
+ Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left;
+ Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top;
+ }
+ }
+
+ /* use default alignment for submenus */
+ Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN);
+
+ MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags );
+
+ MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->iItem, Flags,
+ Rect.left, Rect.top, Rect.right, Rect.bottom );
+ if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
+ {
+ MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT);
+ }
+
+ Ret = ItemInfo.hSubMenu;
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+
+ return Ret;
+}
+
+/**********************************************************************
+ * MENU_EndMenu
+ *
+ * Calls EndMenu() if the hwnd parameter belongs to the menu owner
+ *
+ * Does the (menu stuff) of the default window handling of WM_CANCELMODE
+ */
+void MENU_EndMenu( HWND hwnd )
+{
+ MENU *menu;
+ menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL;
+ if (menu && ( hwnd == menu->hWnd || hwnd == (menu->spwndNotify ? menu->spwndNotify->head.h : NULL)) )
+ EndMenu();
+}
+
+/***********************************************************************
+ * MenuHideSubPopups
+ *
+ * Hide the sub-popup menus of this menu.
+ */
+static void FASTCALL
+MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo,
+ BOOL SendMenuSelect, UINT wFlags)
+{
+ ROSMENUINFO SubMenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+
+ TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect);
+
- || 0 == (ItemInfo.hSubMenu)
- || 0 == (ItemInfo.fState & MF_MOUSESELECT))
++ if (MenuInfo && top_popup && NO_SELECTED_ITEM != MenuInfo->iItem)
+ {
++ //item = &menu->rgItems[menu->iItem];
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE;
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)
- if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
- Mt->TopMenu != PtMenuInfo->Self &&
- 0 == ((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP))
- {
++ || !(ItemInfo.hSubMenu)
++ || !(ItemInfo.fState & MF_MOUSESELECT))
+ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return;
+ }
+ ItemInfo.fState &= ~MF_MOUSESELECT;
+ ItemInfo.fMask |= MIIM_STATE;
+ MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo);
+ if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu))
+ {
+ MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags);
+ MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL);
+
+ DestroyWindow(SubMenuInfo.Wnd);
+ /* Native returns handle to destroyed window */
+ if (!(wFlags & TPM_NONOTIFY))
+ SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu,
+ MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) );
+ ////
+ // Call WM_UNINITMENUPOPUP FIRST before destroy!!
+ // Fixes todo_wine User32 test menu.c line 2233 GetMenuBarInfo callback....
+ //
+ SubMenuInfo.Wnd = NULL;
+ MenuSetRosMenuInfo(&SubMenuInfo);
+ ////
+ }
+ }
+}
+
+/***********************************************************************
+ * MenuSwitchTracking
+ *
+ * Helper function for menu navigation routines.
+ */
+static void FASTCALL
+MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags)
+{
+ ROSMENUINFO TopMenuInfo;
+
+ TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index);
+
- }
++ if ( MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) &&
++ Mt->TopMenu != PtMenuInfo->Self &&
++ !((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP) )
++ {
+ /* both are top level menus (system and menu-bar) */
+ MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
+ MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL);
+ Mt->TopMenu = PtMenuInfo->Self;
- {
++ }
+ else
- }
++ {
+ MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags);
- {
++ }
+
+ MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL);
+}
+
+/***********************************************************************
+ * MenuExecFocusedItem
+ *
+ * Execute a menu item (for instance when user pressed Enter).
+ * Return the wID of the executed item. Otherwise, -1 indicating
+ * that no menu item was executed, -2 if a popup is shown;
+ * Have to receive the flags for the TrackPopupMenu options to avoid
+ * sending unwanted message.
+ *
+ */
+static INT FASTCALL
+MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags)
+{
+ ROSMENUITEMINFO ItemInfo;
+ UINT wID;
+
+ TRACE("%p menu=%p\n", Mt, MenuInfo);
+
+ if (0 == MenuInfo->cItems || NO_SELECTED_ITEM == MenuInfo->iItem)
- }
++ {
+ return -1;
- {
++ }
+
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo))
- }
++ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return -1;
- {
++ }
+
+ TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu);
+
+ if (0 == (ItemInfo.hSubMenu))
- {
++ {
+ if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))
+ && 0 == (ItemInfo.fType & MF_SEPARATOR))
- {
++ {
+ /* If TPM_RETURNCMD is set you return the id, but
+ do not send a message to the owner */
+ if (0 == (Flags & TPM_RETURNCMD))
- {
++ {
+ if (0 != (MenuInfo->fFlags & MNF_SYSDESKMN))
- }
++ {
+ PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID,
+ MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y));
- {
++ }
+ else
- }
- }
++ {
+ ROSMENUINFO topmenuI;
+ BOOL ret = MenuGetRosMenuInfo(&topmenuI, Mt->TopMenu);
+ DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0);
+
+ if (dwStyle & MNS_NOTIFYBYPOS)
+ PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->iItem, (LPARAM)MenuInfo->Self);
+ else
+ PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0);
- }
- }
++ }
++ }
+ wID = ItemInfo.wID;
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return wID;
- {
++ }
++ }
+ else
- }
++ {
+ Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags);
+ return -2;
- {
++ }
+
+ return -1;
+}
+
+/***********************************************************************
+ * MenuButtonDown
+ *
+ * Return TRUE if we can go on with menu tracking.
+ */
+static BOOL FASTCALL
+MENU_ButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags)
+{
+ int Index;
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO Item;
+
+ TRACE("%x PtMenu=%p\n", Mt, PtMenu);
+
+ if (NULL != PtMenu)
- {
++ {
+ if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
- }
++ {
+ return FALSE;
- {
++ }
+ if (IS_SYSTEM_MENU(&MenuInfo))
- }
++ {
+ Index = 0;
- {
++ }
+ else
- }
++ {
+ Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
- {
++ }
+ MenuInitRosMenuItemInfo(&Item);
+ if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item))
- }
++ {
+ MenuCleanupRosMenuItemInfo(&Item);
+ return FALSE;
- {
++ }
+
+ if (!(Item.fType & MF_SEPARATOR) &&
+ !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) )
- {
++ {
+ if (MenuInfo.iItem != Index)
- }
++ {
+ MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
- {
++ }
+
+ /* If the popup menu is not already "popped" */
+ if (0 == (Item.fState & MF_MOUSESELECT))
- }
- }
++ {
+ Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
- {
++ }
++ }
+
+ MenuCleanupRosMenuItemInfo(&Item);
+
+ return TRUE;
+ }
+
+ /* else the click was on the menu bar, finish the tracking */
+
+ return FALSE;
+}
+
+/***********************************************************************
+ * MenuButtonUp
+ *
+ * Return the value of MenuExecFocusedItem if
+ * the selected item was not a popup. Else open the popup.
+ * A -1 return value indicates that we go on with menu tracking.
+ *
+ */
+static INT FASTCALL
+MENU_ButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
+{
+ INT Id;
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+
+ TRACE("%p hmenu=%x\n", Mt, PtMenu);
+
+ if (NULL != PtMenu)
- {
++ {
+ Id = 0;
+ if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
- }
++ {
+ return -1;
- {
++ }
+
+ if (! IS_SYSTEM_MENU(&MenuInfo))
- }
++ {
+ Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y);
- {
++ }
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) &&
+ MenuInfo.iItem == Id)
- {
++ {
+ if (0 == (ItemInfo.hSubMenu))
- }
++ {
+ INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags);
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId;
- {
++ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+
+ /* If we are dealing with the top-level menu */
+ /* and this is a click on an already "popped" item: */
+ /* Stop the menu tracking and close the opened submenus */
+ if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide)
- }
- }
++ {
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ return 0;
- }
++ }
++ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ MenuInfo.TimeToHide = TRUE;
+ MenuSetRosMenuInfo(&MenuInfo);
- {
++ }
+
+ return -1;
+}
+
+/***********************************************************************
+ * MenuPtMenu
+ *
+ * Walks menu chain trying to find a menu pt maps to.
+ */
+static HMENU FASTCALL
+MENU_PtMenu(HMENU hMenu, POINT pt)
+{
+ MENU *menu;
+ PITEM pItem;
+ HMENU ret = NULL;
+
+ menu = MENU_GetMenu( hMenu );
+ if (!menu) return NULL;
+
+ /* try subpopup first (if any) */
+ if (menu->iItem != NO_SELECTED_ITEM)
+ {
+ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ if ( pItem ) pItem = &pItem[menu->iItem];
+ if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT)
+ {
+ PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
+ ret = MENU_PtMenu( UserHMGetHandle(pSubMenu), pt);
+ }
+ }
+
+ /* check the current window (avoiding WM_HITTEST) */
+ if (!ret)
+ {
+ INT ht = DefWndNCHitTest(menu->hWnd, pt);
+ if ( menu->fFlags & MNF_POPUP )
+ {
+ if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu;
+ }
+ else if (ht == HTSYSMENU)
+ ret = NtUserGetSystemMenu(menu->hWnd, FALSE);
+ else if (ht == HTMENU)
+ ret = GetMenu( menu->hWnd );
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * MenuMouseMove
+ *
+ * Return TRUE if we can go on with menu tracking.
+ */
+static BOOL FASTCALL
+MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags)
+{
+ INT Index;
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+
+ if (NULL != PtMenu)
- {
++ {
+ if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu))
- }
++ {
+ return TRUE;
- {
++ }
+ if (IS_SYSTEM_MENU(&MenuInfo))
- }
++ {
+ Index = 0;
- {
++ }
+ else
- }
- }
++ {
+ Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y);
- {
++ }
++ }
+ else
- }
++ {
+ Index = NO_SELECTED_ITEM;
- {
++ }
+
+ if (NO_SELECTED_ITEM == Index)
- {
++ {
+ if (Mt->CurrentMenu == MenuInfo.Self ||
+ MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
- }
- }
++ {
+ MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM,
+ TRUE, Mt->TopMenu);
- {
++ }
++ }
+ else if (MenuInfo.iItem != Index)
- !(ItemInfo.fType & MF_SEPARATOR))
++ {
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) &&
- if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
++ !(ItemInfo.fType & MF_SEPARATOR))
+ {
+ MenuSwitchTracking(Mt, &MenuInfo, Index, Flags);
- }
++ if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED)))
+ Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
+ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
- {
++ }
+
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuGetSubPopup
+ *
+ * Return the handle of the selected sub-popup menu (if any).
+ */
+static
+HMENU MENU_GetSubPopup( HMENU hmenu )
+{
+ MENU *menu;
+ ITEM *item;
+
+ menu = MENU_GetMenu( hmenu );
+
+ if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0;
+
+ //item = &menu->rgItems[menu->iItem];
+ item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL;
+ if (item) item = &item[menu->iItem];
+ if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT))
+ {
+ PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu);
+ return UserHMGetHandle(pSubMenu);
+ }
+ return 0;
+}
+
+/***********************************************************************
+ * MenuDoNextMenu
+ *
+ * NOTE: WM_NEXTMENU documented in Win32 is a bit different.
+ */
+static LRESULT FASTCALL
+MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags)
+{
+ ROSMENUINFO TopMenuInfo;
+ ROSMENUINFO MenuInfo;
+
+ if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
- }
++ {
+ return (LRESULT) FALSE;
- {
++ }
+
+ if ((VK_LEFT == Vk && 0 == TopMenuInfo.iItem)
+ || (VK_RIGHT == Vk && TopMenuInfo.iItem == TopMenuInfo.cItems - 1))
- {
++ {
+ MDINEXTMENU NextMenu;
+ HMENU NewMenu;
+ HWND NewWnd;
+ UINT Id = 0;
+
+ NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu;
+ NextMenu.hmenuNext = NULL;
+ NextMenu.hwndNext = NULL;
+ SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu);
+
+ TRACE("%p [%p] -> %p [%p]\n",
+ Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext );
+
+ if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext)
- {
++ {
+ DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE);
+ NewWnd = Mt->OwnerWnd;
+ if (IS_SYSTEM_MENU(&TopMenuInfo))
- {
++ {
+ /* switch to the menu bar */
+
+ if (0 != (Style & WS_CHILD)
+ || NULL == (NewMenu = GetMenu(NewWnd)))
- }
++ {
+ return FALSE;
- {
++ }
+
+ if (VK_LEFT == Vk)
- {
++ {
+ if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu))
- }
++ {
+ return FALSE;
- }
- }
++ }
+ Id = MenuInfo.cItems - 1;
- {
++ }
++ }
+ else if (0 != (Style & WS_SYSMENU))
- }
++ {
+ /* switch to the system menu */
+ NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
- {
++ }
+ else
- }
- }
++ {
+ return FALSE;
- {
++ }
++ }
+ else /* application returned a new menu to switch to */
- {
++ {
+ NewMenu = NextMenu.hmenuNext;
+ NewWnd = NextMenu.hwndNext;
+
+ if (IsMenu(NewMenu) && IsWindow(NewWnd))
- {
++ {
+ DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE);
+
+ if (0 != (Style & WS_SYSMENU)
+ && GetSystemMenu(NewWnd, FALSE) == NewMenu)
- }
++ {
+ /* get the real system menu */
+ NewMenu = NtUserGetSystemMenu(NewWnd, FALSE);
- {
++ }
+ else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu)
- }
- }
++ {
+ /* FIXME: Not sure what to do here;
+ * perhaps try to track NewMenu as a popup? */
+
+ WARN(" -- got confused.\n");
+ return FALSE;
- {
++ }
++ }
+ else
- }
- }
++ {
+ return FALSE;
- {
++ }
++ }
+
+ if (NewMenu != Mt->TopMenu)
- {
++ {
+ MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM,
+ FALSE, 0 );
+ if (Mt->CurrentMenu != Mt->TopMenu)
- }
- }
++ {
+ MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags);
- {
++ }
++ }
+
+ if (NewWnd != Mt->OwnerWnd)
- }
++ {
+ Mt->OwnerWnd = NewWnd;
+ NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1
+ SetCapture(Mt->OwnerWnd); // 2
- {
++ }
+
+ Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */
+ if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
- }
++ {
+ MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0);
- }
++ }
+
+ return TRUE;
- // Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
++ }
+
+ return FALSE;
+}
+
+/***********************************************************************
+ * MenuSuspendPopup
+ *
+ * The idea is not to show the popup if the next input message is
+ * going to hide it anyway.
+ */
+static BOOL FASTCALL
+MenuSuspendPopup(MTRACKER* Mt, UINT uMsg)
+{
+ MSG msg;
+
+ msg.hwnd = Mt->OwnerWnd;
+
+ PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG
- {
++ //Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!!
+
+ switch( uMsg )
+ {
+ case WM_KEYDOWN:
+ PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
+ if( msg.message == WM_KEYUP || msg.message == WM_PAINT )
+ {
+ PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE);
+ PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE);
+ if( msg.message == WM_KEYDOWN &&
+ (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT))
+ {
+ Mt->TrackFlags |= TF_SUSPENDPOPUP;
+ return TRUE;
+ }
+ }
+ break;
+ }
+ /* failures go through this */
+ Mt->TrackFlags &= ~TF_SUSPENDPOPUP;
+ return FALSE;
+}
+
+/***********************************************************************
+ * MenuKeyEscape
+ *
+ * Handle a VK_ESCAPE key event in a menu.
+ */
+static BOOL FASTCALL
+MenuKeyEscape(MTRACKER *Mt, UINT Flags)
+{
+ BOOL EndMenu = TRUE;
+ ROSMENUINFO MenuInfo;
+ HMENU MenuTmp, MenuPrev;
+
+ if (Mt->CurrentMenu != Mt->TopMenu)
- {
++ {
+ if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)
+ && 0 != (MenuInfo.fFlags & MNF_POPUP))
- {
++ {
+ MenuPrev = MenuTmp = Mt->TopMenu;
+
+ /* close topmost popup */
+ while (MenuTmp != Mt->CurrentMenu)
- }
++ {
+ MenuPrev = MenuTmp;
+ MenuTmp = MENU_GetSubPopup(MenuPrev);
- {
++ }
+
+ if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev))
- }
++ {
+ MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags);
- }
- }
++ }
+ Mt->CurrentMenu = MenuPrev;
+ EndMenu = FALSE;
- {
++ }
++ }
+
+ return EndMenu;
+}
+
+/***********************************************************************
+ * MenuKeyLeft
+ *
+ * Handle a VK_LEFT key event in a menu.
+ */
+static void FASTCALL
+MenuKeyLeft(MTRACKER* Mt, UINT Flags)
+{
+ ROSMENUINFO MenuInfo;
+ ROSMENUINFO TopMenuInfo;
+ ROSMENUINFO PrevMenuInfo;
+ HMENU MenuTmp, MenuPrev;
+ UINT PrevCol;
+
+ MenuPrev = MenuTmp = Mt->TopMenu;
+
+ /* Try to move 1 column left (if possible) */
+ if ( (PrevCol = MENU_GetStartOfPrevColumn(Mt->CurrentMenu)) != NO_SELECTED_ITEM)
+ {
+ if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
+ {
+ MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0);
+ }
+ return;
+ }
+
+ /* close topmost popup */
+ while (MenuTmp != Mt->CurrentMenu)
- }
++ {
+ MenuPrev = MenuTmp;
+ MenuTmp = MENU_GetSubPopup(MenuPrev);
- {
++ }
+
+ if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev))
- }
++ {
+ return;
- {
++ }
+ MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags);
+ Mt->CurrentMenu = MenuPrev;
+
+ if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
- }
++ {
+ return;
- {
++ }
+ if ((MenuPrev == Mt->TopMenu) && !(TopMenuInfo.fFlags & MNF_POPUP))
- {
++ {
+ /* move menu bar selection if no more popups are left */
+
+ if (!MenuDoNextMenu(Mt, VK_LEFT, Flags))
- }
++ {
+ MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV);
- {
++ }
+
+ if (MenuPrev != MenuTmp || Mt->TrackFlags & TF_SUSPENDPOPUP)
- {
++ {
+ /* A sublevel menu was displayed - display the next one
+ * unless there is another displacement coming up */
+
+ if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
+ && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu))
- }
- }
- }
++ {
+ Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo,
+ TRUE, Flags);
- {
++ }
++ }
++ }
+}
+
+/***********************************************************************
+ * MenuKeyRight
+ *
+ * Handle a VK_RIGHT key event in a menu.
+ */
+static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags)
+{
+ HMENU hmenutmp;
+ ROSMENUINFO MenuInfo;
+ ROSMENUINFO CurrentMenuInfo;
+ UINT NextCol;
+
+ TRACE("MenuKeyRight called, cur %p, top %p.\n",
+ Mt->CurrentMenu, Mt->TopMenu);
+
+ if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return;
+ if ((MenuInfo.fFlags & MNF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu))
+ {
+ /* If already displaying a popup, try to display sub-popup */
+
+ hmenutmp = Mt->CurrentMenu;
+ if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu))
+ {
+ Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags);
+ }
+
+ /* if subpopup was displayed then we are done */
+ if (hmenutmp != Mt->CurrentMenu) return;
+ }
+
+ /* Check to see if there's another column */
+ if ( (NextCol = MENU_GetStartOfNextColumn(Mt->CurrentMenu)) != NO_SELECTED_ITEM)
+ {
+ TRACE("Going to %d.\n", NextCol);
+ if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu))
+ {
+ MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0);
+ }
+ return;
+ }
+
+ if (!(MenuInfo.fFlags & MNF_POPUP)) /* menu bar tracking */
+ {
+ if (Mt->CurrentMenu != Mt->TopMenu)
- }
++ {
+ MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags);
+ hmenutmp = Mt->CurrentMenu = Mt->TopMenu;
- {
++ }
+ else
- }
++ {
+ hmenutmp = NULL;
- {
++ }
+
+ /* try to move to the next item */
+ if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags))
+ MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT);
+
+ if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP )
- {
++ {
+ if (! MenuSuspendPopup(Mt, WM_KEYDOWN)
+ && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu))
- }
- }
++ {
+ Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo,
+ TRUE, Flags);
- HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU");
- LRESULT Result = (LRESULT)hmenu;
- MENUINFO menuinfo = {0};
- MENUITEMINFOW info = {0};
-
- // removing space for checkboxes from menu
- menuinfo.cbSize = sizeof(menuinfo);
- menuinfo.fMask = MIM_STYLE;
- GetMenuInfo(hmenu, &menuinfo);
- menuinfo.dwStyle |= MNS_CHECKORBMP; // test_menu_bmp_and_string MNS_CHECKORBMP
- SetMenuInfo(hmenu, &menuinfo);
-
- // adding bitmaps to menu items
- info.cbSize = sizeof(info);
- info.fMask |= MIIM_BITMAP;
- info.hbmpItem = HBMMENU_POPUP_MINIMIZE;
- SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info);
- info.hbmpItem = HBMMENU_POPUP_RESTORE;
- SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info);
- info.hbmpItem = HBMMENU_POPUP_MAXIMIZE;
- SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info);
- info.hbmpItem = HBMMENU_POPUP_CLOSE;
- SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info);
++ }
++ }
+ }
+}
+
+/***********************************************************************
+ * MenuTrackMenu
+ *
+ * Menu tracking code.
+ */
+static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y,
+ HWND hwnd, const RECT *lprect )
+{
+ MSG msg;
+ ROSMENUINFO MenuInfo;
+ ROSMENUITEMINFO ItemInfo;
+ PMENU menu;
+ BOOL fRemove;
+ INT executedMenuId = -1;
+ MTRACKER mt;
+ HWND capture_win;
+ BOOL enterIdleSent = FALSE;
+
+ mt.TrackFlags = 0;
+ mt.CurrentMenu = hmenu;
+ mt.TopMenu = hmenu;
+ mt.OwnerWnd = hwnd;
+ mt.Pt.x = x;
+ mt.Pt.y = y;
+
+ TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n",
+ hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0,
+ lprect ? lprect->right : 0, lprect ? lprect->bottom : 0);
+
+ if (!IsMenu(hmenu))
+ {
+ WARN("Invalid menu handle %p\n", hmenu); // Error already set in IsMenu.
+ return FALSE;
+ }
+
+ if (! MenuGetRosMenuInfo(&MenuInfo, hmenu))
+ {
+ return FALSE;
+ }
+
+ if (wFlags & TPM_BUTTONDOWN)
+ {
+ /* Get the result in order to start the tracking or not */
+ fRemove = MENU_ButtonDown( &mt, hmenu, wFlags );
+ fEndMenu = !fRemove;
+ }
+
+ if (wFlags & TF_ENDMENU) fEndMenu = TRUE;
+
+ /* owner may not be visible when tracking a popup, so use the menu itself */
+ capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd;
+ NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1
+ SetCapture(capture_win); // 2
+
+ while (!fEndMenu)
+ {
+ BOOL ErrorExit = FALSE;
+ menu = MENU_GetMenu( mt.CurrentMenu );
+ if (!menu) /* sometimes happens if I do a window manager close */
+ break;
+
+ /* we have to keep the message in the queue until it's
+ * clear that menu loop is not over yet. */
+
+ for (;;)
+ {
+ if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
+ {
+ if (!CallMsgFilterW( &msg, MSGF_MENU )) break;
+ /* remove the message from the queue */
+ PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
+ }
+ else
+ {
+ /* ReactOS Check */
+ if (!ValidateHwndNoErr(mt.OwnerWnd) || !ValidateHwndNoErr(MenuInfo.Wnd))
+ {
+ ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works.
+ break;
+ }
+ if (!enterIdleSent)
+ {
+ HWND win = MenuInfo.fFlags & MNF_POPUP ? MenuInfo.Wnd : NULL;
+ enterIdleSent = TRUE;
+ SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win);
+ }
+ WaitMessage();
+ }
+ }
+
+ if (ErrorExit) break; // Gracefully dropout.
+
+ /* check if EndMenu() tried to cancel us, by posting this message */
+ if (msg.message == WM_CANCELMODE)
+ {
+ /* we are now out of the loop */
+ fEndMenu = TRUE;
+
+ /* remove the message from the queue */
+ PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
+
+ /* break out of internal loop, ala ESCAPE */
+ break;
+ }
+
+ TranslateMessage( &msg );
+ mt.Pt = msg.pt;
+
+ if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) )
+ enterIdleSent=FALSE;
+
+ fRemove = FALSE;
+ if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST))
+ {
+ /*
+ * Use the mouse coordinates in lParam instead of those in the MSG
+ * struct to properly handle synthetic messages. They are already
+ * in screen coordinates.
+ */
+ mt.Pt.x = (short)LOWORD(msg.lParam);
+ mt.Pt.y = (short)HIWORD(msg.lParam);
+
+ /* Find a menu for this mouse event */
+ hmenu = MENU_PtMenu(mt.TopMenu, mt.Pt);
+
+ switch(msg.message)
+ {
+ /* no WM_NC... messages in captured state */
+
+ case WM_RBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ if (!(wFlags & TPM_RIGHTBUTTON)) break;
+ /* fall through */
+ case WM_LBUTTONDBLCLK:
+ case WM_LBUTTONDOWN:
+ /* If the message belongs to the menu, removes it from the queue */
+ /* Else, end menu tracking */
+ fRemove = MENU_ButtonDown(&mt, hmenu, wFlags);
+ fEndMenu = !fRemove;
+ break;
+
+ case WM_RBUTTONUP:
+ if (!(wFlags & TPM_RIGHTBUTTON)) break;
+ /* fall through */
+ case WM_LBUTTONUP:
+ /* Check if a menu was selected by the mouse */
+ if (hmenu)
+ {
+ executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags);
+ TRACE("executedMenuId %d\n", executedMenuId);
+
+ /* End the loop if executedMenuId is an item ID */
+ /* or if the job was done (executedMenuId = 0). */
+ fEndMenu = fRemove = (executedMenuId != -1);
+ }
+ /* No menu was selected by the mouse */
+ /* if the function was called by TrackPopupMenu, continue
+ with the menu tracking. If not, stop it */
+ else
+ fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE);
+
+ break;
+
+ case WM_MOUSEMOVE:
+ /* the selected menu item must be changed every time */
+ /* the mouse moves. */
+
+ if (hmenu)
+ fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags );
+
+ } /* switch(msg.message) - mouse */
+ }
+ else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST))
+ {
+ fRemove = TRUE; /* Keyboard messages are always removed */
+ switch(msg.message)
+ {
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ switch(msg.wParam)
+ {
+ case VK_MENU:
+ case VK_F10:
+ fEndMenu = TRUE;
+ break;
+
+ case VK_HOME:
+ case VK_END:
+ if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
+ {
+ MenuSelectItem(mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
+ MenuMoveSelection(mt.OwnerWnd, &MenuInfo, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV);
+ }
+ break;
+
+ case VK_UP:
+ case VK_DOWN: /* If on menu bar, pull-down the menu */
+ if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
+ {
+ if (!(MenuInfo.fFlags & MNF_POPUP))
+ {
+ if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
+ mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags);
+ }
+ else /* otherwise try to move selection */
+ MenuMoveSelection(mt.OwnerWnd, &MenuInfo, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT );
+ }
+ break;
+
+ case VK_LEFT:
+ MenuKeyLeft( &mt, wFlags );
+ break;
+
+ case VK_RIGHT:
+ MenuKeyRight( &mt, wFlags );
+ break;
+
+ case VK_ESCAPE:
+ fEndMenu = MenuKeyEscape(&mt, wFlags);
+ break;
+
+ case VK_F1:
+ {
+ HELPINFO hi;
+ hi.cbSize = sizeof(HELPINFO);
+ hi.iContextType = HELPINFO_MENUITEM;
+ if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu))
+ {
+ if (MenuInfo.iItem == NO_SELECTED_ITEM)
+ hi.iCtrlId = 0;
+ else
+ {
+ MenuInitRosMenuItemInfo(&ItemInfo);
+ if (MenuGetRosMenuItemInfo(MenuInfo.Self,
+ MenuInfo.iItem,
+ &ItemInfo))
+ {
+ hi.iCtrlId = ItemInfo.wID;
+ }
+ else
+ {
+ hi.iCtrlId = 0;
+ }
+ MenuCleanupRosMenuItemInfo(&ItemInfo);
+ }
+ }
+ hi.hItemHandle = hmenu;
+ hi.dwContextId = MenuInfo.dwContextHelpID;
+ hi.MousePos = msg.pt;
+ SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi);
+ break;
+ }
+
+ default:
+ break;
+ }
+ break; /* WM_KEYDOWN */
+
+ case WM_CHAR:
+ case WM_SYSCHAR:
+ {
+ UINT pos;
+
+ if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break;
+ if (msg.wParam == L'\r' || msg.wParam == L' ')
+ {
+ executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
+ fEndMenu = (executedMenuId != -2);
+ break;
+ }
+
+ /* Hack to avoid control chars. */
+ /* We will find a better way real soon... */
+ if (msg.wParam < 32) break;
+
+ pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE);
+ if (pos == (UINT)-2) fEndMenu = TRUE;
+ else if (pos == (UINT)-1) MessageBeep(0);
+ else
+ {
+ MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos, TRUE, 0);
+ executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags);
+ fEndMenu = (executedMenuId != -2);
+ }
+ }
+ break;
+ } /* switch(msg.message) - kbd */
+ }
+ else
+ {
+ PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
+ DispatchMessageW( &msg );
+ continue;
+ }
+
+ if (!fEndMenu) fRemove = TRUE;
+
+ /* finally remove message from the queue */
+
+ if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) )
+ PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
+ else mt.TrackFlags &= ~TF_SKIPREMOVE;
+ }
+
+ NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL);
+ SetCapture(NULL); /* release the capture */
+
+ /* If dropdown is still painted and the close box is clicked on
+ then the menu will be destroyed as part of the DispatchMessage above.
+ This will then invalidate the menu handle in mt.hTopMenu. We should
+ check for this first. */
+ if( IsMenu( mt.TopMenu ) )
+ {
+ if (IsWindow(mt.OwnerWnd))
+ {
+ if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
+ {
+ MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags);
+
+ if (MenuInfo.fFlags & MNF_POPUP)
+ {
+ IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0);
+ DestroyWindow(MenuInfo.Wnd);
+ MenuInfo.Wnd = NULL;
+ MenuSetRosMenuInfo(&MenuInfo);
+
+ if (!(wFlags & TPM_NONOTIFY))
+ SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu,
+ MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) );
+ }
+ MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 );
+ }
+
+ SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 );
+ }
+
+ /* Reset the variable for hiding menu */
+ if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu))
+ {
+ MenuInfo.TimeToHide = FALSE;
+ MenuSetRosMenuInfo(&MenuInfo);
+ }
+ }
+
+ /* The return value is only used by TrackPopupMenu */
+ if (!(wFlags & TPM_RETURNCMD)) return TRUE;
+ if (executedMenuId == -1) executedMenuId = 0;
+ return executedMenuId;
+}
+
+/***********************************************************************
+ * MenuInitTracking
+ */
+static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags)
+{
+ ROSMENUINFO MenuInfo;
+
+ TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu);
+
+ HideCaret(0);
+
+ /* This makes the menus of applications built with Delphi work.
+ * It also enables menus to be displayed in more than one window,
+ * but there are some bugs left that need to be fixed in this case.
+ */
+ if (!bPopup && (MenuGetRosMenuInfo(&MenuInfo, hMenu)))
+ {
+ MenuInfo.Wnd = hWnd;
+ MenuSetRosMenuInfo(&MenuInfo);
+ }
+ //if (!bPopup) menu->hWnd = hWnd;
+ if (!top_popup)
+ {
+ top_popup = MenuInfo.Wnd;//menu->hWnd;
+ top_popup_hmenu = hMenu;
+ }
+
+ fEndMenu = FALSE;
+
+ /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */
+ if (!(wFlags & TPM_NONOTIFY))
+ SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 );
+
+ SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION );
+
+ if (!(wFlags & TPM_NONOTIFY))
+ {
+ SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 );
+ /* If an app changed/recreated menu bar entries in WM_INITMENU
+ * menu sizes will be recalculated once the menu created/shown.
+ */
+
+ if (!MenuInfo.cyMenu)
+ {
+ /* app changed/recreated menu bar entries in WM_INITMENU
+ Recalculate menu sizes else clicks will not work */
+ SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE |
+ SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
+
+ }
+ }
+
+ IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART,
+ hWnd,
+ MenuInfo.fFlags & MNF_SYSDESKMN ? OBJID_SYSMENU : OBJID_MENU,
+ CHILDID_SELF, 0);
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuExitTracking
+ */
+static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup)
+{
+ TRACE("hwnd=%p\n", hWnd);
+
+ IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0);
+ SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 );
+ ShowCaret(0);
+ top_popup = 0;
+ top_popup_hmenu = NULL;
+ return TRUE;
+}
+
+/***********************************************************************
+ * MenuTrackMouseMenuBar
+ *
+ * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand().
+ */
+VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt)
+{
+ HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd);
+ UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON;
+
+ TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y);
+
+ if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL;
+ if (IsMenu(hMenu))
+ {
+ /* map point to parent client coordinates */
+ HWND Parent = GetAncestor(hWnd, GA_PARENT );
+ if (Parent != GetDesktopWindow())
+ {
+ ScreenToClient(Parent, &pt);
+ }
+
+ MenuInitTracking(hWnd, hMenu, FALSE, wFlags);
+ MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL);
+ MenuExitTracking(hWnd, FALSE);
+ }
+}
+
+/***********************************************************************
+ * MenuTrackKbdMenuBar
+ *
+ * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand().
+ */
+VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar)
+{
+ UINT uItem = NO_SELECTED_ITEM;
+ HMENU hTrackMenu;
+ ROSMENUINFO MenuInfo;
+ UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON;
+
+ TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar);
+
+ /* find window that has a menu */
+
+ while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) &
+ (WS_CHILD | WS_POPUP)) != WS_CHILD))
+ if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return;
+
+ /* check if we have to track a system menu */
+
+ hTrackMenu = GetMenu( hwnd );
+ if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' )
+ {
+ if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return;
+ hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE);
+ uItem = 0;
+ wParam |= HTSYSMENU; /* prevent item lookup */
+ }
+
+ if (!IsMenu( hTrackMenu )) return;
+
+ MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags );
+
+ /* fetch the window menu again, it may have changed */
+ hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd );
+
+ if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu))
+ {
+ goto track_menu;
+ }
+
+ if( wChar && wChar != ' ' )
+ {
+ uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) );
+ if ( uItem >= (UINT)(-2) )
+ {
+ if( uItem == (UINT)(-1) ) MessageBeep(0);
+ /* schedule end of menu tracking */
+ wFlags |= TF_ENDMENU;
+ goto track_menu;
+ }
+ }
+
+ MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 );
+
+ if (!(wParam & HTSYSMENU) || wChar == ' ')
+ {
+ if( uItem == NO_SELECTED_ITEM )
+ MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT );
+ else
+ PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 );
+ }
+
+track_menu:
+ MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL );
+ MenuExitTracking( hwnd, FALSE );
+}
+
+/**********************************************************************
+ * TrackPopupMenuEx (USER32.@)
+ */
+BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, int x, int y,
+ HWND hWnd, LPTPMPARAMS lpTpm)
+{
+ BOOL ret = FALSE;
+ MENU *menu;
+
+ TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n",
+ hMenu, wFlags, x, y, hWnd, lpTpm,
+ lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" );
+
+ /* Parameter check */
+ /* FIXME: this check is performed several times, here and in the called
+ functions. That could be optimized */
+ if (!(menu = MENU_GetMenu( hMenu )))
+ {
+ SetLastError( ERROR_INVALID_MENU_HANDLE );
+ return FALSE;
+ }
+
+ if (IsWindow(menu->hWnd))
+ {
+ SetLastError( ERROR_POPUP_ALREADY_ACTIVE );
+ return FALSE;
+ }
+
+ if (MENU_InitPopup( hWnd, hMenu, wFlags ))
+ {
+ MenuInitTracking(hWnd, hMenu, TRUE, wFlags);
+
+ /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */
+ if (!(wFlags & TPM_NONOTIFY))
+ SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM) hMenu, 0);
+
+ if (MenuShowPopup(hWnd, hMenu, 0, wFlags, x, y, 0, 0 ))
+ ret = MenuTrackMenu(hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd,
+ lpTpm ? &lpTpm->rcExclude : NULL);
+ MenuExitTracking(hWnd, TRUE);
+
+ if (menu->hWnd)
+ {
+ ROSMENUINFO MenuInfo;
+ if (IsWindow( menu->hWnd )) // wine hack around this with their destroy function.
+ DestroyWindow( menu->hWnd ); // Fix wrong error return.
+ //menu->hWnd = 0;
+ MenuGetRosMenuInfo(&MenuInfo, menu->head.h);
+ MenuInfo.Wnd = 0;
+ MenuSetRosMenuInfo(&MenuInfo);
+
+ if (!(wFlags & TPM_NONOTIFY))
+ SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu,
+ MAKELPARAM(0, IS_SYSTEM_MENU(menu)) );
+ }
+ }
+ return ret;
+}
+
+/**********************************************************************
+ * TrackPopupMenu (USER32.@)
+ */
+BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y,
+ int Reserved, HWND Wnd, CONST RECT *Rect)
+{
+ return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL);
+}
+
+/**********************************************************************
+ * MENU_mnu2mnuii
+ *
+ * Uses flags, id and text ptr, passed by InsertMenu() and
+ * ModifyMenu() to setup a MenuItemInfo structure.
+ */
+static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, LPMENUITEMINFOW pmii, BOOL Unicode)
+{
+ RtlZeroMemory( pmii, sizeof( MENUITEMINFOW));
+ pmii->cbSize = sizeof( MENUITEMINFOW);
+ pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE;
+ /* setting bitmap clears text and vice versa */
+ if( IS_STRING_ITEM(flags)) {
+ pmii->fMask |= MIIM_STRING | MIIM_BITMAP;
+ if( !str)
+ flags |= MF_SEPARATOR;
+ /* Item beginning with a backspace is a help item */
+ /* FIXME: wrong place, this is only true in win16 */
+ else
+ {
+ if (Unicode)
+ {
+ if (*str == '\b')
+ {
+ flags |= MF_HELP;
+ str++;
+ }
+ }
+ else
+ {
+ LPCSTR NewItemA = (LPCSTR) str;
+ if (*NewItemA == '\b')
+ {
+ flags |= MF_HELP;
+ NewItemA++;
+ str = (LPCWSTR) NewItemA;
+ }
+ TRACE("A cch %d\n",strlen(NewItemA));
+ }
+ }
+ pmii->dwTypeData = (LPWSTR)str;
+ } else if( flags & MFT_BITMAP){
+ pmii->fMask |= MIIM_BITMAP | MIIM_STRING;
+ pmii->hbmpItem = (HBITMAP)str;
+ }
+ if( flags & MF_OWNERDRAW){
+ pmii->fMask |= MIIM_DATA;
+ pmii->dwItemData = (ULONG_PTR) str;
+ }
+ if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) {
+ pmii->fMask |= MIIM_SUBMENU;
+ pmii->hSubMenu = (HMENU)id;
+ }
+ if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED;
+ pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT;
+ pmii->fType = flags & MENUITEMINFO_TYPE_MASK;
+ pmii->wID = (UINT)id;
+}
+
+/**********************************************************************
+ * MENU_NormalizeMenuItemInfoStruct
+ *
+ * Helper for SetMenuItemInfo and InsertMenuItemInfo:
+ * check, copy and extend the MENUITEMINFO struct from the version that the application
+ * supplied to the version used by wine source. */
+static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in,
+ MENUITEMINFOW *pmii_out )
+{
+ /* do we recognize the size? */
+ if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) &&
+ pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) {
+ SetLastError( ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ /* copy the fields that we have */
+ memcpy( pmii_out, pmii_in, pmii_in->cbSize);
+ /* if the hbmpItem member is missing then extend */
+ if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) {
+ pmii_out->cbSize = sizeof( MENUITEMINFOW);
+ pmii_out->hbmpItem = NULL;
+ }
+ /* test for invalid bit combinations */
+ if( (pmii_out->fMask & MIIM_TYPE &&
+ pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) ||
+ (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) {
+ ERR("invalid combination of fMask bits used\n");
+ /* this does not happen on Win9x/ME */
+ SetLastError( ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ /* convert old style (MIIM_TYPE) to the new and keep the old one too */
+ if( pmii_out->fMask & MIIM_TYPE){
+ pmii_out->fMask |= MIIM_FTYPE;
+ if( IS_STRING_ITEM(pmii_out->fType)){
+ pmii_out->fMask |= MIIM_STRING;
+ } else if( (pmii_out->fType) & MFT_BITMAP){
+ pmii_out->fMask |= MIIM_BITMAP;
+ pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData));
+ }
+ }
+ if (pmii_out->fMask & MIIM_FTYPE )
+ {
+ pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK;
+ pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK;
+ }
+ if (pmii_out->fMask & MIIM_STATE)
+ /* Other menu items having MFS_DEFAULT are not converted
+ to normal items */
+ pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK;
+
+ return TRUE;
+}
+
+BOOL
+MenuInit(VOID)
+{
+ NONCLIENTMETRICSW ncm;
+
+ /* get the menu font */
+ if(!hMenuFont || !hMenuFontBold)
+ {
+ ncm.cbSize = sizeof(ncm);
+ if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
+ {
+ ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n");
+ return FALSE;
+ }
+
+ hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont);
+ if(hMenuFont == NULL)
+ {
+ ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n");
+ return FALSE;
+ }
+
+ ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000);
+ hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont);
+ if(hMenuFontBold == NULL)
+ {
+ ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n");
+ DeleteObject(hMenuFont);
+ hMenuFont = NULL;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+VOID
+MenuCleanup(VOID)
+{
+ if (hMenuFont)
+ {
+ DeleteObject(hMenuFont);
+ hMenuFont = NULL;
+ }
+
+ if (hMenuFontBold)
+ {
+ DeleteObject(hMenuFontBold);
+ hMenuFontBold = NULL;
+ }
+}
+
+NTSTATUS WINAPI
+User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength)
+{
- Result = (LRESULT)LoadMenuW( Common->hModule,
- IS_INTRESOURCE(Common->MenuName[0]) ?
- MAKEINTRESOURCE(Common->MenuName[0]) :
- (LPCWSTR)&Common->MenuName);
++ LRESULT Result = 0;
++
++ // Will be converted to load bitmaps for OBMI!
+
+ return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS));
+}
+
+NTSTATUS WINAPI
+User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength)
+{
+ PLOADMENU_CALLBACK_ARGUMENTS Common;
+ LRESULT Result;
+
+ Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments;
+
- /* copy the text string, it wll be one or the other */
++ Result = (LRESULT)LoadMenuW( Common->hModule, Common->InterSource ? MAKEINTRESOURCE(Common->InterSource) : (LPCWSTR)&Common->MenuName);
+
+ return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS);
+}
+
+
+/* FUNCTIONS *****************************************************************/
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+AppendMenuA(HMENU hMenu,
+ UINT uFlags,
+ UINT_PTR uIDNewItem,
+ LPCSTR lpNewItem)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING UnicodeString;
+ BOOL res;
+
+ RtlInitUnicodeString(&UnicodeString, 0);
+
+ MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
+
- /* copy the text string, it wll be one or the other */
++ /* copy the text string, it will be one or the other */
+ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
+ {
+ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
+ {
+ SetLastError (ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ mii.dwTypeData = UnicodeString.Buffer;
+ mii.cch = UnicodeString.Length / sizeof(WCHAR);
+ }
+ else
+ {
+ TRACE("AMA Handle bitmaps\n");
+ }
+ ////// Answer a question, why a -1? To hunt for the end of the item list. Get it, to Append?
+ res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &UnicodeString);
+ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+AppendMenuW(HMENU hMenu,
+ UINT uFlags,
+ UINT_PTR uIDNewItem,
+ LPCWSTR lpNewItem)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING MenuText;
+ BOOL res;
+
+ RtlInitUnicodeString(&MenuText, 0);
+
+ MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
+
- /* copy the text string, it wll be one or the other */
++ /* copy the text string, it will be one or the other */
+ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
+ {
+ RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
+ mii.dwTypeData = MenuText.Buffer;
+ mii.cch = MenuText.Length / sizeof(WCHAR);
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &MenuText);
+ return res;
+}
+
+/*
+ * @implemented
+ */
+DWORD WINAPI
+CheckMenuItem(HMENU hmenu,
+ UINT uIDCheckItem,
+ UINT uCheck)
+{
+ PMENU pMenu;
+ PITEM item;
+ DWORD Ret;
+
+ if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
+ return -1;
+
+ if (!(item = MENU_FindItem( &hmenu, &uIDCheckItem, uCheck ))) return -1;
+
+ Ret = item->fState & MFS_CHECKED;
+ if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked...
+
+ return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck);
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+CheckMenuRadioItem(HMENU hMenu,
+ UINT first,
+ UINT last,
+ UINT check,
+ UINT bypos)
+{
+ BOOL done = FALSE;
+ UINT i;
+ PITEM mi_first = NULL, mi_check;
+ HMENU m_first, m_check;
+ MENUITEMINFOW mii;
+ mii.cbSize = sizeof( mii);
+
+ for (i = first; i <= last; i++)
+ {
+ UINT pos = i;
+
+ if (!mi_first)
+ {
+ m_first = hMenu;
+ mi_first = MENU_FindItem(&m_first, &pos, bypos);
+ if (!mi_first) continue;
+ mi_check = mi_first;
+ m_check = m_first;
+ }
+ else
+ {
+ m_check = hMenu;
+ mi_check = MENU_FindItem(&m_check, &pos, bypos);
+ if (!mi_check) continue;
+ }
+
+ if (m_first != m_check) continue;
+ if (mi_check->fType == MFT_SEPARATOR) continue;
+
+ if (i == check)
+ {
+ if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED))
+ {
+ mii.fMask = MIIM_FTYPE | MIIM_STATE;
+ mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK;
+ mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED;
+ NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
+ }
+ done = TRUE;
+ }
+ else
+ {
+ /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */
+ if (mi_check->fState & MFS_CHECKED)
+ {
+ mii.fMask = MIIM_STATE;
+ mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED;
+ NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL);
+ }
+ }
+ }
+ return done;
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+CreateMenu(VOID)
+{
+ MenuLoadBitmaps();
+ return NtUserxCreateMenu();
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+CreatePopupMenu(VOID)
+{
+ MenuLoadBitmaps();
+ return NtUserxCreatePopupMenu();
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+DrawMenuBar(HWND hWnd)
+{
+ return NtUserxDrawMenuBar(hWnd);
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+EnableMenuItem(HMENU hMenu,
+ UINT uIDEnableItem,
+ UINT uEnable)
+{
+ return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable);
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+EndMenu(VOID)
+{
+ GUITHREADINFO guii;
+ guii.cbSize = sizeof(GUITHREADINFO);
+ if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner)
+ {
+ if (!fEndMenu &&
+ top_popup &&
+ guii.hwndMenuOwner != top_popup )
+ {
+ ERR("Capture GUI pti hWnd does not match top_popup!\n");
+ }
+ }
+
+ /* if we are in the menu code, and it is active */
+ if (!fEndMenu && top_popup)
+ {
+ /* terminate the menu handling code */
+ fEndMenu = TRUE;
+
+ /* needs to be posted to wakeup the internal menu handler */
+ /* which will now terminate the menu, in the event that */
+ /* the main window was minimized, or lost focus, so we */
+ /* don't end up with an orphaned menu */
+ PostMessageW( top_popup, WM_CANCELMODE, 0, 0);
+ }
+ return fEndMenu;
+}
+
+BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID,
+ UINT wHilite )
+{
+ ROSMENUINFO MenuInfo;
+ TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite);
+ // Force bits to be set call server side....
+ // This alone works and passes all the menu test_menu_hilitemenuitem tests.
+ if (!NtUserHiliteMenuItem(hWnd, hMenu, wItemID, wHilite)) return FALSE;
+ // Without the above call we fail 3 out of the wine failed todo tests, see CORE-7967
+ // Now redraw menu.
+ if (MenuGetRosMenuInfo(&MenuInfo, hMenu))
+ {
+ if (MenuInfo.iItem == wItemID) return TRUE;
+ MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 );
+ MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 );
+ }
+ return TRUE; // Always returns TRUE!
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+GetMenu(HWND hWnd)
+{
+ PWND Wnd = ValidateHwnd(hWnd);
+
+ if (!Wnd)
+ return NULL;
+
+ return UlongToHandle(Wnd->IDMenu);
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi )
+{
+ BOOL Ret;
+ Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi);
+ // Reason to move to server side!!!!!
+ if (!Ret) return Ret;
+ // EL HAXZO!!!
+ pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu;
+ if (!idItem)
+ {
+ pmbi->fFocused = pmbi->fBarFocused;
+ }
+
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+LONG WINAPI
+GetMenuCheckMarkDimensions(VOID)
+{
+ return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK),
+ GetSystemMetrics(SM_CYMENUCHECK)));
+}
+
+/*
+ * @implemented
+ */
+DWORD
+WINAPI
+GetMenuContextHelpId(HMENU hmenu)
+{
+ PMENU pMenu;
+ if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
+ return pMenu->dwContextHelpId;
+ return 0;
+}
+
+/*
+ * @implemented
+ */
+UINT WINAPI
+GetMenuDefaultItem(HMENU hMenu,
+ UINT fByPos,
+ UINT gmdiFlags)
+{
+ PMENU pMenu;
+ DWORD gismc = 0;
+ if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU)))
+ return (UINT)-1;
+
+ return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc);
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+GetMenuInfo(HMENU hmenu,
+ LPMENUINFO lpcmi)
+{
+ PMENU pMenu;
+
+ if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU)))
+ return FALSE;
+
+ if (lpcmi->fMask & MIM_BACKGROUND)
+ lpcmi->hbrBack = pMenu->hbrBack;
+
+ if (lpcmi->fMask & MIM_HELPID)
+ lpcmi->dwContextHelpID = pMenu->dwContextHelpId;
+
+ if (lpcmi->fMask & MIM_MAXHEIGHT)
+ lpcmi->cyMax = pMenu->cyMax;
+
+ if (lpcmi->fMask & MIM_MENUDATA)
+ lpcmi->dwMenuData = pMenu->dwMenuData;
+
+ if (lpcmi->fMask & MIM_STYLE)
+ lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK;
+
+ return TRUE;
+}
+
+/*
+ * @implemented
+ */
+int WINAPI
+GetMenuItemCount(HMENU hmenu)
+{
+ PMENU pMenu;
+ if ((pMenu = ValidateHandle(hmenu, TYPE_MENU)))
+ return pMenu->cItems;
+ return -1;
+}
+
+/*
+ * @implemented
+ */
+UINT WINAPI
+GetMenuItemID(HMENU hMenu,
+ int nPos)
+{
+ ITEM * lpmi;
+ if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1;
+ if (lpmi->spSubMenu) return -1;
+ return lpmi->wID;
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+GetMenuItemInfoA(
+ HMENU hmenu,
+ UINT item,
+ BOOL bypos,
+ LPMENUITEMINFOA lpmii)
+{
+ BOOL ret;
+ MENUITEMINFOA mii;
+
+ if( lpmii->cbSize != sizeof( mii) &&
+ lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
+ {
+ SetLastError( ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ memcpy( &mii, lpmii, lpmii->cbSize);
+ mii.cbSize = sizeof( mii);
+ ret = GetMenuItemInfo_common (hmenu,
+ item,
+ bypos,
+ (LPMENUITEMINFOW)&mii,
+ FALSE);
+ mii.cbSize = lpmii->cbSize;
+ memcpy( lpmii, &mii, mii.cbSize);
+ return ret;
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+GetMenuItemInfoW(
+ HMENU hMenu,
+ UINT Item,
+ BOOL bypos,
+ LPMENUITEMINFOW lpmii)
+{
+ BOOL ret;
+ MENUITEMINFOW mii;
+ if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem))
+ {
+ SetLastError( ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ memcpy( &mii, lpmii, lpmii->cbSize);
+ mii.cbSize = sizeof( mii);
+ ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE);
+ mii.cbSize = lpmii->cbSize;
+ memcpy( lpmii, &mii, mii.cbSize);
+ return ret;
+}
+
+/*
+ * @implemented
+ */
+UINT
+WINAPI
+GetMenuState(
+ HMENU hMenu,
+ UINT uId,
+ UINT uFlags)
+{
+ PITEM pItem;
+ UINT Type = 0;
+ TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags);
+ if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1;
+
+ if (!pItem->Xlpstr && pItem->hbmp) Type = MFT_BITMAP;
+
+ if (pItem->spSubMenu)
+ {
+ PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
+ HMENU hsubmenu = UserHMGetHandle(pSubMenu);
+ if (!IsMenu(hsubmenu)) return (UINT)-1;
+ else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|Type) & 0xff);
+ }
+ else
+ return (pItem->fType | pItem->fState | Type);
+}
+
+/*
+ * @implemented
+ */
+int
+WINAPI
+GetMenuStringA(
+ HMENU hMenu,
+ UINT uIDItem,
+ LPSTR lpString,
+ int nMaxCount,
+ UINT uFlag)
+{
+ ITEM *item;
+ LPWSTR text;
+ ////// wine Code, seems to be faster.
+ TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
+
+ if (lpString && nMaxCount) lpString[0] = '\0';
+
+ if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
+ {
+ SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
+ return 0;
+ }
+
+ text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
+
+ if (!text) return 0;
+ if (!lpString || !nMaxCount) return WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL );
+ if (!WideCharToMultiByte( CP_ACP, 0, text, -1, lpString, nMaxCount, NULL, NULL ))
+ lpString[nMaxCount-1] = 0;
+ TRACE("A returning %s\n", lpString);
+ return strlen(lpString);
+}
+
+/*
+ * @implemented
+ */
+int
+WINAPI
+GetMenuStringW(
+ HMENU hMenu,
+ UINT uIDItem,
+ LPWSTR lpString,
+ int nMaxCount,
+ UINT uFlag)
+{
+ ITEM *item;
+ LPWSTR text;
+
+ TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag );
+
+ if (lpString && nMaxCount) lpString[0] = '\0';
+
+ if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag )))
+ {
+ SetLastError( ERROR_MENU_ITEM_NOT_FOUND);
+ return 0;
+ }
+
+ text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL;
+
+ if (!lpString || !nMaxCount) return text ? strlenW(text) : 0;
+ if( !(text))
+ {
+ lpString[0] = 0;
+ return 0;
+ }
+ lstrcpynW( lpString, text, nMaxCount );
+ TRACE("W returning %S\n", lpString);
+ return strlenW(lpString);
+}
+
+/*
+ * @implemented
+ */
+HMENU
+WINAPI
+GetSubMenu(
+ HMENU hMenu,
+ int nPos)
+{
+ PITEM pItem;
+ if (!(pItem = MENU_FindItem( &hMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
+
+ if (pItem->spSubMenu)
+ {
+ PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu);
+ HMENU hsubmenu = UserHMGetHandle(pSubMenu);
+ if (IsMenu(hsubmenu)) return hsubmenu;
+ }
+ return NULL;
+}
+
+/*
+ * @implemented
+ */
+HMENU
+WINAPI
+GetSystemMenu(
+ HWND hWnd,
+ BOOL bRevert)
+{
+ HMENU TopMenu;
+
+ TopMenu = NtUserGetSystemMenu(hWnd, bRevert);
+
+ return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+InsertMenuA(
+ HMENU hMenu,
+ UINT uPosition,
+ UINT uFlags,
+ UINT_PTR uIDNewItem,
+ LPCSTR lpNewItem)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING UnicodeString;
+ BOOL res;
+
+ RtlInitUnicodeString(&UnicodeString, 0);
+
+ MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
+
- /* copy the text string, it wll be one or the other */
++ /* copy the text string, it will be one or the other */
+ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
+ {
+ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
+ {
+ SetLastError (ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ mii.dwTypeData = UnicodeString.Buffer;
+ mii.cch = UnicodeString.Length / sizeof(WCHAR);
+ }
+ else
+ {
+ TRACE("Handle bitmaps\n");
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &UnicodeString);
+ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+InsertMenuItemA(
+ HMENU hMenu,
+ UINT uItem,
+ BOOL fByPosition,
+ LPCMENUITEMINFOA lpmii)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING UnicodeString;
+ BOOL res;
+
+ TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
+
+ RtlInitUnicodeString(&UnicodeString, 0);
+
+ if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
+
+ /* copy the text string */
+ if (((mii.fMask & MIIM_STRING) ||
+ ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
+ && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
+ {
+ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
+ {
+ SetLastError (ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ mii.dwTypeData = UnicodeString.Buffer;
+ mii.cch = UnicodeString.Length / sizeof(WCHAR);
+ }
+ else
+ {
+ TRACE("Handle bitmaps\n");
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &UnicodeString);
+ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+InsertMenuItemW(
+ HMENU hMenu,
+ UINT uItem,
+ BOOL fByPosition,
+ LPCMENUITEMINFOW lpmii)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING MenuText;
+ BOOL res = FALSE;
+
+ /* while we could just pass 'lpmii' to win32k, we make a copy so that
+ if a bad user passes bad data, we crash his process instead of the
+ entire kernel */
+
+ TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
+
+ RtlInitUnicodeString(&MenuText, 0);
+
+ if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
+
+ /* copy the text string */
+ if (((mii.fMask & MIIM_STRING) ||
+ ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
+ && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
+ {
+ RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData);
+ mii.dwTypeData = MenuText.Buffer;
+ mii.cch = MenuText.Length / sizeof(WCHAR);
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &MenuText);
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+InsertMenuW(
+ HMENU hMenu,
+ UINT uPosition,
+ UINT uFlags,
+ UINT_PTR uIDNewItem,
+ LPCWSTR lpNewItem)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING MenuText;
+ BOOL res;
+
+ RtlInitUnicodeString(&MenuText, 0);
+
+ MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
+
- /* copy the text string, it wll be one or the other */
++ /* copy the text string, it will be one or the other */
+ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
+ {
+ RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
+ mii.dwTypeData = MenuText.Buffer;
+ mii.cch = MenuText.Length / sizeof(WCHAR);
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &MenuText);
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+IsMenu(
+ HMENU Menu)
+{
+ if (ValidateHandle(Menu, TYPE_MENU)) return TRUE;
+ return FALSE;
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+LoadMenuA(HINSTANCE hInstance,
+ LPCSTR lpMenuName)
+{
+ HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4));
+ if (Resource == NULL)
+ {
+ return(NULL);
+ }
+ return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource)));
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate)
+{
+ return(LoadMenuIndirectW(lpMenuTemplate));
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate)
+{
+ HMENU hMenu;
+ WORD version, offset;
+ LPCSTR p = (LPCSTR)lpMenuTemplate;
+
+ version = GET_WORD(p);
+ p += sizeof(WORD);
+
+ switch (version)
+ {
+ case 0: /* standard format is version of 0 */
+ offset = GET_WORD(p);
+ p += sizeof(WORD) + offset;
+ if (!(hMenu = CreateMenu())) return 0;
+ if (!MENU_ParseResource(p, hMenu))
+ {
+ DestroyMenu(hMenu);
+ return 0;
+ }
+ return hMenu;
+ case 1: /* extended format is version of 1 */
+ offset = GET_WORD(p);
+ p += sizeof(WORD) + offset;
+ if (!(hMenu = CreateMenu())) return 0;
+ if (!MENUEX_ParseResource(p, hMenu))
+ {
+ DestroyMenu( hMenu );
+ return 0;
+ }
+ return hMenu;
+ default:
+ ERR("Menu template version %d not supported.\n", version);
+ return 0;
+ }
+}
+
+/*
+ * @implemented
+ */
+HMENU WINAPI
+LoadMenuW(HINSTANCE hInstance,
+ LPCWSTR lpMenuName)
+{
+ HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU);
+ if (Resource == NULL)
+ {
+ return(NULL);
+ }
+ return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource)));
+}
+
+/*
+ * @implemented
+ */
+int
+WINAPI
+MenuItemFromPoint(
+ HWND hWnd,
+ HMENU hMenu,
+ POINT ptScreen)
+{
+ return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+ModifyMenuA(
+ HMENU hMenu,
+ UINT uPosition,
+ UINT uFlags,
+ UINT_PTR uIDNewItem,
+ LPCSTR lpNewItem)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING UnicodeString;
+ BOOL res;
+
+ RtlInitUnicodeString(&UnicodeString, 0);
+
+ MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE);
+
- /* copy the text string, it wll be one or the other */
++ /* copy the text string, it will be one or the other */
+ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
+ {
+ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
+ {
+ SetLastError (ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ mii.dwTypeData = UnicodeString.Buffer;
+ mii.cch = UnicodeString.Length / sizeof(WCHAR);
+ }
+ else
+ {
+ TRACE("Handle bitmaps\n");
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &UnicodeString);
+ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString );
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+ModifyMenuW(
+ HMENU hMenu,
+ UINT uPosition,
+ UINT uFlags,
+ UINT_PTR uIDNewItem,
+ LPCWSTR lpNewItem)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING MenuText;
+ BOOL res;
+
+ RtlInitUnicodeString(&MenuText, 0);
+
+ MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE);
+
++ /* copy the text string, it will be one or the other */
+ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData)
+ {
+ RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData);
+ mii.dwTypeData = MenuText.Buffer;
+ mii.cch = MenuText.Length / sizeof(WCHAR);
+ }
+ else
+ {
+ TRACE("Handle bitmaps\n");
+ }
+ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &MenuText);
+ return res;
+}
+
+/*
+ * @implemented
+ */
+BOOL WINAPI
+SetMenu(HWND hWnd,
+ HMENU hMenu)
+{
+ return NtUserSetMenu(hWnd, hMenu, TRUE);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetMenuInfo(
+ HMENU hmenu,
+ LPCMENUINFO lpcmi)
+{
+ ROSMENUINFO mi;
+ BOOL res = FALSE;
+
+ if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO)))
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return res;
+ }
+
+ memcpy(&mi, lpcmi, sizeof(MENUINFO));
+ return NtUserThunkedMenuInfo(hmenu, (LPCMENUINFO)&mi);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetMenuItemBitmaps(
+ HMENU hMenu,
+ UINT uPosition,
+ UINT uFlags,
+ HBITMAP hBitmapUnchecked,
+ HBITMAP hBitmapChecked)
+{
+ MENUITEMINFOW uItem;
+ memset ( &uItem, 0, sizeof(uItem) );
+ uItem.cbSize = sizeof(MENUITEMINFOW);
+ uItem.fMask = MIIM_CHECKMARKS;
+ uItem.hbmpUnchecked = hBitmapUnchecked;
+ uItem.hbmpChecked = hBitmapChecked;
+ return SetMenuItemInfoW(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), &uItem);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetMenuItemInfoA(
+ HMENU hmenu,
+ UINT item,
+ BOOL bypos,
+ LPCMENUITEMINFOA lpmii)
+{
+ MENUITEMINFOW mii;
+ UNICODE_STRING UnicodeString;
+ BOOL Ret;
+
+ TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii);
+
+ RtlInitUnicodeString(&UnicodeString, 0);
+
+ if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE;
+/*
+ * MIIM_STRING == good
+ * MIIM_TYPE & MFT_STRING == good
+ * MIIM_STRING & MFT_STRING == good
+ * MIIM_STRING & MFT_OWNERDRAW == good
+ */
+ if (((mii.fMask & MIIM_STRING) ||
+ ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING)))
+ && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) )
+ {
+ /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */
+ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData))
+ {
+ SetLastError (ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ mii.dwTypeData = UnicodeString.Buffer;
+ mii.cch = UnicodeString.Length / sizeof(WCHAR);
+ }
+ else
+ {
+ UnicodeString.Buffer = NULL;
+ }
+ Ret = NtUserThunkedMenuItemInfo(hmenu, item, bypos, FALSE, &mii, &UnicodeString);
+ if (UnicodeString.Buffer != NULL) RtlFreeUnicodeString(&UnicodeString);
+ return Ret;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetMenuItemInfoW(
+ HMENU hMenu,
+ UINT uItem,
+ BOOL fByPosition,
+ LPCMENUITEMINFOW lpmii)
+{
+ MENUITEMINFOW MenuItemInfoW;
+ UNICODE_STRING UnicodeString;
+ BOOL Ret;
+
+ TRACE("hmenu %p, item %u, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii);
+
+ RtlInitUnicodeString(&UnicodeString, 0);
+
+ if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &MenuItemInfoW )) return FALSE;
+
+ if (((MenuItemInfoW.fMask & MIIM_STRING) ||
+ ((MenuItemInfoW.fMask & MIIM_TYPE) &&
+ (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING)))
+ && MenuItemInfoW.dwTypeData && !(GdiValidateHandle((HGDIOBJ)MenuItemInfoW.dwTypeData)) )
+ {
+ RtlInitUnicodeString(&UnicodeString, (PCWSTR)MenuItemInfoW.dwTypeData);
+ MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData);
+ }
+ Ret = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, FALSE, &MenuItemInfoW, &UnicodeString);
+
+ return Ret;
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+SetSystemMenu (
+ HWND hwnd,
+ HMENU hMenu)
+{
+ if(!hwnd)
+ {
+ SetLastError(ERROR_INVALID_WINDOW_HANDLE);
+ return FALSE;
+ }
+ if(!hMenu)
+ {
+ SetLastError(ERROR_INVALID_MENU_HANDLE);
+ return FALSE;
+ }
+ return NtUserSetSystemMenu(hwnd, hMenu);
+}
+
+//
+// Example for the Win32/User32 rewrite.
+// Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24
+//
+//
+BOOL
+WINAPI
+NEWTrackPopupMenu(
+ HMENU Menu,
+ UINT Flags,
+ int x,
+ int y,
+ int Reserved,
+ HWND Wnd,
+ CONST RECT *Rect)
+{
+ return NtUserTrackPopupMenuEx( Menu,
+ Flags,
+ x,
+ y,
+ Wnd,
+ NULL); // LPTPMPARAMS is null
+}
+
+/*
+ * @unimplemented
+ */
+BOOL
+WINAPI
+MenuWindowProcA(
+ HWND hWnd,
+ ULONG_PTR Result,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+{
+ if ( Msg < WM_USER)
+ {
+ LRESULT lResult;
+ lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam );
+ if (Result)
+ {
+ Result = (ULONG_PTR)lResult;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE);
+}
+
+/*
+ * @unimplemented
+ */
+BOOL
+WINAPI
+MenuWindowProcW(
+ HWND hWnd,
+ ULONG_PTR Result,
+ UINT Msg,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+{
+ if ( Msg < WM_USER)
+ {
+ LRESULT lResult;
+ lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam );
+ if (Result)
+ {
+ Result = (ULONG_PTR)lResult;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE);
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+ChangeMenuW(
+ HMENU hMenu,
+ UINT cmd,
+ LPCWSTR lpszNewItem,
+ UINT cmdInsert,
+ UINT flags)
+{
+ /*
+ FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
+ for MF_DELETE. We should check the parameters for all others
+ MF_* actions also (anybody got a doc on ChangeMenu?).
+ */
+
+ switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
+ {
+ case MF_APPEND :
+ return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
+
+ case MF_DELETE :
+ return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
+
+ case MF_CHANGE :
+ return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
+
+ case MF_REMOVE :
+ return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
+ flags &~ MF_REMOVE);
+
+ default : /* MF_INSERT */
+ return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem);
+ };
+}
+
+/*
+ * @implemented
+ */
+BOOL
+WINAPI
+ChangeMenuA(
+ HMENU hMenu,
+ UINT cmd,
+ LPCSTR lpszNewItem,
+ UINT cmdInsert,
+ UINT flags)
+{
+ /*
+ FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert
+ for MF_DELETE. We should check the parameters for all others
+ MF_* actions also (anybody got a doc on ChangeMenu?).
+ */
+
+ switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT))
+ {
+ case MF_APPEND :
+ return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem);
+
+ case MF_DELETE :
+ return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE);
+
+ case MF_CHANGE :
+ return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem);
+
+ case MF_REMOVE :
+ return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert,
+ flags &~ MF_REMOVE);
+
+ default : /* MF_INSERT */
+ return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem);
+ };
+}
+