--- /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;