/* * Copyright 2002 Jon Griffiths * 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 <stdarg.h> #define COBJMACROS #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "initguid.h" #include "ocidl.h" #include "shellscalingapi.h" #include "shlwapi.h" #include "wine/debug.h" #include "wine/heap.h" WINE_DEFAULT_DEBUG_CHANNEL(shcore); static DWORD shcore_tls; static IUnknown *process_ref; BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) { TRACE("(%p, %u, %p)\n", instance, reason, reserved); switch (reason) { case DLL_PROCESS_ATTACH: DisableThreadLibraryCalls(instance); shcore_tls = TlsAlloc(); break; case DLL_PROCESS_DETACH: if (reserved) break; if (shcore_tls != TLS_OUT_OF_INDEXES) TlsFree(shcore_tls); break; } return TRUE; } HRESULT WINAPI GetProcessDpiAwareness(HANDLE process, PROCESS_DPI_AWARENESS *value) { if (GetProcessDpiAwarenessInternal( process, (DPI_AWARENESS *)value )) return S_OK; return HRESULT_FROM_WIN32( GetLastError() ); } HRESULT WINAPI SetProcessDpiAwareness(PROCESS_DPI_AWARENESS value) { if (SetProcessDpiAwarenessInternal( value )) return S_OK; return HRESULT_FROM_WIN32( GetLastError() ); } HRESULT WINAPI GetDpiForMonitor(HMONITOR monitor, MONITOR_DPI_TYPE type, UINT *x, UINT *y) { if (GetDpiForMonitorInternal( monitor, type, x, y )) return S_OK; return HRESULT_FROM_WIN32( GetLastError() ); } HRESULT WINAPI GetScaleFactorForMonitor(HMONITOR monitor, DEVICE_SCALE_FACTOR *scale) { FIXME("(%p %p): stub\n", monitor, scale); *scale = SCALE_100_PERCENT; return S_OK; } DEVICE_SCALE_FACTOR WINAPI GetScaleFactorForDevice(DISPLAY_DEVICE_TYPE device_type) { FIXME("%d\n", device_type); return SCALE_100_PERCENT; } 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; } HRESULT WINAPI SetCurrentProcessExplicitAppUserModelID(const WCHAR *appid) { FIXME("%s: stub\n", debugstr_w(appid)); return S_OK; } HRESULT WINAPI GetCurrentProcessExplicitAppUserModelID(const WCHAR **appid) { FIXME("%p: stub\n", appid); *appid = NULL; return E_NOTIMPL; } /************************************************************************* * 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 *) + (lstrlenW(cmdline) + 1) * sizeof(WCHAR)); if (!argv) return NULL; /* --- Then split and copy the arguments */ argv[0] = d = lstrcpyW((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; } struct shstream { IStream IStream_iface; LONG refcount; union { struct { BYTE *buffer; DWORD length; DWORD position; HKEY hkey; WCHAR *valuename; } mem; struct { HANDLE handle; DWORD mode; WCHAR *path; } file; } u; }; static inline struct shstream *impl_from_IStream(IStream *iface) { return CONTAINING_RECORD(iface, struct shstream, IStream_iface); } static HRESULT WINAPI shstream_QueryInterface(IStream *iface, REFIID riid, void **out) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p)->(%s, %p)\n", stream, debugstr_guid(riid), out); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IStream) || IsEqualIID(riid, &IID_ISequentialStream)) { *out = iface; IStream_AddRef(iface); return S_OK; } *out = NULL; WARN("Unsupported interface %s.\n", debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI shstream_AddRef(IStream *iface) { struct shstream *stream = impl_from_IStream(iface); ULONG refcount = InterlockedIncrement(&stream->refcount); TRACE("(%p)->(%u)\n", stream, refcount); return refcount; } static ULONG WINAPI memstream_Release(IStream *iface) { struct shstream *stream = impl_from_IStream(iface); ULONG refcount = InterlockedDecrement(&stream->refcount); TRACE("(%p)->(%u)\n", stream, refcount); if (!refcount) { heap_free(stream->u.mem.buffer); heap_free(stream); } return refcount; } static HRESULT WINAPI memstream_Read(IStream *iface, void *buff, ULONG buff_size, ULONG *read_len) { struct shstream *stream = impl_from_IStream(iface); DWORD length; TRACE("(%p)->(%p, %u, %p)\n", stream, buff, buff_size, read_len); if (stream->u.mem.position >= stream->u.mem.length) { if (read_len) *read_len = 0; return S_FALSE; } length = stream->u.mem.length - stream->u.mem.position; if (buff_size < length) length = buff_size; memmove(buff, stream->u.mem.buffer + stream->u.mem.position, length); stream->u.mem.position += length; if (read_len) *read_len = length; return S_OK; } static HRESULT WINAPI memstream_Write(IStream *iface, const void *buff, ULONG buff_size, ULONG *written) { struct shstream *stream = impl_from_IStream(iface); DWORD length = stream->u.mem.position + buff_size; TRACE("(%p)->(%p, %u, %p)\n", stream, buff, buff_size, written); if (length < stream->u.mem.position) /* overflow */ return STG_E_INSUFFICIENTMEMORY; if (length > stream->u.mem.length) { BYTE *buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, stream->u.mem.buffer, length); if (!buffer) return STG_E_INSUFFICIENTMEMORY; stream->u.mem.length = length; stream->u.mem.buffer = buffer; } memmove(stream->u.mem.buffer + stream->u.mem.position, buff, buff_size); stream->u.mem.position += buff_size; /* adjust pointer */ if (written) *written = buff_size; return S_OK; } static HRESULT WINAPI memstream_Seek(IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER*new_pos) { struct shstream *stream = impl_from_IStream(iface); LARGE_INTEGER tmp; TRACE("(%p)->(%s, %d, %p)\n", stream, wine_dbgstr_longlong(move.QuadPart), origin, new_pos); if (origin == STREAM_SEEK_SET) tmp = move; else if (origin == STREAM_SEEK_CUR) tmp.QuadPart = stream->u.mem.position + move.QuadPart; else if (origin == STREAM_SEEK_END) tmp.QuadPart = stream->u.mem.length + move.QuadPart; else return STG_E_INVALIDPARAMETER; if (tmp.QuadPart < 0) return STG_E_INVALIDFUNCTION; /* we cut off the high part here */ stream->u.mem.position = tmp.u.LowPart; if (new_pos) new_pos->QuadPart = stream->u.mem.position; return S_OK; } static HRESULT WINAPI memstream_SetSize(IStream *iface, ULARGE_INTEGER new_size) { struct shstream *stream = impl_from_IStream(iface); DWORD length; BYTE *buffer; TRACE("(%p, %s)\n", stream, wine_dbgstr_longlong(new_size.QuadPart)); /* we cut off the high part here */ length = new_size.u.LowPart; buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, stream->u.mem.buffer, length); if (!buffer) return STG_E_INSUFFICIENTMEMORY; stream->u.mem.buffer = buffer; stream->u.mem.length = length; return S_OK; } static HRESULT WINAPI shstream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER size, ULARGE_INTEGER *read_len, ULARGE_INTEGER *written) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p)\n", stream); if (read_len) read_len->QuadPart = 0; if (written) written->QuadPart = 0; /* TODO implement */ return E_NOTIMPL; } static HRESULT WINAPI shstream_Commit(IStream *iface, DWORD flags) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p, %#x)\n", stream, flags); /* Commit is not supported by this stream */ return E_NOTIMPL; } static HRESULT WINAPI shstream_Revert(IStream *iface) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p)\n", stream); /* revert not supported by this stream */ return E_NOTIMPL; } static HRESULT WINAPI shstream_LockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD lock_type) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p)\n", stream); /* lock/unlock not supported by this stream */ return E_NOTIMPL; } static HRESULT WINAPI shstream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size, DWORD lock_type) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p)\n", stream); /* lock/unlock not supported by this stream */ return E_NOTIMPL; } static HRESULT WINAPI memstream_Stat(IStream *iface, STATSTG *statstg, DWORD flags) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p, %p, %#x)\n", stream, statstg, flags); memset(statstg, 0, sizeof(*statstg)); statstg->type = STGTY_STREAM; statstg->cbSize.QuadPart = stream->u.mem.length; statstg->grfMode = STGM_READWRITE; return S_OK; } static HRESULT WINAPI shstream_Clone(IStream *iface, IStream **dest) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p, %p)\n", stream, dest); *dest = NULL; /* clone not supported by this stream */ return E_NOTIMPL; } static const IStreamVtbl memstreamvtbl = { shstream_QueryInterface, shstream_AddRef, memstream_Release, memstream_Read, memstream_Write, memstream_Seek, memstream_SetSize, shstream_CopyTo, shstream_Commit, shstream_Revert, shstream_LockRegion, shstream_UnlockRegion, memstream_Stat, shstream_Clone, }; static struct shstream *shstream_create(const IStreamVtbl *vtbl, const BYTE *data, UINT data_len) { struct shstream *stream; if (!data) data_len = 0; stream = heap_alloc(sizeof(*stream)); stream->IStream_iface.lpVtbl = vtbl; stream->refcount = 1; stream->u.mem.buffer = heap_alloc(data_len); if (!stream->u.mem.buffer) { heap_free(stream); return NULL; } memcpy(stream->u.mem.buffer, data, data_len); stream->u.mem.length = data_len; stream->u.mem.position = 0; return stream; } /************************************************************************* * SHCreateMemStream [SHCORE.@] * * Create an IStream object on a block of memory. * * PARAMS * data [I] Memory block to create the IStream object on * data_len [I] Length of data block * * RETURNS * Success: A pointer to the IStream object. * Failure: NULL, if any parameters are invalid or an error occurs. * * NOTES * A copy of the memory block is made, it's freed when the stream is released. */ IStream * WINAPI SHCreateMemStream(const BYTE *data, UINT data_len) { struct shstream *stream; TRACE("(%p, %u)\n", data, data_len); stream = shstream_create(&memstreamvtbl, data, data_len); return stream ? &stream->IStream_iface : NULL; } static ULONG WINAPI filestream_Release(IStream *iface) { struct shstream *stream = impl_from_IStream(iface); ULONG refcount = InterlockedDecrement(&stream->refcount); TRACE("(%p)->(%u)\n", stream, refcount); if (!refcount) { CloseHandle(stream->u.file.handle); heap_free(stream->u.file.path); heap_free(stream); } return refcount; } static HRESULT WINAPI filestream_Read(IStream *iface, void *buff, ULONG size, ULONG *read_len) { struct shstream *stream = impl_from_IStream(iface); DWORD read = 0; TRACE("(%p, %p, %u, %p)\n", stream, buff, size, read_len); if (!ReadFile(stream->u.file.handle, buff, size, &read, NULL)) { WARN("error %d reading file\n", GetLastError()); return S_FALSE; } if (read_len) *read_len = read; return read == size ? S_OK : S_FALSE; } static HRESULT WINAPI filestream_Write(IStream *iface, const void *buff, ULONG size, ULONG *written) { struct shstream *stream = impl_from_IStream(iface); DWORD written_len = 0; TRACE("(%p, %p, %u, %p)\n", stream, buff, size, written); switch (stream->u.file.mode & 0xf) { case STGM_WRITE: case STGM_READWRITE: break; default: return STG_E_ACCESSDENIED; } if (!WriteFile(stream->u.file.handle, buff, size, &written_len, NULL)) return HRESULT_FROM_WIN32(GetLastError()); if (written) *written = written_len; return S_OK; } static HRESULT WINAPI filestream_Seek(IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *new_pos) { struct shstream *stream = impl_from_IStream(iface); DWORD position; TRACE("(%p, %s, %d, %p)\n", stream, wine_dbgstr_longlong(move.QuadPart), origin, new_pos); position = SetFilePointer(stream->u.file.handle, move.u.LowPart, NULL, origin); if (position == INVALID_SET_FILE_POINTER) return HRESULT_FROM_WIN32(GetLastError()); if (new_pos) { new_pos->u.HighPart = 0; new_pos->u.LowPart = position; } return S_OK; } static HRESULT WINAPI filestream_SetSize(IStream *iface, ULARGE_INTEGER size) { struct shstream *stream = impl_from_IStream(iface); LARGE_INTEGER origin, move; TRACE("(%p, %s)\n", stream, wine_dbgstr_longlong(size.QuadPart)); move.QuadPart = 0; if (!SetFilePointerEx(stream->u.file.handle, move, &origin, FILE_CURRENT)) return E_FAIL; move.QuadPart = size.QuadPart; if (!SetFilePointerEx(stream->u.file.handle, move, NULL, FILE_BEGIN)) return E_FAIL; if (stream->u.file.mode != STGM_READ) { if (!SetEndOfFile(stream->u.file.handle)) return E_FAIL; if (!SetFilePointerEx(stream->u.file.handle, origin, NULL, FILE_BEGIN)) return E_FAIL; } return S_OK; } static HRESULT WINAPI filestream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size, ULARGE_INTEGER *read_len, ULARGE_INTEGER *written) { struct shstream *stream = impl_from_IStream(iface); HRESULT hr = S_OK; char buff[1024]; TRACE("(%p, %p, %s, %p, %p)\n", stream, dest, wine_dbgstr_longlong(size.QuadPart), read_len, written); if (read_len) read_len->QuadPart = 0; if (written) written->QuadPart = 0; if (!dest) return S_OK; while (size.QuadPart) { ULONG left, read_chunk, written_chunk; left = size.QuadPart > sizeof(buff) ? sizeof(buff) : size.QuadPart; /* Read */ hr = IStream_Read(iface, buff, left, &read_chunk); if (FAILED(hr) || read_chunk == 0) break; if (read_len) read_len->QuadPart += read_chunk; /* Write */ hr = IStream_Write(dest, buff, read_chunk, &written_chunk); if (written_chunk) written->QuadPart += written_chunk; if (FAILED(hr) || written_chunk != left) break; size.QuadPart -= left; } return hr; } static HRESULT WINAPI filestream_Commit(IStream *iface, DWORD flags) { struct shstream *stream = impl_from_IStream(iface); TRACE("(%p, %#x)\n", stream, flags); return S_OK; } static HRESULT WINAPI filestream_Stat(IStream *iface, STATSTG *statstg, DWORD flags) { struct shstream *stream = impl_from_IStream(iface); BY_HANDLE_FILE_INFORMATION fi; TRACE("(%p, %p, %#x)\n", stream, statstg, flags); if (!statstg) return STG_E_INVALIDPOINTER; memset(&fi, 0, sizeof(fi)); GetFileInformationByHandle(stream->u.file.handle, &fi); if (flags & STATFLAG_NONAME) statstg->pwcsName = NULL; else { int len = lstrlenW(stream->u.file.path); if ((statstg->pwcsName = CoTaskMemAlloc((len + 1) * sizeof(WCHAR)))) memcpy(statstg->pwcsName, stream->u.file.path, (len + 1) * sizeof(WCHAR)); } statstg->type = 0; statstg->cbSize.u.LowPart = fi.nFileSizeLow; statstg->cbSize.u.HighPart = fi.nFileSizeHigh; statstg->mtime = fi.ftLastWriteTime; statstg->ctime = fi.ftCreationTime; statstg->atime = fi.ftLastAccessTime; statstg->grfMode = stream->u.file.mode; statstg->grfLocksSupported = 0; memcpy(&statstg->clsid, &IID_IStream, sizeof(CLSID)); statstg->grfStateBits = 0; statstg->reserved = 0; return S_OK; } static const IStreamVtbl filestreamvtbl = { shstream_QueryInterface, shstream_AddRef, filestream_Release, filestream_Read, filestream_Write, filestream_Seek, filestream_SetSize, filestream_CopyTo, filestream_Commit, shstream_Revert, shstream_LockRegion, shstream_UnlockRegion, filestream_Stat, shstream_Clone, }; /************************************************************************* * SHCreateStreamOnFileEx [SHCORE.@] */ HRESULT WINAPI SHCreateStreamOnFileEx(const WCHAR *path, DWORD mode, DWORD attributes, BOOL create, IStream *template, IStream **ret) { DWORD access, share, creation_disposition, len; struct shstream *stream; HANDLE hFile; TRACE("(%s, %d, 0x%08X, %d, %p, %p)\n", debugstr_w(path), mode, attributes, create, template, ret); if (!path || !ret || template) return E_INVALIDARG; *ret = NULL; /* Access */ switch (mode & 0xf) { case STGM_WRITE: case STGM_READWRITE: access = GENERIC_READ | GENERIC_WRITE; break; case STGM_READ: access = GENERIC_READ; break; default: return E_INVALIDARG; } /* Sharing */ switch (mode & 0xf0) { case 0: case STGM_SHARE_DENY_NONE: share = FILE_SHARE_READ | FILE_SHARE_WRITE; break; case STGM_SHARE_DENY_READ: share = FILE_SHARE_WRITE; break; case STGM_SHARE_DENY_WRITE: share = FILE_SHARE_READ; break; case STGM_SHARE_EXCLUSIVE: share = 0; break; default: return E_INVALIDARG; } switch (mode & 0xf000) { case STGM_FAILIFTHERE: creation_disposition = create ? CREATE_NEW : OPEN_EXISTING; break; case STGM_CREATE: creation_disposition = CREATE_ALWAYS; break; default: return E_INVALIDARG; } hFile = CreateFileW(path, access, share, NULL, creation_disposition, attributes, 0); if (hFile == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError()); stream = heap_alloc(sizeof(*stream)); stream->IStream_iface.lpVtbl = &filestreamvtbl; stream->refcount = 1; stream->u.file.handle = hFile; stream->u.file.mode = mode; len = lstrlenW(path); stream->u.file.path = heap_alloc((len + 1) * sizeof(WCHAR)); memcpy(stream->u.file.path, path, (len + 1) * sizeof(WCHAR)); *ret = &stream->IStream_iface; return S_OK; } /************************************************************************* * SHCreateStreamOnFileW [SHCORE.@] */ HRESULT WINAPI SHCreateStreamOnFileW(const WCHAR *path, DWORD mode, IStream **stream) { TRACE("(%s, %#x, %p)\n", debugstr_w(path), mode, stream); if (!path || !stream) return E_INVALIDARG; if ((mode & (STGM_CONVERT | STGM_DELETEONRELEASE | STGM_TRANSACTED)) != 0) return E_INVALIDARG; return SHCreateStreamOnFileEx(path, mode, 0, FALSE, NULL, stream); } /************************************************************************* * SHCreateStreamOnFileA [SHCORE.@] */ HRESULT WINAPI SHCreateStreamOnFileA(const char *path, DWORD mode, IStream **stream) { WCHAR *pathW; HRESULT hr; DWORD len; TRACE("(%s, %#x, %p)\n", debugstr_a(path), mode, stream); if (!path) return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0); pathW = heap_alloc(len * sizeof(WCHAR)); if (!pathW) return E_OUTOFMEMORY; MultiByteToWideChar(CP_ACP, 0, path, -1, pathW, len); hr = SHCreateStreamOnFileW(pathW, mode, stream); heap_free(pathW); return hr; } static ULONG WINAPI regstream_Release(IStream *iface) { struct shstream *stream = impl_from_IStream(iface); ULONG refcount = InterlockedDecrement(&stream->refcount); TRACE("(%p)->(%u)\n", stream, refcount); if (!refcount) { if (stream->u.mem.hkey) { if (stream->u.mem.length) RegSetValueExW(stream->u.mem.hkey, stream->u.mem.valuename, 0, REG_BINARY, (const BYTE *)stream->u.mem.buffer, stream->u.mem.length); else RegDeleteValueW(stream->u.mem.hkey, stream->u.mem.valuename); RegCloseKey(stream->u.mem.hkey); } CoTaskMemFree(stream->u.mem.valuename); heap_free(stream->u.mem.buffer); heap_free(stream); } return refcount; } static const IStreamVtbl regstreamvtbl = { shstream_QueryInterface, shstream_AddRef, regstream_Release, memstream_Read, memstream_Write, memstream_Seek, memstream_SetSize, shstream_CopyTo, shstream_Commit, shstream_Revert, shstream_LockRegion, shstream_UnlockRegion, memstream_Stat, shstream_Clone, }; /************************************************************************* * SHOpenRegStream2W [SHCORE.@] */ IStream * WINAPI SHOpenRegStream2W(HKEY hKey, const WCHAR *subkey, const WCHAR *value, DWORD mode) { struct shstream *stream; HKEY hStrKey = NULL; BYTE *buff = NULL; DWORD length = 0; LONG ret; TRACE("(%p, %s, %s, %#x)\n", hKey, debugstr_w(subkey), debugstr_w(value), mode); if (mode == STGM_READ) ret = RegOpenKeyExW(hKey, subkey, 0, KEY_READ, &hStrKey); else /* in write mode we make sure the subkey exits */ ret = RegCreateKeyExW(hKey, subkey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hStrKey, NULL); if (ret == ERROR_SUCCESS) { if (mode == STGM_READ || mode == STGM_READWRITE) { /* read initial data */ ret = RegQueryValueExW(hStrKey, value, 0, 0, 0, &length); if (ret == ERROR_SUCCESS && length) { buff = heap_alloc(length); RegQueryValueExW(hStrKey, value, 0, 0, buff, &length); } } if (!length) buff = heap_alloc(length); stream = shstream_create(®streamvtbl, buff, length); heap_free(buff); if (stream) { stream->u.mem.hkey = hStrKey; SHStrDupW(value, &stream->u.mem.valuename); return &stream->IStream_iface; } } if (hStrKey) RegCloseKey(hStrKey); return NULL; } /************************************************************************* * SHOpenRegStream2A [SHCORE.@] */ IStream * WINAPI SHOpenRegStream2A(HKEY hKey, const char *subkey, const char *value, DWORD mode) { WCHAR *subkeyW = NULL, *valueW = NULL; IStream *stream; TRACE("(%p, %s, %s, %#x)\n", hKey, debugstr_a(subkey), debugstr_a(value), mode); if (subkey && FAILED(SHStrDupA(subkey, &subkeyW))) return NULL; if (value && FAILED(SHStrDupA(value, &valueW))) { CoTaskMemFree(subkeyW); return NULL; } stream = SHOpenRegStream2W(hKey, subkeyW, valueW, mode); CoTaskMemFree(subkeyW); CoTaskMemFree(valueW); return stream; } /************************************************************************* * SHOpenRegStreamA [SHCORE.@] */ IStream * WINAPI SHOpenRegStreamA(HKEY hkey, const char *subkey, const char *value, DWORD mode) { WCHAR *subkeyW = NULL, *valueW = NULL; IStream *stream; TRACE("(%p, %s, %s, %#x)\n", hkey, debugstr_a(subkey), debugstr_a(value), mode); if (subkey && FAILED(SHStrDupA(subkey, &subkeyW))) return NULL; if (value && FAILED(SHStrDupA(value, &valueW))) { CoTaskMemFree(subkeyW); return NULL; } stream = SHOpenRegStreamW(hkey, subkeyW, valueW, mode); CoTaskMemFree(subkeyW); CoTaskMemFree(valueW); return stream; } static ULONG WINAPI dummystream_AddRef(IStream *iface) { TRACE("()\n"); return 2; } static ULONG WINAPI dummystream_Release(IStream *iface) { TRACE("()\n"); return 1; } static HRESULT WINAPI dummystream_Read(IStream *iface, void *buff, ULONG buff_size, ULONG *read_len) { if (read_len) *read_len = 0; return E_NOTIMPL; } static const IStreamVtbl dummystreamvtbl = { shstream_QueryInterface, dummystream_AddRef, dummystream_Release, dummystream_Read, memstream_Write, memstream_Seek, memstream_SetSize, shstream_CopyTo, shstream_Commit, shstream_Revert, shstream_LockRegion, shstream_UnlockRegion, memstream_Stat, shstream_Clone, }; static struct shstream dummyregstream = { { &dummystreamvtbl } }; /************************************************************************* * SHOpenRegStreamW [SHCORE.@] */ IStream * WINAPI SHOpenRegStreamW(HKEY hkey, const WCHAR *subkey, const WCHAR *value, DWORD mode) { IStream *stream; TRACE("(%p, %s, %s, %#x)\n", hkey, debugstr_w(subkey), debugstr_w(value), mode); stream = SHOpenRegStream2W(hkey, subkey, value, mode); return stream ? stream : &dummyregstream.IStream_iface; } struct threadref { IUnknown IUnknown_iface; LONG *refcount; }; static inline struct threadref *threadref_impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, struct threadref, IUnknown_iface); } static HRESULT WINAPI threadref_QueryInterface(IUnknown *iface, REFIID riid, void **out) { struct threadref *threadref = threadref_impl_from_IUnknown(iface); TRACE("(%p, %s, %p)\n", threadref, debugstr_guid(riid), out); if (out == NULL) return E_POINTER; if (IsEqualGUID(&IID_IUnknown, riid)) { *out = iface; IUnknown_AddRef(iface); return S_OK; } *out = NULL; WARN("Interface %s not supported.\n", debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI threadref_AddRef(IUnknown *iface) { struct threadref *threadref = threadref_impl_from_IUnknown(iface); LONG refcount = InterlockedIncrement(threadref->refcount); TRACE("(%p, %d)\n", threadref, refcount); return refcount; } static ULONG WINAPI threadref_Release(IUnknown *iface) { struct threadref *threadref = threadref_impl_from_IUnknown(iface); LONG refcount = InterlockedDecrement(threadref->refcount); TRACE("(%p, %d)\n", threadref, refcount); if (!refcount) heap_free(threadref); return refcount; } static const IUnknownVtbl threadrefvtbl = { threadref_QueryInterface, threadref_AddRef, threadref_Release, }; /************************************************************************* * SHCreateThreadRef [SHCORE.@] */ HRESULT WINAPI SHCreateThreadRef(LONG *refcount, IUnknown **out) { struct threadref *threadref; TRACE("(%p, %p)\n", refcount, out); if (!refcount || !out) return E_INVALIDARG; *out = NULL; threadref = heap_alloc(sizeof(*threadref)); if (!threadref) return E_OUTOFMEMORY; threadref->IUnknown_iface.lpVtbl = &threadrefvtbl; threadref->refcount = refcount; *refcount = 1; *out = &threadref->IUnknown_iface; TRACE("Created %p.\n", threadref); return S_OK; } /************************************************************************* * SHGetThreadRef [SHCORE.@] */ HRESULT WINAPI SHGetThreadRef(IUnknown **out) { TRACE("(%p)\n", out); if (shcore_tls == TLS_OUT_OF_INDEXES) return E_NOINTERFACE; *out = TlsGetValue(shcore_tls); if (!*out) return E_NOINTERFACE; IUnknown_AddRef(*out); return S_OK; } /************************************************************************* * SHSetThreadRef [SHCORE.@] */ HRESULT WINAPI SHSetThreadRef(IUnknown *obj) { TRACE("(%p)\n", obj); if (shcore_tls == TLS_OUT_OF_INDEXES) return E_NOINTERFACE; TlsSetValue(shcore_tls, obj); return S_OK; } /************************************************************************* * SHReleaseThreadRef [SHCORE.@] */ HRESULT WINAPI SHReleaseThreadRef(void) { FIXME("() - stub!\n"); return S_OK; } /************************************************************************* * GetProcessReference [SHCORE.@] */ HRESULT WINAPI GetProcessReference(IUnknown **obj) { TRACE("(%p)\n", obj); *obj = process_ref; if (!process_ref) return E_FAIL; if (*obj) IUnknown_AddRef(*obj); return S_OK; } /************************************************************************* * SetProcessReference [SHCORE.@] */ void WINAPI SetProcessReference(IUnknown *obj) { TRACE("(%p)\n", obj); process_ref = obj; } struct thread_data { LPTHREAD_START_ROUTINE thread_proc; LPTHREAD_START_ROUTINE callback; void *data; DWORD flags; HANDLE hEvent; IUnknown *thread_ref; IUnknown *process_ref; }; static DWORD WINAPI shcore_thread_wrapper(void *data) { struct thread_data thread_data; HRESULT hr = E_FAIL; DWORD retval; TRACE("(%p)\n", data); /* We are now executing in the context of the newly created thread. * So we copy the data passed to us (it is on the stack of the function * that called us, which is waiting for us to signal an event before * returning). */ thread_data = *(struct thread_data *)data; if (thread_data.flags & CTF_COINIT) { hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (FAILED(hr)) hr = CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE); } if (thread_data.callback) thread_data.callback(thread_data.data); /* Signal the thread that created us; it can return now. */ SetEvent(thread_data.hEvent); /* Execute the callers start code. */ retval = thread_data.thread_proc(thread_data.data); /* Release thread and process references. */ if (thread_data.thread_ref) IUnknown_Release(thread_data.thread_ref); if (thread_data.process_ref) IUnknown_Release(thread_data.process_ref); if (SUCCEEDED(hr)) CoUninitialize(); return retval; } /************************************************************************* * SHCreateThread [SHCORE.@] */ BOOL WINAPI SHCreateThread(LPTHREAD_START_ROUTINE thread_proc, void *data, DWORD flags, LPTHREAD_START_ROUTINE callback) { struct thread_data thread_data; BOOL called = FALSE; TRACE("(%p, %p, %#x, %p)\n", thread_proc, data, flags, callback); thread_data.thread_proc = thread_proc; thread_data.callback = callback; thread_data.data = data; thread_data.flags = flags; thread_data.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (flags & CTF_THREAD_REF) SHGetThreadRef(&thread_data.thread_ref); else thread_data.thread_ref = NULL; if (flags & CTF_PROCESS_REF) GetProcessReference(&thread_data.process_ref); else thread_data.process_ref = NULL; /* Create the thread */ if (thread_data.hEvent) { HANDLE hThread; DWORD retval; hThread = CreateThread(NULL, 0, shcore_thread_wrapper, &thread_data, 0, &retval); if (hThread) { /* Wait for the thread to signal us to continue */ WaitForSingleObject(thread_data.hEvent, INFINITE); CloseHandle(hThread); called = TRUE; } CloseHandle(thread_data.hEvent); } if (!called) { if (!thread_data.callback && flags & CTF_INSIST) { /* Couldn't call, call synchronously */ thread_data.thread_proc(data); called = TRUE; } else { if (thread_data.thread_ref) IUnknown_Release(thread_data.thread_ref); if (thread_data.process_ref) IUnknown_Release(thread_data.process_ref); } } return called; } /************************************************************************* * SHStrDupW [SHCORE.@] */ HRESULT WINAPI SHStrDupW(const WCHAR *src, WCHAR **dest) { size_t len; TRACE("(%s, %p)\n", debugstr_w(src), dest); *dest = NULL; if (!src) return E_INVALIDARG; len = (lstrlenW(src) + 1) * sizeof(WCHAR); *dest = CoTaskMemAlloc(len); if (!*dest) return E_OUTOFMEMORY; memcpy(*dest, src, len); return S_OK; } /************************************************************************* * SHStrDupA [SHCORE.@] */ HRESULT WINAPI SHStrDupA(const char *src, WCHAR **dest) { DWORD len; *dest = NULL; if (!src) return E_INVALIDARG; len = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0); *dest = CoTaskMemAlloc(len * sizeof(WCHAR)); if (!*dest) return E_OUTOFMEMORY; MultiByteToWideChar(CP_ACP, 0, src, -1, *dest, len); return S_OK; } /************************************************************************* * SHAnsiToAnsi [SHCORE.@] */ DWORD WINAPI SHAnsiToAnsi(const char *src, char *dest, int dest_len) { DWORD ret; TRACE("(%s, %p, %d)\n", debugstr_a(src), dest, dest_len); if (!src || !dest || dest_len <= 0) return 0; lstrcpynA(dest, src, dest_len); ret = strlen(dest); return src[ret] ? 0 : ret + 1; } /************************************************************************* * SHUnicodeToAnsi [SHCORE.@] */ DWORD WINAPI SHUnicodeToAnsi(const WCHAR *src, char *dest, int dest_len) { int ret = 1; TRACE("(%s, %p, %d)\n", debugstr_w(src), dest, dest_len); if (!dest || !dest_len) return 0; if (src) { ret = WideCharToMultiByte(CP_ACP, 0, src, -1, dest, dest_len, NULL, NULL); if (!ret) { dest[dest_len - 1] = 0; ret = dest_len; } } else dest[0] = 0; return ret; } /************************************************************************* * SHUnicodeToUnicode [SHCORE.@] */ DWORD WINAPI SHUnicodeToUnicode(const WCHAR *src, WCHAR *dest, int dest_len) { DWORD ret; TRACE("(%s, %p, %d)\n", debugstr_w(src), dest, dest_len); if (!src || !dest || dest_len <= 0) return 0; lstrcpynW(dest, src, dest_len); ret = lstrlenW(dest); return src[ret] ? 0 : ret + 1; } /************************************************************************* * SHAnsiToUnicode [SHCORE.@] */ DWORD WINAPI SHAnsiToUnicode(const char *src, WCHAR *dest, int dest_len) { int ret = 1; TRACE("(%s, %p, %d)\n", debugstr_a(src), dest, dest_len); if (!dest || !dest_len) return 0; if (src) { ret = MultiByteToWideChar(CP_ACP, 0, src, -1, dest, dest_len); if (!ret) { dest[dest_len - 1] = 0; ret = dest_len; } } else dest[0] = 0; return ret; } /************************************************************************* * SHRegDuplicateHKey [SHCORE.@] */ HKEY WINAPI SHRegDuplicateHKey(HKEY hKey) { HKEY newKey = 0; RegOpenKeyExW(hKey, 0, 0, MAXIMUM_ALLOWED, &newKey); TRACE("new key is %p\n", newKey); return newKey; } /************************************************************************* * SHDeleteEmptyKeyW [SHCORE.@] */ DWORD WINAPI SHDeleteEmptyKeyW(HKEY hkey, const WCHAR *subkey) { DWORD ret, count = 0; HKEY hsubkey = 0; TRACE("(%p, %s)\n", hkey, debugstr_w(subkey)); ret = RegOpenKeyExW(hkey, subkey, 0, KEY_READ, &hsubkey); if (!ret) { ret = RegQueryInfoKeyW(hsubkey, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL, NULL, NULL, NULL); RegCloseKey(hsubkey); if (!ret) { if (count) ret = ERROR_KEY_HAS_CHILDREN; else ret = RegDeleteKeyW(hkey, subkey); } } return ret; } /************************************************************************* * SHDeleteEmptyKeyA [SHCORE.@] */ DWORD WINAPI SHDeleteEmptyKeyA(HKEY hkey, const char *subkey) { WCHAR *subkeyW = NULL; DWORD ret; TRACE("(%p, %s)\n", hkey, debugstr_a(subkey)); if (subkey && FAILED(SHStrDupA(subkey, &subkeyW))) return ERROR_OUTOFMEMORY; ret = SHDeleteEmptyKeyW(hkey, subkeyW); CoTaskMemFree(subkeyW); return ret; } /************************************************************************* * SHDeleteKeyW [SHCORE.@] */ DWORD WINAPI SHDeleteKeyW(HKEY hkey, const WCHAR *subkey) { TRACE("(%p, %s)\n", hkey, debugstr_w(subkey)); return RegDeleteTreeW(hkey, subkey); } /************************************************************************* * SHDeleteKeyA [SHCORE.@] */ DWORD WINAPI SHDeleteKeyA(HKEY hkey, const char *subkey) { TRACE("(%p, %s)\n", hkey, debugstr_a(subkey)); return RegDeleteTreeA(hkey, subkey); } /************************************************************************* * SHDeleteValueW [SHCORE.@] */ DWORD WINAPI SHDeleteValueW(HKEY hkey, const WCHAR *subkey, const WCHAR *value) { HKEY hsubkey; DWORD ret; TRACE("(%p, %s, %s)\n", hkey, debugstr_w(subkey), debugstr_w(value)); ret = RegOpenKeyExW(hkey, subkey, 0, KEY_SET_VALUE, &hsubkey); if (!ret) { ret = RegDeleteValueW(hsubkey, value); RegCloseKey(hsubkey); } return ret; } /************************************************************************* * SHDeleteValueA [SHCORE.@] */ DWORD WINAPI SHDeleteValueA(HKEY hkey, const char *subkey, const char *value) { WCHAR *subkeyW = NULL, *valueW = NULL; DWORD ret; TRACE("(%p, %s, %s)\n", hkey, debugstr_a(subkey), debugstr_a(value)); if (subkey && FAILED(SHStrDupA(subkey, &subkeyW))) return ERROR_OUTOFMEMORY; if (value && FAILED(SHStrDupA(value, &valueW))) { CoTaskMemFree(subkeyW); return ERROR_OUTOFMEMORY; } ret = SHDeleteValueW(hkey, subkeyW, valueW); CoTaskMemFree(subkeyW); CoTaskMemFree(valueW); return ret; } /************************************************************************* * SHCopyKeyA [SHCORE.@] */ DWORD WINAPI SHCopyKeyA(HKEY hkey_src, const char *subkey, HKEY hkey_dst, DWORD reserved) { WCHAR *subkeyW = NULL; DWORD ret; TRACE("(%p, %s, %p, %d)\n", hkey_src, debugstr_a(subkey), hkey_dst, reserved); if (subkey && FAILED(SHStrDupA(subkey, &subkeyW))) return 0; ret = SHCopyKeyW(hkey_src, subkeyW, hkey_dst, reserved); CoTaskMemFree(subkeyW); return ret; } /************************************************************************* * SHCopyKeyW [SHCORE.@] */ DWORD WINAPI SHCopyKeyW(HKEY hkey_src, const WCHAR *subkey, HKEY hkey_dst, DWORD reserved) { DWORD key_count = 0, value_count = 0, max_key_len = 0; WCHAR name[MAX_PATH], *ptr_name = name; BYTE buff[1024], *ptr = buff; DWORD max_data_len = 0, i; DWORD ret = 0; TRACE("(%p, %s, %p, %d)\n", hkey_src, debugstr_w(subkey), hkey_dst, reserved); if (!hkey_dst || !hkey_src) return ERROR_INVALID_PARAMETER; if (subkey) ret = RegOpenKeyExW(hkey_src, subkey, 0, KEY_ALL_ACCESS, &hkey_src); if (ret) hkey_src = NULL; /* Don't close this key since we didn't open it */ else { DWORD max_value_len; ret = RegQueryInfoKeyW(hkey_src, NULL, NULL, NULL, &key_count, &max_key_len, NULL, &value_count, &max_value_len, &max_data_len, NULL, NULL); if (!ret) { /* Get max size for key/value names */ max_key_len = max(max_key_len, max_value_len); if (max_key_len++ > MAX_PATH - 1) ptr_name = heap_alloc(max_key_len * sizeof(WCHAR)); if (max_data_len > sizeof(buff)) ptr = heap_alloc(max_data_len); if (!ptr_name || !ptr) ret = ERROR_NOT_ENOUGH_MEMORY; } } for (i = 0; i < key_count && !ret; i++) { HKEY hsubkey_src, hsubkey_dst; DWORD length = max_key_len; ret = RegEnumKeyExW(hkey_src, i, ptr_name, &length, NULL, NULL, NULL, NULL); if (!ret) { ret = RegOpenKeyExW(hkey_src, ptr_name, 0, KEY_READ, &hsubkey_src); if (!ret) { /* Create destination sub key */ ret = RegCreateKeyW(hkey_dst, ptr_name, &hsubkey_dst); if (!ret) { /* Recursively copy keys and values from the sub key */ ret = SHCopyKeyW(hsubkey_src, NULL, hsubkey_dst, 0); RegCloseKey(hsubkey_dst); } } RegCloseKey(hsubkey_src); } } /* Copy all the values in this key */ for (i = 0; i < value_count && !ret; i++) { DWORD length = max_key_len, type, data_len = max_data_len; ret = RegEnumValueW(hkey_src, i, ptr_name, &length, NULL, &type, ptr, &data_len); if (!ret) { ret = SHSetValueW(hkey_dst, NULL, ptr_name, type, ptr, data_len); } } /* Free buffers if allocated */ if (ptr_name != name) heap_free(ptr_name); if (ptr != buff) heap_free(ptr); if (subkey && hkey_src) RegCloseKey(hkey_src); return ret; } /************************************************************************* * SHEnumKeyExA [SHCORE.@] */ LONG WINAPI SHEnumKeyExA(HKEY hkey, DWORD index, char *subkey, DWORD *length) { TRACE("(%p, %d, %s, %p)\n", hkey, index, debugstr_a(subkey), length); return RegEnumKeyExA(hkey, index, subkey, length, NULL, NULL, NULL, NULL); } /************************************************************************* * SHEnumKeyExW [SHCORE.@] */ LONG WINAPI SHEnumKeyExW(HKEY hkey, DWORD index, WCHAR *subkey, DWORD *length) { TRACE("(%p, %d, %s, %p)\n", hkey, index, debugstr_w(subkey), length); return RegEnumKeyExW(hkey, index, subkey, length, NULL, NULL, NULL, NULL); } /************************************************************************* * SHEnumValueA [SHCORE.@] */ LONG WINAPI SHEnumValueA(HKEY hkey, DWORD index, char *value, DWORD *length, DWORD *type, void *data, DWORD *data_len) { TRACE("(%p, %d, %s, %p, %p, %p, %p)\n", hkey, index, debugstr_a(value), length, type, data, data_len); return RegEnumValueA(hkey, index, value, length, NULL, type, data, data_len); } /************************************************************************* * SHEnumValueW [SHCORE.@] */ LONG WINAPI SHEnumValueW(HKEY hkey, DWORD index, WCHAR *value, DWORD *length, DWORD *type, void *data, DWORD *data_len) { TRACE("(%p, %d, %s, %p, %p, %p, %p)\n", hkey, index, debugstr_w(value), length, type, data, data_len); return RegEnumValueW(hkey, index, value, length, NULL, type, data, data_len); } /************************************************************************* * SHQueryValueExW [SHCORE.@] */ DWORD WINAPI SHQueryValueExW(HKEY hkey, const WCHAR *name, DWORD *reserved, DWORD *type, void *buff, DWORD *buff_len) { DWORD ret, value_type, data_len = 0; TRACE("(%p, %s, %p, %p, %p, %p)\n", hkey, debugstr_w(name), reserved, type, buff, buff_len); if (buff_len) data_len = *buff_len; ret = RegQueryValueExW(hkey, name, reserved, &value_type, buff, &data_len); if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) return ret; if (buff_len && value_type == REG_EXPAND_SZ) { DWORD length; WCHAR *value; if (!buff || ret == ERROR_MORE_DATA) { length = data_len; value = heap_alloc(length); RegQueryValueExW(hkey, name, reserved, NULL, (BYTE *)value, &length); length = ExpandEnvironmentStringsW(value, NULL, 0); } else { length = (lstrlenW(buff) + 1) * sizeof(WCHAR); value = heap_alloc(length); memcpy(value, buff, length); length = ExpandEnvironmentStringsW(value, buff, *buff_len / sizeof(WCHAR)); if (length > *buff_len) ret = ERROR_MORE_DATA; } data_len = max(data_len, length); heap_free(value); } if (type) *type = value_type == REG_EXPAND_SZ ? REG_SZ : value_type; if (buff_len) *buff_len = data_len; return ret; } /************************************************************************* * SHQueryValueExA [SHCORE.@] */ DWORD WINAPI SHQueryValueExA(HKEY hkey, const char *name, DWORD *reserved, DWORD *type, void *buff, DWORD *buff_len) { DWORD ret, value_type, data_len = 0; TRACE("(%p, %s, %p, %p, %p, %p)\n", hkey, debugstr_a(name), reserved, type, buff, buff_len); if (buff_len) data_len = *buff_len; ret = RegQueryValueExA(hkey, name, reserved, &value_type, buff, &data_len); if (ret != ERROR_SUCCESS && ret != ERROR_MORE_DATA) return ret; if (buff_len && value_type == REG_EXPAND_SZ) { DWORD length; char *value; if (!buff || ret == ERROR_MORE_DATA) { length = data_len; value = heap_alloc(length); RegQueryValueExA(hkey, name, reserved, NULL, (BYTE *)value, &length); length = ExpandEnvironmentStringsA(value, NULL, 0); } else { length = strlen(buff) + 1; value = heap_alloc(length); memcpy(value, buff, length); length = ExpandEnvironmentStringsA(value, buff, *buff_len); if (length > *buff_len) ret = ERROR_MORE_DATA; } data_len = max(data_len, length); heap_free(value); } if (type) *type = value_type == REG_EXPAND_SZ ? REG_SZ : value_type; if (buff_len) *buff_len = data_len; return ret; } /************************************************************************* * SHGetValueA [SHCORE.@] */ DWORD WINAPI SHGetValueA(HKEY hkey, const char *subkey, const char *value, DWORD *type, void *data, DWORD *data_len) { HKEY hsubkey = 0; DWORD ret = 0; TRACE("(%p, %s, %s, %p, %p, %p)\n", hkey, debugstr_a(subkey), debugstr_a(value), type, data, data_len); if (subkey) ret = RegOpenKeyExA(hkey, subkey, 0, KEY_QUERY_VALUE, &hsubkey); if (!ret) { ret = SHQueryValueExA(hsubkey ? hsubkey : hkey, value, 0, type, data, data_len); if (subkey) RegCloseKey(hsubkey); } return ret; } /************************************************************************* * SHGetValueW [SHCORE.@] */ DWORD WINAPI SHGetValueW(HKEY hkey, const WCHAR *subkey, const WCHAR *value, DWORD *type, void *data, DWORD *data_len) { HKEY hsubkey = 0; DWORD ret = 0; TRACE("(%p, %s, %s, %p, %p, %p)\n", hkey, debugstr_w(subkey), debugstr_w(value), type, data, data_len); if (subkey) ret = RegOpenKeyExW(hkey, subkey, 0, KEY_QUERY_VALUE, &hsubkey); if (!ret) { ret = SHQueryValueExW(hsubkey ? hsubkey : hkey, value, 0, type, data, data_len); if (subkey) RegCloseKey(hsubkey); } return ret; } /************************************************************************* * SHRegGetIntW [SHCORE.280] */ int WINAPI SHRegGetIntW(HKEY hkey, const WCHAR *value, int default_value) { WCHAR buff[32]; DWORD buff_len; TRACE("(%p, %s, %d)\n", hkey, debugstr_w(value), default_value); buff[0] = 0; buff_len = sizeof(buff); if (SHQueryValueExW(hkey, value, 0, 0, buff, &buff_len)) return default_value; if (*buff >= '0' && *buff <= '9') return wcstol(buff, NULL, 10); return default_value; } /************************************************************************* * SHRegGetPathA [SHCORE.@] */ DWORD WINAPI SHRegGetPathA(HKEY hkey, const char *subkey, const char *value, char *path, DWORD flags) { DWORD length = MAX_PATH; TRACE("(%p, %s, %s, %p, %#x)\n", hkey, debugstr_a(subkey), debugstr_a(value), path, flags); return SHGetValueA(hkey, subkey, value, 0, path, &length); } /************************************************************************* * SHRegGetPathW [SHCORE.@] */ DWORD WINAPI SHRegGetPathW(HKEY hkey, const WCHAR *subkey, const WCHAR *value, WCHAR *path, DWORD flags) { DWORD length = MAX_PATH; TRACE("(%p, %s, %s, %p, %d)\n", hkey, debugstr_w(subkey), debugstr_w(value), path, flags); return SHGetValueW(hkey, subkey, value, 0, path, &length); } /************************************************************************* * SHSetValueW [SHCORE.@] */ DWORD WINAPI SHSetValueW(HKEY hkey, const WCHAR *subkey, const WCHAR *value, DWORD type, const void *data, DWORD data_len) { DWORD ret = ERROR_SUCCESS, dummy; HKEY hsubkey; TRACE("(%p, %s, %s, %d, %p, %d)\n", hkey, debugstr_w(subkey), debugstr_w(value), type, data, data_len); if (subkey && *subkey) ret = RegCreateKeyExW(hkey, subkey, 0, NULL, 0, KEY_SET_VALUE, NULL, &hsubkey, &dummy); else hsubkey = hkey; if (!ret) { ret = RegSetValueExW(hsubkey, value, 0, type, data, data_len); if (hsubkey != hkey) RegCloseKey(hsubkey); } return ret; } /************************************************************************* * SHSetValueA [SHCORE.@] */ DWORD WINAPI SHSetValueA(HKEY hkey, const char *subkey, const char *value, DWORD type, const void *data, DWORD data_len) { DWORD ret = ERROR_SUCCESS, dummy; HKEY hsubkey; TRACE("(%p, %s, %s, %d, %p, %d)\n", hkey, debugstr_a(subkey), debugstr_a(value), type, data, data_len); if (subkey && *subkey) ret = RegCreateKeyExA(hkey, subkey, 0, NULL, 0, KEY_SET_VALUE, NULL, &hsubkey, &dummy); else hsubkey = hkey; if (!ret) { ret = RegSetValueExA(hsubkey, value, 0, type, data, data_len); if (hsubkey != hkey) RegCloseKey(hsubkey); } return ret; } /************************************************************************* * SHRegSetPathA [SHCORE.@] */ DWORD WINAPI SHRegSetPathA(HKEY hkey, const char *subkey, const char *value, const char *path, DWORD flags) { FIXME("(%p, %s, %s, %s, %#x) - semi-stub\n", hkey, debugstr_a(subkey), debugstr_a(value), debugstr_a(path), flags); /* FIXME: PathUnExpandEnvStringsA() */ return SHSetValueA(hkey, subkey, value, REG_SZ, path, lstrlenA(path)); } /************************************************************************* * SHRegSetPathW [SHCORE.@] */ DWORD WINAPI SHRegSetPathW(HKEY hkey, const WCHAR *subkey, const WCHAR *value, const WCHAR *path, DWORD flags) { FIXME("(%p, %s, %s, %s, %#x) - semi-stub\n", hkey, debugstr_w(subkey), debugstr_w(value), debugstr_w(path), flags); /* FIXME: PathUnExpandEnvStringsW(); */ return SHSetValueW(hkey, subkey, value, REG_SZ, path, lstrlenW(path)); } /************************************************************************* * SHQueryInfoKeyA [SHCORE.@] */ LONG WINAPI SHQueryInfoKeyA(HKEY hkey, DWORD *subkeys, DWORD *subkey_max, DWORD *values, DWORD *value_max) { TRACE("(%p, %p, %p, %p, %p)\n", hkey, subkeys, subkey_max, values, value_max); return RegQueryInfoKeyA(hkey, NULL, NULL, NULL, subkeys, subkey_max, NULL, values, value_max, NULL, NULL, NULL); } /************************************************************************* * SHQueryInfoKeyW [SHCORE.@] */ LONG WINAPI SHQueryInfoKeyW(HKEY hkey, DWORD *subkeys, DWORD *subkey_max, DWORD *values, DWORD *value_max) { TRACE("(%p, %p, %p, %p, %p)\n", hkey, subkeys, subkey_max, values, value_max); return RegQueryInfoKeyW(hkey, NULL, NULL, NULL, subkeys, subkey_max, NULL, values, value_max, NULL, NULL, NULL); } /************************************************************************* * IsOS [SHCORE.@] */ BOOL WINAPI IsOS(DWORD feature) { DWORD platform, majorv, minorv; OSVERSIONINFOA osvi; osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); if (!GetVersionExA(&osvi)) return FALSE; majorv = osvi.dwMajorVersion; minorv = osvi.dwMinorVersion; platform = osvi.dwPlatformId; #define ISOS_RETURN(x) \ TRACE("(0x%x) ret=%d\n",feature,(x)); \ return (x) switch(feature) { case OS_WIN32SORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32s || platform == VER_PLATFORM_WIN32_WINDOWS); case OS_NT: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_WIN95ORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS); case OS_NT4ORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 4); case OS_WIN2000ORGREATER_ALT: case OS_WIN2000ORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5); case OS_WIN98ORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv >= 10); case OS_WIN98_GOLD: ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv == 10); case OS_WIN2000PRO: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5); case OS_WIN2000SERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1)); case OS_WIN2000ADVSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1)); case OS_WIN2000DATACENTER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1)); case OS_WIN2000TERMINAL: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && (minorv == 0 || minorv == 1)); case OS_EMBEDDED: FIXME("(OS_EMBEDDED) What should we return here?\n"); return FALSE; case OS_TERMINALCLIENT: FIXME("(OS_TERMINALCLIENT) What should we return here?\n"); return FALSE; case OS_TERMINALREMOTEADMIN: FIXME("(OS_TERMINALREMOTEADMIN) What should we return here?\n"); return FALSE; case OS_WIN95_GOLD: ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv == 0); case OS_MEORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_WINDOWS && minorv >= 90); case OS_XPORGREATER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5 && minorv >= 1); case OS_HOME: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5 && minorv >= 1); case OS_PROFESSIONAL: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_DATACENTER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_ADVSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 5); case OS_SERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_TERMINALSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_PERSONALTERMINALSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && minorv >= 1 && majorv >= 5); case OS_FASTUSERSWITCHING: FIXME("(OS_FASTUSERSWITCHING) What should we return here?\n"); return TRUE; case OS_WELCOMELOGONUI: FIXME("(OS_WELCOMELOGONUI) What should we return here?\n"); return FALSE; case OS_DOMAINMEMBER: FIXME("(OS_DOMAINMEMBER) What should we return here?\n"); return TRUE; case OS_ANYSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_WOW6432: { BOOL is_wow64; IsWow64Process(GetCurrentProcess(), &is_wow64); return is_wow64; } case OS_WEBSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_SMALLBUSINESSSERVER: ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT); case OS_TABLETPC: FIXME("(OS_TABLETPC) What should we return here?\n"); return FALSE; case OS_SERVERADMINUI: FIXME("(OS_SERVERADMINUI) What should we return here?\n"); return FALSE; case OS_MEDIACENTER: FIXME("(OS_MEDIACENTER) What should we return here?\n"); return FALSE; case OS_APPLIANCE: FIXME("(OS_APPLIANCE) What should we return here?\n"); return FALSE; case 0x25: /*OS_VISTAORGREATER*/ ISOS_RETURN(platform == VER_PLATFORM_WIN32_NT && majorv >= 6); } #undef ISOS_RETURN WARN("(0x%x) unknown parameter\n", feature); return FALSE; }