/* * Copyright 2016 Michael Müller * Copyright 2017 Andrey Gusev * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include "ntstatus.h" #define WIN32_NO_STATUS #include "windows.h" #include "appmodel.h" #include "shlwapi.h" #include "perflib.h" #include "winternl.h" #include "wine/debug.h" #include "kernelbase.h" #include "wine/heap.h" #include "wine/list.h" WINE_DEFAULT_DEBUG_CHANNEL(kernelbase); BOOL is_wow64 = FALSE; /*********************************************************************** * DllMain */ BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) { if (reason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls( hinst ); IsWow64Process( GetCurrentProcess(), &is_wow64 ); init_locale(); init_startup_info( NtCurrentTeb()->Peb->ProcessParameters ); init_console(); } return TRUE; } /************************************************************* * DllMainCRTStartup */ BOOL WINAPI DllMainCRTStartup( HANDLE inst, DWORD reason, LPVOID reserved ) { return DllMain( inst, reason, reserved ); } /*********************************************************************** * MulDiv (kernelbase.@) */ INT WINAPI MulDiv( INT a, INT b, INT c ) { LONGLONG ret; if (!c) return -1; /* We want to deal with a positive divisor to simplify the logic. */ if (c < 0) { a = -a; c = -c; } /* If the result is positive, we "add" to round. else, we subtract to round. */ if ((a < 0 && b < 0) || (a >= 0 && b >= 0)) ret = (((LONGLONG)a * b) + (c / 2)) / c; else ret = (((LONGLONG)a * b) - (c / 2)) / c; if (ret > 2147483647 || ret < -2147483647) return -1; return ret; } /*********************************************************************** * AppPolicyGetMediaFoundationCodecLoading (KERNELBASE.@) */ LONG WINAPI AppPolicyGetMediaFoundationCodecLoading(HANDLE token, AppPolicyMediaFoundationCodecLoading *policy) { FIXME("%p, %p\n", token, policy); if(policy) *policy = AppPolicyMediaFoundationCodecLoading_All; return ERROR_SUCCESS; } /*********************************************************************** * AppPolicyGetProcessTerminationMethod (KERNELBASE.@) */ LONG WINAPI AppPolicyGetProcessTerminationMethod(HANDLE token, AppPolicyProcessTerminationMethod *policy) { FIXME("%p, %p\n", token, policy); if(policy) *policy = AppPolicyProcessTerminationMethod_ExitProcess; return ERROR_SUCCESS; } /*********************************************************************** * AppPolicyGetThreadInitializationType (KERNELBASE.@) */ LONG WINAPI AppPolicyGetThreadInitializationType(HANDLE token, AppPolicyThreadInitializationType *policy) { FIXME("%p, %p\n", token, policy); if(policy) *policy = AppPolicyThreadInitializationType_None; return ERROR_SUCCESS; } /*********************************************************************** * AppPolicyGetShowDeveloperDiagnostic (KERNELBASE.@) */ LONG WINAPI AppPolicyGetShowDeveloperDiagnostic(HANDLE token, AppPolicyShowDeveloperDiagnostic *policy) { FIXME("%p, %p\n", token, policy); if(policy) *policy = AppPolicyShowDeveloperDiagnostic_ShowUI; return ERROR_SUCCESS; } /*********************************************************************** * AppPolicyGetWindowingModel (KERNELBASE.@) */ LONG WINAPI AppPolicyGetWindowingModel(HANDLE token, AppPolicyWindowingModel *policy) { FIXME("%p, %p\n", token, policy); if(policy) *policy = AppPolicyWindowingModel_ClassicDesktop; return ERROR_SUCCESS; } struct counterset_template { PERF_COUNTERSET_INFO counterset; PERF_COUNTER_INFO counter[1]; }; struct counterset_instance { struct list entry; struct counterset_template *template; PERF_COUNTERSET_INSTANCE instance; }; struct perf_provider { GUID guid; PERFLIBREQUEST callback; struct counterset_template **countersets; unsigned int counterset_count; struct list instance_list; }; static struct perf_provider *perf_provider_from_handle(HANDLE prov) { return (struct perf_provider *)prov; } /*********************************************************************** * PerfCreateInstance (KERNELBASE.@) */ PERF_COUNTERSET_INSTANCE WINAPI *PerfCreateInstance( HANDLE handle, const GUID *guid, const WCHAR *name, ULONG id ) { struct perf_provider *prov = perf_provider_from_handle( handle ); struct counterset_template *template; struct counterset_instance *inst; unsigned int i; ULONG size; FIXME( "handle %p, guid %s, name %s, id %lu semi-stub.\n", handle, debugstr_guid(guid), debugstr_w(name), id ); if (!prov || !guid || !name) { SetLastError( ERROR_INVALID_PARAMETER ); return NULL; } for (i = 0; i < prov->counterset_count; ++i) if (IsEqualGUID(guid, &prov->countersets[i]->counterset.CounterSetGuid)) break; if (i == prov->counterset_count) { SetLastError( ERROR_NOT_FOUND ); return NULL; } template = prov->countersets[i]; LIST_FOR_EACH_ENTRY(inst, &prov->instance_list, struct counterset_instance, entry) { if (inst->template == template && inst->instance.InstanceId == id) { SetLastError( ERROR_ALREADY_EXISTS ); return NULL; } } size = (sizeof(PERF_COUNTERSET_INSTANCE) + template->counterset.NumCounters * sizeof(UINT64) + (lstrlenW( name ) + 1) * sizeof(WCHAR) + 7) & ~7; inst = heap_alloc_zero( offsetof(struct counterset_instance, instance) + size ); if (!inst) { SetLastError( ERROR_OUTOFMEMORY ); return NULL; } inst->template = template; inst->instance.CounterSetGuid = *guid; inst->instance.dwSize = size; inst->instance.InstanceId = id; inst->instance.InstanceNameOffset = sizeof(PERF_COUNTERSET_INSTANCE) + template->counterset.NumCounters * sizeof(UINT64); inst->instance.InstanceNameSize = (lstrlenW( name ) + 1) * sizeof(WCHAR); memcpy( (BYTE *)&inst->instance + inst->instance.InstanceNameOffset, name, inst->instance.InstanceNameSize ); list_add_tail( &prov->instance_list, &inst->entry ); return &inst->instance; } /*********************************************************************** * PerfDeleteInstance (KERNELBASE.@) */ ULONG WINAPI PerfDeleteInstance(HANDLE provider, PERF_COUNTERSET_INSTANCE *block) { struct perf_provider *prov = perf_provider_from_handle( provider ); struct counterset_instance *inst; TRACE( "provider %p, block %p.\n", provider, block ); if (!prov || !block) return ERROR_INVALID_PARAMETER; inst = CONTAINING_RECORD(block, struct counterset_instance, instance); list_remove( &inst->entry ); heap_free( inst ); return ERROR_SUCCESS; } /*********************************************************************** * PerfSetCounterSetInfo (KERNELBASE.@) */ ULONG WINAPI PerfSetCounterSetInfo( HANDLE handle, PERF_COUNTERSET_INFO *template, ULONG size ) { struct perf_provider *prov = perf_provider_from_handle( handle ); struct counterset_template **new_array; struct counterset_template *new; unsigned int i; FIXME( "handle %p, template %p, size %lu semi-stub.\n", handle, template, size ); if (!prov || !template) return ERROR_INVALID_PARAMETER; if (!template->NumCounters) return ERROR_INVALID_PARAMETER; if (size < sizeof(*template) || (size - (sizeof(*template))) / sizeof(PERF_COUNTER_INFO) < template->NumCounters) return ERROR_INVALID_PARAMETER; for (i = 0; i < prov->counterset_count; ++i) { if (IsEqualGUID( &template->CounterSetGuid, &prov->countersets[i]->counterset.CounterSetGuid )) return ERROR_ALREADY_EXISTS; } size = offsetof( struct counterset_template, counter[template->NumCounters] ); if (!(new = heap_alloc( size ))) return ERROR_OUTOFMEMORY; if (prov->counterset_count) new_array = heap_realloc( prov->countersets, sizeof(*prov->countersets) * (prov->counterset_count + 1) ); else new_array = heap_alloc( sizeof(*prov->countersets) ); if (!new_array) { heap_free( new ); return ERROR_OUTOFMEMORY; } memcpy( new, template, size ); for (i = 0; i < template->NumCounters; ++i) new->counter[i].Offset = i * sizeof(UINT64); new_array[prov->counterset_count++] = new; prov->countersets = new_array; return STATUS_SUCCESS; } /*********************************************************************** * PerfSetCounterRefValue (KERNELBASE.@) */ ULONG WINAPI PerfSetCounterRefValue(HANDLE provider, PERF_COUNTERSET_INSTANCE *instance, ULONG counterid, void *address) { struct perf_provider *prov = perf_provider_from_handle( provider ); struct counterset_template *template; struct counterset_instance *inst; unsigned int i; FIXME( "provider %p, instance %p, counterid %lu, address %p semi-stub.\n", provider, instance, counterid, address ); if (!prov || !instance || !address) return ERROR_INVALID_PARAMETER; inst = CONTAINING_RECORD(instance, struct counterset_instance, instance); template = inst->template; for (i = 0; i < template->counterset.NumCounters; ++i) if (template->counter[i].CounterId == counterid) break; if (i == template->counterset.NumCounters) return ERROR_NOT_FOUND; *(void **)((BYTE *)&inst->instance + sizeof(PERF_COUNTERSET_INSTANCE) + template->counter[i].Offset) = address; return STATUS_SUCCESS; } /*********************************************************************** * PerfStartProvider (KERNELBASE.@) */ ULONG WINAPI PerfStartProvider( GUID *guid, PERFLIBREQUEST callback, HANDLE *provider ) { PERF_PROVIDER_CONTEXT ctx; FIXME( "guid %s, callback %p, provider %p semi-stub.\n", debugstr_guid(guid), callback, provider ); memset( &ctx, 0, sizeof(ctx) ); ctx.ContextSize = sizeof(ctx); ctx.ControlCallback = callback; return PerfStartProviderEx( guid, &ctx, provider ); } /*********************************************************************** * PerfStartProviderEx (KERNELBASE.@) */ ULONG WINAPI PerfStartProviderEx( GUID *guid, PERF_PROVIDER_CONTEXT *context, HANDLE *provider ) { struct perf_provider *prov; FIXME( "guid %s, context %p, provider %p semi-stub.\n", debugstr_guid(guid), context, provider ); if (!guid || !context || !provider) return ERROR_INVALID_PARAMETER; if (context->ContextSize < sizeof(*context)) return ERROR_INVALID_PARAMETER; if (context->MemAllocRoutine || context->MemFreeRoutine) FIXME("Memory allocation routine is not supported.\n"); if (!(prov = heap_alloc_zero( sizeof(*prov) ))) return ERROR_OUTOFMEMORY; list_init( &prov->instance_list ); memcpy( &prov->guid, guid, sizeof(prov->guid) ); prov->callback = context->ControlCallback; *provider = prov; return STATUS_SUCCESS; } /*********************************************************************** * PerfStopProvider (KERNELBASE.@) */ ULONG WINAPI PerfStopProvider(HANDLE handle) { struct perf_provider *prov = perf_provider_from_handle( handle ); struct counterset_instance *inst, *next; unsigned int i; TRACE( "handle %p.\n", handle ); if (!list_empty( &prov->instance_list )) WARN( "Stopping provider with active counter instances.\n" ); LIST_FOR_EACH_ENTRY_SAFE(inst, next, &prov->instance_list, struct counterset_instance, entry) { list_remove( &inst->entry ); heap_free( inst ); } for (i = 0; i < prov->counterset_count; ++i) heap_free( prov->countersets[i] ); heap_free( prov->countersets ); heap_free( prov ); return STATUS_SUCCESS; } /*********************************************************************** * QuirkIsEnabled (KERNELBASE.@) */ BOOL WINAPI QuirkIsEnabled(void *arg) { FIXME("(%p): stub\n", arg); return FALSE; } /*********************************************************************** * QuirkIsEnabled3 (KERNELBASE.@) */ BOOL WINAPI QuirkIsEnabled3(void *unk1, void *unk2) { static int once; if (!once++) FIXME("(%p, %p) stub!\n", unk1, unk2); return FALSE; } HRESULT WINAPI QISearch(void *base, const QITAB *table, REFIID riid, void **obj) { const QITAB *ptr; IUnknown *unk; TRACE("%p, %p, %s, %p\n", base, table, debugstr_guid(riid), obj); if (!obj) return E_POINTER; for (ptr = table; ptr->piid; ++ptr) { TRACE("trying (offset %ld) %s\n", ptr->dwOffset, debugstr_guid(ptr->piid)); if (IsEqualIID(riid, ptr->piid)) { unk = (IUnknown *)((BYTE *)base + ptr->dwOffset); TRACE("matched, returning (%p)\n", unk); *obj = unk; IUnknown_AddRef(unk); return S_OK; } } if (IsEqualIID(riid, &IID_IUnknown)) { unk = (IUnknown *)((BYTE *)base + table->dwOffset); TRACE("returning first for IUnknown (%p)\n", unk); *obj = unk; IUnknown_AddRef(unk); return S_OK; } WARN("Not found %s.\n", debugstr_guid(riid)); *obj = NULL; return E_NOINTERFACE; } HRESULT WINAPI GetAcceptLanguagesA(LPSTR langbuf, DWORD *buflen) { DWORD buflenW, convlen; WCHAR *langbufW; HRESULT hr; TRACE("%p, %p, *%p: %ld\n", langbuf, buflen, buflen, buflen ? *buflen : -1); if (!langbuf || !buflen || !*buflen) return E_FAIL; buflenW = *buflen; langbufW = heap_alloc(sizeof(WCHAR) * buflenW); hr = GetAcceptLanguagesW(langbufW, &buflenW); if (hr == S_OK) { convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, -1, langbuf, *buflen, NULL, NULL); convlen--; /* do not count the terminating 0 */ } else /* copy partial string anyway */ { convlen = WideCharToMultiByte(CP_ACP, 0, langbufW, *buflen, langbuf, *buflen, NULL, NULL); if (convlen < *buflen) { langbuf[convlen] = 0; convlen--; /* do not count the terminating 0 */ } else { convlen = *buflen; } } *buflen = buflenW ? convlen : 0; heap_free(langbufW); return hr; } static HRESULT lcid_to_rfc1766(LCID lcid, WCHAR *rfc1766, INT len) { WCHAR buffer[6 /* MAX_RFC1766_NAME */]; INT n = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME, buffer, ARRAY_SIZE(buffer)); INT i; if (n) { i = PRIMARYLANGID(lcid); if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) && (SUBLANGID(lcid) == SUBLANG_DEFAULT)) || (SUBLANGID(lcid) > SUBLANG_DEFAULT)) { buffer[n - 1] = '-'; i = GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, ARRAY_SIZE(buffer) - n); if (!i) buffer[n - 1] = '\0'; } else i = 0; LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len); return ((n + i) > len) ? E_INVALIDARG : S_OK; } return E_FAIL; } HRESULT WINAPI GetAcceptLanguagesW(WCHAR *langbuf, DWORD *buflen) { DWORD mystrlen, mytype; WCHAR *mystr; LCID mylcid; HKEY mykey; LONG lres; DWORD len; TRACE("%p, %p, *%p: %ld\n", langbuf, buflen, buflen, buflen ? *buflen : -1); if (!langbuf || !buflen || !*buflen) return E_FAIL; mystrlen = (*buflen > 20) ? *buflen : 20 ; len = mystrlen * sizeof(WCHAR); mystr = heap_alloc(len); mystr[0] = 0; RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Internet Explorer\\International", 0, KEY_QUERY_VALUE, &mykey); lres = RegQueryValueExW(mykey, L"AcceptLanguage", 0, &mytype, (PBYTE)mystr, &len); RegCloseKey(mykey); len = lstrlenW(mystr); if (!lres && (*buflen > len)) { lstrcpyW(langbuf, mystr); *buflen = len; heap_free(mystr); return S_OK; } /* Did not find a value in the registry or the user buffer is too small */ mylcid = GetUserDefaultLCID(); lcid_to_rfc1766(mylcid, mystr, mystrlen); len = lstrlenW(mystr); memcpy(langbuf, mystr, min(*buflen, len + 1)*sizeof(WCHAR)); heap_free(mystr); if (*buflen > len) { *buflen = len; return S_OK; } *buflen = 0; return E_NOT_SUFFICIENT_BUFFER; }