2018-01-24 09:19:03 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Sebastian Lackner
|
|
|
|
*
|
|
|
|
* 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 "config.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
2018-11-22 13:12:07 +01:00
|
|
|
#define COBJMACROS
|
|
|
|
|
2018-01-24 09:19:03 +01:00
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
2018-01-24 09:19:04 +01:00
|
|
|
#include "wingdi.h"
|
|
|
|
#include "winuser.h"
|
2018-11-22 13:12:07 +01:00
|
|
|
#include "initguid.h"
|
|
|
|
#include "ocidl.h"
|
2018-01-24 09:19:03 +01:00
|
|
|
#include "shellscalingapi.h"
|
|
|
|
#include "wine/debug.h"
|
2018-11-26 09:53:12 +01:00
|
|
|
#include "wine/unicode.h"
|
2018-01-24 09:19:03 +01:00
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(shcore);
|
|
|
|
|
|
|
|
BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %u, %p)\n", instance, reason, reserved);
|
|
|
|
|
|
|
|
switch (reason)
|
|
|
|
{
|
|
|
|
case DLL_WINE_PREATTACH:
|
|
|
|
return FALSE; /* prefer native version */
|
|
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
DisableThreadLibraryCalls(instance);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI GetProcessDpiAwareness(HANDLE process, PROCESS_DPI_AWARENESS *value)
|
|
|
|
{
|
2018-04-11 15:46:06 +02:00
|
|
|
if (GetProcessDpiAwarenessInternal( process, (DPI_AWARENESS *)value )) return S_OK;
|
|
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
2018-01-24 09:19:03 +01:00
|
|
|
}
|
2018-01-24 09:19:04 +01:00
|
|
|
|
2018-01-24 09:19:05 +01:00
|
|
|
HRESULT WINAPI SetProcessDpiAwareness(PROCESS_DPI_AWARENESS value)
|
|
|
|
{
|
2018-04-11 15:46:06 +02:00
|
|
|
if (SetProcessDpiAwarenessInternal( value )) return S_OK;
|
|
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
2018-01-24 09:19:05 +01:00
|
|
|
}
|
|
|
|
|
2018-01-24 09:19:04 +01:00
|
|
|
HRESULT WINAPI GetDpiForMonitor(HMONITOR monitor, MONITOR_DPI_TYPE type, UINT *x, UINT *y)
|
|
|
|
{
|
2018-04-19 10:46:43 +02:00
|
|
|
if (GetDpiForMonitorInternal( monitor, type, x, y )) return S_OK;
|
|
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
2018-01-24 09:19:04 +01:00
|
|
|
}
|
2018-11-22 13:12:07 +01:00
|
|
|
|
|
|
|
HRESULT WINAPI _IStream_Read(IStream *stream, void *dest, ULONG size)
|
|
|
|
{
|
|
|
|
ULONG read;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %u)\n", stream, dest, size);
|
|
|
|
|
|
|
|
hr = IStream_Read(stream, dest, size, &read);
|
|
|
|
if (SUCCEEDED(hr) && read != size)
|
|
|
|
hr = E_FAIL;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI IStream_Reset(IStream *stream)
|
|
|
|
{
|
|
|
|
static const LARGE_INTEGER zero;
|
|
|
|
|
|
|
|
TRACE("(%p)\n", stream);
|
|
|
|
|
|
|
|
return IStream_Seek(stream, zero, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI IStream_Size(IStream *stream, ULARGE_INTEGER *size)
|
|
|
|
{
|
|
|
|
STATSTG statstg;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("(%p, %p)\n", stream, size);
|
|
|
|
|
|
|
|
memset(&statstg, 0, sizeof(statstg));
|
|
|
|
|
|
|
|
hr = IStream_Stat(stream, &statstg, STATFLAG_NONAME);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr) && size)
|
|
|
|
*size = statstg.cbSize;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI _IStream_Write(IStream *stream, const void *src, ULONG size)
|
|
|
|
{
|
|
|
|
ULONG written;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("(%p, %p, %u)\n", stream, src, size);
|
|
|
|
|
|
|
|
hr = IStream_Write(stream, src, size, &written);
|
|
|
|
if (SUCCEEDED(hr) && written != size)
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WINAPI IUnknown_AtomicRelease(IUnknown **obj)
|
|
|
|
{
|
|
|
|
TRACE("(%p)\n", obj);
|
|
|
|
|
|
|
|
if (!obj || !*obj)
|
|
|
|
return;
|
|
|
|
|
|
|
|
IUnknown_Release(*obj);
|
|
|
|
*obj = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI IUnknown_GetSite(IUnknown *unk, REFIID iid, void **site)
|
|
|
|
{
|
|
|
|
IObjectWithSite *obj = NULL;
|
|
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
|
|
|
|
TRACE("(%p, %s, %p)\n", unk, debugstr_guid(iid), site);
|
|
|
|
|
|
|
|
if (unk && iid && site)
|
|
|
|
{
|
|
|
|
hr = IUnknown_QueryInterface(unk, &IID_IObjectWithSite, (void **)&obj);
|
|
|
|
if (SUCCEEDED(hr) && obj)
|
|
|
|
{
|
|
|
|
hr = IObjectWithSite_GetSite(obj, iid, site);
|
|
|
|
IObjectWithSite_Release(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI IUnknown_QueryService(IUnknown *obj, REFGUID sid, REFIID iid, void **out)
|
|
|
|
{
|
|
|
|
IServiceProvider *provider = NULL;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!out)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
*out = NULL;
|
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
hr = IUnknown_QueryInterface(obj, &IID_IServiceProvider, (void **)&provider);
|
|
|
|
if (hr == S_OK && provider)
|
|
|
|
{
|
|
|
|
TRACE("Using provider %p.\n", provider);
|
|
|
|
|
|
|
|
hr = IServiceProvider_QueryService(provider, sid, iid, out);
|
|
|
|
|
|
|
|
TRACE("Provider %p returned %p.\n", provider, *out);
|
|
|
|
|
|
|
|
IServiceProvider_Release(provider);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void WINAPI IUnknown_Set(IUnknown **dest, IUnknown *src)
|
|
|
|
{
|
|
|
|
TRACE("(%p, %p)\n", dest, src);
|
|
|
|
|
|
|
|
IUnknown_AtomicRelease(dest);
|
|
|
|
|
|
|
|
if (src)
|
|
|
|
{
|
|
|
|
IUnknown_AddRef(src);
|
|
|
|
*dest = src;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI IUnknown_SetSite(IUnknown *obj, IUnknown *site)
|
|
|
|
{
|
|
|
|
IInternetSecurityManager *sec_manager;
|
|
|
|
IObjectWithSite *objwithsite;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return E_FAIL;
|
|
|
|
|
|
|
|
hr = IUnknown_QueryInterface(obj, &IID_IObjectWithSite, (void **)&objwithsite);
|
|
|
|
TRACE("ObjectWithSite %p, hr %#x.\n", objwithsite, hr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = IObjectWithSite_SetSite(objwithsite, site);
|
|
|
|
TRACE("SetSite() hr %#x.\n", hr);
|
|
|
|
IObjectWithSite_Release(objwithsite);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hr = IUnknown_QueryInterface(obj, &IID_IInternetSecurityManager, (void **)&sec_manager);
|
|
|
|
TRACE("InternetSecurityManager %p, hr %#x.\n", sec_manager, hr);
|
|
|
|
if (FAILED(hr))
|
|
|
|
return hr;
|
|
|
|
|
|
|
|
hr = IInternetSecurityManager_SetSecuritySite(sec_manager, (IInternetSecurityMgrSite *)site);
|
|
|
|
TRACE("SetSecuritySite() hr %#x.\n", hr);
|
|
|
|
IInternetSecurityManager_Release(sec_manager);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
2018-11-26 09:53:11 +01:00
|
|
|
|
|
|
|
HRESULT WINAPI SetCurrentProcessExplicitAppUserModelID(const WCHAR *appid)
|
|
|
|
{
|
|
|
|
FIXME("%s: stub\n", debugstr_w(appid));
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI GetCurrentProcessExplicitAppUserModelID(const WCHAR **appid)
|
|
|
|
{
|
|
|
|
FIXME("%p: stub\n", appid);
|
|
|
|
*appid = NULL;
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
2018-11-26 09:53:12 +01:00
|
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
* CommandLineToArgvW [SHCORE.@]
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
WCHAR** WINAPI CommandLineToArgvW(const WCHAR *cmdline, int *numargs)
|
|
|
|
{
|
|
|
|
int qcount, bcount;
|
|
|
|
const WCHAR *s;
|
|
|
|
WCHAR **argv;
|
|
|
|
DWORD argc;
|
|
|
|
WCHAR *d;
|
|
|
|
|
|
|
|
if (!numargs)
|
|
|
|
{
|
|
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*cmdline == 0)
|
|
|
|
{
|
|
|
|
/* Return the path to the executable */
|
|
|
|
DWORD len, deslen = MAX_PATH, size;
|
|
|
|
|
|
|
|
size = sizeof(WCHAR *) * 2 + deslen * sizeof(WCHAR);
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
if (!(argv = LocalAlloc(LMEM_FIXED, size))) return NULL;
|
|
|
|
len = GetModuleFileNameW(0, (WCHAR *)(argv + 2), deslen);
|
|
|
|
if (!len)
|
|
|
|
{
|
|
|
|
LocalFree(argv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (len < deslen) break;
|
|
|
|
deslen *= 2;
|
|
|
|
size = sizeof(WCHAR *) * 2 + deslen * sizeof(WCHAR);
|
|
|
|
LocalFree(argv);
|
|
|
|
}
|
|
|
|
argv[0] = (WCHAR *)(argv + 2);
|
|
|
|
argv[1] = NULL;
|
|
|
|
*numargs = 1;
|
|
|
|
|
|
|
|
return argv;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* --- First count the arguments */
|
|
|
|
argc = 1;
|
|
|
|
s = cmdline;
|
|
|
|
/* 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 = LocalAlloc(LMEM_FIXED, (argc + 1) * sizeof(WCHAR *) + (strlenW(cmdline) + 1) * sizeof(WCHAR));
|
|
|
|
if (!argv)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* --- Then split and copy the arguments */
|
|
|
|
argv[0] = d = strcpyW((WCHAR *)(argv + argc + 1), 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 */
|
|
|
|
argv[argc] = NULL;
|
|
|
|
*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';
|
|
|
|
argv[argc] = NULL;
|
|
|
|
*numargs = argc;
|
|
|
|
|
|
|
|
return argv;
|
|
|
|
}
|