--- /dev/null
- dwCreationFlags |= CREATE_NEW_CONSOLE;
+/*
+ * 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++;
+ }
+ }
+
+ 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;
+ 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;
+}
+
+/*************************************************************************
+ * 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 (RegOpenKeyExW(HKEY_CLASSES_ROOT, classname, 0, 0x02000000, &hkeyClass))
+ return SE_ERR_NOASSOC;
+ if (!HCR_GetDefaultVerbW(hkeyClass, lpVerb, verb, sizeof(verb) / sizeof(verb[0])))
+ return SE_ERR_NOASSOC;
+ RegCloseKey(hkeyClass);
+
+ /* Looking for ...buffer\shell\<verb>\command */
+ wcscat(classname, L"\\shell\\");
+ wcscat(classname, verb);
+ wcscat(classname, wCommand);
+
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, classname, command,
+ &commandlen) == ERROR_SUCCESS)
+ {
+ commandlen /= sizeof(WCHAR);
+ 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 */
+ tmp = strstrW(classname, wCommand);
+ tmp[0] = '\0';
+ 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
+ * 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)
+ */
+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 */
+ 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;
+ /* 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))
+ {
+ 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 '.') */
+ /* 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 */
+ if (RegQueryValueW(HKEY_CLASSES_ROOT, extension, classname,
+ &classnamelen) == ERROR_SUCCESS)
+ {
+ classnamelen /= sizeof(WCHAR);
+ if (classnamelen == sizeof(classname) / sizeof(WCHAR))
+ classnamelen--;
+
+ classname[classnamelen] = '\0';
+ TRACE("File type: %s\n", debugstr_w(classname));
+ }
+ else
+ {
+ *classname = '\0';
+ }
+ }
+
+ if (*classname)
+ {
+ /* 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);
+ 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;
+ 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);
+ 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
+ {
+ 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;
+ }
-
++
+ 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 */
+ }
+ 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;
+ }
+ }
+
+ 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;
+ 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)
+{
+ 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;
+ UINT_PTR rslt;
-
++
+ /* FIXME: remove following block when SHELL_quote_and_execute supports hkeyClass parameter */
+ if (cmask != SEE_MASK_CLASSNAME)
+ {
+ 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);
+ }
++
+ 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 */
+ 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;
+}
+
+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);
+ }
+
+ TRACE("%s/%s => %s/%s\n", debugstr_w(wszApplicationName), debugstr_w(psei->lpVerb), debugstr_w(wszQuotedCmd), debugstr_w(wszKeyname));
+
+ 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;
+}
+
+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));
+ /* Looking for ...<protocol>\shell\<lpVerb>\command */
+ len = iSize + lstrlenW(wShell) + lstrlenW(wCommand) + 1;
+ 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);
+ strcatW(lpstrProtocol, psei->lpVerb && *psei->lpVerb ? psei->lpVerb : wszOpen);
+ strcatW(lpstrProtocol, wCommand);
+
+ retval = execute_from_key(lpstrProtocol, lpFile, NULL, psei->lpParameters,
+ wcmd, execfunc, psei, psei_out);
+ HeapFree(GetProcessHeap(), 0, lpstrProtocol);
+ return retval;
+}
+
+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]
+ */
+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;
+ 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)
++ DBG_UNREFERENCED_LOCAL_VARIABLE(Info);
+ 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 );
+ }
+
+ /* convert file URLs */
+ if (UrlIsFileUrlW(sei_tmp.lpFile))
+ {
+ LPWSTR buf;
+ 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;
+ }
+
+ HeapFree(GetProcessHeap(), 0, wszApplicationName);
+ dwApplicationNameLen = lstrlenW(buf) + 1;
+ wszApplicationName = buf;
+ sei_tmp.lpFile = wszApplicationName;
+ }
+ else /* or expand environment strings (not both!) */
+ {
+ len = ExpandEnvironmentStringsW(sei_tmp.lpFile, NULL, 0);
+ if (len > 0)
+ {
+ LPWSTR buf;
+ 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;
+ }
+ }
+
+ lpFile = sei_tmp.lpFile;
+ }
+ }
+ else
+ 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_FindExecutable(sei_tmp.lpDirectory, lpFile, sei_tmp.lpVerb, wcmd, wcmdLen, wszKeyname, &env, (LPITEMIDLIST)sei_tmp.lpIDList, sei_tmp.lpParameters);
+ if (retval > 32) /* Found */
+ {
+ 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,
+ 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 */
+ {
+ 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))
+ {
+ /* 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)
++ DBG_UNREFERENCED_LOCAL_VARIABLE(Info);
+ 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]
+ */
+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",
+ 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;
+ 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
+ * WINSHELLAPI HINSTANCE APIENTRY ShellExecuteW(HWND hwnd, LPCWSTR lpVerb,
+ * LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd);
+ */
+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;
+ 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.
+ */
+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
-
+/*
+ * COPYRIGHT: See COPYING in the top level directory
+ * PROJECT: ReactOS Win32k subsystem
+ * PURPOSE: Focus functions
+ * FILE: subsystems/win32/win32k/ntuser/focus.c
+ * PROGRAMER: ReactOS Team
+ */
+
+#include <win32k.h>
+DBG_DEFAULT_CHANNEL(UserFocus);
+
+PUSER_MESSAGE_QUEUE gpqForeground = NULL;
+PUSER_MESSAGE_QUEUE gpqForegroundPrev = NULL;
+PTHREADINFO gptiForeground = NULL;
+PPROCESSINFO gppiLockSFW = NULL;
+ULONG guSFWLockCount = 0; // Rule #8, No menus are active. So should be zero.
+PTHREADINFO ptiLastInput = NULL;
+
+/*
+ Check locking of a process or one or more menus are active.
+*/
+BOOL FASTCALL
+IsFGLocked(VOID)
+{
+ return (gppiLockSFW || guSFWLockCount);
+}
+
+HWND FASTCALL
+IntGetCaptureWindow(VOID)
+{
+ PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
+ return ( ForegroundQueue ? (ForegroundQueue->spwndCapture ? UserHMGetHandle(ForegroundQueue->spwndCapture) : 0) : 0);
+}
+
+HWND FASTCALL
+IntGetThreadFocusWindow(VOID)
+{
+ PTHREADINFO pti;
+ PUSER_MESSAGE_QUEUE ThreadQueue;
+
+ pti = PsGetCurrentThreadWin32Thread();
+ ThreadQueue = pti->MessageQueue;
+ if (!ThreadQueue)
+ return NULL;
+ return ThreadQueue->spwndFocus ? UserHMGetHandle(ThreadQueue->spwndFocus) : 0;
+}
+
+BOOL FASTCALL
+co_IntSendDeactivateMessages(HWND hWndPrev, HWND hWnd)
+{
+ USER_REFERENCE_ENTRY RefPrev;
+ PWND WndPrev;
+ BOOL Ret = TRUE;
+
+ if (hWndPrev && (WndPrev = ValidateHwndNoErr(hWndPrev)))
+ {
+ UserRefObjectCo(WndPrev, &RefPrev);
+
+ if (co_IntSendMessageNoWait(hWndPrev, WM_NCACTIVATE, FALSE, 0)) //(LPARAM)hWnd))
+ {
+ co_IntSendMessageNoWait(hWndPrev, WM_ACTIVATE,
+ MAKEWPARAM(WA_INACTIVE, WndPrev->style & WS_MINIMIZE),
+ (LPARAM)hWnd);
+
+ if (WndPrev)
+ WndPrev->state &= ~WNDS_ACTIVEFRAME;
+ }
+ else
+ {
+ ERR("Application is keeping itself Active to prevent the change!\n");
+ Ret = FALSE;
+ }
+
+ UserDerefObjectCo(WndPrev);
+ }
+ return Ret;
+}
+
+BOOL FASTCALL
+co_IntMakeWindowActive(PWND Window)
+{
+ PWND spwndOwner;
+ if (VerifyWnd(Window))
+ { // Set last active for window and it's owner.
+ spwndOwner = Window;
+ while (spwndOwner->spwndOwner)
+ {
+ spwndOwner = spwndOwner->spwndOwner;
+ }
+ spwndOwner->spwndLastActive = Window;
+ return TRUE;
+ }
+ ERR("MakeWindowActive Failed!\n");
+ return FALSE;
+}
+
+BOOL FASTCALL
+co_IntSendActivateMessages(PWND WindowPrev, PWND Window, BOOL MouseActivate, BOOL Async)
+{
+ USER_REFERENCE_ENTRY Ref, RefPrev;
+ HANDLE OldTID, NewTID;
+ PTHREADINFO pti, ptiOld, ptiNew;
+ BOOL InAAPM = FALSE;
++
++ //ERR("SendActivateMessages\n");
++
+ pti = PsGetCurrentThreadWin32Thread();
+
+ if (Window)
+ {
- ////
+ UserRefObjectCo(Window, &Ref);
+
+ if (WindowPrev) UserRefObjectCo(WindowPrev, &RefPrev);
+
+ /* Send palette messages */
+ if (gpsi->PUSIFlags & PUSIF_PALETTEDISPLAY &&
+ //co_IntPostOrSendMessage(UserHMGetHandle(Window), WM_QUERYNEWPALETTE, 0, 0))
+ co_IntSendMessage(UserHMGetHandle(Window), WM_QUERYNEWPALETTE, 0, 0))
+ {
+ UserSendNotifyMessage( HWND_BROADCAST,
+ WM_PALETTEISCHANGING,
+ (WPARAM)UserHMGetHandle(Window),
+ 0);
+ }
+ //// Fixes CORE-6434.
+ if (!(Window->style & WS_CHILD))
+ {
+ PWND pwndTemp = co_GetDesktopWindow(Window)->spwndChild;
+
+ while (pwndTemp && !(pwndTemp->style & WS_VISIBLE)) pwndTemp = pwndTemp->spwndNext;
+
+ if (Window != pwndTemp || (WindowPrev && !IntIsWindowVisible(WindowPrev)))
+ {
+ if (!Async || pti->MessageQueue == gpqForeground)
+ {
+ UINT flags = SWP_NOSIZE | SWP_NOMOVE;
+ if (Window == pwndTemp) flags |= SWP_NOACTIVATE;
+ //ERR("co_IntSendActivateMessages SetWindowPos! Async %d pti Q == FGQ %d\n",Async,pti->MessageQueue == gpqForeground);
+ co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, flags);
+ }
+ }
+ }
+ ////
+ //// CORE-1161 and CORE-6651
+ if (Window->spwndPrev)
+ {
+ HWND *phwndTopLevel, *phwndCurrent;
+ PWND pwndCurrent, pwndDesktop;
+
+ pwndDesktop = UserGetDesktopWindow();
+ if (Window->spwndParent == pwndDesktop )
+ {
+ phwndTopLevel = IntWinListChildren(pwndDesktop);
+ phwndCurrent = phwndTopLevel;
+ while(*phwndCurrent)
+ {
+ pwndCurrent = UserGetWindowObject(*phwndCurrent);
+
+ if (pwndCurrent && pwndCurrent->spwndOwner == Window )
+ {
+ co_WinPosSetWindowPos(pwndCurrent, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE);
+ }
+ phwndCurrent++;
+ }
+ ExFreePool(phwndTopLevel);
+ }
+ }
- OwnerWnd = Child->spwndOwner;
++ }
++ ////
++ OldTID = WindowPrev ? IntGetWndThreadId(WindowPrev) : NULL;
++ NewTID = Window ? IntGetWndThreadId(Window) : NULL;
++ ptiOld = WindowPrev ? WindowPrev->head.pti : NULL;
++ ptiNew = Window ? Window->head.pti : NULL;
++
++ //ERR("SendActivateMessage Old -> %x, New -> %x\n", OldTID, NewTID);
++
++ if (!(pti->TIF_flags & TIF_INACTIVATEAPPMSG) &&
++ (!WindowPrev || OldTID != NewTID) )
++ {
++ PWND cWindow;
++ HWND *List, *phWnd;
++
++ List = IntWinListChildren(UserGetDesktopWindow());
++ if ( List )
++ {
++ if ( OldTID )
++ {
++ ptiOld->TIF_flags |= TIF_INACTIVATEAPPMSG;
++ // Note: Do not set pci flags, this does crash!
++ for (phWnd = List; *phWnd; ++phWnd)
++ {
++ cWindow = ValidateHwndNoErr(*phWnd);
++ if (cWindow && cWindow->head.pti == ptiOld)
++ { // FALSE if the window is being deactivated,
++ // ThreadId that owns the window being activated.
++ //ERR("SendActivateMessage Old\n");
++ co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, FALSE, (LPARAM)NewTID);
++ }
++ }
++ ptiOld->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
++ }
++ if ( NewTID )
++ { //// Prevents a resource crash due to reentrance!
++ InAAPM = TRUE;
++ pti->TIF_flags |= TIF_INACTIVATEAPPMSG;
++ ////
++ for (phWnd = List; *phWnd; ++phWnd)
++ {
++ cWindow = ValidateHwndNoErr(*phWnd);
++ if (cWindow && cWindow->head.pti == ptiNew)
++ { // TRUE if the window is being activated,
++ // ThreadId that owns the window being deactivated.
++ //ERR("SendActivateMessage New\n");
++ co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, TRUE, (LPARAM)OldTID);
++ }
++ }
++ }
++ ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
++ }
++ }
+
++ if (Window)
++ {
+ if (WindowPrev)
+ UserDerefObjectCo(WindowPrev); // Now allow the previous window to die.
+
+ if (Window->state & WNDS_ACTIVEFRAME)
+ { // If already active frame do not allow NCPaint.
+ //ERR("SendActivateMessage Is Active Frame!\n");
+ Window->state |= WNDS_NONCPAINT;
+ }
+
+ if (Window->style & WS_MINIMIZE)
+ {
+ TRACE("Widow was minimized\n");
+ }
+
+ co_IntMakeWindowActive(Window);
+
+ co_IntSendMessageNoWait( UserHMGetHandle(Window),
+ WM_NCACTIVATE,
+ (WPARAM)(Window == (gpqForeground ? gpqForeground->spwndActive : NULL)),
+ 0); //(LPARAM)hWndPrev);
+
+ co_IntSendMessageNoWait( UserHMGetHandle(Window),
+ WM_ACTIVATE,
+ MAKEWPARAM(MouseActivate ? WA_CLICKACTIVE : WA_ACTIVE, Window->style & WS_MINIMIZE),
+ (LPARAM)(WindowPrev ? UserHMGetHandle(WindowPrev) : 0));
+
+ if (Window->spwndParent == UserGetDesktopWindow() &&
+ Window->spwndOwner == NULL &&
+ (!(Window->ExStyle & WS_EX_TOOLWINDOW) ||
+ (Window->ExStyle & WS_EX_APPWINDOW)))
+ {
+ // FIXME lParam; The value is TRUE if the window is in full-screen mode, or FALSE otherwise.
+ co_IntShellHookNotify(HSHELL_WINDOWACTIVATED, (WPARAM) UserHMGetHandle(Window), FALSE);
+ }
+ else
+ {
+ co_IntShellHookNotify(HSHELL_WINDOWACTIVATED, 0, FALSE);
+ }
+
+ Window->state &= ~WNDS_NONCPAINT;
+
+ UserDerefObjectCo(Window);
+ }
+
+ OldTID = WindowPrev ? IntGetWndThreadId(WindowPrev) : NULL;
+ NewTID = Window ? IntGetWndThreadId(Window) : NULL;
+ ptiOld = WindowPrev ? WindowPrev->head.pti : NULL;
+ ptiNew = Window ? Window->head.pti : NULL;
+
+ //ERR("SendActivateMessage WindowPrev -> %x, Old -> %x, New -> %x\n", WindowPrev, OldTID, NewTID);
+
+ if (!(pti->TIF_flags & TIF_INACTIVATEAPPMSG) &&
+ (OldTID != NewTID) )
+ {
+ PWND cWindow;
+ HWND *List, *phWnd;
+
+ List = IntWinListChildren(UserGetDesktopWindow());
+ if ( List )
+ {
+ if ( OldTID )
+ {
+ ptiOld->TIF_flags |= TIF_INACTIVATEAPPMSG;
+ // Note: Do not set pci flags, this does crash!
+ for (phWnd = List; *phWnd; ++phWnd)
+ {
+ cWindow = ValidateHwndNoErr(*phWnd);
+ if (cWindow && cWindow->head.pti == ptiOld)
+ { // FALSE if the window is being deactivated,
+ // ThreadId that owns the window being activated.
+ co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, FALSE, (LPARAM)NewTID);
+ }
+ }
+ ptiOld->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
+ }
+ if ( NewTID )
+ { //// Prevents a resource crash due to reentrance!
+ InAAPM = TRUE;
+ pti->TIF_flags |= TIF_INACTIVATEAPPMSG;
+ ////
+ for (phWnd = List; *phWnd; ++phWnd)
+ {
+ cWindow = ValidateHwndNoErr(*phWnd);
+ if (cWindow && cWindow->head.pti == ptiNew)
+ { // TRUE if the window is being activated,
+ // ThreadId that owns the window being deactivated.
+ co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, TRUE, (LPARAM)OldTID);
+ }
+ }
+ }
+ ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
+ }
+ }
+ return InAAPM;
+}
+
+VOID FASTCALL
+IntSendFocusMessages( PTHREADINFO pti, PWND pWnd)
+{
+ PWND pWndPrev;
+ PUSER_MESSAGE_QUEUE ThreadQueue = pti->MessageQueue; // Queue can change...
+
+ ThreadQueue->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
+ if (!pWnd && ThreadQueue->spwndActive)
+ {
+ ThreadQueue->QF_flags |= QF_FOCUSNULLSINCEACTIVE;
+ }
+
+ pWndPrev = ThreadQueue->spwndFocus;
+
+ /* check if the specified window can be set in the input data of a given queue */
+ if (!pWnd || ThreadQueue == pWnd->head.pti->MessageQueue)
+ /* set the current thread focus window */
+ ThreadQueue->spwndFocus = pWnd;
+
+ if (pWnd)
+ {
+ if (pWndPrev)
+ {
+ //co_IntPostOrSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, (WPARAM)UserHMGetHandle(pWnd), 0);
+ co_IntSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, (WPARAM)UserHMGetHandle(pWnd), 0);
+ }
+ if (ThreadQueue->spwndFocus == pWnd)
+ {
+ IntNotifyWinEvent(EVENT_OBJECT_FOCUS, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
+ //co_IntPostOrSendMessage(UserHMGetHandle(pWnd), WM_SETFOCUS, (WPARAM)(pWndPrev ? UserHMGetHandle(pWndPrev) : NULL), 0);
+ co_IntSendMessage(UserHMGetHandle(pWnd), WM_SETFOCUS, (WPARAM)(pWndPrev ? UserHMGetHandle(pWndPrev) : NULL), 0);
+ }
+ }
+ else
+ {
+ if (pWndPrev)
+ {
+ IntNotifyWinEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
+ //co_IntPostOrSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, 0, 0);
+ co_IntSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, 0, 0);
+ }
+ }
+}
+
+VOID FASTCALL
+FindRemoveAsyncMsg(PWND Wnd, WPARAM wParam)
+{
+ PTHREADINFO pti;
+ PUSER_SENT_MESSAGE Message;
+ PLIST_ENTRY Entry;
+
+ if (!Wnd) return;
+
+ pti = Wnd->head.pti;
+
+ if (!IsListEmpty(&pti->SentMessagesListHead))
+ {
+ // Scan sent queue messages to see if we received async messages.
+ Entry = pti->SentMessagesListHead.Flink;
+ Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
+ do
+ {
+ if (IsListEmpty(Entry)) return;
+ if (!Message) return;
+ Entry = Message->ListEntry.Flink;
+
+ if (Message->Msg.message == WM_ASYNC_SETACTIVEWINDOW &&
+ Message->Msg.hwnd == UserHMGetHandle(Wnd) &&
+ Message->Msg.wParam == wParam )
+ {
+ ERR("ASYNC SAW: Found one in the Sent Msg Queue! %p Activate/Deactivate %d\n", Message->Msg.hwnd,!!wParam);
+ RemoveEntryList(&Message->ListEntry); // Purge the entry.
+ ExFreePoolWithTag(Message, TAG_USRMSG);
+ }
+ Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
+ }
+ while (Entry != &pti->SentMessagesListHead);
+ }
+}
+
+BOOL FASTCALL
+ToggleFGActivate(PTHREADINFO pti)
+{
+ BOOL Ret;
+ PPROCESSINFO ppi = pti->ppi;
+
+ Ret = !!(pti->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE);
+ if (Ret)
+ {
+ pti->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
+ }
+ else
+ Ret = !!(ppi->W32PF_flags & W32PF_ALLOWFOREGROUNDACTIVATE);
+
+ if (Ret)
+ ppi->W32PF_flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
+ //ERR("ToggleFGActivate is %d\n",Ret);
+ return Ret;
+}
+
+BOOL FASTCALL
+IsAllowedFGActive(PTHREADINFO pti, PWND Wnd)
+{
+ // Not allowed if one or more,,
+ if (!ToggleFGActivate(pti) || // bits not set,
+ pti->rpdesk != gpdeskInputDesktop || // not current Desktop,
+ pti->MessageQueue == gpqForeground || // if already the queue foreground,
+ IsFGLocked() || // foreground is locked,
+ Wnd->ExStyle & WS_EX_NOACTIVATE ) // or,,, does not become the foreground window when the user clicks it.
+ {
+ return FALSE;
+ }
+ //ERR("IsAllowedFGActive is TRUE\n");
+ return TRUE;
+}
+
+/*
+ Can the system force foreground from one or more conditions.
+ */
+BOOL FASTCALL
+CanForceFG(PPROCESSINFO ppi)
+{
+ if (!ptiLastInput ||
+ ptiLastInput->ppi == ppi ||
+ !gptiForeground ||
+ gptiForeground->ppi == ppi ||
+ ppi->W32PF_flags & (W32PF_ALLOWFOREGROUNDACTIVATE | W32PF_SETFOREGROUNDALLOWED) ||
+ gppiInputProvider == ppi ||
+ !gpqForeground
+ ) return TRUE;
+ //ERR("CanForceFG is FALSE\n");
+ return FALSE;
+}
+
+/*
+ MSDN:
+ The system restricts which processes can set the foreground window. A process
+ can set the foreground window only if one of the following conditions is true:
+
+ * The process is the foreground process.
+ * The process was started by the foreground process.
+ * The process received the last input event.
+ * There is no foreground process.
+ * The foreground process is being debugged.
+ * The foreground is not locked (see LockSetForegroundWindow).
+ * The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
+ * No menus are active.
+*/
+static
+BOOL FASTCALL
+co_IntSetForegroundAndFocusWindow(
+ _In_ PWND Wnd,
+ _In_ BOOL MouseActivate)
+{
+ HWND hWnd = Wnd ? UserHMGetHandle(Wnd) : NULL;
+ HWND hWndPrev = NULL;
+ PWND pWndPrev = NULL;
+ PUSER_MESSAGE_QUEUE PrevForegroundQueue;
+ PTHREADINFO pti;
+ BOOL fgRet = FALSE, Ret = FALSE;
+
+ if (Wnd) ASSERT_REFS_CO(Wnd);
+
+ //ERR("SetForegroundAndFocusWindow(%x, %s)\n", hWnd, (MouseActivate ? "TRUE" : "FALSE"));
+
+ PrevForegroundQueue = IntGetFocusMessageQueue(); // Use this active desktop.
+ pti = PsGetCurrentThreadWin32Thread();
+
+ if (PrevForegroundQueue)
+ { // Same Window Q as foreground just do active.
+ if (Wnd && Wnd->head.pti->MessageQueue == PrevForegroundQueue)
+ {
+ //ERR("Same Window Q as foreground just do active.\n");
+ if (pti->MessageQueue == PrevForegroundQueue)
+ { // Same WQ and TQ go active.
+ //ERR("Same WQ and TQ go active.\n");
+ Ret = co_IntSetActiveWindow(Wnd, MouseActivate, TRUE, FALSE);
+ }
+ else if (Wnd->head.pti->MessageQueue->spwndActive == Wnd)
+ { // Same WQ and it is active.
+ //ERR("Same WQ and it is active.\n");
+ Ret = TRUE;
+ }
+ else
+ { // Same WQ as FG but not the same TQ send active.
+ //ERR("Same WQ as FG but not the same TQ send active.\n");
+ co_IntSendMessage(hWnd, WM_ASYNC_SETACTIVEWINDOW, (WPARAM)Wnd, (LPARAM)MouseActivate );
+ Ret = TRUE;
+ }
+ return Ret;
+ }
+
+ hWndPrev = PrevForegroundQueue->spwndActive ? UserHMGetHandle(PrevForegroundQueue->spwndActive) : 0;
+ pWndPrev = PrevForegroundQueue->spwndActive;
+ }
+
+ if ( (( !IsFGLocked() || pti->ppi == gppiInputProvider ) &&
+ ( CanForceFG(pti->ppi) || pti->TIF_flags & (TIF_SYSTEMTHREAD|TIF_CSRSSTHREAD|TIF_ALLOWFOREGROUNDACTIVATE) )) ||
+ pti->ppi == ppiScrnSaver
+ )
+ {
+
+ //ToggleFGActivate(pti); // win.c line 2662 fail
+ if (Wnd)
+ {
+ IntSetFocusMessageQueue(Wnd->head.pti->MessageQueue);
+ gptiForeground = Wnd->head.pti;
+ //ERR("Set Foreground pti 0x%p Q 0x%p hWnd 0x%p\n",Wnd->head.pti, Wnd->head.pti->MessageQueue,Wnd->head.h);
+ }
+ else
+ {
+ IntSetFocusMessageQueue(NULL);
+ gptiForeground = NULL;
+ //ERR("Set Foreground pti 0x0 Q 0x0 hWnd 0x0\n");
+ }
+/*
+ Henri Verbeet,
+ What happens is that we get the WM_WINE_SETACTIVEWINDOW message sent by the
+ other thread after we already changed the foreground window back to our own
+ window.
+ */
+ //ERR("SFAFW: 1\n");
+ FindRemoveAsyncMsg(Wnd, 0); // Do this to fix test_SFW todos!
+
+ fgRet = TRUE;
+ }
+
+ // Fix FG Bounce with regedit.
+ if (hWndPrev != hWnd )
+ {
+ if (PrevForegroundQueue &&
+ fgRet &&
+ PrevForegroundQueue->spwndActive)
+ {
+ //ERR("SFGW: Send NULL to 0x%x\n",hWndPrev);
+ if (pti->MessageQueue == PrevForegroundQueue)
+ {
+ //ERR("SFGW: TI same as Prev TI\n");
+ co_IntSetActiveWindow(NULL, FALSE, TRUE, FALSE);
+ }
+ else if (pWndPrev)
+ {
+ //ERR("SFGW Deactivate: TI not same as Prev TI\n");
+ // No real reason to wait here.
+ co_IntSendMessageNoWait(hWndPrev, WM_ASYNC_SETACTIVEWINDOW, 0, 0 );
+ }
+ }
+ }
+
+ if (!Wnd) return FALSE; // Always return false.
+
+ if (pti->MessageQueue == Wnd->head.pti->MessageQueue)
+ {
+ //ERR("Same PQ and WQ go active.\n");
+ Ret = co_IntSetActiveWindow(Wnd, MouseActivate, TRUE, FALSE);
+ }
+ else if (Wnd->head.pti->MessageQueue->spwndActive == Wnd)
+ {
+ //ERR("Same Active and Wnd.\n");
+ Ret = TRUE;
+ }
+ else
+ {
+ //ERR("Activate Not same PQ and WQ and Wnd.\n");
+ co_IntSendMessageNoWait(hWnd, WM_ASYNC_SETACTIVEWINDOW, (WPARAM)Wnd, (LPARAM)MouseActivate );
+ Ret = TRUE;
+ }
+ return Ret && fgRet;
+}
+
+/*
+ Revision 7888, activate modal dialog when clicking on a disabled window.
+*/
+HWND FASTCALL
+IntFindChildWindowToOwner(PWND Root, PWND Owner)
+{
+ HWND Ret;
+ PWND Child, OwnerWnd;
+
+ for(Child = Root->spwndChild; Child; Child = Child->spwndNext)
+ {
++ OwnerWnd = Child->spwndOwner;
+ if(!OwnerWnd)
+ continue;
+
+ if(OwnerWnd == Owner)
+ {
+ Ret = Child->head.h;
+ return Ret;
+ }
+ }
+ return NULL;
+}
+
+BOOL FASTCALL
+co_IntMouseActivateWindow(PWND Wnd)
+{
+ HWND Top;
+ USER_REFERENCE_ENTRY Ref;
+ ASSERT_REFS_CO(Wnd);
+
+ if (Wnd->style & WS_DISABLED)
+ {
+ BOOL Ret;
+ PWND TopWnd;
+ PWND DesktopWindow = UserGetDesktopWindow();
+ if (DesktopWindow)
+ {
+ ERR("Window Diabled\n");
+ Top = IntFindChildWindowToOwner(DesktopWindow, Wnd);
+ if ((TopWnd = ValidateHwndNoErr(Top)))
+ {
+ UserRefObjectCo(TopWnd, &Ref);
+ Ret = co_IntMouseActivateWindow(TopWnd);
+ UserDerefObjectCo(TopWnd);
+
+ return Ret;
+ }
+ }
+ return FALSE;
+ }
+ ERR("Mouse Active\n");
+ co_IntSetForegroundAndFocusWindow(Wnd, TRUE);
+ return TRUE;
+}
+
+BOOL FASTCALL
+co_IntSetActiveWindow(PWND Wnd OPTIONAL, BOOL bMouse, BOOL bFocus, BOOL Async)
+{
+ PTHREADINFO pti;
+ PUSER_MESSAGE_QUEUE ThreadQueue;
+ PWND pWndChg, WndPrev; // State changes.
+ HWND hWndPrev;
+ HWND hWnd = 0;
+ BOOL InAAPM;
+ CBTACTIVATESTRUCT cbt;
+ //ERR("co_IntSetActiveWindow 1\n");
+ if (Wnd)
+ {
+ ASSERT_REFS_CO(Wnd);
+ hWnd = UserHMGetHandle(Wnd);
+ if ((Wnd->style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
+ if (Wnd == UserGetDesktopWindow()) return FALSE;
+ //ERR("co_IntSetActiveWindow 1a hWnd 0x%p\n",hWnd);
+ }
+
+ //ERR("co_IntSetActiveWindow 2\n");
+ pti = PsGetCurrentThreadWin32Thread();
+ ThreadQueue = pti->MessageQueue;
+ ASSERT(ThreadQueue != 0);
+
+ hWndPrev = ThreadQueue->spwndActive ? UserHMGetHandle(ThreadQueue->spwndActive) : NULL;
+
+ pWndChg = ThreadQueue->spwndActive; // Keep to notify of a preemptive switch.
+
+ while (Wnd)
+ {
+ BOOL Ret, DoFG, AllowFG;
+
+ if (Wnd->state & WNDS_BEINGACTIVATED) return TRUE;
+
+ if (ThreadQueue == Wnd->head.pti->MessageQueue)
+ {
+ if (IsAllowedFGActive(pti, Wnd))
+ {
+ DoFG = TRUE;
+ }
+ else
+ {
+ //ERR("co_IntSetActiveWindow 3 Go Out!\n");
+ break;
+ }
+ AllowFG = !pti->cVisWindows; // Nothing is visable.
+ //ERR("co_IntSetActiveWindow 3a DoFG = %d AllowFG = %d\n",DoFG,AllowFG);
+ }
+ else //if (ThreadQueue != Wnd->head.pti->MessageQueue)
+ {
+ //PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
+ // Rule 1 & 4, We are foreground so set this FG window or NULL foreground....
+ //if (!ForegroundQueue || ForegroundQueue == ThreadQueue)
+ if (!gpqForeground || gpqForeground == ThreadQueue)
+ {
+ DoFG = TRUE;
+ }
+ else
+ DoFG = FALSE;
+ if (DoFG)
+ {
+ if (pti->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE || pti->cVisWindows)
+ AllowFG = TRUE;
+ else
+ AllowFG = FALSE;
+ }
+ else
+ AllowFG = FALSE;
+ //ERR("co_IntSetActiveWindow 3b DoFG = %d AllowFG = %d\n",DoFG,AllowFG);
+ }
+ Ret = FALSE;
+ if (DoFG)
+ {
+ pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
+ //ERR("co_IntSetActiveWindow 3c FG set\n");
+ Ret = co_IntSetForegroundAndFocusWindow(Wnd, bMouse);
+ if (AllowFG)
+ {
+ pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
+ }
+ else
+ {
+ pti->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
+ }
+ }
+ return Ret;
+ }
+
+ /* Call CBT hook chain */
+ cbt.fMouse = bMouse;
+ cbt.hWndActive = hWndPrev;
+ if (co_HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hWnd, (LPARAM)&cbt))
+ {
+ ERR("SetActiveWindow: WH_CBT Call Hook return!\n");
+ return FALSE;
+ }
+
+ if ( ThreadQueue->spwndActive && ThreadQueue->spwndActive->state & WNDS_DESTROYED )
+ ThreadQueue->spwndActive = NULL;
+ else
+ ThreadQueue->spwndActivePrev = ThreadQueue->spwndActive;
+
+ WndPrev = ThreadQueue->spwndActive; // Keep to save changing active.
+
+ if (WndPrev)
+ {
+ if (ThreadQueue == gpqForeground) gpqForegroundPrev = ThreadQueue;
+ if (!co_IntSendDeactivateMessages(UserHMGetHandle(WndPrev), hWnd)) return FALSE;
+ }
+
+ WndPrev = ThreadQueue->spwndActive; // Again keep to save changing active.
+
+ // While in calling message proc or hook:
+ // Fail if a preemptive switch was made, current active not made previous,
+ // focus window is dead or no longer the same thread queue.
+ if ( ThreadQueue->spwndActivePrev != ThreadQueue->spwndActive ||
+ pWndChg != WndPrev ||
+ (Wnd && !VerifyWnd(Wnd)) ||
+ ThreadQueue != pti->MessageQueue )
+ {
+ ERR("SetActiveWindow: Summery ERROR, active state changed!\n");
+ return FALSE;
+ }
+
+ if (!WndPrev) ThreadQueue->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
+
+ if (Wnd) Wnd->state |= WNDS_BEINGACTIVATED;
+
+ IntNotifyWinEvent(EVENT_SYSTEM_FOREGROUND, Wnd, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI);
+ //// Breaks Atl-Esc/Tab via User32.
+ ////FindRemoveAsyncMsg(Wnd,(WPARAM)Wnd); // Clear out activate ASYNC messages.
+
+ /* check if the specified window can be set in the input data of a given queue */
+ if ( !Wnd || ThreadQueue == Wnd->head.pti->MessageQueue)
+ {
+ /* set the current thread active window */
+ ThreadQueue->spwndActive = Wnd;
+ }
+
+ WndPrev = VerifyWnd(ThreadQueue->spwndActivePrev); // Now should be set but verify it again.
+
+ InAAPM = co_IntSendActivateMessages(WndPrev, Wnd, bMouse, Async);
+
+ /* now change focus if necessary */
+ //// Fixes CORE-6452 allows setting focus on window.
+ if (bFocus && !(ThreadQueue->QF_flags & QF_FOCUSNULLSINCEACTIVE))
+ {
+ /* Do not change focus if the window is no longer active */
+ if (pti->MessageQueue->spwndActive != IntGetNonChildAncestor(pti->MessageQueue->spwndFocus))
+ {
+ PWND pWndSend = pti->MessageQueue->spwndActive;
+ // Clear focus if the active window is minimized.
+ if (pWndSend && pti->MessageQueue->spwndActive->style & WS_MINIMIZE) pWndSend = NULL;
+ // Send focus messages and if so, set the focus.
+ IntSendFocusMessages( pti, pWndSend);
+ }
+ }
+ ////
+ if (InAAPM)
+ {
+ pti->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
+ }
+
+ // FIXME: Used in the menu loop!!!
+ //ThreadQueue->QF_flags |= QF_ACTIVATIONCHANGE;
+
+ //ERR("co_IntSetActiveWindow Exit\n");
+ if (Wnd) Wnd->state &= ~WNDS_BEINGACTIVATED;
+ return (ThreadQueue->spwndActive == Wnd);
+}
+
+BOOL FASTCALL
+UserSetActiveWindow(PWND Wnd)
+{
+ if (Wnd) // Must have a window!
+ {
+ if ((Wnd->style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
+
+ return co_IntSetActiveWindow(Wnd, FALSE, TRUE, FALSE);
+ }
+ /*
+ Yes your eye are not deceiving you~!
+
+ First part of wines Win.c test_SetActiveWindow:
+
+ flush_events( TRUE );
+ ShowWindow(hwnd, SW_HIDE);
+ SetFocus(0);
+ SetActiveWindow(0);
+ check_wnd_state(0, 0, 0, 0); <-- This should pass if ShowWindow does it's job!!! As of 10/28/2012 it does!
+
+ */
+ return FALSE;
+}
+
+HWND FASTCALL
+co_UserSetFocus(PWND Window)
+{
+ HWND hWndPrev = 0;
+ PWND pwndTop;
+ PTHREADINFO pti;
+ PUSER_MESSAGE_QUEUE ThreadQueue;
+
+ if (Window)
+ ASSERT_REFS_CO(Window);
+
+ pti = PsGetCurrentThreadWin32Thread();
+ ThreadQueue = pti->MessageQueue;
+ ASSERT(ThreadQueue != 0);
+
+ TRACE("Enter SetFocus hWnd 0x%p pti 0x%p\n",Window ? UserHMGetHandle(Window) : 0, pti );
+
+ hWndPrev = ThreadQueue->spwndFocus ? UserHMGetHandle(ThreadQueue->spwndFocus) : 0;
+
+ if (Window != 0)
+ {
+ if (hWndPrev == UserHMGetHandle(Window))
+ {
+ return hWndPrev ? (IntIsWindow(hWndPrev) ? hWndPrev : 0) : 0; /* Nothing to do */
+ }
+
+ if (Window->head.pti->MessageQueue != ThreadQueue)
+ {
+ ERR("SetFocus Must have the same Q!\n");
+ return 0;
+ }
+
+ /* Check if we can set the focus to this window */
+ //// Fixes wine win test_SetParent both "todo" line 3710 and 3720...
+ for (pwndTop = Window; pwndTop; pwndTop = pwndTop->spwndParent)
+ {
+ if (pwndTop->style & (WS_MINIMIZED|WS_DISABLED)) return 0;
+ if ((pwndTop->style & (WS_POPUP|WS_CHILD)) != WS_CHILD) break;
+ }
+ ////
+ if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)Window->head.h, (LPARAM)hWndPrev))
+ {
+ ERR("SetFocus 1 WH_CBT Call Hook return!\n");
+ return 0;
+ }
+
+ /* Activate pwndTop if needed. */
+ if (pwndTop != ThreadQueue->spwndActive)
+ {
+ PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue(); // Keep it based on desktop.
+ if (ThreadQueue != ForegroundQueue &&am