From: Hermès Bélusca-Maïto Date: Thu, 8 May 2014 14:40:52 +0000 (+0000) Subject: Sync with trunk r63192. X-Git-Tag: backups/0.3.17@66124~619^2~72 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=7a0c3bde06485b108dfcb053ba4ecaa4ac629b5c Sync with trunk r63192. svn path=/branches/condrv_restructure/; revision=63193 --- 7a0c3bde06485b108dfcb053ba4ecaa4ac629b5c diff --cc dll/directx/wine/msdmo/dmoreg.c index 48968b37798,00000000000..84c1ed93a38 mode 100644,000000..100644 --- a/dll/directx/wine/msdmo/dmoreg.c +++ b/dll/directx/wine/msdmo/dmoreg.c @@@ -1,830 -1,0 +1,830 @@@ +/* + * Copyright (C) 2003 Michael Günnewig + * Copyright (C) 2003 CodeWeavers Inc. (Ulrich Czekalla) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +#include +#include +#include +#include + +#define MSDMO_MAJOR_VERSION 6 + +static const WCHAR szDMORootKey[] = +{ + 'D','i','r','e','c','t','S','h','o','w','\\', + 'M','e','d','i','a','O','b','j','e','c','t','s',0 +}; + +static const WCHAR szDMOInputType[] = +{ + 'I','n','p','u','t','T','y','p','e','s',0 +}; + +static const WCHAR szDMOOutputType[] = +{ + 'O','u','t','p','u','t','T','y','p','e','s',0 +}; + +static const WCHAR szDMOKeyed[] = +{ + 'K','e','y','e','d',0 +}; + +static const WCHAR szDMOCategories[] = +{ + 'C','a','t','e','g','o','r','i','e','s',0 +}; + +static const WCHAR szGUIDFmt[] = +{ + '%','0','8','X','-','%','0','4','X','-','%','0','4','X','-','%','0', + '2','X','%','0','2','X','-','%','0','2','X','%','0','2','X','%','0','2', + 'X','%','0','2','X','%','0','2','X','%','0','2','X',0 +}; + +static const WCHAR szCat3Fmt[] = +{ + '%','s','\\','%','s','\\','%','s',0 +}; + +static const WCHAR szCat2Fmt[] = +{ + '%','s','\\','%','s',0 +}; + +static const WCHAR szToGuidFmt[] = +{ + '{','%','s','}',0 +}; + + +typedef struct +{ + IEnumDMO IEnumDMO_iface; + LONG ref; + DWORD index; + const GUID* guidCategory; + DWORD dwFlags; + DWORD cInTypes; + DMO_PARTIAL_MEDIATYPE *pInTypes; + DWORD cOutTypes; + DMO_PARTIAL_MEDIATYPE *pOutTypes; + HKEY hkey; +} IEnumDMOImpl; + +static inline IEnumDMOImpl *impl_from_IEnumDMO(IEnumDMO *iface) +{ + return CONTAINING_RECORD(iface, IEnumDMOImpl, IEnumDMO_iface); +} + +static HRESULT read_types(HKEY root, LPCWSTR key, ULONG *supplied, ULONG requested, DMO_PARTIAL_MEDIATYPE* types); + +static const IEnumDMOVtbl edmovt; + +static LPWSTR GUIDToString(LPWSTR lpwstr, REFGUID lpcguid) +{ + wsprintfW(lpwstr, szGUIDFmt, lpcguid->Data1, lpcguid->Data2, + lpcguid->Data3, lpcguid->Data4[0], lpcguid->Data4[1], + lpcguid->Data4[2], lpcguid->Data4[3], lpcguid->Data4[4], + lpcguid->Data4[5], lpcguid->Data4[6], lpcguid->Data4[7]); + + return lpwstr; +} + +static BOOL IsMediaTypeEqual(const DMO_PARTIAL_MEDIATYPE* mt1, const DMO_PARTIAL_MEDIATYPE* mt2) +{ + + return (IsEqualCLSID(&mt1->type, &mt2->type) || + IsEqualCLSID(&mt2->type, &GUID_NULL) || + IsEqualCLSID(&mt1->type, &GUID_NULL)) && + (IsEqualCLSID(&mt1->subtype, &mt2->subtype) || + IsEqualCLSID(&mt2->subtype, &GUID_NULL) || + IsEqualCLSID(&mt1->subtype, &GUID_NULL)); +} + +static HRESULT write_types(HKEY hkey, LPCWSTR name, const DMO_PARTIAL_MEDIATYPE* types, DWORD count) +{ + HRESULT hres = S_OK; + if (MSDMO_MAJOR_VERSION > 5) + { + hres = RegSetValueExW(hkey, name, 0, REG_BINARY, (const BYTE*) types, + count* sizeof(DMO_PARTIAL_MEDIATYPE)); + } + else + { + HKEY skey1,skey2,skey3; + DWORD index = 0; + WCHAR szGuidKey[64]; + + hres = RegCreateKeyExW(hkey, name, 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &skey1, NULL); + while (index < count) + { + GUIDToString(szGuidKey,&types[index].type); + hres = RegCreateKeyExW(skey1, szGuidKey, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &skey2, NULL); + GUIDToString(szGuidKey,&types[index].subtype); + hres = RegCreateKeyExW(skey2, szGuidKey, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &skey3, NULL); + RegCloseKey(skey3); + RegCloseKey(skey2); + index ++; + } + RegCloseKey(skey1); + } + + return hres; +} + +/*************************************************************** + * DMORegister (MSDMO.@) + * + * Register a DirectX Media Object. + */ +HRESULT WINAPI DMORegister( + LPCWSTR szName, + REFCLSID clsidDMO, + REFGUID guidCategory, + DWORD dwFlags, + DWORD cInTypes, + const DMO_PARTIAL_MEDIATYPE *pInTypes, + DWORD cOutTypes, + const DMO_PARTIAL_MEDIATYPE *pOutTypes +) +{ + WCHAR szguid[64]; + HRESULT hres; + HKEY hrkey = 0; + HKEY hkey = 0; + HKEY hckey = 0; + HKEY hclskey = 0; + + TRACE("%s\n", debugstr_w(szName)); + + hres = RegCreateKeyExW(HKEY_CLASSES_ROOT, szDMORootKey, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hrkey, NULL); + if (ERROR_SUCCESS != hres) + goto lend; + + /* Create clsidDMO key under MediaObjects */ + hres = RegCreateKeyExW(hrkey, GUIDToString(szguid, clsidDMO), 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL); + if (ERROR_SUCCESS != hres) + goto lend; + + /* Set default Name value */ + hres = RegSetValueExW(hkey, NULL, 0, REG_SZ, (const BYTE*) szName, + (strlenW(szName) + 1) * sizeof(WCHAR)); + + /* Set InputTypes */ + hres = write_types(hkey, szDMOInputType, pInTypes, cInTypes); + + /* Set OutputTypes */ + hres = write_types(hkey, szDMOOutputType, pOutTypes, cOutTypes); + + if (dwFlags & DMO_REGISTERF_IS_KEYED) + { + /* Create Keyed key */ + hres = RegCreateKeyExW(hkey, szDMOKeyed, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hckey, NULL); + if (ERROR_SUCCESS != hres) + goto lend; + RegCloseKey(hckey); + } + + /* Register the category */ + hres = RegCreateKeyExW(hrkey, szDMOCategories, 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hckey, NULL); + if (ERROR_SUCCESS != hres) + goto lend; + + RegCloseKey(hkey); + + hres = RegCreateKeyExW(hckey, GUIDToString(szguid, guidCategory), 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL); + if (ERROR_SUCCESS != hres) + goto lend; + hres = RegCreateKeyExW(hkey, GUIDToString(szguid, clsidDMO), 0, NULL, + REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hclskey, NULL); + if (ERROR_SUCCESS != hres) + goto lend; + +lend: + if (hkey) + RegCloseKey(hkey); + if (hckey) + RegCloseKey(hckey); + if (hclskey) + RegCloseKey(hclskey); + if (hrkey) + RegCloseKey(hrkey); + + TRACE(" hresult=0x%08x\n", hres); + return hres; +} + + +/*************************************************************** + * DMOUnregister (MSDMO.@) + * + * Unregister a DirectX Media Object. + */ +HRESULT WINAPI DMOUnregister(REFCLSID clsidDMO, REFGUID guidCategory) +{ + HRESULT hres; + WCHAR szguid[64]; + HKEY hrkey = 0; + HKEY hckey = 0; + + GUIDToString(szguid, clsidDMO); + + TRACE("%s %p\n", debugstr_w(szguid), guidCategory); + + hres = RegOpenKeyExW(HKEY_CLASSES_ROOT, szDMORootKey, 0, KEY_WRITE, &hrkey); + if (ERROR_SUCCESS != hres) + goto lend; + + hres = RegDeleteKeyW(hrkey, szguid); + if (ERROR_SUCCESS != hres) + goto lend; + + hres = RegOpenKeyExW(hrkey, szDMOCategories, 0, KEY_WRITE, &hckey); + if (ERROR_SUCCESS != hres) + goto lend; + + hres = RegDeleteKeyW(hckey, szguid); + if (ERROR_SUCCESS != hres) + goto lend; + +lend: + if (hckey) + RegCloseKey(hckey); + if (hrkey) + RegCloseKey(hrkey); + + return hres; +} + + +/*************************************************************** + * DMOGetName (MSDMO.@) + * + * Get DMP Name from the registry + */ +HRESULT WINAPI DMOGetName(REFCLSID clsidDMO, WCHAR szName[]) +{ + WCHAR szguid[64]; + HRESULT hres; + HKEY hrkey = 0; + HKEY hkey = 0; + static const INT max_name_len = 80; + DWORD count; + + TRACE("%s\n", debugstr_guid(clsidDMO)); + + hres = RegOpenKeyExW(HKEY_CLASSES_ROOT, szDMORootKey, + 0, KEY_READ, &hrkey); + if (ERROR_SUCCESS != hres) + goto lend; + + hres = RegOpenKeyExW(hrkey, GUIDToString(szguid, clsidDMO), + 0, KEY_READ, &hkey); + if (ERROR_SUCCESS != hres) + goto lend; + + count = max_name_len * sizeof(WCHAR); + hres = RegQueryValueExW(hkey, NULL, NULL, NULL, + (LPBYTE) szName, &count); + + TRACE(" szName=%s\n", debugstr_w(szName)); +lend: + if (hkey) + RegCloseKey(hrkey); + if (hkey) + RegCloseKey(hkey); + + return hres; +} + + +/************************************************************************** +* IEnumDMOImpl_Destructor +*/ +static BOOL IEnumDMOImpl_Destructor(IEnumDMOImpl* This) +{ + TRACE("%p\n", This); + + if (This->hkey) + RegCloseKey(This->hkey); + + HeapFree(GetProcessHeap(), 0, This->pInTypes); + HeapFree(GetProcessHeap(), 0, This->pOutTypes); + + return TRUE; +} + + +/************************************************************************** + * IEnumDMO_Constructor + */ +static IEnumDMO * IEnumDMO_Constructor( + REFGUID guidCategory, + DWORD dwFlags, + DWORD cInTypes, + const DMO_PARTIAL_MEDIATYPE *pInTypes, + DWORD cOutTypes, + const DMO_PARTIAL_MEDIATYPE *pOutTypes) +{ + UINT size; + IEnumDMOImpl* lpedmo; + BOOL ret = FALSE; + + lpedmo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumDMOImpl)); + + if (lpedmo) + { + lpedmo->ref = 1; + lpedmo->IEnumDMO_iface.lpVtbl = &edmovt; + lpedmo->index = -1; + lpedmo->guidCategory = guidCategory; + lpedmo->dwFlags = dwFlags; + + if (cInTypes > 0) + { + size = cInTypes * sizeof(DMO_PARTIAL_MEDIATYPE); + lpedmo->pInTypes = HeapAlloc(GetProcessHeap(), 0, size); + if (!lpedmo->pInTypes) + goto lerr; + memcpy(lpedmo->pInTypes, pInTypes, size); + lpedmo->cInTypes = cInTypes; + } + + if (cOutTypes > 0) + { + size = cOutTypes * sizeof(DMO_PARTIAL_MEDIATYPE); + lpedmo->pOutTypes = HeapAlloc(GetProcessHeap(), 0, size); + if (!lpedmo->pOutTypes) + goto lerr; + memcpy(lpedmo->pOutTypes, pOutTypes, size); + lpedmo->cOutTypes = cOutTypes; + } + + /* If not filtering by category enum from media objects root */ + if (IsEqualGUID(guidCategory, &GUID_NULL)) + { + if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_CLASSES_ROOT, szDMORootKey, + 0, KEY_READ, &lpedmo->hkey)) + ret = TRUE; + } + else + { + WCHAR szguid[64]; + WCHAR szKey[MAX_PATH]; + + wsprintfW(szKey, szCat3Fmt, szDMORootKey, szDMOCategories, + GUIDToString(szguid, guidCategory)); + if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_CLASSES_ROOT, szKey, + 0, KEY_READ, &lpedmo->hkey)) + ret = TRUE; + } + +lerr: + if(!ret) + { + IEnumDMOImpl_Destructor(lpedmo); + HeapFree(GetProcessHeap(),0,lpedmo); + lpedmo = NULL; + } + } + + TRACE("returning %p\n", lpedmo); + + return (IEnumDMO*)lpedmo; +} + + +/****************************************************************************** + * IEnumDMO_fnAddRef + */ +static ULONG WINAPI IEnumDMO_fnAddRef(IEnumDMO * iface) +{ + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + return InterlockedIncrement(&This->ref); +} + + +/************************************************************************** + * EnumDMO_QueryInterface + */ +static HRESULT WINAPI IEnumDMO_fnQueryInterface( + IEnumDMO* iface, + REFIID riid, + LPVOID *ppvObj) +{ + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IUnknown)) + *ppvObj = This; + else if(IsEqualIID(riid, &IID_IEnumDMO)) + *ppvObj = This; + + if(*ppvObj) + { + IEnumDMO_fnAddRef(*ppvObj); + return S_OK; + } + + return E_NOINTERFACE; +} + + +/****************************************************************************** + * IEnumDMO_fnRelease + */ +static ULONG WINAPI IEnumDMO_fnRelease(IEnumDMO * iface) +{ + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + ULONG refCount = InterlockedDecrement(&This->ref); + + if (!refCount) + { + IEnumDMOImpl_Destructor(This); + HeapFree(GetProcessHeap(),0,This); + } + return refCount; +} + + +/****************************************************************************** + * IEnumDMO_fnNext + */ +static HRESULT WINAPI IEnumDMO_fnNext( + IEnumDMO * iface, + DWORD cItemsToFetch, + CLSID * pCLSID, + WCHAR ** Names, + DWORD * pcItemsFetched) +{ + FILETIME ft; + HKEY hkey; + WCHAR szNextKey[MAX_PATH]; + WCHAR szGuidKey[64]; + WCHAR szKey[MAX_PATH]; + WCHAR szValue[MAX_PATH]; + DWORD len; + UINT count = 0; + HRESULT hres = S_OK; + + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + + TRACE("--> (%p) %d %p %p %p\n", iface, cItemsToFetch, pCLSID, Names, pcItemsFetched); + + if (!pCLSID || !Names || !pcItemsFetched) + return E_POINTER; + + while (count < cItemsToFetch) + { + This->index++; + + len = MAX_PATH; + hres = RegEnumKeyExW(This->hkey, This->index, szNextKey, &len, NULL, NULL, NULL, &ft); + if (hres != ERROR_SUCCESS) + break; + + TRACE("found %s\n", debugstr_w(szNextKey)); + + if (!(This->dwFlags & DMO_ENUMF_INCLUDE_KEYED)) + { + wsprintfW(szKey, szCat3Fmt, szDMORootKey, szNextKey, szDMOKeyed); + hres = RegOpenKeyExW(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hkey); + if (ERROR_SUCCESS == hres) + { + RegCloseKey(hkey); + /* Skip Keyed entries */ + continue; + } + } + + wsprintfW(szKey, szCat2Fmt, szDMORootKey, szNextKey); + hres = RegOpenKeyExW(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hkey); + + if (This->pInTypes) + { + UINT i, j; + DWORD cInTypes; + DMO_PARTIAL_MEDIATYPE* pInTypes; + + hres = read_types(hkey, szDMOInputType, &cInTypes, + sizeof(szValue)/sizeof(DMO_PARTIAL_MEDIATYPE), + (DMO_PARTIAL_MEDIATYPE*)szValue); + + if (ERROR_SUCCESS != hres) + { + RegCloseKey(hkey); + continue; + } + + pInTypes = (DMO_PARTIAL_MEDIATYPE*) szValue; + + for (i = 0; i < This->cInTypes; i++) + { + for (j = 0; j < cInTypes; j++) + { + if (IsMediaTypeEqual(&pInTypes[j], &This->pInTypes[i])) + break; + } + + if (j >= cInTypes) + break; + } + + if (i < This->cInTypes) + { + RegCloseKey(hkey); + continue; + } + } + + if (This->pOutTypes) + { + UINT i, j; + DWORD cOutTypes; + DMO_PARTIAL_MEDIATYPE* pOutTypes; + + hres = read_types(hkey, szDMOOutputType, &cOutTypes, + sizeof(szValue)/sizeof(DMO_PARTIAL_MEDIATYPE), + (DMO_PARTIAL_MEDIATYPE*)szValue); + + if (ERROR_SUCCESS != hres) + { + RegCloseKey(hkey); + continue; + } + + pOutTypes = (DMO_PARTIAL_MEDIATYPE*) szValue; + + for (i = 0; i < This->cOutTypes; i++) + { + for (j = 0; j < cOutTypes; j++) + { + if (IsMediaTypeEqual(&pOutTypes[j], &This->pOutTypes[i])) + break; + } + + if (j >= cOutTypes) + break; + } + + if (i < This->cOutTypes) + { + RegCloseKey(hkey); + continue; + } + } + + /* Media object wasn't filtered so add it to return list */ + Names[count] = NULL; + len = MAX_PATH * sizeof(WCHAR); + hres = RegQueryValueExW(hkey, NULL, NULL, NULL, (LPBYTE) szValue, &len); + if (ERROR_SUCCESS == hres) + { - Names[count] = HeapAlloc(GetProcessHeap(), 0, strlenW(szValue) + 1); ++ Names[count] = HeapAlloc(GetProcessHeap(), 0, (strlenW(szValue) + 1) * sizeof(WCHAR)); + if (Names[count]) - strcmpW(Names[count], szValue); ++ strcpyW(Names[count], szValue); + } + wsprintfW(szGuidKey,szToGuidFmt,szNextKey); + CLSIDFromString(szGuidKey, &pCLSID[count]); + + TRACE("found match %s %s\n", debugstr_w(szValue), debugstr_w(szNextKey)); + RegCloseKey(hkey); + count++; + } + + *pcItemsFetched = count; + if (*pcItemsFetched < cItemsToFetch) + hres = S_FALSE; + + TRACE("<-- %i found\n",count); + return hres; +} + + +/****************************************************************************** + * IEnumDMO_fnSkip + */ +static HRESULT WINAPI IEnumDMO_fnSkip(IEnumDMO * iface, DWORD cItemsToSkip) +{ + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + + This->index += cItemsToSkip; + + return S_OK; +} + + +/****************************************************************************** + * IEnumDMO_fnReset + */ +static HRESULT WINAPI IEnumDMO_fnReset(IEnumDMO * iface) +{ + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + + This->index = -1; + + return S_OK; +} + + +/****************************************************************************** + * IEnumDMO_fnClone + */ +static HRESULT WINAPI IEnumDMO_fnClone(IEnumDMO * iface, IEnumDMO **ppEnum) +{ + IEnumDMOImpl *This = impl_from_IEnumDMO(iface); + + FIXME("(%p)->() to (%p)->() E_NOTIMPL\n", This, ppEnum); + + return E_NOTIMPL; +} + + +/*************************************************************** + * DMOEnum (MSDMO.@) + * + * Enumerate DirectX Media Objects in the registry. + */ +HRESULT WINAPI DMOEnum( + REFGUID guidCategory, + DWORD dwFlags, + DWORD cInTypes, + const DMO_PARTIAL_MEDIATYPE *pInTypes, + DWORD cOutTypes, + const DMO_PARTIAL_MEDIATYPE *pOutTypes, + IEnumDMO **ppEnum) +{ + HRESULT hres = E_FAIL; + + TRACE("guidCategory=%p dwFlags=0x%08x cInTypes=%d cOutTypes=%d\n", + guidCategory, dwFlags, cInTypes, cOutTypes); + + *ppEnum = IEnumDMO_Constructor(guidCategory, dwFlags, cInTypes, + pInTypes, cOutTypes, pOutTypes); + if (*ppEnum) + hres = S_OK; + + return hres; +} + + +static const IEnumDMOVtbl edmovt = +{ + IEnumDMO_fnQueryInterface, + IEnumDMO_fnAddRef, + IEnumDMO_fnRelease, + IEnumDMO_fnNext, + IEnumDMO_fnSkip, + IEnumDMO_fnReset, + IEnumDMO_fnClone, +}; + + +HRESULT read_types(HKEY root, LPCWSTR key, ULONG *supplied, ULONG requested, DMO_PARTIAL_MEDIATYPE* types ) +{ + HRESULT ret = S_OK; + if (MSDMO_MAJOR_VERSION > 5) + { + DWORD len; + len = requested * sizeof(DMO_PARTIAL_MEDIATYPE); + ret = RegQueryValueExW(root, key, NULL, NULL, (LPBYTE) types, &len); + *supplied = len / sizeof(DMO_PARTIAL_MEDIATYPE); + } + else + { + HKEY hkey; + WCHAR szGuidKey[64]; + + *supplied = 0; + if (ERROR_SUCCESS == RegOpenKeyExW(root, key, 0, KEY_READ, &hkey)) + { + int index = 0; + WCHAR szNextKey[MAX_PATH]; + DWORD len; + LONG rc = ERROR_SUCCESS; + + while (rc == ERROR_SUCCESS) + { + len = MAX_PATH; + rc = RegEnumKeyExW(hkey, index, szNextKey, &len, NULL, NULL, NULL, NULL); + if (rc == ERROR_SUCCESS) + { + HKEY subk; + int sub_index = 0; + LONG rcs = ERROR_SUCCESS; + WCHAR szSubKey[MAX_PATH]; + + RegOpenKeyExW(hkey, szNextKey, 0, KEY_READ, &subk); + while (rcs == ERROR_SUCCESS) + { + len = MAX_PATH; + rcs = RegEnumKeyExW(subk, sub_index, szSubKey, &len, NULL, NULL, NULL, NULL); + if (rcs == ERROR_SUCCESS) + { + if (*supplied >= requested) + { + /* Bailing */ + ret = S_FALSE; + rc = ERROR_MORE_DATA; + rcs = ERROR_MORE_DATA; + break; + } + + wsprintfW(szGuidKey,szToGuidFmt,szNextKey); + CLSIDFromString(szGuidKey, &types[*supplied].type); + wsprintfW(szGuidKey,szToGuidFmt,szSubKey); + CLSIDFromString(szGuidKey, &types[*supplied].subtype); + TRACE("Adding type %s subtype %s at index %i\n", + debugstr_guid(&types[*supplied].type), + debugstr_guid(&types[*supplied].subtype), + *supplied); + (*supplied)++; + } + sub_index++; + } + index++; + } + } + RegCloseKey(hkey); + } + } + return ret; +} + +/*************************************************************** + * DMOGetTypes (MSDMO.@) + */ +HRESULT WINAPI DMOGetTypes(REFCLSID clsidDMO, + ULONG ulInputTypesRequested, + ULONG* pulInputTypesSupplied, + DMO_PARTIAL_MEDIATYPE* pInputTypes, + ULONG ulOutputTypesRequested, + ULONG* pulOutputTypesSupplied, + DMO_PARTIAL_MEDIATYPE* pOutputTypes) +{ + HKEY root,hkey; + HRESULT ret = S_OK; + WCHAR szguid[64]; + + TRACE ("(%s,%u,%p,%p,%u,%p,%p)\n", debugstr_guid(clsidDMO), ulInputTypesRequested, + pulInputTypesSupplied, pInputTypes, ulOutputTypesRequested, pulOutputTypesSupplied, + pOutputTypes); + + if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_CLASSES_ROOT, szDMORootKey, 0, + KEY_READ, &root)) + return E_FAIL; + + if (ERROR_SUCCESS != RegOpenKeyExW(root,GUIDToString(szguid,clsidDMO) , 0, + KEY_READ, &hkey)) + { + RegCloseKey(root); + return E_FAIL; + } + + if (ulInputTypesRequested > 0) + { + ret = read_types(hkey, szDMOInputType, pulInputTypesSupplied, ulInputTypesRequested, pInputTypes ); + } + else + *pulInputTypesSupplied = 0; + + if (ulOutputTypesRequested > 0) + { + HRESULT ret2; + ret2 = read_types(hkey, szDMOOutputType, pulOutputTypesSupplied, ulOutputTypesRequested, pOutputTypes ); + + if (ret == S_OK) + ret = ret2; + } + else + *pulOutputTypesSupplied = 0; + + return ret; +} diff --cc dll/ntdll/CMakeLists.txt index afa15fd2c24,00000000000..d99009315ac mode 100644,000000..100644 --- a/dll/ntdll/CMakeLists.txt +++ b/dll/ntdll/CMakeLists.txt @@@ -1,66 -1,0 +1,67 @@@ + +spec2def(ntdll.dll def/ntdll.spec ADD_IMPORTLIB) + +add_definitions( + -D__NTDLL__ + -D_NTOSKRNL_ + -DCRTDLL) + +include_directories( + BEFORE include + ${REACTOS_SOURCE_DIR}/include/reactos/subsys + ${REACTOS_SOURCE_DIR}/win32ss/include) + +list(APPEND SOURCE + csr/api.c + csr/capture.c + csr/connect.c + dbg/dbgui.c + ldr/ldrapi.c + ldr/ldrinit.c + ldr/ldrpe.c + ldr/ldrutils.c + rtl/libsupp.c + rtl/version.c + include/ntdll.h) + +if(ARCH STREQUAL "i386") + list(APPEND ASM_SOURCE dispatch/i386/dispatch.S) +elseif(ARCH STREQUAL "amd64") + list(APPEND ASM_SOURCE dispatch/amd64/dispatch.S) +elseif(ARCH STREQUAL "arm") + list(APPEND ASM_SOURCE dispatch/arm/stubs_asm.s) +else() + list(APPEND SOURCE dispatch/dispatch.c) +endif() + +add_asm_files(ntdll_asm ${ASM_SOURCE}) + +add_library(ntdll SHARED + ${SOURCE} + ${ntdll_asm} + def/ntdll.rc ++ ${CMAKE_CURRENT_BINARY_DIR}/ntdll_stubs.c + ${CMAKE_CURRENT_BINARY_DIR}/ntdll.def) + +set_module_type(ntdll win32dll HOTPATCHABLE ENTRYPOINT 0) +############################################# +## HACK FOR MSVC COMPILATION WITH win32dll ## +set_subsystem(ntdll console) +################# END HACK ################# + +if(MSVC) + add_target_link_flags(ntdll "/RELEASE") +endif() + +target_link_libraries(ntdll + rtl + ntdllsys + libcntpr + uuid + ${PSEH_LIB}) + +add_pch(ntdll include/ntdll.h SOURCE) +add_dependencies(ntdll ntstatus asm) + +add_cd_file(TARGET ntdll DESTINATION reactos/system32 NO_CAB FOR all) + diff --cc dll/ntdll/csr/capture.c index 5c8d09ef4dd,00000000000..0718a3b1d49 mode 100644,000000..100644 --- a/dll/ntdll/csr/capture.c +++ b/dll/ntdll/csr/capture.c @@@ -1,324 -1,0 +1,327 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: dll/ntdll/csr/capture.c + * PURPOSE: Routines for probing and capturing CSR API Messages + * PROGRAMMER: Alex Ionescu (alex@relsoft.net) + */ + +/* INCLUDES *******************************************************************/ + +#include + +#define NDEBUG +#include + +/* GLOBALS ********************************************************************/ + +extern HANDLE CsrPortHeap; + +/* FUNCTIONS ******************************************************************/ + +/* + * @implemented + */ +VOID +NTAPI +CsrProbeForRead(IN PVOID Address, + IN ULONG Length, + IN ULONG Alignment) +{ + volatile UCHAR *Pointer; + UCHAR Data; + + /* Validate length */ + if (Length == 0) return; + + /* Validate alignment */ + if ((ULONG_PTR)Address & (Alignment - 1)) + { + /* Raise exception if it doesn't match */ + RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); + } + + /* Probe first byte */ + Pointer = Address; + Data = *Pointer; + + /* Probe last byte */ + Pointer = (PUCHAR)Address + Length - 1; + Data = *Pointer; + (void)Data; +} + +/* + * @implemented + */ +VOID +NTAPI +CsrProbeForWrite(IN PVOID Address, + IN ULONG Length, + IN ULONG Alignment) +{ + volatile UCHAR *Pointer; + + /* Validate length */ + if (Length == 0) return; + + /* Validate alignment */ + if ((ULONG_PTR)Address & (Alignment - 1)) + { + /* Raise exception if it doesn't match */ + RtlRaiseStatus(STATUS_DATATYPE_MISALIGNMENT); + } + + /* Probe first byte */ + Pointer = Address; + *Pointer = *Pointer; + + /* Probe last byte */ + Pointer = (PUCHAR)Address + Length - 1; + *Pointer = *Pointer; +} + +/* + * @implemented + */ +PCSR_CAPTURE_BUFFER +NTAPI +CsrAllocateCaptureBuffer(IN ULONG ArgumentCount, + IN ULONG BufferSize) +{ + PCSR_CAPTURE_BUFFER CaptureBuffer; + + /* Validate size */ + if (BufferSize >= MAXLONG) return NULL; + + /* Add the size of the header and for each offset to the pointers */ + BufferSize += FIELD_OFFSET(CSR_CAPTURE_BUFFER, PointerOffsetsArray) + + (ArgumentCount * sizeof(ULONG_PTR)); + + /* Align it to a 4-byte boundary */ + BufferSize = (BufferSize + 3) & ~3; + ++ /* Add the size of the alignment padding for each argument */ ++ BufferSize += ArgumentCount * 3; ++ + /* Allocate memory from the port heap */ + CaptureBuffer = RtlAllocateHeap(CsrPortHeap, HEAP_ZERO_MEMORY, BufferSize); + if (CaptureBuffer == NULL) return NULL; + + /* Initialize the header */ + CaptureBuffer->Size = BufferSize; + CaptureBuffer->PointerCount = 0; + + /* Initialize all the offsets */ + RtlZeroMemory(CaptureBuffer->PointerOffsetsArray, + ArgumentCount * sizeof(ULONG_PTR)); + + /* Point to the start of the free buffer */ + CaptureBuffer->BufferEnd = (PVOID)((ULONG_PTR)CaptureBuffer->PointerOffsetsArray + + ArgumentCount * sizeof(ULONG_PTR)); + + /* Return the address of the buffer */ + return CaptureBuffer; +} + +/* + * @implemented + */ +ULONG +NTAPI +CsrAllocateMessagePointer(IN OUT PCSR_CAPTURE_BUFFER CaptureBuffer, + IN ULONG MessageLength, + OUT PVOID* CapturedData) +{ + if (MessageLength == 0) + { + *CapturedData = NULL; + CapturedData = NULL; + } + else + { + /* Set the capture data at our current available buffer */ + *CapturedData = CaptureBuffer->BufferEnd; + + /* Validate the size */ + if (MessageLength >= MAXLONG) return 0; + + /* Align it to a 4-byte boundary */ + MessageLength = (MessageLength + 3) & ~3; + + /* Move our available buffer beyond this space */ + CaptureBuffer->BufferEnd = (PVOID)((ULONG_PTR)CaptureBuffer->BufferEnd + MessageLength); + } + + /* Write down this pointer in the array and increase the count */ + CaptureBuffer->PointerOffsetsArray[CaptureBuffer->PointerCount++] = (ULONG_PTR)CapturedData; + + /* Return the aligned length */ + return MessageLength; +} + +/* + * @implemented + */ +VOID +NTAPI +CsrCaptureMessageBuffer(IN OUT PCSR_CAPTURE_BUFFER CaptureBuffer, + IN PVOID MessageBuffer OPTIONAL, + IN ULONG MessageLength, + OUT PVOID* CapturedData) +{ + /* Simply allocate a message pointer in the buffer */ + CsrAllocateMessagePointer(CaptureBuffer, MessageLength, CapturedData); + + /* Check if there was any data */ + if (!MessageBuffer || !MessageLength) return; + + /* Copy the data into the buffer */ + RtlMoveMemory(*CapturedData, MessageBuffer, MessageLength); +} + +/* + * @implemented + */ +VOID +NTAPI +CsrFreeCaptureBuffer(IN PCSR_CAPTURE_BUFFER CaptureBuffer) +{ + /* Free it from the heap */ + RtlFreeHeap(CsrPortHeap, 0, CaptureBuffer); +} + +/* + * @implemented + */ +VOID +NTAPI +CsrCaptureMessageString(IN OUT PCSR_CAPTURE_BUFFER CaptureBuffer, + IN PCSTR String OPTIONAL, + IN ULONG StringLength, + IN ULONG MaximumLength, + OUT PSTRING CapturedString) +{ + ASSERT(CapturedString != NULL); + + /* + * If we don't have a string, initialize an empty one, + * otherwise capture the given string. + */ + if (!String) + { + CapturedString->Length = 0; + CapturedString->MaximumLength = (USHORT)MaximumLength; + + /* Allocate a pointer for it */ + CsrAllocateMessagePointer(CaptureBuffer, + MaximumLength, + (PVOID*)&CapturedString->Buffer); + } + else + { + /* Cut-off the string length if needed */ + if (StringLength > MaximumLength) + StringLength = MaximumLength; + + CapturedString->Length = (USHORT)StringLength; + + /* Allocate a buffer and get its size */ + CapturedString->MaximumLength = + (USHORT)CsrAllocateMessagePointer(CaptureBuffer, + MaximumLength, + (PVOID*)&CapturedString->Buffer); + + /* If the string has data, copy it into the buffer */ + if (StringLength) + RtlMoveMemory(CapturedString->Buffer, String, StringLength); + } + + /* Null-terminate the string if we don't take up the whole space */ + if (CapturedString->Length < CapturedString->MaximumLength) + CapturedString->Buffer[CapturedString->Length] = '\0'; +} + +static VOID +CsrCaptureMessageUnicodeStringInPlace(IN OUT PCSR_CAPTURE_BUFFER CaptureBuffer, + IN PUNICODE_STRING String) +{ + ASSERT(String != NULL); + + /* This is a way to capture the UNICODE string, since (Maximum)Length are also in bytes */ + CsrCaptureMessageString(CaptureBuffer, + (PCSTR)String->Buffer, + String->Length, + String->MaximumLength, + (PSTRING)String); + + /* Null-terminate the string */ + if (String->MaximumLength >= String->Length + sizeof(WCHAR)) + { + String->Buffer[String->Length / sizeof(WCHAR)] = L'\0'; + } +} + +/* + * @implemented + */ +NTSTATUS +NTAPI +CsrCaptureMessageMultiUnicodeStringsInPlace(OUT PCSR_CAPTURE_BUFFER* CaptureBuffer, + IN ULONG StringsCount, + IN PUNICODE_STRING* MessageStrings) +{ + ULONG Count; + + if (!CaptureBuffer) return STATUS_INVALID_PARAMETER; + + /* Allocate a new capture buffer if we don't have one already */ + if (!*CaptureBuffer) + { + /* Compute the required size for the capture buffer */ + ULONG Size = 0; + + Count = 0; + while (Count < StringsCount) + { + if (MessageStrings[Count]) + Size += MessageStrings[Count]->MaximumLength; + + ++Count; + } + + /* Allocate the capture buffer */ + *CaptureBuffer = CsrAllocateCaptureBuffer(StringsCount, Size); + if (!*CaptureBuffer) return STATUS_NO_MEMORY; + } + + /* Now capture each UNICODE string */ + Count = 0; + while (Count < StringsCount) + { + if (MessageStrings[Count]) + CsrCaptureMessageUnicodeStringInPlace(*CaptureBuffer, MessageStrings[Count]); + + ++Count; + } + + return STATUS_SUCCESS; +} + +/* + * @implemented + */ +PLARGE_INTEGER +NTAPI +CsrCaptureTimeout(IN ULONG Milliseconds, + OUT PLARGE_INTEGER Timeout) +{ + /* Validate the time */ + if (Milliseconds == -1) return NULL; + + /* Convert to relative ticks */ + Timeout->QuadPart = Int32x32To64(Milliseconds, -10000); + return Timeout; +} + +/* EOF */ diff --cc dll/ntdll/def/ntdll.spec index 4ed630d4604,00000000000..e6f87994c59 mode 100644,000000..100644 --- a/dll/ntdll/def/ntdll.spec +++ b/dll/ntdll/def/ntdll.spec @@@ -1,1398 -1,0 +1,1398 @@@ + +@ stdcall CsrAllocateCaptureBuffer(long long) +@ stdcall CsrAllocateMessagePointer(ptr long ptr) +@ stdcall CsrCaptureMessageBuffer(ptr ptr long ptr) +@ stdcall CsrCaptureMessageMultiUnicodeStringsInPlace(ptr long ptr) +@ stdcall CsrCaptureMessageString(ptr str long long ptr) +@ stdcall CsrCaptureTimeout(long ptr) +@ stdcall CsrClientCallServer(ptr ptr long long) +@ stdcall CsrClientConnectToServer(str long ptr ptr ptr) +@ stdcall CsrFreeCaptureBuffer(ptr) +@ stdcall CsrGetProcessId() +@ stdcall CsrIdentifyAlertableThread() +@ stdcall CsrNewThread() +@ stdcall CsrProbeForRead(ptr long long) +@ stdcall CsrProbeForWrite(ptr long long) +@ stdcall CsrSetPriorityClass(ptr ptr) +@ stdcall DbgBreakPoint() +@ varargs DbgPrint(str) +@ varargs DbgPrintEx(long long str) +@ varargs DbgPrintReturnControlC(str) +@ stdcall DbgPrompt(ptr ptr long) +@ stdcall DbgQueryDebugFilterState(long long) +@ stdcall DbgSetDebugFilterState(long long long) +@ stdcall DbgUiConnectToDbg() +@ stdcall DbgUiContinue(ptr long) +@ stdcall DbgUiConvertStateChangeStructure(ptr ptr) +@ stdcall DbgUiDebugActiveProcess(ptr) +@ stdcall DbgUiGetThreadDebugObject() +@ stdcall DbgUiIssueRemoteBreakin(ptr) +@ stdcall DbgUiRemoteBreakin() +@ stdcall DbgUiSetThreadDebugObject(ptr) +@ stdcall DbgUiStopDebugging(ptr) +@ stdcall DbgUiWaitStateChange(ptr ptr) +@ stdcall DbgUserBreakPoint() +@ stdcall -arch=i386 KiFastSystemCall() +@ stdcall -arch=i386 KiFastSystemCallRet() +@ stdcall -arch=i386 KiIntSystemCall() +@ stdcall -arch=i386,x86_64 ExpInterlockedPopEntrySListEnd() +@ stdcall -arch=i386,x86_64 ExpInterlockedPopEntrySListFault() +@ stdcall -arch=i386,x86_64 ExpInterlockedPopEntrySListResume() +@ stdcall KiRaiseUserExceptionDispatcher() +@ stdcall KiUserApcDispatcher(ptr ptr ptr ptr) +@ stdcall KiUserCallbackDispatcher(ptr ptr long) ; CHECKME +@ stdcall KiUserExceptionDispatcher(ptr ptr) +@ stdcall LdrAccessOutOfProcessResource(ptr ptr ptr ptr ptr) +@ stdcall LdrAccessResource(long ptr ptr ptr) +@ stdcall LdrAddRefDll(long ptr) +;@ stdcall LdrAlternateResourcesEnabled +@ stdcall LdrCreateOutOfProcessImage(long ptr ptr ptr) +@ stdcall LdrDestroyOutOfProcessImage(ptr) +@ stdcall LdrDisableThreadCalloutsForDll(long) +@ stdcall LdrEnumResources(ptr ptr long ptr ptr) +@ stdcall LdrEnumerateLoadedModules(long ptr long) +@ stdcall LdrFindCreateProcessManifest(long ptr ptr long ptr) ; 5.1 and 5.2 only +@ stdcall LdrFindEntryForAddress(ptr ptr) +@ stdcall LdrFindResourceDirectory_U(long ptr long ptr) +;@ stdcall LdrFindResourceEx_U ; 5.1 and higher +@ stdcall LdrFindResource_U(long ptr long ptr) +@ stdcall LdrFlushAlternateResourceModules() +@ stdcall LdrGetDllHandle(wstr long ptr ptr) +@ stdcall LdrGetDllHandleEx(long wstr long ptr ptr) +@ stdcall LdrGetProcedureAddress(ptr ptr long ptr) +;@ stdcall LdrHotPatchRoutine +;@ stdcall LdrInitShimEngineDynamic +@ stdcall LdrInitializeThunk(long long long long) +@ stdcall LdrLoadAlternateResourceModule(ptr ptr) +@ stdcall LdrLoadDll(wstr long ptr ptr) +@ stdcall LdrLockLoaderLock(long ptr ptr) +@ stdcall LdrOpenImageFileOptionsKey(ptr long ptr) ; 5.2 SP1 and higher +@ stdcall LdrProcessRelocationBlock(ptr long ptr long) +@ stdcall LdrQueryImageFileExecutionOptions(ptr str long ptr long ptr) +@ stdcall LdrQueryImageFileKeyOption(ptr ptr long ptr long ptr) +@ stdcall LdrQueryProcessModuleInformation(ptr long ptr) +;@ stdcall LdrSetAppCompatDllRedirectionCallback +@ stdcall LdrSetDllManifestProber(ptr) +@ stdcall LdrShutdownProcess() +@ stdcall LdrShutdownThread() +@ stdcall LdrUnloadAlternateResourceModule(ptr) +@ stdcall LdrUnloadDll(ptr) +@ stdcall LdrUnlockLoaderLock(long long) +@ stdcall LdrVerifyImageMatchesChecksum(ptr long long long) +@ extern NlsAnsiCodePage +@ extern NlsMbCodePageTag +@ extern NlsMbOemCodePageTag +@ stdcall NtAcceptConnectPort(ptr long ptr long long ptr) +@ stdcall NtAccessCheck(ptr long long ptr ptr ptr ptr ptr) +@ stdcall NtAccessCheckAndAuditAlarm(ptr long ptr ptr ptr long ptr long ptr ptr ptr) +@ stdcall NtAccessCheckByType(ptr ptr ptr long ptr long ptr ptr long ptr ptr) +@ stdcall NtAccessCheckByTypeAndAuditAlarm(ptr ptr ptr ptr ptr ptr long long long ptr long ptr long ptr ptr ptr) +@ stdcall NtAccessCheckByTypeResultList(ptr ptr ptr long ptr long ptr ptr long ptr ptr) +@ stdcall NtAccessCheckByTypeResultListAndAuditAlarm(ptr ptr ptr ptr ptr ptr long long long ptr long ptr long ptr ptr ptr) +@ stdcall NtAccessCheckByTypeResultListAndAuditAlarmByHandle(ptr ptr ptr ptr ptr ptr ptr long long long ptr long ptr long ptr ptr ptr) +@ stdcall NtAddAtom(ptr long ptr) +@ stdcall NtAddBootEntry(ptr long) +@ stdcall NtAddDriverEntry(ptr long) ; 5.2 and higher +@ stdcall NtAdjustGroupsToken(long long ptr long ptr ptr) +@ stdcall NtAdjustPrivilegesToken(long long long long long long) +@ stdcall NtAlertResumeThread(long ptr) +@ stdcall NtAlertThread(long) +@ stdcall NtAllocateLocallyUniqueId(ptr) +@ stdcall NtAllocateUserPhysicalPages(ptr ptr ptr) +@ stdcall NtAllocateUuids(ptr ptr ptr ptr) +@ stdcall NtAllocateVirtualMemory(long ptr ptr ptr long long) +@ stdcall NtApphelpCacheControl(long ptr) +@ stdcall NtAreMappedFilesTheSame(ptr ptr) +@ stdcall NtAssignProcessToJobObject(long long) +@ stdcall NtCallbackReturn(ptr long long) +@ stdcall NtCancelDeviceWakeupRequest(ptr) +@ stdcall NtCancelIoFile(long ptr) +@ stdcall NtCancelTimer(long ptr) +@ stdcall NtClearEvent(long) +@ stdcall NtClose(long) +@ stdcall NtCloseObjectAuditAlarm(ptr ptr long) +@ stdcall NtCompactKeys(long ptr) +@ stdcall NtCompareTokens(ptr ptr ptr) +@ stdcall NtCompleteConnectPort(ptr) +@ stdcall NtCompressKey(ptr) +@ stdcall NtConnectPort(ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall NtContinue(ptr long) +@ stdcall NtCreateDebugObject(ptr long ptr long) +@ stdcall NtCreateDirectoryObject(long long long) +@ stdcall NtCreateEvent(long long long long long) +@ stdcall NtCreateEventPair(ptr long ptr) +@ stdcall NtCreateFile(ptr long ptr ptr long long long ptr long long ptr) +@ stdcall NtCreateIoCompletion(ptr long ptr long) +@ stdcall NtCreateJobObject(ptr long ptr) +@ stdcall NtCreateJobSet(long ptr long) +@ stdcall NtCreateKey(ptr long ptr long ptr long long) +@ stdcall NtCreateKeyedEvent(ptr long ptr long) +@ stdcall NtCreateMailslotFile(long long long long long long long long) +@ stdcall NtCreateMutant(ptr long ptr long) +@ stdcall NtCreateNamedPipeFile(ptr long ptr ptr long long long long long long long long long ptr) +@ stdcall NtCreatePagingFile(long long long long) +@ stdcall NtCreatePort(ptr ptr long long ptr) +@ stdcall NtCreateProcess(ptr long ptr ptr long ptr ptr ptr) +@ stdcall NtCreateProcessEx(ptr long ptr ptr long ptr ptr ptr long) +@ stdcall NtCreateProfile(ptr ptr ptr long long ptr long long long) ; CHECKME +@ stdcall NtCreateSection(ptr long ptr ptr long long long) +@ stdcall NtCreateSemaphore(ptr long ptr long long) +@ stdcall NtCreateSymbolicLinkObject(ptr long ptr ptr) +@ stdcall NtCreateThread(ptr long ptr ptr ptr ptr ptr long) +@ stdcall NtCreateTimer(ptr long ptr long) +@ stdcall NtCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall NtCreateWaitablePort(ptr ptr long long long) +@ stdcall -arch=win32 NtCurrentTeb() _NtCurrentTeb +@ stdcall NtDebugActiveProcess(ptr ptr) +@ stdcall NtDebugContinue(ptr ptr long) +@ stdcall NtDelayExecution(long ptr) +@ stdcall NtDeleteAtom(long) +@ stdcall NtDeleteBootEntry(long) +@ stdcall NtDeleteFile(ptr) +@ stdcall NtDeleteKey(long) +@ stdcall NtDeleteObjectAuditAlarm(ptr ptr long) +@ stdcall NtDeleteValueKey(long ptr) +@ stdcall NtDeviceIoControlFile(long long long long long long long long long long) +@ stdcall NtDisplayString(ptr) +@ stdcall NtDuplicateObject(long long long ptr long long long) +@ stdcall NtDuplicateToken(long long long long long long) +@ stdcall NtEnumerateBootEntries(ptr ptr) +@ stdcall NtEnumerateKey (long long long long long long) +@ stdcall NtEnumerateSystemEnvironmentValuesEx(long ptr long) +@ stdcall NtEnumerateValueKey(long long long long long long) +@ stdcall NtExtendSection(ptr ptr) +@ stdcall NtFilterToken(ptr long ptr ptr ptr ptr) +@ stdcall NtFindAtom(ptr long ptr) +@ stdcall NtFlushBuffersFile(long ptr) +@ stdcall NtFlushInstructionCache(long ptr long) +@ stdcall NtFlushKey(long) +@ stdcall NtFlushVirtualMemory(long ptr ptr long) +@ stdcall NtFlushWriteBuffer() +@ stdcall NtFreeUserPhysicalPages(ptr ptr ptr) +@ stdcall NtFreeVirtualMemory(long ptr ptr long) +@ stdcall NtFsControlFile(long long long long long long long long long long) +@ stdcall NtGetContextThread(long ptr) +@ stdcall NtGetCurrentProcessorNumber() ; 5.2 and higher +@ stdcall NtGetDevicePowerState(ptr ptr) +@ stdcall NtGetPlugPlayEvent(long long ptr long) - ;@ stdcall NtGetTickCount() ++@ stdcall -stub NtGetTickCount() +@ stdcall NtGetWriteWatch(long long ptr long ptr ptr ptr) +@ stdcall NtImpersonateAnonymousToken(ptr) +@ stdcall NtImpersonateClientOfPort(ptr ptr) +@ stdcall NtImpersonateThread(ptr ptr ptr) +@ stdcall NtInitializeRegistry(long) +@ stdcall NtInitiatePowerAction (long long long long) +@ stdcall NtIsProcessInJob(long long) +@ stdcall NtIsSystemResumeAutomatic() +@ stdcall NtListenPort(ptr ptr) +@ stdcall NtLoadDriver(ptr) +@ stdcall NtLoadKey2(ptr ptr long) +@ stdcall NtLoadKey(ptr ptr) +@ stdcall NtLockFile(long long ptr ptr ptr ptr ptr ptr long long) +@ stdcall NtLockProductActivationKeys(ptr ptr) +@ stdcall NtLockRegistryKey(ptr) +@ stdcall NtLockVirtualMemory(long ptr ptr long) +@ stdcall NtMakePermanentObject(ptr) +@ stdcall NtMakeTemporaryObject(long) +@ stdcall NtMapUserPhysicalPages(ptr ptr ptr) +@ stdcall NtMapUserPhysicalPagesScatter(ptr ptr ptr) +@ stdcall NtMapViewOfSection(long long ptr long long ptr ptr long long long) +@ stdcall NtModifyBootEntry(ptr) +@ stdcall NtNotifyChangeDirectoryFile(long long ptr ptr ptr ptr long long long) +@ stdcall NtNotifyChangeKey(long long ptr ptr ptr long long ptr long long) +@ stdcall NtNotifyChangeMultipleKeys(ptr long ptr ptr ptr ptr ptr long long ptr long long) +@ stdcall NtOpenDirectoryObject(long long long) +@ stdcall NtOpenEvent(long long long) +@ stdcall NtOpenEventPair(ptr long ptr) +@ stdcall NtOpenFile(ptr long ptr ptr long long) +@ stdcall NtOpenIoCompletion(ptr long ptr) +@ stdcall NtOpenJobObject(ptr long ptr) +@ stdcall NtOpenKey(ptr long ptr) +@ stdcall NtOpenKeyedEvent(ptr long ptr) +@ stdcall NtOpenMutant(ptr long ptr) +@ stdcall NtOpenObjectAuditAlarm(ptr ptr ptr ptr ptr ptr long long ptr long long ptr) +@ stdcall NtOpenProcess(ptr long ptr ptr) +@ stdcall NtOpenProcessToken(long long ptr) +@ stdcall NtOpenProcessTokenEx(long long long ptr) +@ stdcall NtOpenSection(ptr long ptr) +@ stdcall NtOpenSemaphore(long long ptr) +@ stdcall NtOpenSymbolicLinkObject (ptr long ptr) +@ stdcall NtOpenThread(ptr long ptr ptr) +@ stdcall NtOpenThreadToken(long long long ptr) +@ stdcall NtOpenThreadTokenEx(long long long long ptr) +@ stdcall NtOpenTimer(ptr long ptr) +@ stdcall NtPlugPlayControl(ptr ptr long) +@ stdcall NtPowerInformation(long ptr long ptr long) +@ stdcall NtPrivilegeCheck(ptr ptr ptr) +@ stdcall NtPrivilegeObjectAuditAlarm(ptr ptr ptr long ptr long) +@ stdcall NtPrivilegedServiceAuditAlarm(ptr ptr ptr ptr long) +@ stdcall NtProtectVirtualMemory(long ptr ptr long ptr) +@ stdcall NtPulseEvent(long ptr) +@ stdcall NtQueryAttributesFile(ptr ptr) +@ stdcall NtQueryBootEntryOrder(ptr ptr) +@ stdcall NtQueryBootOptions(ptr ptr) +@ stdcall NtQueryDebugFilterState(long long) +@ stdcall NtQueryDefaultLocale(long ptr) +@ stdcall NtQueryDefaultUILanguage(ptr) +@ stdcall NtQueryDirectoryFile(long long ptr ptr ptr ptr long long long ptr long) +@ stdcall NtQueryDirectoryObject(long ptr long long long ptr ptr) +@ stdcall NtQueryEaFile(long ptr ptr long long ptr long ptr long) +@ stdcall NtQueryEvent(long long ptr long ptr) +@ stdcall NtQueryFullAttributesFile(ptr ptr) +@ stdcall NtQueryInformationAtom(long long ptr long ptr) +@ stdcall NtQueryInformationFile(long ptr ptr long long) +@ stdcall NtQueryInformationJobObject(long long ptr long ptr) +@ stdcall NtQueryInformationPort(ptr long ptr long ptr) +@ stdcall NtQueryInformationProcess(long long ptr long ptr) +@ stdcall NtQueryInformationThread(long long ptr long ptr) +@ stdcall NtQueryInformationToken(long long ptr long ptr) +@ stdcall NtQueryInstallUILanguage(ptr) +@ stdcall NtQueryIntervalProfile(long ptr) +@ stdcall NtQueryIoCompletion(long long ptr long ptr) +@ stdcall NtQueryKey (long long ptr long ptr) +@ stdcall NtQueryMultipleValueKey(long ptr long ptr long ptr) +@ stdcall NtQueryMutant(long long ptr long ptr) +@ stdcall NtQueryObject(long long long long long) +@ stdcall NtQueryOpenSubKeys(ptr ptr) +@ stdcall NtQueryPerformanceCounter(ptr ptr) +@ stdcall NtQueryPortInformationProcess() +@ stdcall NtQueryQuotaInformationFile(ptr ptr ptr long long ptr long ptr long) +@ stdcall NtQuerySection (long long long long long) +@ stdcall NtQuerySecurityObject (long long long long long) +@ stdcall NtQuerySemaphore (long long ptr long ptr) +@ stdcall NtQuerySymbolicLinkObject(long ptr ptr) +@ stdcall NtQuerySystemEnvironmentValue(ptr ptr long ptr) +@ stdcall NtQuerySystemEnvironmentValueEx(ptr ptr ptr ptr ptr) +@ stdcall NtQuerySystemInformation(long long long long) +@ stdcall NtQuerySystemTime(ptr) +@ stdcall NtQueryTimer(ptr long ptr long ptr) +@ stdcall NtQueryTimerResolution(long long long) +@ stdcall NtQueryValueKey(long long long long long long) +@ stdcall NtQueryVirtualMemory(long ptr long ptr long ptr) +@ stdcall NtQueryVolumeInformationFile(long ptr ptr long long) +@ stdcall NtQueueApcThread(long ptr long long long) +@ stdcall NtRaiseException(ptr ptr long) +@ stdcall NtRaiseHardError(long long long ptr long ptr) +@ stdcall NtReadFile(long long ptr ptr ptr ptr long ptr ptr) +@ stdcall NtReadFileScatter(long long ptr ptr ptr ptr long ptr ptr) +@ stdcall NtReadRequestData(ptr ptr long ptr long ptr) +@ stdcall NtReadVirtualMemory(long ptr ptr long ptr) +@ stdcall NtRegisterThreadTerminatePort(ptr) +@ stdcall NtReleaseKeyedEvent(ptr ptr long ptr) +@ stdcall NtReleaseMutant(long ptr) +@ stdcall NtReleaseSemaphore(long long ptr) +@ stdcall NtRemoveIoCompletion(ptr ptr ptr ptr ptr) +@ stdcall NtRemoveProcessDebug(ptr ptr) +@ stdcall NtRenameKey(ptr ptr) +@ stdcall NtReplaceKey(ptr long ptr) +@ stdcall NtReplyPort(ptr ptr) +@ stdcall NtReplyWaitReceivePort(ptr ptr ptr ptr) +@ stdcall NtReplyWaitReceivePortEx(ptr ptr ptr ptr ptr) +@ stdcall NtReplyWaitReplyPort(ptr ptr) +@ stdcall NtRequestDeviceWakeup(ptr) +@ stdcall NtRequestPort(ptr ptr) +@ stdcall NtRequestWaitReplyPort(ptr ptr ptr) +@ stdcall NtRequestWakeupLatency(long) +@ stdcall NtResetEvent(long ptr) +@ stdcall NtResetWriteWatch(long ptr long) +@ stdcall NtRestoreKey(long long long) +@ stdcall NtResumeProcess(ptr) +@ stdcall NtResumeThread(long long) +@ stdcall NtSaveKey(long long) +@ stdcall NtSaveKeyEx(ptr ptr long) +@ stdcall NtSaveMergedKeys(ptr ptr ptr) +@ stdcall NtSecureConnectPort(ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall NtSetBootEntryOrder(ptr ptr) +@ stdcall NtSetBootOptions(ptr long) +@ stdcall NtSetContextThread(long ptr) +@ stdcall NtSetDebugFilterState(long long long) +@ stdcall NtSetDefaultHardErrorPort(ptr) +@ stdcall NtSetDefaultLocale(long long) +@ stdcall NtSetDefaultUILanguage(long) +@ stdcall NtSetEaFile(long ptr ptr long) +@ stdcall NtSetEvent(long long) +@ stdcall NtSetEventBoostPriority(ptr) +@ stdcall NtSetHighEventPair(ptr) +@ stdcall NtSetHighWaitLowEventPair(ptr) +@ stdcall NtSetInformationDebugObject(ptr long ptr long ptr) +@ stdcall NtSetInformationFile(long long long long long) +@ stdcall NtSetInformationJobObject(long long ptr long) +@ stdcall NtSetInformationKey(long long ptr long) +@ stdcall NtSetInformationObject(long long ptr long) +@ stdcall NtSetInformationProcess(long long long long) +@ stdcall NtSetInformationThread(long long ptr long) +@ stdcall NtSetInformationToken(long long ptr long) +@ stdcall NtSetIntervalProfile(long long) +@ stdcall NtSetIoCompletion(ptr long ptr long long) +@ stdcall NtSetLdtEntries(long double long double) ; CHECKME +@ stdcall NtSetLowEventPair(ptr) +@ stdcall NtSetLowWaitHighEventPair(ptr) +@ stdcall NtSetQuotaInformationFile(ptr ptr ptr long) +@ stdcall NtSetSecurityObject(long long ptr) +@ stdcall NtSetSystemEnvironmentValue(ptr ptr) +@ stdcall NtSetSystemEnvironmentValueEx(ptr ptr) +@ stdcall NtSetSystemInformation(long ptr long) +@ stdcall NtSetSystemPowerState(long long long) +@ stdcall NtSetSystemTime(ptr ptr) +@ stdcall NtSetThreadExecutionState(long ptr) +@ stdcall NtSetTimer(long ptr ptr ptr long long ptr) +@ stdcall NtSetTimerResolution(long long ptr) +@ stdcall NtSetUuidSeed(ptr) +@ stdcall NtSetValueKey(long long long long long long) +@ stdcall NtSetVolumeInformationFile(long ptr ptr long long) +@ stdcall NtShutdownSystem(long) +@ stdcall NtSignalAndWaitForSingleObject(long long long ptr) +@ stdcall NtStartProfile(ptr) +@ stdcall NtStopProfile(ptr) +@ stdcall NtSuspendProcess(ptr) +@ stdcall NtSuspendThread(long ptr) +@ stdcall NtSystemDebugControl(long ptr long ptr long ptr) +@ stdcall NtTerminateJobObject(long long) +@ stdcall NtTerminateProcess(long long) +@ stdcall NtTerminateThread(long long) +@ stdcall NtTestAlert() +@ stdcall NtTraceEvent(long long long ptr) +@ stdcall NtTranslateFilePath(ptr long ptr long) +@ stdcall NtUnloadDriver(ptr) +@ stdcall NtUnloadKey(long) +@ stdcall NtUnloadKeyEx(ptr ptr) +@ stdcall NtUnlockFile(long ptr ptr ptr ptr) +@ stdcall NtUnlockVirtualMemory(long ptr ptr long) +@ stdcall NtUnmapViewOfSection(long ptr) +@ stdcall NtVdmControl(long ptr) +@ stdcall NtWaitForDebugEvent(ptr long ptr ptr) +@ stdcall NtWaitForKeyedEvent(ptr ptr long ptr) +@ stdcall NtWaitForMultipleObjects(long ptr long long ptr) +@ stdcall NtWaitForSingleObject(long long long) +@ stdcall NtWaitHighEventPair(ptr) +@ stdcall NtWaitLowEventPair(ptr) +@ stdcall NtWriteFile(long long ptr ptr ptr ptr long ptr ptr) +@ stdcall NtWriteFileGather(long long ptr ptr ptr ptr long ptr ptr) +@ stdcall NtWriteRequestData(ptr ptr long ptr long ptr) +@ stdcall NtWriteVirtualMemory(long ptr ptr long ptr) +@ stdcall NtYieldExecution() +;@ stdcall PfxFindPrefix +;@ stdcall PfxInitialize +;@ stdcall PfxInsertPrefix +;@ stdcall PfxRemovePrefix +;@ stdcall PropertyLengthAsVariant +@ stdcall RtlAbortRXact(ptr) +@ stdcall RtlAbsoluteToSelfRelativeSD(ptr ptr ptr) +@ stdcall RtlAcquirePebLock() +@ stdcall RtlAcquirePrivilege(ptr long long ptr) +@ stdcall RtlAcquireResourceExclusive(ptr long) +@ stdcall RtlAcquireResourceShared(ptr long) +@ stdcall RtlAcquireSRWLockExclusive(ptr) +@ stdcall RtlAcquireSRWLockShared(ptr) +@ stdcall RtlActivateActivationContext(long ptr ptr) +@ stdcall RtlActivateActivationContextEx(long ptr ptr ptr) +@ fastcall RtlActivateActivationContextUnsafeFast(ptr ptr) +@ stdcall RtlAddAccessAllowedAce(ptr long long ptr) +@ stdcall RtlAddAccessAllowedAceEx(ptr long long long ptr) +@ stdcall RtlAddAccessAllowedObjectAce(ptr long long long ptr ptr ptr) +@ stdcall RtlAddAccessDeniedAce(ptr long long ptr) +@ stdcall RtlAddAccessDeniedAceEx(ptr long long long ptr) +@ stdcall RtlAddAccessDeniedObjectAce(ptr long long long ptr ptr ptr) +@ stdcall RtlAddAce(ptr long long ptr long) +@ stdcall RtlAddActionToRXact(ptr long ptr long ptr long) +@ stdcall RtlAddAtomToAtomTable(ptr wstr ptr) +@ stdcall RtlAddAttributeActionToRXact(ptr long ptr ptr ptr long ptr long) +@ stdcall RtlAddAuditAccessAce(ptr long long ptr long long) +@ stdcall RtlAddAuditAccessAceEx(ptr long long long ptr long long) +@ stdcall RtlAddAuditAccessObjectAce(ptr long long long ptr ptr ptr long long) +;@ stdcall RtlAddCompoundAce +;@ stdcall RtlAddRange ; 5.0 and 5.1 only +@ stdcall -arch=x86_64 RtlAddFunctionTable(ptr long long) +;@ stdcall RtlAddMandatoryAce(ptr long long long long ptr) +@ stdcall RtlAddRefActivationContext(ptr) +@ stdcall RtlAddRefMemoryStream(ptr) +@ stdcall RtlAddVectoredContinueHandler(long ptr) +@ stdcall RtlAddVectoredExceptionHandler(long ptr) +;@ stdcall RtlAddressInSectionTable +@ stdcall RtlAdjustPrivilege(long long long ptr) +@ stdcall RtlAllocateActivationContextStack(ptr) ; CHECKME +@ stdcall RtlAllocateAndInitializeSid(ptr long long long long long long long long long ptr) +@ stdcall RtlAllocateHandle(ptr ptr) +@ stdcall RtlAllocateHeap(ptr long ptr) +@ stdcall RtlAnsiCharToUnicodeChar(ptr) +@ stdcall RtlAnsiStringToUnicodeSize(ptr) RtlxAnsiStringToUnicodeSize +@ stdcall RtlAnsiStringToUnicodeString(ptr ptr long) +@ stdcall RtlAppendAsciizToString(ptr str) +;@ stdcall RtlAppendPathElement +@ stdcall RtlAppendStringToString(ptr ptr) +@ stdcall RtlAppendUnicodeStringToString(ptr ptr) +@ stdcall RtlAppendUnicodeToString(ptr wstr) +;@ stdcall RtlApplicationVerifierStop +@ stdcall RtlApplyRXact(ptr) +@ stdcall RtlApplyRXactNoFlush(ptr) +@ stdcall RtlAreAllAccessesGranted(long long) +@ stdcall RtlAreAnyAccessesGranted(long long) +@ stdcall RtlAreBitsClear(ptr long long) +@ stdcall RtlAreBitsSet(ptr long long) +@ stdcall RtlAssert(ptr ptr long ptr) +;@ stdcall RtlCancelTimer +@ stdcall -register RtlCaptureContext(ptr) +@ stdcall RtlCaptureStackBackTrace(long long ptr ptr) +;@ stdcall RtlCaptureStackContext +@ stdcall RtlCharToInteger(ptr long ptr) +@ stdcall RtlCheckForOrphanedCriticalSections(ptr) +;@ stdcall RtlCheckProcessParameters +@ stdcall RtlCheckRegistryKey(long ptr) +@ stdcall RtlClearAllBits(ptr) +@ stdcall RtlClearBits(ptr long long) +@ stdcall RtlCloneMemoryStream(ptr ptr) +@ stdcall RtlCommitMemoryStream(ptr long) +@ stdcall RtlCompactHeap(long long) +@ stdcall RtlCompareMemory(ptr ptr long) +@ stdcall RtlCompareMemoryUlong(ptr long long) +@ stdcall RtlCompareString(ptr ptr long) +@ stdcall RtlCompareUnicodeString (ptr ptr long) +@ stdcall RtlCompressBuffer(long ptr long ptr long long ptr ptr) +@ stdcall RtlComputeCrc32(long ptr long) +@ stdcall RtlComputeImportTableHash(ptr ptr long) +@ stdcall RtlComputePrivatizedDllName_U(ptr ptr ptr) +@ stdcall RtlConsoleMultiByteToUnicodeN(ptr long ptr ptr long ptr) +@ stdcall RtlConvertExclusiveToShared(ptr) +@ stdcall -arch=win32 -ret64 RtlConvertLongToLargeInteger(long) +;@ stdcall RtlConvertPropertyToVariant +@ stdcall RtlConvertSharedToExclusive(ptr) +@ stdcall RtlConvertSidToUnicodeString(ptr ptr long) +@ stdcall RtlConvertToAutoInheritSecurityObject(ptr ptr ptr ptr long ptr) +;@ stdcall RtlConvertUiListToApiList +@ stdcall -arch=win32 -ret64 RtlConvertUlongToLargeInteger(long) +;@ stdcall RtlConvertVariantToProperty +@ stdcall RtlCopyLuid(ptr ptr) +@ stdcall RtlCopyLuidAndAttributesArray(long ptr ptr) +;@ stdcall RtlCopyMappedMemory +@ stdcall RtlCopyMemoryStreamTo(ptr ptr int64 ptr ptr) +@ stdcall RtlCopyOutOfProcessMemoryStreamTo(ptr ptr int64 ptr ptr) RtlCopyMemoryStreamTo +;@ stdcall RtlCopyRangeList ; 5.0 and 5.1 only +@ stdcall RtlCopySecurityDescriptor(ptr ptr) +@ stdcall RtlCopySid(long ptr ptr) +@ stdcall RtlCopySidAndAttributesArray(long ptr long ptr ptr ptr ptr) +@ stdcall RtlCopyString(ptr ptr) +@ stdcall RtlCopyUnicodeString(ptr ptr) +@ stdcall RtlCreateAcl(ptr long long) +@ stdcall RtlCreateActivationContext(long ptr long ptr ptr ptr) +@ stdcall RtlCreateAndSetSD(ptr long ptr ptr ptr) +@ stdcall RtlCreateAtomTable(long ptr) +@ stdcall RtlCreateBootStatusDataFile() +@ stdcall RtlCreateEnvironment(long ptr) +@ stdcall RtlCreateHeap(long ptr long long ptr ptr) +@ stdcall RtlCreateProcessParameters(ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RtlCreateQueryDebugBuffer(long long) +@ stdcall RtlCreateRegistryKey(long wstr) +@ stdcall RtlCreateSecurityDescriptor(ptr long) +@ stdcall RtlCreateSystemVolumeInformationFolder(ptr) +@ stdcall RtlCreateTagHeap(ptr long str str) +@ stdcall RtlCreateTimer(ptr ptr ptr ptr long long long) +@ stdcall RtlCreateTimerQueue(ptr) +@ stdcall RtlCreateUnicodeString(ptr wstr) +@ stdcall RtlCreateUnicodeStringFromAsciiz(ptr str) +@ stdcall RtlCreateUserProcess(ptr long ptr ptr ptr ptr long ptr ptr ptr) +@ stdcall RtlCreateUserSecurityObject(ptr long ptr ptr long ptr ptr) +@ stdcall RtlCreateUserThread(long ptr long ptr long long ptr ptr ptr ptr) +@ stdcall RtlCustomCPToUnicodeN(ptr wstr long ptr str long) +@ stdcall RtlCutoverTimeToSystemTime(ptr ptr ptr long) +@ stdcall RtlDeNormalizeProcessParams(ptr) +@ stdcall RtlDeactivateActivationContext(long long) +@ fastcall RtlDeactivateActivationContextUnsafeFast(ptr) +@ stdcall RtlDecodePointer(ptr) +@ stdcall RtlDecodeSystemPointer(ptr) RtlEncodeSystemPointer +@ stdcall RtlDecompressBuffer(long ptr long ptr long ptr) +@ stdcall RtlDecompressFragment(long ptr long ptr long long ptr ptr) +@ stdcall RtlDefaultNpAcl(ptr) +@ stdcall RtlDelete(ptr) +@ stdcall RtlDeleteAce(ptr long) +@ stdcall RtlDeleteAtomFromAtomTable(ptr long) +@ stdcall RtlDeleteCriticalSection(ptr) +@ stdcall RtlDeleteElementGenericTable(ptr ptr) +@ stdcall RtlDeleteElementGenericTableAvl(ptr ptr) +@ cdecl -arch=x86_64 RtlDeleteFunctionTable(ptr) +@ stdcall RtlDeleteNoSplay(ptr ptr) +@ stdcall RtlDeleteOwnersRanges(ptr ptr) +@ stdcall RtlDeleteRange(ptr long long long long ptr) +@ stdcall RtlDeleteRegistryValue(long ptr ptr) +@ stdcall RtlDeleteResource(ptr) +@ stdcall RtlDeleteSecurityObject(ptr) +@ stdcall RtlDeleteTimer(ptr ptr ptr) +@ stdcall RtlDeleteTimerQueue(ptr) +@ stdcall RtlDeleteTimerQueueEx(ptr ptr) +@ stdcall RtlDeregisterWait(ptr) +@ stdcall RtlDeregisterWaitEx(ptr ptr) +@ stdcall RtlDestroyAtomTable(ptr) +@ stdcall RtlDestroyEnvironment(ptr) +@ stdcall RtlDestroyHandleTable(ptr) +@ stdcall RtlDestroyHeap(long) +@ stdcall RtlDestroyProcessParameters(ptr) +@ stdcall RtlDestroyQueryDebugBuffer(ptr) +@ stdcall RtlDetermineDosPathNameType_U(wstr) +@ stdcall RtlDllShutdownInProgress() +@ stdcall RtlDnsHostNameToComputerName(ptr ptr long) +@ stdcall RtlDoesFileExists_U(wstr) +@ stdcall RtlDosApplyFileIsolationRedirection_Ustr(long ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RtlDosPathNameToNtPathName_U(wstr ptr ptr ptr) +@ stdcall RtlDosPathNameToNtPathName_U_WithStatus(wstr ptr ptr ptr) ; 5.2 SP1, and higher +@ stdcall RtlDosPathNameToRelativeNtPathName_U(ptr ptr ptr ptr) +@ stdcall RtlDosPathNameToRelativeNtPathName_U_WithStatus(wstr ptr ptr ptr) +@ stdcall RtlDosSearchPath_U(wstr wstr wstr long ptr ptr) +@ stdcall RtlDosSearchPath_Ustr(long ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RtlDowncaseUnicodeChar(long) +@ stdcall RtlDowncaseUnicodeString(ptr ptr long) +@ stdcall RtlDumpResource(ptr) +@ stdcall RtlDuplicateUnicodeString(long ptr ptr) +@ stdcall RtlEmptyAtomTable(ptr long) +;@ stdcall RtlEnableEarlyCriticalSectionEventCreation +@ stdcall RtlEncodePointer(ptr) +@ stdcall RtlEncodeSystemPointer(ptr) +@ stdcall -arch=win32 -ret64 RtlEnlargedIntegerMultiply(long long) +@ stdcall -arch=win32 RtlEnlargedUnsignedDivide(double long ptr) +@ stdcall -arch=win32 -ret64 RtlEnlargedUnsignedMultiply(long long) +@ stdcall RtlEnterCriticalSection(ptr) +@ stdcall RtlEnumProcessHeaps(ptr ptr) +@ stdcall RtlEnumerateGenericTable(ptr long) +@ stdcall RtlEnumerateGenericTableAvl(ptr long) +@ stdcall RtlEnumerateGenericTableLikeADirectory(ptr ptr ptr long ptr ptr ptr) +@ stdcall RtlEnumerateGenericTableWithoutSplaying(ptr ptr) +@ stdcall RtlEnumerateGenericTableWithoutSplayingAvl(ptr ptr) +@ stdcall RtlEqualComputerName(ptr ptr) +@ stdcall RtlEqualDomainName(ptr ptr) +@ stdcall RtlEqualLuid(ptr ptr) +@ stdcall RtlEqualPrefixSid(ptr ptr) +@ stdcall RtlEqualSid(long long) +@ stdcall RtlEqualString(ptr ptr long) +@ stdcall RtlEqualUnicodeString(ptr ptr long) +@ stdcall RtlEraseUnicodeString(ptr) +@ stdcall RtlExitUserThread(long) +@ stdcall RtlExpandEnvironmentStrings_U(ptr ptr ptr ptr) +@ stdcall RtlExtendHeap(ptr long ptr ptr) +@ stdcall -arch=win32 -ret64 RtlExtendedIntegerMultiply(double long) +@ stdcall -arch=win32 -ret64 RtlExtendedLargeIntegerDivide(double long ptr) +@ stdcall -arch=win32 -ret64 RtlExtendedMagicDivide(double double long) +@ stdcall RtlFillMemory(ptr long long) +@ stdcall RtlFillMemoryUlong(ptr long long) +@ stdcall RtlFinalReleaseOutOfProcessMemoryStream(ptr) +@ stdcall RtlFindActivationContextSectionGuid(long ptr long ptr ptr) +@ stdcall RtlFindActivationContextSectionString(long ptr long ptr ptr) +@ stdcall RtlFindCharInUnicodeString(long ptr ptr ptr) +@ stdcall RtlFindClearBits(ptr long long) +@ stdcall RtlFindClearBitsAndSet(ptr long long) +@ stdcall RtlFindClearRuns(ptr ptr long long) +@ stdcall RtlFindLastBackwardRunClear(ptr long ptr) +;@ stdcall RtlFindLastBackwardRunSet(ptr long ptr) +@ stdcall RtlFindLeastSignificantBit(double) +@ stdcall RtlFindLongestRunClear(ptr long) +@ stdcall RtlFindLongestRunSet(ptr long) +@ stdcall RtlFindMessage(long long long long ptr) +@ stdcall RtlFindMostSignificantBit(double) +@ stdcall RtlFindNextForwardRunClear(ptr long ptr) +;@ stdcall RtlFindNextForwardRunSet(ptr long ptr) +@ stdcall RtlFindRange(ptr long long long long long long long long ptr ptr ptr) +@ stdcall RtlFindSetBits(ptr long long) +@ stdcall RtlFindSetBitsAndClear(ptr long long) +;@ stdcall RtlFindSetRuns(ptr ptr long long) +@ stdcall RtlFirstEntrySList(ptr) +@ stdcall RtlFirstFreeAce(ptr ptr) +@ stdcall RtlFlushSecureMemoryCache(ptr ptr) +@ stdcall RtlFormatCurrentUserKeyPath(ptr) +@ stdcall RtlFormatMessage(ptr long long long long ptr ptr long) +@ stdcall RtlFormatMessageEx(ptr long long long long ptr ptr long long) +@ stdcall RtlFreeActivationContextStack(ptr) +@ stdcall RtlFreeAnsiString(long) +@ stdcall RtlFreeHandle(ptr ptr) +@ stdcall RtlFreeHeap(long long long) +@ stdcall RtlFreeOemString(ptr) +@ stdcall RtlFreeRangeList(ptr) +@ stdcall RtlFreeSid(long) +@ stdcall RtlFreeThreadActivationContextStack() +@ stdcall RtlFreeUnicodeString(ptr) +@ stdcall RtlFreeUserThreadStack(ptr ptr) ; 4.0 to 5.2 only +@ stdcall RtlGUIDFromString(ptr ptr) +@ stdcall RtlGenerate8dot3Name(ptr ptr long ptr) +@ stdcall RtlGetAce(ptr long ptr) +@ stdcall RtlGetActiveActivationContext(ptr) +@ stdcall RtlGetCallersAddress(ptr ptr) +@ stdcall RtlGetCompressionWorkSpaceSize(long ptr ptr) +@ stdcall RtlGetControlSecurityDescriptor(ptr ptr ptr) +@ stdcall RtlGetCriticalSectionRecursionCount(ptr) +@ stdcall RtlGetCurrentDirectory_U(long ptr) +@ stdcall RtlGetCurrentPeb() +@ stdcall RtlGetCurrentProcessorNumber() ; 5.2 SP1 and higher +@ stdcall RtlGetDaclSecurityDescriptor(ptr ptr ptr ptr) +@ stdcall RtlGetElementGenericTable(ptr long) +@ stdcall RtlGetElementGenericTableAvl(ptr long) +@ stdcall RtlGetFirstRange(ptr ptr ptr) +;@ stdcall RtlGetFrame +@ stdcall RtlGetFullPathName_U(wstr long ptr ptr) +@ stdcall RtlGetFullPathName_UstrEx(ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RtlGetGroupSecurityDescriptor(ptr ptr ptr) +@ stdcall RtlGetLastNtStatus() +@ stdcall RtlGetLastWin32Error() +@ stdcall RtlGetLengthWithoutLastFullDosOrNtPathElement(long ptr ptr) +; Yes, Microsoft really misspelled this one! +@ stdcall RtlGetLengthWithoutTrailingPathSeperators(long ptr ptr) RtlGetLengthWithoutTrailingPathSeparators +@ stdcall RtlGetLongestNtPathLength() +@ stdcall RtlGetNativeSystemInformation(long long long long) NtQuerySystemInformation +@ stdcall RtlGetNextRange(ptr ptr long) +@ stdcall RtlGetNtGlobalFlags() +@ stdcall RtlGetNtProductType(ptr) +@ stdcall RtlGetNtVersionNumbers(ptr ptr ptr) +@ stdcall RtlGetOwnerSecurityDescriptor(ptr ptr ptr) +;@ stdcall RtlGetProductInfo(long long long long ptr) +@ stdcall RtlGetProcessHeaps(long ptr) +@ stdcall RtlGetSaclSecurityDescriptor(ptr ptr ptr ptr) +@ stdcall RtlGetSecurityDescriptorRMControl(ptr ptr) +@ stdcall RtlGetSetBootStatusData(ptr long long ptr long long) +@ stdcall RtlGetThreadErrorMode() +;@ stdcall RtlGetUnloadEventTrace +@ stdcall RtlGetUserInfoHeap(ptr long ptr ptr ptr) +@ stdcall RtlGetVersion(ptr) +@ stdcall RtlHashUnicodeString(ptr long long ptr) +@ stdcall RtlIdentifierAuthoritySid(ptr) +@ stdcall RtlImageDirectoryEntryToData(long long long ptr) +@ stdcall RtlImageNtHeader(long) +@ stdcall RtlImageNtHeaderEx(long ptr double ptr) +@ stdcall RtlImageRvaToSection(ptr long long) +@ stdcall RtlImageRvaToVa(ptr long long ptr) +@ stdcall RtlImpersonateSelf(long) +@ stdcall RtlInitAnsiString(ptr str) +@ stdcall RtlInitAnsiStringEx(ptr str) +@ stdcall RtlInitCodePageTable(ptr ptr) +@ stdcall RtlInitMemoryStream(ptr) +@ stdcall RtlInitNlsTables(ptr ptr ptr ptr) +@ stdcall RtlInitOutOfProcessMemoryStream(ptr) +@ stdcall RtlInitString(ptr str) +@ stdcall RtlInitUnicodeString(ptr wstr) +@ stdcall RtlInitUnicodeStringEx(ptr wstr) +;@ stdcall RtlInitializeAtomPackage +@ stdcall RtlInitializeBitMap(ptr long long) +@ stdcall RtlInitializeContext(ptr ptr ptr ptr ptr) +@ stdcall RtlInitializeCriticalSection(ptr) +@ stdcall RtlInitializeCriticalSectionAndSpinCount(ptr long) +;@ stdcall RtlInitializeCriticalSectionEx(ptr long long) +@ stdcall RtlInitializeGenericTable(ptr ptr ptr ptr ptr) +@ stdcall RtlInitializeGenericTableAvl(ptr ptr ptr ptr ptr) +@ stdcall RtlInitializeHandleTable(long long ptr) +@ stdcall RtlInitializeRXact(ptr long ptr) +@ stdcall RtlInitializeRangeList(ptr) +@ stdcall RtlInitializeResource(ptr) +@ stdcall RtlInitializeSListHead(ptr) +@ stdcall RtlInitializeSid(ptr ptr long) +@ stdcall RtlInitializeSRWLock(ptr) +;@ stdcall RtlInitializeStackTraceDataBase ; 5.1 SP2 and SP3, and 5.2 only +@ stdcall RtlInsertElementGenericTable(ptr ptr long ptr) +@ stdcall RtlInsertElementGenericTableAvl(ptr ptr long ptr) +@ stdcall -arch=x86_64 RtlInstallFunctionTableCallback(double double long ptr ptr ptr) +@ stdcall RtlInt64ToUnicodeString(double long ptr) +@ stdcall RtlIntegerToChar(long long long ptr) +@ stdcall RtlIntegerToUnicodeString(long long ptr) +@ stdcall -arch=win32 -ret64 RtlInterlockedCompareExchange64(ptr double double) +@ stdcall -arch=i386,x86_64 RtlInterlockedFlushSList(ptr) +@ stdcall -arch=i386,x86_64 RtlInterlockedPopEntrySList(ptr) +@ stdcall -arch=i386,x86_64 RtlInterlockedPushEntrySList(ptr ptr) +@ stdcall RtlInterlockedPushListSList(ptr ptr ptr long) +@ stdcall RtlInvertRangeList(ptr ptr) +@ stdcall RtlIpv4AddressToStringA(ptr ptr) +@ stdcall RtlIpv4AddressToStringExA(ptr long ptr ptr) +@ stdcall RtlIpv4AddressToStringExW(ptr long ptr ptr) +@ stdcall RtlIpv4AddressToStringW(ptr ptr) +@ stdcall RtlIpv4StringToAddressA(str long ptr ptr) +@ stdcall RtlIpv4StringToAddressExA(str long ptr ptr) +@ stdcall RtlIpv4StringToAddressExW(wstr long ptr ptr) +@ stdcall RtlIpv4StringToAddressW(wstr long ptr ptr) +@ stdcall RtlIpv6AddressToStringA(ptr ptr) +@ stdcall RtlIpv6AddressToStringExA(ptr long long ptr ptr) +@ stdcall RtlIpv6AddressToStringExW(ptr long long ptr ptr) +@ stdcall RtlIpv6AddressToStringW(ptr ptr) +@ stdcall RtlIpv6StringToAddressA(str ptr ptr) +@ stdcall RtlIpv6StringToAddressExA(str ptr ptr ptr) +@ stdcall RtlIpv6StringToAddressExW(wstr ptr ptr ptr) +@ stdcall RtlIpv6StringToAddressW(wstr ptr ptr) +@ stdcall RtlIsActivationContextActive(ptr) +;@ stdcall RtlIsCriticalSectionLocked +;@ stdcall RtlIsCriticalSectionLockedByThread +@ stdcall RtlIsDosDeviceName_U(wstr) +@ stdcall RtlIsGenericTableEmpty(ptr) +@ stdcall RtlIsGenericTableEmptyAvl(ptr) +@ stdcall RtlIsNameLegalDOS8Dot3(ptr ptr ptr) +@ stdcall RtlIsRangeAvailable(ptr long long long long long long ptr ptr ptr) +@ stdcall RtlIsTextUnicode(ptr long ptr) +@ stdcall RtlIsThreadWithinLoaderCallout() +@ stdcall RtlIsValidHandle(ptr ptr) +@ stdcall RtlIsValidIndexHandle(ptr long ptr) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerAdd(double double) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerArithmeticShift(double long) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerDivide(double double ptr) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerNegate(double) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerShiftLeft(double long) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerShiftRight(double long) +@ stdcall -arch=win32 -ret64 RtlLargeIntegerSubtract(double double) +@ stdcall RtlLargeIntegerToChar(ptr long long ptr) +@ stdcall RtlLeaveCriticalSection(ptr) +@ stdcall RtlLengthRequiredSid(long) +@ stdcall RtlLengthSecurityDescriptor(ptr) +@ stdcall RtlLengthSid(ptr) +@ stdcall RtlLocalTimeToSystemTime(ptr ptr) +@ stdcall RtlLockBootStatusData(ptr) +@ stdcall RtlLockHeap(long) +@ stdcall RtlLockMemoryStreamRegion(ptr int64 int64 long) +;@ stdcall RtlLogStackBackTrace +@ stdcall RtlLookupAtomInAtomTable(ptr wstr ptr) +@ stdcall RtlLookupElementGenericTable(ptr ptr) +@ stdcall RtlLookupElementGenericTableAvl(ptr ptr) +@ stdcall -arch=x86_64 RtlLookupFunctionEntry(long ptr ptr) +@ stdcall RtlMakeSelfRelativeSD(ptr ptr ptr) +@ stdcall RtlMapGenericMask(long ptr) +;@ stdcall RtlMapSecurityErrorToNtStatus +@ stdcall RtlMergeRangeLists(ptr ptr ptr long) +@ stdcall RtlMoveMemory(ptr ptr long) +@ stdcall RtlMultiAppendUnicodeStringBuffer(ptr long ptr) +@ stdcall RtlMultiByteToUnicodeN(ptr long ptr ptr long) +@ stdcall RtlMultiByteToUnicodeSize(ptr str long) +@ stdcall RtlNewInstanceSecurityObject(long long ptr ptr ptr ptr ptr long ptr ptr) +@ stdcall RtlNewSecurityGrantedAccess(long ptr ptr ptr ptr ptr) +@ stdcall RtlNewSecurityObject(ptr ptr ptr long ptr ptr) +@ stdcall RtlNewSecurityObjectEx(ptr ptr ptr ptr long long ptr ptr) +@ stdcall RtlNewSecurityObjectWithMultipleInheritance(ptr ptr ptr ptr long long long ptr ptr) +@ stdcall RtlNormalizeProcessParams(ptr) +@ stdcall RtlNtPathNameToDosPathName(ptr ptr ptr ptr) ; CHECKME +@ stdcall RtlNtStatusToDosError(long) +@ stdcall RtlNtStatusToDosErrorNoTeb(long) +@ stdcall RtlNumberGenericTableElements(ptr) +@ stdcall RtlNumberGenericTableElementsAvl(ptr) +@ stdcall RtlNumberOfClearBits(ptr) +@ stdcall RtlNumberOfSetBits(ptr) +;@ stdcall RtlOemStringToUnicodeSize(ptr) +@ stdcall RtlOemStringToUnicodeString(ptr ptr long) +@ stdcall RtlOemToUnicodeN(ptr long ptr ptr long) +@ stdcall RtlOpenCurrentUser(long ptr) +@ stdcall RtlPcToFileHeader(ptr ptr) +@ stdcall RtlPinAtomInAtomTable(ptr long) +;@ stdcall RtlPopFrame +@ stdcall RtlPrefixString(ptr ptr long) +@ stdcall RtlPrefixUnicodeString(ptr ptr long) +;@ stdcall RtlPropertySetNameToGuid ; 4.0 only +@ stdcall RtlProtectHeap(ptr long) +;@ stdcall RtlPushFrame +@ stdcall RtlQueryAtomInAtomTable(ptr long ptr ptr ptr ptr) +@ stdcall RtlQueryDepthSList(ptr) +@ stdcall RtlQueryEnvironmentVariable_U(ptr ptr ptr) +@ stdcall RtlQueryHeapInformation(long long ptr long ptr) +@ stdcall RtlQueryInformationAcl(ptr ptr long long) +@ stdcall RtlQueryInformationActivationContext(long long ptr long ptr long ptr) +@ stdcall RtlQueryInformationActiveActivationContext(long ptr long ptr) +@ stdcall RtlQueryInterfaceMemoryStream(ptr ptr ptr) +;@ stdcall RtlQueryProcessBackTraceInformation +@ stdcall RtlQueryProcessDebugInformation(long long ptr) +;@ stdcall RtlQueryProcessHeapInformation +;@ stdcall RtlQueryProcessLockInformation +;@ stdcall RtlQueryProperties ; 4.0 only +;@ stdcall RtlQueryPropertyNames ; 4.0 only +;@ stdcall RtlQueryPropertySet ; 4.0 only +@ stdcall RtlQueryRegistryValues(long ptr ptr ptr ptr) +@ stdcall RtlQuerySecurityObject(ptr long ptr long ptr) +@ stdcall RtlQueryTagHeap(ptr long long long ptr) +@ stdcall RtlQueryTimeZoneInformation(ptr) +;@ stdcall RtlQueueApcWow64Thread +@ stdcall RtlQueueWorkItem(ptr ptr long) +@ stdcall -register RtlRaiseException(ptr) +@ stdcall RtlRaiseStatus(long) +@ stdcall RtlRandom(ptr) +@ stdcall RtlRandomEx(ptr) +@ stdcall RtlReAllocateHeap(long long ptr long) +@ stdcall RtlReadMemoryStream(ptr ptr long ptr) +@ stdcall RtlReadOutOfProcessMemoryStream(ptr ptr long ptr) +@ stdcall RtlRealPredecessor(ptr) +@ stdcall RtlRealSuccessor(ptr) +@ stdcall RtlRegisterSecureMemoryCacheCallback(ptr) +@ stdcall RtlRegisterWait(ptr ptr ptr ptr long long) +@ stdcall RtlReleaseActivationContext(ptr) +@ stdcall RtlReleaseMemoryStream(ptr) +@ stdcall RtlReleasePebLock() +@ stdcall RtlReleasePrivilege(ptr) +@ stdcall RtlReleaseRelativeName(ptr) +@ stdcall RtlReleaseResource(ptr) +@ stdcall RtlReleaseSRWLockExclusive(ptr) +@ stdcall RtlReleaseSRWLockShared(ptr) +@ stdcall RtlRemoteCall(ptr ptr ptr long ptr long long) +@ stdcall RtlRemoveVectoredContinueHandler(ptr) +@ stdcall RtlRemoveVectoredExceptionHandler(ptr) +@ stdcall RtlResetRtlTranslations(ptr) +@ stdcall -arch=x86_64 RtlRestoreContext(ptr ptr) +@ stdcall RtlRestoreLastWin32Error(long) RtlSetLastWin32Error +@ stdcall RtlRevertMemoryStream(ptr) +@ stdcall RtlRunDecodeUnicodeString(long ptr) +@ stdcall RtlRunEncodeUnicodeString(long ptr) +@ stdcall RtlSecondsSince1970ToTime(long ptr) +@ stdcall RtlSecondsSince1980ToTime(long ptr) +@ stdcall RtlSeekMemoryStream(ptr int64 long ptr) +@ stdcall RtlSelfRelativeToAbsoluteSD2(ptr ptr) +@ stdcall RtlSelfRelativeToAbsoluteSD(ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall RtlSetAllBits(ptr) +@ stdcall RtlSetAttributesSecurityDescriptor(ptr long ptr) +@ stdcall RtlSetBits(ptr long long) +@ stdcall RtlSetControlSecurityDescriptor(ptr long long) +@ stdcall RtlSetCriticalSectionSpinCount(ptr long) +@ stdcall RtlSetCurrentDirectory_U(ptr) +@ stdcall RtlSetCurrentEnvironment(wstr ptr) +@ stdcall RtlSetDaclSecurityDescriptor(ptr long ptr long) +@ stdcall RtlSetEnvironmentStrings(wstr long) +@ stdcall RtlSetEnvironmentVariable(ptr ptr ptr) +@ stdcall RtlSetGroupSecurityDescriptor(ptr ptr long) +@ stdcall RtlSetHeapInformation(ptr long ptr ptr) +@ stdcall RtlSetInformationAcl(ptr ptr long long) +@ stdcall RtlSetIoCompletionCallback(long ptr long) +@ stdcall RtlSetLastWin32Error(long) +@ stdcall RtlSetLastWin32ErrorAndNtStatusFromNtStatus(long) +@ stdcall RtlSetMemoryStreamSize(ptr int64) +@ stdcall RtlSetOwnerSecurityDescriptor(ptr ptr long) +@ cdecl RtlSetProcessIsCritical(long ptr long) +;@ stdcall RtlSetProperties ; RtlSetProperties +;@ stdcall RtlSetPropertyClassId ; 4.0 only +;@ stdcall RtlSetPropertyNames ; 4.0 only +;@ stdcall RtlSetPropertySetClassId ; 4.0 only +@ stdcall RtlSetSaclSecurityDescriptor(ptr long ptr long) +@ stdcall RtlSetSecurityDescriptorRMControl(ptr ptr) +@ stdcall RtlSetSecurityObject(long ptr ptr ptr ptr) +@ stdcall RtlSetSecurityObjectEx(long ptr ptr long ptr ptr) +@ stdcall RtlSetThreadErrorMode(long ptr) +@ cdecl RtlSetThreadIsCritical(long ptr long) +@ stdcall RtlSetThreadPoolStartFunc(ptr ptr) +@ stdcall RtlSetTimeZoneInformation(ptr) +;@ stdcall RtlSetTimer +@ stdcall RtlSetUnhandledExceptionFilter(ptr) +;@ stdcall RtlSetUnicodeCallouts +@ stdcall RtlSetUserFlagsHeap(ptr long ptr long long) +@ stdcall RtlSetUserValueHeap(ptr long ptr ptr) +@ stdcall RtlSizeHeap(long long ptr) +@ stdcall RtlSleepConditionVariableCS(ptr ptr ptr) +@ stdcall RtlSleepConditionVariableSRW(ptr ptr ptr long) +@ stdcall RtlSplay(ptr) +@ stdcall RtlStartRXact(ptr) +@ stdcall RtlStatMemoryStream(ptr ptr long) +@ stdcall RtlStringFromGUID(ptr ptr) +@ stdcall RtlSubAuthorityCountSid(ptr) +@ stdcall RtlSubAuthoritySid(ptr long) +@ stdcall RtlSubtreePredecessor(ptr) +@ stdcall RtlSubtreeSuccessor(ptr) +@ stdcall RtlSystemTimeToLocalTime(ptr ptr) +@ stdcall RtlTimeFieldsToTime(ptr ptr) +@ stdcall RtlTimeToElapsedTimeFields(long long) +@ stdcall RtlTimeToSecondsSince1970(ptr ptr) +@ stdcall RtlTimeToSecondsSince1980(ptr ptr) +@ stdcall RtlTimeToTimeFields (long long) +;@ stdcall RtlTraceDatabaseAdd +;@ stdcall RtlTraceDatabaseCreate +;@ stdcall RtlTraceDatabaseDestroy +;@ stdcall RtlTraceDatabaseEnumerate +;@ stdcall RtlTraceDatabaseFind +;@ stdcall RtlTraceDatabaseLock +;@ stdcall RtlTraceDatabaseUnlock +;@ stdcall RtlTraceDatabaseValidate +@ stdcall RtlTryEnterCriticalSection(ptr) +@ fastcall -arch=i386 RtlUlongByteSwap(long) +@ fastcall -ret64 RtlUlonglongByteSwap(double) +;@ stdcall RtlUnhandledExceptionFilter2 +@ stdcall RtlUnhandledExceptionFilter(ptr) +;@ stdcall RtlUnicodeStringToAnsiSize(ptr) +@ stdcall RtlUnicodeStringToAnsiString(ptr ptr long) +@ stdcall RtlUnicodeStringToCountedOemString(ptr ptr long) +@ stdcall RtlUnicodeStringToInteger(ptr long ptr) +;@ stdcall RtlUnicodeStringToOemSize(ptr) +@ stdcall RtlUnicodeStringToOemString(ptr ptr long) +@ stdcall RtlUnicodeToCustomCPN(ptr ptr long ptr wstr long) +@ stdcall RtlUnicodeToMultiByteN(ptr long ptr ptr long) +@ stdcall RtlUnicodeToMultiByteSize(ptr ptr long) +@ stdcall RtlUnicodeToOemN(ptr long ptr ptr long) +@ stdcall RtlUniform(ptr) +@ stdcall RtlUnlockBootStatusData(ptr) +@ stdcall RtlUnlockHeap(long) +@ stdcall RtlUnlockMemoryStreamRegion(ptr int64 int64 long) +@ stdcall -register RtlUnwind(ptr ptr ptr ptr) +@ stdcall -arch=x86_64 RtlUnwindEx(long long ptr long ptr) +@ stdcall RtlUpcaseUnicodeChar(long) +@ stdcall RtlUpcaseUnicodeString(ptr ptr long) +@ stdcall RtlUpcaseUnicodeStringToAnsiString(ptr ptr long) +@ stdcall RtlUpcaseUnicodeStringToCountedOemString(ptr ptr long) +@ stdcall RtlUpcaseUnicodeStringToOemString(ptr ptr long) +@ stdcall RtlUpcaseUnicodeToCustomCPN(ptr ptr long ptr wstr long) +@ stdcall RtlUpcaseUnicodeToMultiByteN(ptr long ptr ptr long) +@ stdcall RtlUpcaseUnicodeToOemN(ptr long ptr ptr long) +@ stdcall RtlUpdateTimer(ptr ptr long long) +@ stdcall RtlUpperChar(long) +@ stdcall RtlUpperString(ptr ptr) +@ stdcall RtlUsageHeap(ptr long ptr) +@ fastcall -arch=i386 RtlUshortByteSwap(long) +@ stdcall RtlValidAcl(ptr) +@ stdcall RtlValidRelativeSecurityDescriptor(ptr long long) +@ stdcall RtlValidSecurityDescriptor(ptr) +@ stdcall RtlValidSid(ptr) +@ stdcall RtlValidateHeap(long long ptr) +@ stdcall RtlValidateProcessHeaps() +@ stdcall RtlValidateUnicodeString(long ptr) +@ stdcall RtlVerifyVersionInfo(ptr long double) +@ stdcall -arch=x86_64 RtlVirtualUnwind(long long long ptr ptr ptr ptr ptr) +@ stdcall RtlWalkFrameChain(ptr long long) +@ stdcall RtlWalkHeap(long ptr) +@ stdcall RtlWow64EnableFsRedirection(long) +@ stdcall RtlWow64EnableFsRedirectionEx(long ptr) +@ stdcall RtlWakeAllConditionVariable(ptr) +@ stdcall RtlWakeConditionVariable(ptr) +@ stdcall RtlWriteMemoryStream(ptr ptr long ptr) +@ stdcall RtlWriteRegistryValue(long ptr ptr long ptr long) +@ stdcall RtlZeroHeap(ptr long) +@ stdcall RtlZeroMemory(ptr long) +@ stdcall RtlZombifyActivationContext(ptr) +@ stdcall RtlpApplyLengthFunction(long long ptr ptr) +@ stdcall RtlpEnsureBufferSize(ptr ptr ptr) ; CHECKME +;@ stdcall RtlpNotOwnerCriticalSection +@ stdcall RtlpNtCreateKey(ptr long ptr long ptr ptr) +@ stdcall RtlpNtEnumerateSubKey(ptr ptr long long) +@ stdcall RtlpNtMakeTemporaryKey(ptr) +@ stdcall RtlpNtOpenKey(ptr long ptr long) +@ stdcall RtlpNtQueryValueKey(ptr ptr ptr ptr long) +@ stdcall RtlpNtSetValueKey(ptr long ptr long) +@ stdcall RtlpUnWaitCriticalSection(ptr) +@ stdcall RtlpWaitForCriticalSection(ptr) +@ stdcall RtlxAnsiStringToUnicodeSize(ptr) +@ stdcall RtlxOemStringToUnicodeSize(ptr) +@ stdcall RtlxUnicodeStringToAnsiSize(ptr) +@ stdcall RtlxUnicodeStringToOemSize(ptr) ; RtlUnicodeStringToOemSize +@ stdcall -ret64 VerSetConditionMask(double long long) +@ stdcall ZwAcceptConnectPort(ptr long ptr long long ptr) NtAcceptConnectPort +@ stdcall ZwAccessCheck(ptr long long ptr ptr ptr ptr ptr) NtAccessCheck +@ stdcall ZwAccessCheckAndAuditAlarm(ptr long ptr ptr ptr long ptr long ptr ptr ptr) NtAccessCheckAndAuditAlarm +@ stdcall ZwAccessCheckByType(ptr ptr ptr long ptr long ptr ptr long ptr ptr) NtAccessCheckByType +@ stdcall ZwAccessCheckByTypeAndAuditAlarm(ptr ptr ptr ptr ptr ptr long long long ptr long ptr long ptr ptr ptr) NtAccessCheckByTypeAndAuditAlarm +@ stdcall ZwAccessCheckByTypeResultList(ptr ptr ptr long ptr long ptr ptr long ptr ptr) NtAccessCheckByTypeResultList +@ stdcall ZwAccessCheckByTypeResultListAndAuditAlarm(ptr ptr ptr ptr ptr ptr long long long ptr long ptr long ptr ptr ptr) NtAccessCheckByTypeResultListAndAuditAlarm +@ stdcall ZwAccessCheckByTypeResultListAndAuditAlarmByHandle(ptr ptr ptr ptr ptr ptr ptr long long long ptr long ptr long ptr ptr ptr) NtAccessCheckByTypeResultListAndAuditAlarmByHandle +@ stdcall ZwAddAtom(ptr long ptr) NtAddAtom +@ stdcall ZwAddBootEntry(ptr long) +@ stdcall ZwAdjustGroupsToken(long long long long long long) NtAdjustGroupsToken +@ stdcall ZwAdjustPrivilegesToken(long long long long long long) NtAdjustPrivilegesToken +@ stdcall ZwAlertResumeThread(long ptr) NtAlertResumeThread +@ stdcall ZwAlertThread(long) NtAlertThread +@ stdcall ZwAllocateLocallyUniqueId(ptr) NtAllocateLocallyUniqueId +@ stdcall ZwAllocateUserPhysicalPages(ptr ptr ptr) +@ stdcall ZwAllocateUuids(ptr ptr ptr ptr) NtAllocateUuids +@ stdcall ZwAllocateVirtualMemory(long ptr ptr ptr long long) NtAllocateVirtualMemory +@ stdcall ZwAreMappedFilesTheSame(ptr ptr) NtAreMappedFilesTheSame +@ stdcall ZwAssignProcessToJobObject(long long) NtAssignProcessToJobObject +@ stdcall ZwCallbackReturn(ptr long long) +@ stdcall ZwCancelDeviceWakeupRequest(ptr) +@ stdcall ZwCancelIoFile(long ptr) NtCancelIoFile +;@ stdcall ZwCancelIoFileEx(long ptr ptr) NtCancelIoFileEx +@ stdcall ZwCancelTimer(long ptr) NtCancelTimer +@ stdcall ZwClearEvent(long) NtClearEvent +@ stdcall ZwClose(long) NtClose +@ stdcall ZwCloseObjectAuditAlarm(ptr ptr long) +@ stdcall ZwCompactKeys(long ptr) NtCompactKeys +@ stdcall ZwCompareTokens(ptr ptr ptr) NtCompareTokens +@ stdcall ZwCompleteConnectPort(ptr) NtCompleteConnectPort +@ stdcall ZwCompressKey(ptr) NtCompressKey +@ stdcall ZwConnectPort(ptr ptr ptr ptr ptr ptr ptr ptr) NtConnectPort +@ stdcall ZwContinue(ptr long) NtContinue +@ stdcall ZwCreateDebugObject(ptr long ptr long) NtCreateDebugObject +@ stdcall ZwCreateDirectoryObject(long long long) NtCreateDirectoryObject +@ stdcall ZwCreateEvent(long long long long long) NtCreateEvent +@ stdcall ZwCreateEventPair(ptr long ptr) NtCreateEventPair +@ stdcall ZwCreateFile(ptr long ptr ptr long long long ptr long long ptr) NtCreateFile +@ stdcall ZwCreateIoCompletion(ptr long ptr long) NtCreateIoCompletion +@ stdcall ZwCreateJobObject(ptr long ptr) NtCreateJobObject +@ stdcall ZwCreateJobSet(long ptr long) NtCreateJobSet +@ stdcall ZwCreateKey(ptr long ptr long ptr long long) NtCreateKey +@ stdcall ZwCreateKeyedEvent(ptr long ptr long) NtCreateKeyedEvent +@ stdcall ZwCreateMailslotFile(long long long long long long long long) NtCreateMailslotFile +@ stdcall ZwCreateMutant(ptr long ptr long) NtCreateMutant +@ stdcall ZwCreateNamedPipeFile(ptr long ptr ptr long long long long long long long long long ptr) NtCreateNamedPipeFile +@ stdcall ZwCreatePagingFile(long long long long) NtCreatePagingFile +@ stdcall ZwCreatePort(ptr ptr long long long) NtCreatePort +@ stdcall ZwCreateProcess(ptr long ptr ptr long ptr ptr ptr) +@ stdcall ZwCreateProcessEx(ptr long ptr ptr long ptr ptr ptr long) NtCreateProcessEx +@ stdcall ZwCreateProfile(ptr ptr ptr long long ptr long long long) NtCreateProfile ; CHECKME +@ stdcall ZwCreateSection(ptr long ptr ptr long long long) NtCreateSection +@ stdcall ZwCreateSemaphore(ptr long ptr long long) NtCreateSemaphore +@ stdcall ZwCreateSymbolicLinkObject(ptr long ptr ptr) NtCreateSymbolicLinkObject +@ stdcall ZwCreateThread(ptr long ptr ptr ptr ptr ptr long) +@ stdcall ZwCreateTimer(ptr long ptr long) NtCreateTimer +@ stdcall ZwCreateToken(ptr long ptr long ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall ZwCreateWaitablePort(ptr ptr long long long) NtCreateWaitablePort +@ stdcall ZwDebugActiveProcess(ptr ptr) NtDebugActiveProcess +@ stdcall ZwDebugContinue(ptr ptr long) NtDebugContinue +@ stdcall ZwDelayExecution(long ptr) NtDelayExecution +@ stdcall ZwDeleteAtom(long) NtDeleteAtom +@ stdcall ZwDeleteBootEntry(long) NtDeleteBootEntry +@ stdcall ZwDeleteFile(ptr) NtDeleteFile +@ stdcall ZwDeleteKey(long) NtDeleteKey +@ stdcall ZwDeleteObjectAuditAlarm(ptr ptr long) +@ stdcall ZwDeleteValueKey(long ptr) NtDeleteValueKey +@ stdcall ZwDeviceIoControlFile(long long long long long long long long long long) NtDeviceIoControlFile +@ stdcall ZwDisplayString(ptr) NtDisplayString +@ stdcall ZwDuplicateObject(long long long ptr long long long) NtDuplicateObject +@ stdcall ZwDuplicateToken(long long long long long long) NtDuplicateToken +@ stdcall ZwEnumerateBootEntries(ptr ptr) +;@ stdcall ZwEnumerateBus ; 3.51 only +@ stdcall ZwEnumerateKey(long long long ptr long ptr) NtEnumerateKey +@ stdcall ZwEnumerateSystemEnvironmentValuesEx(long ptr long) NtEnumerateSystemEnvironmentValuesEx +@ stdcall ZwEnumerateValueKey(long long long ptr long ptr) NtEnumerateValueKey +@ stdcall ZwExtendSection(ptr ptr) NtExtendSection +@ stdcall ZwFilterToken(ptr long ptr ptr ptr ptr) NtFilterToken +@ stdcall ZwFindAtom(ptr long ptr) NtFindAtom +@ stdcall ZwFlushBuffersFile(long ptr) NtFlushBuffersFile +@ stdcall ZwFlushInstructionCache(long ptr long) NtFlushInstructionCache +@ stdcall ZwFlushKey(long) NtFlushKey +@ stdcall ZwFlushVirtualMemory(long ptr ptr long) NtFlushVirtualMemory +@ stdcall ZwFlushWriteBuffer() +@ stdcall ZwFreeUserPhysicalPages(ptr ptr ptr) +@ stdcall ZwFreeVirtualMemory(long ptr ptr long) NtFreeVirtualMemory +@ stdcall ZwFsControlFile(long long long long long long long long long long) NtFsControlFile +@ stdcall ZwGetContextThread(long ptr) NtGetContextThread +@ stdcall ZwGetCurrentProcessorNumber() +@ stdcall ZwGetDevicePowerState(ptr ptr) +@ stdcall ZwGetPlugPlayEvent(long long ptr long) +;@ stdcall ZwGetTickCount() NtGetTickCount +@ stdcall ZwGetWriteWatch(long long ptr long ptr ptr ptr) NtGetWriteWatch +@ stdcall ZwImpersonateAnonymousToken(ptr) +@ stdcall ZwImpersonateClientOfPort(ptr ptr) NtImpersonateClientOfPort +@ stdcall ZwImpersonateThread(ptr ptr ptr) NtImpersonateThread +@ stdcall ZwInitializeRegistry(long) +@ stdcall ZwInitiatePowerAction(long long long long) NtInitiatePowerAction +@ stdcall ZwIsProcessInJob(long long) NtIsProcessInJob +@ stdcall ZwIsSystemResumeAutomatic() +@ stdcall ZwListenPort(ptr ptr) NtListenPort +@ stdcall ZwLoadDriver(ptr) NtLoadDriver +@ stdcall ZwLoadKey2(ptr ptr long) NtLoadKey2 +@ stdcall ZwLoadKey(ptr ptr) NtLoadKey +@ stdcall ZwLockFile(long long ptr ptr ptr ptr ptr ptr long long) NtLockFile +@ stdcall ZwLockProductActivationKeys(ptr ptr) NtLockProductActivationKeys +@ stdcall ZwLockRegistryKey(ptr) NtLockRegistryKey +@ stdcall ZwLockVirtualMemory(long ptr ptr long) NtLockVirtualMemory +@ stdcall ZwMakePermanentObject(ptr) NtMakePermanentObject +@ stdcall ZwMakeTemporaryObject(long) NtMakeTemporaryObject +@ stdcall ZwMapUserPhysicalPages(ptr ptr ptr) +@ stdcall ZwMapUserPhysicalPagesScatter(ptr ptr ptr) +@ stdcall ZwMapViewOfSection(long long ptr long long ptr ptr long long long) NtMapViewOfSection +@ stdcall ZwModifyBootEntry(ptr) NtModifyBootEntry +@ stdcall ZwNotifyChangeDirectoryFile(long long ptr ptr ptr ptr long long long) NtNotifyChangeDirectoryFile +@ stdcall ZwNotifyChangeKey(long long ptr ptr ptr long long ptr long long) NtNotifyChangeKey +@ stdcall ZwNotifyChangeMultipleKeys(ptr long ptr ptr ptr ptr ptr long long ptr long long) NtNotifyChangeMultipleKeys +@ stdcall ZwOpenDirectoryObject(long long long) NtOpenDirectoryObject +@ stdcall ZwOpenEvent(long long long) NtOpenEvent +@ stdcall ZwOpenEventPair(ptr long ptr) NtOpenEventPair +@ stdcall ZwOpenFile(ptr long ptr ptr long long) NtOpenFile +@ stdcall ZwOpenIoCompletion(ptr long ptr) NtOpenIoCompletion +@ stdcall ZwOpenJobObject(ptr long ptr) NtOpenJobObject +@ stdcall ZwOpenKey(ptr long ptr) NtOpenKey +@ stdcall ZwOpenKeyedEvent(ptr long ptr) NtOpenKeyedEvent +@ stdcall ZwOpenMutant(ptr long ptr) NtOpenMutant +@ stdcall ZwOpenObjectAuditAlarm(ptr ptr ptr ptr ptr ptr long long ptr long long ptr) +@ stdcall ZwOpenProcess(ptr long ptr ptr) NtOpenProcess +@ stdcall ZwOpenProcessToken(long long ptr) NtOpenProcessToken +@ stdcall ZwOpenProcessTokenEx(long long long ptr) NtOpenProcessTokenEx +@ stdcall ZwOpenSection(ptr long ptr) NtOpenSection +@ stdcall ZwOpenSemaphore(long long ptr) NtOpenSemaphore +@ stdcall ZwOpenSymbolicLinkObject (ptr long ptr) NtOpenSymbolicLinkObject +@ stdcall ZwOpenThread(ptr long ptr ptr) NtOpenThread +@ stdcall ZwOpenThreadToken(long long long ptr) NtOpenThreadToken +@ stdcall ZwOpenThreadTokenEx(long long long long ptr) NtOpenThreadTokenEx +@ stdcall ZwOpenTimer(ptr long ptr) NtOpenTimer +@ stdcall ZwPlugPlayControl(ptr ptr long) +@ stdcall ZwPowerInformation(long ptr long ptr long) NtPowerInformation +@ stdcall ZwPrivilegeCheck(ptr ptr ptr) NtPrivilegeCheck +@ stdcall ZwPrivilegeObjectAuditAlarm(ptr ptr ptr long ptr long) +@ stdcall ZwPrivilegedServiceAuditAlarm(ptr ptr ptr ptr long) +@ stdcall ZwProtectVirtualMemory(long ptr ptr long ptr) NtProtectVirtualMemory +@ stdcall ZwPulseEvent(long ptr) NtPulseEvent +@ stdcall ZwQueryAttributesFile(ptr ptr) NtQueryAttributesFile +@ stdcall ZwQueryBootEntryOrder(ptr ptr) NtQueryBootEntryOrder +@ stdcall ZwQueryBootOptions(ptr ptr) NtQueryBootOptions +@ stdcall ZwQueryDebugFilterState(long long) NtQueryDebugFilterState +@ stdcall ZwQueryDefaultLocale(long ptr) NtQueryDefaultLocale +@ stdcall ZwQueryDefaultUILanguage(ptr) NtQueryDefaultUILanguage +@ stdcall ZwQueryDirectoryFile(long long ptr ptr ptr ptr long long long ptr long) NtQueryDirectoryFile +@ stdcall ZwQueryDirectoryObject(long ptr long long long ptr ptr) NtQueryDirectoryObject +@ stdcall ZwQueryEaFile(long ptr ptr long long ptr long ptr long) NtQueryEaFile +@ stdcall ZwQueryEvent(long long ptr long ptr) NtQueryEvent +@ stdcall ZwQueryFullAttributesFile(ptr ptr) NtQueryFullAttributesFile +@ stdcall ZwQueryInformationAtom(long long ptr long ptr) NtQueryInformationAtom +@ stdcall ZwQueryInformationFile(long ptr ptr long long) NtQueryInformationFile +@ stdcall ZwQueryInformationJobObject(long long ptr long ptr) NtQueryInformationJobObject +@ stdcall ZwQueryInformationPort(ptr long ptr long ptr) NtQueryInformationPort +@ stdcall ZwQueryInformationProcess(long long ptr long ptr) NtQueryInformationProcess +@ stdcall ZwQueryInformationThread(long long ptr long ptr) NtQueryInformationThread +@ stdcall ZwQueryInformationToken(long long ptr long ptr) NtQueryInformationToken +@ stdcall ZwQueryInstallUILanguage(ptr) NtQueryInstallUILanguage +@ stdcall ZwQueryIntervalProfile(long ptr) NtQueryIntervalProfile +@ stdcall ZwQueryIoCompletion(long long ptr long ptr) NtQueryIoCompletion +@ stdcall ZwQueryKey(long long ptr long ptr) NtQueryKey +@ stdcall ZwQueryMultipleValueKey(long ptr long ptr long ptr) NtQueryMultipleValueKey +@ stdcall ZwQueryMutant(long long ptr long ptr) NtQueryMutant +@ stdcall ZwQueryObject(long long long long long) NtQueryObject +@ stdcall ZwQueryOpenSubKeys(ptr ptr) NtQueryOpenSubKeys +@ stdcall ZwQueryPerformanceCounter (long long) NtQueryPerformanceCounter +@ stdcall ZwQueryPortInformationProcess() NtQueryPortInformationProcess +@ stdcall ZwQueryQuotaInformationFile(ptr ptr ptr long long ptr long ptr long) NtQueryQuotaInformationFile +@ stdcall ZwQuerySection (long long long long long) NtQuerySection +@ stdcall ZwQuerySecurityObject (long long long long long) NtQuerySecurityObject +@ stdcall ZwQuerySemaphore (long long long long long) NtQuerySemaphore +@ stdcall ZwQuerySymbolicLinkObject(long ptr ptr) NtQuerySymbolicLinkObject +@ stdcall ZwQuerySystemEnvironmentValue(ptr ptr long ptr) NtQuerySystemEnvironmentValue +@ stdcall ZwQuerySystemEnvironmentValueEx(ptr ptr ptr ptr ptr) NtQuerySystemEnvironmentValueEx +@ stdcall ZwQuerySystemInformation(long long long long) NtQuerySystemInformation +@ stdcall ZwQuerySystemTime(ptr) NtQuerySystemTime +@ stdcall ZwQueryTimer(ptr long ptr long ptr) NtQueryTimer +@ stdcall ZwQueryTimerResolution(long long long) NtQueryTimerResolution +@ stdcall ZwQueryValueKey(long ptr long ptr long ptr) NtQueryValueKey +@ stdcall ZwQueryVirtualMemory(long ptr long ptr long ptr) NtQueryVirtualMemory +@ stdcall ZwQueryVolumeInformationFile(long ptr ptr long long) NtQueryVolumeInformationFile +@ stdcall ZwQueueApcThread(long ptr long long long) NtQueueApcThread +@ stdcall ZwRaiseException(ptr ptr long) NtRaiseException +@ stdcall ZwRaiseHardError(long long long ptr long ptr) NtRaiseHardError +@ stdcall ZwReadFile(long long ptr ptr ptr ptr long ptr ptr) NtReadFile +@ stdcall ZwReadFileScatter(long long ptr ptr ptr ptr long ptr ptr) NtReadFileScatter +@ stdcall ZwReadRequestData(ptr ptr long ptr long ptr) NtReadRequestData +@ stdcall ZwReadVirtualMemory(long ptr ptr long ptr) NtReadVirtualMemory +;@ stdcall ZwRegisterNewDevice ; 3.51 only +@ stdcall ZwRegisterThreadTerminatePort(ptr) NtRegisterThreadTerminatePort +@ stdcall ZwReleaseKeyedEvent(ptr ptr long ptr) NtReleaseKeyedEvent +@ stdcall ZwReleaseMutant(long ptr) NtReleaseMutant +;@ stdcall ZwReleaseProcessMutant ; 3.51 only +@ stdcall ZwReleaseSemaphore(long long ptr) NtReleaseSemaphore +@ stdcall ZwRemoveIoCompletion(ptr ptr ptr ptr ptr) NtRemoveIoCompletion +@ stdcall ZwRemoveProcessDebug(ptr ptr) NtRemoveProcessDebug +@ stdcall ZwRenameKey(ptr ptr) NtRenameKey +@ stdcall ZwReplaceKey(ptr long ptr) NtReplaceKey +@ stdcall ZwReplyPort(ptr ptr) NtReplyPort +@ stdcall ZwReplyWaitReceivePort(ptr ptr ptr ptr) NtReplyWaitReceivePort +@ stdcall ZwReplyWaitReceivePortEx(ptr ptr ptr ptr ptr) +@ stdcall ZwReplyWaitReplyPort(ptr ptr) +@ stdcall ZwRequestDeviceWakeup(ptr) +@ stdcall ZwRequestPort(ptr ptr) +@ stdcall ZwRequestWaitReplyPort(ptr ptr ptr) +@ stdcall ZwRequestWakeupLatency(long) +@ stdcall ZwResetEvent(long ptr) +@ stdcall ZwResetWriteWatch(long ptr long) +@ stdcall ZwRestoreKey(long long long) +@ stdcall ZwResumeProcess(ptr) +@ stdcall ZwResumeThread(long long) +@ stdcall ZwSaveKey(long long) +@ stdcall ZwSaveKeyEx(ptr ptr long) +@ stdcall ZwSaveMergedKeys(ptr ptr ptr) +@ stdcall ZwSecureConnectPort(ptr ptr ptr ptr ptr ptr ptr ptr ptr) +@ stdcall ZwSetBootEntryOrder(ptr ptr) +@ stdcall ZwSetBootOptions(ptr long) +@ stdcall ZwSetContextThread(long ptr) +@ stdcall ZwSetDebugFilterState(long long long) +@ stdcall ZwSetDefaultHardErrorPort(ptr) +@ stdcall ZwSetDefaultLocale(long long) +@ stdcall ZwSetDefaultUILanguage(long) +@ stdcall ZwSetEaFile(long ptr ptr long) +@ stdcall ZwSetEvent(long long) +@ stdcall ZwSetEventBoostPriority(ptr) +@ stdcall ZwSetHighEventPair(ptr) +@ stdcall ZwSetHighWaitLowEventPair(ptr) +;@ stdcall ZwSetHighWaitLowThread ; 3.51 and 4.0 only +@ stdcall ZwSetInformationDebugObject(ptr long ptr long ptr) +@ stdcall ZwSetInformationFile(long long long long long) +@ stdcall ZwSetInformationJobObject(long long ptr long) +@ stdcall ZwSetInformationKey(long long ptr long) +@ stdcall ZwSetInformationObject(long long ptr long) +@ stdcall ZwSetInformationProcess(long long long long) +@ stdcall ZwSetInformationThread(long long ptr long) +@ stdcall ZwSetInformationToken(long long ptr long) +@ stdcall ZwSetIntervalProfile(long long) +@ stdcall ZwSetIoCompletion(ptr long ptr long long) +@ stdcall ZwSetLdtEntries(long double long double) ; CHECKME +@ stdcall ZwSetLowEventPair(ptr) +@ stdcall ZwSetLowWaitHighEventPair(ptr) +;@ stdcall ZwSetLowWaitHighThread ; 3.51 and 4.0 only +@ stdcall ZwSetQuotaInformationFile(ptr ptr ptr long) +@ stdcall ZwSetSecurityObject(long long ptr) +@ stdcall ZwSetSystemEnvironmentValue(ptr ptr) +@ stdcall ZwSetSystemEnvironmentValueEx(ptr ptr) +@ stdcall ZwSetSystemInformation(long ptr long) +@ stdcall ZwSetSystemPowerState(long long long) +@ stdcall ZwSetSystemTime(ptr ptr) +@ stdcall ZwSetThreadExecutionState(long ptr) +@ stdcall ZwSetTimer(long ptr ptr ptr long long ptr) +@ stdcall ZwSetTimerResolution(long long ptr) +@ stdcall ZwSetUuidSeed(ptr) +@ stdcall ZwSetValueKey(long long long long long long) +@ stdcall ZwSetVolumeInformationFile(long ptr ptr long long) +@ stdcall ZwShutdownSystem(long) +@ stdcall ZwSignalAndWaitForSingleObject(long long long ptr) +@ stdcall ZwStartProfile(ptr) +@ stdcall ZwStopProfile(ptr) +@ stdcall ZwSuspendProcess(ptr) +@ stdcall ZwSuspendThread(long ptr) +@ stdcall ZwSystemDebugControl(long ptr long ptr long ptr) +@ stdcall ZwTerminateJobObject(long long) +@ stdcall ZwTerminateProcess(long long) +@ stdcall ZwTerminateThread(long long) +@ stdcall ZwTestAlert() +@ stdcall ZwTraceEvent(long long long ptr) +@ stdcall ZwTranslateFilePath(ptr long ptr long) +@ stdcall ZwUnloadDriver(ptr) +@ stdcall ZwUnloadKey(long) +@ stdcall ZwUnloadKeyEx(ptr ptr) +@ stdcall ZwUnlockFile(long ptr ptr ptr ptr) +@ stdcall ZwUnlockVirtualMemory(long ptr ptr long) +@ stdcall ZwUnmapViewOfSection(long ptr) +@ stdcall ZwVdmControl(long ptr) +;@ stdcall ZwW32Call(long ptr long ptr ptr) +@ stdcall ZwWaitForDebugEvent(ptr long ptr ptr) +@ stdcall ZwWaitForKeyedEvent(ptr ptr long ptr) +@ stdcall ZwWaitForMultipleObjects(long ptr long long ptr) +;@ stdcall ZwWaitForProcessMutant ; 3.51 only +@ stdcall ZwWaitForSingleObject(long long long) +@ stdcall ZwWaitHighEventPair(ptr) +@ stdcall ZwWaitLowEventPair(ptr) +@ stdcall ZwWriteFile(long long ptr ptr ptr ptr long ptr ptr) +@ stdcall ZwWriteFileGather(long long ptr ptr ptr ptr long ptr ptr) +@ stdcall ZwWriteRequestData(ptr ptr long ptr long ptr) +@ stdcall ZwWriteVirtualMemory(long ptr ptr long ptr) +@ stdcall ZwYieldExecution() +@ cdecl -arch=i386 _CIcos() +@ cdecl -arch=i386 _CIlog() +@ cdecl -arch=i386 _CIpow() +@ cdecl -arch=i386 _CIsin() +@ cdecl -arch=i386 _CIsqrt() +@ cdecl -arch=x86_64 __C_specific_handler(ptr long ptr ptr) +@ cdecl __isascii(long) +@ cdecl __iscsym(long) +@ cdecl __iscsymf(long) +@ cdecl __toascii(long) +@ cdecl -arch=i386 -ret64 _alldiv(double double) +@ cdecl -arch=i386 _alldvrm() +@ cdecl -arch=i386 -ret64 _allmul(double double) +@ cdecl -arch=i386 -norelay _alloca_probe() +@ cdecl -arch=i386 -ret64 _allrem(double double) +@ cdecl -arch=i386 _allshl() +@ cdecl -arch=i386 _allshr() +@ cdecl -ret64 _atoi64(str) +@ cdecl -arch=i386 -ret64 _aulldiv(double double) +@ cdecl -arch=i386 _aulldvrm() +@ cdecl -arch=i386 -ret64 _aullrem(double double) +@ cdecl -arch=i386 _aullshr() +@ extern -arch=i386 _chkstk +@ cdecl -arch=i386,x86_64 _fltused() +@ cdecl -arch=i386 -ret64 _ftol() +@ cdecl _i64toa(double ptr long) +@ cdecl _i64tow(double ptr long) +@ cdecl _itoa(long ptr long) +@ cdecl _itow(long ptr long) +@ cdecl _lfind(ptr ptr ptr long ptr) +@ cdecl -arch=x86_64 _local_unwind() +@ cdecl _ltoa(long ptr long) +@ cdecl _ltow(long ptr long) +@ cdecl _memccpy(ptr ptr long long) +@ cdecl _memicmp(str str long) +@ cdecl -arch=x86_64 _setjmp(ptr ptr) +@ cdecl -arch=x86_64 _setjmpex(ptr ptr) +@ varargs _snprintf(ptr long str) +@ varargs _snwprintf(ptr long wstr) +@ cdecl _splitpath(str ptr ptr ptr ptr) +@ cdecl _strcmpi(str str) _stricmp +@ cdecl _stricmp(str str) +@ cdecl _strlwr(str) +@ cdecl _strnicmp(str str long) +@ cdecl _strupr(str) +@ cdecl _tolower(long) +@ cdecl _toupper(long) +@ cdecl _ui64toa(double ptr long) +@ cdecl _ui64tow(double ptr long) +@ cdecl _ultoa(long ptr long) +@ cdecl _ultow(long ptr long) +@ cdecl _vscwprintf(wstr ptr) +@ cdecl _vsnprintf(ptr long str ptr) +@ cdecl _vsnwprintf(ptr long wstr ptr) +@ cdecl _wcsicmp(wstr wstr) +@ cdecl _wcslwr(wstr) +@ cdecl _wcsnicmp(wstr wstr long) +@ cdecl _wcsupr(wstr) +@ cdecl _wtoi(wstr) +@ cdecl _wtoi64(wstr) +@ cdecl _wtol(wstr) +@ cdecl abs(long) +@ cdecl -arch=i386,x86_64 atan(double) +@ cdecl atoi(str) +@ cdecl atol(str) +@ cdecl bsearch(ptr ptr long long ptr) +@ cdecl -arch=i386,x86_64 ceil(double) +@ cdecl -arch=i386,x86_64 cos(double) +@ cdecl -arch=i386,x86_64 fabs(double) +@ cdecl -arch=i386,x86_64 floor(double) +@ cdecl isalnum(long) +@ cdecl isalpha(long) +@ cdecl iscntrl(long) +@ cdecl isdigit(long) +@ cdecl isgraph(long) +@ cdecl islower(long) +@ cdecl isprint(long) +@ cdecl ispunct(long) +@ cdecl isspace(long) +@ cdecl isupper(long) +@ cdecl iswalpha(long) +@ cdecl iswctype(long long) +@ cdecl iswdigit(long) +@ cdecl iswlower(long) +@ cdecl iswspace(long) +@ cdecl iswxdigit(long) +@ cdecl isxdigit(long) +@ cdecl labs(long) +@ cdecl -arch=i386,x86_64 log(double) +@ cdecl -arch=x86_64 longjmp(ptr) +@ cdecl mbstowcs(ptr str long) +@ cdecl memchr(ptr long long) +@ cdecl memcmp(ptr ptr long) +@ cdecl memcpy(ptr ptr long) memmove +@ cdecl memmove(ptr ptr long) +@ cdecl memset(ptr long long) +@ cdecl -arch=i386,x86_64 pow(double double) +@ cdecl qsort(ptr long long ptr) +@ cdecl -arch=i386,x86_64 sin(double) +@ varargs sprintf(ptr str) +@ cdecl -arch=i386,x86_64 sqrt(double) +@ varargs sscanf(str str) +@ cdecl strcat(str str) +@ cdecl strchr(str long) +@ cdecl strcmp(str str) +@ cdecl strcpy(ptr str) +@ cdecl strcspn(str str) +@ cdecl strlen(str) +@ cdecl strncat(str str long) +@ cdecl strncmp(str str long) +@ cdecl strncpy(ptr str long) +@ cdecl strpbrk(str str) +@ cdecl strrchr(str long) +@ cdecl strspn(str str) +@ cdecl strstr(str str) +@ cdecl strtol(str ptr long) +@ cdecl strtoul(str ptr long) +@ varargs swprintf(ptr wstr) +@ cdecl -arch=i386,x86_64 tan(double) +@ cdecl tolower(long) +@ cdecl toupper(long) +@ cdecl towlower(long) +@ cdecl towupper(long) +@ stdcall vDbgPrintEx(long long str ptr) +@ stdcall vDbgPrintExWithPrefix(str long long str ptr) +@ cdecl vsprintf(ptr str ptr) +@ cdecl wcscat(wstr wstr) +@ cdecl wcschr(wstr long) +@ cdecl wcscmp(wstr wstr) +@ cdecl wcscpy(ptr wstr) +@ cdecl wcscspn(wstr wstr) +@ cdecl wcslen(wstr) +@ cdecl wcsncat(wstr wstr long) +@ cdecl wcsncmp(wstr wstr long) +@ cdecl wcsncpy(ptr wstr long) +@ cdecl wcspbrk(wstr wstr) +@ cdecl wcsrchr(wstr long) +@ cdecl wcsspn(wstr wstr) +@ cdecl wcsstr(wstr wstr) +;@ cdecl wcstok(wstr wstr) +@ cdecl wcstol(wstr ptr long) +@ cdecl wcstombs(ptr ptr long) +@ cdecl wcstoul(wstr ptr long) diff --cc dll/win32/kernel32/wine/actctx.c index 7eb91a90c4c,00000000000..c1a73d16972 mode 100644,000000..100644 --- a/dll/win32/kernel32/wine/actctx.c +++ b/dll/win32/kernel32/wine/actctx.c @@@ -1,188 -1,0 +1,192 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS system libraries + * FILE: dll/win32/kernel32/wine/actctx.c + * PURPOSE: Activation contexts + * PROGRAMMERS: Jacek Caban for CodeWeavers + * Eric Pouech + * Jon Griffiths + * Dmitry Chapyshev (dmitry@reactos.org) + * Samuel Serapión + */ + - /* synched with wine 1.1.26 */ ++/* Partly synched with Wine 1.7.17 */ + +#include + +#define NDEBUG +#include +DEBUG_CHANNEL(actctx); + +#define ACTCTX_FAKE_HANDLE ((HANDLE) 0xf00baa) + +/*********************************************************************** + * CreateActCtxA (KERNEL32.@) + * + * Create an activation context. + */ +HANDLE WINAPI CreateActCtxA(PCACTCTXA pActCtx) +{ + ACTCTXW actw; + SIZE_T len; + HANDLE ret = INVALID_HANDLE_VALUE; + LPWSTR src = NULL, assdir = NULL, resname = NULL, appname = NULL; + + TRACE("%p %08x\n", pActCtx, pActCtx ? pActCtx->dwFlags : 0); + + if (!pActCtx || pActCtx->cbSize != sizeof(*pActCtx)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + actw.cbSize = sizeof(actw); + actw.dwFlags = pActCtx->dwFlags; + if (pActCtx->lpSource) + { + len = MultiByteToWideChar(CP_ACP, 0, pActCtx->lpSource, -1, NULL, 0); + src = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!src) return INVALID_HANDLE_VALUE; + MultiByteToWideChar(CP_ACP, 0, pActCtx->lpSource, -1, src, len); + } + actw.lpSource = src; + + if (actw.dwFlags & ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID) + actw.wProcessorArchitecture = pActCtx->wProcessorArchitecture; + if (actw.dwFlags & ACTCTX_FLAG_LANGID_VALID) + actw.wLangId = pActCtx->wLangId; + if (actw.dwFlags & ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID) + { + len = MultiByteToWideChar(CP_ACP, 0, pActCtx->lpAssemblyDirectory, -1, NULL, 0); + assdir = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!assdir) goto done; + MultiByteToWideChar(CP_ACP, 0, pActCtx->lpAssemblyDirectory, -1, assdir, len); + actw.lpAssemblyDirectory = assdir; + } + if (actw.dwFlags & ACTCTX_FLAG_RESOURCE_NAME_VALID) + { + if ((ULONG_PTR)pActCtx->lpResourceName >> 16) + { + len = MultiByteToWideChar(CP_ACP, 0, pActCtx->lpResourceName, -1, NULL, 0); + resname = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!resname) goto done; + MultiByteToWideChar(CP_ACP, 0, pActCtx->lpResourceName, -1, resname, len); + actw.lpResourceName = resname; + } + else actw.lpResourceName = (LPCWSTR)pActCtx->lpResourceName; + } + if (actw.dwFlags & ACTCTX_FLAG_APPLICATION_NAME_VALID) + { + len = MultiByteToWideChar(CP_ACP, 0, pActCtx->lpApplicationName, -1, NULL, 0); + appname = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!appname) goto done; + MultiByteToWideChar(CP_ACP, 0, pActCtx->lpApplicationName, -1, appname, len); + actw.lpApplicationName = appname; + } + if (actw.dwFlags & ACTCTX_FLAG_HMODULE_VALID) + actw.hModule = pActCtx->hModule; + + ret = CreateActCtxW(&actw); + +done: + HeapFree(GetProcessHeap(), 0, src); + HeapFree(GetProcessHeap(), 0, assdir); + HeapFree(GetProcessHeap(), 0, resname); + HeapFree(GetProcessHeap(), 0, appname); + return ret; +} + +/*********************************************************************** + * CreateActCtxW (KERNEL32.@) + * + * Create an activation context. + */ +HANDLE WINAPI CreateActCtxW(PCACTCTXW pActCtx) +{ + NTSTATUS status; + HANDLE hActCtx; + + TRACE("%p %08x\n", pActCtx, pActCtx ? pActCtx->dwFlags : 0); + + if ((status = RtlCreateActivationContext(0, (PVOID)pActCtx, 0, NULL, NULL, &hActCtx))) + { + SetLastError(RtlNtStatusToDosError(status)); + return INVALID_HANDLE_VALUE; + } + return hActCtx; +} + +/*********************************************************************** + * FindActCtxSectionStringA (KERNEL32.@) + * + * Find information about a string in an activation context. + */ +BOOL WINAPI FindActCtxSectionStringA(DWORD dwFlags, const GUID* lpExtGuid, + ULONG ulId, LPCSTR lpSearchStr, + PACTCTX_SECTION_KEYED_DATA pInfo) +{ + LPWSTR search_str; + DWORD len; + BOOL ret; + + TRACE("%08x %s %u %s %p\n", dwFlags, debugstr_guid(lpExtGuid), + ulId, debugstr_a(lpSearchStr), pInfo); + + if (!lpSearchStr) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + len = MultiByteToWideChar(CP_ACP, 0, lpSearchStr, -1, NULL, 0); + search_str = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, lpSearchStr, -1, search_str, len); + + ret = FindActCtxSectionStringW(dwFlags, lpExtGuid, ulId, search_str, pInfo); + + HeapFree(GetProcessHeap(), 0, search_str); + return ret; +} + +/*********************************************************************** + * FindActCtxSectionStringW (KERNEL32.@) + * + * Find information about a string in an activation context. + */ +BOOL WINAPI FindActCtxSectionStringW(DWORD dwFlags, const GUID* lpExtGuid, + ULONG ulId, LPCWSTR lpSearchStr, + PACTCTX_SECTION_KEYED_DATA pInfo) +{ + UNICODE_STRING us; + NTSTATUS status; + + RtlInitUnicodeString(&us, lpSearchStr); + if ((status = RtlFindActivationContextSectionString(dwFlags, lpExtGuid, ulId, &us, pInfo))) + { + SetLastError(RtlNtStatusToDosError(status)); + return FALSE; + } + return TRUE; +} + +/*********************************************************************** + * FindActCtxSectionGuid (KERNEL32.@) + * + * Find information about a GUID in an activation context. + */ +BOOL WINAPI FindActCtxSectionGuid(DWORD dwFlags, const GUID* lpExtGuid, + ULONG ulId, const GUID* lpSearchGuid, + PACTCTX_SECTION_KEYED_DATA pInfo) +{ - FIXME("%08x %s %u %s %p\n", dwFlags, debugstr_guid(lpExtGuid), - ulId, debugstr_guid(lpSearchGuid), pInfo); - SetLastError( ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; - } ++ NTSTATUS status; + ++ if ((status = RtlFindActivationContextSectionGuid(dwFlags, lpExtGuid, ulId, lpSearchGuid, pInfo))) ++ { ++ SetLastError(RtlNtStatusToDosError(status)); ++ return FALSE; ++ } ++ ++ return TRUE; ++} + +/* EOF */ diff --cc dll/win32/shlwapi/path.c index 46eb8d93279,00000000000..c0589ac9780 mode 100644,000000..100644 --- a/dll/win32/shlwapi/path.c +++ b/dll/win32/shlwapi/path.c @@@ -1,4333 -1,0 +1,4333 @@@ +/* + * Path Functions + * + * Copyright 1999, 2000 Juergen Schmied + * Copyright 2001, 2002 Jon Griffiths + * + * 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" + +/* Get a function pointer from a DLL handle */ +#define GET_FUNC(func, module, name, fail) \ + do { \ + if (!func) { \ + if (!SHLWAPI_h##module && !(SHLWAPI_h##module = LoadLibraryA(#module ".dll"))) return fail; \ + func = (fn##func)GetProcAddress(SHLWAPI_h##module, name); \ + if (!func) return fail; \ + } \ + } while (0) + +/* DLL handles for late bound calls */ +static HMODULE SHLWAPI_hshell32; + +/* Function pointers for GET_FUNC macro; these need to be global because of gcc bug */ +typedef BOOL (WINAPI *fnpIsNetDrive)(int); +static fnpIsNetDrive pIsNetDrive; + +HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR,LPWSTR,DWORD); + +static inline WCHAR* heap_strdupAtoW(LPCSTR str) +{ + WCHAR *ret = NULL; + + if (str) + { + DWORD len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); + if (ret) + MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); + } + + return ret; +} + +/************************************************************************* + * PathAppendA [SHLWAPI.@] + * + * Append one path to another. + * + * PARAMS + * lpszPath [I/O] Initial part of path, and destination for output + * lpszAppend [I] Path to append + * + * RETURNS + * Success: TRUE. lpszPath contains the newly created path. + * Failure: FALSE, if either path is NULL, or PathCombineA() fails. + * + * NOTES + * lpszAppend must contain at least one backslash ('\') if not NULL. + * Because PathCombineA() is used to join the paths, the resulting + * path is also canonicalized. + */ +BOOL WINAPI PathAppendA (LPSTR lpszPath, LPCSTR lpszAppend) +{ + TRACE("(%s,%s)\n",debugstr_a(lpszPath), debugstr_a(lpszAppend)); + + if (lpszPath && lpszAppend) + { + if (!PathIsUNCA(lpszAppend)) + while (*lpszAppend == '\\') + lpszAppend++; + if (PathCombineA(lpszPath, lpszPath, lpszAppend)) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathAppendW [SHLWAPI.@] + * + * See PathAppendA. + */ +BOOL WINAPI PathAppendW(LPWSTR lpszPath, LPCWSTR lpszAppend) +{ + TRACE("(%s,%s)\n",debugstr_w(lpszPath), debugstr_w(lpszAppend)); + + if (lpszPath && lpszAppend) + { + if (!PathIsUNCW(lpszAppend)) + while (*lpszAppend == '\\') + lpszAppend++; + if (PathCombineW(lpszPath, lpszPath, lpszAppend)) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathCombineA [SHLWAPI.@] + * + * Combine two paths together. + * + * PARAMS + * lpszDest [O] Destination for combined path + * lpszDir [I] Directory path + * lpszFile [I] File path + * + * RETURNS + * Success: The output path + * Failure: NULL, if inputs are invalid. + * + * NOTES + * lpszDest should be at least MAX_PATH in size, and may point to the same + * memory location as lpszDir. The combined path is canonicalised. + */ +LPSTR WINAPI PathCombineA(LPSTR lpszDest, LPCSTR lpszDir, LPCSTR lpszFile) +{ + WCHAR szDest[MAX_PATH]; + WCHAR szDir[MAX_PATH]; + WCHAR szFile[MAX_PATH]; + TRACE("(%p,%s,%s)\n", lpszDest, debugstr_a(lpszDir), debugstr_a(lpszFile)); + + /* Invalid parameters */ + if (!lpszDest) + return NULL; + if (!lpszDir && !lpszFile) + { + lpszDest[0] = 0; + return NULL; + } + + if (lpszDir) + MultiByteToWideChar(CP_ACP,0,lpszDir,-1,szDir,MAX_PATH); + if (lpszFile) + MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH); + + if (PathCombineW(szDest, lpszDir ? szDir : NULL, lpszFile ? szFile : NULL)) + if (WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0)) + return lpszDest; + + lpszDest[0] = 0; + return NULL; +} + +/************************************************************************* + * PathCombineW [SHLWAPI.@] + * + * See PathCombineA. + */ +LPWSTR WINAPI PathCombineW(LPWSTR lpszDest, LPCWSTR lpszDir, LPCWSTR lpszFile) +{ + WCHAR szTemp[MAX_PATH]; + BOOL bUseBoth = FALSE, bStrip = FALSE; + + TRACE("(%p,%s,%s)\n", lpszDest, debugstr_w(lpszDir), debugstr_w(lpszFile)); + + /* Invalid parameters */ + if (!lpszDest) + return NULL; + if (!lpszDir && !lpszFile) + { + lpszDest[0] = 0; + return NULL; + } + + if ((!lpszFile || !*lpszFile) && lpszDir) + { + /* Use dir only */ + lstrcpynW(szTemp, lpszDir, MAX_PATH); + } + else if (!lpszDir || !*lpszDir || !PathIsRelativeW(lpszFile)) + { + if (!lpszDir || !*lpszDir || *lpszFile != '\\' || PathIsUNCW(lpszFile)) + { + /* Use file only */ + lstrcpynW(szTemp, lpszFile, MAX_PATH); + } + else + { + bUseBoth = TRUE; + bStrip = TRUE; + } + } + else + bUseBoth = TRUE; + + if (bUseBoth) + { + lstrcpynW(szTemp, lpszDir, MAX_PATH); + if (bStrip) + { + PathStripToRootW(szTemp); + lpszFile++; /* Skip '\' */ + } + if (!PathAddBackslashW(szTemp) || strlenW(szTemp) + strlenW(lpszFile) >= MAX_PATH) + { + lpszDest[0] = 0; + return NULL; + } + strcatW(szTemp, lpszFile); + } + + PathCanonicalizeW(lpszDest, szTemp); + return lpszDest; +} + +/************************************************************************* + * PathAddBackslashA [SHLWAPI.@] + * + * Append a backslash ('\') to a path if one doesn't exist. + * + * PARAMS + * lpszPath [I/O] The path to append a backslash to. + * + * RETURNS + * Success: The position of the last backslash in the path. + * Failure: NULL, if lpszPath is NULL or the path is too large. + */ +LPSTR WINAPI PathAddBackslashA(LPSTR lpszPath) +{ + size_t iLen; + LPSTR prev = lpszPath; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath || (iLen = strlen(lpszPath)) >= MAX_PATH) + return NULL; + + if (iLen) + { + do { + lpszPath = CharNextA(prev); + if (*lpszPath) + prev = lpszPath; + } while (*lpszPath); + if (*prev != '\\') + { + *lpszPath++ = '\\'; + *lpszPath = '\0'; + } + } + return lpszPath; +} + +/************************************************************************* + * PathAddBackslashW [SHLWAPI.@] + * + * See PathAddBackslashA. + */ +LPWSTR WINAPI PathAddBackslashW( LPWSTR lpszPath ) +{ + size_t iLen; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath || (iLen = strlenW(lpszPath)) >= MAX_PATH) + return NULL; + + if (iLen) + { + lpszPath += iLen; + if (lpszPath[-1] != '\\') + { + *lpszPath++ = '\\'; + *lpszPath = '\0'; + } + } + return lpszPath; +} + +/************************************************************************* + * PathBuildRootA [SHLWAPI.@] + * + * Create a root drive string (e.g. "A:\") from a drive number. + * + * PARAMS + * lpszPath [O] Destination for the drive string + * + * RETURNS + * lpszPath + * + * NOTES + * If lpszPath is NULL or drive is invalid, nothing is written to lpszPath. + */ +LPSTR WINAPI PathBuildRootA(LPSTR lpszPath, int drive) +{ + TRACE("(%p,%d)\n", lpszPath, drive); + + if (lpszPath && drive >= 0 && drive < 26) + { + lpszPath[0] = 'A' + drive; + lpszPath[1] = ':'; + lpszPath[2] = '\\'; + lpszPath[3] = '\0'; + } + return lpszPath; +} + +/************************************************************************* + * PathBuildRootW [SHLWAPI.@] + * + * See PathBuildRootA. + */ +LPWSTR WINAPI PathBuildRootW(LPWSTR lpszPath, int drive) +{ + TRACE("(%p,%d)\n", lpszPath, drive); + + if (lpszPath && drive >= 0 && drive < 26) + { + lpszPath[0] = 'A' + drive; + lpszPath[1] = ':'; + lpszPath[2] = '\\'; + lpszPath[3] = '\0'; + } + return lpszPath; +} + +/************************************************************************* + * PathFindFileNameA [SHLWAPI.@] + * + * Locate the start of the file name in a path + * + * PARAMS + * lpszPath [I] Path to search + * + * RETURNS + * A pointer to the first character of the file name + */ +LPSTR WINAPI PathFindFileNameA(LPCSTR lpszPath) +{ + LPCSTR lastSlash = lpszPath; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + while (lpszPath && *lpszPath) + { + if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && + lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') + lastSlash = lpszPath + 1; + lpszPath = CharNextA(lpszPath); + } + return (LPSTR)lastSlash; +} + +/************************************************************************* + * PathFindFileNameW [SHLWAPI.@] + * + * See PathFindFileNameA. + */ +LPWSTR WINAPI PathFindFileNameW(LPCWSTR lpszPath) +{ + LPCWSTR lastSlash = lpszPath; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + while (lpszPath && *lpszPath) + { + if ((*lpszPath == '\\' || *lpszPath == '/' || *lpszPath == ':') && + lpszPath[1] && lpszPath[1] != '\\' && lpszPath[1] != '/') + lastSlash = lpszPath + 1; + lpszPath++; + } + return (LPWSTR)lastSlash; +} + +/************************************************************************* + * PathFindExtensionA [SHLWAPI.@] + * + * Locate the start of the file extension in a path + * + * PARAMS + * lpszPath [I] The path to search + * + * RETURNS + * A pointer to the first character of the extension, the end of + * the string if the path has no extension, or NULL If lpszPath is NULL + */ +LPSTR WINAPI PathFindExtensionA( LPCSTR lpszPath ) +{ + LPCSTR lastpoint = NULL; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath==' ') + lastpoint = NULL; + else if (*lpszPath == '.') + lastpoint = lpszPath; + lpszPath = CharNextA(lpszPath); + } + } + return (LPSTR)(lastpoint ? lastpoint : lpszPath); +} + +/************************************************************************* + * PathFindExtensionW [SHLWAPI.@] + * + * See PathFindExtensionA. + */ +LPWSTR WINAPI PathFindExtensionW( LPCWSTR lpszPath ) +{ + LPCWSTR lastpoint = NULL; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath==' ') + lastpoint = NULL; + else if (*lpszPath == '.') + lastpoint = lpszPath; + lpszPath++; + } + } + return (LPWSTR)(lastpoint ? lastpoint : lpszPath); +} + +/************************************************************************* + * PathGetArgsA [SHLWAPI.@] + * + * Find the next argument in a string delimited by spaces. + * + * PARAMS + * lpszPath [I] The string to search for arguments in + * + * RETURNS + * The start of the next argument in lpszPath, or NULL if lpszPath is NULL + * + * NOTES + * Spaces in quoted strings are ignored as delimiters. + */ +LPSTR WINAPI PathGetArgsA(LPCSTR lpszPath) +{ + BOOL bSeenQuote = FALSE; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if ((*lpszPath==' ') && !bSeenQuote) + return (LPSTR)lpszPath + 1; + if (*lpszPath == '"') + bSeenQuote = !bSeenQuote; + lpszPath = CharNextA(lpszPath); + } + } + return (LPSTR)lpszPath; +} + +/************************************************************************* + * PathGetArgsW [SHLWAPI.@] + * + * See PathGetArgsA. + */ +LPWSTR WINAPI PathGetArgsW(LPCWSTR lpszPath) +{ + BOOL bSeenQuote = FALSE; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath) + { + while (*lpszPath) + { + if ((*lpszPath==' ') && !bSeenQuote) + return (LPWSTR)lpszPath + 1; + if (*lpszPath == '"') + bSeenQuote = !bSeenQuote; + lpszPath++; + } + } + return (LPWSTR)lpszPath; +} + +/************************************************************************* + * PathGetDriveNumberA [SHLWAPI.@] + * + * Return the drive number from a path + * + * PARAMS + * lpszPath [I] Path to get the drive number from + * + * RETURNS + * Success: The drive number corresponding to the drive in the path + * Failure: -1, if lpszPath contains no valid drive + */ +int WINAPI PathGetDriveNumberA(LPCSTR lpszPath) +{ + TRACE ("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath && !IsDBCSLeadByte(*lpszPath) && lpszPath[1] == ':' && + tolower(*lpszPath) >= 'a' && tolower(*lpszPath) <= 'z') + return tolower(*lpszPath) - 'a'; + return -1; +} + +/************************************************************************* + * PathGetDriveNumberW [SHLWAPI.@] + * + * See PathGetDriveNumberA. + */ +int WINAPI PathGetDriveNumberW(LPCWSTR lpszPath) +{ + TRACE ("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath) + { + WCHAR tl = tolowerW(lpszPath[0]); + if (tl >= 'a' && tl <= 'z' && lpszPath[1] == ':') + return tl - 'a'; + } + return -1; +} + +/************************************************************************* + * PathRemoveFileSpecA [SHLWAPI.@] + * + * Remove the file specification from a path. + * + * PARAMS + * lpszPath [I/O] Path to remove the file spec from + * + * RETURNS + * TRUE If the path was valid and modified + * FALSE Otherwise + */ +BOOL WINAPI PathRemoveFileSpecA(LPSTR lpszPath) +{ + LPSTR lpszFileSpec = lpszPath; + BOOL bModified = FALSE; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if(lpszPath) + { + /* Skip directory or UNC path */ + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + + while (*lpszPath) + { + if(*lpszPath == '\\') + lpszFileSpec = lpszPath; /* Skip dir */ + else if(*lpszPath == ':') + { + lpszFileSpec = ++lpszPath; /* Skip drive */ + if (*lpszPath == '\\') + lpszFileSpec++; + } + if (!(lpszPath = CharNextA(lpszPath))) + break; + } + + if (*lpszFileSpec) + { + *lpszFileSpec = '\0'; + bModified = TRUE; + } + } + return bModified; +} + +/************************************************************************* + * PathRemoveFileSpecW [SHLWAPI.@] + * + * See PathRemoveFileSpecA. + */ +BOOL WINAPI PathRemoveFileSpecW(LPWSTR lpszPath) +{ + LPWSTR lpszFileSpec = lpszPath; + BOOL bModified = FALSE; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if(lpszPath) + { + /* Skip directory or UNC path */ + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + if (*lpszPath == '\\') + lpszFileSpec = ++lpszPath; + + while (*lpszPath) + { + if(*lpszPath == '\\') + lpszFileSpec = lpszPath; /* Skip dir */ + else if(*lpszPath == ':') + { + lpszFileSpec = ++lpszPath; /* Skip drive */ + if (*lpszPath == '\\') + lpszFileSpec++; + } + lpszPath++; + } + + if (*lpszFileSpec) + { + *lpszFileSpec = '\0'; + bModified = TRUE; + } + } + return bModified; +} + +/************************************************************************* + * PathStripPathA [SHLWAPI.@] + * + * Remove the initial path from the beginning of a filename + * + * PARAMS + * lpszPath [I/O] Path to remove the initial path from + * + * RETURNS + * Nothing. + */ +void WINAPI PathStripPathA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + LPSTR lpszFileName = PathFindFileNameA(lpszPath); + if(lpszFileName) + RtlMoveMemory(lpszPath, lpszFileName, strlen(lpszFileName)+1); + } +} + +/************************************************************************* + * PathStripPathW [SHLWAPI.@] + * + * See PathStripPathA. + */ +void WINAPI PathStripPathW(LPWSTR lpszPath) +{ + LPWSTR lpszFileName; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + lpszFileName = PathFindFileNameW(lpszPath); + if(lpszFileName) + RtlMoveMemory(lpszPath, lpszFileName, (strlenW(lpszFileName)+1)*sizeof(WCHAR)); +} + +/************************************************************************* + * PathStripToRootA [SHLWAPI.@] + * + * Reduce a path to its root. + * + * PARAMS + * lpszPath [I/O] the path to reduce + * + * RETURNS + * Success: TRUE if the stripped path is a root path + * Failure: FALSE if the path cannot be stripped or is NULL + */ +BOOL WINAPI PathStripToRootA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + while(!PathIsRootA(lpszPath)) + if (!PathRemoveFileSpecA(lpszPath)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathStripToRootW [SHLWAPI.@] + * + * See PathStripToRootA. + */ +BOOL WINAPI PathStripToRootW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + while(!PathIsRootW(lpszPath)) + if (!PathRemoveFileSpecW(lpszPath)) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathRemoveArgsA [SHLWAPI.@] + * + * Strip space separated arguments from a path. + * + * PARAMS + * lpszPath [I/O] Path to remove arguments from + * + * RETURNS + * Nothing. + */ +void WINAPI PathRemoveArgsA(LPSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if(lpszPath) + { + LPSTR lpszArgs = PathGetArgsA(lpszPath); + if (*lpszArgs) + lpszArgs[-1] = '\0'; + else + { + LPSTR lpszLastChar = CharPrevA(lpszPath, lpszArgs); + if(*lpszLastChar == ' ') + *lpszLastChar = '\0'; + } + } +} + +/************************************************************************* + * PathRemoveArgsW [SHLWAPI.@] + * + * See PathRemoveArgsA. + */ +void WINAPI PathRemoveArgsW(LPWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if(lpszPath) + { + LPWSTR lpszArgs = PathGetArgsW(lpszPath); + if (*lpszArgs || (lpszArgs > lpszPath && lpszArgs[-1] == ' ')) + lpszArgs[-1] = '\0'; + } +} + +/************************************************************************* + * PathRemoveExtensionA [SHLWAPI.@] + * + * Remove the file extension from a path + * + * PARAMS + * lpszPath [I/O] Path to remove the extension from + * + * NOTES + * The NUL terminator must be written only if extension exists + * and if the pointed character is not already NUL. + * + * RETURNS + * Nothing. + */ +void WINAPI PathRemoveExtensionA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + lpszPath = PathFindExtensionA(lpszPath); + if (lpszPath && *lpszPath != '\0') + *lpszPath = '\0'; + } +} + +/************************************************************************* + * PathRemoveExtensionW [SHLWAPI.@] + * + * See PathRemoveExtensionA. +*/ +void WINAPI PathRemoveExtensionW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath) + { + lpszPath = PathFindExtensionW(lpszPath); + if (lpszPath && *lpszPath != '\0') + *lpszPath = '\0'; + } +} + +/************************************************************************* + * PathRemoveBackslashA [SHLWAPI.@] + * + * Remove a trailing backslash from a path. + * + * PARAMS + * lpszPath [I/O] Path to remove backslash from + * + * RETURNS + * Success: A pointer to the end of the path + * Failure: NULL, if lpszPath is NULL + */ +LPSTR WINAPI PathRemoveBackslashA( LPSTR lpszPath ) +{ + LPSTR szTemp = NULL; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(lpszPath) + { + szTemp = CharPrevA(lpszPath, lpszPath + strlen(lpszPath)); + if (!PathIsRootA(lpszPath) && *szTemp == '\\') + *szTemp = '\0'; + } + return szTemp; +} + +/************************************************************************* + * PathRemoveBackslashW [SHLWAPI.@] + * + * See PathRemoveBackslashA. + */ +LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpszPath ) +{ + LPWSTR szTemp = NULL; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(lpszPath) + { + szTemp = lpszPath + strlenW(lpszPath); + if (szTemp > lpszPath) szTemp--; + if (!PathIsRootW(lpszPath) && *szTemp == '\\') + *szTemp = '\0'; + } + return szTemp; +} + +/************************************************************************* + * PathRemoveBlanksA [SHLWAPI.@] + * + * Remove Spaces from the start and end of a path. + * + * PARAMS + * lpszPath [I/O] Path to strip blanks from + * + * RETURNS + * Nothing. + */ +VOID WINAPI PathRemoveBlanksA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(lpszPath && *lpszPath) + { + LPSTR start = lpszPath; + + while (*lpszPath == ' ') + lpszPath = CharNextA(lpszPath); + + while(*lpszPath) + *start++ = *lpszPath++; + + if (start != lpszPath) + while (start[-1] == ' ') + start--; + *start = '\0'; + } +} + +/************************************************************************* + * PathRemoveBlanksW [SHLWAPI.@] + * + * See PathRemoveBlanksA. + */ +VOID WINAPI PathRemoveBlanksW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(lpszPath && *lpszPath) + { + LPWSTR start = lpszPath; + + while (*lpszPath == ' ') + lpszPath++; + + while(*lpszPath) + *start++ = *lpszPath++; + + if (start != lpszPath) + while (start[-1] == ' ') + start--; + *start = '\0'; + } +} + +/************************************************************************* + * PathQuoteSpacesA [SHLWAPI.@] + * + * Surround a path containing spaces in quotes. + * + * PARAMS + * lpszPath [I/O] Path to quote + * + * RETURNS + * Nothing. + * + * NOTES + * The path is not changed if it is invalid or has no spaces. + */ +VOID WINAPI PathQuoteSpacesA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(lpszPath && StrChrA(lpszPath,' ')) + { + size_t iLen = strlen(lpszPath) + 1; + + if (iLen + 2 < MAX_PATH) + { + memmove(lpszPath + 1, lpszPath, iLen); + lpszPath[0] = '"'; + lpszPath[iLen] = '"'; + lpszPath[iLen + 1] = '\0'; + } + } +} + +/************************************************************************* + * PathQuoteSpacesW [SHLWAPI.@] + * + * See PathQuoteSpacesA. + */ +VOID WINAPI PathQuoteSpacesW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(lpszPath && StrChrW(lpszPath,' ')) + { + int iLen = strlenW(lpszPath) + 1; + + if (iLen + 2 < MAX_PATH) + { + memmove(lpszPath + 1, lpszPath, iLen * sizeof(WCHAR)); + lpszPath[0] = '"'; + lpszPath[iLen] = '"'; + lpszPath[iLen + 1] = '\0'; + } + } +} + +/************************************************************************* + * PathUnquoteSpacesA [SHLWAPI.@] + * + * Remove quotes ("") from around a path, if present. + * + * PARAMS + * lpszPath [I/O] Path to strip quotes from + * + * RETURNS + * Nothing + * + * NOTES + * If the path contains a single quote only, an empty string will result. + * Otherwise quotes are only removed if they appear at the start and end + * of the path. + */ +VOID WINAPI PathUnquoteSpacesA(LPSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath == '"') + { + DWORD dwLen = strlen(lpszPath) - 1; + + if (lpszPath[dwLen] == '"') + { + lpszPath[dwLen] = '\0'; + for (; *lpszPath; lpszPath++) + *lpszPath = lpszPath[1]; + } + } +} + +/************************************************************************* + * PathUnquoteSpacesW [SHLWAPI.@] + * + * See PathUnquoteSpacesA. + */ +VOID WINAPI PathUnquoteSpacesW(LPWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath == '"') + { + DWORD dwLen = strlenW(lpszPath) - 1; + + if (lpszPath[dwLen] == '"') + { + lpszPath[dwLen] = '\0'; + for (; *lpszPath; lpszPath++) + *lpszPath = lpszPath[1]; + } + } +} + +/************************************************************************* + * PathParseIconLocationA [SHLWAPI.@] + * + * Parse the location of an icon from a path. + * + * PARAMS + * lpszPath [I/O] The path to parse the icon location from. + * + * RETURNS + * Success: The number of the icon + * Failure: 0 if the path does not contain an icon location or is NULL + * + * NOTES + * The path has surrounding quotes and spaces removed regardless + * of whether the call succeeds or not. + */ +int WINAPI PathParseIconLocationA(LPSTR lpszPath) +{ + int iRet = 0; + LPSTR lpszComma; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath) + { + if ((lpszComma = strchr(lpszPath, ','))) + { + *lpszComma++ = '\0'; + iRet = StrToIntA(lpszComma); + } + PathUnquoteSpacesA(lpszPath); + PathRemoveBlanksA(lpszPath); + } + return iRet; +} + +/************************************************************************* + * PathParseIconLocationW [SHLWAPI.@] + * + * See PathParseIconLocationA. + */ +int WINAPI PathParseIconLocationW(LPWSTR lpszPath) +{ + int iRet = 0; + LPWSTR lpszComma; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath) + { + if ((lpszComma = StrChrW(lpszPath, ','))) + { + *lpszComma++ = '\0'; + iRet = StrToIntW(lpszComma); + } + PathUnquoteSpacesW(lpszPath); + PathRemoveBlanksW(lpszPath); + } + return iRet; +} + +/************************************************************************* + * @ [SHLWAPI.4] + * + * Unicode version of PathFileExistsDefExtA. + */ +BOOL WINAPI PathFileExistsDefExtW(LPWSTR lpszPath,DWORD dwWhich) +{ + static const WCHAR pszExts[][5] = { { '.', 'p', 'i', 'f', 0}, + { '.', 'c', 'o', 'm', 0}, + { '.', 'e', 'x', 'e', 0}, + { '.', 'b', 'a', 't', 0}, + { '.', 'l', 'n', 'k', 0}, + { '.', 'c', 'm', 'd', 0}, + { 0, 0, 0, 0, 0} }; + + TRACE("(%s,%d)\n", debugstr_w(lpszPath), dwWhich); + + if (!lpszPath || PathIsUNCServerW(lpszPath) || PathIsUNCServerShareW(lpszPath)) + return FALSE; + + if (dwWhich) + { + LPCWSTR szExt = PathFindExtensionW(lpszPath); + if (!*szExt || dwWhich & 0x40) + { + size_t iChoose = 0; + int iLen = lstrlenW(lpszPath); + if (iLen > (MAX_PATH - 5)) + return FALSE; + while ( (dwWhich & 0x1) && pszExts[iChoose][0] ) + { + lstrcpyW(lpszPath + iLen, pszExts[iChoose]); + if (PathFileExistsW(lpszPath)) + return TRUE; + iChoose++; + dwWhich >>= 1; + } + *(lpszPath + iLen) = (WCHAR)'\0'; + return FALSE; + } + } + return PathFileExistsW(lpszPath); +} + +/************************************************************************* + * @ [SHLWAPI.3] + * + * Determine if a file exists locally and is of an executable type. + * + * PARAMS + * lpszPath [I/O] File to search for + * dwWhich [I] Type of executable to search for + * + * RETURNS + * TRUE If the file was found. lpszPath contains the file name. + * FALSE Otherwise. + * + * NOTES + * lpszPath is modified in place and must be at least MAX_PATH in length. + * If the function returns FALSE, the path is modified to its original state. + * If the given path contains an extension or dwWhich is 0, executable + * extensions are not checked. + * + * Ordinals 3-6 are a classic case of MS exposing limited functionality to + * users (here through PathFindOnPathA()) and keeping advanced functionality for + * their own developers exclusive use. Monopoly, anyone? + */ +BOOL WINAPI PathFileExistsDefExtA(LPSTR lpszPath,DWORD dwWhich) +{ + BOOL bRet = FALSE; + + TRACE("(%s,%d)\n", debugstr_a(lpszPath), dwWhich); + + if (lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathFileExistsDefExtW(szPath, dwWhich); + if (bRet) + WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * SHLWAPI_PathFindInOtherDirs + * + * Internal helper for SHLWAPI_PathFindOnPathExA/W. + */ +static BOOL SHLWAPI_PathFindInOtherDirs(LPWSTR lpszFile, DWORD dwWhich) +{ + static const WCHAR szSystem[] = { 'S','y','s','t','e','m','\0'}; + static const WCHAR szPath[] = { 'P','A','T','H','\0'}; + DWORD dwLenPATH; + LPCWSTR lpszCurr; + WCHAR *lpszPATH; + WCHAR buff[MAX_PATH]; + + TRACE("(%s,%08x)\n", debugstr_w(lpszFile), dwWhich); + + /* Try system directories */ + GetSystemDirectoryW(buff, MAX_PATH); + if (!PathAppendW(buff, lpszFile)) + return FALSE; + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + GetWindowsDirectoryW(buff, MAX_PATH); + if (!PathAppendW(buff, szSystem ) || !PathAppendW(buff, lpszFile)) + return FALSE; + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + GetWindowsDirectoryW(buff, MAX_PATH); + if (!PathAppendW(buff, lpszFile)) + return FALSE; + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + /* Try dirs listed in %PATH% */ + dwLenPATH = GetEnvironmentVariableW(szPath, buff, MAX_PATH); + + if (!dwLenPATH || !(lpszPATH = HeapAlloc(GetProcessHeap(), 0, (dwLenPATH + 1) * sizeof (WCHAR)))) + return FALSE; + + GetEnvironmentVariableW(szPath, lpszPATH, dwLenPATH + 1); + lpszCurr = lpszPATH; + while (lpszCurr) + { + LPCWSTR lpszEnd = lpszCurr; + LPWSTR pBuff = buff; + + while (*lpszEnd == ' ') + lpszEnd++; + while (*lpszEnd && *lpszEnd != ';') + *pBuff++ = *lpszEnd++; + *pBuff = '\0'; + + if (*lpszEnd) + lpszCurr = lpszEnd + 1; + else + lpszCurr = NULL; /* Last Path, terminate after this */ + + if (!PathAppendW(buff, lpszFile)) + { + HeapFree(GetProcessHeap(), 0, lpszPATH); + return FALSE; + } + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + HeapFree(GetProcessHeap(), 0, lpszPATH); + return TRUE; + } + } + HeapFree(GetProcessHeap(), 0, lpszPATH); + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.5] + * + * Search a range of paths for a specific type of executable. + * + * PARAMS + * lpszFile [I/O] File to search for + * lppszOtherDirs [I] Other directories to look in + * dwWhich [I] Type of executable to search for + * + * RETURNS + * Success: TRUE. The path to the executable is stored in lpszFile. + * Failure: FALSE. The path to the executable is unchanged. + */ +BOOL WINAPI PathFindOnPathExA(LPSTR lpszFile,LPCSTR *lppszOtherDirs,DWORD dwWhich) +{ + WCHAR szFile[MAX_PATH]; + WCHAR buff[MAX_PATH]; + + TRACE("(%s,%p,%08x)\n", debugstr_a(lpszFile), lppszOtherDirs, dwWhich); + + if (!lpszFile || !PathIsFileSpecA(lpszFile)) + return FALSE; + + MultiByteToWideChar(CP_ACP,0,lpszFile,-1,szFile,MAX_PATH); + + /* Search provided directories first */ + if (lppszOtherDirs && *lppszOtherDirs) + { + WCHAR szOther[MAX_PATH]; + LPCSTR *lpszOtherPath = lppszOtherDirs; + + while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) + { + MultiByteToWideChar(CP_ACP,0,*lpszOtherPath,-1,szOther,MAX_PATH); + PathCombineW(buff, szOther, szFile); + if (PathFileExistsDefExtW(buff, dwWhich)) + { + WideCharToMultiByte(CP_ACP,0,buff,-1,lpszFile,MAX_PATH,0,0); + return TRUE; + } + lpszOtherPath++; + } + } + /* Not found, try system and path dirs */ + if (SHLWAPI_PathFindInOtherDirs(szFile, dwWhich)) + { + WideCharToMultiByte(CP_ACP,0,szFile,-1,lpszFile,MAX_PATH,0,0); + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * @ [SHLWAPI.6] + * + * Unicode version of PathFindOnPathExA. + */ +BOOL WINAPI PathFindOnPathExW(LPWSTR lpszFile,LPCWSTR *lppszOtherDirs,DWORD dwWhich) +{ + WCHAR buff[MAX_PATH]; + + TRACE("(%s,%p,%08x)\n", debugstr_w(lpszFile), lppszOtherDirs, dwWhich); + + if (!lpszFile || !PathIsFileSpecW(lpszFile)) + return FALSE; + + /* Search provided directories first */ + if (lppszOtherDirs && *lppszOtherDirs) + { + LPCWSTR *lpszOtherPath = lppszOtherDirs; + while (lpszOtherPath && *lpszOtherPath && (*lpszOtherPath)[0]) + { + PathCombineW(buff, *lpszOtherPath, lpszFile); + if (PathFileExistsDefExtW(buff, dwWhich)) + { + strcpyW(lpszFile, buff); + return TRUE; + } + lpszOtherPath++; + } + } + /* Not found, try system and path dirs */ + return SHLWAPI_PathFindInOtherDirs(lpszFile, dwWhich); +} + +/************************************************************************* + * PathFindOnPathA [SHLWAPI.@] + * + * Search a range of paths for an executable. + * + * PARAMS + * lpszFile [I/O] File to search for + * lppszOtherDirs [I] Other directories to look in + * + * RETURNS + * Success: TRUE. The path to the executable is stored in lpszFile. + * Failure: FALSE. The path to the executable is unchanged. + */ +BOOL WINAPI PathFindOnPathA(LPSTR lpszFile, LPCSTR *lppszOtherDirs) +{ + TRACE("(%s,%p)\n", debugstr_a(lpszFile), lppszOtherDirs); + return PathFindOnPathExA(lpszFile, lppszOtherDirs, 0); + } + +/************************************************************************* + * PathFindOnPathW [SHLWAPI.@] + * + * See PathFindOnPathA. + */ +BOOL WINAPI PathFindOnPathW(LPWSTR lpszFile, LPCWSTR *lppszOtherDirs) +{ + TRACE("(%s,%p)\n", debugstr_w(lpszFile), lppszOtherDirs); + return PathFindOnPathExW(lpszFile,lppszOtherDirs, 0); +} + +/************************************************************************* + * PathCompactPathExA [SHLWAPI.@] + * + * Compact a path into a given number of characters. + * + * PARAMS + * lpszDest [O] Destination for compacted path + * lpszPath [I] Source path + * cchMax [I] Maximum size of compacted path + * dwFlags [I] Reserved + * + * RETURNS + * Success: TRUE. The compacted path is written to lpszDest. + * Failure: FALSE. lpszPath is undefined. + * + * NOTES + * If cchMax is given as 0, lpszDest will still be NUL terminated. + * + * The Win32 version of this function contains a bug: When cchMax == 7, + * 8 bytes will be written to lpszDest. This bug is fixed in the Wine + * implementation. + * + * Some relative paths will be different when cchMax == 5 or 6. This occurs + * because Win32 will insert a "\" in lpszDest, even if one is + * not present in the original path. + */ +BOOL WINAPI PathCompactPathExA(LPSTR lpszDest, LPCSTR lpszPath, + UINT cchMax, DWORD dwFlags) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_a(lpszPath), cchMax, dwFlags); + + if (lpszPath && lpszDest) + { + WCHAR szPath[MAX_PATH]; + WCHAR szDest[MAX_PATH]; + + MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + szDest[0] = '\0'; + bRet = PathCompactPathExW(szDest, szPath, cchMax, dwFlags); + WideCharToMultiByte(CP_ACP,0,szDest,-1,lpszDest,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * PathCompactPathExW [SHLWAPI.@] + * + * See PathCompactPathExA. + */ +BOOL WINAPI PathCompactPathExW(LPWSTR lpszDest, LPCWSTR lpszPath, + UINT cchMax, DWORD dwFlags) +{ + static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; + LPCWSTR lpszFile; + DWORD dwLen, dwFileLen = 0; + + TRACE("(%p,%s,%d,0x%08x)\n", lpszDest, debugstr_w(lpszPath), cchMax, dwFlags); + + if (!lpszPath) + return FALSE; + + if (!lpszDest) + { + WARN("Invalid lpszDest would crash under Win32!\n"); + return FALSE; + } + + *lpszDest = '\0'; + + if (cchMax < 2) + return TRUE; + + dwLen = strlenW(lpszPath) + 1; + + if (dwLen < cchMax) + { + /* Don't need to compact */ + memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); + return TRUE; + } + + /* Path must be compacted to fit into lpszDest */ + lpszFile = PathFindFileNameW(lpszPath); + dwFileLen = lpszPath + dwLen - lpszFile; + + if (dwFileLen == dwLen) + { + /* No root in psth */ + if (cchMax <= 4) + { + while (--cchMax > 0) /* No room left for anything but ellipses */ + *lpszDest++ = '.'; + *lpszDest = '\0'; + return TRUE; + } + /* Compact the file name with ellipses at the end */ + cchMax -= 4; + memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); + strcpyW(lpszDest + cchMax, szEllipses); + return TRUE; + } + /* We have a root in the path */ + lpszFile--; /* Start compacted filename with the path separator */ + dwFileLen++; + + if (dwFileLen + 3 > cchMax) + { + /* Compact the file name */ + if (cchMax <= 4) + { + while (--cchMax > 0) /* No room left for anything but ellipses */ + *lpszDest++ = '.'; + *lpszDest = '\0'; + return TRUE; + } + strcpyW(lpszDest, szEllipses); + lpszDest += 3; + cchMax -= 4; + *lpszDest++ = *lpszFile++; + if (cchMax <= 4) + { + while (--cchMax > 0) /* No room left for anything but ellipses */ + *lpszDest++ = '.'; + *lpszDest = '\0'; + return TRUE; + } + cchMax -= 4; + memcpy(lpszDest, lpszFile, cchMax * sizeof(WCHAR)); + strcpyW(lpszDest + cchMax, szEllipses); + return TRUE; + } + + /* Only the root needs to be Compacted */ + dwLen = cchMax - dwFileLen - 3; + memcpy(lpszDest, lpszPath, dwLen * sizeof(WCHAR)); + strcpyW(lpszDest + dwLen, szEllipses); + strcpyW(lpszDest + dwLen + 3, lpszFile); + return TRUE; +} + +/************************************************************************* + * PathIsRelativeA [SHLWAPI.@] + * + * Determine if a path is a relative path. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE: The path is relative, or is invalid. + * FALSE: The path is not relative. + */ +BOOL WINAPI PathIsRelativeA (LPCSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath || !*lpszPath || IsDBCSLeadByte(*lpszPath)) + return TRUE; + if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsRelativeW [SHLWAPI.@] + * + * See PathIsRelativeA. + */ +BOOL WINAPI PathIsRelativeW (LPCWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath) + return TRUE; + if (*lpszPath == '\\' || (*lpszPath && lpszPath[1] == ':')) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsRootA [SHLWAPI.@] + * + * Determine if a path is a root path. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath is valid and a root path, + * FALSE Otherwise + */ +BOOL WINAPI PathIsRootA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath) + { + if (*lpszPath == '\\') + { + if (!lpszPath[1]) + return TRUE; /* \ */ + else if (lpszPath[1]=='\\') + { + BOOL bSeenSlash = FALSE; + lpszPath += 2; + + /* Check for UNC root path */ + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath = CharNextA(lpszPath); + } + return TRUE; + } + } + else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') + return TRUE; /* X:\ */ + } + return FALSE; +} + +/************************************************************************* + * PathIsRootW [SHLWAPI.@] + * + * See PathIsRootA. + */ +BOOL WINAPI PathIsRootW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath) + { + if (*lpszPath == '\\') + { + if (!lpszPath[1]) + return TRUE; /* \ */ + else if (lpszPath[1]=='\\') + { + BOOL bSeenSlash = FALSE; + lpszPath += 2; + + /* Check for UNC root path */ + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath++; + } + return TRUE; + } + } + else if (lpszPath[1] == ':' && lpszPath[2] == '\\' && lpszPath[3] == '\0') + return TRUE; /* X:\ */ + } + return FALSE; +} + +/************************************************************************* + * PathIsDirectoryA [SHLWAPI.@] + * + * Determine if a path is a valid directory + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * FILE_ATTRIBUTE_DIRECTORY if lpszPath exists and can be read (See Notes) + * FALSE if lpszPath is invalid or not a directory. + * + * NOTES + * Although this function is prototyped as returning a BOOL, it returns + * FILE_ATTRIBUTE_DIRECTORY for success. This means that code such as: + * + *| if (PathIsDirectoryA("c:\\windows\\") == TRUE) + *| ... + * + * will always fail. + */ +BOOL WINAPI PathIsDirectoryA(LPCSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath || PathIsUNCServerA(lpszPath)) + return FALSE; + + if (PathIsUNCServerShareA(lpszPath)) + { + FIXME("UNC Server Share not yet supported - FAILING\n"); + return FALSE; + } + + if ((dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES) + return FALSE; + return dwAttr & FILE_ATTRIBUTE_DIRECTORY; +} + +/************************************************************************* + * PathIsDirectoryW [SHLWAPI.@] + * + * See PathIsDirectoryA. + */ +BOOL WINAPI PathIsDirectoryW(LPCWSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || PathIsUNCServerW(lpszPath)) + return FALSE; + + if (PathIsUNCServerShareW(lpszPath)) + { + FIXME("UNC Server Share not yet supported - FAILING\n"); + return FALSE; + } + + if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) + return FALSE; + return dwAttr & FILE_ATTRIBUTE_DIRECTORY; +} + +/************************************************************************* + * PathFileExistsA [SHLWAPI.@] + * + * Determine if a file exists. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If the file exists and is readable + * FALSE Otherwise + */ +BOOL WINAPI PathFileExistsA(LPCSTR lpszPath) +{ + UINT iPrevErrMode; + DWORD dwAttr; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + + /* Prevent a dialog box if path is on a disk that has been ejected. */ + iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); + dwAttr = GetFileAttributesA(lpszPath); + SetErrorMode(iPrevErrMode); + return dwAttr != INVALID_FILE_ATTRIBUTES; +} + +/************************************************************************* + * PathFileExistsW [SHLWAPI.@] + * + * See PathFileExistsA. + */ +BOOL WINAPI PathFileExistsW(LPCWSTR lpszPath) +{ + UINT iPrevErrMode; + DWORD dwAttr; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + + iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); + dwAttr = GetFileAttributesW(lpszPath); + SetErrorMode(iPrevErrMode); + return dwAttr != INVALID_FILE_ATTRIBUTES; +} + +/************************************************************************* + * PathFileExistsAndAttributesA [SHLWAPI.445] + * + * Determine if a file exists. + * + * PARAMS + * lpszPath [I] Path to check + * dwAttr [O] attributes of file + * + * RETURNS + * TRUE If the file exists and is readable + * FALSE Otherwise + */ +BOOL WINAPI PathFileExistsAndAttributesA(LPCSTR lpszPath, DWORD *dwAttr) +{ + UINT iPrevErrMode; + DWORD dwVal = 0; + + TRACE("(%s %p)\n", debugstr_a(lpszPath), dwAttr); + + if (dwAttr) + *dwAttr = INVALID_FILE_ATTRIBUTES; + + if (!lpszPath) + return FALSE; + + iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); + dwVal = GetFileAttributesA(lpszPath); + SetErrorMode(iPrevErrMode); + if (dwAttr) + *dwAttr = dwVal; + return (dwVal != INVALID_FILE_ATTRIBUTES); +} + +/************************************************************************* + * PathFileExistsAndAttributesW [SHLWAPI.446] + * + * See PathFileExistsA. + */ +BOOL WINAPI PathFileExistsAndAttributesW(LPCWSTR lpszPath, DWORD *dwAttr) +{ + UINT iPrevErrMode; + DWORD dwVal; + + TRACE("(%s %p)\n", debugstr_w(lpszPath), dwAttr); + + if (!lpszPath) + return FALSE; + + iPrevErrMode = SetErrorMode(SEM_FAILCRITICALERRORS); + dwVal = GetFileAttributesW(lpszPath); + SetErrorMode(iPrevErrMode); + if (dwAttr) + *dwAttr = dwVal; + return (dwVal != INVALID_FILE_ATTRIBUTES); +} + +/************************************************************************* + * PathMatchSingleMaskA [internal] + */ +static BOOL PathMatchSingleMaskA(LPCSTR name, LPCSTR mask) +{ + while (*name && *mask && *mask!=';') + { + if (*mask == '*') + { + do + { + if (PathMatchSingleMaskA(name,mask+1)) + return TRUE; /* try substrings */ + } while (*name++); + return FALSE; + } + + if (toupper(*mask) != toupper(*name) && *mask != '?') + return FALSE; + + name = CharNextA(name); + mask = CharNextA(mask); + } + + if (!*name) + { + while (*mask == '*') + mask++; + if (!*mask || *mask == ';') + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathMatchSingleMaskW [internal] + */ +static BOOL PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask) +{ + while (*name && *mask && *mask != ';') + { + if (*mask == '*') + { + do + { + if (PathMatchSingleMaskW(name,mask+1)) + return TRUE; /* try substrings */ + } while (*name++); + return FALSE; + } + + if (toupperW(*mask) != toupperW(*name) && *mask != '?') + return FALSE; + + name++; + mask++; + } + if (!*name) + { + while (*mask == '*') + mask++; + if (!*mask || *mask == ';') + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathMatchSpecA [SHLWAPI.@] + * + * Determine if a path matches one or more search masks. + * + * PARAMS + * lpszPath [I] Path to check + * lpszMask [I] Search mask(s) + * + * RETURNS + * TRUE If lpszPath is valid and is matched + * FALSE Otherwise + * + * NOTES + * Multiple search masks may be given if they are separated by ";". The + * pattern "*.*" is treated specially in that it matches all paths (for + * backwards compatibility with DOS). + */ +BOOL WINAPI PathMatchSpecA(LPCSTR lpszPath, LPCSTR lpszMask) +{ + TRACE("(%s,%s)\n", lpszPath, lpszMask); + + if (!lstrcmpA(lpszMask, "*.*")) + return TRUE; /* Matches every path */ + + while (*lpszMask) + { + while (*lpszMask == ' ') + lpszMask++; /* Eat leading spaces */ + + if (PathMatchSingleMaskA(lpszPath, lpszMask)) + return TRUE; /* Matches the current mask */ + + while (*lpszMask && *lpszMask != ';') + lpszMask = CharNextA(lpszMask); /* masks separated by ';' */ + + if (*lpszMask == ';') + lpszMask++; + } + return FALSE; +} + +/************************************************************************* + * PathMatchSpecW [SHLWAPI.@] + * + * See PathMatchSpecA. + */ +BOOL WINAPI PathMatchSpecW(LPCWSTR lpszPath, LPCWSTR lpszMask) +{ + static const WCHAR szStarDotStar[] = { '*', '.', '*', '\0' }; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszMask)); + + if (!lstrcmpW(lpszMask, szStarDotStar)) + return TRUE; /* Matches every path */ + + while (*lpszMask) + { + while (*lpszMask == ' ') + lpszMask++; /* Eat leading spaces */ + + if (PathMatchSingleMaskW(lpszPath, lpszMask)) + return TRUE; /* Matches the current path */ + + while (*lpszMask && *lpszMask != ';') + lpszMask++; /* masks separated by ';' */ + + if (*lpszMask == ';') + lpszMask++; + } + return FALSE; +} + +/************************************************************************* + * PathIsSameRootA [SHLWAPI.@] + * + * Determine if two paths share the same root. + * + * PARAMS + * lpszPath1 [I] Source path + * lpszPath2 [I] Path to compare with + * + * RETURNS + * TRUE If both paths are valid and share the same root. + * FALSE If either path is invalid or the paths do not share the same root. + */ +BOOL WINAPI PathIsSameRootA(LPCSTR lpszPath1, LPCSTR lpszPath2) +{ + LPCSTR lpszStart; + int dwLen; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath1), debugstr_a(lpszPath2)); + + if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootA(lpszPath1))) + return FALSE; + + dwLen = PathCommonPrefixA(lpszPath1, lpszPath2, NULL) + 1; + if (lpszStart - lpszPath1 > dwLen) + return FALSE; /* Paths not common up to length of the root */ + return TRUE; +} + +/************************************************************************* + * PathIsSameRootW [SHLWAPI.@] + * + * See PathIsSameRootA. + */ +BOOL WINAPI PathIsSameRootW(LPCWSTR lpszPath1, LPCWSTR lpszPath2) +{ + LPCWSTR lpszStart; + int dwLen; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath1), debugstr_w(lpszPath2)); + + if (!lpszPath1 || !lpszPath2 || !(lpszStart = PathSkipRootW(lpszPath1))) + return FALSE; + + dwLen = PathCommonPrefixW(lpszPath1, lpszPath2, NULL) + 1; + if (lpszStart - lpszPath1 > dwLen) + return FALSE; /* Paths not common up to length of the root */ + return TRUE; +} + +/************************************************************************* + * PathIsContentTypeA [SHLWAPI.@] + * + * Determine if a file is of a given registered content type. + * + * PARAMS + * lpszPath [I] File to check + * lpszContentType [I] Content type to check for + * + * RETURNS + * TRUE If lpszPath is a given registered content type, + * FALSE Otherwise. + * + * NOTES + * This function looks up the registered content type for lpszPath. If + * a content type is registered, it is compared (case insensitively) to + * lpszContentType. Only if this matches does the function succeed. + */ +BOOL WINAPI PathIsContentTypeA(LPCSTR lpszPath, LPCSTR lpszContentType) +{ + LPCSTR szExt; + DWORD dwDummy; + char szBuff[MAX_PATH]; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszContentType)); + + if (lpszPath && (szExt = PathFindExtensionA(lpszPath)) && *szExt && + !SHGetValueA(HKEY_CLASSES_ROOT, szExt, "Content Type", + REG_NONE, szBuff, &dwDummy) && + !strcasecmp(lpszContentType, szBuff)) + { + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsContentTypeW [SHLWAPI.@] + * + * See PathIsContentTypeA. + */ +BOOL WINAPI PathIsContentTypeW(LPCWSTR lpszPath, LPCWSTR lpszContentType) +{ + static const WCHAR szContentType[] = { 'C','o','n','t','e','n','t',' ','T','y','p','e','\0' }; + LPCWSTR szExt; + DWORD dwDummy; + WCHAR szBuff[MAX_PATH]; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszContentType)); + + if (lpszPath && (szExt = PathFindExtensionW(lpszPath)) && *szExt && + !SHGetValueW(HKEY_CLASSES_ROOT, szExt, szContentType, + REG_NONE, szBuff, &dwDummy) && + !strcmpiW(lpszContentType, szBuff)) + { + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsFileSpecA [SHLWAPI.@] + * + * Determine if a path is a file specification. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath is a file specification (i.e. Contains no directories). + * FALSE Otherwise. + */ +BOOL WINAPI PathIsFileSpecA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath == ':') + return FALSE; + lpszPath = CharNextA(lpszPath); + } + return TRUE; +} + +/************************************************************************* + * PathIsFileSpecW [SHLWAPI.@] + * + * See PathIsFileSpecA. + */ +BOOL WINAPI PathIsFileSpecW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == '\\' || *lpszPath == ':') + return FALSE; + lpszPath++; + } + return TRUE; +} + +/************************************************************************* + * PathIsPrefixA [SHLWAPI.@] + * + * Determine if a path is a prefix of another. + * + * PARAMS + * lpszPrefix [I] Prefix + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath has lpszPrefix as its prefix, + * FALSE If either path is NULL or lpszPrefix is not a prefix + */ +BOOL WINAPI PathIsPrefixA (LPCSTR lpszPrefix, LPCSTR lpszPath) +{ + TRACE("(%s,%s)\n", debugstr_a(lpszPrefix), debugstr_a(lpszPath)); + + if (lpszPrefix && lpszPath && + PathCommonPrefixA(lpszPath, lpszPrefix, NULL) == (int)strlen(lpszPrefix)) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsPrefixW [SHLWAPI.@] + * + * See PathIsPrefixA. + */ +BOOL WINAPI PathIsPrefixW(LPCWSTR lpszPrefix, LPCWSTR lpszPath) +{ + TRACE("(%s,%s)\n", debugstr_w(lpszPrefix), debugstr_w(lpszPath)); + + if (lpszPrefix && lpszPath && + PathCommonPrefixW(lpszPath, lpszPrefix, NULL) == (int)strlenW(lpszPrefix)) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsSystemFolderA [SHLWAPI.@] + * + * Determine if a path or file attributes are a system folder. + * + * PARAMS + * lpszPath [I] Path to check. + * dwAttrib [I] Attributes to check, if lpszPath is NULL. + * + * RETURNS + * TRUE If lpszPath or dwAttrib are a system folder. + * FALSE If GetFileAttributesA() fails or neither parameter is a system folder. + */ +BOOL WINAPI PathIsSystemFolderA(LPCSTR lpszPath, DWORD dwAttrib) +{ + TRACE("(%s,0x%08x)\n", debugstr_a(lpszPath), dwAttrib); + + if (lpszPath && *lpszPath) + dwAttrib = GetFileAttributesA(lpszPath); + + if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || + !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsSystemFolderW [SHLWAPI.@] + * + * See PathIsSystemFolderA. + */ +BOOL WINAPI PathIsSystemFolderW(LPCWSTR lpszPath, DWORD dwAttrib) +{ + TRACE("(%s,0x%08x)\n", debugstr_w(lpszPath), dwAttrib); + + if (lpszPath && *lpszPath) + dwAttrib = GetFileAttributesW(lpszPath); + + if (dwAttrib == INVALID_FILE_ATTRIBUTES || !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY) || + !(dwAttrib & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY))) + return FALSE; + return TRUE; +} + +/************************************************************************* + * PathIsUNCA [SHLWAPI.@] + * + * Determine if a path is in UNC format. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE: The path is UNC. + * FALSE: The path is not UNC or is NULL. + */ +BOOL WINAPI PathIsUNCA(LPCSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?')) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsUNCW [SHLWAPI.@] + * + * See PathIsUNCA. + */ +BOOL WINAPI PathIsUNCW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath && (lpszPath[0]=='\\') && (lpszPath[1]=='\\') && (lpszPath[2]!='?')) + return TRUE; + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerA [SHLWAPI.@] + * + * Determine if a path is a UNC server name ("\\SHARENAME"). + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * TRUE If lpszPath is a valid UNC server name. + * FALSE Otherwise. + * + * NOTES + * This routine is bug compatible with Win32: Server names with a + * trailing backslash (e.g. "\\FOO\"), return FALSE incorrectly. + * Fixing this bug may break other shlwapi functions! + */ +BOOL WINAPI PathIsUNCServerA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + while (*lpszPath) + { + if (*lpszPath == '\\') + return FALSE; + lpszPath = CharNextA(lpszPath); + } + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerW [SHLWAPI.@] + * + * See PathIsUNCServerA. + */ +BOOL WINAPI PathIsUNCServerW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && lpszPath[0] == '\\' && lpszPath[1] == '\\') + { + return !strchrW( lpszPath + 2, '\\' ); + } + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerShareA [SHLWAPI.@] + * + * Determine if a path is a UNC server share ("\\SHARENAME\SHARE"). + * + * PARAMS + * lpszPath [I] Path to check. + * + * RETURNS + * TRUE If lpszPath is a valid UNC server share. + * FALSE Otherwise. + * + * NOTES + * This routine is bug compatible with Win32: Server shares with a + * trailing backslash (e.g. "\\FOO\BAR\"), return FALSE incorrectly. + * Fixing this bug may break other shlwapi functions! + */ +BOOL WINAPI PathIsUNCServerShareA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + BOOL bSeenSlash = FALSE; + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath = CharNextA(lpszPath); + } + return bSeenSlash; + } + return FALSE; +} + +/************************************************************************* + * PathIsUNCServerShareW [SHLWAPI.@] + * + * See PathIsUNCServerShareA. + */ +BOOL WINAPI PathIsUNCServerShareW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (lpszPath && *lpszPath++ == '\\' && *lpszPath++ == '\\') + { + BOOL bSeenSlash = FALSE; + while (*lpszPath) + { + if (*lpszPath == '\\') + { + if (bSeenSlash) + return FALSE; + bSeenSlash = TRUE; + } + lpszPath++; + } + return bSeenSlash; + } + return FALSE; +} + +/************************************************************************* + * PathCanonicalizeA [SHLWAPI.@] + * + * Convert a path to its canonical form. + * + * PARAMS + * lpszBuf [O] Output path + * lpszPath [I] Path to canonicalize + * + * RETURNS + * Success: TRUE. lpszBuf contains the output path, + * Failure: FALSE, If input path is invalid. lpszBuf is undefined + */ +BOOL WINAPI PathCanonicalizeA(LPSTR lpszBuf, LPCSTR lpszPath) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s)\n", lpszBuf, debugstr_a(lpszPath)); + + if (lpszBuf) + *lpszBuf = '\0'; + + if (!lpszBuf || !lpszPath) + SetLastError(ERROR_INVALID_PARAMETER); + else + { + WCHAR szPath[MAX_PATH]; + WCHAR szBuff[MAX_PATH]; + int ret = MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + + if (!ret) { + WARN("Failed to convert string to widechar (too long?), LE %d.\n", GetLastError()); + return FALSE; + } + bRet = PathCanonicalizeW(szBuff, szPath); + WideCharToMultiByte(CP_ACP,0,szBuff,-1,lpszBuf,MAX_PATH,0,0); + } + return bRet; +} + + +/************************************************************************* + * PathCanonicalizeW [SHLWAPI.@] + * + * See PathCanonicalizeA. + */ +BOOL WINAPI PathCanonicalizeW(LPWSTR lpszBuf, LPCWSTR lpszPath) +{ + LPWSTR lpszDst = lpszBuf; + LPCWSTR lpszSrc = lpszPath; + + TRACE("(%p,%s)\n", lpszBuf, debugstr_w(lpszPath)); + + if (lpszBuf) + *lpszDst = '\0'; + + if (!lpszBuf || !lpszPath) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!*lpszPath) + { + *lpszBuf++ = '\\'; + *lpszBuf = '\0'; + return TRUE; + } + + /* Copy path root */ + if (*lpszSrc == '\\') + { + *lpszDst++ = *lpszSrc++; + } + else if (*lpszSrc && lpszSrc[1] == ':') + { + /* X:\ */ + *lpszDst++ = *lpszSrc++; + *lpszDst++ = *lpszSrc++; + if (*lpszSrc == '\\') + *lpszDst++ = *lpszSrc++; + } + + /* Canonicalize the rest of the path */ + while (*lpszSrc) + { + if (*lpszSrc == '.') + { + if (lpszSrc[1] == '\\' && (lpszSrc == lpszPath || lpszSrc[-1] == '\\' || lpszSrc[-1] == ':')) + { + lpszSrc += 2; /* Skip .\ */ + } + else if (lpszSrc[1] == '.' && (lpszDst == lpszBuf || lpszDst[-1] == '\\')) + { + /* \.. backs up a directory, over the root if it has no \ following X:. + * .. is ignored if it would remove a UNC server name or initial \\ + */ + if (lpszDst != lpszBuf) + { + *lpszDst = '\0'; /* Allow PathIsUNCServerShareA test on lpszBuf */ + if (lpszDst > lpszBuf+1 && lpszDst[-1] == '\\' && + (lpszDst[-2] != '\\' || lpszDst > lpszBuf+2)) + { + if (lpszDst[-2] == ':' && (lpszDst > lpszBuf+3 || lpszDst[-3] == ':')) + { + lpszDst -= 2; + while (lpszDst > lpszBuf && *lpszDst != '\\') + lpszDst--; + if (*lpszDst == '\\') + lpszDst++; /* Reset to last '\' */ + else + lpszDst = lpszBuf; /* Start path again from new root */ + } + else if (lpszDst[-2] != ':' && !PathIsUNCServerShareW(lpszBuf)) + lpszDst -= 2; + } + while (lpszDst > lpszBuf && *lpszDst != '\\') + lpszDst--; + if (lpszDst == lpszBuf) + { + *lpszDst++ = '\\'; + lpszSrc++; + } + } + lpszSrc += 2; /* Skip .. in src path */ + } + else + *lpszDst++ = *lpszSrc++; + } + else + *lpszDst++ = *lpszSrc++; + } + /* Append \ to naked drive specs */ + if (lpszDst - lpszBuf == 2 && lpszDst[-1] == ':') + *lpszDst++ = '\\'; + *lpszDst++ = '\0'; + return TRUE; +} + +/************************************************************************* + * PathFindNextComponentA [SHLWAPI.@] + * + * Find the next component in a path. + * + * PARAMS + * lpszPath [I] Path to find next component in + * + * RETURNS + * Success: A pointer to the next component, or the end of the string. + * Failure: NULL, If lpszPath is invalid + * + * NOTES + * A 'component' is either a backslash character (\) or UNC marker (\\). + * Because of this, relative paths (e.g "c:foo") are regarded as having + * only one component. + */ +LPSTR WINAPI PathFindNextComponentA(LPCSTR lpszPath) +{ + LPSTR lpszSlash; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if(!lpszPath || !*lpszPath) + return NULL; + + if ((lpszSlash = StrChrA(lpszPath, '\\'))) + { + if (lpszSlash[1] == '\\') + lpszSlash++; + return lpszSlash + 1; + } + return (LPSTR)lpszPath + strlen(lpszPath); +} + +/************************************************************************* + * PathFindNextComponentW [SHLWAPI.@] + * + * See PathFindNextComponentA. + */ +LPWSTR WINAPI PathFindNextComponentW(LPCWSTR lpszPath) +{ + LPWSTR lpszSlash; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if(!lpszPath || !*lpszPath) + return NULL; + + if ((lpszSlash = StrChrW(lpszPath, '\\'))) + { + if (lpszSlash[1] == '\\') + lpszSlash++; + return lpszSlash + 1; + } + return (LPWSTR)lpszPath + strlenW(lpszPath); +} + +/************************************************************************* + * PathAddExtensionA [SHLWAPI.@] + * + * Add a file extension to a path + * + * PARAMS + * lpszPath [I/O] Path to add extension to + * lpszExtension [I] Extension to add to lpszPath + * + * RETURNS + * TRUE If the path was modified, + * FALSE If lpszPath or lpszExtension are invalid, lpszPath has an + * extension already, or the new path length is too big. + * + * FIXME + * What version of shlwapi.dll adds "exe" if lpszExtension is NULL? Win2k + * does not do this, so the behaviour was removed. + */ +BOOL WINAPI PathAddExtensionA(LPSTR lpszPath, LPCSTR lpszExtension) +{ + size_t dwLen; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExtension)); + + if (!lpszPath || !lpszExtension || *(PathFindExtensionA(lpszPath))) + return FALSE; + + dwLen = strlen(lpszPath); + + if (dwLen + strlen(lpszExtension) >= MAX_PATH) + return FALSE; + + strcpy(lpszPath + dwLen, lpszExtension); + return TRUE; +} + +/************************************************************************* + * PathAddExtensionW [SHLWAPI.@] + * + * See PathAddExtensionA. + */ +BOOL WINAPI PathAddExtensionW(LPWSTR lpszPath, LPCWSTR lpszExtension) +{ + size_t dwLen; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExtension)); + + if (!lpszPath || !lpszExtension || *(PathFindExtensionW(lpszPath))) + return FALSE; + + dwLen = strlenW(lpszPath); + + if (dwLen + strlenW(lpszExtension) >= MAX_PATH) + return FALSE; + + strcpyW(lpszPath + dwLen, lpszExtension); + return TRUE; +} + +/************************************************************************* + * PathMakePrettyA [SHLWAPI.@] + * + * Convert an uppercase DOS filename into lowercase. + * + * PARAMS + * lpszPath [I/O] Path to convert. + * + * RETURNS + * TRUE If the path was an uppercase DOS path and was converted, + * FALSE Otherwise. + */ +BOOL WINAPI PathMakePrettyA(LPSTR lpszPath) +{ + LPSTR pszIter = lpszPath; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!pszIter) + return FALSE; + + if (*pszIter) + { + do + { + if (islower(*pszIter) || IsDBCSLeadByte(*pszIter)) + return FALSE; /* Not DOS path */ + pszIter++; + } while (*pszIter); + pszIter = lpszPath + 1; + while (*pszIter) + { + *pszIter = tolower(*pszIter); + pszIter++; + } + } + return TRUE; +} + +/************************************************************************* + * PathMakePrettyW [SHLWAPI.@] + * + * See PathMakePrettyA. + */ +BOOL WINAPI PathMakePrettyW(LPWSTR lpszPath) +{ + LPWSTR pszIter = lpszPath; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!pszIter) + return FALSE; + + if (*pszIter) + { + do + { + if (islowerW(*pszIter)) + return FALSE; /* Not DOS path */ + pszIter++; + } while (*pszIter); + pszIter = lpszPath + 1; + while (*pszIter) + { + *pszIter = tolowerW(*pszIter); + pszIter++; + } + } + return TRUE; +} + +/************************************************************************* + * PathCommonPrefixA [SHLWAPI.@] + * + * Determine the length of the common prefix between two paths. + * + * PARAMS + * lpszFile1 [I] First path for comparison + * lpszFile2 [I] Second path for comparison + * achPath [O] Destination for common prefix string + * + * RETURNS + * The length of the common prefix. This is 0 if there is no common + * prefix between the paths or if any parameters are invalid. If the prefix + * is non-zero and achPath is not NULL, achPath is filled with the common + * part of the prefix and NUL terminated. + * + * NOTES + * A common prefix of 2 is always returned as 3. It is thus possible for + * the length returned to be invalid (i.e. Longer than one or both of the + * strings given as parameters). This Win32 behaviour has been implemented + * here, and cannot be changed (fixed?) without breaking other SHLWAPI calls. + * To work around this when using this function, always check that the byte + * at [common_prefix_len-1] is not a NUL. If it is, deduct 1 from the prefix. + */ +int WINAPI PathCommonPrefixA(LPCSTR lpszFile1, LPCSTR lpszFile2, LPSTR achPath) +{ + size_t iLen = 0; + LPCSTR lpszIter1 = lpszFile1; + LPCSTR lpszIter2 = lpszFile2; + + TRACE("(%s,%s,%p)\n", debugstr_a(lpszFile1), debugstr_a(lpszFile2), achPath); + + if (achPath) + *achPath = '\0'; + + if (!lpszFile1 || !lpszFile2) + return 0; + + /* Handle roots first */ + if (PathIsUNCA(lpszFile1)) + { + if (!PathIsUNCA(lpszFile2)) + return 0; + lpszIter1 += 2; + lpszIter2 += 2; + } + else if (PathIsUNCA(lpszFile2)) + return 0; /* Know already lpszFile1 is not UNC */ + + do + { + /* Update len */ + if ((!*lpszIter1 || *lpszIter1 == '\\') && + (!*lpszIter2 || *lpszIter2 == '\\')) + iLen = lpszIter1 - lpszFile1; /* Common to this point */ + + if (!*lpszIter1 || (tolower(*lpszIter1) != tolower(*lpszIter2))) + break; /* Strings differ at this point */ + + lpszIter1++; + lpszIter2++; + } while (1); + + if (iLen == 2) + iLen++; /* Feature/Bug compatible with Win32 */ + + if (iLen && achPath) + { + memcpy(achPath,lpszFile1,iLen); + achPath[iLen] = '\0'; + } + return iLen; +} + +/************************************************************************* + * PathCommonPrefixW [SHLWAPI.@] + * + * See PathCommonPrefixA. + */ +int WINAPI PathCommonPrefixW(LPCWSTR lpszFile1, LPCWSTR lpszFile2, LPWSTR achPath) +{ + size_t iLen = 0; + LPCWSTR lpszIter1 = lpszFile1; + LPCWSTR lpszIter2 = lpszFile2; + + TRACE("(%s,%s,%p)\n", debugstr_w(lpszFile1), debugstr_w(lpszFile2), achPath); + + if (achPath) + *achPath = '\0'; + + if (!lpszFile1 || !lpszFile2) + return 0; + + /* Handle roots first */ + if (PathIsUNCW(lpszFile1)) + { + if (!PathIsUNCW(lpszFile2)) + return 0; + lpszIter1 += 2; + lpszIter2 += 2; + } + else if (PathIsUNCW(lpszFile2)) + return 0; /* Know already lpszFile1 is not UNC */ + + do + { + /* Update len */ + if ((!*lpszIter1 || *lpszIter1 == '\\') && + (!*lpszIter2 || *lpszIter2 == '\\')) + iLen = lpszIter1 - lpszFile1; /* Common to this point */ + + if (!*lpszIter1 || (tolowerW(*lpszIter1) != tolowerW(*lpszIter2))) + break; /* Strings differ at this point */ + + lpszIter1++; + lpszIter2++; + } while (1); + + if (iLen == 2) + iLen++; /* Feature/Bug compatible with Win32 */ + + if (iLen && achPath) + { + memcpy(achPath,lpszFile1,iLen * sizeof(WCHAR)); + achPath[iLen] = '\0'; + } + return iLen; +} + +/************************************************************************* + * PathCompactPathA [SHLWAPI.@] + * + * Make a path fit into a given width when printed to a DC. + * + * PARAMS + * hDc [I] Destination DC + * lpszPath [I/O] Path to be printed to hDc + * dx [I] Desired width + * + * RETURNS + * TRUE If the path was modified/went well. + * FALSE Otherwise. + */ +BOOL WINAPI PathCompactPathA(HDC hDC, LPSTR lpszPath, UINT dx) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s,%d)\n", hDC, debugstr_a(lpszPath), dx); + + if (lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathCompactPathW(hDC, szPath, dx); + WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * PathCompactPathW [SHLWAPI.@] + * + * See PathCompactPathA. + */ +BOOL WINAPI PathCompactPathW(HDC hDC, LPWSTR lpszPath, UINT dx) +{ + static const WCHAR szEllipses[] = { '.', '.', '.', '\0' }; + BOOL bRet = TRUE; + HDC hdc = 0; + WCHAR buff[MAX_PATH]; + SIZE size; + DWORD dwLen; + + TRACE("(%p,%s,%d)\n", hDC, debugstr_w(lpszPath), dx); + + if (!lpszPath) + return FALSE; + + if (!hDC) + hdc = hDC = GetDC(0); + + /* Get the length of the whole path */ + dwLen = strlenW(lpszPath); + GetTextExtentPointW(hDC, lpszPath, dwLen, &size); + + if ((UINT)size.cx > dx) + { + /* Path too big, must reduce it */ + LPWSTR sFile; + DWORD dwEllipsesLen = 0, dwPathLen = 0; + + sFile = PathFindFileNameW(lpszPath); + if (sFile != lpszPath) sFile--; + + /* Get the size of ellipses */ + GetTextExtentPointW(hDC, szEllipses, 3, &size); + dwEllipsesLen = size.cx; + /* Get the size of the file name */ + GetTextExtentPointW(hDC, sFile, strlenW(sFile), &size); + dwPathLen = size.cx; + + if (sFile != lpszPath) + { + LPWSTR sPath = sFile; + BOOL bEllipses = FALSE; + + /* The path includes a file name. Include as much of the path prior to + * the file name as possible, allowing for the ellipses, e.g: + * c:\some very long path\filename ==> c:\some v...\filename + */ + lstrcpynW(buff, sFile, MAX_PATH); + + do + { + DWORD dwTotalLen = bEllipses? dwPathLen + dwEllipsesLen : dwPathLen; + + GetTextExtentPointW(hDC, lpszPath, sPath - lpszPath, &size); + dwTotalLen += size.cx; + if (dwTotalLen <= dx) + break; + sPath--; + if (!bEllipses) + { + bEllipses = TRUE; + sPath -= 2; + } + } while (sPath > lpszPath); + + if (sPath > lpszPath) + { + if (bEllipses) + { + strcpyW(sPath, szEllipses); + strcpyW(sPath+3, buff); + } + bRet = TRUE; + goto end; + } + strcpyW(lpszPath, szEllipses); + strcpyW(lpszPath+3, buff); + bRet = FALSE; + goto end; + } + + /* Trim the path by adding ellipses to the end, e.g: + * A very long file name.txt ==> A very... + */ + dwLen = strlenW(lpszPath); + + if (dwLen > MAX_PATH - 3) + dwLen = MAX_PATH - 3; + lstrcpynW(buff, sFile, dwLen); + + do { + dwLen--; + GetTextExtentPointW(hDC, buff, dwLen, &size); + } while (dwLen && size.cx + dwEllipsesLen > dx); + + if (!dwLen) + { + DWORD dwWritten = 0; + + dwEllipsesLen /= 3; /* Size of a single '.' */ + + /* Write as much of the Ellipses string as possible */ + while (dwWritten + dwEllipsesLen < dx && dwLen < 3) + { + *lpszPath++ = '.'; + dwWritten += dwEllipsesLen; + dwLen++; + } + *lpszPath = '\0'; + bRet = FALSE; + } + else + { + strcpyW(buff + dwLen, szEllipses); + strcpyW(lpszPath, buff); + } + } + +end: + if (hdc) + ReleaseDC(0, hdc); + + return bRet; +} + +/************************************************************************* + * PathGetCharTypeA [SHLWAPI.@] + * + * Categorise a character from a file path. + * + * PARAMS + * ch [I] Character to get the type of + * + * RETURNS + * A set of GCT_ bit flags (from "shlwapi.h") indicating the character type. + */ +UINT WINAPI PathGetCharTypeA(UCHAR ch) +{ + return PathGetCharTypeW(ch); +} + +/************************************************************************* + * PathGetCharTypeW [SHLWAPI.@] + * + * See PathGetCharTypeA. + */ +UINT WINAPI PathGetCharTypeW(WCHAR ch) +{ + UINT flags = 0; + + TRACE("(%d)\n", ch); + + if (!ch || ch < ' ' || ch == '<' || ch == '>' || + ch == '"' || ch == '|' || ch == '/') + flags = GCT_INVALID; /* Invalid */ + else if (ch == '*' || ch=='?') + flags = GCT_WILD; /* Wildchars */ + else if ((ch == '\\') || (ch == ':')) + return GCT_SEPARATOR; /* Path separators */ + else + { + if (ch < 126) + { + if (((ch & 0x1) && ch != ';') || !ch || isalnum(ch) || ch == '$' || ch == '&' || ch == '(' || + ch == '.' || ch == '@' || ch == '^' || + ch == '\'' || ch == 130 || ch == '`') + flags |= GCT_SHORTCHAR; /* All these are valid for DOS */ + } + else + flags |= GCT_SHORTCHAR; /* Bug compatible with win32 */ + flags |= GCT_LFNCHAR; /* Valid for long file names */ + } + return flags; +} + +/************************************************************************* + * SHLWAPI_UseSystemForSystemFolders + * + * Internal helper for PathMakeSystemFolderW. + */ +static BOOL SHLWAPI_UseSystemForSystemFolders(void) +{ + static BOOL bCheckedReg = FALSE; + static BOOL bUseSystemForSystemFolders = FALSE; + + if (!bCheckedReg) + { + bCheckedReg = TRUE; + + /* Key tells Win what file attributes to use on system folders */ + if (SHGetValueA(HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer", + "UseSystemForSystemFolders", 0, 0, 0)) + bUseSystemForSystemFolders = TRUE; + } + return bUseSystemForSystemFolders; +} + +/************************************************************************* + * PathMakeSystemFolderA [SHLWAPI.@] + * + * Set system folder attribute for a path. + * + * PARAMS + * lpszPath [I] The path to turn into a system folder + * + * RETURNS + * TRUE If the path was changed to/already was a system folder + * FALSE If the path is invalid or SetFileAttributesA() fails + */ +BOOL WINAPI PathMakeSystemFolderA(LPCSTR lpszPath) +{ + BOOL bRet = FALSE; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (lpszPath && *lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathMakeSystemFolderW(szPath); + } + return bRet; +} + +/************************************************************************* + * PathMakeSystemFolderW [SHLWAPI.@] + * + * See PathMakeSystemFolderA. + */ +BOOL WINAPI PathMakeSystemFolderW(LPCWSTR lpszPath) +{ + DWORD dwDefaultAttr = FILE_ATTRIBUTE_READONLY, dwAttr; + WCHAR buff[MAX_PATH]; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath) + return FALSE; + + /* If the directory is already a system directory, don't do anything */ + GetSystemDirectoryW(buff, MAX_PATH); + if (!strcmpW(buff, lpszPath)) + return TRUE; + + GetWindowsDirectoryW(buff, MAX_PATH); + if (!strcmpW(buff, lpszPath)) + return TRUE; + + /* "UseSystemForSystemFolders" Tells Win what attributes to use */ + if (SHLWAPI_UseSystemForSystemFolders()) + dwDefaultAttr = FILE_ATTRIBUTE_SYSTEM; + + if ((dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES) + return FALSE; + + /* Change file attributes to system attributes */ + dwAttr &= ~(FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY); + return SetFileAttributesW(lpszPath, dwAttr | dwDefaultAttr); +} + +/************************************************************************* + * PathRenameExtensionA [SHLWAPI.@] + * + * Swap the file extension in a path with another extension. + * + * PARAMS + * lpszPath [I/O] Path to swap the extension in + * lpszExt [I] The new extension + * + * RETURNS + * TRUE if lpszPath was modified, + * FALSE if lpszPath or lpszExt is NULL, or the new path is too long + */ +BOOL WINAPI PathRenameExtensionA(LPSTR lpszPath, LPCSTR lpszExt) +{ + LPSTR lpszExtension; + + TRACE("(%s,%s)\n", debugstr_a(lpszPath), debugstr_a(lpszExt)); + + lpszExtension = PathFindExtensionA(lpszPath); + + if (!lpszExtension || (lpszExtension - lpszPath + strlen(lpszExt) >= MAX_PATH)) + return FALSE; + + strcpy(lpszExtension, lpszExt); + return TRUE; +} + +/************************************************************************* + * PathRenameExtensionW [SHLWAPI.@] + * + * See PathRenameExtensionA. + */ +BOOL WINAPI PathRenameExtensionW(LPWSTR lpszPath, LPCWSTR lpszExt) +{ + LPWSTR lpszExtension; + + TRACE("(%s,%s)\n", debugstr_w(lpszPath), debugstr_w(lpszExt)); + + lpszExtension = PathFindExtensionW(lpszPath); + + if (!lpszExtension || (lpszExtension - lpszPath + strlenW(lpszExt) >= MAX_PATH)) + return FALSE; + + strcpyW(lpszExtension, lpszExt); + return TRUE; +} + +/************************************************************************* + * PathSearchAndQualifyA [SHLWAPI.@] + * + * Determine if a given path is correct and fully qualified. + * + * PARAMS + * lpszPath [I] Path to check + * lpszBuf [O] Output for correct path + * cchBuf [I] Size of lpszBuf + * + * RETURNS + * Unknown. + */ +BOOL WINAPI PathSearchAndQualifyA(LPCSTR lpszPath, LPSTR lpszBuf, UINT cchBuf) +{ + TRACE("(%s,%p,0x%08x)\n", debugstr_a(lpszPath), lpszBuf, cchBuf); + + if(SearchPathA(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL)) + return TRUE; + return !!GetFullPathNameA(lpszPath, cchBuf, lpszBuf, NULL); +} + +/************************************************************************* + * PathSearchAndQualifyW [SHLWAPI.@] + * + * See PathSearchAndQualifyA. + */ +BOOL WINAPI PathSearchAndQualifyW(LPCWSTR lpszPath, LPWSTR lpszBuf, UINT cchBuf) +{ + TRACE("(%s,%p,0x%08x)\n", debugstr_w(lpszPath), lpszBuf, cchBuf); + + if(SearchPathW(NULL, lpszPath, NULL, cchBuf, lpszBuf, NULL)) + return TRUE; + return !!GetFullPathNameW(lpszPath, cchBuf, lpszBuf, NULL); +} + +/************************************************************************* + * PathSkipRootA [SHLWAPI.@] + * + * Return the portion of a path following the drive letter or mount point. + * + * PARAMS + * lpszPath [I] The path to skip on + * + * RETURNS + * Success: A pointer to the next character after the root. + * Failure: NULL, if lpszPath is invalid, has no root or is a multibyte string. + */ +LPSTR WINAPI PathSkipRootA(LPCSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath || !*lpszPath) + return NULL; + + if (*lpszPath == '\\' && lpszPath[1] == '\\') + { + /* Network share: skip share server and mount point */ + lpszPath += 2; + if ((lpszPath = StrChrA(lpszPath, '\\')) && + (lpszPath = StrChrA(lpszPath + 1, '\\'))) + lpszPath++; + return (LPSTR)lpszPath; + } + + if (IsDBCSLeadByte(*lpszPath)) + return NULL; + + /* Check x:\ */ + if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') + return (LPSTR)lpszPath + 3; + return NULL; +} + +/************************************************************************* + * PathSkipRootW [SHLWAPI.@] + * + * See PathSkipRootA. + */ +LPWSTR WINAPI PathSkipRootW(LPCWSTR lpszPath) +{ + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath) + return NULL; + + if (*lpszPath == '\\' && lpszPath[1] == '\\') + { + /* Network share: skip share server and mount point */ + lpszPath += 2; + if ((lpszPath = StrChrW(lpszPath, '\\')) && + (lpszPath = StrChrW(lpszPath + 1, '\\'))) + lpszPath++; + return (LPWSTR)lpszPath; + } + + /* Check x:\ */ + if (lpszPath[0] && lpszPath[1] == ':' && lpszPath[2] == '\\') + return (LPWSTR)lpszPath + 3; + return NULL; +} + +/************************************************************************* + * PathCreateFromUrlA [SHLWAPI.@] + * + * See PathCreateFromUrlW + */ +HRESULT WINAPI PathCreateFromUrlA(LPCSTR pszUrl, LPSTR pszPath, + LPDWORD pcchPath, DWORD dwReserved) +{ + WCHAR bufW[MAX_PATH]; + WCHAR *pathW = bufW; + UNICODE_STRING urlW; + HRESULT ret; + DWORD lenW = sizeof(bufW)/sizeof(WCHAR), lenA; + + if (!pszUrl || !pszPath || !pcchPath || !*pcchPath) + return E_INVALIDARG; + + if(!RtlCreateUnicodeStringFromAsciiz(&urlW, pszUrl)) + return E_INVALIDARG; + if((ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved)) == E_POINTER) { + pathW = HeapAlloc(GetProcessHeap(), 0, lenW * sizeof(WCHAR)); + ret = PathCreateFromUrlW(urlW.Buffer, pathW, &lenW, dwReserved); + } + if(ret == S_OK) { + RtlUnicodeToMultiByteSize(&lenA, pathW, lenW * sizeof(WCHAR)); + if(*pcchPath > lenA) { + RtlUnicodeToMultiByteN(pszPath, *pcchPath - 1, &lenA, pathW, lenW * sizeof(WCHAR)); + pszPath[lenA] = 0; + *pcchPath = lenA; + } else { + *pcchPath = lenA + 1; + ret = E_POINTER; + } + } + if(pathW != bufW) HeapFree(GetProcessHeap(), 0, pathW); + RtlFreeUnicodeString(&urlW); + return ret; +} + +/************************************************************************* + * PathCreateFromUrlW [SHLWAPI.@] + * + * Create a path from a URL + * + * PARAMS + * lpszUrl [I] URL to convert into a path + * lpszPath [O] Output buffer for the resulting Path + * pcchPath [I] Length of lpszPath + * dwFlags [I] Flags controlling the conversion + * + * RETURNS + * Success: S_OK. lpszPath contains the URL in path format, + * Failure: An HRESULT error code such as E_INVALIDARG. + */ +HRESULT WINAPI PathCreateFromUrlW(LPCWSTR pszUrl, LPWSTR pszPath, + LPDWORD pcchPath, DWORD dwReserved) +{ + static const WCHAR file_colon[] = { 'f','i','l','e',':',0 }; + static const WCHAR localhost[] = { 'l','o','c','a','l','h','o','s','t',0 }; + DWORD nslashes, unescape, len; + const WCHAR *src; + WCHAR *tpath, *dst; + HRESULT ret; + + TRACE("(%s,%p,%p,0x%08x)\n", debugstr_w(pszUrl), pszPath, pcchPath, dwReserved); + + if (!pszUrl || !pszPath || !pcchPath || !*pcchPath) + return E_INVALIDARG; + + if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, pszUrl, 5, + file_colon, 5) != CSTR_EQUAL) + return E_INVALIDARG; + pszUrl += 5; + ret = S_OK; + + src = pszUrl; + nslashes = 0; + while (*src == '/' || *src == '\\') { + nslashes++; + src++; + } + + /* We need a temporary buffer so we can compute what size to ask for. + * We know that the final string won't be longer than the current pszUrl + * plus at most two backslashes. All the other transformations make it + * shorter. + */ + len = 2 + lstrlenW(pszUrl) + 1; + if (*pcchPath < len) + tpath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + else + tpath = pszPath; + + len = 0; + dst = tpath; + unescape = 1; + switch (nslashes) + { + case 0: + /* 'file:' + escaped DOS path */ + break; + case 1: + /* 'file:/' + escaped DOS path */ + /* fall through */ + case 3: + /* 'file:///' (implied localhost) + escaped DOS path */ + if (!isalphaW(*src) || (src[1] != ':' && src[1] != '|')) + src -= 1; + break; + case 2: + if (CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, src, 9, + localhost, 9) == CSTR_EQUAL && + (src[9] == '/' || src[9] == '\\')) + { + /* 'file://localhost/' + escaped DOS path */ + src += 10; + } + else if (isalphaW(*src) && (src[1] == ':' || src[1] == '|')) + { + /* 'file://' + unescaped DOS path */ + unescape = 0; + } + else + { + /* 'file://hostname:port/path' (where path is escaped) + * or 'file:' + escaped UNC path (\\server\share\path) + * The second form is clearly specific to Windows and it might + * even be doing a network lookup to try to figure it out. + */ + while (*src && *src != '/' && *src != '\\') + src++; + len = src - pszUrl; + StrCpyNW(dst, pszUrl, len + 1); + dst += len; + if (isalphaW(src[1]) && (src[2] == ':' || src[2] == '|')) + { + /* 'Forget' to add a trailing '/', just like Windows */ + src++; + } + } + break; + case 4: + /* 'file://' + unescaped UNC path (\\server\share\path) */ + unescape = 0; + if (isalphaW(*src) && (src[1] == ':' || src[1] == '|')) + break; + /* fall through */ + default: + /* 'file:/...' + escaped UNC path (\\server\share\path) */ + src -= 2; + } + + /* Copy the remainder of the path */ + len += lstrlenW(src); + StrCpyW(dst, src); + + /* First do the Windows-specific path conversions */ + for (dst = tpath; *dst; dst++) + if (*dst == '/') *dst = '\\'; + if (isalphaW(*tpath) && tpath[1] == '|') + tpath[1] = ':'; /* c| -> c: */ + + /* And only then unescape the path (i.e. escaped slashes are left as is) */ + if (unescape) + { + ret = UrlUnescapeW(tpath, NULL, &len, URL_UNESCAPE_INPLACE); + if (ret == S_OK) + { + /* When working in-place UrlUnescapeW() does not set len */ + len = lstrlenW(tpath); + } + } + + if (*pcchPath < len + 1) + { + ret = E_POINTER; + *pcchPath = len + 1; + } + else + { + *pcchPath = len; + if (tpath != pszPath) + StrCpyW(pszPath, tpath); + } + if (tpath != pszPath) + HeapFree(GetProcessHeap(), 0, tpath); + + TRACE("Returning (%u) %s\n", *pcchPath, debugstr_w(pszPath)); + return ret; +} + +/************************************************************************* + * PathCreateFromUrlAlloc [SHLWAPI.@] + */ +HRESULT WINAPI PathCreateFromUrlAlloc(LPCWSTR pszUrl, LPWSTR *pszPath, + DWORD dwReserved) +{ + WCHAR pathW[MAX_PATH]; + DWORD size; + HRESULT hr; + + size = MAX_PATH; + hr = PathCreateFromUrlW(pszUrl, pathW, &size, dwReserved); + if (SUCCEEDED(hr)) + { + /* Yes, this is supposed to crash if pszPath is NULL */ + *pszPath = StrDupW(pathW); + } + return hr; +} + +/************************************************************************* + * PathRelativePathToA [SHLWAPI.@] + * + * Create a relative path from one path to another. + * + * PARAMS + * lpszPath [O] Destination for relative path + * lpszFrom [I] Source path + * dwAttrFrom [I] File attribute of source path + * lpszTo [I] Destination path + * dwAttrTo [I] File attributes of destination path + * + * RETURNS + * TRUE If a relative path can be formed. lpszPath contains the new path + * FALSE If the paths are not relative or any parameters are invalid + * + * NOTES + * lpszTo should be at least MAX_PATH in length. + * + * Calling this function with relative paths for lpszFrom or lpszTo may + * give erroneous results. + * + * The Win32 version of this function contains a bug where the lpszTo string + * may be referenced 1 byte beyond the end of the string. As a result random + * garbage may be written to the output path, depending on what lies beyond + * the last byte of the string. This bug occurs because of the behaviour of + * PathCommonPrefix() (see notes for that function), and no workaround seems + * possible with Win32. + * + * This bug has been fixed here, so for example the relative path from "\\" + * to "\\" is correctly determined as "." in this implementation. + */ +BOOL WINAPI PathRelativePathToA(LPSTR lpszPath, LPCSTR lpszFrom, DWORD dwAttrFrom, + LPCSTR lpszTo, DWORD dwAttrTo) +{ + BOOL bRet = FALSE; + + TRACE("(%p,%s,0x%08x,%s,0x%08x)\n", lpszPath, debugstr_a(lpszFrom), + dwAttrFrom, debugstr_a(lpszTo), dwAttrTo); + + if(lpszPath && lpszFrom && lpszTo) + { + WCHAR szPath[MAX_PATH]; + WCHAR szFrom[MAX_PATH]; + WCHAR szTo[MAX_PATH]; + MultiByteToWideChar(CP_ACP,0,lpszFrom,-1,szFrom,MAX_PATH); + MultiByteToWideChar(CP_ACP,0,lpszTo,-1,szTo,MAX_PATH); + bRet = PathRelativePathToW(szPath,szFrom,dwAttrFrom,szTo,dwAttrTo); + WideCharToMultiByte(CP_ACP,0,szPath,-1,lpszPath,MAX_PATH,0,0); + } + return bRet; +} + +/************************************************************************* + * PathRelativePathToW [SHLWAPI.@] + * + * See PathRelativePathToA. + */ +BOOL WINAPI PathRelativePathToW(LPWSTR lpszPath, LPCWSTR lpszFrom, DWORD dwAttrFrom, + LPCWSTR lpszTo, DWORD dwAttrTo) +{ + static const WCHAR szPrevDirSlash[] = { '.', '.', '\\', '\0' }; + static const WCHAR szPrevDir[] = { '.', '.', '\0' }; + WCHAR szFrom[MAX_PATH]; + WCHAR szTo[MAX_PATH]; + DWORD dwLen; + + TRACE("(%p,%s,0x%08x,%s,0x%08x)\n", lpszPath, debugstr_w(lpszFrom), + dwAttrFrom, debugstr_w(lpszTo), dwAttrTo); + + if(!lpszPath || !lpszFrom || !lpszTo) + return FALSE; + + *lpszPath = '\0'; + lstrcpynW(szFrom, lpszFrom, MAX_PATH); + lstrcpynW(szTo, lpszTo, MAX_PATH); + + if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) + PathRemoveFileSpecW(szFrom); - if(!(dwAttrFrom & FILE_ATTRIBUTE_DIRECTORY)) ++ if(!(dwAttrTo & FILE_ATTRIBUTE_DIRECTORY)) + PathRemoveFileSpecW(szTo); + + /* Paths can only be relative if they have a common root */ + if(!(dwLen = PathCommonPrefixW(szFrom, szTo, 0))) + return FALSE; + + /* Strip off lpszFrom components to the root, by adding "..\" */ + lpszFrom = szFrom + dwLen; + if (!*lpszFrom) + { + lpszPath[0] = '.'; + lpszPath[1] = '\0'; + } + if (*lpszFrom == '\\') + lpszFrom++; + + while (*lpszFrom) + { + lpszFrom = PathFindNextComponentW(lpszFrom); + strcatW(lpszPath, *lpszFrom ? szPrevDirSlash : szPrevDir); + } + + /* From the root add the components of lpszTo */ + lpszTo += dwLen; + /* We check lpszTo[-1] to avoid skipping end of string. See the notes for + * this function. + */ + if (*lpszTo && lpszTo[-1]) + { + if (*lpszTo != '\\') + lpszTo--; + dwLen = strlenW(lpszPath); + if (dwLen + strlenW(lpszTo) >= MAX_PATH) + { + *lpszPath = '\0'; + return FALSE; + } + strcpyW(lpszPath + dwLen, lpszTo); + } + return TRUE; +} + +/************************************************************************* + * PathUnmakeSystemFolderA [SHLWAPI.@] + * + * Remove the system folder attributes from a path. + * + * PARAMS + * lpszPath [I] The path to remove attributes from + * + * RETURNS + * Success: TRUE. + * Failure: FALSE, if lpszPath is NULL, empty, not a directory, or calling + * SetFileAttributesA() fails. + */ +BOOL WINAPI PathUnmakeSystemFolderA(LPCSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_a(lpszPath)); + + if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesA(lpszPath)) == INVALID_FILE_ATTRIBUTES || + !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + return FALSE; + + dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + return SetFileAttributesA(lpszPath, dwAttr); +} + +/************************************************************************* + * PathUnmakeSystemFolderW [SHLWAPI.@] + * + * See PathUnmakeSystemFolderA. + */ +BOOL WINAPI PathUnmakeSystemFolderW(LPCWSTR lpszPath) +{ + DWORD dwAttr; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath || !*lpszPath || (dwAttr = GetFileAttributesW(lpszPath)) == INVALID_FILE_ATTRIBUTES || + !(dwAttr & FILE_ATTRIBUTE_DIRECTORY)) + return FALSE; + + dwAttr &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); + return SetFileAttributesW(lpszPath, dwAttr); +} + + +/************************************************************************* + * PathSetDlgItemPathA [SHLWAPI.@] + * + * Set the text of a dialog item to a path, shrinking the path to fit + * if it is too big for the item. + * + * PARAMS + * hDlg [I] Dialog handle + * id [I] ID of item in the dialog + * lpszPath [I] Path to set as the items text + * + * RETURNS + * Nothing. + * + * NOTES + * If lpszPath is NULL, a blank string ("") is set (i.e. The previous + * window text is erased). + */ +VOID WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR lpszPath) +{ + WCHAR szPath[MAX_PATH]; + + TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_a(lpszPath)); + + if (lpszPath) + MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + else + szPath[0] = '\0'; + PathSetDlgItemPathW(hDlg, id, szPath); +} + +/************************************************************************* + * PathSetDlgItemPathW [SHLWAPI.@] + * + * See PathSetDlgItemPathA. + */ +VOID WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR lpszPath) +{ + WCHAR path[MAX_PATH + 1]; + HWND hwItem; + RECT rect; + HDC hdc; + HGDIOBJ hPrevObj; + + TRACE("(%p,%8x,%s)\n",hDlg, id, debugstr_w(lpszPath)); + + if (!(hwItem = GetDlgItem(hDlg, id))) + return; + + if (lpszPath) + lstrcpynW(path, lpszPath, sizeof(path) / sizeof(WCHAR)); + else + path[0] = '\0'; + + GetClientRect(hwItem, &rect); + hdc = GetDC(hDlg); + hPrevObj = SelectObject(hdc, (HGDIOBJ)SendMessageW(hwItem,WM_GETFONT,0,0)); + + if (hPrevObj) + { + PathCompactPathW(hdc, path, rect.right); + SelectObject(hdc, hPrevObj); + } + + ReleaseDC(hDlg, hdc); + SetWindowTextW(hwItem, path); +} + +/************************************************************************* + * PathIsNetworkPathA [SHLWAPI.@] + * + * Determine if the given path is a network path. + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If lpszPath is a UNC share or mapped network drive, or + * FALSE If lpszPath is a local drive or cannot be determined + */ +BOOL WINAPI PathIsNetworkPathA(LPCSTR lpszPath) +{ + int dwDriveNum; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + if (*lpszPath == '\\' && lpszPath[1] == '\\') + return TRUE; + dwDriveNum = PathGetDriveNumberA(lpszPath); + if (dwDriveNum == -1) + return FALSE; + GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ + return pIsNetDrive(dwDriveNum); +} + +/************************************************************************* + * PathIsNetworkPathW [SHLWAPI.@] + * + * See PathIsNetworkPathA. + */ +BOOL WINAPI PathIsNetworkPathW(LPCWSTR lpszPath) +{ + int dwDriveNum; + + TRACE("(%s)\n", debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + if (*lpszPath == '\\' && lpszPath[1] == '\\') + return TRUE; + dwDriveNum = PathGetDriveNumberW(lpszPath); + if (dwDriveNum == -1) + return FALSE; + GET_FUNC(pIsNetDrive, shell32, (LPCSTR)66, FALSE); /* ord 66 = shell32.IsNetDrive */ + return pIsNetDrive(dwDriveNum); +} + +/************************************************************************* + * PathIsLFNFileSpecA [SHLWAPI.@] + * + * Determine if the given path is a long file name + * + * PARAMS + * lpszPath [I] Path to check + * + * RETURNS + * TRUE If path is a long file name, + * FALSE If path is a valid DOS short file name + */ +BOOL WINAPI PathIsLFNFileSpecA(LPCSTR lpszPath) +{ + DWORD dwNameLen = 0, dwExtLen = 0; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == ' ') + return TRUE; /* DOS names cannot have spaces */ + if (*lpszPath == '.') + { + if (dwExtLen) + return TRUE; /* DOS names have only one dot */ + dwExtLen = 1; + } + else if (dwExtLen) + { + dwExtLen++; + if (dwExtLen > 4) + return TRUE; /* DOS extensions are <= 3 chars*/ + } + else + { + dwNameLen++; + if (dwNameLen > 8) + return TRUE; /* DOS names are <= 8 chars */ + } + lpszPath += IsDBCSLeadByte(*lpszPath) ? 2 : 1; + } + return FALSE; /* Valid DOS path */ +} + +/************************************************************************* + * PathIsLFNFileSpecW [SHLWAPI.@] + * + * See PathIsLFNFileSpecA. + */ +BOOL WINAPI PathIsLFNFileSpecW(LPCWSTR lpszPath) +{ + DWORD dwNameLen = 0, dwExtLen = 0; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath) + return FALSE; + + while (*lpszPath) + { + if (*lpszPath == ' ') + return TRUE; /* DOS names cannot have spaces */ + if (*lpszPath == '.') + { + if (dwExtLen) + return TRUE; /* DOS names have only one dot */ + dwExtLen = 1; + } + else if (dwExtLen) + { + dwExtLen++; + if (dwExtLen > 4) + return TRUE; /* DOS extensions are <= 3 chars*/ + } + else + { + dwNameLen++; + if (dwNameLen > 8) + return TRUE; /* DOS names are <= 8 chars */ + } + lpszPath++; + } + return FALSE; /* Valid DOS path */ +} + +/************************************************************************* + * PathIsDirectoryEmptyA [SHLWAPI.@] + * + * Determine if a given directory is empty. + * + * PARAMS + * lpszPath [I] Directory to check + * + * RETURNS + * TRUE If the directory exists and contains no files, + * FALSE Otherwise + */ +BOOL WINAPI PathIsDirectoryEmptyA(LPCSTR lpszPath) +{ + BOOL bRet = FALSE; + + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath) + { + WCHAR szPath[MAX_PATH]; + MultiByteToWideChar(CP_ACP,0,lpszPath,-1,szPath,MAX_PATH); + bRet = PathIsDirectoryEmptyW(szPath); + } + return bRet; +} + +/************************************************************************* + * PathIsDirectoryEmptyW [SHLWAPI.@] + * + * See PathIsDirectoryEmptyA. + */ +BOOL WINAPI PathIsDirectoryEmptyW(LPCWSTR lpszPath) +{ + static const WCHAR szAllFiles[] = { '*', '.', '*', '\0' }; + WCHAR szSearch[MAX_PATH]; + DWORD dwLen; + HANDLE hfind; + BOOL retVal = FALSE; + WIN32_FIND_DATAW find_data; + + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (!lpszPath || !PathIsDirectoryW(lpszPath)) + return FALSE; + + lstrcpynW(szSearch, lpszPath, MAX_PATH); + PathAddBackslashW(szSearch); + dwLen = strlenW(szSearch); + if (dwLen > MAX_PATH - 4) + return FALSE; + + strcpyW(szSearch + dwLen, szAllFiles); + hfind = FindFirstFileW(szSearch, &find_data); + if (hfind != INVALID_HANDLE_VALUE) + { + if (find_data.cFileName[0] == '.' && find_data.cFileName[1] == '.') + /* The only directory entry should be the parent */ + retVal = !FindNextFileW(hfind, &find_data); + FindClose(hfind); + } + + return retVal; +} + + +/************************************************************************* + * PathFindSuffixArrayA [SHLWAPI.@] + * + * Find a suffix string in an array of suffix strings + * + * PARAMS + * lpszSuffix [I] Suffix string to search for + * lppszArray [I] Array of suffix strings to search + * dwCount [I] Number of elements in lppszArray + * + * RETURNS + * Success: The index of the position of lpszSuffix in lppszArray + * Failure: 0, if any parameters are invalid or lpszSuffix is not found + * + * NOTES + * The search is case sensitive. + * The match is made against the end of the suffix string, so for example: + * lpszSuffix="fooBAR" matches "BAR", but lpszSuffix="fooBARfoo" does not. + */ +LPCSTR WINAPI PathFindSuffixArrayA(LPCSTR lpszSuffix, LPCSTR *lppszArray, int dwCount) +{ + size_t dwLen; + int dwRet = 0; + + TRACE("(%s,%p,%d)\n",debugstr_a(lpszSuffix), lppszArray, dwCount); + + if (lpszSuffix && lppszArray && dwCount > 0) + { + dwLen = strlen(lpszSuffix); + + while (dwRet < dwCount) + { + size_t dwCompareLen = strlen(*lppszArray); + if (dwCompareLen < dwLen) + { + if (!strcmp(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) + return *lppszArray; /* Found */ + } + dwRet++; + lppszArray++; + } + } + return NULL; +} + +/************************************************************************* + * PathFindSuffixArrayW [SHLWAPI.@] + * + * See PathFindSuffixArrayA. + */ +LPCWSTR WINAPI PathFindSuffixArrayW(LPCWSTR lpszSuffix, LPCWSTR *lppszArray, int dwCount) +{ + size_t dwLen; + int dwRet = 0; + + TRACE("(%s,%p,%d)\n",debugstr_w(lpszSuffix), lppszArray, dwCount); + + if (lpszSuffix && lppszArray && dwCount > 0) + { + dwLen = strlenW(lpszSuffix); + + while (dwRet < dwCount) + { + size_t dwCompareLen = strlenW(*lppszArray); + if (dwCompareLen < dwLen) + { + if (!strcmpW(lpszSuffix + dwLen - dwCompareLen, *lppszArray)) + return *lppszArray; /* Found */ + } + dwRet++; + lppszArray++; + } + } + return NULL; +} + +/************************************************************************* + * PathUndecorateA [SHLWAPI.@] + * + * Undecorate a file path + * + * PARAMS + * lpszPath [I/O] Path to remove any decoration from + * + * RETURNS + * Nothing + * + * NOTES + * A decorations form is "path[n].ext" where "n" is an optional decimal number. + */ +VOID WINAPI PathUndecorateA(LPSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_a(lpszPath)); + + if (lpszPath) + { + LPSTR lpszExt = PathFindExtensionA(lpszPath); + if (lpszExt > lpszPath && lpszExt[-1] == ']') + { + LPSTR lpszSkip = lpszExt - 2; + if (*lpszSkip == '[') + lpszSkip++; /* [] (no number) */ + else + while (lpszSkip > lpszPath && isdigit(lpszSkip[-1])) + lpszSkip--; + if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') + { + /* remove the [n] */ + lpszSkip--; + while (*lpszExt) + *lpszSkip++ = *lpszExt++; + *lpszSkip = '\0'; + } + } + } +} + +/************************************************************************* + * PathUndecorateW [SHLWAPI.@] + * + * See PathUndecorateA. + */ +VOID WINAPI PathUndecorateW(LPWSTR lpszPath) +{ + TRACE("(%s)\n",debugstr_w(lpszPath)); + + if (lpszPath) + { + LPWSTR lpszExt = PathFindExtensionW(lpszPath); + if (lpszExt > lpszPath && lpszExt[-1] == ']') + { + LPWSTR lpszSkip = lpszExt - 2; + if (*lpszSkip == '[') + lpszSkip++; /* [] (no number) */ + else + while (lpszSkip > lpszPath && isdigitW(lpszSkip[-1])) + lpszSkip--; + if (lpszSkip > lpszPath && lpszSkip[-1] == '[' && lpszSkip[-2] != '\\') + { + /* remove the [n] */ + lpszSkip--; + while (*lpszExt) + *lpszSkip++ = *lpszExt++; + *lpszSkip = '\0'; + } + } + } +} + +/************************************************************************* + * PathUnExpandEnvStringsA [SHLWAPI.@] + * + * Substitute folder names in a path with their corresponding environment + * strings. + * + * PARAMS + * path [I] Buffer containing the path to unexpand. + * buffer [O] Buffer to receive the unexpanded path. + * buf_len [I] Size of pszBuf in characters. + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ +BOOL WINAPI PathUnExpandEnvStringsA(LPCSTR path, LPSTR buffer, UINT buf_len) +{ + WCHAR bufferW[MAX_PATH], *pathW; + DWORD len; + BOOL ret; + + TRACE("(%s, %p, %d)\n", debugstr_a(path), buffer, buf_len); + + pathW = heap_strdupAtoW(path); + if (!pathW) return FALSE; + + ret = PathUnExpandEnvStringsW(pathW, bufferW, MAX_PATH); + HeapFree(GetProcessHeap(), 0, pathW); + if (!ret) return FALSE; + + len = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL); + if (buf_len < len + 1) return FALSE; + + WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buf_len, NULL, NULL); + return TRUE; +} + +static const WCHAR allusersprofileW[] = {'%','A','L','L','U','S','E','R','S','P','R','O','F','I','L','E','%',0}; +static const WCHAR appdataW[] = {'%','A','P','P','D','A','T','A','%',0}; +static const WCHAR computernameW[] = {'%','C','O','M','P','U','T','E','R','N','A','M','E','%',0}; +static const WCHAR programfilesW[] = {'%','P','r','o','g','r','a','m','F','i','l','e','s','%',0}; +static const WCHAR systemrootW[] = {'%','S','y','s','t','e','m','R','o','o','t','%',0}; +static const WCHAR systemdriveW[] = {'%','S','y','s','t','e','m','D','r','i','v','e','%',0}; +static const WCHAR userprofileW[] = {'%','U','S','E','R','P','R','O','F','I','L','E','%',0}; + +struct envvars_map +{ + const WCHAR *var; + UINT varlen; + WCHAR path[MAX_PATH]; + DWORD len; +}; + +static void init_envvars_map(struct envvars_map *map) +{ + while (map->var) + { + map->len = ExpandEnvironmentStringsW(map->var, map->path, sizeof(map->path)/sizeof(WCHAR)); + /* exclude null from length */ + if (map->len) map->len--; + map++; + } +} + +/************************************************************************* + * PathUnExpandEnvStringsW [SHLWAPI.@] + * + * Unicode version of PathUnExpandEnvStringsA. + */ +BOOL WINAPI PathUnExpandEnvStringsW(LPCWSTR path, LPWSTR buffer, UINT buf_len) +{ + static struct envvars_map null_var = {NULL, 0, {0}, 0}; + struct envvars_map *match = &null_var, *cur; + struct envvars_map envvars[] = { + { allusersprofileW, sizeof(allusersprofileW)/sizeof(WCHAR) }, + { appdataW, sizeof(appdataW)/sizeof(WCHAR) }, + { computernameW, sizeof(computernameW)/sizeof(WCHAR) }, + { programfilesW, sizeof(programfilesW)/sizeof(WCHAR) }, + { systemrootW, sizeof(systemrootW)/sizeof(WCHAR) }, + { systemdriveW, sizeof(systemdriveW)/sizeof(WCHAR) }, + { userprofileW, sizeof(userprofileW)/sizeof(WCHAR) }, + { NULL } + }; + DWORD pathlen; + UINT needed; + + TRACE("(%s, %p, %d)\n", debugstr_w(path), buffer, buf_len); + + pathlen = strlenW(path); + init_envvars_map(envvars); + cur = envvars; + while (cur->var) + { + /* path can't contain expanded value or value wasn't retrieved */ + if (cur->len == 0 || cur->len > pathlen || strncmpiW(cur->path, path, cur->len)) + { + cur++; + continue; + } + + if (cur->len > match->len) + match = cur; + cur++; + } + + /* 'varlen' includes NULL termination char */ + needed = match->varlen + pathlen - match->len; + if (match->len == 0 || needed > buf_len) return FALSE; + + strcpyW(buffer, match->var); + strcatW(buffer, &path[match->len]); + TRACE("ret %s\n", debugstr_w(buffer)); + + return TRUE; +} + +/************************************************************************* + * @ [SHLWAPI.440] + * + * Find localised or default web content in "%WINDOWS%\web\". + * + * PARAMS + * lpszFile [I] File name containing content to look for + * lpszPath [O] Buffer to contain the full path to the file + * dwPathLen [I] Length of lpszPath + * + * RETURNS + * Success: S_OK. lpszPath contains the full path to the content. + * Failure: E_FAIL. The content does not exist or lpszPath is too short. + */ +HRESULT WINAPI SHGetWebFolderFilePathA(LPCSTR lpszFile, LPSTR lpszPath, DWORD dwPathLen) +{ + WCHAR szFile[MAX_PATH], szPath[MAX_PATH]; + HRESULT hRet; + + TRACE("(%s,%p,%d)\n", lpszFile, lpszPath, dwPathLen); + + MultiByteToWideChar(CP_ACP, 0, lpszFile, -1, szFile, MAX_PATH); + szPath[0] = '\0'; + hRet = SHGetWebFolderFilePathW(szFile, szPath, dwPathLen); + WideCharToMultiByte(CP_ACP, 0, szPath, -1, lpszPath, dwPathLen, 0, 0); + return hRet; +} + +/************************************************************************* + * @ [SHLWAPI.441] + * + * Unicode version of SHGetWebFolderFilePathA. + */ +HRESULT WINAPI SHGetWebFolderFilePathW(LPCWSTR lpszFile, LPWSTR lpszPath, DWORD dwPathLen) +{ + static const WCHAR szWeb[] = {'\\','W','e','b','\\','\0'}; + static const WCHAR szWebMui[] = {'m','u','i','\\','%','0','4','x','\\','\0'}; +#define szWebLen (sizeof(szWeb)/sizeof(WCHAR)) +#define szWebMuiLen ((sizeof(szWebMui)+1)/sizeof(WCHAR)) + DWORD dwLen, dwFileLen; + LANGID lidSystem, lidUser; + + TRACE("(%s,%p,%d)\n", debugstr_w(lpszFile), lpszPath, dwPathLen); + + /* Get base directory for web content */ + dwLen = GetSystemWindowsDirectoryW(lpszPath, dwPathLen); + if (dwLen > 0 && lpszPath[dwLen-1] == '\\') + dwLen--; + + dwFileLen = strlenW(lpszFile); + + if (dwLen + dwFileLen + szWebLen >= dwPathLen) + return E_FAIL; /* lpszPath too short */ + + strcpyW(lpszPath+dwLen, szWeb); + dwLen += szWebLen; + dwPathLen = dwPathLen - dwLen; /* Remaining space */ + + lidSystem = GetSystemDefaultUILanguage(); + lidUser = GetUserDefaultUILanguage(); + + if (lidSystem != lidUser) + { + if (dwFileLen + szWebMuiLen < dwPathLen) + { + /* Use localised content in the users UI language if present */ + wsprintfW(lpszPath + dwLen, szWebMui, lidUser); + strcpyW(lpszPath + dwLen + szWebMuiLen, lpszFile); + if (PathFileExistsW(lpszPath)) + return S_OK; + } + } + + /* Fall back to OS default installed content */ + strcpyW(lpszPath + dwLen, lpszFile); + if (PathFileExistsW(lpszPath)) + return S_OK; + return E_FAIL; +} + +#define PATH_CHAR_CLASS_LETTER 0x00000001 +#define PATH_CHAR_CLASS_ASTERIX 0x00000002 +#define PATH_CHAR_CLASS_DOT 0x00000004 +#define PATH_CHAR_CLASS_BACKSLASH 0x00000008 +#define PATH_CHAR_CLASS_COLON 0x00000010 +#define PATH_CHAR_CLASS_SEMICOLON 0x00000020 +#define PATH_CHAR_CLASS_COMMA 0x00000040 +#define PATH_CHAR_CLASS_SPACE 0x00000080 +#define PATH_CHAR_CLASS_OTHER_VALID 0x00000100 +#define PATH_CHAR_CLASS_DOUBLEQUOTE 0x00000200 + +#define PATH_CHAR_CLASS_INVALID 0x00000000 +#define PATH_CHAR_CLASS_ANY 0xffffffff + +static const DWORD SHELL_charclass[] = +{ + /* 0x00 */ PATH_CHAR_CLASS_INVALID, /* 0x01 */ PATH_CHAR_CLASS_INVALID, + /* 0x02 */ PATH_CHAR_CLASS_INVALID, /* 0x03 */ PATH_CHAR_CLASS_INVALID, + /* 0x04 */ PATH_CHAR_CLASS_INVALID, /* 0x05 */ PATH_CHAR_CLASS_INVALID, + /* 0x06 */ PATH_CHAR_CLASS_INVALID, /* 0x07 */ PATH_CHAR_CLASS_INVALID, + /* 0x08 */ PATH_CHAR_CLASS_INVALID, /* 0x09 */ PATH_CHAR_CLASS_INVALID, + /* 0x0a */ PATH_CHAR_CLASS_INVALID, /* 0x0b */ PATH_CHAR_CLASS_INVALID, + /* 0x0c */ PATH_CHAR_CLASS_INVALID, /* 0x0d */ PATH_CHAR_CLASS_INVALID, + /* 0x0e */ PATH_CHAR_CLASS_INVALID, /* 0x0f */ PATH_CHAR_CLASS_INVALID, + /* 0x10 */ PATH_CHAR_CLASS_INVALID, /* 0x11 */ PATH_CHAR_CLASS_INVALID, + /* 0x12 */ PATH_CHAR_CLASS_INVALID, /* 0x13 */ PATH_CHAR_CLASS_INVALID, + /* 0x14 */ PATH_CHAR_CLASS_INVALID, /* 0x15 */ PATH_CHAR_CLASS_INVALID, + /* 0x16 */ PATH_CHAR_CLASS_INVALID, /* 0x17 */ PATH_CHAR_CLASS_INVALID, + /* 0x18 */ PATH_CHAR_CLASS_INVALID, /* 0x19 */ PATH_CHAR_CLASS_INVALID, + /* 0x1a */ PATH_CHAR_CLASS_INVALID, /* 0x1b */ PATH_CHAR_CLASS_INVALID, + /* 0x1c */ PATH_CHAR_CLASS_INVALID, /* 0x1d */ PATH_CHAR_CLASS_INVALID, + /* 0x1e */ PATH_CHAR_CLASS_INVALID, /* 0x1f */ PATH_CHAR_CLASS_INVALID, + /* ' ' */ PATH_CHAR_CLASS_SPACE, /* '!' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '"' */ PATH_CHAR_CLASS_DOUBLEQUOTE, /* '#' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '$' */ PATH_CHAR_CLASS_OTHER_VALID, /* '%' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '&' */ PATH_CHAR_CLASS_OTHER_VALID, /* '\'' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '(' */ PATH_CHAR_CLASS_OTHER_VALID, /* ')' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '*' */ PATH_CHAR_CLASS_ASTERIX, /* '+' */ PATH_CHAR_CLASS_OTHER_VALID, + /* ',' */ PATH_CHAR_CLASS_COMMA, /* '-' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '.' */ PATH_CHAR_CLASS_DOT, /* '/' */ PATH_CHAR_CLASS_INVALID, + /* '0' */ PATH_CHAR_CLASS_OTHER_VALID, /* '1' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '2' */ PATH_CHAR_CLASS_OTHER_VALID, /* '3' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '4' */ PATH_CHAR_CLASS_OTHER_VALID, /* '5' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '6' */ PATH_CHAR_CLASS_OTHER_VALID, /* '7' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '8' */ PATH_CHAR_CLASS_OTHER_VALID, /* '9' */ PATH_CHAR_CLASS_OTHER_VALID, + /* ':' */ PATH_CHAR_CLASS_COLON, /* ';' */ PATH_CHAR_CLASS_SEMICOLON, + /* '<' */ PATH_CHAR_CLASS_INVALID, /* '=' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '>' */ PATH_CHAR_CLASS_INVALID, /* '?' */ PATH_CHAR_CLASS_LETTER, + /* '@' */ PATH_CHAR_CLASS_OTHER_VALID, /* 'A' */ PATH_CHAR_CLASS_ANY, + /* 'B' */ PATH_CHAR_CLASS_ANY, /* 'C' */ PATH_CHAR_CLASS_ANY, + /* 'D' */ PATH_CHAR_CLASS_ANY, /* 'E' */ PATH_CHAR_CLASS_ANY, + /* 'F' */ PATH_CHAR_CLASS_ANY, /* 'G' */ PATH_CHAR_CLASS_ANY, + /* 'H' */ PATH_CHAR_CLASS_ANY, /* 'I' */ PATH_CHAR_CLASS_ANY, + /* 'J' */ PATH_CHAR_CLASS_ANY, /* 'K' */ PATH_CHAR_CLASS_ANY, + /* 'L' */ PATH_CHAR_CLASS_ANY, /* 'M' */ PATH_CHAR_CLASS_ANY, + /* 'N' */ PATH_CHAR_CLASS_ANY, /* 'O' */ PATH_CHAR_CLASS_ANY, + /* 'P' */ PATH_CHAR_CLASS_ANY, /* 'Q' */ PATH_CHAR_CLASS_ANY, + /* 'R' */ PATH_CHAR_CLASS_ANY, /* 'S' */ PATH_CHAR_CLASS_ANY, + /* 'T' */ PATH_CHAR_CLASS_ANY, /* 'U' */ PATH_CHAR_CLASS_ANY, + /* 'V' */ PATH_CHAR_CLASS_ANY, /* 'W' */ PATH_CHAR_CLASS_ANY, + /* 'X' */ PATH_CHAR_CLASS_ANY, /* 'Y' */ PATH_CHAR_CLASS_ANY, + /* 'Z' */ PATH_CHAR_CLASS_ANY, /* '[' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '\\' */ PATH_CHAR_CLASS_BACKSLASH, /* ']' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '^' */ PATH_CHAR_CLASS_OTHER_VALID, /* '_' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '`' */ PATH_CHAR_CLASS_OTHER_VALID, /* 'a' */ PATH_CHAR_CLASS_ANY, + /* 'b' */ PATH_CHAR_CLASS_ANY, /* 'c' */ PATH_CHAR_CLASS_ANY, + /* 'd' */ PATH_CHAR_CLASS_ANY, /* 'e' */ PATH_CHAR_CLASS_ANY, + /* 'f' */ PATH_CHAR_CLASS_ANY, /* 'g' */ PATH_CHAR_CLASS_ANY, + /* 'h' */ PATH_CHAR_CLASS_ANY, /* 'i' */ PATH_CHAR_CLASS_ANY, + /* 'j' */ PATH_CHAR_CLASS_ANY, /* 'k' */ PATH_CHAR_CLASS_ANY, + /* 'l' */ PATH_CHAR_CLASS_ANY, /* 'm' */ PATH_CHAR_CLASS_ANY, + /* 'n' */ PATH_CHAR_CLASS_ANY, /* 'o' */ PATH_CHAR_CLASS_ANY, + /* 'p' */ PATH_CHAR_CLASS_ANY, /* 'q' */ PATH_CHAR_CLASS_ANY, + /* 'r' */ PATH_CHAR_CLASS_ANY, /* 's' */ PATH_CHAR_CLASS_ANY, + /* 't' */ PATH_CHAR_CLASS_ANY, /* 'u' */ PATH_CHAR_CLASS_ANY, + /* 'v' */ PATH_CHAR_CLASS_ANY, /* 'w' */ PATH_CHAR_CLASS_ANY, + /* 'x' */ PATH_CHAR_CLASS_ANY, /* 'y' */ PATH_CHAR_CLASS_ANY, + /* 'z' */ PATH_CHAR_CLASS_ANY, /* '{' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '|' */ PATH_CHAR_CLASS_INVALID, /* '}' */ PATH_CHAR_CLASS_OTHER_VALID, + /* '~' */ PATH_CHAR_CLASS_OTHER_VALID +}; + +/************************************************************************* + * @ [SHLWAPI.455] + * + * Check if an ASCII char is of a certain class + */ +BOOL WINAPI PathIsValidCharA( char c, DWORD class ) +{ + if ((unsigned)c > 0x7e) + return class & PATH_CHAR_CLASS_OTHER_VALID; + + return class & SHELL_charclass[(unsigned)c]; +} + +/************************************************************************* + * @ [SHLWAPI.456] + * + * Check if a Unicode char is of a certain class + */ +BOOL WINAPI PathIsValidCharW( WCHAR c, DWORD class ) +{ + if (c > 0x7e) + return class & PATH_CHAR_CLASS_OTHER_VALID; + + return class & SHELL_charclass[c]; +} diff --cc include/ddk/isvbop.h index 00000000000,088e9e6dcda..088e9e6dcda mode 000000,100644..100644 --- a/include/ddk/isvbop.h +++ b/include/ddk/isvbop.h diff --cc include/ddk/isvbop.inc index 00000000000,7a13b4bc27a..7a13b4bc27a mode 000000,100644..100644 --- a/include/ddk/isvbop.inc +++ b/include/ddk/isvbop.inc diff --cc include/ddk/nt_vdd.h index 00000000000,b4b6c7df67d..b4b6c7df67d mode 000000,100644..100644 --- a/include/ddk/nt_vdd.h +++ b/include/ddk/nt_vdd.h diff --cc include/ddk/vddsvc.h index 00000000000,eb808223141..eb808223141 mode 000000,100644..100644 --- a/include/ddk/vddsvc.h +++ b/include/ddk/vddsvc.h diff --cc include/ndk/rtlfuncs.h index c0a205c473f,00000000000..b38a982197a mode 100644,000000..100644 --- a/include/ndk/rtlfuncs.h +++ b/include/ndk/rtlfuncs.h @@@ -1,4426 -1,0 +1,4437 @@@ +/*++ NDK Version: 0098 + +Copyright (c) Alex Ionescu. All rights reserved. + +Header Name: + + rtlfuncs.h + +Abstract: + + Function definitions for the Run-Time Library + +Author: + + Alex Ionescu (alexi@tinykrnl.org) - Updated - 27-Feb-2006 + +--*/ + +#ifndef _RTLFUNCS_H +#define _RTLFUNCS_H + +// +// Dependencies +// +#include +#include +#include +#include +#include +#include "in6addr.h" +#include "inaddr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef NTOS_MODE_USER + +// +// List Functions +// +FORCEINLINE +VOID +InitializeListHead( + _Out_ PLIST_ENTRY ListHead +) +{ + ListHead->Flink = ListHead->Blink = ListHead; +} + +FORCEINLINE +VOID +InsertHeadList( + _Inout_ PLIST_ENTRY ListHead, + _Inout_ PLIST_ENTRY Entry +) +{ + PLIST_ENTRY OldFlink; + OldFlink = ListHead->Flink; + Entry->Flink = OldFlink; + Entry->Blink = ListHead; + OldFlink->Blink = Entry; + ListHead->Flink = Entry; +} + +FORCEINLINE +VOID +InsertTailList( + _Inout_ PLIST_ENTRY ListHead, + _Inout_ PLIST_ENTRY Entry +) +{ + PLIST_ENTRY OldBlink; + OldBlink = ListHead->Blink; + Entry->Flink = ListHead; + Entry->Blink = OldBlink; + OldBlink->Flink = Entry; + ListHead->Blink = Entry; +} + +_Must_inspect_result_ +FORCEINLINE +BOOLEAN +IsListEmpty( + _In_ const LIST_ENTRY * ListHead +) +{ + return (BOOLEAN)(ListHead->Flink == ListHead); +} + +FORCEINLINE +PSINGLE_LIST_ENTRY +PopEntryList( + _Inout_ PSINGLE_LIST_ENTRY ListHead +) +{ + PSINGLE_LIST_ENTRY FirstEntry; + FirstEntry = ListHead->Next; + if (FirstEntry != NULL) { + ListHead->Next = FirstEntry->Next; + } + + return FirstEntry; +} + +FORCEINLINE +VOID +PushEntryList( + _Inout_ PSINGLE_LIST_ENTRY ListHead, + _Inout_ PSINGLE_LIST_ENTRY Entry +) +{ + Entry->Next = ListHead->Next; + ListHead->Next = Entry; +} + +FORCEINLINE +BOOLEAN +RemoveEntryList( + _In_ PLIST_ENTRY Entry) +{ + PLIST_ENTRY OldFlink; + PLIST_ENTRY OldBlink; + + OldFlink = Entry->Flink; + OldBlink = Entry->Blink; + OldFlink->Blink = OldBlink; + OldBlink->Flink = OldFlink; + return (BOOLEAN)(OldFlink == OldBlink); +} + +FORCEINLINE +PLIST_ENTRY +RemoveHeadList( + _Inout_ PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Flink; + PLIST_ENTRY Entry; + + Entry = ListHead->Flink; + Flink = Entry->Flink; + ListHead->Flink = Flink; + Flink->Blink = ListHead; + return Entry; +} + +FORCEINLINE +PLIST_ENTRY +RemoveTailList( + _Inout_ PLIST_ENTRY ListHead) +{ + PLIST_ENTRY Blink; + PLIST_ENTRY Entry; + + Entry = ListHead->Blink; + Blink = Entry->Blink; + ListHead->Blink = Blink; + Blink->Flink = ListHead; + return Entry; +} + +// +// Unicode string macros +// +_At_(UnicodeString->Buffer, _Post_equal_to_(Buffer)) +_At_(UnicodeString->Length, _Post_equal_to_(0)) +_At_(UnicodeString->MaximumLength, _Post_equal_to_(BufferSize)) +FORCEINLINE +VOID +RtlInitEmptyUnicodeString( + _Out_ PUNICODE_STRING UnicodeString, + _When_(BufferSize != 0, _Notnull_) _Writable_bytes_(BufferSize) __drv_aliasesMem PWCHAR Buffer, + _In_ USHORT BufferSize) +{ + UnicodeString->Length = 0; + UnicodeString->MaximumLength = BufferSize; + UnicodeString->Buffer = Buffer; +} + +_At_(AnsiString->Buffer, _Post_equal_to_(Buffer)) +_At_(AnsiString->Length, _Post_equal_to_(0)) +_At_(AnsiString->MaximumLength, _Post_equal_to_(BufferSize)) +FORCEINLINE +VOID +RtlInitEmptyAnsiString( + _Out_ PANSI_STRING AnsiString, + _When_(BufferSize != 0, _Notnull_) _Writable_bytes_(BufferSize) __drv_aliasesMem PCHAR Buffer, + _In_ USHORT BufferSize) +{ + AnsiString->Length = 0; + AnsiString->MaximumLength = BufferSize; + AnsiString->Buffer = Buffer; +} + +// +// LUID Macros +// +#define RtlEqualLuid(L1, L2) (((L1)->HighPart == (L2)->HighPart) && \ + ((L1)->LowPart == (L2)->LowPart)) +FORCEINLINE +LUID +NTAPI_INLINE +RtlConvertUlongToLuid( + _In_ ULONG Ulong) +{ + LUID TempLuid; + + TempLuid.LowPart = Ulong; + TempLuid.HighPart = 0; + return TempLuid; +} + +// +// ASSERT Macros +// +#ifndef ASSERT +#if DBG + +#define ASSERT( exp ) \ + ((void)((!(exp)) ? \ + (RtlAssert( (PVOID)#exp, (PVOID)__FILE__, __LINE__, NULL ),FALSE) : \ + TRUE)) + +#define ASSERTMSG( msg, exp ) \ + ((void)((!(exp)) ? \ + (RtlAssert( (PVOID)#exp, (PVOID)__FILE__, __LINE__, (PCHAR)msg ),FALSE) : \ + TRUE)) + +#else + +#define ASSERT( exp ) ((void) 0) +#define ASSERTMSG( msg, exp ) ((void) 0) + +#endif +#endif + +#ifdef NTOS_KERNEL_RUNTIME + +// +// Executing RTL functions at DISPATCH_LEVEL or higher will result in a +// bugcheck. +// +#define RTL_PAGED_CODE PAGED_CODE + +#else + +// +// This macro does nothing in user mode +// +#define RTL_PAGED_CODE NOP_FUNCTION + +#endif + +// +// RTL Splay Tree Functions +// +#ifndef RTL_USE_AVL_TABLES + +NTSYSAPI +VOID +NTAPI +RtlInitializeGenericTable( + _Out_ PRTL_GENERIC_TABLE Table, + _In_ PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine, + _In_opt_ PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine, + _In_opt_ PRTL_GENERIC_FREE_ROUTINE FreeRoutine, + _In_opt_ PVOID TableContext +); + +NTSYSAPI +PVOID +NTAPI +RtlInsertElementGenericTable( + _In_ PRTL_GENERIC_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement +); + +NTSYSAPI +PVOID +NTAPI +RtlInsertElementGenericTableFull( + _In_ PRTL_GENERIC_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement, + _In_ PVOID NodeOrParent, + _In_ TABLE_SEARCH_RESULT SearchResult +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDeleteElementGenericTable( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID Buffer +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlLookupElementGenericTable( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID Buffer +); + +NTSYSAPI +PVOID +NTAPI +RtlLookupElementGenericTableFull( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *NodeOrParent, + _Out_ TABLE_SEARCH_RESULT *SearchResult +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlEnumerateGenericTable( + _In_ PRTL_GENERIC_TABLE Table, + _In_ BOOLEAN Restart +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlEnumerateGenericTableWithoutSplaying( + _In_ PRTL_GENERIC_TABLE Table, + _Inout_ PVOID *RestartKey +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlGetElementGenericTable( + _In_ PRTL_GENERIC_TABLE Table, + _In_ ULONG I +); + +NTSYSAPI +ULONG +NTAPI +RtlNumberGenericTableElements( + _In_ PRTL_GENERIC_TABLE Table +); + +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlIsGenericTableEmpty( + _In_ PRTL_GENERIC_TABLE Table +); + +#endif /* !RTL_USE_AVL_TABLES */ + +NTSYSAPI +PRTL_SPLAY_LINKS +NTAPI +RtlSplay( + _Inout_ PRTL_SPLAY_LINKS Links +); + +NTSYSAPI +PRTL_SPLAY_LINKS +NTAPI +RtlDelete( + _In_ PRTL_SPLAY_LINKS Links +); + +NTSYSAPI +VOID +NTAPI +RtlDeleteNoSplay( + _In_ PRTL_SPLAY_LINKS Links, + _Inout_ PRTL_SPLAY_LINKS *Root +); + +_Must_inspect_result_ +NTSYSAPI +PRTL_SPLAY_LINKS +NTAPI +RtlSubtreeSuccessor( + _In_ PRTL_SPLAY_LINKS Links +); + +_Must_inspect_result_ +NTSYSAPI +PRTL_SPLAY_LINKS +NTAPI +RtlSubtreePredecessor( + _In_ PRTL_SPLAY_LINKS Links +); + +_Must_inspect_result_ +NTSYSAPI +PRTL_SPLAY_LINKS +NTAPI +RtlRealSuccessor( + _In_ PRTL_SPLAY_LINKS Links +); + +_Must_inspect_result_ +NTSYSAPI +PRTL_SPLAY_LINKS +NTAPI +RtlRealPredecessor( + _In_ PRTL_SPLAY_LINKS Links +); + +#define RtlIsLeftChild(Links) \ + (RtlLeftChild(RtlParent(Links)) == (PRTL_SPLAY_LINKS)(Links)) + +#define RtlIsRightChild(Links) \ + (RtlRightChild(RtlParent(Links)) == (PRTL_SPLAY_LINKS)(Links)) + +#define RtlRightChild(Links) \ + ((PRTL_SPLAY_LINKS)(Links))->RightChild + +#define RtlIsRoot(Links) \ + (RtlParent(Links) == (PRTL_SPLAY_LINKS)(Links)) + +#define RtlLeftChild(Links) \ + ((PRTL_SPLAY_LINKS)(Links))->LeftChild + +#define RtlParent(Links) \ + ((PRTL_SPLAY_LINKS)(Links))->Parent + +// FIXME: use inline function + +#define RtlInitializeSplayLinks(Links) \ + { \ + PRTL_SPLAY_LINKS _SplayLinks; \ + _SplayLinks = (PRTL_SPLAY_LINKS)(Links); \ + _SplayLinks->Parent = _SplayLinks; \ + _SplayLinks->LeftChild = NULL; \ + _SplayLinks->RightChild = NULL; \ + } + +#define RtlInsertAsLeftChild(ParentLinks,ChildLinks) \ + { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->LeftChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + +#define RtlInsertAsRightChild(ParentLinks,ChildLinks) \ + { \ + PRTL_SPLAY_LINKS _SplayParent; \ + PRTL_SPLAY_LINKS _SplayChild; \ + _SplayParent = (PRTL_SPLAY_LINKS)(ParentLinks); \ + _SplayChild = (PRTL_SPLAY_LINKS)(ChildLinks); \ + _SplayParent->RightChild = _SplayChild; \ + _SplayChild->Parent = _SplayParent; \ + } + +// +// RTL AVL Tree Functions +// +NTSYSAPI +VOID +NTAPI +RtlInitializeGenericTableAvl( + _Out_ PRTL_AVL_TABLE Table, + _In_ PRTL_AVL_COMPARE_ROUTINE CompareRoutine, + _In_opt_ PRTL_AVL_ALLOCATE_ROUTINE AllocateRoutine, + _In_opt_ PRTL_AVL_FREE_ROUTINE FreeRoutine, + _In_opt_ PVOID TableContext +); + +NTSYSAPI +PVOID +NTAPI +RtlInsertElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement +); + +NTSYSAPI +PVOID +NTAPI +RtlInsertElementGenericTableFullAvl( + _In_ PRTL_AVL_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement, + _In_ PVOID NodeOrParent, + _In_ TABLE_SEARCH_RESULT SearchResult +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDeleteElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlLookupElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer +); + +NTSYSAPI +PVOID +NTAPI +RtlLookupElementGenericTableFullAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *NodeOrParent, + _Out_ TABLE_SEARCH_RESULT *SearchResult +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlEnumerateGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ BOOLEAN Restart +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlEnumerateGenericTableWithoutSplayingAvl( + _In_ PRTL_AVL_TABLE Table, + _Inout_ PVOID *RestartKey +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlLookupFirstMatchingElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *RestartKey +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlEnumerateGenericTableLikeADirectory( + _In_ PRTL_AVL_TABLE Table, + _In_opt_ PRTL_AVL_MATCH_FUNCTION MatchFunction, + _In_opt_ PVOID MatchData, + _In_ ULONG NextFlag, + _Inout_ PVOID *RestartKey, + _Inout_ PULONG DeleteCount, + _In_ PVOID Buffer +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlGetElementGenericTableAvl( + _In_ PRTL_AVL_TABLE Table, + _In_ ULONG I +); + +NTSYSAPI +ULONG +NTAPI +RtlNumberGenericTableElementsAvl( + _In_ PRTL_AVL_TABLE Table +); + +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlIsGenericTableEmptyAvl( + _In_ PRTL_AVL_TABLE Table +); + +#ifdef RTL_USE_AVL_TABLES + +#define RtlInitializeGenericTable RtlInitializeGenericTableAvl +#define RtlInsertElementGenericTable RtlInsertElementGenericTableAvl +#define RtlInsertElementGenericTableFull RtlInsertElementGenericTableFullAvl +#define RtlDeleteElementGenericTable RtlDeleteElementGenericTableAvl +#define RtlLookupElementGenericTable RtlLookupElementGenericTableAvl +#define RtlLookupElementGenericTableFull RtlLookupElementGenericTableFullAvl +#define RtlEnumerateGenericTable RtlEnumerateGenericTableAvl +#define RtlEnumerateGenericTableWithoutSplaying RtlEnumerateGenericTableWithoutSplayingAvl +#define RtlGetElementGenericTable RtlGetElementGenericTableAvl +#define RtlNumberGenericTableElements RtlNumberGenericTableElementsAvl +#define RtlIsGenericTableEmpty RtlIsGenericTableEmptyAvl + +#endif /* RTL_USE_AVL_TABLES */ + +// +// Error and Exception Functions +// +NTSYSAPI +PVOID +NTAPI +RtlAddVectoredExceptionHandler( + _In_ ULONG FirstHandler, + _In_ PVECTORED_EXCEPTION_HANDLER VectoredHandler +); + +__analysis_noreturn +NTSYSAPI +VOID +NTAPI +RtlAssert( + _In_ PVOID FailedAssertion, + _In_ PVOID FileName, + _In_ ULONG LineNumber, + _In_opt_z_ PCHAR Message +); + +NTSYSAPI +VOID +NTAPI +RtlSetUnhandledExceptionFilter( + _In_ PRTLP_UNHANDLED_EXCEPTION_FILTER TopLevelExceptionFilter +); + +#endif /* NTOS_MODE_USER */ + +NTSYSAPI +VOID +NTAPI +RtlCaptureContext( + _Out_ PCONTEXT ContextRecord +); + +NTSYSAPI +PVOID +NTAPI +RtlEncodePointer( + _In_ PVOID Pointer +); + +NTSYSAPI +PVOID +NTAPI +RtlDecodePointer( + _In_ PVOID Pointer +); + +NTSYSAPI +PVOID +NTAPI +RtlEncodeSystemPointer( + _In_ PVOID Pointer +); + +NTSYSAPI +PVOID +NTAPI +RtlDecodeSystemPointer( + _In_ PVOID Pointer +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDispatchException( + _In_ PEXCEPTION_RECORD ExceptionRecord, + _In_ PCONTEXT Context +); + +_IRQL_requires_max_(APC_LEVEL) +_When_(Status < 0, _Out_range_(>, 0)) +_When_(Status >= 0, _Out_range_(==, 0)) +NTSYSAPI +ULONG +NTAPI +RtlNtStatusToDosError( + _In_ NTSTATUS Status +); + +_When_(Status < 0, _Out_range_(>, 0)) +_When_(Status >= 0, _Out_range_(==, 0)) +NTSYSAPI +ULONG +NTAPI +RtlNtStatusToDosErrorNoTeb( + _In_ NTSTATUS Status +); + +NTSYSAPI +VOID +NTAPI +RtlSetLastWin32ErrorAndNtStatusFromNtStatus( + _In_ NTSTATUS Status +); + +NTSYSAPI +VOID +NTAPI +RtlRaiseException( + _In_ PEXCEPTION_RECORD ExceptionRecord +); + +DECLSPEC_NORETURN +NTSYSAPI +VOID +NTAPI +RtlRaiseStatus( + _In_ NTSTATUS Status +); + +NTSYSAPI +LONG +NTAPI +RtlUnhandledExceptionFilter( + _In_ struct _EXCEPTION_POINTERS* ExceptionInfo +); + +NTSYSAPI +VOID +NTAPI +RtlUnwind( + _In_opt_ PVOID TargetFrame, + _In_opt_ PVOID TargetIp, + _In_opt_ PEXCEPTION_RECORD ExceptionRecord, + _In_ PVOID ReturnValue +); + +#define RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT 8 + +// +// Tracing Functions +// +NTSYSAPI +ULONG +NTAPI +RtlWalkFrameChain( + _Out_writes_(Count - (Flags >> RTL_STACK_WALKING_MODE_FRAMES_TO_SKIP_SHIFT)) PVOID *Callers, + _In_ ULONG Count, + _In_ ULONG Flags +); + +NTSYSAPI +USHORT +NTAPI +RtlLogStackBackTrace( + VOID +); + +#ifdef NTOS_MODE_USER +// +// Heap Functions +// +_Must_inspect_result_ +_Ret_maybenull_ +_Post_writable_byte_size_(Size) +NTSYSAPI +PVOID +NTAPI +RtlAllocateHeap( + _In_ PVOID HeapHandle, + _In_opt_ ULONG Flags, + _In_ SIZE_T Size +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlCreateHeap( + _In_ ULONG Flags, + _In_opt_ PVOID BaseAddress, + _In_opt_ SIZE_T SizeToReserve, + _In_opt_ SIZE_T SizeToCommit, + _In_opt_ PVOID Lock, + _In_opt_ PRTL_HEAP_PARAMETERS Parameters +); + +NTSYSAPI +ULONG +NTAPI +RtlCreateTagHeap( + _In_ HANDLE HeapHandle, + _In_ ULONG Flags, + _In_ PWSTR TagName, + _In_ PWSTR TagSubName +); + +ULONG +NTAPI +RtlCompactHeap( + _In_ HANDLE Heap, + _In_ ULONG Flags +); + +_Must_inspect_result_ +NTSYSAPI +PVOID +NTAPI +RtlDebugCreateHeap( + _In_ ULONG Flags, + _In_opt_ PVOID BaseAddress, + _In_opt_ SIZE_T SizeToReserve, + _In_opt_ SIZE_T SizeToCommit, + _In_opt_ PVOID Lock, + _In_opt_ PRTL_HEAP_PARAMETERS Parameters +); + +NTSYSAPI +HANDLE +NTAPI +RtlDestroyHeap( + _In_ _Post_invalid_ HANDLE Heap +); + +NTSYSAPI +ULONG +NTAPI +RtlExtendHeap( + _In_ HANDLE Heap, + _In_ ULONG Flags, + _In_ PVOID P, + _In_ SIZE_T Size +); + +_Success_(return != 0) +NTSYSAPI +BOOLEAN +NTAPI +RtlFreeHeap( + _In_ HANDLE HeapHandle, + _In_opt_ ULONG Flags, + _In_ _Post_invalid_ PVOID P +); + +ULONG +NTAPI +RtlGetProcessHeaps( + _In_ ULONG HeapCount, + _Out_cap_(HeapCount) HANDLE *HeapArray +); + +_Success_(return != 0) +BOOLEAN +NTAPI +RtlGetUserInfoHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ PVOID BaseAddress, + _Inout_opt_ PVOID *UserValue, + _Out_opt_ PULONG UserFlags +); + +NTSYSAPI +PVOID +NTAPI +RtlProtectHeap( + _In_ PVOID HeapHandle, + _In_ BOOLEAN Protect +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryHeapInformation( + _In_ PVOID HeapHandle, + _In_ HEAP_INFORMATION_CLASS HeapInformationClass, + _Out_ PVOID HeapInformation, + _In_ SIZE_T HeapInformationLength, + _When_(HeapInformationClass==HeapCompatibilityInformation, _On_failure_(_Out_opt_)) + _Out_opt_ PSIZE_T ReturnLength +); + +_Ret_opt_z_ +NTSYSAPI +PWSTR +NTAPI +RtlQueryTagHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ USHORT TagIndex, + _In_ BOOLEAN ResetCounters, + _Out_ PRTL_HEAP_TAG_INFO HeapTagInfo +); + +_Must_inspect_result_ +_Ret_maybenull_ +_Post_writable_byte_size_(Size) +NTSYSAPI +PVOID +NTAPI +RtlReAllocateHeap( + _In_ HANDLE Heap, + _In_opt_ ULONG Flags, + _In_ _Post_invalid_ PVOID Ptr, + _In_ SIZE_T Size +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetHeapInformation( + _In_ PVOID HeapHandle, + _In_ HEAP_INFORMATION_CLASS HeapInformationClass, + _When_(HeapInformationClass==HeapCompatibilityInformation,_In_) PVOID HeapInformation, + _In_ SIZE_T HeapInformationLength +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlLockHeap( + _In_ HANDLE Heap +); + +_Must_inspect_result_ +NTSYSAPI +NTSTATUS +NTAPI +RtlMultipleAllocateHeap ( + _In_ HANDLE HeapHandle, + _In_ ULONG Flags, + _In_ SIZE_T Size, + _In_ ULONG Count, + _Out_cap_(Count) _Deref_post_bytecap_(Size) PVOID * Array + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlMultipleFreeHeap ( + _In_ HANDLE HeapHandle, + _In_ ULONG Flags, + _In_ ULONG Count, + _In_count_(Count) /* _Deref_ _Post_invalid_ */ PVOID * Array + ); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUsageHeap( + _In_ HANDLE Heap, + _In_ ULONG Flags, + _Out_ PRTL_HEAP_USAGE Usage +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlUnlockHeap( + _In_ HANDLE Heap +); + +BOOLEAN +NTAPI +RtlSetUserValueHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ PVOID BaseAddress, + _In_ PVOID UserValue +); + +BOOLEAN +NTAPI +RtlSetUserFlagsHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ PVOID BaseAddress, + _In_ ULONG UserFlagsReset, + _In_ ULONG UserFlagsSet +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlValidateHeap( + _In_ HANDLE Heap, + _In_ ULONG Flags, + _In_opt_ PVOID P +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlWalkHeap( + _In_ HANDLE HeapHandle, + _In_ PVOID HeapEntry +); + +#define RtlGetProcessHeap() (NtCurrentPeb()->ProcessHeap) + +#endif // NTOS_MODE_USER + +NTSYSAPI +SIZE_T +NTAPI +RtlSizeHeap( + _In_ PVOID HeapHandle, + _In_ ULONG Flags, + _In_ PVOID MemoryPointer +); + + +// +// Security Functions +// +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlAbsoluteToSelfRelativeSD( + _In_ PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, + _Out_writes_bytes_to_opt_(*BufferLength, *BufferLength) PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor, + _Inout_ PULONG BufferLength +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAccessAllowedAce( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ACCESS_MASK AccessMask, + _In_ PSID Sid +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAccessAllowedAceEx( + _Inout_ PACL pAcl, + _In_ ULONG dwAceRevision, + _In_ ULONG AceFlags, + _In_ ACCESS_MASK AccessMask, + _In_ PSID pSid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAccessAllowedObjectAce( + _Inout_ PACL pAcl, + _In_ ULONG dwAceRevision, + _In_ ULONG AceFlags, + _In_ ACCESS_MASK AccessMask, + _In_opt_ GUID *ObjectTypeGuid, + _In_opt_ GUID *InheritedObjectTypeGuid, + _In_ PSID pSid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAccessDeniedAce( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ACCESS_MASK AccessMask, + _In_ PSID Sid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAccessDeniedAceEx( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ULONG Flags, + _In_ ACCESS_MASK AccessMask, + _In_ PSID Sid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAccessDeniedObjectAce( + _Inout_ PACL pAcl, + _In_ ULONG dwAceRevision, + _In_ ULONG AceFlags, + _In_ ACCESS_MASK AccessMask, + _In_opt_ GUID *ObjectTypeGuid, + _In_opt_ GUID *InheritedObjectTypeGuid, + _In_ PSID pSid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAce( + _Inout_ PACL Acl, + _In_ ULONG AceRevision, + _In_ ULONG StartingAceIndex, + _In_reads_bytes_(AceListLength) PVOID AceList, + _In_ ULONG AceListLength +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAuditAccessAce( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ACCESS_MASK AccessMask, + _In_ PSID Sid, + _In_ BOOLEAN Success, + _In_ BOOLEAN Failure +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAcquirePrivilege( + _In_ PULONG Privilege, + _In_ ULONG NumPriv, + _In_ ULONG Flags, + _Out_ PVOID *ReturnedState +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAuditAccessAceEx( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ULONG Flags, + _In_ ACCESS_MASK AccessMask, + _In_ PSID Sid, + _In_ BOOLEAN Success, + _In_ BOOLEAN Failure +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAuditAccessObjectAce( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ULONG Flags, + _In_ ACCESS_MASK AccessMask, + _In_opt_ GUID *ObjectTypeGuid, + _In_opt_ GUID *InheritedObjectTypeGuid, + _In_ PSID Sid, + _In_ BOOLEAN Success, + _In_ BOOLEAN Failure +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddMandatoryAce( + _Inout_ PACL Acl, + _In_ ULONG Revision, + _In_ ULONG Flags, + _In_ ULONG MandatoryFlags, + _In_ UCHAR AceType, + _In_ PSID LabelSid); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAdjustPrivilege( + _In_ ULONG Privilege, + _In_ BOOLEAN NewValue, + _In_ BOOLEAN ForThread, + _Out_ PBOOLEAN OldValue +); + +_Must_inspect_result_ +NTSYSAPI +NTSTATUS +NTAPI +RtlAllocateAndInitializeSid( + _In_ PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, + _In_ UCHAR SubAuthorityCount, + _In_ ULONG SubAuthority0, + _In_ ULONG SubAuthority1, + _In_ ULONG SubAuthority2, + _In_ ULONG SubAuthority3, + _In_ ULONG SubAuthority4, + _In_ ULONG SubAuthority5, + _In_ ULONG SubAuthority6, + _In_ ULONG SubAuthority7, + _Outptr_ PSID *Sid +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlAreAllAccessesGranted( + ACCESS_MASK GrantedAccess, + ACCESS_MASK DesiredAccess +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlAreAnyAccessesGranted( + ACCESS_MASK GrantedAccess, + ACCESS_MASK DesiredAccess +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlCopyLuid ( + _Out_ PLUID DestinationLuid, + _In_ PLUID SourceLuid + ); + +NTSYSAPI +VOID +NTAPI +RtlCopyLuidAndAttributesArray( + ULONG Count, + PLUID_AND_ATTRIBUTES Src, + PLUID_AND_ATTRIBUTES Dest +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCopySidAndAttributesArray( + ULONG Count, + PSID_AND_ATTRIBUTES Src, + ULONG SidAreaSize, + PSID_AND_ATTRIBUTES Dest, + PVOID SidArea, + PVOID* RemainingSidArea, + PULONG RemainingSidAreaSize +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlConvertSidToUnicodeString( + _Inout_ PUNICODE_STRING UnicodeString, + _In_ PSID Sid, + _In_ BOOLEAN AllocateDestinationString +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlCopySid( + _In_ ULONG DestinationSidLength, + _Out_writes_bytes_(DestinationSidLength) PSID DestinationSid, + _In_ PSID SourceSid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateAcl( + PACL Acl, + ULONG AclSize, + ULONG AclRevision +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateSecurityDescriptor( + _Out_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ ULONG Revision +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateSecurityDescriptorRelative( + _Out_ PISECURITY_DESCRIPTOR_RELATIVE SecurityDescriptor, + _In_ ULONG Revision +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCopySecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR pSourceSecurityDescriptor, + _Out_ PSECURITY_DESCRIPTOR *pDestinationSecurityDescriptor +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteAce( + PACL Acl, + ULONG AceIndex +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlEqualPrefixSid( + PSID Sid1, + PSID Sid2 +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlEqualSid ( + _In_ PSID Sid1, + _In_ PSID Sid2 +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlFirstFreeAce( + PACL Acl, + PACE* Ace +); + +NTSYSAPI +PVOID +NTAPI +RtlFreeSid( + _In_ _Post_invalid_ PSID Sid +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetAce( + PACL Acl, + ULONG AceIndex, + PVOID *Ace +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetControlSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSECURITY_DESCRIPTOR_CONTROL Control, + _Out_ PULONG Revision +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetDaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN DaclPresent, + _Out_ PACL *Dacl, + _Out_ PBOOLEAN DaclDefaulted +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetSaclSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PBOOLEAN SaclPresent, + _Out_ PACL* Sacl, + _Out_ PBOOLEAN SaclDefaulted +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetGroupSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Group, + _Out_ PBOOLEAN GroupDefaulted +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetOwnerSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PSID *Owner, + _Out_ PBOOLEAN OwnerDefaulted +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlGetSecurityDescriptorRMControl( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _Out_ PUCHAR RMControl +); + +NTSYSAPI +PSID_IDENTIFIER_AUTHORITY +NTAPI +RtlIdentifierAuthoritySid(PSID Sid); + +NTSYSAPI +NTSTATUS +NTAPI +RtlImpersonateSelf(IN SECURITY_IMPERSONATION_LEVEL ImpersonationLevel); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlInitializeSid( + _Out_ PSID Sid, + _In_ PSID_IDENTIFIER_AUTHORITY IdentifierAuthority, + _In_ UCHAR SubAuthorityCount +); + +NTSYSAPI +ULONG +NTAPI +RtlLengthRequiredSid(IN ULONG SubAuthorityCount); + +NTSYSAPI +ULONG +NTAPI +RtlLengthSid(IN PSID Sid); + +NTSYSAPI +NTSTATUS +NTAPI +RtlMakeSelfRelativeSD( + _In_ PSECURITY_DESCRIPTOR AbsoluteSD, + _Out_ PSECURITY_DESCRIPTOR SelfRelativeSD, + _Inout_ PULONG BufferLength); + +NTSYSAPI +VOID +NTAPI +RtlMapGenericMask( + PACCESS_MASK AccessMask, + PGENERIC_MAPPING GenericMapping +); + +#ifdef NTOS_MODE_USER + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryInformationAcl( + PACL Acl, + PVOID Information, + ULONG InformationLength, + ACL_INFORMATION_CLASS InformationClass +); + +#endif + +NTSYSAPI +VOID +NTAPI +RtlReleasePrivilege( + _In_ PVOID ReturnedState +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlSelfRelativeToAbsoluteSD( + _In_ PSECURITY_DESCRIPTOR SelfRelativeSecurityDescriptor, + _Out_writes_bytes_to_opt_(*AbsoluteSecurityDescriptorSize, *AbsoluteSecurityDescriptorSize) PSECURITY_DESCRIPTOR AbsoluteSecurityDescriptor, + _Inout_ PULONG AbsoluteSecurityDescriptorSize, + _Out_writes_bytes_to_opt_(*DaclSize, *DaclSize) PACL Dacl, + _Inout_ PULONG DaclSize, + _Out_writes_bytes_to_opt_(*SaclSize, *SaclSize) PACL Sacl, + _Inout_ PULONG SaclSize, + _Out_writes_bytes_to_opt_(*OwnerSize, *OwnerSize) PSID Owner, + _Inout_ PULONG OwnerSize, + _Out_writes_bytes_to_opt_(*PrimaryGroupSize, *PrimaryGroupSize) PSID PrimaryGroup, + _Inout_ PULONG PrimaryGroupSize +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSelfRelativeToAbsoluteSD2( + _Inout_ PSECURITY_DESCRIPTOR SelfRelativeSD, + _Out_ PULONG BufferSize +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetAttributesSecurityDescriptor( + _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ SECURITY_DESCRIPTOR_CONTROL Control, + _Out_ PULONG Revision +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetControlSecurityDescriptor( + _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, + _In_ SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlSetDaclSecurityDescriptor( + _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ BOOLEAN DaclPresent, + _In_opt_ PACL Dacl, + _In_opt_ BOOLEAN DaclDefaulted +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlSetGroupSecurityDescriptor( + _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_opt_ PSID Group, + _In_opt_ BOOLEAN GroupDefaulted +); + +#ifdef NTOS_MODE_USER + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetInformationAcl( + PACL Acl, + PVOID Information, + ULONG InformationLength, + ACL_INFORMATION_CLASS InformationClass +); + +#endif + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlSetOwnerSecurityDescriptor( + _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_opt_ PSID Owner, + _In_opt_ BOOLEAN OwnerDefaulted +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetSaclSecurityDescriptor( + _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ BOOLEAN SaclPresent, + _In_ PACL Sacl, + _In_ BOOLEAN SaclDefaulted +); + +NTSYSAPI +VOID +NTAPI +RtlSetSecurityDescriptorRMControl( + _Inout_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ PUCHAR RMControl +); + +NTSYSAPI +PUCHAR +NTAPI +RtlSubAuthorityCountSid( + _In_ PSID Sid +); + +NTSYSAPI +PULONG +NTAPI +RtlSubAuthoritySid( + _In_ PSID Sid, + _In_ ULONG SubAuthority +); + +_IRQL_requires_max_(APC_LEVEL) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlValidRelativeSecurityDescriptor( + _In_reads_bytes_(SecurityDescriptorLength) PSECURITY_DESCRIPTOR SecurityDescriptorInput, + _In_ ULONG SecurityDescriptorLength, + _In_ SECURITY_INFORMATION RequiredInformation +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlValidSecurityDescriptor(IN PSECURITY_DESCRIPTOR SecurityDescriptor); + +NTSYSAPI +BOOLEAN +NTAPI +RtlValidSid(IN PSID Sid); + +NTSYSAPI +BOOLEAN +NTAPI +RtlValidAcl(PACL Acl); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteSecurityObject( + _In_ PSECURITY_DESCRIPTOR *ObjectDescriptor +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlNewSecurityObject( + _In_ PSECURITY_DESCRIPTOR ParentDescriptor, + _In_ PSECURITY_DESCRIPTOR CreatorDescriptor, + _Out_ PSECURITY_DESCRIPTOR *NewDescriptor, + _In_ BOOLEAN IsDirectoryObject, + _In_ HANDLE Token, + _In_ PGENERIC_MAPPING GenericMapping +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQuerySecurityObject( + _In_ PSECURITY_DESCRIPTOR ObjectDescriptor, + _In_ SECURITY_INFORMATION SecurityInformation, + _Out_ PSECURITY_DESCRIPTOR ResultantDescriptor, + _In_ ULONG DescriptorLength, + _Out_ PULONG ReturnLength +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetSecurityObject( + _In_ SECURITY_INFORMATION SecurityInformation, + _In_ PSECURITY_DESCRIPTOR ModificationDescriptor, + _Out_ PSECURITY_DESCRIPTOR *ObjectsSecurityDescriptor, + _In_ PGENERIC_MAPPING GenericMapping, + _In_ HANDLE Token +); + +// +// Single-Character Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlLargeIntegerToChar( + _In_ PLARGE_INTEGER Value, + _In_ ULONG Base, + _In_ ULONG Length, + _Out_ PCHAR String +); + +NTSYSAPI +CHAR +NTAPI +RtlUpperChar(CHAR Source); + +NTSYSAPI +WCHAR +NTAPI +RtlUpcaseUnicodeChar(WCHAR Source); + +NTSYSAPI +WCHAR +NTAPI +RtlDowncaseUnicodeChar(IN WCHAR Source); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIntegerToChar( + _In_ ULONG Value, + _In_ ULONG Base, + _In_ ULONG Length, + _Out_ PCHAR String +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIntegerToUnicode( + _In_ ULONG Value, + _In_opt_ ULONG Base, + _In_opt_ ULONG Length, + _Inout_ LPWSTR String +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_At_(String->MaximumLength, _Const_) +NTSYSAPI +NTSTATUS +NTAPI +RtlIntegerToUnicodeString( + _In_ ULONG Value, + _In_opt_ ULONG Base, + _Inout_ PUNICODE_STRING String +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCharToInteger( + PCSZ String, + ULONG Base, + PULONG Value +); + +// +// Byte Swap Functions +// +#ifdef NTOS_MODE_USER + +#if (defined(_M_IX86) && (_MSC_FULL_VER > 13009037)) || \ + ((defined(_M_AMD64) || \ + defined(_M_IA64)) && (_MSC_FULL_VER > 13009175)) + +unsigned short __cdecl _byteswap_ushort(unsigned short); +unsigned long __cdecl _byteswap_ulong (unsigned long); +unsigned __int64 __cdecl _byteswap_uint64(unsigned __int64); +#pragma intrinsic(_byteswap_ushort) +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define RtlUshortByteSwap(_x) _byteswap_ushort((USHORT)(_x)) +#define RtlUlongByteSwap(_x) _byteswap_ulong((_x)) +#define RtlUlonglongByteSwap(_x) _byteswap_uint64((_x)) + +#elif defined (__GNUC__) + +#define RtlUshortByteSwap(_x) _byteswap_ushort((USHORT)(_x)) +#define RtlUlongByteSwap(_x) _byteswap_ulong((_x)) +#define RtlUlonglongByteSwap(_x) _byteswap_uint64((_x)) + +#else + +#if (NTDDI_VERSION >= NTDDI_WIN2K) +NTSYSAPI +USHORT +FASTCALL +RtlUshortByteSwap(IN USHORT Source); + +NTSYSAPI +ULONG +FASTCALL +RtlUlongByteSwap(IN ULONG Source); + +NTSYSAPI +ULONGLONG +FASTCALL +RtlUlonglongByteSwap(IN ULONGLONG Source); +#endif + +#endif +#endif // NTOS_MODE_USER + +// +// Unicode->Ansi String Functions +// +NTSYSAPI +ULONG +NTAPI +RtlxUnicodeStringToAnsiSize(IN PCUNICODE_STRING UnicodeString); + +#ifdef NTOS_MODE_USER + +#define RtlUnicodeStringToAnsiSize(STRING) ( \ + NLS_MB_CODE_PAGE_TAG ? \ + RtlxUnicodeStringToAnsiSize(STRING) : \ + ((STRING)->Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR) \ +) + +#endif + +NTSYSAPI +NTSTATUS +NTAPI +RtlUnicodeStringToAnsiString( + PANSI_STRING DestinationString, + PCUNICODE_STRING SourceString, + BOOLEAN AllocateDestinationString +); + +// +// Unicode->OEM String Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlUpcaseUnicodeStringToOemString( + POEM_STRING DestinationString, + PCUNICODE_STRING SourceString, + BOOLEAN AllocateDestinationString +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +//_At_(DestinationString->Buffer, _Post_bytecount_(DestinationString->Length)) +NTSYSAPI +NTSTATUS +NTAPI +RtlUpcaseUnicodeStringToCountedOemString( + _When_(AllocateDestinationString, _Out_ _At_(DestinationString->Buffer, __drv_allocatesMem(Mem))) + _When_(!AllocateDestinationString, _Inout_) + POEM_STRING DestinationString, + _In_ PCUNICODE_STRING SourceString, + _In_ BOOLEAN AllocateDestinationString +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUnicodeStringToOemString( + POEM_STRING DestinationString, + PCUNICODE_STRING SourceString, + BOOLEAN AllocateDestinationString +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUpcaseUnicodeToOemN( + PCHAR OemString, + ULONG OemSize, + PULONG ResultSize, + PCWCH UnicodeString, + ULONG UnicodeSize +); + +NTSYSAPI +ULONG +NTAPI +RtlxUnicodeStringToOemSize(IN PCUNICODE_STRING UnicodeString); + +#ifdef NTOS_MODE_USER + +#define RtlUnicodeStringToOemSize(STRING) ( \ + NLS_MB_OEM_CODE_PAGE_TAG ? \ + RtlxUnicodeStringToOemSize(STRING) : \ + ((STRING)->Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR) \ +) + +#define RtlUnicodeStringToCountedOemSize(STRING) ( \ + (ULONG)(RtlUnicodeStringToOemSize(STRING) - sizeof(ANSI_NULL)) \ +) + +#endif + +NTSYSAPI +NTSTATUS +NTAPI +RtlUnicodeToOemN( + PCHAR OemString, + ULONG OemSize, + PULONG ResultSize, + PCWCH UnicodeString, + ULONG UnicodeSize +); + +// +// Unicode->MultiByte String Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlUnicodeToMultiByteN( + PCHAR MbString, + ULONG MbSize, + PULONG ResultSize, + PCWCH UnicodeString, + ULONG UnicodeSize +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUpcaseUnicodeToMultiByteN( + PCHAR MbString, + ULONG MbSize, + PULONG ResultSize, + PCWCH UnicodeString, + ULONG UnicodeSize +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUnicodeToMultiByteSize( + PULONG MbSize, + PCWCH UnicodeString, + ULONG UnicodeSize +); + +NTSYSAPI +ULONG +NTAPI +RtlxOemStringToUnicodeSize(IN PCOEM_STRING OemString); + +// +// OEM to Unicode Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlOemStringToUnicodeString( + PUNICODE_STRING DestinationString, + PCOEM_STRING SourceString, + BOOLEAN AllocateDestinationString +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlOemToUnicodeN( + _Out_writes_bytes_to_(MaxBytesInUnicodeString, *BytesInUnicodeString) PWCH UnicodeString, + _In_ ULONG MaxBytesInUnicodeString, + _Out_opt_ PULONG BytesInUnicodeString, + _In_reads_bytes_(BytesInOemString) PCCH OemString, + _In_ ULONG BytesInOemString +); + +#ifdef NTOS_MODE_USER + +#define RtlOemStringToUnicodeSize(STRING) ( \ + NLS_MB_OEM_CODE_PAGE_TAG ? \ + RtlxOemStringToUnicodeSize(STRING) : \ + ((STRING)->Length + sizeof(ANSI_NULL)) * sizeof(WCHAR) \ +) + +#define RtlOemStringToCountedUnicodeSize(STRING) ( \ + (ULONG)(RtlOemStringToUnicodeSize(STRING) - sizeof(UNICODE_NULL)) \ +) + +#endif + +// +// Ansi->Unicode String Functions +// +NTSYSAPI +ULONG +NTAPI +RtlxAnsiStringToUnicodeSize( + PCANSI_STRING AnsiString +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAnsiStringToUnicodeString( + PUNICODE_STRING DestinationString, + PCANSI_STRING SourceString, + BOOLEAN AllocateDestinationString +); + +#ifdef NTOS_MODE_USER + +#define RtlAnsiStringToUnicodeSize(STRING) ( \ + NLS_MB_CODE_PAGE_TAG ? \ + RtlxAnsiStringToUnicodeSize(STRING) : \ + ((STRING)->Length + sizeof(ANSI_NULL)) * sizeof(WCHAR) \ +) + +#endif + +NTSYSAPI +BOOLEAN +NTAPI +RtlCreateUnicodeStringFromAsciiz( + _Out_ PUNICODE_STRING Destination, + _In_ PCSZ Source +); + +// +// Unicode String Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlAppendUnicodeToString( + PUNICODE_STRING Destination, + PCWSTR Source +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAppendUnicodeStringToString( + PUNICODE_STRING Destination, + PCUNICODE_STRING Source +); + +NTSYSAPI +LONG +NTAPI +RtlCompareUnicodeString( + PCUNICODE_STRING String1, + PCUNICODE_STRING String2, + BOOLEAN CaseInsensitive +); + +NTSYSAPI +VOID +NTAPI +RtlCopyUnicodeString( + PUNICODE_STRING DestinationString, + PCUNICODE_STRING SourceString +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlCreateUnicodeString( + PUNICODE_STRING DestinationString, + PCWSTR SourceString +); + +#ifdef NTOS_MODE_USER + +NTSYSAPI +NTSTATUS +NTAPI +RtlDowncaseUnicodeString( + _Inout_ PUNICODE_STRING UniDest, + _In_ PCUNICODE_STRING UniSource, + _In_ BOOLEAN AllocateDestinationString +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDuplicateUnicodeString( + _In_ ULONG Flags, + _In_ PCUNICODE_STRING SourceString, + _Out_ PUNICODE_STRING DestinationString +); + +// +// Memory Functions +// +NTSYSAPI +VOID +NTAPI +RtlFillMemoryUlong( + _In_ PVOID Destination, + _In_ SIZE_T Length, + _In_ ULONG Fill +); + +NTSYSAPI +VOID +NTAPI +RtlFillMemoryUlonglong( + _Out_ PVOID Destination, + _In_ SIZE_T Length, + _In_ ULONGLONG Pattern +); + + +NTSYSAPI +SIZE_T +NTAPI +RtlCompareMemoryUlong( + _In_ PVOID Source, + _In_ SIZE_T Length, + _In_ ULONG Pattern +); + +#define RtlEqualMemory(Destination, Source, Length) \ + (!memcmp(Destination, Source, Length)) + +#define RtlCopyBytes RtlCopyMemory +#define RtlFillBytes RtlFillMemory +#define RtlZeroBytes RtlZeroMemory + +#endif + +NTSYSAPI +BOOLEAN +NTAPI +RtlEqualUnicodeString( + PCUNICODE_STRING String1, + PCUNICODE_STRING String2, + BOOLEAN CaseInsensitive +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlFindCharInUnicodeString( + _In_ ULONG Flags, + _In_ PCUNICODE_STRING SearchString, + _In_ PCUNICODE_STRING MatchString, + _Out_ PUSHORT Position +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlFreeUnicodeString( + _Inout_ _At_(UnicodeString->Buffer, __drv_freesMem(Mem)) + PUNICODE_STRING UnicodeString +); + +NTSYSAPI +VOID +NTAPI +RtlEraseUnicodeString( + _Inout_ PUNICODE_STRING String +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlHashUnicodeString( + _In_ CONST UNICODE_STRING *String, + _In_ BOOLEAN CaseInSensitive, + _In_ ULONG HashAlgorithm, + _Out_ PULONG HashValue +); + +_IRQL_requires_max_(DISPATCH_LEVEL) +_At_(DestinationString->Buffer, _Post_equal_to_(SourceString)) +_When_(SourceString != NULL, +_At_(DestinationString->Length, _Post_equal_to_(_String_length_(SourceString) * sizeof(WCHAR))) +_At_(DestinationString->MaximumLength, _Post_equal_to_(DestinationString->Length + sizeof(WCHAR)))) +_When_(SourceString == NULL, +_At_(DestinationString->Length, _Post_equal_to_(0)) +_At_(DestinationString->MaximumLength, _Post_equal_to_(0))) +NTSYSAPI +VOID +NTAPI +RtlInitUnicodeString( + _Out_ PUNICODE_STRING DestinationString, + _In_opt_z_ __drv_aliasesMem PCWSTR SourceString +); + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlInitUnicodeStringEx( + _Out_ PUNICODE_STRING DestinationString, + _In_opt_z_ __drv_aliasesMem PCWSTR SourceString +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsTextUnicode( + PVOID Buffer, + INT Length, + INT *Flags +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlPrefixString( + _In_ const STRING *String1, + _In_ const STRING *String2, + _In_ BOOLEAN CaseInsensitive +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlPrefixUnicodeString( + _In_ PCUNICODE_STRING String1, + _In_ PCUNICODE_STRING String2, + _In_ BOOLEAN CaseInsensitive +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlUpperString( + _Inout_ PSTRING DestinationString, + _In_ const STRING *SourceString +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +LONG +NTAPI +RtlCompareString( + _In_ const STRING *String1, + _In_ const STRING *String2, + _In_ BOOLEAN CaseInSensitive +); + +NTSYSAPI +VOID +NTAPI +RtlCopyString( + _Out_ PSTRING DestinationString, + _In_opt_ const STRING *SourceString +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlEqualString( + _In_ const STRING *String1, + _In_ const STRING *String2, + _In_ BOOLEAN CaseInSensitive +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlAppendStringToString( + _Inout_ PSTRING Destination, + _In_ const STRING *Source +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_When_(AllocateDestinationString, _Must_inspect_result_) +NTSYSAPI +NTSTATUS +NTAPI +RtlUpcaseUnicodeString( + _When_(AllocateDestinationString, _Out_ _At_(DestinationString->Buffer, __drv_allocatesMem(Mem))) + _When_(!AllocateDestinationString, _Inout_) + PUNICODE_STRING DestinationString, + _In_ PCUNICODE_STRING SourceString, + _In_ BOOLEAN AllocateDestinationString +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlUnicodeStringToInteger( + _In_ PCUNICODE_STRING String, + _In_opt_ ULONG Base, + _Out_ PULONG Value +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlValidateUnicodeString( + _In_ ULONG Flags, + _In_ PCUNICODE_STRING String +); + +// +// Ansi String Functions +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlFreeAnsiString( + _Inout_ _At_(AnsiString->Buffer, __drv_freesMem(Mem)) + PANSI_STRING AnsiString +); + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlInitAnsiString( + _Out_ PANSI_STRING DestinationString, + _In_opt_z_ __drv_aliasesMem PCSZ SourceString +); + +_IRQL_requires_max_(DISPATCH_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlInitAnsiStringEx( + _Out_ PANSI_STRING DestinationString, + _In_opt_z_ __drv_aliasesMem PCSZ SourceString +); + +// +// OEM String Functions +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlFreeOemString( + _Inout_ _At_(OemString->Buffer, __drv_freesMem(Mem)) + POEM_STRING OemString +); + +// +// MultiByte->Unicode String Functions +// +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlMultiByteToUnicodeN( + _Out_writes_bytes_to_(MaxBytesInUnicodeString, *BytesInUnicodeString) PWCH UnicodeString, + _In_ ULONG MaxBytesInUnicodeString, + _Out_opt_ PULONG BytesInUnicodeString, + _In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString, + _In_ ULONG BytesInMultiByteString +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlMultiByteToUnicodeSize( + _Out_ PULONG BytesInUnicodeString, + _In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString, + _In_ ULONG BytesInMultiByteString +); + +// +// Atom Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlAddAtomToAtomTable( + _In_ PRTL_ATOM_TABLE AtomTable, + _In_ PWSTR AtomName, + _Out_ PRTL_ATOM Atom +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateAtomTable( + _In_ ULONG TableSize, + _Inout_ PRTL_ATOM_TABLE *AtomTable +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteAtomFromAtomTable( + _In_ PRTL_ATOM_TABLE AtomTable, + _In_ RTL_ATOM Atom +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDestroyAtomTable(IN PRTL_ATOM_TABLE AtomTable); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryAtomInAtomTable( + _In_ PRTL_ATOM_TABLE AtomTable, + _In_ RTL_ATOM Atom, + _Out_opt_ PULONG RefCount, + _Out_opt_ PULONG PinCount, + _Out_opt_z_bytecap_(*NameLength) PWSTR AtomName, + _Inout_opt_ PULONG NameLength +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlPinAtomInAtomTable( + _In_ PRTL_ATOM_TABLE AtomTable, + _In_ RTL_ATOM Atom +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlLookupAtomInAtomTable( + _In_ PRTL_ATOM_TABLE AtomTable, + _In_ PWSTR AtomName, + _Out_ PRTL_ATOM Atom +); + +// +// Process Management Functions +// +NTSYSAPI +PPEB +NTAPI +RtlGetCurrentPeb( + VOID +); + +NTSYSAPI +VOID +NTAPI +RtlAcquirePebLock(VOID); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateProcessParameters ( + _Out_ PRTL_USER_PROCESS_PARAMETERS *ProcessParameters, + _In_ PUNICODE_STRING ImagePathName, + _In_opt_ PUNICODE_STRING DllPath, + _In_opt_ PUNICODE_STRING CurrentDirectory, + _In_opt_ PUNICODE_STRING CommandLine, + _In_opt_ PWSTR Environment, + _In_opt_ PUNICODE_STRING WindowTitle, + _In_opt_ PUNICODE_STRING DesktopInfo, + _In_opt_ PUNICODE_STRING ShellInfo, + _In_opt_ PUNICODE_STRING RuntimeInfo +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateUserProcess( + _In_ PUNICODE_STRING ImageFileName, + _In_ ULONG Attributes, + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters, + _In_opt_ PSECURITY_DESCRIPTOR ProcessSecutityDescriptor, + _In_opt_ PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, + _In_opt_ HANDLE ParentProcess, + _In_ BOOLEAN CurrentDirectory, + _In_opt_ HANDLE DebugPort, + _In_opt_ HANDLE ExceptionPort, + _Out_ PRTL_USER_PROCESS_INFORMATION ProcessInfo +); + +#if (NTDDI_VERSION >= NTDDI_WIN7) +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateUserThread( + _In_ PVOID ThreadContext, + _Out_ HANDLE *OutThreadHandle, + _Reserved_ PVOID Reserved1, + _Reserved_ PVOID Reserved2, + _Reserved_ PVOID Reserved3, + _Reserved_ PVOID Reserved4, + _Reserved_ PVOID Reserved5, + _Reserved_ PVOID Reserved6, + _Reserved_ PVOID Reserved7, + _Reserved_ PVOID Reserved8 +); +#else +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateUserThread( + _In_ HANDLE ProcessHandle, + _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor, + _In_ BOOLEAN CreateSuspended, + _In_ ULONG StackZeroBits, + _In_ SIZE_T StackReserve, + _In_ SIZE_T StackCommit, + _In_ PTHREAD_START_ROUTINE StartAddress, + _In_ PVOID Parameter, + _Out_opt_ PHANDLE ThreadHandle, + _Out_opt_ PCLIENT_ID ClientId +); +#endif + +NTSYSAPI +PRTL_USER_PROCESS_PARAMETERS +NTAPI +RtlDeNormalizeProcessParams( + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDestroyProcessParameters( + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters); + +NTSYSAPI +VOID +NTAPI +RtlExitUserThread( + _In_ NTSTATUS Status); + +NTSYSAPI +VOID +NTAPI +RtlInitializeContext( + _In_ HANDLE ProcessHandle, + _Out_ PCONTEXT ThreadContext, + _In_opt_ PVOID ThreadStartParam, + _In_ PTHREAD_START_ROUTINE ThreadStartAddress, + _In_ PINITIAL_TEB InitialTeb +); + +#ifdef _M_AMD64 +typedef struct _WOW64_CONTEXT *PWOW64_CONTEXT; + +NTSYSAPI +NTSTATUS +NTAPI +RtlWow64GetThreadContext( + _In_ HANDLE ThreadHandle, + _Inout_ PWOW64_CONTEXT ThreadContext +); + + +NTSYSAPI +NTSTATUS +NTAPI +RtlWow64SetThreadContext( + _In_ HANDLE ThreadHandle, + _In_ PWOW64_CONTEXT ThreadContext +); +#endif + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsThreadWithinLoaderCallout(VOID); + +NTSYSAPI +PRTL_USER_PROCESS_PARAMETERS +NTAPI +RtlNormalizeProcessParams( + _In_ PRTL_USER_PROCESS_PARAMETERS ProcessParameters); + +NTSYSAPI +VOID +NTAPI +RtlReleasePebLock(VOID); + +NTSYSAPI +NTSTATUS +NTAPI +RtlRemoteCall( + _In_ HANDLE Process, + _In_ HANDLE Thread, + _In_ PVOID CallSite, + _In_ ULONG ArgumentCount, + _In_ PULONG Arguments, + _In_ BOOLEAN PassContext, + _In_ BOOLEAN AlreadySuspended +); + +NTSYSAPI +NTSTATUS +__cdecl +RtlSetProcessIsCritical( + _In_ BOOLEAN NewValue, + _Out_opt_ PBOOLEAN OldValue, + _In_ BOOLEAN NeedBreaks +); + +NTSYSAPI +NTSTATUS +__cdecl +RtlSetThreadIsCritical( + _In_ BOOLEAN NewValue, + _Out_opt_ PBOOLEAN OldValue, + _In_ BOOLEAN NeedBreaks +); + +NTSYSAPI +ULONG +NTAPI +RtlGetCurrentProcessorNumber( + VOID +); + +#define NtCurrentPeb() (NtCurrentTeb()->ProcessEnvironmentBlock) + +// +// Thread Pool Functions +// +// +NTSTATUS +NTAPI +RtlSetThreadPoolStartFunc( + _In_ PRTL_START_POOL_THREAD StartPoolThread, + _In_ PRTL_EXIT_POOL_THREAD ExitPoolThread +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeregisterWaitEx( + _In_ HANDLE hWaitHandle, + _In_opt_ HANDLE hCompletionEvent +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeregisterWait( + _In_ HANDLE hWaitHandle +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueueWorkItem( + _In_ WORKERCALLBACKFUNC Function, + _In_opt_ PVOID Context, + _In_ ULONG Flags +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetIoCompletionCallback( + _In_ HANDLE FileHandle, + _In_ PIO_APC_ROUTINE Callback, + _In_ ULONG Flags +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlRegisterWait( + _In_ PHANDLE phNewWaitObject, + _In_ HANDLE hObject, + _In_ WAITORTIMERCALLBACKFUNC Callback, + _In_ PVOID pvContext, + _In_ ULONG ulMilliseconds, + _In_ ULONG ulFlags +); + +// +// Environment/Path Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateEnvironment( + _In_ BOOLEAN Inherit, + _Out_ PWSTR *Environment +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlComputePrivatizedDllName_U( + _In_ PUNICODE_STRING DllName, + _Out_ PUNICODE_STRING RealName, + _Out_ PUNICODE_STRING LocalName +); + +NTSYSAPI +VOID +NTAPI +RtlDestroyEnvironment( + _In_ PWSTR Environment +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDoesFileExists_U( + _In_ PCWSTR FileName +); + +NTSYSAPI +RTL_PATH_TYPE +NTAPI +RtlDetermineDosPathNameType_U( + _In_ PCWSTR Path +); + +NTSYSAPI +ULONG +NTAPI +RtlDosSearchPath_U( + _In_ PCWSTR Path, + _In_ PCWSTR FileName, + _In_ PCWSTR Extension, + _In_ ULONG BufferSize, + _Out_ PWSTR Buffer, + _Out_ PWSTR *PartName +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDosSearchPath_Ustr( + _In_ ULONG Flags, + _In_ PUNICODE_STRING PathString, + _In_ PUNICODE_STRING FileNameString, + _In_ PUNICODE_STRING ExtensionString, + _In_ PUNICODE_STRING CallerBuffer, + _Inout_opt_ PUNICODE_STRING DynamicString, + _Out_opt_ PUNICODE_STRING* FullNameOut, + _Out_opt_ PSIZE_T FilePartSize, + _Out_opt_ PSIZE_T LengthNeeded +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDosPathNameToNtPathName_U( + _In_opt_z_ PCWSTR DosPathName, + _Out_ PUNICODE_STRING NtPathName, + _Out_opt_ PCWSTR *NtFileNamePart, + _Out_opt_ PRTL_RELATIVE_NAME_U DirectoryInfo +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlDosPathNameToRelativeNtPathName_U( + _In_ PCWSTR DosName, + _Out_ PUNICODE_STRING NtName, + _Out_ PCWSTR *PartName, + _Out_ PRTL_RELATIVE_NAME_U RelativeName +); + +_At_(Destination->Buffer, _Out_bytecap_(Destination->MaximumLength)) +NTSYSAPI +NTSTATUS +NTAPI +RtlExpandEnvironmentStrings_U( + _In_z_ PWSTR Environment, + _In_ PUNICODE_STRING Source, + _Inout_ PUNICODE_STRING Destination, + _Out_ PULONG Length +); + +NTSYSAPI +ULONG +NTAPI +RtlGetCurrentDirectory_U( + _In_ ULONG MaximumLength, + _Out_bytecap_(MaximumLength) PWSTR Buffer +); + +NTSYSAPI +ULONG +NTAPI +RtlGetFullPathName_U( + _In_ PCWSTR FileName, + _In_ ULONG Size, + _Out_z_bytecap_(Size) PWSTR Buffer, + _Out_opt_ PWSTR *ShortName +); + +#if (NTDDI_VERSION >= NTDDI_WIN7) +NTSYSAPI +NTSTATUS +NTAPI +RtlGetFullPathName_UEx( + _In_ PWSTR FileName, + _In_ ULONG BufferLength, + _Out_ PWSTR Buffer, + _Out_opt_ PWSTR *FilePart, + _Out_opt_ RTL_PATH_TYPE *InputPathType + ); +#endif + +NTSTATUS +NTAPI +RtlGetFullPathName_UstrEx( + _In_ PUNICODE_STRING FileName, + _In_opt_ PUNICODE_STRING StaticString, + _In_opt_ PUNICODE_STRING DynamicString, + _Out_opt_ PUNICODE_STRING *StringUsed, + _Out_opt_ PSIZE_T FilePartSize, + _Out_opt_ PBOOLEAN NameInvalid, + _Out_ RTL_PATH_TYPE* PathType, + _Out_opt_ PSIZE_T LengthNeeded +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetLengthWithoutTrailingPathSeperators( + _Reserved_ ULONG Flags, + _In_ PCUNICODE_STRING PathString, + _Out_ PULONG Length +); + +NTSYSAPI +ULONG +NTAPI +RtlGetLongestNtPathLength( + VOID +); + +NTSYSAPI +ULONG +NTAPI +RtlIsDosDeviceName_U( + _In_ PCWSTR Name +); + +NTSYSAPI +ULONG +NTAPI +RtlIsDosDeviceName_Ustr( + _In_ PCUNICODE_STRING Name +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlIsNameLegalDOS8Dot3( + _In_ PCUNICODE_STRING Name, + _Inout_opt_ POEM_STRING OemName, + _Out_opt_ PBOOLEAN NameContainsSpaces +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryEnvironmentVariable_U( + _In_opt_ PWSTR Environment, + _In_ PUNICODE_STRING Name, + _Out_ PUNICODE_STRING Value +); + +VOID +NTAPI +RtlReleaseRelativeName( + _In_ PRTL_RELATIVE_NAME_U RelativeName +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetCurrentDirectory_U( + _In_ PUNICODE_STRING name +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetEnvironmentVariable( + _In_z_ PWSTR *Environment, + _In_ PUNICODE_STRING Name, + _In_ PUNICODE_STRING Value +); + +// +// Critical Section/Resource Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteCriticalSection ( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlEnterCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlInitializeCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlInitializeCriticalSectionAndSpinCount( + _In_ PRTL_CRITICAL_SECTION CriticalSection, + _In_ ULONG SpinCount +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlLeaveCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlTryEnterCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +VOID +NTAPI +RtlpUnWaitCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlpWaitForCriticalSection( + _In_ PRTL_CRITICAL_SECTION CriticalSection +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlAcquireResourceExclusive( + _In_ PRTL_RESOURCE Resource, + _In_ BOOLEAN Wait +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlAcquireResourceShared( + _In_ PRTL_RESOURCE Resource, + _In_ BOOLEAN Wait +); + +NTSYSAPI +VOID +NTAPI +RtlConvertExclusiveToShared( + _In_ PRTL_RESOURCE Resource +); + +NTSYSAPI +VOID +NTAPI +RtlConvertSharedToExclusive( + _In_ PRTL_RESOURCE Resource +); + +NTSYSAPI +VOID +NTAPI +RtlDeleteResource( + _In_ PRTL_RESOURCE Resource +); + +NTSYSAPI +VOID +NTAPI +RtlDumpResource( + _In_ PRTL_RESOURCE Resource +); + +NTSYSAPI +VOID +NTAPI +RtlInitializeResource( + _In_ PRTL_RESOURCE Resource +); + +NTSYSAPI +VOID +NTAPI +RtlReleaseResource( + _In_ PRTL_RESOURCE Resource +); + +// +// Compression Functions +// +NTSYSAPI //NT_RTL_COMPRESS_API +NTSTATUS +NTAPI +RtlCompressBuffer( + _In_ USHORT CompressionFormatAndEngine, + _In_reads_bytes_(UncompressedBufferSize) PUCHAR UncompressedBuffer, + _In_ ULONG UncompressedBufferSize, + _Out_writes_bytes_to_(CompressedBufferSize, *FinalCompressedSize) PUCHAR CompressedBuffer, + _In_ ULONG CompressedBufferSize, + _In_ ULONG UncompressedChunkSize, + _Out_ PULONG FinalCompressedSize, + _In_ PVOID WorkSpace +); + +_IRQL_requires_max_(APC_LEVEL) +NTSYSAPI //NT_RTL_COMPRESS_API +NTSTATUS +NTAPI +RtlDecompressBuffer( + _In_ USHORT CompressionFormat, + _Out_writes_bytes_to_(UncompressedBufferSize, *FinalUncompressedSize) PUCHAR UncompressedBuffer, + _In_ ULONG UncompressedBufferSize, + _In_reads_bytes_(CompressedBufferSize) PUCHAR CompressedBuffer, + _In_ ULONG CompressedBufferSize, + _Out_ PULONG FinalUncompressedSize +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetCompressionWorkSpaceSize( + _In_ USHORT CompressionFormatAndEngine, + _Out_ PULONG CompressBufferWorkSpaceSize, + _Out_ PULONG CompressFragmentWorkSpaceSize +); + +// +// Debug Info Functions +// +NTSYSAPI +PRTL_DEBUG_INFORMATION +NTAPI +RtlCreateQueryDebugBuffer( + _In_ ULONG Size, + _In_ BOOLEAN EventPair +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDestroyQueryDebugBuffer(IN PRTL_DEBUG_INFORMATION DebugBuffer); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryProcessDebugInformation( + _In_ ULONG ProcessId, + _In_ ULONG DebugInfoClassMask, + _Inout_ PRTL_DEBUG_INFORMATION DebugBuffer +); + +// +// Bitmap Functions +// +NTSYSAPI +BOOLEAN +NTAPI +RtlAreBitsClear( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG StartingIndex, + _In_ ULONG Length +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlAreBitsSet( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG StartingIndex, + _In_ ULONG Length +); + +NTSYSAPI +VOID +NTAPI +RtlClearAllBits( + _In_ PRTL_BITMAP BitMapHeader +); + +NTSYSAPI +VOID +NTAPI +RtlClearBits( + _In_ PRTL_BITMAP BitMapHeader, + _In_range_(0, BitMapHeader->SizeOfBitMap - NumberToClear) ULONG StartingIndex, + _In_range_(0, BitMapHeader->SizeOfBitMap - StartingIndex) ULONG NumberToClear +); + +NTSYSAPI +ULONG +NTAPI +RtlFindClearBits( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG NumberToFind, + _In_ ULONG HintIndex +); + +NTSYSAPI +ULONG +NTAPI +RtlFindClearBitsAndSet( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG NumberToFind, + _In_ ULONG HintIndex +); + +NTSYSAPI +CCHAR +NTAPI +RtlFindLeastSignificantBit( + _In_ ULONGLONG Value +); + +NTSYSAPI +CCHAR +NTAPI +RtlFindMostSignificantBit( + _In_ ULONGLONG Value +); + +NTSYSAPI +ULONG +NTAPI +RtlFindNextForwardRunClear( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG FromIndex, + _Out_ PULONG StartingRunIndex +); + +NTSYSAPI +ULONG +NTAPI +RtlFindNextForwardRunSet( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG FromIndex, + _Out_ PULONG StartingRunIndex +); + +NTSYSAPI +ULONG +NTAPI +RtlFindSetBits( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG NumberToFind, + _In_ ULONG HintIndex +); + +NTSYSAPI +ULONG +NTAPI +RtlFindSetBitsAndClear( + _In_ PRTL_BITMAP BitMapHeader, + _In_ ULONG NumberToFind, + _In_ ULONG HintIndex +); + +#ifdef _REACTOS_ // ReactOS improvement +_At_(BitMapHeader->SizeOfBitMap, _Post_equal_to_(SizeOfBitMap)) +_At_(BitMapHeader->Buffer, _Post_equal_to_(BitMapBuffer)) +#endif +NTSYSAPI +VOID +NTAPI +RtlInitializeBitMap( + _Out_ PRTL_BITMAP BitMapHeader, + _In_opt_ __drv_aliasesMem PULONG BitMapBuffer, + _In_opt_ ULONG SizeOfBitMap +); + +NTSYSAPI +ULONG +NTAPI +RtlNumberOfClearBits( + _In_ PRTL_BITMAP BitMapHeader +); + +NTSYSAPI +ULONG +NTAPI +RtlNumberOfSetBits( + _In_ PRTL_BITMAP BitMapHeader +); + +NTSYSAPI +VOID +NTAPI +RtlSetBit( + _In_ PRTL_BITMAP BitMapHeader, + _In_range_(<, BitMapHeader->SizeOfBitMap) ULONG BitNumber +); + +NTSYSAPI +VOID +NTAPI +RtlSetBits( + _In_ PRTL_BITMAP BitMapHeader, + _In_range_(0, BitMapHeader->SizeOfBitMap - NumberToSet) ULONG StartingIndex, + _In_range_(0, BitMapHeader->SizeOfBitMap - StartingIndex) ULONG NumberToSet +); + +NTSYSAPI +VOID +NTAPI +RtlSetAllBits( + _In_ PRTL_BITMAP BitMapHeader +); + +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlTestBit( + _In_ PRTL_BITMAP BitMapHeader, + _In_range_(<, BitMapHeader->SizeOfBitMap) ULONG BitNumber +); + +// +// Timer Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateTimer( + _In_ HANDLE TimerQueue, + _In_ PHANDLE phNewTimer, + _In_ WAITORTIMERCALLBACKFUNC Callback, + _In_ PVOID Parameter, + _In_ ULONG DueTime, + _In_ ULONG Period, + _In_ ULONG Flags +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateTimerQueue(PHANDLE TimerQueue); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteTimer( + _In_ HANDLE TimerQueue, + _In_ HANDLE Timer, + _In_ HANDLE CompletionEvent +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUpdateTimer( + _In_ HANDLE TimerQueue, + _In_ HANDLE Timer, + _In_ ULONG DueTime, + _In_ ULONG Period +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteTimerQueueEx( + _In_ HANDLE TimerQueue, + _In_opt_ HANDLE CompletionEvent +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeleteTimerQueue(HANDLE TimerQueue); + +// +// SList functions +// +PSLIST_ENTRY +FASTCALL +InterlockedPushListSList( + _Inout_ PSLIST_HEADER ListHead, + _Inout_ __drv_aliasesMem PSLIST_ENTRY List, + _Inout_ PSLIST_ENTRY ListEnd, + _In_ ULONG Count +); + +// +// Range List functions +// +NTSYSAPI +VOID +NTAPI +RtlInitializeRangeList( + _Inout_ PRTL_RANGE_LIST RangeList +); + +NTSYSAPI +VOID +NTAPI +RtlFreeRangeList( + _In_ PRTL_RANGE_LIST RangeList +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAddRange( + _Inout_ PRTL_RANGE_LIST RangeList, + _In_ ULONGLONG Start, + _In_ ULONGLONG End, + _In_ UCHAR Attributes, + _In_ ULONG Flags, + _In_opt_ PVOID UserData, + _In_opt_ PVOID Owner +); + +// +// Debug Functions +// +ULONG +__cdecl +DbgPrint( + _In_z_ _Printf_format_string_ PCSTR Format, + ... +); + +NTSYSAPI +ULONG +__cdecl +DbgPrintEx( + _In_ ULONG ComponentId, + _In_ ULONG Level, + _In_z_ _Printf_format_string_ PCSTR Format, + ... +); + +NTSYSAPI +ULONG +NTAPI +DbgPrompt( + _In_z_ PCCH Prompt, + _Out_writes_bytes_(MaximumResponseLength) PCH Response, + _In_ ULONG MaximumResponseLength +); + +#undef DbgBreakPoint +VOID +NTAPI +DbgBreakPoint( + VOID +); + +VOID +NTAPI +DbgLoadImageSymbols( + _In_ PSTRING Name, + _In_ PVOID Base, + _In_ ULONG_PTR ProcessId +); + +VOID +NTAPI +DbgUnLoadImageSymbols( + _In_ PSTRING Name, + _In_ PVOID Base, + _In_ ULONG_PTR ProcessId +); + +VOID +NTAPI +DbgCommandString( + _In_ PCCH Name, + _In_ PCCH Command +); + +// +// Generic Table Functions +// +#if defined(NTOS_MODE_USER) || defined(_NTIFS_) +NTSYSAPI +PVOID +NTAPI +RtlInsertElementGenericTable( + _In_ PRTL_GENERIC_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement +); + +NTSYSAPI +PVOID +NTAPI +RtlInsertElementGenericTableFull( + _In_ PRTL_GENERIC_TABLE Table, + _In_reads_bytes_(BufferSize) PVOID Buffer, + _In_ CLONG BufferSize, + _Out_opt_ PBOOLEAN NewElement, + _In_ PVOID NodeOrParent, + _In_ TABLE_SEARCH_RESULT SearchResult +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsGenericTableEmpty( + _In_ PRTL_GENERIC_TABLE Table +); + +NTSYSAPI +PVOID +NTAPI +RtlLookupElementGenericTableFull( + _In_ PRTL_GENERIC_TABLE Table, + _In_ PVOID Buffer, + _Out_ PVOID *NodeOrParent, + _Out_ TABLE_SEARCH_RESULT *SearchResult +); +#endif + +// +// Handle Table Functions +// +NTSYSAPI +PRTL_HANDLE_TABLE_ENTRY +NTAPI +RtlAllocateHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _Inout_ PULONG Index +); + +NTSYSAPI +VOID +NTAPI +RtlDestroyHandleTable( + _Inout_ PRTL_HANDLE_TABLE HandleTable); + +NTSYSAPI +BOOLEAN +NTAPI +RtlFreeHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _In_ PRTL_HANDLE_TABLE_ENTRY Handle +); + +NTSYSAPI +VOID +NTAPI +RtlInitializeHandleTable( + _In_ ULONG TableSize, + _In_ ULONG HandleSize, + _In_ PRTL_HANDLE_TABLE HandleTable +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlIsValidHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _In_ PRTL_HANDLE_TABLE_ENTRY Handle +); + +_Success_(return!=FALSE) +NTSYSAPI +BOOLEAN +NTAPI +RtlIsValidIndexHandle( + _In_ PRTL_HANDLE_TABLE HandleTable, + _In_ ULONG Index, + _Out_ PRTL_HANDLE_TABLE_ENTRY *Handle +); + +// +// PE Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlFindMessage( + _In_ PVOID BaseAddress, + _In_ ULONG Type, + _In_ ULONG Language, + _In_ ULONG MessageId, + _Out_ PMESSAGE_RESOURCE_ENTRY *MessageResourceEntry +); + +NTSYSAPI +ULONG +NTAPI +RtlGetNtGlobalFlags(VOID); + +_Success_(return!=NULL) +NTSYSAPI +PVOID +NTAPI +RtlImageDirectoryEntryToData( + _In_ PVOID BaseAddress, + _In_ BOOLEAN MappedAsImage, + _In_ USHORT Directory, + _Out_ PULONG Size +); + +NTSYSAPI +PVOID +NTAPI +RtlImageRvaToVa( + _In_ PIMAGE_NT_HEADERS NtHeader, + _In_ PVOID BaseAddress, + _In_ ULONG Rva, + _Inout_opt_ PIMAGE_SECTION_HEADER *SectionHeader +); + +NTSYSAPI +PIMAGE_NT_HEADERS +NTAPI +RtlImageNtHeader( + _In_ PVOID BaseAddress); + +NTSYSAPI +NTSTATUS +NTAPI +RtlImageNtHeaderEx( + _In_ ULONG Flags, + _In_ PVOID BaseAddress, + _In_ ULONGLONG Size, + _Out_ PIMAGE_NT_HEADERS *NtHeader +); + +NTSYSAPI +PIMAGE_SECTION_HEADER +NTAPI +RtlImageRvaToSection( + _In_ PIMAGE_NT_HEADERS NtHeader, + _In_ PVOID BaseAddress, + _In_ ULONG Rva +); + +NTSYSAPI +ULONG +NTAPI +LdrRelocateImageWithBias( + _In_ PVOID NewAddress, + _In_ LONGLONG AdditionalBias, + _In_ PCCH LoaderName, + _In_ ULONG Success, + _In_ ULONG Conflict, + _In_ ULONG Invalid +); + +// +// Activation Context Functions +// +#ifdef NTOS_MODE_USER +NTSYSAPI +NTSTATUS +NTAPI +RtlActivateActivationContextEx( + _In_ ULONG Flags, + _In_ PTEB Teb, + _In_ PVOID Context, + _Out_ PULONG_PTR Cookie +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlActivateActivationContext( + _In_ ULONG Flags, + _In_ HANDLE Handle, + _Out_ PULONG_PTR Cookie +); + +NTSYSAPI +VOID +NTAPI +RtlAddRefActivationContext( + _In_ PVOID Context +); + +NTSYSAPI +PRTL_ACTIVATION_CONTEXT_STACK_FRAME +FASTCALL +RtlActivateActivationContextUnsafeFast( + _In_ PRTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED Frame, + _In_ PVOID Context +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlAllocateActivationContextStack( + _In_ PACTIVATION_CONTEXT_STACK *Stack +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateActivationContext( + _In_ ULONG Flags, + _In_ PACTIVATION_CONTEXT_DATA ActivationContextData, + _In_ ULONG ExtraBytes, + _In_ PVOID NotificationRoutine, + _In_ PVOID NotificationContext, + _Out_ PACTIVATION_CONTEXT *ActCtx +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetActiveActivationContext( + _In_ PVOID *Context +); + +NTSYSAPI +VOID +NTAPI +RtlReleaseActivationContext( + _In_ HANDLE handle +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDeactivateActivationContext( + _In_ ULONG dwFlags, + _In_ ULONG_PTR ulCookie +); + +NTSYSAPI +VOID +NTAPI +RtlFreeActivationContextStack( + _In_ PACTIVATION_CONTEXT_STACK Stack +); + +NTSYSAPI +VOID +NTAPI +RtlFreeThreadActivationContextStack(VOID); + +NTSYSAPI +PRTL_ACTIVATION_CONTEXT_STACK_FRAME +FASTCALL +RtlDeactivateActivationContextUnsafeFast( + _In_ PRTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED Frame +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlDosApplyFileIsolationRedirection_Ustr( + _In_ ULONG Flags, + _In_ PUNICODE_STRING OriginalName, + _In_ PUNICODE_STRING Extension, + _Inout_ PUNICODE_STRING StaticString, + _Inout_ PUNICODE_STRING DynamicString, + _Inout_ PUNICODE_STRING *NewName, + _In_ PULONG NewFlags, + _In_ PSIZE_T FileNameSize, + _In_ PSIZE_T RequiredLength +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlFindActivationContextSectionString( + _In_ ULONG dwFlags, + _In_ const GUID *ExtensionGuid, + _In_ ULONG SectionType, - _In_ PUNICODE_STRING SectionName, ++ _In_ const UNICODE_STRING *SectionName, + _Inout_ PVOID ReturnedData +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryInformationActivationContext( + _In_ DWORD dwFlags, + _In_opt_ PVOID Context, + _In_opt_ PVOID pvSubInstance, + _In_ ULONG ulInfoClass, + _Out_bytecap_(cbBuffer) PVOID pvBuffer, + _In_opt_ SIZE_T cbBuffer, + _Out_opt_ SIZE_T *pcbWrittenOrRequired +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryInformationActiveActivationContext( + _In_ ULONG ulInfoClass, + _Out_bytecap_(cbBuffer) PVOID pvBuffer, + _In_opt_ SIZE_T cbBuffer, + _Out_opt_ SIZE_T *pcbWrittenOrRequired +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlZombifyActivationContext( + PVOID Context +); + +// +// WOW64 Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlWow64EnableFsRedirection( + _In_ BOOLEAN Wow64FsEnableRedirection +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlWow64EnableFsRedirectionEx( + _In_ PVOID Wow64FsEnableRedirection, + _Out_ PVOID *OldFsRedirectionLevel +); + +#endif + +// +// Registry Functions +// +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +NTSTATUS +NTAPI +RtlCheckRegistryKey( + _In_ ULONG RelativeTo, + _In_ PWSTR Path +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateRegistryKey( + _In_ ULONG RelativeTo, + _In_ PWSTR Path +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlFormatCurrentUserKeyPath( + _Out_ _At_(KeyPath->Buffer, __drv_allocatesMem(Mem) _Post_bytecap_(KeyPath->MaximumLength) _Post_bytecount_(KeyPath->Length)) + PUNICODE_STRING KeyPath +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlOpenCurrentUser( + _In_ ACCESS_MASK DesiredAccess, + _Out_ PHANDLE KeyHandle +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryRegistryValues( + _In_ ULONG RelativeTo, + _In_ PCWSTR Path, + _Inout_ _At_(*(*QueryTable).EntryContext, _Pre_unknown_) + PRTL_QUERY_REGISTRY_TABLE QueryTable, + _In_opt_ PVOID Context, + _In_opt_ PVOID Environment +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlWriteRegistryValue( + _In_ ULONG RelativeTo, + _In_ PCWSTR Path, + _In_z_ PCWSTR ValueName, + _In_ ULONG ValueType, + _In_reads_bytes_opt_(ValueLength) PVOID ValueData, + _In_ ULONG ValueLength +); + +#ifdef NTOS_MODE_USER +NTSYSAPI +NTSTATUS +NTAPI +RtlpNtCreateKey( + _Out_ HANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG TitleIndex, + _In_ PUNICODE_STRING Class, + _Out_ PULONG Disposition +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlpNtEnumerateSubKey( + _In_ HANDLE KeyHandle, + _Inout_ PUNICODE_STRING SubKeyName, + _In_ ULONG Index, + _In_ ULONG Unused +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlpNtMakeTemporaryKey( + _In_ HANDLE KeyHandle +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlpNtOpenKey( + _Out_ HANDLE KeyHandle, + _In_ ACCESS_MASK DesiredAccess, + _In_ POBJECT_ATTRIBUTES ObjectAttributes, + _In_ ULONG Unused +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlpNtQueryValueKey( + _In_ HANDLE KeyHandle, + _Out_opt_ PULONG Type, + _Out_opt_ PVOID Data, + _Inout_opt_ PULONG DataLength, + _In_ ULONG Unused +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlpNtSetValueKey( + _In_ HANDLE KeyHandle, + _In_ ULONG Type, + _In_ PVOID Data, + _In_ ULONG DataLength +); +#endif + +// +// NLS Functions +// +NTSYSAPI +VOID +NTAPI +RtlGetDefaultCodePage( + _Out_ PUSHORT AnsiCodePage, + _Out_ PUSHORT OemCodePage +); + +NTSYSAPI +VOID +NTAPI +RtlInitNlsTables( + _In_ PUSHORT AnsiTableBase, + _In_ PUSHORT OemTableBase, + _In_ PUSHORT CaseTableBase, + _Out_ PNLSTABLEINFO NlsTable +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +VOID +NTAPI +RtlInitCodePageTable( + _In_ PUSHORT TableBase, + _Out_ PCPTABLEINFO CodePageTable +); + +NTSYSAPI +VOID +NTAPI +RtlResetRtlTranslations( + _In_ PNLSTABLEINFO NlsTable); + +#if defined(NTOS_MODE_USER) && !defined(NO_RTL_INLINES) + +// +// Misc conversion functions +// +static __inline +LARGE_INTEGER +NTAPI_INLINE +RtlConvertLongToLargeInteger( + _In_ LONG SignedInteger +) +{ + LARGE_INTEGER Result; + + Result.QuadPart = SignedInteger; + return Result; +} + +static __inline +LARGE_INTEGER +NTAPI_INLINE +RtlEnlargedIntegerMultiply( + _In_ LONG Multiplicand, + _In_ LONG Multiplier +) +{ + LARGE_INTEGER Product; + + Product.QuadPart = (LONGLONG)Multiplicand * (ULONGLONG)Multiplier; + return Product; +} + +static __inline +ULONG +NTAPI_INLINE +RtlEnlargedUnsignedDivide( + _In_ ULARGE_INTEGER Dividend, + _In_ ULONG Divisor, + _In_opt_ PULONG Remainder +) +{ + ULONG Quotient; + + Quotient = (ULONG)(Dividend.QuadPart / Divisor); + if (Remainder) { + *Remainder = (ULONG)(Dividend.QuadPart % Divisor); + } + + return Quotient; +} + +static __inline +LARGE_INTEGER +NTAPI_INLINE +RtlEnlargedUnsignedMultiply( + _In_ ULONG Multiplicand, + _In_ ULONG Multiplier +) +{ + LARGE_INTEGER Product; + + Product.QuadPart = (ULONGLONG)Multiplicand * (ULONGLONG)Multiplier; + return Product; +} + +#if defined(_AMD64_) || defined(_IA64_) +static __inline +LARGE_INTEGER +NTAPI_INLINE +RtlExtendedLargeIntegerDivide( + _In_ LARGE_INTEGER Dividend, + _In_ ULONG Divisor, + _Out_opt_ PULONG Remainder) +{ + LARGE_INTEGER ret; + ret.QuadPart = (ULONG64)Dividend.QuadPart / Divisor; + if (Remainder) + *Remainder = (ULONG)(Dividend.QuadPart % Divisor); + return ret; +} + +#else +NTSYSAPI +LARGE_INTEGER +NTAPI +RtlExtendedLargeIntegerDivide( + _In_ LARGE_INTEGER Dividend, + _In_ ULONG Divisor, + _Out_opt_ PULONG Remainder +); + +#endif /* defined(_AMD64_) || defined(_IA64_) */ + +#endif + + +NTSYSAPI +ULONG +NTAPI +RtlUniform( + _In_ PULONG Seed +); + +NTSYSAPI +ULONG +NTAPI +RtlRandom( + _Inout_ PULONG Seed +); + +NTSYSAPI +ULONG +NTAPI +RtlComputeCrc32( + _In_ ULONG InitialCrc, + _In_ PUCHAR Buffer, + _In_ ULONG Length +); + +// +// Network Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv4StringToAddressA( + _In_ PCSTR String, + _In_ BOOLEAN Strict, + _Out_ PCSTR *Terminator, + _Out_ struct in_addr *Addr +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv4StringToAddressW( + _In_ PCWSTR String, + _In_ BOOLEAN Strict, + _Out_ PCWSTR *Terminator, + _Out_ struct in_addr *Addr +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv4StringToAddressExA( + _In_ PCSTR AddressString, + _In_ BOOLEAN Strict, + _Out_ struct in_addr *Address, + _Out_ PUSHORT Port +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv4StringToAddressExW( + _In_ PCWSTR AddressString, + _In_ BOOLEAN Strict, + _Out_ struct in_addr *Address, + _Out_ PUSHORT Port +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv6StringToAddressA( + _In_ PCHAR Name, + _Out_ PCHAR *Terminator, + _Out_ struct in6_addr *Addr +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv6StringToAddressW( + _In_ PWCHAR Name, + _Out_ PCHAR *Terminator, + _Out_ struct in6_addr *Addr +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv6StringToAddressExA( + _In_ PCHAR AddressString, + _In_ struct in6_addr *Address, + _In_ PULONG ScopeId, + _In_ PUSHORT Port +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlIpv6StringToAddressExW( + _In_ PWCHAR AddressName, + _In_ struct in6_addr *Address, + _In_ PULONG ScopeId, + _In_ PUSHORT Port +); + + +// +// Time Functions +// +NTSYSAPI +NTSTATUS +NTAPI +RtlQueryTimeZoneInformation( + _Out_ PRTL_TIME_ZONE_INFORMATION TimeZoneInformation); + +NTSYSAPI +VOID +NTAPI +RtlSecondsSince1970ToTime( + _In_ ULONG SecondsSince1970, + _Out_ PLARGE_INTEGER Time +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSetTimeZoneInformation( + _In_ PRTL_TIME_ZONE_INFORMATION TimeZoneInformation); + +_Success_(return!=FALSE) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlTimeFieldsToTime( + _In_ PTIME_FIELDS TimeFields, + _Out_ PLARGE_INTEGER Time +); + +_Success_(return != 0) +_Must_inspect_result_ +NTSYSAPI +BOOLEAN +NTAPI +RtlTimeToSecondsSince1970( + _In_ PLARGE_INTEGER Time, + _Out_ PULONG ElapsedSeconds +); + +NTSYSAPI +VOID +NTAPI +RtlTimeToTimeFields( + PLARGE_INTEGER Time, + PTIME_FIELDS TimeFields +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlSystemTimeToLocalTime( + _In_ PLARGE_INTEGER SystemTime, + _Out_ PLARGE_INTEGER LocalTime +); + +// +// Version Functions +// +_IRQL_requires_max_(PASSIVE_LEVEL) +_Must_inspect_result_ +NTSYSAPI +NTSTATUS +NTAPI +RtlVerifyVersionInfo( + _In_ PRTL_OSVERSIONINFOEXW VersionInfo, + _In_ ULONG TypeMask, + _In_ ULONGLONG ConditionMask +); + +_IRQL_requires_max_(PASSIVE_LEVEL) +NTSYSAPI +NTSTATUS +NTAPI +RtlGetVersion( + _Out_ + _At_(lpVersionInformation->dwOSVersionInfoSize, _Pre_ _Valid_) + _When_(lpVersionInformation->dwOSVersionInfoSize == sizeof(RTL_OSVERSIONINFOEXW), + _At_((PRTL_OSVERSIONINFOEXW)lpVersionInformation, _Out_)) + PRTL_OSVERSIONINFOW lpVersionInformation +); + +NTSYSAPI +BOOLEAN +NTAPI +RtlGetNtProductType(OUT PNT_PRODUCT_TYPE ProductType); + +// +// Secure Memory Functions +// +#ifdef NTOS_MODE_USER +NTSYSAPI +NTSTATUS +NTAPI +RtlRegisterSecureMemoryCacheCallback( + _In_ PRTL_SECURE_MEMORY_CACHE_CALLBACK Callback); + +NTSYSAPI +BOOLEAN +NTAPI +RtlFlushSecureMemoryCache( + _In_ PVOID MemoryCache, + _In_opt_ SIZE_T MemoryLength +); +#endif + +// +// Boot Status Data Functions +// +#ifdef NTOS_MODE_USER +NTSYSAPI +NTSTATUS +NTAPI +RtlCreateBootStatusDataFile( + VOID +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlGetSetBootStatusData( + _In_ HANDLE FileHandle, + _In_ BOOLEAN WriteMode, + _In_ RTL_BSD_ITEM_TYPE DataClass, + _In_ PVOID Buffer, + _In_ ULONG BufferSize, + _Out_opt_ PULONG ReturnLength +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlLockBootStatusData( + _Out_ PHANDLE FileHandle +); + +NTSYSAPI +NTSTATUS +NTAPI +RtlUnlockBootStatusData( + _In_ HANDLE FileHandle +); +#endif + +#ifdef NTOS_MODE_USER +_Must_inspect_result_ +NTSYSAPI +NTSTATUS +NTAPI +RtlGUIDFromString( + _In_ PUNICODE_STRING GuidString, + _Out_ GUID *Guid); + +_Must_inspect_result_ +NTSYSAPI +NTSTATUS +NTAPI +RtlStringFromGUID( + _In_ REFGUID Guid, + _Out_ _At_(GuidString->Buffer, __drv_allocatesMem(Mem)) + PUNICODE_STRING GuidString); + +NTSYSAPI +NTSTATUS +NTAPI +RtlComputeImportTableHash( + _In_ HANDLE hFile, + _Out_ PCHAR Hash, + _In_ ULONG ImportTableHashRevision +); +#endif + +// +// MemoryStream functions +// +#ifdef NTOS_MODE_USER + +NTSYSAPI +VOID +NTAPI +RtlInitMemoryStream( + _Out_ PRTL_MEMORY_STREAM Stream +); + +NTSYSAPI +VOID +NTAPI +RtlInitOutOfProcessMemoryStream( + _Out_ PRTL_MEMORY_STREAM Stream +); + +NTSYSAPI +VOID +NTAPI +RtlFinalReleaseOutOfProcessMemoryStream( + _In_ PRTL_MEMORY_STREAM Stream +); + +NTSYSAPI +HRESULT +NTAPI +RtlQueryInterfaceMemoryStream( + _In_ struct IStream *This, + _In_ REFIID RequestedIid, + _Outptr_ PVOID *ResultObject +); + +NTSYSAPI +ULONG +NTAPI +RtlAddRefMemoryStream( + _In_ struct IStream *This +); + +NTSYSAPI +ULONG +NTAPI +RtlReleaseMemoryStream( + _In_ struct IStream *This +); + +NTSYSAPI +HRESULT +NTAPI +RtlReadMemoryStream( + _In_ struct IStream *This, + _Out_writes_bytes_(Length) PVOID Buffer, + _In_ ULONG Length, + _Out_opt_ PULONG BytesRead +); + +NTSYSAPI +HRESULT +NTAPI +RtlReadOutOfProcessMemoryStream( + _In_ struct IStream *This, + _Out_writes_bytes_(Length) PVOID Buffer, + _In_ ULONG Length, + _Out_opt_ PULONG BytesRead +); + +NTSYSAPI +HRESULT +NTAPI +RtlSeekMemoryStream( + _In_ struct IStream *This, + _In_ LARGE_INTEGER RelativeOffset, + _In_ ULONG Origin, + _Out_opt_ PULARGE_INTEGER ResultOffset +); + +NTSYSAPI +HRESULT +NTAPI +RtlCopyMemoryStreamTo( + _In_ struct IStream *This, + _In_ struct IStream *Target, + _In_ ULARGE_INTEGER Length, + _Out_opt_ PULARGE_INTEGER BytesRead, + _Out_opt_ PULARGE_INTEGER BytesWritten +); + +NTSYSAPI +HRESULT +NTAPI +RtlCopyOutOfProcessMemoryStreamTo( + _In_ struct IStream *This, + _In_ struct IStream *Target, + _In_ ULARGE_INTEGER Length, + _Out_opt_ PULARGE_INTEGER BytesRead, + _Out_opt_ PULARGE_INTEGER BytesWritten +); + +NTSYSAPI +HRESULT +NTAPI +RtlStatMemoryStream( + _In_ struct IStream *This, + _Out_ struct tagSTATSTG *Stats, + _In_ ULONG Flags +); + +// Dummy functions +NTSYSAPI +HRESULT +NTAPI +RtlWriteMemoryStream( + _In_ struct IStream *This, + _In_reads_bytes_(Length) CONST VOID *Buffer, + _In_ ULONG Length, + _Out_opt_ PULONG BytesWritten +); + +NTSYSAPI +HRESULT +NTAPI +RtlSetMemoryStreamSize( + _In_ struct IStream *This, + _In_ ULARGE_INTEGER NewSize +); + +NTSYSAPI +HRESULT +NTAPI +RtlCommitMemoryStream( + _In_ struct IStream *This, + _In_ ULONG CommitFlags +); + +NTSYSAPI +HRESULT +NTAPI +RtlRevertMemoryStream( + _In_ struct IStream *This +); + +NTSYSAPI +HRESULT +NTAPI +RtlLockMemoryStreamRegion( + _In_ struct IStream *This, + _In_ ULARGE_INTEGER Offset, + _In_ ULARGE_INTEGER Length, + _In_ ULONG LockType +); + +NTSYSAPI +HRESULT +NTAPI +RtlUnlockMemoryStreamRegion( + _In_ struct IStream *This, + _In_ ULARGE_INTEGER Offset, + _In_ ULARGE_INTEGER Length, + _In_ ULONG LockType +); + +NTSYSAPI +HRESULT +NTAPI +RtlCloneMemoryStream( + _In_ struct IStream *This, + _Outptr_ struct IStream **ResultStream +); + +#endif // NTOS_MODE_USER + ++NTSYSAPI ++NTSTATUS ++NTAPI ++RtlFindActivationContextSectionGuid( ++ ULONG flags, ++ const GUID *extguid, ++ ULONG section_kind, ++ const GUID *guid, ++ void *ptr ++); ++ +#ifdef __cplusplus +} +#endif + +#endif diff --cc include/reactos/libs/fast486/fast486.h index 00000000000,339ecb6a77e..339ecb6a77e mode 000000,100644..100644 --- a/include/reactos/libs/fast486/fast486.h +++ b/include/reactos/libs/fast486/fast486.h diff --cc include/reactos/subsys/win/conmsg.h index 4d117a717b6,00000000000..5fc398f5c5f mode 100644,000000..100644 --- a/include/reactos/subsys/win/conmsg.h +++ b/include/reactos/subsys/win/conmsg.h @@@ -1,858 -1,0 +1,872 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Console Server DLL + * FILE: include/reactos/subsys/win/conmsg.h + * PURPOSE: Public definitions for communication + * between Console API Clients and Servers + * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr) + */ + +#ifndef _CONMSG_H +#define _CONMSG_H + +#pragma once + +#define CONSRV_SERVERDLL_INDEX 2 +#define CONSRV_FIRST_API_NUMBER 512 + +// Windows Server 2003 table from http://j00ru.vexillium.org/csrss_list/api_list.html#Windows_2k3 +typedef enum _CONSRV_API_NUMBER +{ + ConsolepOpenConsole = CONSRV_FIRST_API_NUMBER, + ConsolepGetConsoleInput, + ConsolepWriteConsoleInput, + ConsolepReadConsoleOutput, + ConsolepWriteConsoleOutput, + ConsolepReadConsoleOutputString, + ConsolepWriteConsoleOutputString, + ConsolepFillConsoleOutput, + ConsolepGetMode, + ConsolepGetNumberOfFonts, + ConsolepGetNumberOfInputEvents, + ConsolepGetScreenBufferInfo, + ConsolepGetCursorInfo, + ConsolepGetMouseInfo, + ConsolepGetFontInfo, + ConsolepGetFontSize, + ConsolepGetCurrentFont, + ConsolepSetMode, + ConsolepSetActiveScreenBuffer, + ConsolepFlushInputBuffer, + ConsolepGetLargestWindowSize, + ConsolepSetScreenBufferSize, + ConsolepSetCursorPosition, + ConsolepSetCursorInfo, + ConsolepSetWindowInfo, + ConsolepScrollScreenBuffer, + ConsolepSetTextAttribute, + ConsolepSetFont, + ConsolepSetIcon, + ConsolepReadConsole, + ConsolepWriteConsole, + ConsolepDuplicateHandle, + ConsolepGetHandleInformation, + ConsolepSetHandleInformation, + ConsolepCloseHandle, + ConsolepVerifyIoHandle, + ConsolepAlloc, // Not present in Win7 + ConsolepFree, // Not present in Win7 + ConsolepGetTitle, + ConsolepSetTitle, + ConsolepCreateScreenBuffer, + ConsolepInvalidateBitMapRect, + ConsolepVDMOperation, + ConsolepSetCursor, + ConsolepShowCursor, + ConsolepMenuControl, + ConsolepSetPalette, + ConsolepSetDisplayMode, + ConsolepRegisterVDM, + ConsolepGetHardwareState, + ConsolepSetHardwareState, + ConsolepGetDisplayMode, + ConsolepAddAlias, + ConsolepGetAlias, + ConsolepGetAliasesLength, + ConsolepGetAliasExesLength, + ConsolepGetAliases, + ConsolepGetAliasExes, + ConsolepExpungeCommandHistory, + ConsolepSetNumberOfCommands, + ConsolepGetCommandHistoryLength, + ConsolepGetCommandHistory, + ConsolepSetCommandHistoryMode, // Not present in Vista+ + ConsolepGetCP, + ConsolepSetCP, + ConsolepSetKeyShortcuts, + ConsolepSetMenuClose, + ConsolepNotifyLastClose, + ConsolepGenerateCtrlEvent, + ConsolepGetKeyboardLayoutName, + ConsolepGetConsoleWindow, + ConsolepCharType, + ConsolepSetLocalEUDC, + ConsolepSetCursorMode, + ConsolepGetCursorMode, + ConsolepRegisterOS2, + ConsolepSetOS2OemFormat, + ConsolepGetNlsMode, + ConsolepSetNlsMode, + ConsolepRegisterConsoleIME, // Not present in Win7 + ConsolepUnregisterConsoleIME, // Not present in Win7 + // ConsolepQueryConsoleIME, // Added only in Vista and Win2k8, not present in Win7 + ConsolepGetLangId, + ConsolepAttach, // Not present in Win7 + ConsolepGetSelectionInfo, + ConsolepGetProcessList, + + ConsolepGetHistory, // Added in Vista+ + ConsolepSetHistory, // Added in Vista+ + // ConsolepSetCurrentFont, // Added in Vista+ + // ConsolepSetScreenBufferInfo, // Added in Vista+ + // ConsolepClientConnect, // Added in Win7 + + ConsolepMaxApiNumber +} CONSRV_API_NUMBER, *PCONSRV_API_NUMBER; + +// +// See http://msdn.microsoft.com/en-us/library/windows/desktop/bb773359(v=vs.85).aspx +// +typedef struct _CONSOLE_PROPERTIES +{ + WORD wFillAttribute; + WORD wPopupFillAttribute; + + // + // Not on MSDN, but show up in binary + // + WORD wShowWindow; + WORD wUnknown; + + COORD dwScreenBufferSize; + COORD dwWindowSize; + COORD dwWindowOrigin; + DWORD nFont; + DWORD nInputBufferSize; + COORD dwFontSize; + UINT uFontFamily; + UINT uFontWeight; + WCHAR FaceName[LF_FACESIZE]; + UINT uCursorSize; + BOOL bFullScreen; + BOOL bQuickEdit; + BOOL bInsertMode; + BOOL bAutoPosition; + UINT uHistoryBufferSize; + UINT uNumberOfHistoryBuffers; + BOOL bHistoryNoDup; + COLORREF ColorTable[16]; + + //NT_FE_CONSOLE_PROPS + UINT uCodePage; +} CONSOLE_PROPERTIES; + +// +// To minimize code changes, some fields were put here even though they really only belong in +// CONSRV_API_CONNECTINFO. Do not change the ordering however, as it's required for Windows +// compatibility. +// +typedef struct _CONSOLE_START_INFO +{ + INT IconIndex; + HICON IconHandle1; + HICON IconHandle2; + DWORD dwHotKey; + DWORD dwStartupFlags; + CONSOLE_PROPERTIES; + BOOLEAN ConsoleNeeded; // Used for GUI apps only. + LPTHREAD_START_ROUTINE CtrlDispatcher; + LPTHREAD_START_ROUTINE ImeDispatcher; + LPTHREAD_START_ROUTINE PropDispatcher; + ULONG TitleLength; + WCHAR ConsoleTitle[MAX_PATH + 1]; // Console title or full path to the startup shortcut + ULONG DesktopLength; + PWCHAR DesktopPath; + ULONG AppNameLength; + WCHAR AppPath[128]; // Full path of the launched app + ULONG IconPathLength; + WCHAR IconPath[MAX_PATH + 1]; // Path to the file containing the icon +} CONSOLE_START_INFO, *PCONSOLE_START_INFO; + +typedef struct _CONSRV_API_CONNECTINFO +{ + HANDLE ConsoleHandle; + HANDLE InputWaitHandle; + HANDLE InputHandle; + HANDLE OutputHandle; + HANDLE ErrorHandle; + HANDLE Event1; + HANDLE Event2; + /* Adapted from CONSOLE_ALLOCCONSOLE */ + CONSOLE_START_INFO ConsoleStartInfo; +} CONSRV_API_CONNECTINFO, *PCONSRV_API_CONNECTINFO; + +#if defined(_M_IX86) +C_ASSERT(sizeof(CONSRV_API_CONNECTINFO) == 0x638); +#endif + +typedef struct +{ + HANDLE ConsoleHandle; + ULONG ProcessCount; + PDWORD ProcessIdsList; +} CONSOLE_GETPROCESSLIST, *PCONSOLE_GETPROCESSLIST; + +typedef struct +{ + HANDLE ConsoleHandle; + DWORD CtrlEvent; + DWORD ProcessGroupId; +} CONSOLE_GENERATECTRLEVENT, *PCONSOLE_GENERATECTRLEVENT; + +typedef struct +{ + HANDLE ConsoleHandle; +} CONSOLE_NOTIFYLASTCLOSE, *PCONSOLE_NOTIFYLASTCLOSE; + + + +typedef struct +{ + HANDLE OutputHandle; + + BOOL Unicode; + ULONG NrCharactersToWrite; + ULONG NrCharactersWritten; + + ULONG BufferSize; + PVOID Buffer; +} CONSOLE_WRITECONSOLE, *PCONSOLE_WRITECONSOLE; + +typedef struct +{ + HANDLE InputHandle; + + BOOL Unicode; + ULONG NrCharactersToRead; + ULONG NrCharactersRead; + + UNICODE_STRING ExeName; + DWORD CtrlWakeupMask; + DWORD ControlKeyState; + + ULONG BufferSize; + PVOID Buffer; +} CONSOLE_READCONSOLE, *PCONSOLE_READCONSOLE; + +typedef struct +{ + PCONSOLE_START_INFO ConsoleStartInfo; + + HANDLE ConsoleHandle; + HANDLE InputHandle; + HANDLE OutputHandle; + HANDLE ErrorHandle; + HANDLE InputWaitHandle; + LPTHREAD_START_ROUTINE CtrlDispatcher; + LPTHREAD_START_ROUTINE PropDispatcher; +} CONSOLE_ALLOCCONSOLE, *PCONSOLE_ALLOCCONSOLE; + +typedef struct +{ + DWORD ProcessId; // If ProcessId == ATTACH_PARENT_PROCESS == -1, then attach the current process to its parent process console. + HANDLE ConsoleHandle; + HANDLE InputHandle; + HANDLE OutputHandle; + HANDLE ErrorHandle; + HANDLE InputWaitHandle; + LPTHREAD_START_ROUTINE CtrlDispatcher; + LPTHREAD_START_ROUTINE PropDispatcher; +} CONSOLE_ATTACHCONSOLE, *PCONSOLE_ATTACHCONSOLE; + +typedef struct +{ + ULONG Dummy; +} CONSOLE_FREECONSOLE, *PCONSOLE_FREECONSOLE; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + COORD ScreenBufferSize; + COORD CursorPosition; + COORD ViewOrigin; + WORD Attributes; + COORD ViewSize; + COORD MaximumViewSize; +} CONSOLE_GETSCREENBUFFERINFO, *PCONSOLE_GETSCREENBUFFERINFO; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + COORD Position; +} CONSOLE_SETCURSORPOSITION, *PCONSOLE_SETCURSORPOSITION; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + BOOL Show; + INT RefCount; +} CONSOLE_SHOWCURSOR, *PCONSOLE_SHOWCURSOR; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + HCURSOR CursorHandle; +} CONSOLE_SETCURSOR, *PCONSOLE_SETCURSOR; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + CONSOLE_CURSOR_INFO Info; +/* + DWORD Size; + BOOLEAN Visible; +*/ +} CONSOLE_GETSETCURSORINFO, *PCONSOLE_GETSETCURSORINFO; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + WORD Attributes; +} CONSOLE_SETTEXTATTRIB, *PCONSOLE_SETTEXTATTRIB; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE Handle; + DWORD Mode; +} CONSOLE_GETSETCONSOLEMODE, *PCONSOLE_GETSETCONSOLEMODE; + +typedef struct +{ + HANDLE ConsoleHandle; + DWORD DisplayMode; // ModeFlags +} CONSOLE_GETDISPLAYMODE, *PCONSOLE_GETDISPLAYMODE; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + DWORD DisplayMode; // ModeFlags + COORD NewSBDim; + HANDLE EventHandle; +} CONSOLE_SETDISPLAYMODE, *PCONSOLE_SETDISPLAYMODE; + +/* + * Console hardware states. + */ +#define CONSOLE_HARDWARE_STATE_GDI_MANAGED 0 +#define CONSOLE_HARDWARE_STATE_DIRECT 1 + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + DWORD Flags; + DWORD State; +} CONSOLE_GETSETHWSTATE, *PCONSOLE_GETSETHWSTATE; + + + +typedef struct +{ + HANDLE ConsoleHandle; + DWORD DesiredAccess; + BOOL InheritHandle; + DWORD ShareMode; + DWORD ScreenBufferType; /* Type of the screen buffer: CONSOLE_TEXTMODE_BUFFER or CONSOLE_GRAPHICS_BUFFER */ + /* + * This structure holds the initialization information + * for graphics screen buffers. + */ + CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo; + HANDLE hMutex; + PVOID lpBitMap; + HANDLE OutputHandle; /* Handle to newly created screen buffer */ +} CONSOLE_CREATESCREENBUFFER, *PCONSOLE_CREATESCREENBUFFER; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; /* Handle to screen buffer to switch to */ +} CONSOLE_SETACTIVESCREENBUFFER, *PCONSOLE_SETACTIVESCREENBUFFER; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + SMALL_RECT Region; +} CONSOLE_INVALIDATEDIBITS, *PCONSOLE_INVALIDATEDIBITS; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + HPALETTE PaletteHandle; + UINT Usage; +} CONSOLE_SETPALETTE, *PCONSOLE_SETPALETTE; + +typedef struct +{ + HANDLE ConsoleHandle; + DWORD Length; + PVOID Title; + BOOLEAN Unicode; +} CONSOLE_GETSETCONSOLETITLE, *PCONSOLE_GETSETCONSOLETITLE; + +typedef struct +{ + HANDLE OutputHandle; + + BOOL Unicode; + COORD BufferSize; + COORD BufferCoord; + SMALL_RECT WriteRegion; + PCHAR_INFO CharInfo; +} CONSOLE_WRITEOUTPUT, *PCONSOLE_WRITEOUTPUT; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE InputHandle; +} CONSOLE_FLUSHINPUTBUFFER, *PCONSOLE_FLUSHINPUTBUFFER; + +typedef struct +{ - HANDLE OutputHandle; - - BOOL Unicode; ++ HANDLE ConsoleHandle; ++ HANDLE OutputHandle; + SMALL_RECT ScrollRectangle; - BOOL UseClipRectangle; + SMALL_RECT ClipRectangle; - COORD DestinationOrigin; - CHAR_INFO Fill; ++ BOOL UseClipRectangle; ++ COORD DestinationOrigin; ++ CHAR_INFO Fill; ++ BOOLEAN Unicode; +} CONSOLE_SCROLLSCREENBUFFER, *PCONSOLE_SCROLLSCREENBUFFER; + + +/* + * An attribute or a character are instances of the same entity, namely + * a "code" (what would be called an (ANSI) escape sequence). Therefore + * encode them inside the same structure. + */ +typedef enum _CODE_TYPE +{ + CODE_ASCII = 0x01, + CODE_UNICODE = 0x02, + CODE_ATTRIBUTE = 0x03 +} CODE_TYPE; + +typedef struct +{ + HANDLE OutputHandle; + + DWORD NumCodesToRead; + COORD ReadCoord; + COORD EndCoord; + + DWORD CodesRead; + + CODE_TYPE CodeType; + union + { + PVOID pCode; + PCHAR AsciiChar; + PWCHAR UnicodeChar; + PWORD Attribute; + } pCode; // Either a pointer to a character or to an attribute. +} CONSOLE_READOUTPUTCODE, *PCONSOLE_READOUTPUTCODE; + +typedef struct +{ + HANDLE OutputHandle; + + ULONG BufferSize; // Seems unusued + WORD Length; + COORD Coord; + COORD EndCoord; + + ULONG NrCharactersWritten; // Seems unusued + + CODE_TYPE CodeType; + union + { + PVOID pCode; + PCHAR AsciiChar; + PWCHAR UnicodeChar; + PWORD Attribute; + } pCode; // Either a pointer to a character or to an attribute. +} CONSOLE_WRITEOUTPUTCODE, *PCONSOLE_WRITEOUTPUTCODE; + +typedef struct +{ + HANDLE OutputHandle; + + CODE_TYPE CodeType; + union + { + CHAR AsciiChar; + WCHAR UnicodeChar; + WORD Attribute; + } Code; // Either a character or an attribute. + + COORD Coord; + ULONG Length; + + ULONG NrCharactersWritten; // FIXME: Only for chars, is it removable ? +} CONSOLE_FILLOUTPUTCODE, *PCONSOLE_FILLOUTPUTCODE; + +typedef struct +{ + HANDLE InputHandle; + ULONG InputsRead; + PINPUT_RECORD InputRecord; + ULONG Length; + WORD wFlags; + BOOLEAN Unicode; +} CONSOLE_GETINPUT, *PCONSOLE_GETINPUT; + +typedef struct +{ + HANDLE OutputHandle; + + BOOL Unicode; + COORD BufferSize; + COORD BufferCoord; + SMALL_RECT ReadRegion; + PCHAR_INFO CharInfo; +} CONSOLE_READOUTPUT, *PCONSOLE_READOUTPUT; + +typedef struct +{ + HANDLE InputHandle; + DWORD Length; + INPUT_RECORD* InputRecord; + BOOL Unicode; + BOOL AppendToEnd; +} CONSOLE_WRITEINPUT, *PCONSOLE_WRITEINPUT; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE InputHandle; + DWORD NumberOfEvents; +} CONSOLE_GETNUMINPUTEVENTS, *PCONSOLE_GETNUMINPUTEVENTS; + + + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE Handle; +} CONSOLE_CLOSEHANDLE, *PCONSOLE_CLOSEHANDLE; + +typedef struct +{ + BOOL IsValid; + HANDLE ConsoleHandle; + HANDLE Handle; +} CONSOLE_VERIFYHANDLE, *PCONSOLE_VERIFYHANDLE; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE SourceHandle; + DWORD DesiredAccess; + BOOLEAN InheritHandle; + DWORD Options; + HANDLE TargetHandle; +} CONSOLE_DUPLICATEHANDLE, *PCONSOLE_DUPLICATEHANDLE; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE Handle; + DWORD Flags; +} CONSOLE_GETHANDLEINFO, *PCONSOLE_GETHANDLEINFO; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE Handle; + DWORD Mask; + DWORD Flags; +} CONSOLE_SETHANDLEINFO, *PCONSOLE_SETHANDLEINFO; + +/* + * Type of handles. + */ +typedef enum _CONSOLE_HANDLE_TYPE +{ + HANDLE_INPUT = 0x01, + HANDLE_OUTPUT = 0x02 +} CONSOLE_HANDLE_TYPE; + +typedef struct +{ + HANDLE ConsoleHandle; + CONSOLE_HANDLE_TYPE HandleType; + DWORD DesiredAccess; + BOOL InheritHandle; + DWORD ShareMode; + HANDLE Handle; +} CONSOLE_OPENCONSOLE, *PCONSOLE_OPENCONSOLE; + + + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + COORD Size; +} CONSOLE_GETLARGESTWINDOWSIZE, *PCONSOLE_GETLARGESTWINDOWSIZE; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + DWORD CmdIdLow; + DWORD CmdIdHigh; + HMENU MenuHandle; +} CONSOLE_MENUCONTROL, *PCONSOLE_MENUCONTROL; + +typedef struct +{ + HANDLE ConsoleHandle; + BOOL Enable; +} CONSOLE_SETMENUCLOSE, *PCONSOLE_SETMENUCLOSE; + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + BOOL Absolute; + SMALL_RECT WindowRect; // New console window position in the screen-buffer frame (Absolute == TRUE) + // or in the old window position frame (Absolute == FALSE). +} CONSOLE_SETWINDOWINFO, *PCONSOLE_SETWINDOWINFO; + +typedef struct +{ + HANDLE ConsoleHandle; + HWND WindowHandle; +} CONSOLE_GETWINDOW, *PCONSOLE_GETWINDOW; + +typedef struct +{ + HANDLE ConsoleHandle; + HICON IconHandle; +} CONSOLE_SETICON, *PCONSOLE_SETICON; + + + +typedef struct +{ + ULONG SourceLength; + ULONG TargetLength; // Also used for storing the number of bytes written. + ULONG ExeLength; + LPWSTR Source; + LPWSTR Target; + LPWSTR Exe; +} CONSOLE_ADDGETALIAS, *PCONSOLE_ADDGETALIAS; + +typedef struct +{ + DWORD ExeLength; + DWORD AliasesBufferLength; + LPWSTR ExeName; + LPWSTR AliasesBuffer; +} CONSOLE_GETALLALIASES, *PCONSOLE_GETALLALIASES; + +typedef struct +{ - DWORD Length; - DWORD ExeLength; - LPWSTR ExeName; ++ HANDLE ConsoleHandle; ++ USHORT ExeLength; ++ PVOID ExeName; ++ ULONG Length; ++ BOOLEAN Unicode; ++ BOOLEAN Unicode2; +} CONSOLE_GETALLALIASESLENGTH, *PCONSOLE_GETALLALIASESLENGTH; + +typedef struct +{ - DWORD Length; - LPWSTR ExeNames; ++ HANDLE ConsoleHandle; ++ ULONG Length ; // ExeLength; // ExesLength ++ PVOID ExeNames; ++ BOOLEAN Unicode; +} CONSOLE_GETALIASESEXES, *PCONSOLE_GETALIASESEXES; + +typedef struct +{ - DWORD Length; ++ HANDLE ConsoleHandle; ++ ULONG Length; ++ BOOLEAN Unicode; +} CONSOLE_GETALIASESEXESLENGTH, *PCONSOLE_GETALIASESEXESLENGTH; + + + +typedef struct +{ + UNICODE_STRING ExeName; + PWCHAR History; + DWORD Length; +} CONSOLE_GETCOMMANDHISTORY, *PCONSOLE_GETCOMMANDHISTORY; + +typedef struct +{ + UNICODE_STRING ExeName; + DWORD Length; +} CONSOLE_GETCOMMANDHISTORYLENGTH, *PCONSOLE_GETCOMMANDHISTORYLENGTH; + +typedef struct +{ + UNICODE_STRING ExeName; +} CONSOLE_EXPUNGECOMMANDHISTORY, *PCONSOLE_EXPUNGECOMMANDHISTORY; + ++typedef struct ++{ ++ UINT HistoryBufferSize; ++ UINT NumberOfHistoryBuffers; ++ DWORD dwFlags; ++} CONSOLE_GETSETHISTORYINFO, *PCONSOLE_GETSETHISTORYINFO; ++ +typedef struct +{ + UNICODE_STRING ExeName; + DWORD NumCommands; +} CONSOLE_SETHISTORYNUMBERCOMMANDS, *PCONSOLE_SETHISTORYNUMBERCOMMANDS; + +typedef struct +{ - UINT HistoryBufferSize; - UINT NumberOfHistoryBuffers; - DWORD dwFlags; - } CONSOLE_GETSETHISTORYINFO, *PCONSOLE_GETSETHISTORYINFO; ++ HANDLE ConsoleHandle; ++ ULONG Mode; ++} CONSOLE_SETHISTORYMODE, *PCONSOLE_SETHISTORYMODE; + + + +typedef struct +{ + HANDLE ConsoleHandle; + HANDLE OutputHandle; + COORD Size; +} CONSOLE_SETSCREENBUFFERSIZE, *PCONSOLE_SETSCREENBUFFERSIZE; + +typedef struct +{ + HANDLE ConsoleHandle; + CONSOLE_SELECTION_INFO Info; +} CONSOLE_GETSELECTIONINFO, *PCONSOLE_GETSELECTIONINFO; + +typedef struct +{ + HANDLE ConsoleHandle; + UINT CodePage; + BOOL OutputCP; // TRUE : Output Code Page ; FALSE : Input Code Page +} CONSOLE_GETINPUTOUTPUTCP, *PCONSOLE_GETINPUTOUTPUTCP; + +typedef struct +{ + HANDLE ConsoleHandle; + UINT CodePage; + BOOL OutputCP; // TRUE : Output Code Page ; FALSE : Input Code Page + HANDLE EventHandle; +} CONSOLE_SETINPUTOUTPUTCP, *PCONSOLE_SETINPUTOUTPUTCP; + +typedef struct _CONSOLE_API_MESSAGE +{ + PORT_MESSAGE Header; + + PCSR_CAPTURE_BUFFER CsrCaptureData; + CSR_API_NUMBER ApiNumber; + NTSTATUS Status; + ULONG Reserved; + union + { + /* Console initialization and uninitialization */ + CONSOLE_ALLOCCONSOLE AllocConsoleRequest; + CONSOLE_ATTACHCONSOLE AttachConsoleRequest; + CONSOLE_FREECONSOLE FreeConsoleRequest; + + /* Processes */ + CONSOLE_GETPROCESSLIST GetProcessListRequest; + CONSOLE_GENERATECTRLEVENT GenerateCtrlEventRequest; + CONSOLE_NOTIFYLASTCLOSE NotifyLastCloseRequest; + + /* Handles */ + CONSOLE_OPENCONSOLE OpenConsoleRequest; + CONSOLE_CLOSEHANDLE CloseHandleRequest; + CONSOLE_VERIFYHANDLE VerifyHandleRequest; + CONSOLE_DUPLICATEHANDLE DuplicateHandleRequest; + CONSOLE_GETHANDLEINFO GetHandleInfoRequest; + CONSOLE_SETHANDLEINFO SetHandleInfoRequest; + + /* Cursor */ + CONSOLE_SHOWCURSOR ShowCursorRequest; + CONSOLE_SETCURSOR SetCursorRequest; + CONSOLE_GETSETCURSORINFO CursorInfoRequest; + CONSOLE_SETCURSORPOSITION SetCursorPositionRequest; + + /* Screen-buffer */ + CONSOLE_CREATESCREENBUFFER CreateScreenBufferRequest; + CONSOLE_SETACTIVESCREENBUFFER SetScreenBufferRequest; + CONSOLE_GETSCREENBUFFERINFO ScreenBufferInfoRequest; + CONSOLE_SETSCREENBUFFERSIZE SetScreenBufferSizeRequest; + CONSOLE_SCROLLSCREENBUFFER ScrollScreenBufferRequest; + + CONSOLE_GETSELECTIONINFO GetSelectionInfoRequest; + CONSOLE_FLUSHINPUTBUFFER FlushInputBufferRequest; + + /* Console mode */ + CONSOLE_GETSETCONSOLEMODE ConsoleModeRequest; + CONSOLE_GETDISPLAYMODE GetDisplayModeRequest; + CONSOLE_SETDISPLAYMODE SetDisplayModeRequest; + CONSOLE_GETSETHWSTATE HardwareStateRequest; + + /* Console window */ + CONSOLE_INVALIDATEDIBITS InvalidateDIBitsRequest; + CONSOLE_SETPALETTE SetPaletteRequest; + CONSOLE_GETSETCONSOLETITLE TitleRequest; + CONSOLE_GETLARGESTWINDOWSIZE GetLargestWindowSizeRequest; + CONSOLE_MENUCONTROL MenuControlRequest; + CONSOLE_SETMENUCLOSE SetMenuCloseRequest; + CONSOLE_SETWINDOWINFO SetWindowInfoRequest; + CONSOLE_GETWINDOW GetWindowRequest; + CONSOLE_SETICON SetIconRequest; + + /* Read */ + CONSOLE_READCONSOLE ReadConsoleRequest; // SrvReadConsole / ReadConsole + CONSOLE_GETINPUT GetInputRequest; // SrvGetConsoleInput / PeekConsoleInput & ReadConsoleInput + CONSOLE_READOUTPUT ReadOutputRequest; // SrvReadConsoleOutput / ReadConsoleOutput + CONSOLE_READOUTPUTCODE ReadOutputCodeRequest; // SrvReadConsoleOutputString / ReadConsoleOutputAttribute & ReadConsoleOutputCharacter + CONSOLE_GETNUMINPUTEVENTS GetNumInputEventsRequest; + + /* Write */ + CONSOLE_WRITECONSOLE WriteConsoleRequest; // SrvWriteConsole / WriteConsole + CONSOLE_WRITEINPUT WriteInputRequest; + CONSOLE_WRITEOUTPUT WriteOutputRequest; + CONSOLE_WRITEOUTPUTCODE WriteOutputCodeRequest; + + CONSOLE_FILLOUTPUTCODE FillOutputRequest; + CONSOLE_SETTEXTATTRIB SetTextAttribRequest; + + /* Aliases */ + CONSOLE_ADDGETALIAS ConsoleAliasRequest; + CONSOLE_GETALLALIASES GetAllAliasesRequest; + CONSOLE_GETALLALIASESLENGTH GetAllAliasesLengthRequest; + CONSOLE_GETALIASESEXES GetAliasesExesRequest; + CONSOLE_GETALIASESEXESLENGTH GetAliasesExesLengthRequest; + + /* History */ + CONSOLE_GETCOMMANDHISTORY GetCommandHistoryRequest; + CONSOLE_GETCOMMANDHISTORYLENGTH GetCommandHistoryLengthRequest; + CONSOLE_EXPUNGECOMMANDHISTORY ExpungeCommandHistoryRequest; - CONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest; + CONSOLE_GETSETHISTORYINFO HistoryInfoRequest; ++ CONSOLE_SETHISTORYNUMBERCOMMANDS SetHistoryNumberCommandsRequest; ++ CONSOLE_SETHISTORYMODE SetHistoryModeRequest; + + /* Input and Output Code Pages */ + CONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest; + CONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest; + } Data; +} CONSOLE_API_MESSAGE, *PCONSOLE_API_MESSAGE; + +// Check that a CONSOLE_API_MESSAGE can hold in a CSR_API_MESSAGE. +CHECK_API_MSG_SIZE(CONSOLE_API_MESSAGE); + +#endif // _CONMSG_H + +/* EOF */ diff --cc include/reactos/subsys/win/vdm.h index 00000000000,e493dcdc009..e493dcdc009 mode 000000,100644..100644 --- a/include/reactos/subsys/win/vdm.h +++ b/include/reactos/subsys/win/vdm.h diff --cc include/reactos/wine/port.h index 7b42643de67,00000000000..8c075637343 mode 100644,000000..100644 --- a/include/reactos/wine/port.h +++ b/include/reactos/wine/port.h @@@ -1,388 -1,0 +1,388 @@@ +/* + * Wine porting definitions + * + * Copyright 1996 Alexandre Julliard + * + * 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 + */ + +#ifndef __WINE_WINE_PORT_H +#define __WINE_WINE_PORT_H + +#ifndef __WINE_CONFIG_H +# error You must include config.h to use this header +#endif + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE /* for pread/pwrite, isfinite */ +#endif +#include +#include +#include +#include +#ifdef HAVE_DIRECT_H +# include +#endif +#ifdef HAVE_IO_H +# include +#endif +#ifdef HAVE_PROCESS_H +# include +#endif +#include +#ifdef HAVE_UNISTD_H +# include +#endif + + +/**************************************************************** + * Type definitions + */ + +#if !defined(_MSC_VER) && !defined(__int64) +# if defined(__x86_64__) || defined(_WIN64) +# define __int64 long +# else +# define __int64 long long +# endif +#endif + +#if !defined(HAVE_MODE_T) && !defined(_MODE_T) +typedef int mode_t; +#endif +#if !defined(HAVE_OFF_T) && !defined(_OFF_T) +typedef long off_t; +#endif +#if !defined(HAVE_PID_T) && !defined(_PID_T) +typedef int pid_t; +#endif +#if !defined(HAVE_SIZE_T) && !defined(_SIZE_T) +typedef unsigned int size_t; +#endif +#if !defined(HAVE_SSIZE_T) && !defined(_SSIZE_T) +typedef int ssize_t; +#endif +//#ifndef HAVE_SOCKLEN_T +//typedef unsigned int socklen_t; +//#endif + +#ifndef HAVE_STATFS +# ifdef __BEOS__ +# define HAVE_STRUCT_STATFS_F_BFREE +struct statfs { + long f_bsize; /* block_size */ + long f_blocks; /* total_blocks */ + long f_bfree; /* free_blocks */ +}; +# else /* defined(__BEOS__) */ +struct statfs; +# endif /* defined(__BEOS__) */ +#endif /* !defined(HAVE_STATFS) */ + + +/**************************************************************** + * Macro definitions + */ + +#ifdef HAVE_DLFCN_H +#include +#else +#define RTLD_LAZY 0x001 +#define RTLD_NOW 0x002 +#define RTLD_GLOBAL 0x100 +#endif + +#if !defined(HAVE_FTRUNCATE) && defined(HAVE_CHSIZE) +#define ftruncate chsize +#endif + +#if !defined(HAVE_POPEN) && defined(HAVE__POPEN) +#define popen _popen +#endif + +#if !defined(HAVE_PCLOSE) && defined(HAVE__PCLOSE) +#define pclose _pclose +#endif + +#if !defined(HAVE_SNPRINTF) && defined(HAVE__SNPRINTF) +#define snprintf _snprintf +#endif + +#if !defined(HAVE_VSNPRINTF) && defined(HAVE__VSNPRINTF) +#define vsnprintf _vsnprintf +#endif + +#ifndef S_ISLNK +# define S_ISLNK(mod) (0) +#endif /* S_ISLNK */ + +/* So we open files in 64 bit access mode on Linux */ +#ifndef O_LARGEFILE +# define O_LARGEFILE 0 +#endif + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/**************************************************************** + * Constants + */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef M_PI_2 +#define M_PI_2 1.570796326794896619 +#endif + +#ifndef M_PI_4 +#define M_PI_4 0.785398163397448309616 +#endif + +#ifndef INFINITY +static inline float __port_infinity(void) +{ + static const unsigned __inf_bytes = 0x7f800000; + return *(const float *)&__inf_bytes; +} +#define INFINITY __port_infinity() +#endif + +#ifndef NAN +static inline float __port_nan(void) +{ + static const unsigned __nan_bytes = 0x7fc00000; + return *(const float *)&__nan_bytes; +} +#define NAN __port_nan() +#endif + +/* Constructor functions */ + +#ifdef _MSC_VER +# define DECL_GLOBAL_CONSTRUCTOR(func) /* nothing */ +#elif defined(__GNUC__) +# define DECL_GLOBAL_CONSTRUCTOR(func) \ + static void func(void) __attribute__((constructor)); \ + static void func(void) +#elif defined(__i386__) +# define DECL_GLOBAL_CONSTRUCTOR(func) \ + static void __dummy_init_##func(void) { \ + asm(".section .init,\"ax\"\n\t" \ + "call " #func "\n\t" \ + ".previous"); } \ + static void func(void) +#elif defined(__sparc__) +# define DECL_GLOBAL_CONSTRUCTOR(func) \ + static void __dummy_init_##func(void) { \ + asm("\t.section \".init\",#alloc,#execinstr\n" \ + "\tcall " #func "\n" \ + "\tnop\n" \ + "\t.section \".text\",#alloc,#execinstr\n" ); } \ + static void func(void) +#elif defined(_M_AMD64) +#pragma message("You must define the DECL_GLOBAL_CONSTRUCTOR macro for amd64") +#else +# error You must define the DECL_GLOBAL_CONSTRUCTOR macro for your platform +#endif + + +/* Register functions */ + +#ifdef __i386__ +#define DEFINE_REGS_ENTRYPOINT( name, fn, args, pop_args ) \ + __ASM_GLOBAL_FUNC( name, \ + "call " __ASM_NAME("__wine_call_from_32_regs") "\n\t" \ + ".long " __ASM_NAME(#fn) "\n\t" \ + ".byte " #args "," #pop_args ) +/* FIXME: add support for other CPUs */ +#endif /* __i386__ */ + + +/**************************************************************** + * Function definitions (only when using libwine_port) + */ + +#ifndef NO_LIBWINE_PORT + +#ifndef HAVE_GETOPT_LONG +extern char *optarg; +extern int optind; +extern int opterr; +extern int optopt; +struct option; + +#ifndef HAVE_STRUCT_OPTION_NAME +struct option +{ + const char *name; + int has_arg; + int *flag; + int val; +}; +#endif + +extern int getopt_long (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int ___argc, char *const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); +#endif /* HAVE_GETOPT_LONG */ + +#ifndef HAVE_GETPAGESIZE +size_t getpagesize(void); +#endif /* HAVE_GETPAGESIZE */ + +#if !defined(HAVE_ISFINITE) && !defined(isfinite) +int isfinite(double x); +#endif + +#if !defined(HAVE_ISINF) && !defined(isinf) +int isinf(double x); +#endif + +#if !defined(HAVE_ISNAN) && !defined(isnan) +int isnan(double x); +#endif + +#ifndef HAVE_LSTAT +int lstat(const char *file_name, struct stat *buf); +#endif /* HAVE_LSTAT */ + +#ifndef HAVE_MEMMOVE +void *memmove(void *dest, const void *src, size_t len); +#endif /* !defined(HAVE_MEMMOVE) */ + +#ifndef __REACTOS__ +#ifndef HAVE_PREAD +ssize_t pread( int fd, void *buf, size_t count, off_t offset ); +#endif /* HAVE_PREAD */ + +#ifndef HAVE_PWRITE +ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset ); +#endif /* HAVE_PWRITE */ +#endif /* __REACTOS__ */ + +#ifdef WIN32 +#ifndef HAVE_SIGSETJMP +# include +typedef jmp_buf sigjmp_buf; +int sigsetjmp( sigjmp_buf buf, int savesigs ); +void siglongjmp( sigjmp_buf buf, int val ); +#endif /* HAVE_SIGSETJMP */ +#endif + +#ifndef HAVE_STATFS +int statfs(const char *name, struct statfs *info); +#endif /* !defined(HAVE_STATFS) */ + +#ifndef HAVE_STRNCASECMP +# ifndef HAVE__STRNICMP +int strncasecmp(const char *str1, const char *str2, size_t n); +# else +# define strncasecmp _strnicmp +# endif +#endif /* !defined(HAVE_STRNCASECMP) */ + +#ifndef HAVE_STRERROR +const char *strerror(int err); +#endif /* !defined(HAVE_STRERROR) */ + +#ifndef HAVE_STRCASECMP +# ifndef HAVE__STRICMP +int strcasecmp(const char *str1, const char *str2); +# else +# define strcasecmp _stricmp +# endif +#endif /* !defined(HAVE_STRCASECMP) */ + +#if !defined(HAVE_USLEEP) && !defined(__CYGWIN__) +int usleep (unsigned int useconds); +#endif /* !defined(HAVE_USLEEP) */ + +#ifdef __i386__ +static inline void *memcpy_unaligned( void *dst, const void *src, size_t size ) +{ + return memcpy( dst, src, size ); +} +#else +extern void *memcpy_unaligned( void *dst, const void *src, size_t size ); +#endif /* __i386__ */ + +extern int mkstemps(char *template, int suffix_len); + +/* Process creation flags */ +#ifndef _P_WAIT +# define _P_WAIT 0 +# define _P_NOWAIT 1 +# define _P_OVERLAY 2 +# define _P_NOWAITO 3 +# define _P_DETACH 4 +#endif +#ifndef HAVE_SPAWNVP +extern int spawnvp(int mode, const char *cmdname, const char * const argv[]); +#endif + +/* Interlocked functions */ + +#if defined(_MSC_VER) || (defined(__i386__) && defined(__GNUC__) && !defined(WINE_PORT_NO_INTERLOCKED)) + +#define interlocked_cmpxchg InterlockedCompareExchange - #define interlocked_cmpxchg_ptr InterlockedCompareExchangePtr ++#define interlocked_cmpxchg_ptr InterlockedCompareExchangePointer +#define interlocked_xchg InterlockedExchange - #define interlocked_xchg_ptr InterlockedExchangePtr ++#define interlocked_xchg_ptr InterlockedExchangePointer +#define interlocked_xchg_add InterlockedExchangeAdd + + +#endif /* __i386___ && __GNUC__ */ + +#if defined(_MSC_VER) +__forceinline +int +ffs(int mask) +{ + long index; + if (_BitScanForward(&index, mask) == 0) return 0; + return index; +} +#else +#define ffs __builtin_ffs +#endif + +#else /* NO_LIBWINE_PORT */ + +#define __WINE_NOT_PORTABLE(func) func##_is_not_portable func##_is_not_portable + +#define getopt_long __WINE_NOT_PORTABLE(getopt_long) +#define getopt_long_only __WINE_NOT_PORTABLE(getopt_long_only) +#define getpagesize __WINE_NOT_PORTABLE(getpagesize) +#define lstat __WINE_NOT_PORTABLE(lstat) +#define memcpy_unaligned __WINE_NOT_PORTABLE(memcpy_unaligned) +#define memmove __WINE_NOT_PORTABLE(memmove) +#define pread __WINE_NOT_PORTABLE(pread) +#define pwrite __WINE_NOT_PORTABLE(pwrite) +#define spawnvp __WINE_NOT_PORTABLE(spawnvp) +#define statfs __WINE_NOT_PORTABLE(statfs) +#define strcasecmp __WINE_NOT_PORTABLE(strcasecmp) +#define strerror __WINE_NOT_PORTABLE(strerror) +#define strncasecmp __WINE_NOT_PORTABLE(strncasecmp) +#define usleep __WINE_NOT_PORTABLE(usleep) + +#endif /* NO_LIBWINE_PORT */ + +#endif /* !defined(__WINE_WINE_PORT_H) */ diff --cc lib/fast486/CMakeLists.txt index 00000000000,2fbdb31586f..2fbdb31586f mode 000000,100644..100644 --- a/lib/fast486/CMakeLists.txt +++ b/lib/fast486/CMakeLists.txt diff --cc lib/fast486/COPYING index 00000000000,d159169d105..d159169d105 mode 000000,100644..100644 --- a/lib/fast486/COPYING +++ b/lib/fast486/COPYING diff --cc lib/fast486/common.c index 00000000000,36f026ec331..36f026ec331 mode 000000,100644..100644 --- a/lib/fast486/common.c +++ b/lib/fast486/common.c diff --cc lib/fast486/common.h index 00000000000,1bfdb311ef2..1bfdb311ef2 mode 000000,100644..100644 --- a/lib/fast486/common.h +++ b/lib/fast486/common.h diff --cc lib/fast486/common.inl index 00000000000,fff8fc01474..fff8fc01474 mode 000000,100644..100644 --- a/lib/fast486/common.inl +++ b/lib/fast486/common.inl diff --cc lib/fast486/extraops.c index 00000000000,f9fe386a638..f9fe386a638 mode 000000,100644..100644 --- a/lib/fast486/extraops.c +++ b/lib/fast486/extraops.c diff --cc lib/fast486/extraops.h index 00000000000,eab8e28fb94..eab8e28fb94 mode 000000,100644..100644 --- a/lib/fast486/extraops.h +++ b/lib/fast486/extraops.h diff --cc lib/fast486/fast486.c index 00000000000,c47814bb526..c47814bb526 mode 000000,100644..100644 --- a/lib/fast486/fast486.c +++ b/lib/fast486/fast486.c diff --cc lib/fast486/fpu.c index 00000000000,18f4a892afc..18f4a892afc mode 000000,100644..100644 --- a/lib/fast486/fpu.c +++ b/lib/fast486/fpu.c diff --cc lib/fast486/fpu.h index 00000000000,63128a42599..63128a42599 mode 000000,100644..100644 --- a/lib/fast486/fpu.h +++ b/lib/fast486/fpu.h diff --cc lib/fast486/opcodes.c index 00000000000,3dffdaacc18..3dffdaacc18 mode 000000,100644..100644 --- a/lib/fast486/opcodes.c +++ b/lib/fast486/opcodes.c diff --cc lib/fast486/opcodes.h index 00000000000,3d29e4794c7..3d29e4794c7 mode 000000,100644..100644 --- a/lib/fast486/opcodes.h +++ b/lib/fast486/opcodes.h diff --cc lib/fast486/opgroups.c index 00000000000,264fa66ba2c..264fa66ba2c mode 000000,100644..100644 --- a/lib/fast486/opgroups.c +++ b/lib/fast486/opgroups.c diff --cc lib/fast486/opgroups.h index 00000000000,bf55cce9c3f..bf55cce9c3f mode 000000,100644..100644 --- a/lib/fast486/opgroups.h +++ b/lib/fast486/opgroups.h diff --cc lib/rtl/actctx.c index ff625ec8c26,00000000000..bf425a9f530 mode 100644,000000..100644 --- a/lib/rtl/actctx.c +++ b/lib/rtl/actctx.c @@@ -1,3156 -1,0 +1,5340 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Runtime Library + * PURPOSE: Activation Context Support + * FILE: lib/rtl/actctx.c + * PROGRAMERS: + * Jon Griffiths + * Eric Pouech + * Jacek Caban for CodeWeavers + * Alexandre Julliard + * Stefan Ginsberg (stefan__100__@hotmail.com) + * Samuel Serapión + */ + - /* Based on Wine 1.1.26 */ ++/* Based on Wine 1.7.17 */ + +#include + +#define NDEBUG +#include + +#include + +BOOLEAN RtlpNotAllowingMultipleActivation; + +#define ACTCTX_FLAGS_ALL (\ - ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID |\ - ACTCTX_FLAG_LANGID_VALID |\ - ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID |\ - ACTCTX_FLAG_RESOURCE_NAME_VALID |\ - ACTCTX_FLAG_SET_PROCESS_DEFAULT |\ - ACTCTX_FLAG_APPLICATION_NAME_VALID |\ - ACTCTX_FLAG_SOURCE_IS_ASSEMBLYREF |\ - ACTCTX_FLAG_HMODULE_VALID ) ++ ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID |\ ++ ACTCTX_FLAG_LANGID_VALID |\ ++ ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID |\ ++ ACTCTX_FLAG_RESOURCE_NAME_VALID |\ ++ ACTCTX_FLAG_SET_PROCESS_DEFAULT |\ ++ ACTCTX_FLAG_APPLICATION_NAME_VALID |\ ++ ACTCTX_FLAG_SOURCE_IS_ASSEMBLYREF |\ ++ ACTCTX_FLAG_HMODULE_VALID ) ++ ++#define STRSECTION_MAGIC 0x64487353 /* dHsS */ ++#define GUIDSECTION_MAGIC 0x64487347 /* dHsG */ + +#define ACTCTX_MAGIC_MARKER (PVOID)'gMcA' + +#define ACTCTX_FAKE_HANDLE ((HANDLE) 0xf00baa) +#define ACTCTX_FAKE_COOKIE ((ULONG_PTR) 0xf00bad) + + + +typedef struct +{ + const WCHAR *ptr; + unsigned int len; +} xmlstr_t; + +typedef struct +{ + const WCHAR *ptr; + const WCHAR *end; +} xmlbuf_t; + +struct file_info +{ + ULONG type; + WCHAR *info; +}; + +struct assembly_version +{ + USHORT major; + USHORT minor; + USHORT build; + USHORT revision; +}; + +struct assembly_identity +{ + WCHAR *name; + WCHAR *arch; + WCHAR *public_key; + WCHAR *language; + WCHAR *type; + struct assembly_version version; + BOOL optional; +}; + ++struct strsection_header ++{ ++ DWORD magic; ++ ULONG size; ++ DWORD unk1[3]; ++ ULONG count; ++ ULONG index_offset; ++ DWORD unk2[2]; ++ ULONG global_offset; ++ ULONG global_len; ++}; ++ ++struct string_index ++{ ++ ULONG hash; /* key string hash */ ++ ULONG name_offset; ++ ULONG name_len; ++ ULONG data_offset; /* redirect data offset */ ++ ULONG data_len; ++ ULONG rosterindex; ++}; ++ ++struct guidsection_header ++{ ++ DWORD magic; ++ ULONG size; ++ DWORD unk[3]; ++ ULONG count; ++ ULONG index_offset; ++ DWORD unk2; ++ ULONG names_offset; ++ ULONG names_len; ++}; ++ ++struct guid_index ++{ ++ GUID guid; ++ ULONG data_offset; ++ ULONG data_len; ++ ULONG rosterindex; ++}; ++ ++struct wndclass_redirect_data ++{ ++ ULONG size; ++ DWORD res; ++ ULONG name_len; ++ ULONG name_offset; /* versioned name offset */ ++ ULONG module_len; ++ ULONG module_offset;/* container name offset */ ++}; ++ ++struct dllredirect_data ++{ ++ ULONG size; ++ ULONG unk; ++ DWORD res[3]; ++}; ++ ++struct tlibredirect_data ++{ ++ ULONG size; ++ DWORD res; ++ ULONG name_len; ++ ULONG name_offset; ++ LANGID langid; ++ WORD flags; ++ ULONG help_len; ++ ULONG help_offset; ++ WORD major_version; ++ WORD minor_version; ++}; ++ ++enum comclass_threadingmodel ++{ ++ ThreadingModel_Apartment = 1, ++ ThreadingModel_Free = 2, ++ ThreadingModel_No = 3, ++ ThreadingModel_Both = 4, ++ ThreadingModel_Neutral = 5 ++}; ++ ++enum comclass_miscfields ++{ ++ MiscStatus = 1, ++ MiscStatusIcon = 2, ++ MiscStatusContent = 4, ++ MiscStatusThumbnail = 8, ++ MiscStatusDocPrint = 16 ++}; ++ ++struct comclassredirect_data ++{ ++ ULONG size; ++ BYTE res; ++ BYTE miscmask; ++ BYTE res1[2]; ++ DWORD model; ++ GUID clsid; ++ GUID alias; ++ GUID clsid2; ++ GUID tlbid; ++ ULONG name_len; ++ ULONG name_offset; ++ ULONG progid_len; ++ ULONG progid_offset; ++ ULONG clrdata_len; ++ ULONG clrdata_offset; ++ DWORD miscstatus; ++ DWORD miscstatuscontent; ++ DWORD miscstatusthumbnail; ++ DWORD miscstatusicon; ++ DWORD miscstatusdocprint; ++}; ++ ++enum ifaceps_mask ++{ ++ NumMethods = 1, ++ BaseIface = 2 ++}; ++ ++struct ifacepsredirect_data ++{ ++ ULONG size; ++ DWORD mask; ++ GUID iid; ++ ULONG nummethods; ++ GUID tlbid; ++ GUID base; ++ ULONG name_len; ++ ULONG name_offset; ++}; ++ ++struct clrsurrogate_data ++{ ++ ULONG size; ++ DWORD res; ++ GUID clsid; ++ ULONG version_offset; ++ ULONG version_len; ++ ULONG name_offset; ++ ULONG name_len; ++}; ++ ++struct clrclass_data ++{ ++ ULONG size; ++ DWORD res[2]; ++ ULONG module_len; ++ ULONG module_offset; ++ ULONG name_len; ++ ULONG name_offset; ++ ULONG version_len; ++ ULONG version_offset; ++ DWORD res2[2]; ++}; ++ ++struct progidredirect_data ++{ ++ ULONG size; ++ DWORD reserved; ++ ULONG clsid_offset; ++}; ++ ++/* ++ ++ Sections structure. ++ ++ Sections are accessible by string or guid key, that defines two types of sections. ++ All sections of each type have same magic value and header structure, index ++ data could be of two possible types too. So every string based section uses ++ the same index format, same applies to guid sections - they share same guid index ++ format. ++ ++ - window class redirection section is a plain buffer with following format: ++ ++
++ ++ --- ++ ++ ++ ++ ++ Header is fixed length structure - struct strsection_header, ++ contains redirected classes count; ++ ++ Index is an array of fixed length index records, each record is ++ struct string_index. ++ ++ All strings in data itself are WCHAR, null terminated, 4-bytes aligned. ++ ++ Versioned name offset is relative to redirect data structure (struct wndclass_redirect_data), ++ others are relative to section itself. ++ ++ - dll redirect section format: ++ ++
++ ++ --- ++ ++ ++ This section doesn't seem to carry any payload data except dll names. ++ ++ - typelib section format: ++ ++
++ ++ ++ --- ++ ++ ++ Header is fixed length, index is an array of fixed length 'struct guid_index'. ++ All strings are WCHAR, null terminated, 4-bytes aligned. Module names part is ++ 4-bytes aligned as a whole. ++ ++ Module name offsets are relative to section, helpstring offset is relative to data ++ structure itself. ++ ++ - comclass section format: ++ ++
++ ++ ++ --- --- ++ ++ ++ ++ ++ ++ This section uses two index records per comclass, one entry contains original guid ++ as specified by context, another one has a generated guid. Index and strings handling ++ is similar to typelib sections. ++ ++ For CLR classes additional data is stored after main COM class data, it contains ++ class name and runtime version string, see 'struct clrclass_data'. ++ ++ Module name offsets are relative to section, progid offset is relative to data ++ structure itself. ++ ++ - COM interface section format: ++ ++
++ ++ --- ++ ++ ++ Interface section contains data for proxy/stubs and external proxy/stubs. External ++ ones are defined at assembly level, so this section has no module information. ++ All records are indexed with 'iid' value from manifest. There an exception for ++ external variants - if 'proxyStubClsid32' is specified, it's stored as iid in ++ redirect data, but index is still 'iid' from manifest. ++ ++ Interface name offset is relative to data structure itself. ++ ++ - CLR surrogates section format: ++ ++
++ ++ --- ++ ++ ++ ++ There's nothing special about this section, same way to store strings is used, ++ no modules part as it belongs to assembly level, not a file. ++ ++ - ProgID section format: ++ ++
++ ++ ++ --- ++ ++ ++ This sections uses generated alias guids from COM server section. This way ++ ProgID -> CLSID mapping returns generated guid, not the real one. ProgID string ++ is stored too, aligned. ++*/ ++ ++struct progids ++{ ++ WCHAR **progids; ++ unsigned int num; ++ unsigned int allocated; ++}; ++ +struct entity +{ + DWORD kind; + union + { + struct + { + WCHAR *tlbid; - WCHAR *version; + WCHAR *helpdir; - } typelib; ++ WORD flags; ++ WORD major; ++ WORD minor; ++ } typelib; + struct + { + WCHAR *clsid; - } comclass; - struct { ++ WCHAR *tlbid; ++ WCHAR *progid; ++ WCHAR *name; /* clrClass: class name */ ++ WCHAR *version; /* clrClass: CLR runtime version */ ++ DWORD model; ++ DWORD miscstatus; ++ DWORD miscstatuscontent; ++ DWORD miscstatusthumbnail; ++ DWORD miscstatusicon; ++ DWORD miscstatusdocprint; ++ struct progids progids; ++ } comclass; ++ struct { + WCHAR *iid; ++ WCHAR *base; ++ WCHAR *tlib; + WCHAR *name; - } proxy; ++ WCHAR *ps32; /* only stored for 'comInterfaceExternalProxyStub' */ ++ DWORD mask; ++ ULONG nummethods; ++ } ifaceps; + struct + { + WCHAR *name; ++ BOOL versioned; + } class; + struct + { + WCHAR *name; + WCHAR *clsid; - } clrclass; - struct - { - WCHAR *name; - WCHAR *clsid; ++ WCHAR *version; + } clrsurrogate; + } u; +}; + +struct entity_array +{ + struct entity *base; + unsigned int num; + unsigned int allocated; +}; + +struct dll_redirect +{ + WCHAR *name; + WCHAR *hash; + struct entity_array entities; +}; + +enum assembly_type +{ + APPLICATION_MANIFEST, + ASSEMBLY_MANIFEST, + ASSEMBLY_SHARED_MANIFEST, +}; + +struct assembly +{ + enum assembly_type type; + struct assembly_identity id; + struct file_info manifest; + WCHAR *directory; + BOOL no_inherit; + struct dll_redirect *dlls; + unsigned int num_dlls; + unsigned int allocated_dlls; + struct entity_array entities; +}; + ++enum context_sections ++{ ++ WINDOWCLASS_SECTION = 1, ++ DLLREDIRECT_SECTION = 2, ++ TLIBREDIRECT_SECTION = 4, ++ SERVERREDIRECT_SECTION = 8, ++ IFACEREDIRECT_SECTION = 16, ++ CLRSURROGATES_SECTION = 32, ++ PROGIDREDIRECT_SECTION = 64 ++}; ++ +typedef struct _ASSEMBLY_STORAGE_MAP_ENTRY +{ + ULONG Flags; + UNICODE_STRING DosPath; + HANDLE Handle; +} ASSEMBLY_STORAGE_MAP_ENTRY, *PASSEMBLY_STORAGE_MAP_ENTRY; + +typedef struct _ASSEMBLY_STORAGE_MAP +{ + ULONG Flags; + ULONG AssemblyCount; + PASSEMBLY_STORAGE_MAP_ENTRY *AssemblyArray; +} ASSEMBLY_STORAGE_MAP, *PASSEMBLY_STORAGE_MAP; + +typedef struct _ACTIVATION_CONTEXT +{ + LONG RefCount; + ULONG Flags; + LIST_ENTRY Links; + PACTIVATION_CONTEXT_DATA ActivationContextData; + PVOID NotificationRoutine; + PVOID NotificationContext; + ULONG SentNotifications[8]; + ULONG DisabledNotifications[8]; + ASSEMBLY_STORAGE_MAP StorageMap; + PASSEMBLY_STORAGE_MAP_ENTRY InlineStorageMapEntries; + ULONG StackTraceIndex; + PVOID StackTraces[4][4]; + struct file_info config; + struct file_info appdir; + struct assembly *assemblies; + unsigned int num_assemblies; + unsigned int allocated_assemblies; ++ /* section data */ ++ DWORD sections; ++ struct strsection_header *wndclass_section; ++ struct strsection_header *dllredirect_section; ++ struct strsection_header *progid_section; ++ struct guidsection_header *tlib_section; ++ struct guidsection_header *comserver_section; ++ struct guidsection_header *ifaceps_section; ++ struct guidsection_header *clrsurrogate_section; +} ACTIVATION_CONTEXT, *PIACTIVATION_CONTEXT; + +struct actctx_loader +{ + ACTIVATION_CONTEXT *actctx; + struct assembly_identity *dependencies; + unsigned int num_dependencies; + unsigned int allocated_dependencies; +}; + +static const WCHAR asmv1W[] = {'a','s','m','v','1',':',0}; +static const WCHAR asmv2W[] = {'a','s','m','v','2',':',0}; + +typedef struct _ACTIVATION_CONTEXT_WRAPPED +{ + PVOID MagicMarker; + ACTIVATION_CONTEXT ActivationContext; +} ACTIVATION_CONTEXT_WRAPPED, *PACTIVATION_CONTEXT_WRAPPED; + +VOID +NTAPI +RtlpSxsBreakOnInvalidMarker(IN PACTIVATION_CONTEXT ActCtx, + IN ULONG FailureCode) +{ + EXCEPTION_RECORD ExceptionRecord; + + /* Fatal SxS exception header */ + ExceptionRecord.ExceptionRecord = NULL; + ExceptionRecord.ExceptionCode = STATUS_SXS_CORRUPTION; + ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + + /* With SxS-specific information plus the context itself */ + ExceptionRecord.ExceptionInformation[0] = 1; + ExceptionRecord.ExceptionInformation[1] = FailureCode; + ExceptionRecord.ExceptionInformation[2] = (ULONG_PTR)ActCtx; + ExceptionRecord.NumberParameters = 3; + + /* Raise it */ + RtlRaiseException(&ExceptionRecord); +} + +FORCEINLINE +VOID +RtlpValidateActCtx(IN PACTIVATION_CONTEXT ActCtx) +{ + PACTIVATION_CONTEXT_WRAPPED pActual; + + /* Get the caller-opaque header */ + pActual = CONTAINING_RECORD(ActCtx, + ACTIVATION_CONTEXT_WRAPPED, + ActivationContext); + + /* Check if the header matches as expected */ + if (pActual->MagicMarker != ACTCTX_MAGIC_MARKER) + { + /* Nope, print out a warning, assert, and then throw an exception */ + DbgPrint("%s : Invalid activation context marker %p found in activation context %p\n" + " This means someone stepped on the allocation, or someone is using a\n" + " deallocated activation context\n", + __FUNCTION__, + pActual->MagicMarker, + ActCtx); + ASSERT(pActual->MagicMarker == ACTCTX_MAGIC_MARKER); + RtlpSxsBreakOnInvalidMarker(ActCtx, 1); + } +} + +static const WCHAR assemblyW[] = {'a','s','s','e','m','b','l','y',0}; +static const WCHAR assemblyIdentityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0}; +static const WCHAR bindingRedirectW[] = {'b','i','n','d','i','n','g','R','e','d','i','r','e','c','t',0}; +static const WCHAR clrClassW[] = {'c','l','r','C','l','a','s','s',0}; +static const WCHAR clrSurrogateW[] = {'c','l','r','S','u','r','r','o','g','a','t','e',0}; +static const WCHAR comClassW[] = {'c','o','m','C','l','a','s','s',0}; +static const WCHAR comInterfaceExternalProxyStubW[] = {'c','o','m','I','n','t','e','r','f','a','c','e','E','x','t','e','r','n','a','l','P','r','o','x','y','S','t','u','b',0}; +static const WCHAR comInterfaceProxyStubW[] = {'c','o','m','I','n','t','e','r','f','a','c','e','P','r','o','x','y','S','t','u','b',0}; +static const WCHAR dependencyW[] = {'d','e','p','e','n','d','e','n','c','y',0}; +static const WCHAR dependentAssemblyW[] = {'d','e','p','e','n','d','e','n','t','A','s','s','e','m','b','l','y',0}; +static const WCHAR descriptionW[] = {'d','e','s','c','r','i','p','t','i','o','n',0}; +static const WCHAR fileW[] = {'f','i','l','e',0}; +static const WCHAR asmv2hashW[] = {'a','s','m','v','2',':','h','a','s','h',0}; +static const WCHAR noInheritW[] = {'n','o','I','n','h','e','r','i','t',0}; +static const WCHAR noInheritableW[] = {'n','o','I','n','h','e','r','i','t','a','b','l','e',0}; +static const WCHAR typelibW[] = {'t','y','p','e','l','i','b',0}; +static const WCHAR windowClassW[] = {'w','i','n','d','o','w','C','l','a','s','s',0}; + +static const WCHAR clsidW[] = {'c','l','s','i','d',0}; +static const WCHAR hashW[] = {'h','a','s','h',0}; +static const WCHAR hashalgW[] = {'h','a','s','h','a','l','g',0}; +static const WCHAR helpdirW[] = {'h','e','l','p','d','i','r',0}; +static const WCHAR iidW[] = {'i','i','d',0}; +static const WCHAR languageW[] = {'l','a','n','g','u','a','g','e',0}; +static const WCHAR manifestVersionW[] = {'m','a','n','i','f','e','s','t','V','e','r','s','i','o','n',0}; +static const WCHAR g_nameW[] = {'n','a','m','e',0}; ++static const WCHAR neutralW[] = {'n','e','u','t','r','a','l',0}; +static const WCHAR newVersionW[] = {'n','e','w','V','e','r','s','i','o','n',0}; +static const WCHAR oldVersionW[] = {'o','l','d','V','e','r','s','i','o','n',0}; +static const WCHAR optionalW[] = {'o','p','t','i','o','n','a','l',0}; +static const WCHAR processorArchitectureW[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0}; ++static const WCHAR progidW[] = {'p','r','o','g','i','d',0}; +static const WCHAR publicKeyTokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0}; ++static const WCHAR threadingmodelW[] = {'t','h','r','e','a','d','i','n','g','M','o','d','e','l',0}; +static const WCHAR tlbidW[] = {'t','l','b','i','d',0}; +static const WCHAR typeW[] = {'t','y','p','e',0}; +static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0}; +static const WCHAR xmlnsW[] = {'x','m','l','n','s',0}; ++static const WCHAR versionedW[] = {'v','e','r','s','i','o','n','e','d',0}; ++static const WCHAR yesW[] = {'y','e','s',0}; ++static const WCHAR noW[] = {'n','o',0}; ++static const WCHAR restrictedW[] = {'R','E','S','T','R','I','C','T','E','D',0}; ++static const WCHAR controlW[] = {'C','O','N','T','R','O','L',0}; ++static const WCHAR hiddenW[] = {'H','I','D','D','E','N',0}; ++static const WCHAR hasdiskimageW[] = {'H','A','S','D','I','S','K','I','M','A','G','E',0}; ++static const WCHAR flagsW[] = {'f','l','a','g','s',0}; ++static const WCHAR miscstatusW[] = {'m','i','s','c','S','t','a','t','u','s',0}; ++static const WCHAR miscstatusiconW[] = {'m','i','s','c','S','t','a','t','u','s','I','c','o','n',0}; ++static const WCHAR miscstatuscontentW[] = {'m','i','s','c','S','t','a','t','u','s','C','o','n','t','e','n','t',0}; ++static const WCHAR miscstatusthumbnailW[] = {'m','i','s','c','S','t','a','t','u','s','T','h','u','m','b','n','a','i','l',0}; ++static const WCHAR miscstatusdocprintW[] = {'m','i','s','c','S','t','a','t','u','s','D','o','c','P','r','i','n','t',0}; ++static const WCHAR baseInterfaceW[] = {'b','a','s','e','I','n','t','e','r','f','a','c','e',0}; ++static const WCHAR nummethodsW[] = {'n','u','m','M','e','t','h','o','d','s',0}; ++static const WCHAR proxyStubClsid32W[] = {'p','r','o','x','y','S','t','u','b','C','l','s','i','d','3','2',0}; ++static const WCHAR runtimeVersionW[] = {'r','u','n','t','i','m','e','V','e','r','s','i','o','n',0}; ++static const WCHAR mscoreeW[] = {'M','S','C','O','R','E','E','.','D','L','L',0}; ++static const WCHAR mscoree2W[] = {'m','s','c','o','r','e','e','.','d','l','l',0}; ++ ++static const WCHAR activatewhenvisibleW[] = {'a','c','t','i','v','a','t','e','w','h','e','n','v','i','s','i','b','l','e',0}; ++static const WCHAR actslikebuttonW[] = {'a','c','t','s','l','i','k','e','b','u','t','t','o','n',0}; ++static const WCHAR actslikelabelW[] = {'a','c','t','s','l','i','k','e','l','a','b','e','l',0}; ++static const WCHAR alignableW[] = {'a','l','i','g','n','a','b','l','e',0}; ++static const WCHAR alwaysrunW[] = {'a','l','w','a','y','s','r','u','n',0}; ++static const WCHAR canlinkbyole1W[] = {'c','a','n','l','i','n','k','b','y','o','l','e','1',0}; ++static const WCHAR cantlinkinsideW[] = {'c','a','n','t','l','i','n','k','i','n','s','i','d','e',0}; ++static const WCHAR ignoreactivatewhenvisibleW[] = {'i','g','n','o','r','e','a','c','t','i','v','a','t','e','w','h','e','n','v','i','s','i','b','l','e',0}; ++static const WCHAR imemodeW[] = {'i','m','e','m','o','d','e',0}; ++static const WCHAR insertnotreplaceW[] = {'i','n','s','e','r','t','n','o','t','r','e','p','l','a','c','e',0}; ++static const WCHAR insideoutW[] = {'i','n','s','i','d','e','o','u','t',0}; ++static const WCHAR invisibleatruntimeW[] = {'i','n','v','i','s','i','b','l','e','a','t','r','u','n','t','i','m','e',0}; ++static const WCHAR islinkobjectW[] = {'i','s','l','i','n','k','o','b','j','e','c','t',0}; ++static const WCHAR nouiactivateW[] = {'n','o','u','i','a','c','t','i','v','a','t','e',0}; ++static const WCHAR onlyiconicW[] = {'o','n','l','y','i','c','o','n','i','c',0}; ++static const WCHAR recomposeonresizeW[] = {'r','e','c','o','m','p','o','s','e','o','n','r','e','s','i','z','e',0}; ++static const WCHAR renderingisdeviceindependentW[] = {'r','e','n','d','e','r','i','n','g','i','s','d','e','v','i','c','e','i','n','d','e','p','e','n','d','e','n','t',0}; ++static const WCHAR setclientsitefirstW[] = {'s','e','t','c','l','i','e','n','t','s','i','t','e','f','i','r','s','t',0}; ++static const WCHAR simpleframeW[] = {'s','i','m','p','l','e','f','r','a','m','e',0}; ++static const WCHAR staticW[] = {'s','t','a','t','i','c',0}; ++static const WCHAR supportsmultilevelundoW[] = {'s','u','p','p','o','r','t','s','m','u','l','t','i','l','e','v','e','l','u','n','d','o',0}; ++static const WCHAR wantstomenumergeW[] = {'w','a','n','t','s','t','o','m','e','n','u','m','e','r','g','e',0}; ++ ++struct olemisc_entry ++{ ++ const WCHAR *name; ++ OLEMISC value; ++}; ++ ++static const struct olemisc_entry olemisc_values[] = ++{ ++ { activatewhenvisibleW, OLEMISC_ACTIVATEWHENVISIBLE }, ++ { actslikebuttonW, OLEMISC_ACTSLIKEBUTTON }, ++ { actslikelabelW, OLEMISC_ACTSLIKELABEL }, ++ { alignableW, OLEMISC_ALIGNABLE }, ++ { alwaysrunW, OLEMISC_ALWAYSRUN }, ++ { canlinkbyole1W, OLEMISC_CANLINKBYOLE1 }, ++ { cantlinkinsideW, OLEMISC_CANTLINKINSIDE }, ++ { ignoreactivatewhenvisibleW, OLEMISC_IGNOREACTIVATEWHENVISIBLE }, ++ { imemodeW, OLEMISC_IMEMODE }, ++ { insertnotreplaceW, OLEMISC_INSERTNOTREPLACE }, ++ { insideoutW, OLEMISC_INSIDEOUT }, ++ { invisibleatruntimeW, OLEMISC_INVISIBLEATRUNTIME }, ++ { islinkobjectW, OLEMISC_ISLINKOBJECT }, ++ { nouiactivateW, OLEMISC_NOUIACTIVATE }, ++ { onlyiconicW, OLEMISC_ONLYICONIC }, ++ { recomposeonresizeW, OLEMISC_RECOMPOSEONRESIZE }, ++ { renderingisdeviceindependentW, OLEMISC_RENDERINGISDEVICEINDEPENDENT }, ++ { setclientsitefirstW, OLEMISC_SETCLIENTSITEFIRST }, ++ { simpleframeW, OLEMISC_SIMPLEFRAME }, ++ { staticW, OLEMISC_STATIC }, ++ { supportsmultilevelundoW, OLEMISC_SUPPORTSMULTILEVELUNDO }, ++ { wantstomenumergeW, OLEMISC_WANTSTOMENUMERGE } ++}; + +static const WCHAR g_xmlW[] = {'?','x','m','l',0}; +static const WCHAR manifestv1W[] = {'u','r','n',':','s','c','h','e','m','a','s','-','m','i','c','r','o','s','o','f','t','-','c','o','m',':','a','s','m','.','v','1',0}; +static const WCHAR manifestv3W[] = {'u','r','n',':','s','c','h','e','m','a','s','-','m','i','c','r','o','s','o','f','t','-','c','o','m',':','a','s','m','.','v','3',0}; + +static const WCHAR dotManifestW[] = {'.','m','a','n','i','f','e','s','t',0}; +static const WCHAR version_formatW[] = {'%','u','.','%','u','.','%','u','.','%','u',0}; +static const WCHAR wildcardW[] = {'*',0}; + +static ACTIVATION_CONTEXT_WRAPPED system_actctx = { ACTCTX_MAGIC_MARKER, { 1 } }; +static ACTIVATION_CONTEXT *process_actctx = &system_actctx.ActivationContext; + +static WCHAR *strdupW(const WCHAR* str) +{ + WCHAR* ptr; + + if (!(ptr = RtlAllocateHeap(RtlGetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR)))) + return NULL; + return strcpyW(ptr, str); +} + +static WCHAR *xmlstrdupW(const xmlstr_t* str) +{ + WCHAR *strW; + + if ((strW = RtlAllocateHeap(RtlGetProcessHeap(), 0, (str->len + 1) * sizeof(WCHAR)))) + { + memcpy( strW, str->ptr, str->len * sizeof(WCHAR) ); + strW[str->len] = 0; + } + return strW; +} + +static inline BOOL xmlstr_cmp(const xmlstr_t* xmlstr, const WCHAR *str) +{ + return !strncmpW(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len]; +} + +static inline BOOL xmlstr_cmpi(const xmlstr_t* xmlstr, const WCHAR *str) +{ + return !strncmpiW(xmlstr->ptr, str, xmlstr->len) && !str[xmlstr->len]; +} + +static inline BOOL xmlstr_cmp_end(const xmlstr_t* xmlstr, const WCHAR *str) +{ + return (xmlstr->len && xmlstr->ptr[0] == '/' && + !strncmpW(xmlstr->ptr + 1, str, xmlstr->len - 1) && !str[xmlstr->len - 1]); +} + +static inline BOOL xml_elem_cmp(const xmlstr_t *elem, const WCHAR *str, const WCHAR *namespace) +{ + UINT len = strlenW( namespace ); + + if (!strncmpW(elem->ptr, str, elem->len) && !str[elem->len]) return TRUE; + return (elem->len > len && !strncmpW(elem->ptr, namespace, len) && + !strncmpW(elem->ptr + len, str, elem->len - len) && !str[elem->len - len]); +} + +static inline BOOL xml_elem_cmp_end(const xmlstr_t *elem, const WCHAR *str, const WCHAR *namespace) +{ + if (elem->len && elem->ptr[0] == '/') + { + xmlstr_t elem_end; + elem_end.ptr = elem->ptr + 1; + elem_end.len = elem->len - 1; + return xml_elem_cmp( &elem_end, str, namespace ); + } + return FALSE; +} + +static inline BOOL isxmlspace( WCHAR ch ) +{ + return (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t'); +} + +static UNICODE_STRING xmlstr2unicode(const xmlstr_t *xmlstr) +{ + UNICODE_STRING res; + + res.Buffer = (PWSTR)xmlstr->ptr; + res.Length = res.MaximumLength = (USHORT)xmlstr->len * sizeof(WCHAR); + + return res; +} + +static struct assembly *add_assembly(ACTIVATION_CONTEXT *actctx, enum assembly_type at) +{ + struct assembly *assembly; + + if (actctx->num_assemblies == actctx->allocated_assemblies) + { + void *ptr; + unsigned int new_count; + if (actctx->assemblies) + { + new_count = actctx->allocated_assemblies * 2; + ptr = RtlReAllocateHeap( RtlGetProcessHeap(), HEAP_ZERO_MEMORY, + actctx->assemblies, new_count * sizeof(*assembly) ); + } + else + { + new_count = 4; + ptr = RtlAllocateHeap( RtlGetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*assembly) ); + } + if (!ptr) return NULL; + actctx->assemblies = ptr; + actctx->allocated_assemblies = new_count; + } + + assembly = &actctx->assemblies[actctx->num_assemblies++]; + assembly->type = at; + return assembly; +} + +static struct dll_redirect* add_dll_redirect(struct assembly* assembly) +{ + if (assembly->num_dlls == assembly->allocated_dlls) + { + void *ptr; + unsigned int new_count; + if (assembly->dlls) + { + new_count = assembly->allocated_dlls * 2; + ptr = RtlReAllocateHeap( RtlGetProcessHeap(), HEAP_ZERO_MEMORY, + assembly->dlls, new_count * sizeof(*assembly->dlls) ); + } + else + { + new_count = 4; + ptr = RtlAllocateHeap( RtlGetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*assembly->dlls) ); + } + if (!ptr) return NULL; + assembly->dlls = ptr; + assembly->allocated_dlls = new_count; + } + return &assembly->dlls[assembly->num_dlls++]; +} + +static void free_assembly_identity(struct assembly_identity *ai) +{ + RtlFreeHeap( RtlGetProcessHeap(), 0, ai->name ); + RtlFreeHeap( RtlGetProcessHeap(), 0, ai->arch ); + RtlFreeHeap( RtlGetProcessHeap(), 0, ai->public_key ); + RtlFreeHeap( RtlGetProcessHeap(), 0, ai->language ); + RtlFreeHeap( RtlGetProcessHeap(), 0, ai->type ); +} + +static struct entity* add_entity(struct entity_array *array, DWORD kind) +{ + struct entity* entity; + + if (array->num == array->allocated) + { + void *ptr; + unsigned int new_count; + if (array->base) + { + new_count = array->allocated * 2; + ptr = RtlReAllocateHeap( RtlGetProcessHeap(), HEAP_ZERO_MEMORY, + array->base, new_count * sizeof(*array->base) ); + } + else + { + new_count = 4; + ptr = RtlAllocateHeap( RtlGetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*array->base) ); + } + if (!ptr) return NULL; + array->base = ptr; + array->allocated = new_count; + } + entity = &array->base[array->num++]; + entity->kind = kind; + return entity; +} + +static void free_entity_array(struct entity_array *array) +{ - unsigned int i; ++ unsigned int i, j; + for (i = 0; i < array->num; i++) + { + struct entity *entity = &array->base[i]; + switch (entity->kind) + { + case ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION: + RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.clsid); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.tlbid); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.progid); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.name); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.version); ++ for (j = 0; j < entity->u.comclass.progids.num; j++) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.progids.progids[j]); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.comclass.progids.progids); + break; + case ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION: - RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.proxy.iid); - RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.proxy.name); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.ifaceps.iid); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.ifaceps.base); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.ifaceps.ps32); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.ifaceps.name); + break; + case ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION: + RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.typelib.tlbid); - RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.typelib.version); + RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.typelib.helpdir); + break; + case ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION: + RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.class.name); + break; - case ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION: - RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.clrclass.name); - RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.clrclass.clsid); - break; + case ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES: + RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.clrsurrogate.name); + RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.clrsurrogate.clsid); ++ RtlFreeHeap(RtlGetProcessHeap(), 0, entity->u.clrsurrogate.version); + break; + default: + DPRINT1("Unknown entity kind %u\n", entity->kind); + } + } + RtlFreeHeap( RtlGetProcessHeap(), 0, array->base ); +} + +static BOOL is_matching_string( const WCHAR *str1, const WCHAR *str2 ) +{ + if (!str1) return !str2; + return str2 && !strcmpiW( str1, str2 ); +} + +static BOOL is_matching_identity( const struct assembly_identity *id1, + const struct assembly_identity *id2 ) +{ + if (!is_matching_string( id1->name, id2->name )) return FALSE; + if (!is_matching_string( id1->arch, id2->arch )) return FALSE; + if (!is_matching_string( id1->public_key, id2->public_key )) return FALSE; + + if (id1->language && id2->language && strcmpiW( id1->language, id2->language )) + { + if (strcmpW( wildcardW, id1->language ) && strcmpW( wildcardW, id2->language )) + return FALSE; + } + if (id1->version.major != id2->version.major) return FALSE; + if (id1->version.minor != id2->version.minor) return FALSE; + if (id1->version.build > id2->version.build) return FALSE; + if (id1->version.build == id2->version.build && + id1->version.revision > id2->version.revision) return FALSE; + return TRUE; +} + +static BOOL add_dependent_assembly_id(struct actctx_loader* acl, + struct assembly_identity* ai) +{ + unsigned int i; + + /* check if we already have that assembly */ + + for (i = 0; i < acl->actctx->num_assemblies; i++) + if (is_matching_identity( ai, &acl->actctx->assemblies[i].id )) + { + DPRINT( "reusing existing assembly for %S arch %S version %u.%u.%u.%u\n", + ai->name, ai->arch, ai->version.major, ai->version.minor, + ai->version.build, ai->version.revision ); + return TRUE; + } + + for (i = 0; i < acl->num_dependencies; i++) + if (is_matching_identity( ai, &acl->dependencies[i] )) + { + DPRINT( "reusing existing dependency for %S arch %S version %u.%u.%u.%u\n", + ai->name, ai->arch, ai->version.major, ai->version.minor, + ai->version.build, ai->version.revision ); + return TRUE; + } + + if (acl->num_dependencies == acl->allocated_dependencies) + { + void *ptr; + unsigned int new_count; + if (acl->dependencies) + { + new_count = acl->allocated_dependencies * 2; + ptr = RtlReAllocateHeap(RtlGetProcessHeap(), 0, acl->dependencies, + new_count * sizeof(acl->dependencies[0])); + } + else + { + new_count = 4; + ptr = RtlAllocateHeap(RtlGetProcessHeap(), 0, new_count * sizeof(acl->dependencies[0])); + } + if (!ptr) return FALSE; + acl->dependencies = ptr; + acl->allocated_dependencies = new_count; + } + acl->dependencies[acl->num_dependencies++] = *ai; + + return TRUE; +} + +static void free_depend_manifests(struct actctx_loader* acl) +{ + unsigned int i; + for (i = 0; i < acl->num_dependencies; i++) + free_assembly_identity(&acl->dependencies[i]); + RtlFreeHeap(RtlGetProcessHeap(), 0, acl->dependencies); +} + +static WCHAR *build_assembly_dir(struct assembly_identity* ai) +{ + static const WCHAR undW[] = {'_',0}; + static const WCHAR noneW[] = {'n','o','n','e',0}; + static const WCHAR mskeyW[] = {'d','e','a','d','b','e','e','f',0}; + + const WCHAR *arch = ai->arch ? ai->arch : noneW; + const WCHAR *key = ai->public_key ? ai->public_key : noneW; + const WCHAR *lang = ai->language ? ai->language : noneW; + const WCHAR *name = ai->name ? ai->name : noneW; + SIZE_T size = (strlenW(arch) + 1 + strlenW(name) + 1 + strlenW(key) + 24 + 1 + + strlenW(lang) + 1) * sizeof(WCHAR) + sizeof(mskeyW); + WCHAR *ret; + + if (!(ret = RtlAllocateHeap( RtlGetProcessHeap(), 0, size ))) return NULL; + + strcpyW( ret, arch ); + strcatW( ret, undW ); + strcatW( ret, name ); + strcatW( ret, undW ); + strcatW( ret, key ); + strcatW( ret, undW ); + sprintfW( ret + strlenW(ret), version_formatW, + ai->version.major, ai->version.minor, ai->version.build, ai->version.revision ); + strcatW( ret, undW ); + strcatW( ret, lang ); + strcatW( ret, undW ); + strcatW( ret, mskeyW ); + return ret; +} + +static inline void append_string( WCHAR *buffer, const WCHAR *prefix, const WCHAR *str ) +{ + WCHAR *p = buffer; + + if (!str) return; + strcatW( buffer, prefix ); + p += strlenW(p); + *p++ = '"'; + strcpyW( p, str ); + p += strlenW(p); + *p++ = '"'; + *p = 0; +} + +static WCHAR *build_assembly_id( const struct assembly_identity *ai ) +{ + static const WCHAR archW[] = + {',','p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e','=',0}; + static const WCHAR public_keyW[] = + {',','p','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0}; + static const WCHAR typeW2[] = + {',','t','y','p','e','=',0}; + static const WCHAR versionW2[] = + {',','v','e','r','s','i','o','n','=',0}; + + WCHAR version[64], *ret; + SIZE_T size = 0; + + sprintfW( version, version_formatW, + ai->version.major, ai->version.minor, ai->version.build, ai->version.revision ); + if (ai->name) size += strlenW(ai->name) * sizeof(WCHAR); + if (ai->arch) size += strlenW(archW) + strlenW(ai->arch) + 2; + if (ai->public_key) size += strlenW(public_keyW) + strlenW(ai->public_key) + 2; + if (ai->type) size += strlenW(typeW2) + strlenW(ai->type) + 2; + size += strlenW(versionW2) + strlenW(version) + 2; + + if (!(ret = RtlAllocateHeap( RtlGetProcessHeap(), 0, (size + 1) * sizeof(WCHAR) ))) + return NULL; + + if (ai->name) strcpyW( ret, ai->name ); + else *ret = 0; + append_string( ret, archW, ai->arch ); + append_string( ret, public_keyW, ai->public_key ); + append_string( ret, typeW2, ai->type ); + append_string( ret, versionW2, version ); + return ret; +} +static ACTIVATION_CONTEXT *check_actctx( HANDLE h ) +{ + ACTIVATION_CONTEXT *ret = NULL, *actctx = h; + PACTIVATION_CONTEXT_WRAPPED pActual; + + if (!h || h == INVALID_HANDLE_VALUE) return NULL; + _SEH2_TRY + { + if (actctx) + { + pActual = CONTAINING_RECORD(actctx, ACTIVATION_CONTEXT_WRAPPED, ActivationContext); + if (pActual->MagicMarker == ACTCTX_MAGIC_MARKER) ret = &pActual->ActivationContext; + } + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + DPRINT1("Invalid activation context handle!\n"); + } + _SEH2_END; + return ret; +} + +static inline void actctx_addref( ACTIVATION_CONTEXT *actctx ) +{ + InterlockedExchangeAdd( &actctx->RefCount, 1 ); +} + +static void actctx_release( ACTIVATION_CONTEXT *actctx ) +{ + PACTIVATION_CONTEXT_WRAPPED pActual; + + if (InterlockedExchangeAdd(&actctx->RefCount, -1) == 1) + { + unsigned int i, j; + + for (i = 0; i < actctx->num_assemblies; i++) + { + struct assembly *assembly = &actctx->assemblies[i]; + for (j = 0; j < assembly->num_dlls; j++) + { + struct dll_redirect *dll = &assembly->dlls[j]; + free_entity_array( &dll->entities ); + RtlFreeHeap( RtlGetProcessHeap(), 0, dll->name ); + RtlFreeHeap( RtlGetProcessHeap(), 0, dll->hash ); + } + RtlFreeHeap( RtlGetProcessHeap(), 0, assembly->dlls ); + RtlFreeHeap( RtlGetProcessHeap(), 0, assembly->manifest.info ); + RtlFreeHeap( RtlGetProcessHeap(), 0, assembly->directory ); + free_entity_array( &assembly->entities ); + free_assembly_identity(&assembly->id); + } + RtlFreeHeap( RtlGetProcessHeap(), 0, actctx->config.info ); + RtlFreeHeap( RtlGetProcessHeap(), 0, actctx->appdir.info ); + RtlFreeHeap( RtlGetProcessHeap(), 0, actctx->assemblies ); + pActual = CONTAINING_RECORD(actctx, ACTIVATION_CONTEXT_WRAPPED, ActivationContext); + pActual->MagicMarker = 0; + RtlFreeHeap(RtlGetProcessHeap(), 0, pActual); + } +} + +static BOOL next_xml_attr(xmlbuf_t* xmlbuf, xmlstr_t* name, xmlstr_t* value, + BOOL* error, BOOL* end) +{ + const WCHAR* ptr; + + *error = TRUE; + + while (xmlbuf->ptr < xmlbuf->end && isxmlspace(*xmlbuf->ptr)) + xmlbuf->ptr++; + + if (xmlbuf->ptr == xmlbuf->end) return FALSE; + + if (*xmlbuf->ptr == '/') + { + xmlbuf->ptr++; + if (xmlbuf->ptr == xmlbuf->end || *xmlbuf->ptr != '>') + return FALSE; + + xmlbuf->ptr++; + *end = TRUE; + *error = FALSE; + return FALSE; + } + + if (*xmlbuf->ptr == '>') + { + xmlbuf->ptr++; + *error = FALSE; + return FALSE; + } + + ptr = xmlbuf->ptr; + while (ptr < xmlbuf->end && *ptr != '=' && *ptr != '>' && !isxmlspace(*ptr)) ptr++; + + if (ptr == xmlbuf->end) return FALSE; + + name->ptr = xmlbuf->ptr; + name->len = ptr-xmlbuf->ptr; + xmlbuf->ptr = ptr; + + /* skip spaces before '=' */ + while (ptr < xmlbuf->end && *ptr != '=' && isxmlspace(*ptr)) ptr++; + if (ptr == xmlbuf->end || *ptr != '=') return FALSE; + + /* skip '=' itself */ + ptr++; + if (ptr == xmlbuf->end) return FALSE; + + /* skip spaces after '=' */ + while (ptr < xmlbuf->end && *ptr != '"' && *ptr != '\'' && isxmlspace(*ptr)) ptr++; + + if (ptr == xmlbuf->end || (*ptr != '"' && *ptr != '\'')) return FALSE; + + value->ptr = ++ptr; + if (ptr == xmlbuf->end) return FALSE; + + ptr = memchrW(ptr, ptr[-1], xmlbuf->end - ptr); + if (!ptr) + { + xmlbuf->ptr = xmlbuf->end; + return FALSE; + } + + value->len = ptr - value->ptr; + xmlbuf->ptr = ptr + 1; + + if (xmlbuf->ptr == xmlbuf->end) return FALSE; + + *error = FALSE; + return TRUE; +} + +static BOOL next_xml_elem(xmlbuf_t* xmlbuf, xmlstr_t* elem) +{ + const WCHAR* ptr; + + for (;;) + { + ptr = memchrW(xmlbuf->ptr, '<', xmlbuf->end - xmlbuf->ptr); + if (!ptr) + { + xmlbuf->ptr = xmlbuf->end; + return FALSE; + } + ptr++; + if (ptr + 3 < xmlbuf->end && ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') /* skip comment */ + { + for (ptr += 3; ptr + 3 <= xmlbuf->end; ptr++) + if (ptr[0] == '-' && ptr[1] == '-' && ptr[2] == '>') break; + + if (ptr + 3 > xmlbuf->end) + { + xmlbuf->ptr = xmlbuf->end; + return FALSE; + } + xmlbuf->ptr = ptr + 3; + } + else break; + } + + xmlbuf->ptr = ptr; + while (ptr < xmlbuf->end && !isxmlspace(*ptr) && *ptr != '>' && (*ptr != '/' || ptr == xmlbuf->ptr)) + ptr++; + + elem->ptr = xmlbuf->ptr; + elem->len = ptr - xmlbuf->ptr; + xmlbuf->ptr = ptr; + return xmlbuf->ptr != xmlbuf->end; +} + +static BOOL parse_xml_header(xmlbuf_t* xmlbuf) +{ + /* FIXME: parse attributes */ + const WCHAR *ptr; + + for (ptr = xmlbuf->ptr; ptr < xmlbuf->end - 1; ptr++) + { + if (ptr[0] == '?' && ptr[1] == '>') + { + xmlbuf->ptr = ptr + 2; + return TRUE; + } + } + return FALSE; +} + +static BOOL parse_text_content(xmlbuf_t* xmlbuf, xmlstr_t* content) +{ + const WCHAR *ptr = memchrW(xmlbuf->ptr, '<', xmlbuf->end - xmlbuf->ptr); + + if (!ptr) return FALSE; + + content->ptr = xmlbuf->ptr; + content->len = ptr - xmlbuf->ptr; + xmlbuf->ptr = ptr; + + return TRUE; +} + +static BOOL parse_version(const xmlstr_t *str, struct assembly_version *version) +{ + unsigned int ver[4]; + unsigned int pos; + const WCHAR *curr; + UNICODE_STRING strU; + + /* major.minor.build.revision */ + ver[0] = ver[1] = ver[2] = ver[3] = pos = 0; + for (curr = str->ptr; curr < str->ptr + str->len; curr++) + { + if (*curr >= '0' && *curr <= '9') + { + ver[pos] = ver[pos] * 10 + *curr - '0'; + if (ver[pos] >= 0x10000) goto error; + } + else if (*curr == '.') + { + if (++pos >= 4) goto error; + } + else goto error; + } + version->major = ver[0]; + version->minor = ver[1]; + version->build = ver[2]; + version->revision = ver[3]; + return TRUE; + +error: + strU = xmlstr2unicode(str); + DPRINT1( "Wrong version definition in manifest file (%wZ)\n", &strU ); + return FALSE; +} + +static BOOL parse_expect_elem(xmlbuf_t* xmlbuf, const WCHAR* name, const WCHAR *namespace) +{ + xmlstr_t elem; + UNICODE_STRING elemU; + if (!next_xml_elem(xmlbuf, &elem)) return FALSE; + if (xml_elem_cmp(&elem, name, namespace)) return TRUE; + elemU = xmlstr2unicode(&elem); + DPRINT1( "unexpected element %wZ\n", &elemU ); + return FALSE; +} + +static BOOL parse_expect_no_attr(xmlbuf_t* xmlbuf, BOOL* end) +{ + xmlstr_t attr_name, attr_value; + UNICODE_STRING attr_nameU, attr_valueU; + BOOL error; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, end)) + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1( "unexpected attr %wZ=%wZ\n", &attr_nameU, + &attr_valueU); + } + return !error; +} + +static BOOL parse_end_element(xmlbuf_t *xmlbuf) +{ + BOOL end = FALSE; + return parse_expect_no_attr(xmlbuf, &end) && !end; +} + +static BOOL parse_expect_end_elem(xmlbuf_t *xmlbuf, const WCHAR *name, const WCHAR *namespace) +{ + xmlstr_t elem; + UNICODE_STRING elemU; + if (!next_xml_elem(xmlbuf, &elem)) return FALSE; + if (!xml_elem_cmp_end(&elem, name, namespace)) + { + elemU = xmlstr2unicode(&elem); + DPRINT1( "unexpected element %wZ\n", &elemU ); + return FALSE; + } + return parse_end_element(xmlbuf); +} + +static BOOL parse_unknown_elem(xmlbuf_t *xmlbuf, const xmlstr_t *unknown_elem) +{ + xmlstr_t attr_name, attr_value, elem; + BOOL end = FALSE, error, ret = TRUE; + + while(next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)); + if(error || end) return end; + + while(ret && (ret = next_xml_elem(xmlbuf, &elem))) + { + if(*elem.ptr == '/' && elem.len - 1 == unknown_elem->len && + !strncmpW(elem.ptr+1, unknown_elem->ptr, unknown_elem->len)) + break; + else + ret = parse_unknown_elem(xmlbuf, &elem); + } + + return ret && parse_end_element(xmlbuf); +} + +static BOOL parse_assembly_identity_elem(xmlbuf_t* xmlbuf, ACTIVATION_CONTEXT* actctx, + struct assembly_identity* ai) +{ + xmlstr_t attr_name, attr_value; + BOOL end = FALSE, error; + UNICODE_STRING attr_valueU, attr_nameU; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, g_nameW)) + { + if (!(ai->name = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, typeW)) + { + if (!(ai->type = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, versionW)) + { + if (!parse_version(&attr_value, &ai->version)) return FALSE; + } + else if (xmlstr_cmp(&attr_name, processorArchitectureW)) + { + if (!(ai->arch = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, publicKeyTokenW)) + { + if (!(ai->public_key = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, languageW)) + { + DPRINT1("Unsupported yet language attribute (%S)\n", + ai->language); + if (!(ai->language = xmlstrdupW(&attr_value))) return FALSE; + } + else + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + + if (error || end) return end; + return parse_expect_end_elem(xmlbuf, assemblyIdentityW, asmv1W); +} + - static BOOL parse_com_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll) ++static enum comclass_threadingmodel parse_com_class_threadingmodel(xmlstr_t *value) ++{ ++ static const WCHAR apartW[] = {'A','p','a','r','t','m','e','n','t',0}; ++ static const WCHAR neutralW[] = {'N','e','u','t','r','a','l',0}; ++ static const WCHAR freeW[] = {'F','r','e','e',0}; ++ static const WCHAR bothW[] = {'B','o','t','h',0}; ++ ++ if (value->len == 0) return ThreadingModel_No; ++ if (xmlstr_cmp(value, apartW)) ++ return ThreadingModel_Apartment; ++ else if (xmlstr_cmp(value, freeW)) ++ return ThreadingModel_Free; ++ else if (xmlstr_cmp(value, bothW)) ++ return ThreadingModel_Both; ++ else if (xmlstr_cmp(value, neutralW)) ++ return ThreadingModel_Neutral; ++ else ++ return ThreadingModel_No; ++}; ++ ++static OLEMISC get_olemisc_value(const WCHAR *str, int len) ++{ ++ int min, max; ++ ++ min = 0; ++ max = sizeof(olemisc_values)/sizeof(struct olemisc_entry) - 1; ++ ++ while (min <= max) ++ { ++ int n, c; ++ ++ n = (min+max)/2; ++ ++ c = strncmpW(olemisc_values[n].name, str, len); ++ if (!c && !olemisc_values[n].name[len]) ++ return olemisc_values[n].value; ++ ++ if (c >= 0) ++ max = n-1; ++ else ++ min = n+1; ++ } ++ ++ DPRINT1("unknown flag %S\n", str); ++ return 0; ++} ++ ++static DWORD parse_com_class_misc(const xmlstr_t *value) ++{ ++ const WCHAR *str = value->ptr, *start; ++ DWORD flags = 0; ++ int i = 0; ++ ++ /* it's comma separated list of flags */ ++ while (i < value->len) ++ { ++ start = str; ++ while (*str != ',' && (i++ < value->len)) str++; ++ ++ flags |= get_olemisc_value(start, str-start); ++ ++ /* skip separator */ ++ str++; ++ i++; ++ } ++ ++ return flags; ++} ++ ++static BOOL com_class_add_progid(const xmlstr_t *progid, struct entity *entity) ++{ ++ struct progids *progids = &entity->u.comclass.progids; ++ ++ if (progids->allocated == 0) ++ { ++ progids->allocated = 4; ++ if (!(progids->progids = RtlAllocateHeap(RtlGetProcessHeap(), 0, progids->allocated * sizeof(WCHAR*)))) return FALSE; ++ } ++ ++ if (progids->allocated == progids->num) ++ { ++ progids->allocated *= 2; ++ progids->progids = RtlReAllocateHeap(RtlGetProcessHeap(), 0, progids->progids, progids->allocated * sizeof(WCHAR*)); ++ } ++ ++ if (!(progids->progids[progids->num] = xmlstrdupW(progid))) return FALSE; ++ progids->num++; ++ ++ return TRUE; ++} ++ ++static BOOL parse_com_class_progid(xmlbuf_t* xmlbuf, struct entity *entity) ++{ ++ xmlstr_t content; ++ BOOL end = FALSE; ++ ++ if (!parse_expect_no_attr(xmlbuf, &end) || end || !parse_text_content(xmlbuf, &content)) ++ return FALSE; ++ ++ if (!com_class_add_progid(&content, entity)) return FALSE; ++ return parse_expect_end_elem(xmlbuf, progidW, asmv1W); ++} ++ ++static BOOL parse_com_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll, struct actctx_loader *acl) +{ + xmlstr_t elem, attr_name, attr_value; + BOOL ret = TRUE, end = FALSE, error; + struct entity* entity; + UNICODE_STRING attr_valueU, attr_nameU; + + if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION))) + return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, clsidW)) + { + if (!(entity->u.comclass.clsid = xmlstrdupW(&attr_value))) return FALSE; + } ++ else if (xmlstr_cmp(&attr_name, progidW)) ++ { ++ if (!(entity->u.comclass.progid = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, tlbidW)) ++ { ++ if (!(entity->u.comclass.tlbid = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, threadingmodelW)) ++ { ++ entity->u.comclass.model = parse_com_class_threadingmodel(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, miscstatusW)) ++ { ++ entity->u.comclass.miscstatus = parse_com_class_misc(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, miscstatuscontentW)) ++ { ++ entity->u.comclass.miscstatuscontent = parse_com_class_misc(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, miscstatusthumbnailW)) ++ { ++ entity->u.comclass.miscstatusthumbnail = parse_com_class_misc(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, miscstatusiconW)) ++ { ++ entity->u.comclass.miscstatusicon = parse_com_class_misc(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, miscstatusdocprintW)) ++ { ++ entity->u.comclass.miscstatusdocprint = parse_com_class_misc(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, descriptionW)) ++ { ++ /* not stored */ ++ } + else + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + - if (error || end) return end; ++ if (error) return FALSE; + - while ((ret = next_xml_elem(xmlbuf, &elem))) ++ acl->actctx->sections |= SERVERREDIRECT_SECTION; ++ if (entity->u.comclass.progid) ++ acl->actctx->sections |= PROGIDREDIRECT_SECTION; ++ ++ if (end) return TRUE; ++ ++ while (ret && (ret = next_xml_elem(xmlbuf, &elem))) + { + if (xmlstr_cmp_end(&elem, comClassW)) + { + ret = parse_end_element(xmlbuf); + break; + } ++ else if (xmlstr_cmp(&elem, progidW)) ++ { ++ ret = parse_com_class_progid(xmlbuf, entity); ++ } + else + { + attr_nameU = xmlstr2unicode(&elem); + DPRINT1("unknown elem %wZ\n", &attr_nameU); + ret = parse_unknown_elem(xmlbuf, &elem); + } + } ++ ++ if (entity->u.comclass.progids.num) ++ acl->actctx->sections |= PROGIDREDIRECT_SECTION; ++ + return ret; +} + - static BOOL parse_cominterface_proxy_stub_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll) ++static BOOL parse_nummethods(const xmlstr_t *str, struct entity *entity) ++{ ++ const WCHAR *curr; ++ ULONG num = 0; ++ ++ for (curr = str->ptr; curr < str->ptr + str->len; curr++) ++ { ++ if (*curr >= '0' && *curr <= '9') ++ num = num * 10 + *curr - '0'; ++ else ++ { ++ UNICODE_STRING strU = xmlstr2unicode(str); ++ DPRINT1("wrong numeric value %wZ\n", &strU); ++ return FALSE; ++ } ++ } ++ entity->u.ifaceps.nummethods = num; ++ ++ return TRUE; ++} ++ ++static BOOL parse_cominterface_proxy_stub_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll, struct actctx_loader* acl) +{ + xmlstr_t attr_name, attr_value; + BOOL end = FALSE, error; + struct entity* entity; + UNICODE_STRING attr_valueU, attr_nameU; + + if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION))) + return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, iidW)) + { - if (!(entity->u.proxy.iid = xmlstrdupW(&attr_value))) return FALSE; ++ if (!(entity->u.ifaceps.iid = xmlstrdupW(&attr_value))) return FALSE; + } - if (xmlstr_cmp(&attr_name, g_nameW)) ++ else if (xmlstr_cmp(&attr_name, g_nameW)) ++ { ++ if (!(entity->u.ifaceps.name = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, baseInterfaceW)) ++ { ++ if (!(entity->u.ifaceps.base = xmlstrdupW(&attr_value))) return FALSE; ++ entity->u.ifaceps.mask |= BaseIface; ++ } ++ else if (xmlstr_cmp(&attr_name, nummethodsW)) ++ { ++ if (!(parse_nummethods(&attr_value, entity))) return FALSE; ++ entity->u.ifaceps.mask |= NumMethods; ++ } ++ else if (xmlstr_cmp(&attr_name, tlbidW)) ++ { ++ if (!(entity->u.ifaceps.tlib = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ /* not used */ ++ else if (xmlstr_cmp(&attr_name, proxyStubClsid32W) || xmlstr_cmp(&attr_name, threadingmodelW)) + { - if (!(entity->u.proxy.name = xmlstrdupW(&attr_value))) return FALSE; + } + else + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + - if (error || end) return end; ++ if (error) return FALSE; ++ acl->actctx->sections |= IFACEREDIRECT_SECTION; ++ if (end) return TRUE; ++ + return parse_expect_end_elem(xmlbuf, comInterfaceProxyStubW, asmv1W); +} + - static BOOL parse_typelib_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll) ++static BOOL parse_typelib_flags(const xmlstr_t *value, struct entity *entity) ++{ ++ WORD *flags = &entity->u.typelib.flags; ++ const WCHAR *str = value->ptr, *start; ++ int i = 0; ++ ++ *flags = 0; ++ ++ /* it's comma separated list of flags */ ++ while (i < value->len) ++ { ++ start = str; ++ while (*str != ',' && (i++ < value->len)) str++; ++ ++ if (!strncmpiW(start, restrictedW, str-start)) ++ *flags |= LIBFLAG_FRESTRICTED; ++ else if (!strncmpiW(start, controlW, str-start)) ++ *flags |= LIBFLAG_FCONTROL; ++ else if (!strncmpiW(start, hiddenW, str-start)) ++ *flags |= LIBFLAG_FHIDDEN; ++ else if (!strncmpiW(start, hasdiskimageW, str-start)) ++ *flags |= LIBFLAG_FHASDISKIMAGE; ++ else ++ { ++ UNICODE_STRING valueU = xmlstr2unicode(value); ++ DPRINT1("unknown flags value %wZ\n", &valueU); ++ return FALSE; ++ } ++ ++ /* skip separator */ ++ str++; ++ i++; ++ } ++ ++ return TRUE; ++} ++ ++static BOOL parse_typelib_version(const xmlstr_t *str, struct entity *entity) ++{ ++ unsigned int ver[2]; ++ unsigned int pos; ++ const WCHAR *curr; ++ UNICODE_STRING strW; ++ ++ /* major.minor */ ++ ver[0] = ver[1] = pos = 0; ++ for (curr = str->ptr; curr < str->ptr + str->len; curr++) ++ { ++ if (*curr >= '0' && *curr <= '9') ++ { ++ ver[pos] = ver[pos] * 10 + *curr - '0'; ++ if (ver[pos] >= 0x10000) goto error; ++ } ++ else if (*curr == '.') ++ { ++ if (++pos >= 2) goto error; ++ } ++ else goto error; ++ } ++ entity->u.typelib.major = ver[0]; ++ entity->u.typelib.minor = ver[1]; ++ return TRUE; ++ ++error: ++ strW = xmlstr2unicode(str); ++ DPRINT1("FIXME: wrong typelib version value (%wZ)\n", &strW); ++ return FALSE; ++} ++ ++static BOOL parse_typelib_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll, struct actctx_loader* acl) +{ + xmlstr_t attr_name, attr_value; + BOOL end = FALSE, error; + struct entity* entity; + UNICODE_STRING attr_valueU, attr_nameU; + + if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION))) + return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, tlbidW)) + { + if (!(entity->u.typelib.tlbid = xmlstrdupW(&attr_value))) return FALSE; + } - if (xmlstr_cmp(&attr_name, versionW)) ++ else if (xmlstr_cmp(&attr_name, versionW)) + { - if (!(entity->u.typelib.version = xmlstrdupW(&attr_value))) return FALSE; ++ if (!parse_typelib_version(&attr_value, entity)) return FALSE; + } - if (xmlstr_cmp(&attr_name, helpdirW)) ++ else if (xmlstr_cmp(&attr_name, helpdirW)) + { + if (!(entity->u.typelib.helpdir = xmlstrdupW(&attr_value))) return FALSE; + } ++ else if (xmlstr_cmp(&attr_name, flagsW)) ++ { ++ if (!parse_typelib_flags(&attr_value, entity)) return FALSE; ++ } + else + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + - if (error || end) return end; ++ if (error) return FALSE; ++ ++ acl->actctx->sections |= TLIBREDIRECT_SECTION; ++ ++ if (end) return TRUE; ++ + return parse_expect_end_elem(xmlbuf, typelibW, asmv1W); +} + - static BOOL parse_window_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll) ++static inline int aligned_string_len(int len) +{ - xmlstr_t elem, content; - BOOL end = FALSE, ret = TRUE; - struct entity* entity; - UNICODE_STRING elemU; ++ return (len + 3) & ~3; ++} + - if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION))) - return FALSE; ++static int get_assembly_version(struct assembly *assembly, WCHAR *ret) ++{ ++ static const WCHAR fmtW[] = {'%','u','.','%','u','.','%','u','.','%','u',0}; ++ struct assembly_version *ver = &assembly->id.version; ++ WCHAR buff[25]; + - if (!parse_expect_no_attr(xmlbuf, &end)) return FALSE; - if (end) return FALSE; ++ if (!ret) ret = buff; ++ return sprintfW(ret, fmtW, ver->major, ver->minor, ver->build, ver->revision); ++} + - if (!parse_text_content(xmlbuf, &content)) return FALSE; ++static BOOL parse_window_class_elem(xmlbuf_t* xmlbuf, struct dll_redirect* dll, struct actctx_loader* acl) ++{ ++ xmlstr_t elem, content, attr_name, attr_value; ++ BOOL end = FALSE, ret = TRUE, error; ++ struct entity* entity; ++ UNICODE_STRING elemU, attr_nameU, attr_valueU; + - if (!(entity->u.class.name = xmlstrdupW(&content))) return FALSE; ++ if (!(entity = add_entity(&dll->entities, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION))) ++ return FALSE; + - while (ret && (ret = next_xml_elem(xmlbuf, &elem))) ++ entity->u.class.versioned = TRUE; ++ while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { - if (xmlstr_cmp_end(&elem, windowClassW)) ++ if (xmlstr_cmp(&attr_name, versionedW)) ++ { ++ if (xmlstr_cmpi(&attr_value, noW)) ++ entity->u.class.versioned = FALSE; ++ else if (!xmlstr_cmpi(&attr_value, yesW)) ++ return FALSE; ++ } ++ else ++ { ++ attr_nameU = xmlstr2unicode(&attr_name); ++ attr_valueU = xmlstr2unicode(&attr_value); ++ DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); ++ } ++ } ++ ++ if (error || end) return end; ++ ++ if (!parse_text_content(xmlbuf, &content)) return FALSE; ++ ++ if (!(entity->u.class.name = xmlstrdupW(&content))) return FALSE; ++ ++ acl->actctx->sections |= WINDOWCLASS_SECTION; ++ ++ while (ret && (ret = next_xml_elem(xmlbuf, &elem))) ++ { ++ if (xmlstr_cmp_end(&elem, windowClassW)) + { + ret = parse_end_element(xmlbuf); + break; + } + else + { + elemU = xmlstr2unicode(&elem); + DPRINT1("unknown elem %wZ\n", &elemU); + ret = parse_unknown_elem(xmlbuf, &elem); + } + } + + return ret; +} + +static BOOL parse_binding_redirect_elem(xmlbuf_t* xmlbuf) +{ + xmlstr_t attr_name, attr_value; + UNICODE_STRING attr_valueU, attr_nameU; + BOOL end = FALSE, error; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + + if (xmlstr_cmp(&attr_name, oldVersionW)) + { + DPRINT1("Not stored yet oldVersion=%wZ\n", &attr_valueU); + } + else if (xmlstr_cmp(&attr_name, newVersionW)) + { + DPRINT1("Not stored yet newVersion=%wZ\n", &attr_valueU); + } + else + { + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + + if (error || end) return end; + return parse_expect_end_elem(xmlbuf, bindingRedirectW, asmv1W); +} + +static BOOL parse_description_elem(xmlbuf_t* xmlbuf) +{ + xmlstr_t elem, content; + UNICODE_STRING elemU; + BOOL end = FALSE, ret = TRUE; + + if (!parse_expect_no_attr(xmlbuf, &end) || end || + !parse_text_content(xmlbuf, &content)) + return FALSE; + + elemU = xmlstr2unicode(&content); + DPRINT("Got description %wZ\n", &elemU); + + while (ret && (ret = next_xml_elem(xmlbuf, &elem))) + { + if (xmlstr_cmp_end(&elem, descriptionW)) + { + ret = parse_end_element(xmlbuf); + break; + } + else + { + elemU = xmlstr2unicode(&elem); + DPRINT1("unknown elem %wZ\n", &elemU); + ret = parse_unknown_elem(xmlbuf, &elem); + } + } + + return ret; +} + +static BOOL parse_com_interface_external_proxy_stub_elem(xmlbuf_t* xmlbuf, - struct assembly* assembly) ++ struct assembly* assembly, ++ struct actctx_loader* acl) +{ + xmlstr_t attr_name, attr_value; + UNICODE_STRING attr_nameU, attr_valueU; + BOOL end = FALSE, error; + struct entity* entity; + + entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION); + if (!entity) return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, iidW)) + { - if (!(entity->u.proxy.iid = xmlstrdupW(&attr_value))) return FALSE; ++ if (!(entity->u.ifaceps.iid = xmlstrdupW(&attr_value))) return FALSE; + } - if (xmlstr_cmp(&attr_name, g_nameW)) ++ else if (xmlstr_cmp(&attr_name, g_nameW)) ++ { ++ if (!(entity->u.ifaceps.name = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, baseInterfaceW)) ++ { ++ if (!(entity->u.ifaceps.base = xmlstrdupW(&attr_value))) return FALSE; ++ entity->u.ifaceps.mask |= BaseIface; ++ } ++ else if (xmlstr_cmp(&attr_name, nummethodsW)) ++ { ++ if (!(parse_nummethods(&attr_value, entity))) return FALSE; ++ entity->u.ifaceps.mask |= NumMethods; ++ } ++ else if (xmlstr_cmp(&attr_name, proxyStubClsid32W)) ++ { ++ if (!(entity->u.ifaceps.ps32 = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, tlbidW)) + { - if (!(entity->u.proxy.name = xmlstrdupW(&attr_value))) return FALSE; ++ if (!(entity->u.ifaceps.tlib = xmlstrdupW(&attr_value))) return FALSE; + } + else + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + - if (error || end) return end; ++ if (error) return FALSE; ++ acl->actctx->sections |= IFACEREDIRECT_SECTION; ++ if (end) return TRUE; ++ + return parse_expect_end_elem(xmlbuf, comInterfaceExternalProxyStubW, asmv1W); +} + - static BOOL parse_clr_class_elem(xmlbuf_t* xmlbuf, struct assembly* assembly) ++static BOOL parse_clr_class_elem(xmlbuf_t* xmlbuf, struct assembly* assembly, struct actctx_loader *acl) +{ - xmlstr_t attr_name, attr_value; - UNICODE_STRING attr_nameU, attr_valueU; - BOOL end = FALSE, error; ++ xmlstr_t attr_name, attr_value, elem; ++ BOOL end = FALSE, error, ret = TRUE; + struct entity* entity; + - entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION); ++ entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION); + if (!entity) return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, g_nameW)) + { - if (!(entity->u.clrclass.name = xmlstrdupW(&attr_value))) return FALSE; ++ if (!(entity->u.comclass.name = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, clsidW)) + { - if (!(entity->u.clrclass.clsid = xmlstrdupW(&attr_value))) return FALSE; ++ if (!(entity->u.comclass.clsid = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, progidW)) ++ { ++ if (!(entity->u.comclass.progid = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, tlbidW)) ++ { ++ if (!(entity->u.comclass.tlbid = xmlstrdupW(&attr_value))) return FALSE; ++ } ++ else if (xmlstr_cmp(&attr_name, threadingmodelW)) ++ { ++ entity->u.comclass.model = parse_com_class_threadingmodel(&attr_value); ++ } ++ else if (xmlstr_cmp(&attr_name, runtimeVersionW)) ++ { ++ if (!(entity->u.comclass.version = xmlstrdupW(&attr_value))) return FALSE; + } + else + { ++ UNICODE_STRING attr_nameU, attr_valueU; + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + - if (error || end) return end; - return parse_expect_end_elem(xmlbuf, clrClassW, asmv1W); ++ if (error) return FALSE; ++ acl->actctx->sections |= SERVERREDIRECT_SECTION; ++ if (entity->u.comclass.progid) ++ acl->actctx->sections |= PROGIDREDIRECT_SECTION; ++ if (end) return TRUE; ++ ++ while (ret && (ret = next_xml_elem(xmlbuf, &elem))) ++ { ++ if (xmlstr_cmp_end(&elem, clrClassW)) ++ { ++ ret = parse_end_element(xmlbuf); ++ break; ++ } ++ else if (xmlstr_cmp(&elem, progidW)) ++ { ++ ret = parse_com_class_progid(xmlbuf, entity); ++ } ++ else ++ { ++ UNICODE_STRING elemU = xmlstr2unicode(&elem); ++ DPRINT1("unknown elem %wZ\n", &elemU); ++ ret = parse_unknown_elem(xmlbuf, &elem); ++ } ++ } ++ ++ if (entity->u.comclass.progids.num) ++ acl->actctx->sections |= PROGIDREDIRECT_SECTION; ++ ++ return ret; +} + - static BOOL parse_clr_surrogate_elem(xmlbuf_t* xmlbuf, struct assembly* assembly) ++static BOOL parse_clr_surrogate_elem(xmlbuf_t* xmlbuf, struct assembly* assembly, struct actctx_loader *acl) +{ + xmlstr_t attr_name, attr_value; + UNICODE_STRING attr_nameU, attr_valueU; + BOOL end = FALSE, error; + struct entity* entity; + + entity = add_entity(&assembly->entities, ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES); + if (!entity) return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + if (xmlstr_cmp(&attr_name, g_nameW)) + { + if (!(entity->u.clrsurrogate.name = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, clsidW)) + { + if (!(entity->u.clrsurrogate.clsid = xmlstrdupW(&attr_value))) return FALSE; + } ++ else if (xmlstr_cmp(&attr_name, runtimeVersionW)) ++ { ++ if (!(entity->u.clrsurrogate.version = xmlstrdupW(&attr_value))) return FALSE; ++ } + else + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + - if (error || end) return end; ++ if (error) return FALSE; ++ acl->actctx->sections |= CLRSURROGATES_SECTION; ++ if (end) return TRUE; ++ + return parse_expect_end_elem(xmlbuf, clrSurrogateW, asmv1W); +} + +static BOOL parse_dependent_assembly_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl, BOOL optional) +{ + struct assembly_identity ai; + xmlstr_t elem; + BOOL end = FALSE, ret = TRUE; + + if (!parse_expect_no_attr(xmlbuf, &end) || end) return end; + + memset(&ai, 0, sizeof(ai)); + ai.optional = optional; + + if (!parse_expect_elem(xmlbuf, assemblyIdentityW, asmv1W) || + !parse_assembly_identity_elem(xmlbuf, acl->actctx, &ai)) + return FALSE; + ++ //TRACE( "adding name=%s version=%s arch=%s\n", debugstr_w(ai.name), debugstr_version(&ai.version), debugstr_w(ai.arch) ); ++ + /* store the newly found identity for later loading */ + if (!add_dependent_assembly_id(acl, &ai)) return FALSE; + + while (ret && (ret = next_xml_elem(xmlbuf, &elem))) + { + if (xmlstr_cmp_end(&elem, dependentAssemblyW)) + { + ret = parse_end_element(xmlbuf); + break; + } + else if (xmlstr_cmp(&elem, bindingRedirectW)) + { + ret = parse_binding_redirect_elem(xmlbuf); + } + else + { + DPRINT1("unknown elem %S\n", elem.ptr); + ret = parse_unknown_elem(xmlbuf, &elem); + } + } + + return ret; +} + +static BOOL parse_dependency_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl) +{ + xmlstr_t attr_name, attr_value, elem; + UNICODE_STRING attr_nameU, attr_valueU; + BOOL end = FALSE, ret = TRUE, error, optional = FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + + if (xmlstr_cmp(&attr_name, optionalW)) + { - static const WCHAR yesW[] = {'y','e','s',0}; + optional = xmlstr_cmpi( &attr_value, yesW ); + DPRINT1("optional=%wZ\n", &attr_valueU); + } + else + { + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + + while (ret && (ret = next_xml_elem(xmlbuf, &elem))) + { + if (xmlstr_cmp_end(&elem, dependencyW)) + { + ret = parse_end_element(xmlbuf); + break; + } + else if (xmlstr_cmp(&elem, dependentAssemblyW)) + { + ret = parse_dependent_assembly_elem(xmlbuf, acl, optional); + } + else + { + attr_nameU = xmlstr2unicode(&elem); + DPRINT1("unknown element %wZ\n", &attr_nameU); + ret = parse_unknown_elem(xmlbuf, &elem); + } + } + + return ret; +} + +static BOOL parse_noinherit_elem(xmlbuf_t* xmlbuf) +{ + BOOL end = FALSE; + + if (!parse_expect_no_attr(xmlbuf, &end)) return FALSE; + return end || parse_expect_end_elem(xmlbuf, noInheritW, asmv1W); +} + +static BOOL parse_noinheritable_elem(xmlbuf_t* xmlbuf) +{ + BOOL end = FALSE; + + if (!parse_expect_no_attr(xmlbuf, &end)) return FALSE; + return end || parse_expect_end_elem(xmlbuf, noInheritableW, asmv1W); +} + - static BOOL parse_file_elem(xmlbuf_t* xmlbuf, struct assembly* assembly) ++static BOOL parse_file_elem(xmlbuf_t* xmlbuf, struct assembly* assembly, struct actctx_loader* acl) +{ + xmlstr_t attr_name, attr_value, elem; + UNICODE_STRING attr_nameU, attr_valueU; + BOOL end = FALSE, error, ret = TRUE; + struct dll_redirect* dll; + + if (!(dll = add_dll_redirect(assembly))) return FALSE; + + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + + if (xmlstr_cmp(&attr_name, g_nameW)) + { + if (!(dll->name = xmlstrdupW(&attr_value))) return FALSE; + DPRINT("name=%wZ\n", &attr_valueU); + } + else if (xmlstr_cmp(&attr_name, hashW)) + { + if (!(dll->hash = xmlstrdupW(&attr_value))) return FALSE; + } + else if (xmlstr_cmp(&attr_name, hashalgW)) + { + static const WCHAR sha1W[] = {'S','H','A','1',0}; + if (!xmlstr_cmpi(&attr_value, sha1W)) + DPRINT1("hashalg should be SHA1, got %wZ\n", &attr_valueU); + } + else + { + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + + if (error || !dll->name) return FALSE; ++ ++ acl->actctx->sections |= DLLREDIRECT_SECTION; ++ + if (end) return TRUE; + + while (ret && (ret = next_xml_elem(xmlbuf, &elem))) + { + if (xmlstr_cmp_end(&elem, fileW)) + { + ret = parse_end_element(xmlbuf); + break; + } + else if (xmlstr_cmp(&elem, comClassW)) + { - ret = parse_com_class_elem(xmlbuf, dll); ++ ret = parse_com_class_elem(xmlbuf, dll, acl); + } + else if (xmlstr_cmp(&elem, comInterfaceProxyStubW)) + { - ret = parse_cominterface_proxy_stub_elem(xmlbuf, dll); ++ ret = parse_cominterface_proxy_stub_elem(xmlbuf, dll, acl); + } - else if (xmlstr_cmp(&elem, asmv2hashW)) ++ else if (xml_elem_cmp(&elem, hashW, asmv2W)) + { + DPRINT1("asmv2hash (undocumented) not supported\n"); + ret = parse_unknown_elem(xmlbuf, &elem); + } + else if (xmlstr_cmp(&elem, typelibW)) + { - ret = parse_typelib_elem(xmlbuf, dll); ++ ret = parse_typelib_elem(xmlbuf, dll, acl); + } + else if (xmlstr_cmp(&elem, windowClassW)) + { - ret = parse_window_class_elem(xmlbuf, dll); ++ ret = parse_window_class_elem(xmlbuf, dll, acl); + } + else + { + attr_nameU = xmlstr2unicode(&elem); + DPRINT1("unknown elem %wZ\n", &attr_nameU); + ret = parse_unknown_elem( xmlbuf, &elem ); + } + } + + return ret; +} + +static BOOL parse_assembly_elem(xmlbuf_t* xmlbuf, struct actctx_loader* acl, + struct assembly* assembly, + struct assembly_identity* expected_ai) +{ + xmlstr_t attr_name, attr_value, elem; + UNICODE_STRING attr_nameU, attr_valueU; + BOOL end = FALSE, error, version = FALSE, xmlns = FALSE, ret = TRUE; + ++ DPRINT("(%p)\n", xmlbuf); ++ + while (next_xml_attr(xmlbuf, &attr_name, &attr_value, &error, &end)) + { + attr_nameU = xmlstr2unicode(&attr_name); + attr_valueU = xmlstr2unicode(&attr_value); + + if (xmlstr_cmp(&attr_name, manifestVersionW)) + { + static const WCHAR v10W[] = {'1','.','0',0}; + if (!xmlstr_cmp(&attr_value, v10W)) + { + DPRINT1("wrong version %wZ\n", &attr_valueU); + return FALSE; + } + version = TRUE; + } + else if (xmlstr_cmp(&attr_name, xmlnsW)) + { + if (!xmlstr_cmp(&attr_value, manifestv1W) && !xmlstr_cmp(&attr_value, manifestv3W)) + { + DPRINT1("wrong namespace %wZ\n", &attr_valueU); + return FALSE; + } + xmlns = TRUE; + } + else + { + DPRINT1("unknown attr %wZ=%wZ\n", &attr_nameU, &attr_valueU); + } + } + + if (error || end || !xmlns || !version) return FALSE; + if (!next_xml_elem(xmlbuf, &elem)) return FALSE; + + if (assembly->type == APPLICATION_MANIFEST && xmlstr_cmp(&elem, noInheritW)) + { + if (!parse_noinherit_elem(xmlbuf) || !next_xml_elem(xmlbuf, &elem)) + return FALSE; + assembly->no_inherit = TRUE; + } + - if (xmlstr_cmp(&elem, noInheritableW)) ++ if (xml_elem_cmp(&elem, noInheritableW, asmv1W)) + { + if (!parse_noinheritable_elem(xmlbuf) || !next_xml_elem(xmlbuf, &elem)) + return FALSE; + } + else if ((assembly->type == ASSEMBLY_MANIFEST || assembly->type == ASSEMBLY_SHARED_MANIFEST) && + assembly->no_inherit) + return FALSE; + + while (ret) + { - if (xmlstr_cmp_end(&elem, assemblyW)) ++ if (xml_elem_cmp_end(&elem, assemblyW, asmv1W)) + { + ret = parse_end_element(xmlbuf); + break; + } - else if (xmlstr_cmp(&elem, descriptionW)) ++ else if (xml_elem_cmp(&elem, descriptionW, asmv1W)) + { + ret = parse_description_elem(xmlbuf); + } - else if (xmlstr_cmp(&elem, comInterfaceExternalProxyStubW)) ++ else if (xml_elem_cmp(&elem, comInterfaceExternalProxyStubW, asmv1W)) + { - ret = parse_com_interface_external_proxy_stub_elem(xmlbuf, assembly); ++ ret = parse_com_interface_external_proxy_stub_elem(xmlbuf, assembly, acl); + } - else if (xmlstr_cmp(&elem, dependencyW)) ++ else if (xml_elem_cmp(&elem, dependencyW, asmv1W)) + { + ret = parse_dependency_elem(xmlbuf, acl); + } - else if (xmlstr_cmp(&elem, fileW)) ++ else if (xml_elem_cmp(&elem, fileW, asmv1W)) + { - ret = parse_file_elem(xmlbuf, assembly); ++ ret = parse_file_elem(xmlbuf, assembly, acl); + } - else if (xmlstr_cmp(&elem, clrClassW)) ++ else if (xml_elem_cmp(&elem, clrClassW, asmv1W)) + { - ret = parse_clr_class_elem(xmlbuf, assembly); ++ ret = parse_clr_class_elem(xmlbuf, assembly, acl); + } - else if (xmlstr_cmp(&elem, clrSurrogateW)) ++ else if (xml_elem_cmp(&elem, clrSurrogateW, asmv1W)) + { - ret = parse_clr_surrogate_elem(xmlbuf, assembly); ++ ret = parse_clr_surrogate_elem(xmlbuf, assembly, acl); + } - else if (xmlstr_cmp(&elem, assemblyIdentityW)) ++ else if (xml_elem_cmp(&elem, assemblyIdentityW, asmv1W)) + { + if (!parse_assembly_identity_elem(xmlbuf, acl->actctx, &assembly->id)) return FALSE; + + if (expected_ai) + { + /* FIXME: more tests */ + if (assembly->type == ASSEMBLY_MANIFEST && + memcmp(&assembly->id.version, &expected_ai->version, sizeof(assembly->id.version))) + { + DPRINT1("wrong version for assembly manifest: %u.%u.%u.%u / %u.%u.%u.%u\n", + expected_ai->version.major, expected_ai->version.minor, + expected_ai->version.build, expected_ai->version.revision, + assembly->id.version.major, assembly->id.version.minor, + assembly->id.version.build, assembly->id.version.revision); + ret = FALSE; + } + else if (assembly->type == ASSEMBLY_SHARED_MANIFEST && + (assembly->id.version.major != expected_ai->version.major || + assembly->id.version.minor != expected_ai->version.minor || + assembly->id.version.build < expected_ai->version.build || + (assembly->id.version.build == expected_ai->version.build && + assembly->id.version.revision < expected_ai->version.revision))) + { + DPRINT1("wrong version for shared assembly manifest\n"); + ret = FALSE; + } + } + } + else + { + attr_nameU = xmlstr2unicode(&elem); + DPRINT1("unknown element %wZ\n", &attr_nameU); + ret = parse_unknown_elem(xmlbuf, &elem); + } + if (ret) ret = next_xml_elem(xmlbuf, &elem); + } + + return ret; +} + +static NTSTATUS parse_manifest_buffer( struct actctx_loader* acl, struct assembly *assembly, + struct assembly_identity* ai, xmlbuf_t *xmlbuf ) +{ + xmlstr_t elem; + UNICODE_STRING elemU; + + if (!next_xml_elem(xmlbuf, &elem)) return STATUS_SXS_CANT_GEN_ACTCTX; + + if (xmlstr_cmp(&elem, g_xmlW) && + (!parse_xml_header(xmlbuf) || !next_xml_elem(xmlbuf, &elem))) + return STATUS_SXS_CANT_GEN_ACTCTX; + - if (!xmlstr_cmp(&elem, assemblyW)) ++ if (!xml_elem_cmp(&elem, assemblyW, asmv1W)) + { + elemU = xmlstr2unicode(&elem); + DPRINT1("root element is %wZ, not \n", &elemU); + return STATUS_SXS_CANT_GEN_ACTCTX; + } + + if (!parse_assembly_elem(xmlbuf, acl, assembly, ai)) + { + DPRINT1("failed to parse manifest %S\n", assembly->manifest.info ); + return STATUS_SXS_CANT_GEN_ACTCTX; + } + + if (next_xml_elem(xmlbuf, &elem)) + { + elemU = xmlstr2unicode(&elem); + DPRINT1("unexpected element %wZ\n", &elemU); + return STATUS_SXS_CANT_GEN_ACTCTX; + } + + if (xmlbuf->ptr != xmlbuf->end) + { + DPRINT1("parse error\n"); + return STATUS_SXS_CANT_GEN_ACTCTX; + } + return STATUS_SUCCESS; +} + +static NTSTATUS parse_manifest( struct actctx_loader* acl, struct assembly_identity* ai, + LPCWSTR filename, LPCWSTR directory, BOOL shared, + const void *buffer, SIZE_T size ) +{ + xmlbuf_t xmlbuf; + NTSTATUS status; + struct assembly *assembly; + int unicode_tests; + + DPRINT( "parsing manifest loaded from %S base dir %S\n", filename, directory ); + + if (!(assembly = add_assembly(acl->actctx, shared ? ASSEMBLY_SHARED_MANIFEST : ASSEMBLY_MANIFEST))) + return STATUS_SXS_CANT_GEN_ACTCTX; + + if (directory && !(assembly->directory = strdupW(directory))) + return STATUS_NO_MEMORY; + + if (filename) assembly->manifest.info = strdupW( filename + 4 /* skip \??\ prefix */ ); + assembly->manifest.type = assembly->manifest.info ? ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE + : ACTIVATION_CONTEXT_PATH_TYPE_NONE; + + unicode_tests = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE; + if (RtlIsTextUnicode((PVOID)buffer, (ULONG)size, &unicode_tests )) + { + xmlbuf.ptr = buffer; + xmlbuf.end = xmlbuf.ptr + size / sizeof(WCHAR); + status = parse_manifest_buffer( acl, assembly, ai, &xmlbuf ); + } + else if (unicode_tests & IS_TEXT_UNICODE_REVERSE_SIGNATURE) + { + const WCHAR *buf = buffer; + WCHAR *new_buff; + unsigned int i; + + if (!(new_buff = RtlAllocateHeap( RtlGetProcessHeap(), 0, size ))) + return STATUS_NO_MEMORY; + for (i = 0; i < size / sizeof(WCHAR); i++) + new_buff[i] = RtlUshortByteSwap( buf[i] ); + xmlbuf.ptr = new_buff; + xmlbuf.end = xmlbuf.ptr + size / sizeof(WCHAR); + status = parse_manifest_buffer( acl, assembly, ai, &xmlbuf ); + RtlFreeHeap( RtlGetProcessHeap(), 0, new_buff ); + } + else + { + /* TODO: this doesn't handle arbitrary encodings */ + WCHAR *new_buff; + ULONG sizeU; + + status = RtlMultiByteToUnicodeSize(&sizeU, buffer, size); + if (!NT_SUCCESS(status)) + { + DPRINT1("RtlMultiByteToUnicodeSize failed with %lx\n", status); + return STATUS_SXS_CANT_GEN_ACTCTX; + } + + new_buff = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeU); + if (!new_buff) + return STATUS_NO_MEMORY; + + status = RtlMultiByteToUnicodeN(new_buff, sizeU, &sizeU, buffer, size); + if (!NT_SUCCESS(status)) + { + DPRINT1("RtlMultiByteToUnicodeN failed with %lx\n", status); + return STATUS_SXS_CANT_GEN_ACTCTX; + } + + xmlbuf.ptr = new_buff; + xmlbuf.end = xmlbuf.ptr + sizeU / sizeof(WCHAR); + status = parse_manifest_buffer(acl, assembly, ai, &xmlbuf); + RtlFreeHeap(RtlGetProcessHeap(), 0, new_buff); + } + return status; +} + +static NTSTATUS open_nt_file( HANDLE *handle, UNICODE_STRING *name ) +{ + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = name; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + return NtOpenFile(handle, + GENERIC_READ | SYNCHRONIZE, + &attr, &io, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_ALERT); +} + +static NTSTATUS get_module_filename( HMODULE module, UNICODE_STRING *str, USHORT extra_len ) +{ + NTSTATUS status; + ULONG magic; + LDR_DATA_TABLE_ENTRY *pldr; + + LdrLockLoaderLock(0, NULL, &magic); + status = LdrFindEntryForAddress( module, &pldr ); + if (status == STATUS_SUCCESS) + { + if ((str->Buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, + pldr->FullDllName.Length + extra_len + sizeof(WCHAR) ))) + { + memcpy( str->Buffer, pldr->FullDllName.Buffer, pldr->FullDllName.Length + sizeof(WCHAR) ); + str->Length = pldr->FullDllName.Length; + str->MaximumLength = pldr->FullDllName.Length + extra_len + sizeof(WCHAR); + } + else status = STATUS_NO_MEMORY; + } + LdrUnlockLoaderLock(0, magic); + return status; +} + +static NTSTATUS get_manifest_in_module( struct actctx_loader* acl, struct assembly_identity* ai, + LPCWSTR filename, LPCWSTR directory, BOOL shared, + HANDLE hModule, LPCWSTR resname, ULONG lang ) +{ + NTSTATUS status; + UNICODE_STRING nameW; + LDR_RESOURCE_INFO info; + IMAGE_RESOURCE_DATA_ENTRY* entry = NULL; + void *ptr; + + //DPRINT( "looking for res %s in module %p %s\n", resname, + // hModule, filename ); + +#if 0 + if (TRACE_ON(actctx)) + { + if (!filename && !get_module_filename( hModule, &nameW, 0 )) + { + DPRINT( "looking for res %s in module %p %s\n", debugstr_w(resname), + hModule, debugstr_w(nameW.Buffer) ); + RtlFreeUnicodeString( &nameW ); + } + else DPRINT( "looking for res %s in module %p %s\n", debugstr_w(resname), + hModule, debugstr_w(filename) ); + } +#endif + + if (!resname) return STATUS_INVALID_PARAMETER; + + info.Type = (ULONG_PTR)RT_MANIFEST; + info.Language = lang; + if (!((ULONG_PTR)resname >> 16)) + { + info.Name = (ULONG_PTR)resname; + status = LdrFindResource_U(hModule, &info, 3, &entry); + } + else if (resname[0] == '#') + { + ULONG value; + RtlInitUnicodeString(&nameW, resname + 1); + if (RtlUnicodeStringToInteger(&nameW, 10, &value) != STATUS_SUCCESS || HIWORD(value)) + return STATUS_INVALID_PARAMETER; + info.Name = value; + status = LdrFindResource_U(hModule, &info, 3, &entry); + } + else + { + RtlCreateUnicodeString(&nameW, resname); + RtlUpcaseUnicodeString(&nameW, &nameW, FALSE); + info.Name = (ULONG_PTR)nameW.Buffer; + status = LdrFindResource_U(hModule, &info, 3, &entry); + RtlFreeUnicodeString(&nameW); + } + if (status == STATUS_SUCCESS) status = LdrAccessResource(hModule, entry, &ptr, NULL); + + if (status == STATUS_SUCCESS) + status = parse_manifest(acl, ai, filename, directory, shared, ptr, entry->Size); + + return status; +} + +static NTSTATUS get_manifest_in_pe_file( struct actctx_loader* acl, struct assembly_identity* ai, + LPCWSTR filename, LPCWSTR directory, BOOL shared, + HANDLE file, LPCWSTR resname, ULONG lang ) +{ + HANDLE mapping; + OBJECT_ATTRIBUTES attr; + LARGE_INTEGER size; + LARGE_INTEGER offset; + NTSTATUS status; + SIZE_T count; + void *base; + + DPRINT( "looking for res %S in %S\n", resname, filename ); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = NULL; + attr.Attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + size.QuadPart = 0; + status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ, + &attr, &size, PAGE_READONLY, SEC_COMMIT, file ); + if (status != STATUS_SUCCESS) return status; + + offset.QuadPart = 0; + count = 0; + base = NULL; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &base, 0, 0, &offset, + &count, ViewShare, 0, PAGE_READONLY ); + NtClose( mapping ); + if (status != STATUS_SUCCESS) return status; + + if (RtlImageNtHeader(base)) /* we got a PE file */ + { + HANDLE module = (HMODULE)((ULONG_PTR)base | 1); /* make it a LOAD_LIBRARY_AS_DATAFILE handle */ + status = get_manifest_in_module( acl, ai, filename, directory, shared, module, resname, lang ); + } + else status = STATUS_INVALID_IMAGE_FORMAT; + + NtUnmapViewOfSection( NtCurrentProcess(), base ); + return status; +} + +static NTSTATUS get_manifest_in_manifest_file( struct actctx_loader* acl, struct assembly_identity* ai, + LPCWSTR filename, LPCWSTR directory, BOOL shared, HANDLE file ) +{ + FILE_STANDARD_INFORMATION info; + IO_STATUS_BLOCK io; + HANDLE mapping; + OBJECT_ATTRIBUTES attr; + LARGE_INTEGER size; + LARGE_INTEGER offset; + NTSTATUS status; + SIZE_T count; + void *base; + + DPRINT( "loading manifest file %S\n", filename ); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.ObjectName = NULL; + attr.Attributes = OBJ_CASE_INSENSITIVE | OBJ_OPENIF; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + size.QuadPart = 0; + status = NtCreateSection( &mapping, STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ, + &attr, &size, PAGE_READONLY, SEC_COMMIT, file ); + + if (status != STATUS_SUCCESS) return status; + + offset.QuadPart = 0; + count = 0; + base = NULL; + status = NtMapViewOfSection( mapping, NtCurrentProcess(), &base, 0, 0, &offset, + &count, ViewShare, 0, PAGE_READONLY ); - ++ NtClose( mapping ); + if (status != STATUS_SUCCESS) return status; + + /* Fixme: WINE uses FileEndOfFileInformation with NtQueryInformationFile. */ + status = NtQueryInformationFile( file, &io, &info, sizeof(info), FileStandardInformation); + + if (status == STATUS_SUCCESS) + status = parse_manifest(acl, ai, filename, directory, shared, base, (SIZE_T)info.EndOfFile.QuadPart); + + NtUnmapViewOfSection( NtCurrentProcess(), base ); - NtClose( mapping ); + return status; +} + +/* try to load the .manifest file associated to the file */ +static NTSTATUS get_manifest_in_associated_manifest( struct actctx_loader* acl, struct assembly_identity* ai, + LPCWSTR filename, LPCWSTR directory, HMODULE module, LPCWSTR resname ) +{ + static const WCHAR fmtW[] = { '.','%','l','u',0 }; + WCHAR *buffer; + NTSTATUS status; + UNICODE_STRING nameW; + HANDLE file; + ULONG_PTR resid = (ULONG_PTR)CREATEPROCESS_MANIFEST_RESOURCE_ID; + + if (!((ULONG_PTR)resname >> 16)) resid = (ULONG_PTR)resname & 0xffff; + + DPRINT( "looking for manifest associated with %S id %lu\n", filename, resid ); + + if (module) /* use the module filename */ + { + UNICODE_STRING name; + + if (!(status = get_module_filename( module, &name, sizeof(dotManifestW) + 10*sizeof(WCHAR) ))) + { + if (resid != 1) sprintfW( name.Buffer + strlenW(name.Buffer), fmtW, resid ); + strcatW( name.Buffer, dotManifestW ); + if (!RtlDosPathNameToNtPathName_U( name.Buffer, &nameW, NULL, NULL )) + status = STATUS_RESOURCE_DATA_NOT_FOUND; + RtlFreeUnicodeString( &name ); + } + if (status) return status; + } + else + { + if (!(buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, + (strlenW(filename) + 10) * sizeof(WCHAR) + sizeof(dotManifestW) ))) + return STATUS_NO_MEMORY; + strcpyW( buffer, filename ); + if (resid != 1) sprintfW( buffer + strlenW(buffer), fmtW, resid ); + strcatW( buffer, dotManifestW ); + RtlInitUnicodeString( &nameW, buffer ); + } + + if (!open_nt_file( &file, &nameW )) + { + status = get_manifest_in_manifest_file( acl, ai, nameW.Buffer, directory, FALSE, file ); + NtClose( file ); + } - else status = STATUS_RESOURCE_DATA_NOT_FOUND; ++ else status = STATUS_RESOURCE_TYPE_NOT_FOUND; + RtlFreeUnicodeString( &nameW ); + return status; +} + +static WCHAR *lookup_manifest_file( HANDLE dir, struct assembly_identity *ai ) +{ + static const WCHAR lookup_fmtW[] = + {'%','s','_','%','s','_','%','s','_','%','u','.','%','u','.','*','.','*','_', + '*', /* FIXME */ + '.','m','a','n','i','f','e','s','t',0}; + + WCHAR *lookup, *ret = NULL; + UNICODE_STRING lookup_us; + IO_STATUS_BLOCK io; ++ const WCHAR *lang = ai->language; + unsigned int data_pos = 0, data_len; + char buffer[8192]; + + if (!(lookup = RtlAllocateHeap( RtlGetProcessHeap(), 0, + (strlenW(ai->arch) + strlenW(ai->name) + + strlenW(ai->public_key) + 20) * sizeof(WCHAR) + + sizeof(lookup_fmtW) ))) + return NULL; + - sprintfW( lookup, lookup_fmtW, ai->arch, ai->name, ai->public_key, ai->version.major, ai->version.minor); ++ if (!lang || !strcmpiW( lang, neutralW )) lang = wildcardW; ++ sprintfW( lookup, lookup_fmtW, ai->arch, ai->name, ai->public_key, ++ ai->version.major, ai->version.minor, lang ); + RtlInitUnicodeString( &lookup_us, lookup ); + + NtQueryDirectoryFile( dir, 0, NULL, NULL, &io, buffer, sizeof(buffer), + FileBothDirectoryInformation, FALSE, &lookup_us, TRUE ); + if (io.Status == STATUS_SUCCESS) + { + FILE_BOTH_DIR_INFORMATION *dir_info; + WCHAR *tmp; + ULONG build, revision; + + data_len = (ULONG)io.Information; + + for (;;) + { + if (data_pos >= data_len) + { + NtQueryDirectoryFile( dir, 0, NULL, NULL, &io, buffer, sizeof(buffer), + FileBothDirectoryInformation, FALSE, &lookup_us, FALSE ); + if (io.Status != STATUS_SUCCESS) break; + data_len = (ULONG)io.Information; + data_pos = 0; + } + dir_info = (FILE_BOTH_DIR_INFORMATION*)(buffer + data_pos); + + if (dir_info->NextEntryOffset) data_pos += dir_info->NextEntryOffset; + else data_pos = data_len; + + tmp = (WCHAR *)dir_info->FileName + (strchrW(lookup, '*') - lookup); + build = atoiW(tmp); + if (build < ai->version.build) continue; + tmp = strchrW(tmp, '.') + 1; + revision = atoiW(tmp); + if (build == ai->version.build && revision < ai->version.revision) + continue; + ai->version.build = (USHORT)build; + ai->version.revision = (USHORT)revision; + + if ((ret = RtlAllocateHeap( RtlGetProcessHeap(), 0, dir_info->FileNameLength * sizeof(WCHAR) ))) + { + memcpy( ret, dir_info->FileName, dir_info->FileNameLength ); + ret[dir_info->FileNameLength/sizeof(WCHAR)] = 0; + } + break; + } + } + else DPRINT1("no matching file for %S\n", lookup); + RtlFreeHeap( RtlGetProcessHeap(), 0, lookup ); + return ret; +} + +static NTSTATUS lookup_winsxs(struct actctx_loader* acl, struct assembly_identity* ai) +{ + struct assembly_identity sxs_ai; + UNICODE_STRING path_us; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + WCHAR *path, *file = NULL; + HANDLE handle; + + static const WCHAR manifest_dirW[] = + {'\\','w','i','n','s','x','s','\\','m','a','n','i','f','e','s','t','s',0}; + + if (!ai->arch || !ai->name || !ai->public_key) return STATUS_NO_SUCH_FILE; + + if (!(path = RtlAllocateHeap( RtlGetProcessHeap(), 0, + ((strlenW(SharedUserData->NtSystemRoot) + 1) *sizeof(WCHAR)) + sizeof(manifest_dirW) ))) + return STATUS_NO_MEMORY; + + memcpy( path, SharedUserData->NtSystemRoot, strlenW(SharedUserData->NtSystemRoot) * sizeof(WCHAR) ); + memcpy( path + strlenW(SharedUserData->NtSystemRoot), manifest_dirW, sizeof(manifest_dirW) ); + + if (!RtlDosPathNameToNtPathName_U( path, &path_us, NULL, NULL )) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, path ); + return STATUS_NO_SUCH_FILE; + } + RtlFreeHeap( RtlGetProcessHeap(), 0, path ); + + attr.Length = sizeof(attr); + attr.RootDirectory = 0; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.ObjectName = &path_us; + attr.SecurityDescriptor = NULL; + attr.SecurityQualityOfService = NULL; + + if (!NtOpenFile(&handle, + GENERIC_READ | SYNCHRONIZE, + &attr, &io, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT)) + { + sxs_ai = *ai; + file = lookup_manifest_file( handle, &sxs_ai ); + NtClose( handle ); + } + if (!file) + { + RtlFreeUnicodeString( &path_us ); + return STATUS_NO_SUCH_FILE; + } + + /* append file name to directory path */ + if (!(path = RtlReAllocateHeap( RtlGetProcessHeap(), 0, path_us.Buffer, + path_us.Length + (strlenW(file) + 2) * sizeof(WCHAR) ))) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, file ); + RtlFreeUnicodeString( &path_us ); + return STATUS_NO_MEMORY; + } + + path[path_us.Length/sizeof(WCHAR)] = '\\'; + strcpyW( path + path_us.Length/sizeof(WCHAR) + 1, file ); + RtlInitUnicodeString( &path_us, path ); + *strrchrW(file, '.') = 0; /* remove .manifest extension */ + + if (!open_nt_file( &handle, &path_us )) + { + io.Status = get_manifest_in_manifest_file(acl, &sxs_ai, path_us.Buffer, file, TRUE, handle); + NtClose( handle ); + } + else io.Status = STATUS_NO_SUCH_FILE; + + RtlFreeHeap( RtlGetProcessHeap(), 0, file ); + RtlFreeUnicodeString( &path_us ); + return io.Status; +} + +static NTSTATUS lookup_assembly(struct actctx_loader* acl, + struct assembly_identity* ai) +{ + static const WCHAR dotDllW[] = {'.','d','l','l',0}; + unsigned int i; + WCHAR *buffer, *p, *directory; + NTSTATUS status; + UNICODE_STRING nameW; + HANDLE file; + DWORD len; + + DPRINT( "looking for name=%S version=%u.%u.%u.%u arch=%S\n", + ai->name, ai->version.major, ai->version.minor, ai->version.build, ai->version.revision, ai->arch ); + + if ((status = lookup_winsxs(acl, ai)) != STATUS_NO_SUCH_FILE) return status; + + /* FIXME: add support for language specific lookup */ + + len = max(RtlGetFullPathName_U(acl->actctx->assemblies->manifest.info, 0, NULL, NULL) / sizeof(WCHAR), + strlenW(acl->actctx->appdir.info)); + + nameW.Buffer = NULL; + if (!(buffer = RtlAllocateHeap( RtlGetProcessHeap(), 0, + (len + 2 * strlenW(ai->name) + 2) * sizeof(WCHAR) + sizeof(dotManifestW) ))) + return STATUS_NO_MEMORY; + + if (!(directory = build_assembly_dir( ai ))) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, buffer ); + return STATUS_NO_MEMORY; + } + + /* Lookup in \name.dll + * \name.manifest + * \name\name.dll + * \name\name.manifest + * + * First 'appdir' is used as , if that failed + * it tries application manifest file path. + */ + strcpyW( buffer, acl->actctx->appdir.info ); + p = buffer + strlenW(buffer); + for (i = 0; i < 4; i++) + { + if (i == 2) + { + struct assembly *assembly = acl->actctx->assemblies; + if (!RtlGetFullPathName_U(assembly->manifest.info, len * sizeof(WCHAR), buffer, &p)) break; + } + else *p++ = '\\'; + + strcpyW( p, ai->name ); + p += strlenW(p); + + strcpyW( p, dotDllW ); + if (RtlDosPathNameToNtPathName_U( buffer, &nameW, NULL, NULL )) + { + status = open_nt_file( &file, &nameW ); + if (!status) + { + status = get_manifest_in_pe_file( acl, ai, nameW.Buffer, directory, FALSE, file, + (LPCWSTR)CREATEPROCESS_MANIFEST_RESOURCE_ID, 0 ); + NtClose( file ); + break; + } + RtlFreeUnicodeString( &nameW ); + } + + strcpyW( p, dotManifestW ); + if (RtlDosPathNameToNtPathName_U( buffer, &nameW, NULL, NULL )) + { + status = open_nt_file( &file, &nameW ); + if (!status) + { + status = get_manifest_in_manifest_file( acl, ai, nameW.Buffer, directory, FALSE, file ); + NtClose( file ); + break; + } + RtlFreeUnicodeString( &nameW ); + } + status = STATUS_SXS_ASSEMBLY_NOT_FOUND; + } + RtlFreeUnicodeString( &nameW ); + RtlFreeHeap( RtlGetProcessHeap(), 0, directory ); + RtlFreeHeap( RtlGetProcessHeap(), 0, buffer ); + return status; +} + +static NTSTATUS parse_depend_manifests(struct actctx_loader* acl) +{ + NTSTATUS status = STATUS_SUCCESS; + unsigned int i; + + for (i = 0; i < acl->num_dependencies; i++) + { + if (lookup_assembly(acl, &acl->dependencies[i]) != STATUS_SUCCESS) + { + if (!acl->dependencies[i].optional) + { - DPRINT1( "Could not find dependent assembly %S\n", acl->dependencies[i].name ); ++ const struct assembly_version *ver = &acl->dependencies[i].version; ++ DPRINT1( "Could not find dependent assembly %S (%u.%u.%u.%u)\n", ++ acl->dependencies[i].name, ++ ver->major, ver->minor, ver->build, ver->revision ); + status = STATUS_SXS_CANT_GEN_ACTCTX; + break; + } + } + } + /* FIXME should now iterate through all refs */ + return status; +} + +/* find the appropriate activation context for RtlQueryInformationActivationContext */ +static NTSTATUS find_query_actctx( HANDLE *handle, DWORD flags, ULONG class ) +{ + NTSTATUS status = STATUS_SUCCESS; + + if (flags & RTL_QUERY_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT) + { + if (*handle) return STATUS_INVALID_PARAMETER; + + if (NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame) + *handle = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame->ActivationContext; + } + else if (flags & (RTL_QUERY_ACTIVATION_CONTEXT_FLAG_IS_ADDRESS | RTL_QUERY_ACTIVATION_CONTEXT_FLAG_IS_HMODULE)) + { + ULONG magic; + LDR_DATA_TABLE_ENTRY *pldr; + + if (!*handle) return STATUS_INVALID_PARAMETER; + + LdrLockLoaderLock( 0, NULL, &magic ); + if (!LdrFindEntryForAddress( *handle, &pldr )) + { + if ((flags & RTL_QUERY_ACTIVATION_CONTEXT_FLAG_IS_HMODULE) && *handle != pldr->DllBase) + status = STATUS_DLL_NOT_FOUND; + else + *handle = pldr->EntryPointActivationContext; + } + else status = STATUS_DLL_NOT_FOUND; + LdrUnlockLoaderLock( 0, magic ); + } + else if (!*handle && (class != ActivationContextBasicInformation)) + *handle = process_actctx; + + return status; +} + - static NTSTATUS fill_keyed_data(PACTCTX_SECTION_KEYED_DATA data, PVOID v1, PVOID v2, unsigned int i) ++static NTSTATUS build_dllredirect_section(ACTIVATION_CONTEXT* actctx, struct strsection_header **section) ++{ ++ unsigned int i, j, total_len = 0, dll_count = 0; ++ struct strsection_header *header; ++ struct dllredirect_data *data; ++ struct string_index *index; ++ ULONG name_offset; ++ ++ /* compute section length */ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ ++ /* each entry needs index, data and string data */ ++ total_len += sizeof(*index); ++ total_len += sizeof(*data); ++ total_len += aligned_string_len((strlenW(dll->name)+1)*sizeof(WCHAR)); ++ } ++ ++ dll_count += assembly->num_dlls; ++ } ++ ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = STRSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = dll_count; ++ header->index_offset = sizeof(*header); ++ index = (struct string_index*)((BYTE*)header + header->index_offset); ++ name_offset = header->index_offset + header->count*sizeof(*index); ++ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ UNICODE_STRING str; ++ WCHAR *ptrW; ++ ++ /* setup new index entry */ ++ str.Buffer = dll->name; ++ str.Length = strlenW(dll->name)*sizeof(WCHAR); ++ str.MaximumLength = str.Length + sizeof(WCHAR); ++ /* hash original class name */ ++ RtlHashUnicodeString(&str, TRUE, HASH_STRING_ALGORITHM_X65599, &index->hash); ++ ++ index->name_offset = name_offset; ++ index->name_len = str.Length; ++ index->data_offset = index->name_offset + aligned_string_len(str.MaximumLength); ++ index->data_len = sizeof(*data); ++ index->rosterindex = i + 1; ++ ++ /* setup data */ ++ data = (struct dllredirect_data*)((BYTE*)header + index->data_offset); ++ data->size = sizeof(*data); ++ data->unk = 2; /* FIXME: seems to be constant */ ++ memset(data->res, 0, sizeof(data->res)); ++ ++ /* dll name */ ++ ptrW = (WCHAR*)((BYTE*)header + index->name_offset); ++ memcpy(ptrW, dll->name, index->name_len); ++ ptrW[index->name_len/sizeof(WCHAR)] = 0; ++ ++ name_offset += sizeof(*data) + aligned_string_len(str.MaximumLength); ++ ++ index++; ++ } ++ } ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; ++} ++ ++static struct string_index *find_string_index(const struct strsection_header *section, const UNICODE_STRING *name) ++{ ++ struct string_index *iter, *index = NULL; ++ ULONG hash = 0, i; ++ ++ RtlHashUnicodeString(name, TRUE, HASH_STRING_ALGORITHM_X65599, &hash); ++ iter = (struct string_index*)((BYTE*)section + section->index_offset); ++ ++ for (i = 0; i < section->count; i++) ++ { ++ if (iter->hash == hash) ++ { ++ const WCHAR *nameW = (WCHAR*)((BYTE*)section + iter->name_offset); ++ ++ if (!strcmpiW(nameW, name->Buffer)) ++ { ++ index = iter; ++ break; ++ } ++ else ++ DPRINT1("hash collision 0x%08x, %wZ, %S\n", hash, name, nameW); ++ } ++ iter++; ++ } ++ ++ return index; ++} ++ ++static struct guid_index *find_guid_index(const struct guidsection_header *section, const GUID *guid) ++{ ++ struct guid_index *iter, *index = NULL; ++ ULONG i; ++ ++ iter = (struct guid_index*)((BYTE*)section + section->index_offset); ++ ++ for (i = 0; i < section->count; i++) ++ { ++ if (!memcmp(guid, &iter->guid, sizeof(*guid))) ++ { ++ index = iter; ++ break; ++ } ++ iter++; ++ } ++ ++ return index; ++} ++ ++static inline struct dllredirect_data *get_dllredirect_data(ACTIVATION_CONTEXT *ctxt, struct string_index *index) ++{ ++ return (struct dllredirect_data*)((BYTE*)ctxt->dllredirect_section + index->data_offset); ++} ++ ++static NTSTATUS find_dll_redirection(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name, ++ PACTCTX_SECTION_KEYED_DATA data) +{ ++ struct dllredirect_data *dll; ++ struct string_index *index; ++ ++ if (!(actctx->sections & DLLREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->dllredirect_section) ++ { ++ struct strsection_header *section; ++ ++ NTSTATUS status = build_dllredirect_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->dllredirect_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ index = find_string_index(actctx->dllredirect_section, name); ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ dll = get_dllredirect_data(actctx, index); ++ + data->ulDataFormatVersion = 1; - data->lpData = v1; - data->ulLength = 20; /* FIXME */ - data->lpSectionGlobalData = NULL; /* FIXME */ - data->ulSectionGlobalDataLength = 0; /* FIXME */ - data->lpSectionBase = v2; - data->ulSectionTotalLength = 0; /* FIXME */ ++ data->lpData = dll; ++ data->ulLength = dll->size; ++ data->lpSectionGlobalData = NULL; ++ data->ulSectionGlobalDataLength = 0; ++ data->lpSectionBase = actctx->dllredirect_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->dllredirect_section ); + data->hActCtx = NULL; - if (data->cbSize >= offsetof(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) - data->ulAssemblyRosterIndex = i + 1; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; + + return STATUS_SUCCESS; +} + - static NTSTATUS find_dll_redirection(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *section_name, - PACTCTX_SECTION_KEYED_DATA data) ++static inline struct string_index *get_wndclass_first_index(ACTIVATION_CONTEXT *actctx) ++{ ++ return (struct string_index*)((BYTE*)actctx->wndclass_section + actctx->wndclass_section->index_offset); ++} ++ ++static inline struct wndclass_redirect_data *get_wndclass_data(ACTIVATION_CONTEXT *ctxt, struct string_index *index) +{ - unsigned int i, j, snlen = section_name->Length / sizeof(WCHAR); ++ return (struct wndclass_redirect_data*)((BYTE*)ctxt->wndclass_section + index->data_offset); ++} ++ ++static NTSTATUS build_wndclass_section(ACTIVATION_CONTEXT* actctx, struct strsection_header **section) ++{ ++ unsigned int i, j, k, total_len = 0, class_count = 0; ++ struct wndclass_redirect_data *data; ++ struct strsection_header *header; ++ struct string_index *index; ++ ULONG name_offset; + ++ /* compute section length */ + for (i = 0; i < actctx->num_assemblies; i++) + { + struct assembly *assembly = &actctx->assemblies[i]; + for (j = 0; j < assembly->num_dlls; j++) + { + struct dll_redirect *dll = &assembly->dlls[j]; - if (!strncmpiW(section_name->Buffer, dll->name, snlen) && !dll->name[snlen]) - return fill_keyed_data(data, dll, assembly, i); ++ for (k = 0; k < dll->entities.num; k++) ++ { ++ struct entity *entity = &dll->entities.base[k]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION) ++ { ++ int class_len = strlenW(entity->u.class.name) + 1; ++ int len; ++ ++ /* each class entry needs index, data and string data */ ++ total_len += sizeof(*index); ++ total_len += sizeof(*data); ++ /* original name is stored separately */ ++ total_len += aligned_string_len(class_len*sizeof(WCHAR)); ++ /* versioned name and module name are stored one after another */ ++ if (entity->u.class.versioned) ++ len = get_assembly_version(assembly, NULL) + class_len + 1 /* '!' separator */; ++ else ++ len = class_len; ++ len += strlenW(dll->name) + 1; ++ total_len += aligned_string_len(len*sizeof(WCHAR)); ++ ++ class_count++; ++ } ++ } + } + } - return STATUS_SXS_KEY_NOT_FOUND; - } + - static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *section_name, - PACTCTX_SECTION_KEYED_DATA data) - { - unsigned int i, j, k, snlen = section_name->Length / sizeof(WCHAR); ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = STRSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = class_count; ++ header->index_offset = sizeof(*header); ++ index = (struct string_index*)((BYTE*)header + header->index_offset); ++ name_offset = header->index_offset + header->count*sizeof(*index); + + for (i = 0; i < actctx->num_assemblies; i++) + { + struct assembly *assembly = &actctx->assemblies[i]; + for (j = 0; j < assembly->num_dlls; j++) + { + struct dll_redirect *dll = &assembly->dlls[j]; + for (k = 0; k < dll->entities.num; k++) + { + struct entity *entity = &dll->entities.base[k]; + if (entity->kind == ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION) + { - if (!strncmpiW(section_name->Buffer, entity->u.class.name, snlen) && !entity->u.class.name[snlen]) - return fill_keyed_data(data, entity, dll, i); ++ static const WCHAR exclW[] = {'!',0}; ++ ULONG versioned_len, module_len; ++ UNICODE_STRING str; ++ WCHAR *ptrW; ++ ++ /* setup new index entry */ ++ str.Buffer = entity->u.class.name; ++ str.Length = strlenW(entity->u.class.name)*sizeof(WCHAR); ++ str.MaximumLength = str.Length + sizeof(WCHAR); ++ /* hash original class name */ ++ RtlHashUnicodeString(&str, TRUE, HASH_STRING_ALGORITHM_X65599, &index->hash); ++ ++ /* include '!' separator too */ ++ if (entity->u.class.versioned) ++ versioned_len = (get_assembly_version(assembly, NULL) + 1)*sizeof(WCHAR) + str.Length; ++ else ++ versioned_len = str.Length; ++ module_len = strlenW(dll->name)*sizeof(WCHAR); ++ ++ index->name_offset = name_offset; ++ index->name_len = str.Length; ++ index->data_offset = index->name_offset + aligned_string_len(str.MaximumLength); ++ index->data_len = sizeof(*data) + versioned_len + module_len + 2*sizeof(WCHAR) /* two nulls */; ++ index->rosterindex = i + 1; ++ ++ /* setup data */ ++ data = (struct wndclass_redirect_data*)((BYTE*)header + index->data_offset); ++ data->size = sizeof(*data); ++ data->res = 0; ++ data->name_len = versioned_len; ++ data->name_offset = sizeof(*data); ++ data->module_len = module_len; ++ data->module_offset = index->data_offset + data->name_offset + data->name_len + sizeof(WCHAR); ++ ++ /* original class name */ ++ ptrW = (WCHAR*)((BYTE*)header + index->name_offset); ++ memcpy(ptrW, entity->u.class.name, index->name_len); ++ ptrW[index->name_len/sizeof(WCHAR)] = 0; ++ ++ /* module name */ ++ ptrW = (WCHAR*)((BYTE*)header + data->module_offset); ++ memcpy(ptrW, dll->name, data->module_len); ++ ptrW[data->module_len/sizeof(WCHAR)] = 0; ++ ++ /* versioned name */ ++ ptrW = (WCHAR*)((BYTE*)data + data->name_offset); ++ if (entity->u.class.versioned) ++ { ++ get_assembly_version(assembly, ptrW); ++ strcatW(ptrW, exclW); ++ strcatW(ptrW, entity->u.class.name); ++ } ++ else ++ { ++ memcpy(ptrW, entity->u.class.name, index->name_len); ++ ptrW[index->name_len/sizeof(WCHAR)] = 0; ++ } ++ ++ name_offset += sizeof(*data); ++ name_offset += aligned_string_len(str.MaximumLength) + aligned_string_len(versioned_len + module_len + 2*sizeof(WCHAR)); ++ ++ index++; + } + } + } + } - return STATUS_SXS_KEY_NOT_FOUND; ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; +} + - static NTSTATUS find_string(ACTIVATION_CONTEXT* actctx, ULONG section_kind, - const UNICODE_STRING *section_name, - DWORD flags, PACTCTX_SECTION_KEYED_DATA data) ++static NTSTATUS find_window_class(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name, ++ PACTCTX_SECTION_KEYED_DATA data) +{ - NTSTATUS status; ++ struct string_index *iter, *index = NULL; ++ struct wndclass_redirect_data *class; ++ ULONG hash; ++ int i; + - switch (section_kind) ++ if (!(actctx->sections & WINDOWCLASS_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->wndclass_section) + { - case ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION: - status = find_dll_redirection(actctx, section_name, data); - break; - case ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION: - status = find_window_class(actctx, section_name, data); - break; - case ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION: - case ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION: - case ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION: - case ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION: - case ACTIVATION_CONTEXT_SECTION_GLOBAL_OBJECT_RENAME_TABLE: - case ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES: - DPRINT1("Unsupported yet section_kind %x\n", section_kind); - return STATUS_SXS_SECTION_NOT_FOUND; - default: - DPRINT1("Unknown section_kind %x\n", section_kind); - return STATUS_SXS_SECTION_NOT_FOUND; ++ struct strsection_header *section; ++ ++ NTSTATUS status = build_wndclass_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->wndclass_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); + } + - if (status != STATUS_SUCCESS) return status; ++ hash = 0; ++ RtlHashUnicodeString(name, TRUE, HASH_STRING_ALGORITHM_X65599, &hash); ++ iter = get_wndclass_first_index(actctx); + - if (flags & FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX) ++ for (i = 0; i < actctx->wndclass_section->count; i++) + { - actctx_addref(actctx); - data->hActCtx = actctx; ++ if (iter->hash == hash) ++ { ++ const WCHAR *nameW = (WCHAR*)((BYTE*)actctx->wndclass_section + iter->name_offset); ++ ++ if (!strcmpW(nameW, name->Buffer)) ++ { ++ index = iter; ++ break; ++ } ++ else ++ DPRINT1("hash collision 0x%08x, %wZ, %S\n", hash, name, nameW); ++ } ++ iter++; + } ++ ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ class = get_wndclass_data(actctx, index); ++ ++ data->ulDataFormatVersion = 1; ++ data->lpData = class; ++ /* full length includes string length with nulls */ ++ data->ulLength = class->size + class->name_len + class->module_len + 2*sizeof(WCHAR); ++ data->lpSectionGlobalData = NULL; ++ data->ulSectionGlobalDataLength = 0; ++ data->lpSectionBase = actctx->wndclass_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->wndclass_section ); ++ data->hActCtx = NULL; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; ++ + return STATUS_SUCCESS; +} + - static NTSTATUS find_guid(ACTIVATION_CONTEXT* actctx, ULONG section_kind, - const GUID *guid, DWORD flags, PACTCTX_SECTION_KEYED_DATA data) ++static NTSTATUS build_tlib_section(ACTIVATION_CONTEXT* actctx, struct guidsection_header **section) +{ - NTSTATUS status; ++ unsigned int i, j, k, total_len = 0, tlib_count = 0, names_len = 0; ++ struct guidsection_header *header; ++ ULONG module_offset, data_offset; ++ struct tlibredirect_data *data; ++ struct guid_index *index; + - switch (section_kind) ++ /* compute section length */ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ for (k = 0; k < dll->entities.num; k++) ++ { ++ struct entity *entity = &dll->entities.base[k]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION) ++ { ++ /* each entry needs index, data and string data for module name and help string */ ++ total_len += sizeof(*index); ++ total_len += sizeof(*data); ++ /* help string is stored separately */ ++ if (*entity->u.typelib.helpdir) ++ total_len += aligned_string_len((strlenW(entity->u.typelib.helpdir)+1)*sizeof(WCHAR)); ++ ++ /* module names are packed one after another */ ++ names_len += (strlenW(dll->name)+1)*sizeof(WCHAR); ++ ++ tlib_count++; ++ } ++ } ++ } ++ } ++ ++ total_len += aligned_string_len(names_len); ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = GUIDSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = tlib_count; ++ header->index_offset = sizeof(*header) + aligned_string_len(names_len); ++ index = (struct guid_index*)((BYTE*)header + header->index_offset); ++ module_offset = sizeof(*header); ++ data_offset = header->index_offset + tlib_count*sizeof(*index); ++ ++ for (i = 0; i < actctx->num_assemblies; i++) + { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ for (k = 0; k < dll->entities.num; k++) ++ { ++ struct entity *entity = &dll->entities.base[k]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION) ++ { ++ ULONG module_len, help_len; ++ UNICODE_STRING str; ++ WCHAR *ptrW; ++ ++ if (*entity->u.typelib.helpdir) ++ help_len = strlenW(entity->u.typelib.helpdir)*sizeof(WCHAR); ++ else ++ help_len = 0; ++ ++ module_len = strlenW(dll->name)*sizeof(WCHAR); ++ ++ /* setup new index entry */ ++ RtlInitUnicodeString(&str, entity->u.typelib.tlbid); ++ RtlGUIDFromString(&str, &index->guid); ++ index->data_offset = data_offset; ++ index->data_len = sizeof(*data) + aligned_string_len(help_len); ++ index->rosterindex = i + 1; ++ ++ /* setup data */ ++ data = (struct tlibredirect_data*)((BYTE*)header + index->data_offset); ++ data->size = sizeof(*data); ++ data->res = 0; ++ data->name_len = module_len; ++ data->name_offset = module_offset; ++ /* FIXME: resourceid handling is really weird, and it doesn't seem to be useful */ ++ data->langid = 0; ++ data->flags = entity->u.typelib.flags; ++ data->help_len = help_len; ++ data->help_offset = sizeof(*data); ++ data->major_version = entity->u.typelib.major; ++ data->minor_version = entity->u.typelib.minor; ++ ++ /* module name */ ++ ptrW = (WCHAR*)((BYTE*)header + data->name_offset); ++ memcpy(ptrW, dll->name, data->name_len); ++ ptrW[data->name_len/sizeof(WCHAR)] = 0; ++ ++ /* help string */ ++ if (data->help_len) ++ { ++ ptrW = (WCHAR*)((BYTE*)data + data->help_offset); ++ memcpy(ptrW, entity->u.typelib.helpdir, data->help_len); ++ ptrW[data->help_len/sizeof(WCHAR)] = 0; ++ } ++ ++ data_offset += sizeof(*data); ++ if (help_len) ++ data_offset += aligned_string_len(help_len + sizeof(WCHAR)); ++ ++ module_offset += module_len + sizeof(WCHAR); ++ ++ index++; ++ } ++ } ++ } ++ } ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; ++} ++ ++static inline struct tlibredirect_data *get_tlib_data(ACTIVATION_CONTEXT *actctx, struct guid_index *index) ++{ ++ return (struct tlibredirect_data*)((BYTE*)actctx->tlib_section + index->data_offset); ++} ++ ++static NTSTATUS find_tlib_redirection(ACTIVATION_CONTEXT* actctx, const GUID *guid, ACTCTX_SECTION_KEYED_DATA* data) ++{ ++ struct guid_index *index = NULL; ++ struct tlibredirect_data *tlib; ++ ++ if (!(actctx->sections & TLIBREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->tlib_section) ++ { ++ struct guidsection_header *section; ++ ++ NTSTATUS status = build_tlib_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->tlib_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ index = find_guid_index(actctx->tlib_section, guid); ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ tlib = get_tlib_data(actctx, index); ++ ++ data->ulDataFormatVersion = 1; ++ data->lpData = tlib; ++ /* full length includes string length with nulls */ ++ data->ulLength = tlib->size + tlib->help_len + sizeof(WCHAR); ++ data->lpSectionGlobalData = (BYTE*)actctx->tlib_section + actctx->tlib_section->names_offset; ++ data->ulSectionGlobalDataLength = actctx->tlib_section->names_len; ++ data->lpSectionBase = actctx->tlib_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->tlib_section ); ++ data->hActCtx = NULL; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; ++ ++ return STATUS_SUCCESS; ++} ++ ++static void generate_uuid(ULONG *seed, GUID *guid) ++{ ++ ULONG *ptr = (ULONG*)guid; ++ int i; ++ ++ /* GUID is 16 bytes long */ ++ for (i = 0; i < sizeof(GUID)/sizeof(ULONG); i++, ptr++) ++ *ptr = RtlUniform(seed); ++ ++ guid->Data3 &= 0x0fff; ++ guid->Data3 |= (4 << 12); ++ guid->Data4[0] &= 0x3f; ++ guid->Data4[0] |= 0x80; ++} ++ ++static void get_comserver_datalen(const struct entity_array *entities, const struct dll_redirect *dll, ++ unsigned int *count, unsigned int *len, unsigned int *module_len) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < entities->num; i++) ++ { ++ struct entity *entity = &entities->base[i]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION) ++ { ++ /* each entry needs two index entries, extra one goes for alias GUID */ ++ *len += 2*sizeof(struct guid_index); ++ /* To save some memory we don't allocated two data structures, ++ instead alias index and normal index point to the same data structure. */ ++ *len += sizeof(struct comclassredirect_data); ++ ++ /* for clrClass store some more */ ++ if (entity->u.comclass.name) ++ { ++ unsigned int str_len; ++ ++ /* all string data is stored together in aligned block */ ++ str_len = strlenW(entity->u.comclass.name)+1; ++ if (entity->u.comclass.progid) ++ str_len += strlenW(entity->u.comclass.progid)+1; ++ if (entity->u.comclass.version) ++ str_len += strlenW(entity->u.comclass.version)+1; ++ ++ *len += sizeof(struct clrclass_data); ++ *len += aligned_string_len(str_len*sizeof(WCHAR)); ++ ++ /* module name is forced to mscoree.dll, and stored two times with different case */ ++ *module_len += sizeof(mscoreeW) + sizeof(mscoree2W); ++ } ++ else ++ { ++ /* progid string is stored separately */ ++ if (entity->u.comclass.progid) ++ *len += aligned_string_len((strlenW(entity->u.comclass.progid)+1)*sizeof(WCHAR)); ++ ++ *module_len += (strlenW(dll->name)+1)*sizeof(WCHAR); ++ } ++ ++ *count += 1; ++ } ++ } ++} ++ ++static void add_comserver_record(const struct guidsection_header *section, const struct entity_array *entities, ++ const struct dll_redirect *dll, struct guid_index **index, ULONG *data_offset, ULONG *module_offset, ++ ULONG *seed, ULONG rosterindex) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < entities->num; i++) ++ { ++ struct entity *entity = &entities->base[i]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION) ++ { ++ ULONG module_len, progid_len, str_len = 0; ++ struct comclassredirect_data *data; ++ struct guid_index *alias_index; ++ struct clrclass_data *clrdata; ++ UNICODE_STRING str; ++ WCHAR *ptrW; ++ ++ if (entity->u.comclass.progid) ++ progid_len = strlenW(entity->u.comclass.progid)*sizeof(WCHAR); ++ else ++ progid_len = 0; ++ ++ module_len = dll ? strlenW(dll->name)*sizeof(WCHAR) : strlenW(mscoreeW)*sizeof(WCHAR); ++ ++ /* setup new index entry */ ++ RtlInitUnicodeString(&str, entity->u.comclass.clsid); ++ RtlGUIDFromString(&str, &(*index)->guid); ++ ++ (*index)->data_offset = *data_offset; ++ (*index)->data_len = sizeof(*data); /* additional length added later */ ++ (*index)->rosterindex = rosterindex; ++ ++ /* Setup new index entry for alias guid. Alias index records are placed after ++ normal records, so normal guids are hit first on search. Note that class count ++ is doubled. */ ++ alias_index = (*index) + section->count/2; ++ generate_uuid(seed, &alias_index->guid); ++ alias_index->data_offset = (*index)->data_offset; ++ alias_index->data_len = 0; ++ alias_index->rosterindex = (*index)->rosterindex; ++ ++ /* setup data */ ++ data = (struct comclassredirect_data*)((BYTE*)section + (*index)->data_offset); ++ data->size = sizeof(*data); ++ data->res = 0; ++ data->res1[0] = 0; ++ data->res1[1] = 0; ++ data->model = entity->u.comclass.model; ++ data->clsid = (*index)->guid; ++ data->alias = alias_index->guid; ++ data->clsid2 = data->clsid; ++ if (entity->u.comclass.tlbid) ++ { ++ RtlInitUnicodeString(&str, entity->u.comclass.tlbid); ++ RtlGUIDFromString(&str, &data->tlbid); ++ } ++ else ++ memset(&data->tlbid, 0, sizeof(data->tlbid)); ++ data->name_len = module_len; ++ data->name_offset = *module_offset; ++ data->progid_len = progid_len; ++ data->progid_offset = data->progid_len ? data->size : 0; /* in case of clrClass additional offset is added later */ ++ data->clrdata_len = 0; /* will be set later */ ++ data->clrdata_offset = entity->u.comclass.name ? sizeof(*data) : 0; ++ data->miscstatus = entity->u.comclass.miscstatus; ++ data->miscstatuscontent = entity->u.comclass.miscstatuscontent; ++ data->miscstatusthumbnail = entity->u.comclass.miscstatusthumbnail; ++ data->miscstatusicon = entity->u.comclass.miscstatusicon; ++ data->miscstatusdocprint = entity->u.comclass.miscstatusdocprint; ++ ++ /* mask describes which misc* data is available */ ++ data->miscmask = 0; ++ if (data->miscstatus) ++ data->miscmask |= MiscStatus; ++ if (data->miscstatuscontent) ++ data->miscmask |= MiscStatusContent; ++ if (data->miscstatusthumbnail) ++ data->miscmask |= MiscStatusThumbnail; ++ if (data->miscstatusicon) ++ data->miscmask |= MiscStatusIcon; ++ if (data->miscstatusdocprint) ++ data->miscmask |= MiscStatusDocPrint; ++ ++ if (data->clrdata_offset) ++ { ++ clrdata = (struct clrclass_data*)((BYTE*)data + data->clrdata_offset); ++ ++ clrdata->size = sizeof(*clrdata); ++ clrdata->res[0] = 0; ++ clrdata->res[1] = 2; /* FIXME: unknown field */ ++ clrdata->module_len = strlenW(mscoreeW)*sizeof(WCHAR); ++ clrdata->module_offset = *module_offset + data->name_len + sizeof(WCHAR); ++ clrdata->name_len = strlenW(entity->u.comclass.name)*sizeof(WCHAR); ++ clrdata->name_offset = clrdata->size; ++ clrdata->version_len = entity->u.comclass.version ? strlenW(entity->u.comclass.version)*sizeof(WCHAR) : 0; ++ clrdata->version_offset = clrdata->version_len ? clrdata->name_offset + clrdata->name_len + sizeof(WCHAR) : 0; ++ clrdata->res2[0] = 0; ++ clrdata->res2[1] = 0; ++ ++ data->clrdata_len = clrdata->size + clrdata->name_len + sizeof(WCHAR); ++ ++ /* module name */ ++ ptrW = (WCHAR*)((BYTE*)section + clrdata->module_offset); ++ memcpy(ptrW, mscoree2W, clrdata->module_len); ++ ptrW[clrdata->module_len/sizeof(WCHAR)] = 0; ++ ++ ptrW = (WCHAR*)((BYTE*)section + data->name_offset); ++ memcpy(ptrW, mscoreeW, data->name_len); ++ ptrW[data->name_len/sizeof(WCHAR)] = 0; ++ ++ /* class name */ ++ ptrW = (WCHAR*)((BYTE*)clrdata + clrdata->name_offset); ++ memcpy(ptrW, entity->u.comclass.name, clrdata->name_len); ++ ptrW[clrdata->name_len/sizeof(WCHAR)] = 0; ++ ++ /* runtime version, optional */ ++ if (clrdata->version_len) ++ { ++ data->clrdata_len += clrdata->version_len + sizeof(WCHAR); ++ ++ ptrW = (WCHAR*)((BYTE*)clrdata + clrdata->version_offset); ++ memcpy(ptrW, entity->u.comclass.version, clrdata->version_len); ++ ptrW[clrdata->version_len/sizeof(WCHAR)] = 0; ++ } ++ ++ if (data->progid_len) ++ data->progid_offset += data->clrdata_len; ++ (*index)->data_len += sizeof(*clrdata); ++ } ++ else ++ { ++ clrdata = NULL; ++ ++ /* module name */ ++ ptrW = (WCHAR*)((BYTE*)section + data->name_offset); ++ memcpy(ptrW, dll->name, data->name_len); ++ ptrW[data->name_len/sizeof(WCHAR)] = 0; ++ } ++ ++ /* progid string */ ++ if (data->progid_len) ++ { ++ ptrW = (WCHAR*)((BYTE*)data + data->progid_offset); ++ memcpy(ptrW, entity->u.comclass.progid, data->progid_len); ++ ptrW[data->progid_len/sizeof(WCHAR)] = 0; ++ } ++ ++ /* string block length */ ++ str_len = 0; ++ if (clrdata) ++ { ++ str_len += clrdata->name_len + sizeof(WCHAR); ++ if (clrdata->version_len) ++ str_len += clrdata->version_len + sizeof(WCHAR); ++ } ++ if (progid_len) ++ str_len += progid_len + sizeof(WCHAR); ++ ++ (*index)->data_len += aligned_string_len(str_len); ++ alias_index->data_len = (*index)->data_len; ++ ++ /* move to next data record */ ++ (*data_offset) += sizeof(*data) + aligned_string_len(str_len); ++ (*module_offset) += module_len + sizeof(WCHAR); ++ ++ if (clrdata) ++ { ++ (*data_offset) += sizeof(*clrdata); ++ (*module_offset) += clrdata->module_len + sizeof(WCHAR); ++ } ++ (*index) += 1; ++ } ++ } ++} ++ ++static NTSTATUS build_comserver_section(ACTIVATION_CONTEXT* actctx, struct guidsection_header **section) ++{ ++ unsigned int i, j, total_len = 0, class_count = 0, names_len = 0; ++ struct guidsection_header *header; ++ ULONG module_offset, data_offset; ++ struct guid_index *index; ++ ULONG seed; ++ ++ /* compute section length */ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ get_comserver_datalen(&assembly->entities, NULL, &class_count, &total_len, &names_len); ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ get_comserver_datalen(&dll->entities, dll, &class_count, &total_len, &names_len); ++ } ++ } ++ ++ total_len += aligned_string_len(names_len); ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = GUIDSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = 2*class_count; ++ header->index_offset = sizeof(*header) + aligned_string_len(names_len); ++ index = (struct guid_index*)((BYTE*)header + header->index_offset); ++ module_offset = sizeof(*header); ++ data_offset = header->index_offset + 2*class_count*sizeof(*index); ++ ++ seed = NtGetTickCount(); ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ add_comserver_record(header, &assembly->entities, NULL, &index, &data_offset, &module_offset, &seed, i+1); ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ add_comserver_record(header, &dll->entities, dll, &index, &data_offset, &module_offset, &seed, i+1); ++ } ++ } ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; ++} ++ ++static inline struct comclassredirect_data *get_comclass_data(ACTIVATION_CONTEXT *actctx, struct guid_index *index) ++{ ++ return (struct comclassredirect_data*)((BYTE*)actctx->comserver_section + index->data_offset); ++} ++ ++static NTSTATUS find_comserver_redirection(ACTIVATION_CONTEXT* actctx, const GUID *guid, ACTCTX_SECTION_KEYED_DATA* data) ++{ ++ struct comclassredirect_data *comclass; ++ struct guid_index *index = NULL; ++ ++ if (!(actctx->sections & SERVERREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->comserver_section) ++ { ++ struct guidsection_header *section; ++ ++ NTSTATUS status = build_comserver_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->comserver_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ index = find_guid_index(actctx->comserver_section, guid); ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ comclass = get_comclass_data(actctx, index); ++ ++ data->ulDataFormatVersion = 1; ++ data->lpData = comclass; ++ /* full length includes string length with nulls */ ++ data->ulLength = comclass->size + comclass->clrdata_len; ++ if (comclass->progid_len) data->ulLength += comclass->progid_len + sizeof(WCHAR); ++ data->lpSectionGlobalData = (BYTE*)actctx->comserver_section + actctx->comserver_section->names_offset; ++ data->ulSectionGlobalDataLength = actctx->comserver_section->names_len; ++ data->lpSectionBase = actctx->comserver_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->comserver_section ); ++ data->hActCtx = NULL; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; ++ ++ return STATUS_SUCCESS; ++} ++ ++static void get_ifaceps_datalen(const struct entity_array *entities, unsigned int *count, unsigned int *len) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < entities->num; i++) ++ { ++ struct entity *entity = &entities->base[i]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION) ++ { ++ *len += sizeof(struct guid_index) + sizeof(struct ifacepsredirect_data); ++ if (entity->u.ifaceps.name) ++ *len += aligned_string_len((strlenW(entity->u.ifaceps.name)+1)*sizeof(WCHAR)); ++ *count += 1; ++ } ++ } ++} ++ ++static void add_ifaceps_record(struct guidsection_header *section, struct entity_array *entities, ++ struct guid_index **index, ULONG *data_offset, ULONG rosterindex) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < entities->num; i++) ++ { ++ struct entity *entity = &entities->base[i]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION) ++ { ++ struct ifacepsredirect_data *data = (struct ifacepsredirect_data*)((BYTE*)section + *data_offset); ++ UNICODE_STRING str; ++ ULONG name_len; ++ ++ if (entity->u.ifaceps.name) ++ name_len = strlenW(entity->u.ifaceps.name)*sizeof(WCHAR); ++ else ++ name_len = 0; ++ ++ /* setup index */ ++ RtlInitUnicodeString(&str, entity->u.ifaceps.iid); ++ RtlGUIDFromString(&str, &(*index)->guid); ++ (*index)->data_offset = *data_offset; ++ (*index)->data_len = sizeof(*data) + name_len ? aligned_string_len(name_len + sizeof(WCHAR)) : 0; ++ (*index)->rosterindex = rosterindex; ++ ++ /* setup data record */ ++ data->size = sizeof(*data); ++ data->mask = entity->u.ifaceps.mask; ++ ++ /* proxyStubClsid32 value is only stored for external PS, ++ if set it's used as iid, otherwise 'iid' attribute value is used */ ++ if (entity->u.ifaceps.ps32) ++ { ++ RtlInitUnicodeString(&str, entity->u.ifaceps.ps32); ++ RtlGUIDFromString(&str, &data->iid); ++ } ++ else ++ data->iid = (*index)->guid; ++ ++ data->nummethods = entity->u.ifaceps.nummethods; ++ ++ if (entity->u.ifaceps.tlib) ++ { ++ RtlInitUnicodeString(&str, entity->u.ifaceps.tlib); ++ RtlGUIDFromString(&str, &data->tlbid); ++ } ++ else ++ memset(&data->tlbid, 0, sizeof(data->tlbid)); ++ ++ if (entity->u.ifaceps.base) ++ { ++ RtlInitUnicodeString(&str, entity->u.ifaceps.base); ++ RtlGUIDFromString(&str, &data->base); ++ } ++ else ++ memset(&data->base, 0, sizeof(data->base)); ++ ++ data->name_len = name_len; ++ data->name_offset = data->name_len ? sizeof(*data) : 0; ++ ++ /* name string */ ++ if (data->name_len) ++ { ++ WCHAR *ptrW = (WCHAR*)((BYTE*)data + data->name_offset); ++ memcpy(ptrW, entity->u.ifaceps.name, data->name_len); ++ ptrW[data->name_len/sizeof(WCHAR)] = 0; ++ } ++ ++ /* move to next record */ ++ (*index) += 1; ++ *data_offset += sizeof(*data); ++ if (data->name_len) ++ *data_offset += aligned_string_len(data->name_len + sizeof(WCHAR)); ++ } ++ } ++} ++ ++static NTSTATUS build_ifaceps_section(ACTIVATION_CONTEXT* actctx, struct guidsection_header **section) ++{ ++ unsigned int i, j, total_len = 0, count = 0; ++ struct guidsection_header *header; ++ struct guid_index *index; ++ ULONG data_offset; ++ ++ /* compute section length */ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ ++ get_ifaceps_datalen(&assembly->entities, &count, &total_len); ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ get_ifaceps_datalen(&dll->entities, &count, &total_len); ++ } ++ } ++ ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = GUIDSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = count; ++ header->index_offset = sizeof(*header); ++ index = (struct guid_index*)((BYTE*)header + header->index_offset); ++ data_offset = header->index_offset + count*sizeof(*index); ++ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ ++ add_ifaceps_record(header, &assembly->entities, &index, &data_offset, i + 1); ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ add_ifaceps_record(header, &dll->entities, &index, &data_offset, i + 1); ++ } ++ } ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; ++} ++ ++static inline struct ifacepsredirect_data *get_ifaceps_data(ACTIVATION_CONTEXT *actctx, struct guid_index *index) ++{ ++ return (struct ifacepsredirect_data*)((BYTE*)actctx->ifaceps_section + index->data_offset); ++} ++ ++static NTSTATUS find_cominterface_redirection(ACTIVATION_CONTEXT* actctx, const GUID *guid, ACTCTX_SECTION_KEYED_DATA* data) ++{ ++ struct ifacepsredirect_data *iface; ++ struct guid_index *index = NULL; ++ ++ if (!(actctx->sections & IFACEREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->ifaceps_section) ++ { ++ struct guidsection_header *section; ++ ++ NTSTATUS status = build_ifaceps_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->ifaceps_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ index = find_guid_index(actctx->ifaceps_section, guid); ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ iface = get_ifaceps_data(actctx, index); ++ ++ data->ulDataFormatVersion = 1; ++ data->lpData = iface; ++ data->ulLength = iface->size + (iface->name_len ? iface->name_len + sizeof(WCHAR) : 0); ++ data->lpSectionGlobalData = NULL; ++ data->ulSectionGlobalDataLength = 0; ++ data->lpSectionBase = actctx->ifaceps_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->ifaceps_section ); ++ data->hActCtx = NULL; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; ++ ++ return STATUS_SUCCESS; ++} ++ ++static NTSTATUS build_clr_surrogate_section(ACTIVATION_CONTEXT* actctx, struct guidsection_header **section) ++{ ++ unsigned int i, j, total_len = 0, count = 0; ++ struct guidsection_header *header; ++ struct clrsurrogate_data *data; ++ struct guid_index *index; ++ ULONG data_offset; ++ ++ /* compute section length */ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ for (j = 0; j < assembly->entities.num; j++) ++ { ++ struct entity *entity = &assembly->entities.base[j]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES) ++ { ++ ULONG len; ++ ++ total_len += sizeof(*index) + sizeof(*data); ++ len = strlenW(entity->u.clrsurrogate.name) + 1; ++ if (entity->u.clrsurrogate.version) ++ len += strlenW(entity->u.clrsurrogate.version) + 1; ++ total_len += aligned_string_len(len*sizeof(WCHAR)); ++ ++ count++; ++ } ++ } ++ } ++ ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = GUIDSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = count; ++ header->index_offset = sizeof(*header); ++ index = (struct guid_index*)((BYTE*)header + header->index_offset); ++ data_offset = header->index_offset + count*sizeof(*index); ++ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ for (j = 0; j < assembly->entities.num; j++) ++ { ++ struct entity *entity = &assembly->entities.base[j]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES) ++ { ++ ULONG version_len, name_len; ++ UNICODE_STRING str; ++ WCHAR *ptrW; ++ ++ if (entity->u.clrsurrogate.version) ++ version_len = strlenW(entity->u.clrsurrogate.version)*sizeof(WCHAR); ++ else ++ version_len = 0; ++ name_len = strlenW(entity->u.clrsurrogate.name)*sizeof(WCHAR); ++ ++ /* setup new index entry */ ++ RtlInitUnicodeString(&str, entity->u.clrsurrogate.clsid); ++ RtlGUIDFromString(&str, &index->guid); ++ ++ index->data_offset = data_offset; ++ index->data_len = sizeof(*data) + aligned_string_len(name_len + sizeof(WCHAR) + (version_len ? version_len + sizeof(WCHAR) : 0)); ++ index->rosterindex = i + 1; ++ ++ /* setup data */ ++ data = (struct clrsurrogate_data*)((BYTE*)header + index->data_offset); ++ data->size = sizeof(*data); ++ data->res = 0; ++ data->clsid = index->guid; ++ data->version_offset = version_len ? data->size : 0; ++ data->version_len = version_len; ++ data->name_offset = data->size + version_len; ++ if (version_len) ++ data->name_offset += sizeof(WCHAR); ++ data->name_len = name_len; ++ ++ /* surrogate name */ ++ ptrW = (WCHAR*)((BYTE*)data + data->name_offset); ++ memcpy(ptrW, entity->u.clrsurrogate.name, data->name_len); ++ ptrW[data->name_len/sizeof(WCHAR)] = 0; ++ ++ /* runtime version */ ++ if (data->version_len) ++ { ++ ptrW = (WCHAR*)((BYTE*)data + data->version_offset); ++ memcpy(ptrW, entity->u.clrsurrogate.version, data->version_len); ++ ptrW[data->version_len/sizeof(WCHAR)] = 0; ++ } ++ ++ data_offset += index->data_offset; ++ index++; ++ } ++ } ++ } ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; ++} ++ ++static inline struct clrsurrogate_data *get_surrogate_data(ACTIVATION_CONTEXT *actctx, const struct guid_index *index) ++{ ++ return (struct clrsurrogate_data*)((BYTE*)actctx->clrsurrogate_section + index->data_offset); ++} ++ ++static NTSTATUS find_clr_surrogate(ACTIVATION_CONTEXT* actctx, const GUID *guid, ACTCTX_SECTION_KEYED_DATA* data) ++{ ++ struct clrsurrogate_data *surrogate; ++ struct guid_index *index = NULL; ++ ++ if (!(actctx->sections & CLRSURROGATES_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->clrsurrogate_section) ++ { ++ struct guidsection_header *section; ++ ++ NTSTATUS status = build_clr_surrogate_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->clrsurrogate_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ index = find_guid_index(actctx->clrsurrogate_section, guid); ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ surrogate = get_surrogate_data(actctx, index); ++ ++ data->ulDataFormatVersion = 1; ++ data->lpData = surrogate; ++ /* full length includes string length with nulls */ ++ data->ulLength = surrogate->size + surrogate->name_len + sizeof(WCHAR); ++ if (surrogate->version_len) ++ data->ulLength += surrogate->version_len + sizeof(WCHAR); ++ ++ data->lpSectionGlobalData = NULL; ++ data->ulSectionGlobalDataLength = 0; ++ data->lpSectionBase = actctx->clrsurrogate_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->clrsurrogate_section ); ++ data->hActCtx = NULL; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; ++ ++ return STATUS_SUCCESS; ++} ++ ++static void get_progid_datalen(struct entity_array *entities, unsigned int *count, unsigned int *total_len) ++{ ++ unsigned int i, j, single_len; ++ ++ single_len = sizeof(struct progidredirect_data) + sizeof(struct string_index) + sizeof(GUID); ++ for (i = 0; i < entities->num; i++) ++ { ++ struct entity *entity = &entities->base[i]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION) ++ { ++ if (entity->u.comclass.progid) ++ { ++ *total_len += single_len + aligned_string_len((strlenW(entity->u.comclass.progid)+1)*sizeof(WCHAR)); ++ *count += 1; ++ } ++ ++ for (j = 0; j < entity->u.comclass.progids.num; j++) ++ *total_len += aligned_string_len((strlenW(entity->u.comclass.progids.progids[j])+1)*sizeof(WCHAR)); ++ ++ *total_len += single_len*entity->u.comclass.progids.num; ++ *count += entity->u.comclass.progids.num; ++ } ++ } ++} ++ ++static void write_progid_record(struct strsection_header *section, const WCHAR *progid, const GUID *alias, ++ struct string_index **index, ULONG *data_offset, ULONG *global_offset, ULONG rosterindex) ++{ ++ struct progidredirect_data *data; ++ UNICODE_STRING str; ++ GUID *guid_ptr; ++ WCHAR *ptrW; ++ ++ /* setup new index entry */ ++ ++ /* hash progid name */ ++ RtlInitUnicodeString(&str, progid); ++ RtlHashUnicodeString(&str, TRUE, HASH_STRING_ALGORITHM_X65599, &(*index)->hash); ++ ++ (*index)->name_offset = *data_offset; ++ (*index)->name_len = str.Length; ++ (*index)->data_offset = (*index)->name_offset + aligned_string_len(str.MaximumLength); ++ (*index)->data_len = sizeof(*data); ++ (*index)->rosterindex = rosterindex; ++ ++ *data_offset += aligned_string_len(str.MaximumLength); ++ ++ /* setup data structure */ ++ data = (struct progidredirect_data*)((BYTE*)section + *data_offset); ++ data->size = sizeof(*data); ++ data->reserved = 0; ++ data->clsid_offset = *global_offset; ++ ++ /* write progid string */ ++ ptrW = (WCHAR*)((BYTE*)section + (*index)->name_offset); ++ memcpy(ptrW, progid, (*index)->name_len); ++ ptrW[(*index)->name_len/sizeof(WCHAR)] = 0; ++ ++ /* write guid to global area */ ++ guid_ptr = (GUID*)((BYTE*)section + data->clsid_offset); ++ *guid_ptr = *alias; ++ ++ /* to next entry */ ++ *global_offset += sizeof(GUID); ++ *data_offset += data->size; ++ (*index) += 1; ++} ++ ++static void add_progid_record(ACTIVATION_CONTEXT* actctx, struct strsection_header *section, const struct entity_array *entities, ++ struct string_index **index, ULONG *data_offset, ULONG *global_offset, ULONG rosterindex) ++{ ++ unsigned int i, j; ++ ++ for (i = 0; i < entities->num; i++) ++ { ++ struct entity *entity = &entities->base[i]; ++ if (entity->kind == ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION) ++ { ++ const struct progids *progids = &entity->u.comclass.progids; ++ struct comclassredirect_data *comclass; ++ struct guid_index *guid_index; ++ UNICODE_STRING str; ++ GUID clsid; ++ ++ RtlInitUnicodeString(&str, entity->u.comclass.clsid); ++ RtlGUIDFromString(&str, &clsid); ++ ++ guid_index = find_guid_index(actctx->comserver_section, &clsid); ++ comclass = get_comclass_data(actctx, guid_index); ++ ++ if (entity->u.comclass.progid) ++ write_progid_record(section, entity->u.comclass.progid, &comclass->alias, ++ index, data_offset, global_offset, rosterindex); ++ ++ for (j = 0; j < progids->num; j++) ++ write_progid_record(section, progids->progids[j], &comclass->alias, ++ index, data_offset, global_offset, rosterindex); ++ } ++ } ++} ++ ++static NTSTATUS build_progid_section(ACTIVATION_CONTEXT* actctx, struct strsection_header **section) ++{ ++ unsigned int i, j, total_len = 0, count = 0; ++ struct strsection_header *header; ++ ULONG data_offset, global_offset; ++ struct string_index *index; ++ ++ /* compute section length */ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ ++ get_progid_datalen(&assembly->entities, &count, &total_len); ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ get_progid_datalen(&dll->entities, &count, &total_len); ++ } ++ } ++ ++ total_len += sizeof(*header); ++ ++ header = RtlAllocateHeap(RtlGetProcessHeap(), 0, total_len); ++ if (!header) return STATUS_NO_MEMORY; ++ ++ memset(header, 0, sizeof(*header)); ++ header->magic = STRSECTION_MAGIC; ++ header->size = sizeof(*header); ++ header->count = count; ++ header->global_offset = header->size; ++ header->global_len = count*sizeof(GUID); ++ header->index_offset = header->size + header->global_len; ++ ++ index = (struct string_index*)((BYTE*)header + header->index_offset); ++ data_offset = header->index_offset + count*sizeof(*index); ++ global_offset = header->global_offset; ++ ++ for (i = 0; i < actctx->num_assemblies; i++) ++ { ++ struct assembly *assembly = &actctx->assemblies[i]; ++ ++ add_progid_record(actctx, header, &assembly->entities, &index, &data_offset, &global_offset, i + 1); ++ for (j = 0; j < assembly->num_dlls; j++) ++ { ++ struct dll_redirect *dll = &assembly->dlls[j]; ++ add_progid_record(actctx, header, &dll->entities, &index, &data_offset, &global_offset, i + 1); ++ } ++ } ++ ++ *section = header; ++ ++ return STATUS_SUCCESS; ++} ++ ++static inline struct progidredirect_data *get_progid_data(ACTIVATION_CONTEXT *actctx, const struct string_index *index) ++{ ++ return (struct progidredirect_data*)((BYTE*)actctx->progid_section + index->data_offset); ++} ++ ++static NTSTATUS find_progid_redirection(ACTIVATION_CONTEXT* actctx, const UNICODE_STRING *name, ++ PACTCTX_SECTION_KEYED_DATA data) ++{ ++ struct progidredirect_data *progid; ++ struct string_index *index; ++ ++ if (!(actctx->sections & PROGIDREDIRECT_SECTION)) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ if (!actctx->comserver_section) ++ { ++ struct guidsection_header *section; ++ ++ NTSTATUS status = build_comserver_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->comserver_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ if (!actctx->progid_section) ++ { ++ struct strsection_header *section; ++ ++ NTSTATUS status = build_progid_section(actctx, §ion); ++ if (status) return status; ++ ++ if (InterlockedCompareExchangePointer((void**)&actctx->progid_section, section, NULL)) ++ RtlFreeHeap(RtlGetProcessHeap(), 0, section); ++ } ++ ++ index = find_string_index(actctx->progid_section, name); ++ if (!index) return STATUS_SXS_KEY_NOT_FOUND; ++ ++ progid = get_progid_data(actctx, index); ++ ++ data->ulDataFormatVersion = 1; ++ data->lpData = progid; ++ data->ulLength = progid->size; ++ data->lpSectionGlobalData = (BYTE*)actctx->progid_section + actctx->progid_section->global_offset; ++ data->ulSectionGlobalDataLength = actctx->progid_section->global_len; ++ data->lpSectionBase = actctx->progid_section; ++ data->ulSectionTotalLength = RtlSizeHeap( RtlGetProcessHeap(), 0, actctx->progid_section ); ++ data->hActCtx = NULL; ++ ++ if (data->cbSize >= FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) + sizeof(ULONG)) ++ data->ulAssemblyRosterIndex = index->rosterindex; ++ ++ return STATUS_SUCCESS; ++} ++ ++static NTSTATUS find_string(ACTIVATION_CONTEXT* actctx, ULONG section_kind, ++ const UNICODE_STRING *section_name, ++ DWORD flags, PACTCTX_SECTION_KEYED_DATA data) ++{ ++ NTSTATUS status; ++ ++ switch (section_kind) ++ { ++ case ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION: ++ status = find_dll_redirection(actctx, section_name, data); ++ break; ++ case ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION: ++ status = find_window_class(actctx, section_name, data); ++ break; ++ case ACTIVATION_CONTEXT_SECTION_COM_PROGID_REDIRECTION: ++ status = find_progid_redirection(actctx, section_name, data); ++ break; ++ case ACTIVATION_CONTEXT_SECTION_GLOBAL_OBJECT_RENAME_TABLE: ++ DPRINT1("Unsupported yet section_kind %x\n", section_kind); ++ return STATUS_SXS_SECTION_NOT_FOUND; ++ default: ++ DPRINT1("Unknown section_kind %x\n", section_kind); ++ return STATUS_SXS_SECTION_NOT_FOUND; ++ } ++ ++ if (status != STATUS_SUCCESS) return status; ++ ++ if (flags & FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX) ++ { ++ actctx_addref(actctx); ++ data->hActCtx = actctx; ++ } ++ return STATUS_SUCCESS; ++} ++ ++static NTSTATUS find_guid(ACTIVATION_CONTEXT* actctx, ULONG section_kind, ++ const GUID *guid, DWORD flags, PACTCTX_SECTION_KEYED_DATA data) ++{ ++ NTSTATUS status; ++ ++ switch (section_kind) ++ { ++ case ACTIVATION_CONTEXT_SECTION_COM_TYPE_LIBRARY_REDIRECTION: ++ status = find_tlib_redirection(actctx, guid, data); ++ break; ++ case ACTIVATION_CONTEXT_SECTION_COM_SERVER_REDIRECTION: ++ status = find_comserver_redirection(actctx, guid, data); ++ break; ++ case ACTIVATION_CONTEXT_SECTION_COM_INTERFACE_REDIRECTION: ++ status = find_cominterface_redirection(actctx, guid, data); ++ break; ++ case ACTIVATION_CONTEXT_SECTION_CLR_SURROGATES: ++ status = find_clr_surrogate(actctx, guid, data); ++ break; + default: + DPRINT("Unknown section_kind %x\n", section_kind); + return STATUS_SXS_SECTION_NOT_FOUND; + } + + if (status != STATUS_SUCCESS) return status; + + if (flags & FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX) + { + actctx_addref(actctx); + data->hActCtx = actctx; + } + return STATUS_SUCCESS; +} + +/* initialize the activation context for the current process */ +void actctx_init(void) +{ + ACTCTXW ctx; + HANDLE handle; + + ctx.cbSize = sizeof(ctx); + ctx.lpSource = NULL; + ctx.dwFlags = ACTCTX_FLAG_RESOURCE_NAME_VALID | ACTCTX_FLAG_HMODULE_VALID; + ctx.hModule = NtCurrentTeb()->ProcessEnvironmentBlock->ImageBaseAddress; + ctx.lpResourceName = (LPCWSTR)CREATEPROCESS_MANIFEST_RESOURCE_ID; + + if (NT_SUCCESS(RtlCreateActivationContext(0, (PVOID)&ctx, 0, NULL, NULL, &handle))) + { + process_actctx = check_actctx(handle); + } +} + +/* FUNCTIONS ***************************************************************/ + +NTSTATUS +NTAPI +RtlCreateActivationContext(IN ULONG Flags, + IN PACTIVATION_CONTEXT_DATA ActivationContextData, + IN ULONG ExtraBytes, + IN PVOID NotificationRoutine, + IN PVOID NotificationContext, + OUT PACTIVATION_CONTEXT *ActCtx) +{ + const ACTCTXW *pActCtx = (PVOID)ActivationContextData; + const WCHAR *directory = NULL; + PACTIVATION_CONTEXT_WRAPPED ActualActCtx; + ACTIVATION_CONTEXT *actctx; + UNICODE_STRING nameW; + ULONG lang = 0; + NTSTATUS status = STATUS_NO_MEMORY; + HANDLE file = 0; + struct actctx_loader acl; + + DPRINT("%p %08x\n", pActCtx, pActCtx ? pActCtx->dwFlags : 0); + + if (!pActCtx || pActCtx->cbSize < sizeof(*pActCtx) || + (pActCtx->dwFlags & ~ACTCTX_FLAGS_ALL)) + return STATUS_INVALID_PARAMETER; + + + if (!(ActualActCtx = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ActualActCtx)))) + return STATUS_NO_MEMORY; + + ActualActCtx->MagicMarker = ACTCTX_MAGIC_MARKER; + + actctx = &ActualActCtx->ActivationContext; + actctx->RefCount = 1; + actctx->config.type = ACTIVATION_CONTEXT_PATH_TYPE_NONE; + actctx->config.info = NULL; + actctx->appdir.type = ACTIVATION_CONTEXT_PATH_TYPE_WIN32_FILE; + if (pActCtx->dwFlags & ACTCTX_FLAG_APPLICATION_NAME_VALID) + { + if (!(actctx->appdir.info = strdupW( pActCtx->lpApplicationName ))) goto error; + } + else + { + UNICODE_STRING dir; + WCHAR *p; + HMODULE module; + + if (pActCtx->dwFlags & ACTCTX_FLAG_HMODULE_VALID) module = pActCtx->hModule; + else module = NtCurrentTeb()->ProcessEnvironmentBlock->ImageBaseAddress; + + status = get_module_filename(module, &dir, 0); + if (!NT_SUCCESS(status)) goto error; + if ((p = strrchrW( dir.Buffer, '\\' ))) p[1] = 0; + actctx->appdir.info = dir.Buffer; + } + + nameW.Buffer = NULL; + + /* open file only if it's going to be used */ + if (pActCtx->lpSource && !((pActCtx->dwFlags & ACTCTX_FLAG_RESOURCE_NAME_VALID) && + (pActCtx->dwFlags & ACTCTX_FLAG_HMODULE_VALID))) + { + if (!RtlDosPathNameToNtPathName_U(pActCtx->lpSource, &nameW, NULL, NULL)) + { + status = STATUS_NO_SUCH_FILE; + goto error; + } + status = open_nt_file( &file, &nameW ); + if (!NT_SUCCESS(status)) + { + RtlFreeUnicodeString( &nameW ); + goto error; + } + } + + acl.actctx = actctx; + acl.dependencies = NULL; + acl.num_dependencies = 0; + acl.allocated_dependencies = 0; + + if (pActCtx->dwFlags & ACTCTX_FLAG_LANGID_VALID) lang = pActCtx->wLangId; + if (pActCtx->dwFlags & ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID) directory = pActCtx->lpAssemblyDirectory; + + if (pActCtx->dwFlags & ACTCTX_FLAG_RESOURCE_NAME_VALID) + { + /* if we have a resource it's a PE file */ + if (pActCtx->dwFlags & ACTCTX_FLAG_HMODULE_VALID) + { + status = get_manifest_in_module( &acl, NULL, NULL, directory, FALSE, pActCtx->hModule, + pActCtx->lpResourceName, lang ); + if (status && status != STATUS_SXS_CANT_GEN_ACTCTX) + /* FIXME: what to do if pActCtx->lpSource is set */ + status = get_manifest_in_associated_manifest( &acl, NULL, NULL, directory, + pActCtx->hModule, pActCtx->lpResourceName ); + } + else if (pActCtx->lpSource) + { + status = get_manifest_in_pe_file( &acl, NULL, nameW.Buffer, directory, FALSE, + file, pActCtx->lpResourceName, lang ); + if (status && status != STATUS_SXS_CANT_GEN_ACTCTX) + status = get_manifest_in_associated_manifest( &acl, NULL, nameW.Buffer, directory, + NULL, pActCtx->lpResourceName ); + } + else status = STATUS_INVALID_PARAMETER; + } + else + { + status = get_manifest_in_manifest_file( &acl, NULL, nameW.Buffer, directory, FALSE, file ); + } + + if (file) NtClose( file ); + RtlFreeUnicodeString( &nameW ); + + if (NT_SUCCESS(status)) status = parse_depend_manifests(&acl); + free_depend_manifests( &acl ); + + if (NT_SUCCESS(status)) + *ActCtx = actctx; + else actctx_release( actctx ); + return status; + +error: + if (file) NtClose( file ); + actctx_release( actctx ); + return status; +} + +#if 0 +#define ACT_CTX_VALID(p) ((((ULONG_PTR)p - 1) | 7) != -1) + +VOID +NTAPI +RtlAddRefActivationContext(IN PACTIVATION_CONTEXT Handle) +{ + PIACTIVATION_CONTEXT ActCtx = (PIACTIVATION_CONTEXT)Handle; + LONG OldRefCount, NewRefCount; + + if ((ActCtx) && (ACT_CTX_VALID(ActCtx)) && (ActCtx->RefCount != LONG_MAX)) + { + RtlpValidateActCtx(ActCtx); + + while (TRUE) + { + OldRefCount = ActCtx->RefCount; + ASSERT(OldRefCount > 0); + + if (OldRefCount == LONG_MAX) break; + + NewRefCount = OldRefCount + 1; + if (InterlockedCompareExchange(&ActCtx->RefCount, + NewRefCount, + OldRefCount) == OldRefCount) + { + break; + } + } + + NewRefCount = LONG_MAX; + ASSERT(NewRefCount > 0); + } +} + +VOID +NTAPI +RtlReleaseActivationContext( HANDLE handle ) +{ + PIACTIVATION_CONTEXT ActCtx = (PIACTIVATION_CONTEXT) Handle; + + if ((ActCtx) && (ACT_CTX_VALID(ActCtx)) && (ActCtx->RefCount != LONG_MAX)) + { + RtlpValidateActCtx(ActCtx); + + actctx_release(ActCtx); + } +} +#else +VOID +NTAPI +RtlAddRefActivationContext( HANDLE handle ) +{ + ACTIVATION_CONTEXT *actctx; + + if ((actctx = check_actctx(handle))) actctx_addref(actctx); +} + +VOID +NTAPI +RtlReleaseActivationContext( HANDLE handle ) +{ + ACTIVATION_CONTEXT *actctx; + + if ((actctx = check_actctx(handle))) actctx_release(actctx); +} +#endif + +NTSTATUS +NTAPI RtlActivateActivationContextEx( ULONG flags, PTEB tebAddress, HANDLE handle, PULONG_PTR cookie ) +{ + RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame; + + if (!(frame = RtlAllocateHeap( RtlGetProcessHeap(), 0, sizeof(*frame) ))) + return STATUS_NO_MEMORY; + + frame->Previous = tebAddress->ActivationContextStackPointer->ActiveFrame; + frame->ActivationContext = handle; + frame->Flags = 0; + + tebAddress->ActivationContextStackPointer->ActiveFrame = frame; + RtlAddRefActivationContext( handle ); + + *cookie = (ULONG_PTR)frame; + DPRINT( "%p cookie=%lx\n", handle, *cookie ); + return STATUS_SUCCESS; +} + + +NTSTATUS +NTAPI RtlActivateActivationContext( ULONG flags, HANDLE handle, PULONG_PTR cookie ) +{ + return RtlActivateActivationContextEx(flags, NtCurrentTeb(), handle, cookie); +} + +NTSTATUS +NTAPI +RtlDeactivateActivationContext( ULONG flags, ULONG_PTR cookie ) +{ + RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame, *top; + + DPRINT( "%x cookie=%lx\n", flags, cookie ); + + /* find the right frame */ + top = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame; + for (frame = top; frame; frame = frame->Previous) + if ((ULONG_PTR)frame == cookie) break; + + if (!frame) + RtlRaiseStatus( STATUS_SXS_INVALID_DEACTIVATION ); + + if (frame != top && !(flags & RTL_DEACTIVATE_ACTIVATION_CONTEXT_FLAG_FORCE_EARLY_DEACTIVATION)) + RtlRaiseStatus( STATUS_SXS_EARLY_DEACTIVATION ); + + /* pop everything up to and including frame */ + NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame = frame->Previous; + + while (top != NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame) + { + frame = top->Previous; + RtlReleaseActivationContext( top->ActivationContext ); + RtlFreeHeap( RtlGetProcessHeap(), 0, top ); + top = frame; + } + + return STATUS_SUCCESS; +} + +VOID +NTAPI +RtlFreeActivationContextStack(IN PACTIVATION_CONTEXT_STACK Stack) +{ + PRTL_ACTIVATION_CONTEXT_STACK_FRAME ActiveFrame, PrevFrame; + + /* Nothing to do if there is no stack */ + if (!Stack) return; + + /* Get the current active frame */ + ActiveFrame = Stack->ActiveFrame; + + /* Go through them in backwards order and release */ + while (ActiveFrame) + { + PrevFrame = ActiveFrame->Previous; + RtlReleaseActivationContext(ActiveFrame->ActivationContext); + RtlFreeHeap(RtlGetProcessHeap(), 0, ActiveFrame); + ActiveFrame = PrevFrame; + } + + /* Zero out the active frame */ + Stack->ActiveFrame = NULL; + + /* TODO: Empty the Frame List Cache */ + ASSERT(IsListEmpty(&Stack->FrameListCache)); + + /* Free activation stack memory */ + RtlFreeHeap(RtlGetProcessHeap(), 0, Stack); +} + +VOID +NTAPI RtlFreeThreadActivationContextStack(VOID) +{ + RtlFreeActivationContextStack(NtCurrentTeb()->ActivationContextStackPointer); + NtCurrentTeb()->ActivationContextStackPointer = NULL; +} + + +NTSTATUS +NTAPI RtlGetActiveActivationContext( HANDLE *handle ) +{ + if (NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame) + { + *handle = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame->ActivationContext; + RtlAddRefActivationContext( *handle ); + } + else + *handle = 0; + + return STATUS_SUCCESS; +} + + +BOOLEAN +NTAPI RtlIsActivationContextActive( HANDLE handle ) +{ + RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame; + + for (frame = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame; frame; frame = frame->Previous) + if (frame->ActivationContext == handle) return TRUE; + return FALSE; +} + +NTSTATUS +NTAPI +RtlQueryInformationActivationContext( ULONG flags, HANDLE handle, PVOID subinst, + ULONG class, PVOID buffer, + SIZE_T bufsize, SIZE_T *retlen ) +{ + ACTIVATION_CONTEXT *actctx; + NTSTATUS status; + + DPRINT("%08x %p %p %u %p %Iu %p\n", flags, handle, + subinst, class, buffer, bufsize, retlen); + + if (retlen) *retlen = 0; + if ((status = find_query_actctx( &handle, flags, class ))) return status; + + switch (class) + { + case ActivationContextBasicInformation: + { + ACTIVATION_CONTEXT_BASIC_INFORMATION *info = buffer; + + if (retlen) *retlen = sizeof(*info); + if (!info || bufsize < sizeof(*info)) return STATUS_BUFFER_TOO_SMALL; + + info->hActCtx = handle; + info->dwFlags = 0; /* FIXME */ + if (!(flags & RTL_QUERY_ACTIVATION_CONTEXT_FLAG_NO_ADDREF)) RtlAddRefActivationContext(handle); + } + break; + + case ActivationContextDetailedInformation: + { + ACTIVATION_CONTEXT_DETAILED_INFORMATION *acdi = buffer; + struct assembly *assembly = NULL; + SIZE_T len, manifest_len = 0, config_len = 0, appdir_len = 0; + LPWSTR ptr; + + if (!(actctx = check_actctx(handle))) return STATUS_INVALID_PARAMETER; + + if (actctx->num_assemblies) assembly = actctx->assemblies; + + if (assembly && assembly->manifest.info) + manifest_len = strlenW(assembly->manifest.info) + 1; + if (actctx->config.info) config_len = strlenW(actctx->config.info) + 1; + if (actctx->appdir.info) appdir_len = strlenW(actctx->appdir.info) + 1; + len = sizeof(*acdi) + (manifest_len + config_len + appdir_len) * sizeof(WCHAR); + + if (retlen) *retlen = len; + if (!buffer || bufsize < len) return STATUS_BUFFER_TOO_SMALL; + + acdi->dwFlags = 0; + acdi->ulFormatVersion = assembly ? 1 : 0; /* FIXME */ + acdi->ulAssemblyCount = actctx->num_assemblies; + acdi->ulRootManifestPathType = assembly ? assembly->manifest.type : 0 /* FIXME */; + acdi->ulRootManifestPathChars = assembly && assembly->manifest.info ? (DWORD)manifest_len - 1 : 0; + acdi->ulRootConfigurationPathType = actctx->config.type; + acdi->ulRootConfigurationPathChars = actctx->config.info ? (DWORD)config_len - 1 : 0; + acdi->ulAppDirPathType = actctx->appdir.type; + acdi->ulAppDirPathChars = actctx->appdir.info ? (DWORD)appdir_len - 1 : 0; + ptr = (LPWSTR)(acdi + 1); + if (manifest_len) + { + acdi->lpRootManifestPath = ptr; + memcpy(ptr, assembly->manifest.info, manifest_len * sizeof(WCHAR)); + ptr += manifest_len; + } + else acdi->lpRootManifestPath = NULL; + if (config_len) + { + acdi->lpRootConfigurationPath = ptr; + memcpy(ptr, actctx->config.info, config_len * sizeof(WCHAR)); + ptr += config_len; + } + else acdi->lpRootConfigurationPath = NULL; + if (appdir_len) + { + acdi->lpAppDirPath = ptr; + memcpy(ptr, actctx->appdir.info, appdir_len * sizeof(WCHAR)); + } + else acdi->lpAppDirPath = NULL; + } + break; + + case AssemblyDetailedInformationInActivationContext: + { + ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *afdi = buffer; + struct assembly *assembly; + WCHAR *assembly_id; + DWORD index; + SIZE_T len, id_len = 0, ad_len = 0, path_len = 0; + LPWSTR ptr; + + if (!(actctx = check_actctx(handle))) return STATUS_INVALID_PARAMETER; + if (!subinst) return STATUS_INVALID_PARAMETER; + + index = *(DWORD*)subinst; + if (!index || index > actctx->num_assemblies) return STATUS_INVALID_PARAMETER; + + assembly = &actctx->assemblies[index - 1]; + + if (!(assembly_id = build_assembly_id( &assembly->id ))) return STATUS_NO_MEMORY; + id_len = strlenW(assembly_id) + 1; + if (assembly->directory) ad_len = strlenW(assembly->directory) + 1; + + if (assembly->manifest.info && + (assembly->type == ASSEMBLY_MANIFEST || assembly->type == ASSEMBLY_SHARED_MANIFEST)) + path_len = strlenW(assembly->manifest.info) + 1; + + len = sizeof(*afdi) + (id_len + ad_len + path_len) * sizeof(WCHAR); + + if (retlen) *retlen = len; + if (!buffer || bufsize < len) + { + RtlFreeHeap( RtlGetProcessHeap(), 0, assembly_id ); + return STATUS_BUFFER_TOO_SMALL; + } + + afdi->ulFlags = 0; /* FIXME */ + afdi->ulEncodedAssemblyIdentityLength = (DWORD)(id_len - 1) * sizeof(WCHAR); + afdi->ulManifestPathType = assembly->manifest.type; + afdi->ulManifestPathLength = assembly->manifest.info ? (DWORD)(path_len - 1) * sizeof(WCHAR) : 0; + /* FIXME afdi->liManifestLastWriteTime = 0; */ + afdi->ulPolicyPathType = ACTIVATION_CONTEXT_PATH_TYPE_NONE; /* FIXME */ + afdi->ulPolicyPathLength = 0; + /* FIXME afdi->liPolicyLastWriteTime = 0; */ + afdi->ulMetadataSatelliteRosterIndex = 0; /* FIXME */ + afdi->ulManifestVersionMajor = 1; + afdi->ulManifestVersionMinor = 0; + afdi->ulPolicyVersionMajor = 0; /* FIXME */ + afdi->ulPolicyVersionMinor = 0; /* FIXME */ + afdi->ulAssemblyDirectoryNameLength = ad_len ? (DWORD)(ad_len - 1) * sizeof(WCHAR) : 0; + ptr = (LPWSTR)(afdi + 1); + afdi->lpAssemblyEncodedAssemblyIdentity = ptr; + memcpy( ptr, assembly_id, id_len * sizeof(WCHAR) ); + ptr += id_len; + if (path_len) + { + afdi->lpAssemblyManifestPath = ptr; + memcpy(ptr, assembly->manifest.info, path_len * sizeof(WCHAR)); + ptr += path_len; + } else afdi->lpAssemblyManifestPath = NULL; + afdi->lpAssemblyPolicyPath = NULL; /* FIXME */ + if (ad_len) + { + afdi->lpAssemblyDirectoryName = ptr; + memcpy(ptr, assembly->directory, ad_len * sizeof(WCHAR)); + } + else afdi->lpAssemblyDirectoryName = NULL; + RtlFreeHeap( RtlGetProcessHeap(), 0, assembly_id ); + } + break; + + case FileInformationInAssemblyOfAssemblyInActivationContext: + { + const ACTIVATION_CONTEXT_QUERY_INDEX *acqi = subinst; + ASSEMBLY_FILE_DETAILED_INFORMATION *afdi = buffer; + struct assembly *assembly; + struct dll_redirect *dll; + SIZE_T len, dll_len = 0; + LPWSTR ptr; + + if (!(actctx = check_actctx(handle))) return STATUS_INVALID_PARAMETER; + if (!acqi) return STATUS_INVALID_PARAMETER; + + if (acqi->ulAssemblyIndex >= actctx->num_assemblies) + return STATUS_INVALID_PARAMETER; + assembly = &actctx->assemblies[acqi->ulAssemblyIndex]; + + if (acqi->ulFileIndexInAssembly >= assembly->num_dlls) + return STATUS_INVALID_PARAMETER; + dll = &assembly->dlls[acqi->ulFileIndexInAssembly]; + + if (dll->name) dll_len = strlenW(dll->name) + 1; + len = sizeof(*afdi) + dll_len * sizeof(WCHAR); + + if (!buffer || bufsize < len) + { + if (retlen) *retlen = len; + return STATUS_BUFFER_TOO_SMALL; + } + if (retlen) *retlen = 0; /* yes that's what native does !! */ + afdi->ulFlags = ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION; + afdi->ulFilenameLength = dll_len ? (DWORD)(dll_len - 1) * sizeof(WCHAR) : 0; + afdi->ulPathLength = 0; /* FIXME */ + ptr = (LPWSTR)(afdi + 1); + if (dll_len) + { + afdi->lpFileName = ptr; + memcpy( ptr, dll->name, dll_len * sizeof(WCHAR) ); + } else afdi->lpFileName = NULL; + afdi->lpFilePath = NULL; /* FIXME */ + } + break; + + default: + DPRINT( "class %u not implemented\n", class ); + return STATUS_NOT_IMPLEMENTED; + } + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +RtlQueryInformationActiveActivationContext(ULONG ulInfoClass, + PVOID pvBuffer, + SIZE_T cbBuffer OPTIONAL, + SIZE_T *pcbWrittenOrRequired OPTIONAL) +{ + return RtlQueryInformationActivationContext(RTL_QUERY_ACTIVATION_CONTEXT_FLAG_USE_ACTIVE_ACTIVATION_CONTEXT, + NULL, + NULL, + ulInfoClass, + pvBuffer, + cbBuffer, + pcbWrittenOrRequired); +} + +#define FIND_ACTCTX_RETURN_FLAGS 0x00000002 +#define FIND_ACTCTX_RETURN_ASSEMBLY_METADATA 0x00000004 +#define FIND_ACTCTX_VALID_MASK (FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX | FIND_ACTCTX_RETURN_FLAGS | FIND_ACTCTX_RETURN_ASSEMBLY_METADATA) + +NTSTATUS +NTAPI +RtlpFindActivationContextSection_CheckParameters( ULONG flags, const GUID *guid, ULONG section_kind, - UNICODE_STRING *section_name, PACTCTX_SECTION_KEYED_DATA data ) ++ const UNICODE_STRING *section_name, PACTCTX_SECTION_KEYED_DATA data ) +{ + /* Check general parameter combinations */ + if (!section_name || + (flags & ~FIND_ACTCTX_VALID_MASK) || + ((flags & FIND_ACTCTX_VALID_MASK) && !data) || + (data && data->cbSize < offsetof(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex))) + { + DPRINT1("invalid parameter\n"); + return STATUS_INVALID_PARAMETER; + } + + /* TODO */ + if (flags & FIND_ACTCTX_RETURN_FLAGS || + flags & FIND_ACTCTX_RETURN_ASSEMBLY_METADATA) + { + DPRINT1("unknown flags %08x\n", flags); + return STATUS_INVALID_PARAMETER; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +RtlFindActivationContextSectionString( ULONG flags, const GUID *guid, ULONG section_kind, - UNICODE_STRING *section_name, PVOID ptr ) ++ const UNICODE_STRING *section_name, PVOID ptr ) +{ + PACTCTX_SECTION_KEYED_DATA data = ptr; + NTSTATUS status; + + status = RtlpFindActivationContextSection_CheckParameters(flags, guid, section_kind, section_name, data); + if (!NT_SUCCESS(status)) return status; + + status = STATUS_SXS_KEY_NOT_FOUND; + + /* if there is no data, but params are valid, + we return that sxs key is not found to be at least somehow compatible */ + if (!data) return status; + + ASSERT(NtCurrentTeb()); + ASSERT(NtCurrentTeb()->ActivationContextStackPointer); + + if (NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame) + { + ACTIVATION_CONTEXT *actctx = check_actctx(NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame->ActivationContext); + if (actctx) status = find_string( actctx, section_kind, section_name, flags, data ); + } + + if (status != STATUS_SUCCESS) + status = find_string( process_actctx, section_kind, section_name, flags, data ); + + return status; +} + +NTSTATUS WINAPI RtlFindActivationContextSectionGuid( ULONG flags, const GUID *extguid, ULONG section_kind, + const GUID *guid, void *ptr ) +{ + ACTCTX_SECTION_KEYED_DATA *data = ptr; + NTSTATUS status = STATUS_SXS_KEY_NOT_FOUND; + + if (extguid) + { + DPRINT1("expected extguid == NULL\n"); + return STATUS_INVALID_PARAMETER; + } + + if (flags & ~FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX) + { + DPRINT1("unknown flags %08x\n", flags); + return STATUS_INVALID_PARAMETER; + } + + if (!data || data->cbSize < FIELD_OFFSET(ACTCTX_SECTION_KEYED_DATA, ulAssemblyRosterIndex) || !guid) + return STATUS_INVALID_PARAMETER; + + if (NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame) + { + ACTIVATION_CONTEXT *actctx = check_actctx(NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame->ActivationContext); + if (actctx) status = find_guid( actctx, section_kind, guid, flags, data ); + } + + if (status != STATUS_SUCCESS) + status = find_guid( process_actctx, section_kind, guid, flags, data ); + + return status; +} + +/* Stubs */ + +NTSTATUS +NTAPI +RtlAllocateActivationContextStack(IN PACTIVATION_CONTEXT_STACK *Stack) +{ + PACTIVATION_CONTEXT_STACK ContextStack; + + /* Check if it's already allocated */ + if (*Stack) return STATUS_SUCCESS; + + /* Allocate space for the context stack */ + ContextStack = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ACTIVATION_CONTEXT_STACK)); + if (!ContextStack) + { + return STATUS_NO_MEMORY; + } + + /* Initialize the context stack */ + ContextStack->Flags = 0; + ContextStack->ActiveFrame = NULL; + InitializeListHead(&ContextStack->FrameListCache); + ContextStack->NextCookieSequenceNumber = 1; + ContextStack->StackId = 1; //TODO: Timer-based + + *Stack = ContextStack; + + return STATUS_SUCCESS; +} + +PRTL_ACTIVATION_CONTEXT_STACK_FRAME +FASTCALL +RtlActivateActivationContextUnsafeFast(IN PRTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED Frame, + IN PVOID Context) +{ +#if NEW_NTDLL_LOADER + RTL_ACTIVATION_CONTEXT_STACK_FRAME *ActiveFrame; + + /* Get the curren active frame */ + ActiveFrame = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame; + + DPRINT1("ActiveFrame %p, &Frame->Frame %p, Context %p\n", ActiveFrame, &Frame->Frame, Context); + + /* Actually activate it */ + Frame->Frame.Previous = ActiveFrame; + Frame->Frame.ActivationContext = Context; + Frame->Frame.Flags = 0; + + /* Check if we can activate this context */ + if ((ActiveFrame && (ActiveFrame->ActivationContext != Context)) || + Context) + { + /* Set new active frame */ + NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame = &Frame->Frame; + return &Frame->Frame; + } + + /* We can get here only one way: it was already activated */ + DPRINT1("Trying to activate improper activation context\n"); + + /* Activate only if we are allowing multiple activation */ + if (!RtlpNotAllowingMultipleActivation) + { + NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame = &Frame->Frame; + } + else + { + /* Set flag */ + Frame->Frame.Flags = 0x30; + } + + /* Return pointer to the activation frame */ + return &Frame->Frame; +#else + + RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame = &Frame->Frame; + + frame->Previous = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame; + frame->ActivationContext = Context; + frame->Flags = 0; + + NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame = frame; + + return STATUS_SUCCESS; +#endif +} + +PRTL_ACTIVATION_CONTEXT_STACK_FRAME +FASTCALL +RtlDeactivateActivationContextUnsafeFast(IN PRTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED Frame) +{ + RTL_ACTIVATION_CONTEXT_STACK_FRAME *frame; + //RTL_ACTIVATION_CONTEXT_STACK_FRAME *top; + + /* find the right frame */ + //top = NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame; + frame = &Frame->Frame; + + if (!frame) + { + DPRINT1("No top frame!\n"); + RtlRaiseStatus( STATUS_SXS_INVALID_DEACTIVATION ); + } + + /* pop everything up to and including frame */ + NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame = frame->Previous; + + return frame; +} + + +NTSTATUS +NTAPI +RtlZombifyActivationContext(PVOID Context) +{ + UNIMPLEMENTED; + + if (Context == ACTCTX_FAKE_HANDLE) + return STATUS_SUCCESS; + + return STATUS_NOT_IMPLEMENTED; +} + diff --cc media/doc/README.WINE index 5010d91db1d,00000000000..b88a56ed424 mode 100644,000000..100644 --- a/media/doc/README.WINE +++ b/media/doc/README.WINE @@@ -1,342 -1,0 +1,343 @@@ +# ReactOS WINE porting guide for 0.3 +# Update this file when you port a dll/program from WINE. + +The ReactOS Project shares quite a bit of code with the WINE project. +This document should provide a compleate reference for all of the +locations in the ReactOS source tree where code is shared between the +two projects. If you find something is missing from this documentation +please add it. + +If you find that a function in ReactOS that is not implemented properly +and is based on WINE sources, check the latest Winehq CVS and see if +it has been fixed there. If so, please submit a patch to +ros-dev@reactos.org. Otherwise please send a patch to both +wine-patches@winehq.com and ros-dev@reactos.org + +When porting a new DLL from Wine to ReactOS, please do the following steps +- Create a new directory in dll/win32/ of the same name as the new Wine DLL +- Add a new entry in dll/win32/win32.rbuild +- Follow the guide "Update process for autosync DLLs" at the bottom of this file + + +The following build tools are shared with Wine. + +reactos/tools/unicode # Synced to Wine-1.7.17 +reactos/tools/widl # Synced to Wine-1.7.17 +reactos/tools/wpp # Synced to Wine-1.7.17 + +The following libraries are shared with Wine. + +reactos/dll/directx/wine/amstream # Synced to Wine-1.7.17 +reactos/dll/directx/wine/d3d8 # Synced to Wine-1.7.17 +reactos/dll/directx/wine/d3d9 # Synced to Wine-1.7.17 +reactos/dll/directx/wine/d3dcompiler_43 # Synced to Wine-1.7.17 +reactos/dll/directx/wine/d3dx9_24 => 43 # Synced to Wine-1.7.17 +reactos/dll/directx/wine/d3dxof # Synced to Wine-1.7.17 +reactos/dll/directx/wine/ddraw # Synced to Wine-1.7.17 +reactos/dll/directx/wine/devenum # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dinput # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dinput8 # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dmusic # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dplay # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dplayx # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dsound # Synced to Wine-1.5.26 +reactos/dll/directx/wine/dxdiagn # Synced to Wine-1.7.17 +reactos/dll/directx/wine/dxgi # Synced to Wine-1.7.17 +reactos/dll/directx/wine/msdmo # Synced to Wine-1.7.17 +reactos/dll/directx/wine/qedit # Synced to Wine-1.7.17 +reactos/dll/directx/wine/quartz # Synced to Wine-1.7.17 +reactos/dll/directx/wine/wined3d # Synced to Wine-1.7.17 + +reactos/dll/win32/activeds # Synced to Wine-1.7.17 +reactos/dll/win32/actxprxy # Synced to Wine-1.7.17 +reactos/dll/win32/advpack # Synced to Wine-1.7.17 +reactos/dll/win32/atl # Synced to Wine-1.7.17 +reactos/dll/win32/atl100 # Synced to Wine-1.7.17 +reactos/dll/win32/atl80 # Synced to Wine-1.7.17 +reactos/dll/win32/avifil32 # Synced to Wine-1.7.17 +reactos/dll/win32/bcrypt # Synced to Wine-1.7.17 +reactos/dll/win32/browseui # Out of sync +reactos/dll/win32/cabinet # Synced to Wine-1.7.17 +reactos/dll/win32/clusapi # Synced to Wine-1.7.17 +reactos/dll/win32/comcat # Synced to Wine-1.7.17 +reactos/dll/win32/comctl32 # Synced to Wine-1.7.17 +reactos/dll/win32/comdlg32 # Synced to Wine-1.7.17 +reactos/dll/win32/compstui # Synced to Wine-1.7.17 +reactos/dll/win32/credui # Synced to Wine-1.7.17 +reactos/dll/win32/crypt32 # Synced to Wine-1.7.17 +reactos/dll/win32/cryptdlg # Synced to Wine-1.7.17 +reactos/dll/win32/cryptdll # Synced to Wine-1.7.17 +reactos/dll/win32/cryptnet # Synced to Wine-1.7.17 +reactos/dll/win32/cryptui # Synced to Wine-1.7.17 +reactos/dll/win32/dbghelp # Synced to Wine-1.7.17 +reactos/dll/win32/dciman32 # Synced to Wine-1.7.17 +reactos/dll/win32/dwmapi # Synced to Wine-1.7.17 +reactos/dll/win32/faultrep # Synced to Wine-1.7.17 +reactos/dll/win32/fusion # Synced to Wine-1.7.17 +reactos/dll/win32/gdiplus # Synced to Wine-1.7.17 +reactos/dll/win32/hhctrl.ocx # Synced to Wine-1.7.17 +reactos/dll/win32/hlink # Synced to Wine-1.7.17 +reactos/dll/win32/hnetcfg # Synced to Wine-1.7.17 +reactos/dll/win32/httpapi # Synced to Wine-1.7.17 +reactos/dll/win32/iccvid # Synced to Wine-1.7.17 +reactos/dll/win32/icmp # Out of sync +reactos/dll/win32/ieframe # Synced to Wine-1.7.17 +reactos/dll/win32/imaadp32.acm # Synced to Wine-1.7.17 +reactos/dll/win32/imagehlp # Synced to Wine-1.7.17 +reactos/dll/win32/imm32 # Synced to Wine-1.7.17 +reactos/dll/win32/inetcomm # Synced to Wine-1.7.17 +reactos/dll/win32/inetmib1 # Synced to Wine-1.7.1 +reactos/dll/win32/initpki # Synced to Wine-1.7.17 +reactos/dll/win32/inseng # Synced to Wine-1.7.17 +reactos/dll/win32/iphlpapi # Out of sync +reactos/dll/win32/itircl # Synced to Wine-1.7.17 +reactos/dll/win32/itss # Synced to Wine-1.7.17 +reactos/dll/win32/jscript # Synced to Wine-1.7.17 +reactos/dll/win32/loadperf # Synced to Wine-1.7.17 +reactos/dll/win32/localspl # Synced to Wine-1.7.17 +reactos/dll/win32/localui # Synced to Wine-1.7.17 +reactos/dll/win32/lz32 # Synced to Wine-1.7.17 +reactos/dll/win32/mapi32 # Synced to Wine-1.7.17 +reactos/dll/win32/mciavi32 # Synced to Wine-1.7.17 +reactos/dll/win32/mcicda # Synced to Wine-1.7.17 +reactos/dll/win32/mciqtz32 # Synced to Wine-1.7.17 +reactos/dll/win32/mciseq # Synced to Wine-1.7.17 +reactos/dll/win32/mciwave # Synced to Wine-1.7.17 +reactos/dll/win32/mlang # Synced to Wine-1.7.17 +reactos/dll/win32/mmdevapi # Synced to Wine-1.7.1 +reactos/dll/win32/mpr # Synced to Wine-1.7.17 +reactos/dll/win32/mprapi # Synced to Wine-1.7.17 +reactos/dll/win32/msacm32 # Synced to Wine-1.7.17 +reactos/dll/win32/msacm32/msacm32.drv # Synced to Wine-1.7.17 +reactos/dll/win32/msadp32.acm # Synced to Wine-1.7.17 +reactos/dll/win32/mscat32 # Synced to Wine-1.7.17 +reactos/dll/win32/mscms # Synced to Wine-1.7.17 +reactos/dll/win32/mscoree # Synced to Wine-1.5.4 +reactos/dll/win32/msctf # Synced to Wine-1.7.17 +reactos/dll/win32/msftedit # Synced to Wine-1.7.17 +reactos/dll/win32/msg711.acm # Synced to Wine-1.7.17 +reactos/dll/win32/msgsm32.acm # Synced to Wine-1.7.17 +reactos/dll/win32/mshtml # Synced to Wine-1.7.17 +reactos/dll/win32/mshtml.tlb # Synced to Wine-1.7.17 +reactos/dll/win32/msi # Synced to Wine-1.7.17 +reactos/dll/win32/msimg32 # Synced to Wine-1.7.17 +reactos/dll/win32/msimtf # Synced to Wine-1.7.17 +reactos/dll/win32/msisip # Synced to Wine-1.7.17 +reactos/dll/win32/msisys.ocx # Synced to Wine-1.7.17 +reactos/dll/win32/msnet32 # Synced to Wine-1.7.17 +reactos/dll/win32/msrle32 # Synced to Wine-1.7.17 +reactos/dll/win32/mssign32 # Synced to Wine-1.7.17 +reactos/dll/win32/mssip32 # Synced to Wine-1.7.17 +reactos/dll/win32/mstask # Synced to Wine-1.7.17 +reactos/dll/win32/msvcrt20 # Out of sync +reactos/dll/win32/msvcrt40 # Out of sync +reactos/dll/win32/msvfw32 # Synced to Wine-1.7.17 +reactos/dll/win32/msvidc32 # Synced to Wine-1.7.17 +reactos/dll/win32/msxml # Synced to Wine-1.7.17 +reactos/dll/win32/msxml2 # Synced to Wine-1.7.17 +reactos/dll/win32/msxml3 # Synced to Wine-1.7.17 +reactos/dll/win32/msxml4 # Synced to Wine-1.7.17 +reactos/dll/win32/msxml6 # Synced to Wine-1.7.17 +reactos/dll/win32/nddeapi # Synced to Wine-1.7.17 +reactos/dll/win32/netapi32 # Forked at Wine-1.3.34 +reactos/dll/win32/ntdsapi # Synced to Wine-1.7.17 +reactos/dll/win32/ntprint # Synced to Wine-1.7.17 +reactos/dll/win32/objsel # Synced to Wine-1.7.17 +reactos/dll/win32/odbc32 # Synced to Wine-1.7.17. Depends on port of Linux ODBC. +reactos/dll/win32/odbccp32 # Synced to Wine-1.7.17 +reactos/dll/win32/ole32 # Synced to Wine-1.7.17 +reactos/dll/win32/oleacc # Synced to Wine-1.7.17 +reactos/dll/win32/oleaut32 # Synced to Wine-1.7.17 +reactos/dll/win32/olecli32 # Synced to Wine-1.7.17 +reactos/dll/win32/oledlg # Synced to Wine-1.7.17 +reactos/dll/win32/olepro32 # Synced to Wine-1.7.17 +reactos/dll/win32/olesvr32 # Synced to Wine-1.7.17 +reactos/dll/win32/olethk32 # Synced to Wine-1.7.17 +reactos/dll/win32/pdh # Synced to Wine-1.7.17 +reactos/dll/win32/pidgen # Synced to Wine-1.7.17 +reactos/dll/win32/powrprof # Forked at Wine-1.0rc5 +reactos/dll/win32/printui # Synced to Wine-1.7.17 +reactos/dll/win32/propsys # Synced to Wine-1.7.17 +reactos/dll/win32/pstorec # Synced to Wine-1.7.17 +reactos/dll/win32/qmgr # Synced to Wine-1.7.17 +reactos/dll/win32/qmgrprxy # Synced to Wine-1.7.17 +reactos/dll/win32/query # Synced to Wine-1.7.17 +reactos/dll/win32/rasapi32 # Synced to Wine-1.7.17 +reactos/dll/win32/resutils # Synced to Wine-1.7.17 +reactos/dll/win32/riched20 # Synced to Wine-1.7.17 +reactos/dll/win32/riched32 # Synced to Wine-1.7.17 +reactos/dll/win32/rpcrt4 # Synced to Wine-1.7.17 +reactos/dll/win32/rsabase # Synced to Wine-1.7.17 +reactos/dll/win32/rsaenh # Synced to Wine-1.7.17 +reactos/dll/win32/sccbase # Synced to Wine-1.7.17 +reactos/dll/win32/schannel # Synced to Wine-1.7.17 +reactos/dll/win32/scrrun # Synced to Wine-1.7.17 +reactos/dll/win32/secur32 # Forked +reactos/dll/win32/security # Forked (different .spec) +reactos/dll/win32/sensapi # Synced to Wine-1.7.17 +reactos/dll/win32/setupapi # Forked at Wine-20050524 +reactos/dll/win32/shdoclc # Synced to Wine-1.7.17 +reactos/dll/win32/shdocvw # Synced to Wine-1.7.17 +reactos/dll/win32/shell32 # Forked at Wine-20071011 +reactos/dll/win32/shfolder # Synced to Wine-1.7.17 +reactos/dll/win32/shlwapi # Synced to Wine-1.7.17 +reactos/dll/win32/slbcsp # Synced to Wine-1.7.17 +reactos/dll/win32/snmpapi # Synced to Wine-1.7.17 +reactos/dll/win32/softpub # Synced to Wine-1.7.17 +reactos/dll/win32/spoolss # Synced to Wine-1.7.17 +reactos/dll/win32/stdole2.tlb # Synced to Wine-1.7.17 +reactos/dll/win32/stdole32.tlb # Synced to Wine-1.7.17 +reactos/dll/win32/sti # Synced to Wine-1.7.17 +reactos/dll/win32/sxs # Synced to Wine-1.7.17 +reactos/dll/win32/tapi32 # Synced to Wine-1.7.17 +reactos/dll/win32/traffic # Synced to Wine-1.7.17 +reactos/dll/win32/twain_32 # Synced to Wine-1.7.17 +reactos/dll/win32/unicows # Synced to Wine-1.3.32 (Win9x only, why do we need this?!) +reactos/dll/win32/updspapi # Synced to Wine-1.7.17 +reactos/dll/win32/url # Synced to Wine-1.7.17 +reactos/dll/win32/urlmon # Synced to Wine-1.7.17 +reactos/dll/win32/usp10 # Synced to Wine-1.7.17 +reactos/dll/win32/uxtheme # Forked +reactos/dll/win32/vbscript # Synced to Wine-1.7.17 +reactos/dll/win32/version # Synced to Wine-1.7.17 +reactos/dll/win32/wbemdisp # Synced to Wine-1.7.17 +reactos/dll/win32/wbemprox # Synced to Wine-1.7.17 +reactos/dll/win32/wer # Autosync +reactos/dll/win32/windowscodecs # Synced to Wine-1.7.17 +reactos/dll/win32/windowscodecsext # Synced to Wine-1.7.17 +reactos/dll/win32/winemp3.acm # Synced to Wine-1.7.17 +reactos/dll/win32/wing32 # Out of sync +reactos/dll/win32/winhttp # Synced to Wine-1.7.17 +reactos/dll/win32/wininet # Synced to Wine-1.7.17 +reactos/dll/win32/winmm # Forked at Wine-20050628 +reactos/dll/win32/winmm/midimap # Forked at Wine-20050628 +reactos/dll/win32/winmm/wavemap # Forked at Wine-20050628 +reactos/dll/win32/wintrust # Synced to Wine-1.7.17 +reactos/dll/win32/wldap32 # Synced to Wine-1.7.17 +reactos/dll/win32/wmi # Synced to Wine-1.7.17 +reactos/dll/win32/wtsapi32 # Synced to Wine-1.7.17 +reactos/dll/win32/wuapi # Synced to Wine-1.7.17 +reactos/dll/win32/xinput1_1 # Synced to Wine-1.7.17 +reactos/dll/win32/xinput1_2 # Synced to Wine-1.7.17 +reactos/dll/win32/xinput1_3 # Synced to Wine-1.7.17 +reactos/dll/win32/xinput9_1_0 # Synced to Wine-1.7.17 +reactos/dll/win32/xmllite # Synced to Wine-1.7.17 + +reactos/dll/cpl/inetcpl # Synced to Wine-1.7.1 + +ReactOS shares the following programs with Winehq. + +reactos/base/applications/cmdutils/reg # Synced to Wine-1.7.1 +reactos/base/applications/cmdutils/taskkill # Synced to Wine-1.7.1 +reactos/base/applications/cmdutils/wmic # Synced to Wine-1.7.1 +reactos/base/applications/cmdutils/xcopy # Synced to Wine-1.7.1 +reactos/base/applications/games/winmine # Forked at Wine-1_3_5 +reactos/base/applications/extrac32 # Synced to Wine-1.7.1 +reactos/base/applications/iexplore # Synced to Wine-1.7.1 +reactos/base/applications/notepad # Forked at Wine-20041201 +reactos/base/applications/regedit # Out of sync +reactos/base/applications/winhlp32 # Synced to Wine-1.5.26 +reactos/base/applications/wordpad # Synced to Wine-1.7.9 +reactos/base/services/rpcss # Synced to Wine-1.7.1 +reactos/base/system/expand # Synced to Wine-1.7.1 +reactos/base/system/msiexec # Synced to Wine-1.7.1 +reactos/modules/rosapps/winfile # Autosync + +In addition the following libs, dlls and source files are mostly based on code ported +from Winehq CVS. If you are looking to update something in these files +check Wine current souces first as it may already be fixed. + +reactos/lib/3rdparty/strmbase # Synced to Wine-1.7.17 + +advapi32 - + reactos/dll/win32/advapi32/crypt/*.c # Synced to Wine-1.7.1 + reactos/dll/win32/advapi32/sec/cred.c # Synced to Wine-1.7.1 + reactos/dll/win32/advapi32/sec/sid.c # Out of Sync + +gdi32 - + reactos/dll/win32/gdi32/objects/linedda.c # Synced at 20090410 + +kernel32 - - reactos/dll/win32/kernel32/wine/actctx.c # Partly synced ++ reactos/dll/win32/kernel32/wine/actctx.c # Partly synced with Wine 1.7.17 + reactos/dll/win32/kernel32/wine/comm.c # Synced in r52754 + reactos/dll/win32/kernel32/wine/lzexpand.c # Synced in r52754 + reactos/dll/win32/kernel32/wine/profile.c # Synced in r52754 + reactos/dll/win32/kernel32/wine/res.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/casemap.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/chartype.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/collation.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/format_msg.c # Synced to Wine-1.5.4 + reactos/dll/win32/kernel32/winnls/string/lang.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/lcformat.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/nls.c # Synced in r52754 + reactos/dll/win32/kernel32/winnls/string/sortkey.c # Synced in r52754 + +msvcrt - + reactos/lib/sdk/crt/except/cpp.c # Synced at 20080528 + reactos/lib/sdk/crt/except/cppexcept.c # Synced at 20071111 ++ reactos/lib/sdk/crt/signal/xcptinfo.c # Synced to Wine-1.7.17 + reactos/lib/sdk/crt/string/scanf.c/h # Synced to Wine-1_1_27 + reactos/lib/sdk/crt/strings/wcs.c # Synced at 20080611 + reactos/lib/sdk/crt/wine/heap.c # Synced at 20080529 + reactos/lib/sdk/crt/wine/undname.c # Synced at 20081130 + reactos/lib/sdk/crt/thread/thread.c # Synced at 20080604 + +User32 - + reactos/dll/win32/user32/controls/button.c # Synced to Wine-1_1_39 + reactos/dll/win32/user32/controls/combo.c # Synced to Wine-1.5.10 + reactos/dll/win32/user32/controls/edit.c # Synced to Wine-1_1_40 + reactos/dll/win32/user32/controls/icontitle.c # Synced to Wine-1_1_39 + reactos/dll/win32/user32/controls/listbox.c # Synced to Wine-1_1_39 + reactos/dll/win32/user32/controls/scrollbar.c # Forked + reactos/dll/win32/user32/controls/static.c # Synced to Wine-1_1_39 + + reactos/dll/win32/user32/include/dde_private.h # Synced to Wine-1.1.24 + + reactos/dll/win32/user32/misc/dde.c # Synced to Wine-1.1.24 (dde_misc.c) + reactos/dll/win32/user32/misc/ddeclient.c # Synced to Wine-1_1_23 + reactos/dll/win32/user32/misc/ddeserver.c # Synced to Wine-1_1_23 + reactos/dll/win32/user32/misc/exticon.c # Synced to Wine-1_1_22 + reactos/dll/win32/user32/misc/resources.c # Forked? + reactos/dll/win32/user32/misc/winhelp.c # Last sync date unknown + reactos/dll/win32/user32/misc/wsprintf.c # Synced to Wine-1_1_23 + + reactos/dll/win32/user32/windows/cursoricon # Forked from Wine-1.2-rc7 + reactos/dll/win32/user32/windows/defwnd.c # Forked + reactos/dll/win32/user32/windows/draw.c # Forked at Wine-20020904 (uitools.c) + reactos/dll/win32/user32/windows/mdi.c # Synced to Wine-1_1_40 + reactos/dll/win32/user32/windows/menu.c # Forked + reactos/dll/win32/user32/windows/messagebox.c # Forked + reactos/dll/win32/user32/windows/rect.c # Forked (uitools.c) + reactos/dll/win32/user32/windows/spy.c # Synced to Wine-1_1_40 + reactos/dll/win32/user32/windows/text.c # Forked (lstr.c) + reactos/dll/win32/user32/windows/winpos.c # Forked + +ws2_32 - + reactos/dll/win32/ws2_32/misc/async.c # Synced to Wine-1.5.4 + +Update process for autosync DLLs + +*** This process can only be done on Microsoft Windows *** +- Download wineimport.cmd at svn://svn.reactos.org/project-tools/trunk/ +- Download cvs command line program and be sure to put it in your path + (available at http://ftp.gnu.org/non-gnu/cvs/binary/stable/x86-woe/ ) +- Download svn command line program and be sure to put it in your path + (available at http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 ) +- Download patch command line program and be sure to put it in your path + (available at http://gnuwin32.sourceforge.net/packages/patch.htm ) +- Optionally, download junction.exe and put it in your path + (available at http://www.sysinternals.com/Utilities/Junction.html ) +- Cd to the directory containing wineimport.cmd +- Run + "wineimport.cmd fullprocessing {path_to_your_reactos_directory} {name_of_the_wine_dll}" +- In case of error, you can + 1) add missing definitions to ReactOS headers + 2) fix {name_of_the_wine_dll}_ros.diff patch file, by removing outdated changes + 3) or modify Wine code, by adding a {name_of_the_wine_dll}_ros.diff patch, + which will be applied after Wine source checkout + DO NOT DIRECTLY MODIFY WINE SOURCE CODE +- Once Wine DLL compiles, install and run ReactOS and search for regressions +- In case of regression, you can + 1) Fix ReactOS code (not in the shared DLL) + 2) or update the {name_of_the_wine_dll}_ros.diff patch file +- Commit your changes with the "Autosyncing with Wine HEAD" commit message diff --cc subsystems/ntvdm/bios/bios.c index 00000000000,12066b53dd3..12066b53dd3 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios.c +++ b/subsystems/ntvdm/bios/bios.c diff --cc subsystems/ntvdm/bios/bios.h index 00000000000,cc1bb94b803..cc1bb94b803 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios.h +++ b/subsystems/ntvdm/bios/bios.h diff --cc subsystems/ntvdm/bios/bios32/bios32.c index 00000000000,fc272826242..fc272826242 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/bios32.c +++ b/subsystems/ntvdm/bios/bios32/bios32.c diff --cc subsystems/ntvdm/bios/bios32/bios32.h index 00000000000,4bd5d30b15c..4bd5d30b15c mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/bios32.h +++ b/subsystems/ntvdm/bios/bios32/bios32.h diff --cc subsystems/ntvdm/bios/bios32/bios32p.h index 00000000000,604fda30cc7..604fda30cc7 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/bios32p.h +++ b/subsystems/ntvdm/bios/bios32/bios32p.h diff --cc subsystems/ntvdm/bios/bios32/kbdbios32.c index 00000000000,9d95ccd368b..9d95ccd368b mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/kbdbios32.c +++ b/subsystems/ntvdm/bios/bios32/kbdbios32.c diff --cc subsystems/ntvdm/bios/bios32/kbdbios32.h index 00000000000,b5f9c145a12..b5f9c145a12 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/kbdbios32.h +++ b/subsystems/ntvdm/bios/bios32/kbdbios32.h diff --cc subsystems/ntvdm/bios/bios32/vidbios32.c index 00000000000,732776c2db6..732776c2db6 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/vidbios32.c +++ b/subsystems/ntvdm/bios/bios32/vidbios32.c diff --cc subsystems/ntvdm/bios/bios32/vidbios32.h index 00000000000,46b853bb61e..46b853bb61e mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/bios32/vidbios32.h +++ b/subsystems/ntvdm/bios/bios32/vidbios32.h diff --cc subsystems/ntvdm/bios/kbdbios.c index 00000000000,72550869f9d..72550869f9d mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/kbdbios.c +++ b/subsystems/ntvdm/bios/kbdbios.c diff --cc subsystems/ntvdm/bios/kbdbios.h index 00000000000,46486785b2b..46486785b2b mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/kbdbios.h +++ b/subsystems/ntvdm/bios/kbdbios.h diff --cc subsystems/ntvdm/bios/rom.c index 00000000000,e4dbc6bf5f6..e4dbc6bf5f6 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/rom.c +++ b/subsystems/ntvdm/bios/rom.c diff --cc subsystems/ntvdm/bios/rom.h index 00000000000,01f95e85dc8..01f95e85dc8 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/rom.h +++ b/subsystems/ntvdm/bios/rom.h diff --cc subsystems/ntvdm/bios/vidbios.c index 00000000000,cec5a7060e2..cec5a7060e2 mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/vidbios.c +++ b/subsystems/ntvdm/bios/vidbios.c diff --cc subsystems/ntvdm/bios/vidbios.h index 00000000000,70b3e54606e..70b3e54606e mode 000000,100644..100644 --- a/subsystems/ntvdm/bios/vidbios.h +++ b/subsystems/ntvdm/bios/vidbios.h diff --cc subsystems/ntvdm/bop.c index 00000000000,407004290b7..407004290b7 mode 000000,100644..100644 --- a/subsystems/ntvdm/bop.c +++ b/subsystems/ntvdm/bop.c diff --cc subsystems/ntvdm/bop.h index 00000000000,a936d1f25cb..a936d1f25cb mode 000000,100644..100644 --- a/subsystems/ntvdm/bop.h +++ b/subsystems/ntvdm/bop.h diff --cc subsystems/ntvdm/callback.c index 00000000000,34c0bcc7824..34c0bcc7824 mode 000000,100644..100644 --- a/subsystems/ntvdm/callback.c +++ b/subsystems/ntvdm/callback.c diff --cc subsystems/ntvdm/callback.h index 00000000000,c29ef9cb687..c29ef9cb687 mode 000000,100644..100644 --- a/subsystems/ntvdm/callback.h +++ b/subsystems/ntvdm/callback.h diff --cc subsystems/ntvdm/clock.c index 00000000000,74e1fe4b4f5..74e1fe4b4f5 mode 000000,100644..100644 --- a/subsystems/ntvdm/clock.c +++ b/subsystems/ntvdm/clock.c diff --cc subsystems/ntvdm/clock.h index 00000000000,53927d2b257..53927d2b257 mode 000000,100644..100644 --- a/subsystems/ntvdm/clock.h +++ b/subsystems/ntvdm/clock.h diff --cc subsystems/ntvdm/dos/dem.c index 00000000000,f78dab86a3a..f78dab86a3a mode 000000,100644..100644 --- a/subsystems/ntvdm/dos/dem.c +++ b/subsystems/ntvdm/dos/dem.c diff --cc subsystems/ntvdm/dos/dem.h index 00000000000,1e5f2de1b05..1e5f2de1b05 mode 000000,100644..100644 --- a/subsystems/ntvdm/dos/dem.h +++ b/subsystems/ntvdm/dos/dem.h diff --cc subsystems/ntvdm/dos/dos32krnl/bios.c index 00000000000,b7ce9d313e5..b7ce9d313e5 mode 000000,100644..100644 --- a/subsystems/ntvdm/dos/dos32krnl/bios.c +++ b/subsystems/ntvdm/dos/dos32krnl/bios.c diff --cc subsystems/ntvdm/dos/dos32krnl/dos.c index 00000000000,f1f9fffd70b..f1f9fffd70b mode 000000,100644..100644 --- a/subsystems/ntvdm/dos/dos32krnl/dos.c +++ b/subsystems/ntvdm/dos/dos32krnl/dos.c diff --cc subsystems/ntvdm/dos/dos32krnl/dos.h index 00000000000,18e0b14cd07..18e0b14cd07 mode 000000,100644..100644 --- a/subsystems/ntvdm/dos/dos32krnl/dos.h +++ b/subsystems/ntvdm/dos/dos32krnl/dos.h diff --cc subsystems/ntvdm/emulator.c index 00000000000,cbe3e1eb01c..cbe3e1eb01c mode 000000,100644..100644 --- a/subsystems/ntvdm/emulator.c +++ b/subsystems/ntvdm/emulator.c diff --cc subsystems/ntvdm/emulator.h index 00000000000,20e9a07a98c..20e9a07a98c mode 000000,100644..100644 --- a/subsystems/ntvdm/emulator.h +++ b/subsystems/ntvdm/emulator.h diff --cc subsystems/ntvdm/hardware/cmos.c index 00000000000,4f86467249b..4f86467249b mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/cmos.c +++ b/subsystems/ntvdm/hardware/cmos.c diff --cc subsystems/ntvdm/hardware/cmos.h index 00000000000,f84d27ccc28..f84d27ccc28 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/cmos.h +++ b/subsystems/ntvdm/hardware/cmos.h diff --cc subsystems/ntvdm/hardware/pic.c index 00000000000,aa40821f449..aa40821f449 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/pic.c +++ b/subsystems/ntvdm/hardware/pic.c diff --cc subsystems/ntvdm/hardware/pic.h index 00000000000,03d7dd1738e..03d7dd1738e mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/pic.h +++ b/subsystems/ntvdm/hardware/pic.h diff --cc subsystems/ntvdm/hardware/ps2.c index 00000000000,c98d32e76df..c98d32e76df mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/ps2.c +++ b/subsystems/ntvdm/hardware/ps2.c diff --cc subsystems/ntvdm/hardware/ps2.h index 00000000000,119e9bdc471..119e9bdc471 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/ps2.h +++ b/subsystems/ntvdm/hardware/ps2.h diff --cc subsystems/ntvdm/hardware/speaker.c index 00000000000,03bf25cc84a..03bf25cc84a mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/speaker.c +++ b/subsystems/ntvdm/hardware/speaker.c diff --cc subsystems/ntvdm/hardware/speaker.h index 00000000000,94c9fc07db5..94c9fc07db5 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/speaker.h +++ b/subsystems/ntvdm/hardware/speaker.h diff --cc subsystems/ntvdm/hardware/timer.c index 00000000000,87f68634479..87f68634479 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/timer.c +++ b/subsystems/ntvdm/hardware/timer.c diff --cc subsystems/ntvdm/hardware/timer.h index 00000000000,21f7f8ee379..21f7f8ee379 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/timer.h +++ b/subsystems/ntvdm/hardware/timer.h diff --cc subsystems/ntvdm/hardware/vga.c index 00000000000,4a4dc1b9237..4a4dc1b9237 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/vga.c +++ b/subsystems/ntvdm/hardware/vga.c diff --cc subsystems/ntvdm/hardware/vga.h index 00000000000,1f142c175a8..1f142c175a8 mode 000000,100644..100644 --- a/subsystems/ntvdm/hardware/vga.h +++ b/subsystems/ntvdm/hardware/vga.h diff --cc subsystems/ntvdm/int32.c index 00000000000,b89eb137e03..b89eb137e03 mode 000000,100644..100644 --- a/subsystems/ntvdm/int32.c +++ b/subsystems/ntvdm/int32.c diff --cc subsystems/ntvdm/int32.h index 00000000000,d6cced23348..d6cced23348 mode 000000,100644..100644 --- a/subsystems/ntvdm/int32.h +++ b/subsystems/ntvdm/int32.h diff --cc subsystems/ntvdm/io.c index 00000000000,9a85a114cf5..9a85a114cf5 mode 000000,100644..100644 --- a/subsystems/ntvdm/io.c +++ b/subsystems/ntvdm/io.c diff --cc subsystems/ntvdm/io.h index 00000000000,9ed80ec794b..9ed80ec794b mode 000000,100644..100644 --- a/subsystems/ntvdm/io.h +++ b/subsystems/ntvdm/io.h diff --cc subsystems/ntvdm/ntvdm.h index 00000000000,3555ce41d6b..3555ce41d6b mode 000000,100644..100644 --- a/subsystems/ntvdm/ntvdm.h +++ b/subsystems/ntvdm/ntvdm.h diff --cc subsystems/ntvdm/ntvdm.spec index 00000000000,244bda9200f..244bda9200f mode 000000,100644..100644 --- a/subsystems/ntvdm/ntvdm.spec +++ b/subsystems/ntvdm/ntvdm.spec diff --cc subsystems/ntvdm/registers.c index 00000000000,d3d5ce16675..d3d5ce16675 mode 000000,100644..100644 --- a/subsystems/ntvdm/registers.c +++ b/subsystems/ntvdm/registers.c diff --cc subsystems/ntvdm/registers.h index 00000000000,09568f3b7a8..09568f3b7a8 mode 000000,100644..100644 --- a/subsystems/ntvdm/registers.h +++ b/subsystems/ntvdm/registers.h diff --cc subsystems/ntvdm/res/ntvdm.ico index 00000000000,161c1b07a9a..161c1b07a9a mode 000000,100644..100644 Binary files differ diff --cc subsystems/ntvdm/utils.c index 00000000000,0619376ee98..0619376ee98 mode 000000,100644..100644 --- a/subsystems/ntvdm/utils.c +++ b/subsystems/ntvdm/utils.c diff --cc subsystems/ntvdm/utils.h index 00000000000,98ce0936a96..98ce0936a96 mode 000000,100644..100644 --- a/subsystems/ntvdm/utils.h +++ b/subsystems/ntvdm/utils.h diff --cc subsystems/ntvdm/vddsup.c index 00000000000,92a0ff1ac3b..92a0ff1ac3b mode 000000,100644..100644 --- a/subsystems/ntvdm/vddsup.c +++ b/subsystems/ntvdm/vddsup.c diff --cc subsystems/ntvdm/vddsup.h index 00000000000,4de9178a3ea..4de9178a3ea mode 000000,100644..100644 --- a/subsystems/ntvdm/vddsup.h +++ b/subsystems/ntvdm/vddsup.h diff --cc subsystems/win/basesrv/vdm.h index 00000000000,3da59d90442..3da59d90442 mode 000000,100644..100644 --- a/subsystems/win/basesrv/vdm.h +++ b/subsystems/win/basesrv/vdm.h diff --cc win32ss/user/user32/windows/menu.c index bf335d60d4a,00000000000..08e33325c31 mode 100644,000000..100644 --- a/win32ss/user/user32/windows/menu.c +++ b/win32ss/user/user32/windows/menu.c @@@ -1,5864 -1,0 +1,5664 @@@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS user32.dll + * FILE: user32/windows/menu.c + * PURPOSE: Menus + * + * PROGRAMMERS: Casper S. Hornstrup + * James Tabor + */ + +/* INCLUDES ******************************************************************/ + +#include +#include + +LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active); +BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj); ++LRESULT DefWndNCHitTest(HWND hWnd, POINT Point); ++void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect); + +WINE_DEFAULT_DEBUG_CHANNEL(menu); + +/* internal popup menu window messages */ + +#define MM_SETMENUHANDLE (WM_USER + 0) +#define MM_GETMENUHANDLE (WM_USER + 1) + +/* internal flags for menu tracking */ + +#define TF_ENDMENU 0x10000 +#define TF_SUSPENDPOPUP 0x20000 +#define TF_SKIPREMOVE 0x40000 + +#define ITEM_PREV -1 +#define ITEM_NEXT 1 + +/* Internal MenuTrackMenu() flags */ +#define TPM_INTERNAL 0xF0000000 +#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */ +#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */ + +/* top and bottom margins for popup menus */ +#define MENU_TOP_MARGIN 3 +#define MENU_BOTTOM_MARGIN 2 + +#define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR) + +#define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK) + +#define MNS_STYLE_MASK (MNS_NOCHECK|MNS_MODELESS|MNS_DRAGDROP|MNS_AUTODISMISS|MNS_NOTIFYBYPOS|MNS_CHECKORBMP) + +#define MENUITEMINFO_TYPE_MASK \ + (MFT_STRING | MFT_BITMAP | MFT_OWNERDRAW | MFT_SEPARATOR | \ + MFT_MENUBARBREAK | MFT_MENUBREAK | MFT_RADIOCHECK | \ + MFT_RIGHTORDER | MFT_RIGHTJUSTIFY /* same as MF_HELP */ ) + +#define TYPE_MASK (MENUITEMINFO_TYPE_MASK | MF_POPUP | MF_SYSMENU) + +#define STATE_MASK (~TYPE_MASK) + +#define MENUITEMINFO_STATE_MASK (STATE_MASK & ~(MF_BYPOSITION | MF_MOUSESELECT)) + +#define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT) + +/* macro to test that flags do not indicate bitmap, ownerdraw or separator */ +#define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags)) +#define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1)) + +#define IS_SYSTEM_MENU(MenuInfo) \ + (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN)) + +#define IS_SYSTEM_POPUP(MenuInfo) \ + (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSDESKMN)) + +#define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags)) + +/* Use global popup window because there's no way 2 menus can + * be tracked at the same time. */ +static HWND top_popup; +static HMENU top_popup_hmenu; + +/* Flag set by EndMenu() to force an exit from menu tracking */ +static BOOL fEndMenu = FALSE; + +#define MENU_ITEM_HBMP_SPACE (5) +#define MENU_BAR_ITEMS_SPACE (12) +#define SEPARATOR_HEIGHT (5) +#define MENU_TAB_SPACE (8) + +typedef struct +{ + UINT TrackFlags; + HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/ + HMENU TopMenu; /* initial menu */ + HWND OwnerWnd; /* where notifications are sent */ + POINT Pt; +} MTRACKER; + + +/********************************************************************* + * PopupMenu class descriptor + */ +const struct builtin_class_descr POPUPMENU_builtin_class = +{ + WC_MENU, /* name */ + CS_SAVEBITS | CS_DBLCLKS, /* style */ + (WNDPROC) NULL, /* FIXME - procA */ + (WNDPROC) PopupMenuWndProcW, /* FIXME - procW */ + sizeof(MENUINFO *), /* extra */ + (LPCWSTR) IDC_ARROW, /* cursor */ + (HBRUSH)(COLOR_MENU + 1) /* brush */ +}; + +#ifndef GET_WORD +#define GET_WORD(ptr) (*(WORD *)(ptr)) +#endif +#ifndef GET_DWORD +#define GET_DWORD(ptr) (*(DWORD *)(ptr)) +#endif + +HFONT hMenuFont = NULL; +HFONT hMenuFontBold = NULL; + +/* Dimension of the menu bitmaps */ +static HBITMAP BmpSysMenu = NULL; + +static SIZE MenuCharSize; + + +/*********************************************************************** + * MENU_GetMenu + * + * Validate the given menu handle and returns the menu structure pointer. + */ - PMENU FORCEINLINE MENU_GetMenu(HMENU hMenu) ++FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu) +{ - return ValidateHandle(hMenu, TYPE_MENU); ++ return ValidateHandleNoErr(hMenu, TYPE_MENU); ++} ++ ++/*********************************************************************** ++ * get_win_sys_menu ++ * ++ * Get the system menu of a window ++ */ ++static HMENU get_win_sys_menu( HWND hwnd ) ++{ ++ HMENU ret = 0; ++ WND *win = ValidateHwnd( hwnd ); ++ if (win) ++ { ++ ret = win->SystemMenu; ++ } ++ return ret; +} + +/*********************************************************************** + * MENU_FindItem + * + * Find a menu item. Return a pointer on the item, and modifies *hmenu + * in case the item was in a sub-menu. + */ +ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags ) +{ + MENU *menu; + ITEM *fallback = NULL; + UINT fallback_pos = 0; + UINT i; + PITEM pItem; + + if ((*hmenu == (HMENU)0xffff) || (!(menu = MENU_GetMenu(*hmenu)))) return NULL; + if (wFlags & MF_BYPOSITION) + { + if (*nPos >= menu->cItems) return NULL; + pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - //pItem = &menu->rgItems[*nPos]; - i = 0; - while(pItem) // Do this for now. - { - if (i < (INT)menu->cItems) - { - if ( *nPos == i ) return pItem; - } - pItem = pItem->Next ? DesktopPtrToUser(pItem->Next) : NULL; - i++; - } ++ if (pItem) pItem = &pItem[*nPos]; ++ return pItem; + } + else + { + PITEM item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - for (i = 0; item ,i < menu->cItems; i++, item = item->Next ? DesktopPtrToUser(item->Next) : NULL)//, item++) ++ for (i = 0; item, i < menu->cItems; i++, item++) + { + if (item->spSubMenu) + { + PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu); + HMENU hsubmenu = UserHMGetHandle(pSubMenu); + ITEM *subitem = MENU_FindItem( &hsubmenu, nPos, wFlags ); + if (subitem) + { + *hmenu = hsubmenu; + return subitem; + } + else if (item->wID == *nPos) + { + /* fallback to this item if nothing else found */ + fallback_pos = i; + fallback = item; + } + } + else if (item->wID == *nPos) + { + *nPos = i; + return item; + } + } + } + + if (fallback) + *nPos = fallback_pos; + + return fallback; +} + +#define MAX_GOINTOSUBMENU (0x10) +UINT FASTCALL +IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc) +{ - UINT x = 0; - UINT res = -1; - UINT sres; ++ UINT i = 0; + PITEM Item = Menu->rgItems ? DesktopPtrToUser(Menu->rgItems) : NULL; + - while(Item) - { - if (Item->fState & MFS_DEFAULT) - { - if (!(gmdiFlags & GMDI_USEDISABLED) && - (Item->fState & MFS_DISABLED) ) - break; ++ /* empty menu */ ++ if (!Item) return -1; + - res = fByPos ? x : Item->wID; ++ while ( !( Item->fState & MFS_DEFAULT ) ) ++ { ++ i++; Item++; ++ if (i >= Menu->cItems ) return -1; ++ } + - if ((*gismc < MAX_GOINTOSUBMENU) && - (gmdiFlags & GMDI_GOINTOPOPUPS) && - Item->spSubMenu) - { - if (DesktopPtrToUser(Item->spSubMenu) == Menu) - break; ++ /* default: don't return disabled items */ ++ if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (Item->fState & MFS_DISABLED )) return -1; + - (*gismc)++; - sres = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc); - (*gismc)--; ++ /* search rekursiv when needed */ ++ if ( (Item->fType & MF_POPUP) && (gmdiFlags & GMDI_GOINTOPOPUPS) && Item->spSubMenu) ++ { ++ UINT ret; ++ (*gismc)++; ++ ret = IntGetMenuDefaultItem( DesktopPtrToUser(Item->spSubMenu), fByPos, gmdiFlags, gismc ); ++ (*gismc)--; ++ if ( -1 != ret ) return ret; + - if(sres > (UINT)-1) - res = sres; - } - break; - } - Item = Item->Next ? DesktopPtrToUser(Item->Next) : NULL; - x++; ++ /* when item not found in submenu, return the popup item */ + } - return res; ++ return ( fByPos ) ? i : Item->wID; +} + +static BOOL GetMenuItemInfo_common ( HMENU hmenu, + UINT item, + BOOL bypos, + LPMENUITEMINFOW lpmii, + BOOL unicode) +{ + ITEM *pItem = MENU_FindItem (&hmenu, &item, bypos ? MF_BYPOSITION : 0); + + //debug_print_menuitem("GetMenuItemInfo_common: ", pItem, ""); + + if (!pItem) + { + SetLastError( ERROR_MENU_ITEM_NOT_FOUND); - //SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if( lpmii->fMask & MIIM_TYPE) + { + if( lpmii->fMask & ( MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) + { + ERR("invalid combination of fMask bits used\n"); + /* this does not happen on Win9x/ME */ + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK; + if( pItem->hbmp) lpmii->fType |= MFT_BITMAP; + lpmii->hbmpItem = pItem->hbmp; /* not on Win9x/ME */ + if( lpmii->fType & MFT_BITMAP) + { + lpmii->dwTypeData = (LPWSTR) pItem->hbmp; + lpmii->cch = 0; + } + else if( lpmii->fType & (MFT_OWNERDRAW | MFT_SEPARATOR)) + { + /* this does not happen on Win9x/ME */ + lpmii->dwTypeData = 0; + lpmii->cch = 0; + } + } + + /* copy the text string */ + if ((lpmii->fMask & (MIIM_TYPE|MIIM_STRING))) + { + if( !pItem->Xlpstr ) + { // Very strange this fixes a wine test with a crash. + if(lpmii->dwTypeData && lpmii->cch && !(GdiValidateHandle((HGDIOBJ)lpmii->dwTypeData)) ) + { + lpmii->cch = 0; + if( unicode) + *((WCHAR *)lpmii->dwTypeData) = 0; + else + *((CHAR *)lpmii->dwTypeData) = 0; + } + } + else + { + int len; + LPWSTR text = DesktopPtrToUser(pItem->Xlpstr); + if (unicode) + { + len = strlenW(text); + if(lpmii->dwTypeData && lpmii->cch) + lstrcpynW(lpmii->dwTypeData, text, lpmii->cch); + } + else + { + len = WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ) - 1; + if(lpmii->dwTypeData && lpmii->cch) + if (!WideCharToMultiByte( CP_ACP, 0, text, -1, + (LPSTR)lpmii->dwTypeData, lpmii->cch, NULL, NULL )) + ((LPSTR)lpmii->dwTypeData)[lpmii->cch - 1] = 0; + } + /* if we've copied a substring we return its length */ + if(lpmii->dwTypeData && lpmii->cch) + if (lpmii->cch <= len + 1) + lpmii->cch--; + else + lpmii->cch = len; + else + { + /* return length of string */ + /* not on Win9x/ME if fType & MFT_BITMAP */ + lpmii->cch = len; + } + } + } + + if (lpmii->fMask & MIIM_FTYPE) + lpmii->fType = pItem->fType & MENUITEMINFO_TYPE_MASK; + + if (lpmii->fMask & MIIM_BITMAP) + lpmii->hbmpItem = pItem->hbmp; + + if (lpmii->fMask & MIIM_STATE) - lpmii->fState = pItem->fState & MII_STATE_MASK; //MENUITEMINFO_STATE_MASK; ++ lpmii->fState = pItem->fState & MENUITEMINFO_STATE_MASK; + + if (lpmii->fMask & MIIM_ID) + lpmii->wID = pItem->wID; + + if (lpmii->fMask & MIIM_SUBMENU && pItem->spSubMenu ) + { + PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); + HMENU hSubMenu = UserHMGetHandle(pSubMenu); + lpmii->hSubMenu = hSubMenu; + } + else + { + /* hSubMenu is always cleared + * (not on Win9x/ME ) */ + lpmii->hSubMenu = 0; + } + + if (lpmii->fMask & MIIM_CHECKMARKS) + { + lpmii->hbmpChecked = pItem->hbmpChecked; + lpmii->hbmpUnchecked = pItem->hbmpUnchecked; + } + if (lpmii->fMask & MIIM_DATA) + lpmii->dwItemData = pItem->dwItemData; + + return TRUE; +} + +/*********************************************************************** + * MenuGetRosMenuInfo + * + * Get full information about menu + */ +static BOOL FASTCALL +MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu) +{ + PMENU pMenu; - if (!(pMenu = ValidateHandle(Menu, TYPE_MENU))) return FALSE; ++ if (!(pMenu = ValidateHandleNoErr(Menu, TYPE_MENU))) return FALSE; + + MenuInfo->hbrBack = pMenu->hbrBack; + MenuInfo->dwContextHelpID = pMenu->dwContextHelpId; + MenuInfo->cyMax = pMenu->cyMax; + MenuInfo->dwMenuData = pMenu->dwMenuData; + MenuInfo->dwStyle = pMenu->fFlags & MNS_STYLE_MASK; + + MenuInfo->cItems = pMenu->cItems; + + MenuInfo->iItem = pMenu->iItem; + MenuInfo->cxMenu = pMenu->cxMenu; + MenuInfo->cyMenu = pMenu->cyMenu; + MenuInfo->spwndNotify = pMenu->spwndNotify; + MenuInfo->cxTextAlign = pMenu->cxTextAlign; + MenuInfo->iTop = pMenu->iMaxTop; + MenuInfo->iMaxTop = pMenu->iMaxTop; + MenuInfo->dwArrowsOn = pMenu->dwArrowsOn; + + MenuInfo->fFlags = pMenu->fFlags; + MenuInfo->Self = pMenu->head.h; + MenuInfo->TimeToHide = pMenu->TimeToHide; + MenuInfo->Wnd = pMenu->hWnd; + return TRUE; +} + +/*********************************************************************** + * MenuSetRosMenuInfo + * + * Set full information about menu + */ +static BOOL FASTCALL +MenuSetRosMenuInfo(PROSMENUINFO MenuInfo) +{ + MenuInfo->cbSize = sizeof(ROSMENUINFO); + MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE; + + return NtUserThunkedMenuInfo(MenuInfo->Self, (LPCMENUINFO)MenuInfo); +} + +/*********************************************************************** + * MenuInitRosMenuItemInfo + * + * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo + */ +static VOID FASTCALL +MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) +{ + ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO)); + ItemInfo->cbSize = sizeof(ROSMENUITEMINFO); +} + +/*********************************************************************** + * MenuGetRosMenuItemInfo + * + * Get full information about a menu item + */ +static BOOL FASTCALL +MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo) +{ + PITEM pItem; + UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */ + + if (ItemInfo->dwTypeData != NULL) + { + HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData); + } + + ItemInfo->dwTypeData = NULL; + + if (!(pItem = MENU_FindItem(&Menu, &Index, MF_BYPOSITION))) + { + ItemInfo->fType = 0; + return FALSE; + } + + ItemInfo->fType = pItem->fType; + ItemInfo->hbmpItem = pItem->hbmp; + ItemInfo->hbmpChecked = pItem->hbmpChecked; + ItemInfo->hbmpUnchecked = pItem->hbmpUnchecked; + ItemInfo->dwItemData = pItem->dwItemData; + ItemInfo->wID = pItem->wID; + ItemInfo->fState = pItem->fState; + + if (pItem->spSubMenu) + { + PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); + HMENU hSubMenu = UserHMGetHandle(pSubMenu); + ItemInfo->hSubMenu = hSubMenu; + } + else + ItemInfo->hSubMenu = NULL; + + if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING) + { + LPWSTR lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL; + if (lpstr) + { + ItemInfo->cch = pItem->lpstr.Length / sizeof(WCHAR); + ItemInfo->cch++; + ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0, ItemInfo->cch * sizeof(WCHAR)); + if (ItemInfo->dwTypeData == NULL) + { + return FALSE; + } + RtlCopyMemory(ItemInfo->dwTypeData, lpstr, min(ItemInfo->cch * sizeof(WCHAR), pItem->lpstr.MaximumLength)); + } + else + { + ItemInfo->cch = 0; + } + } + + ItemInfo->Rect.left = pItem->xItem; + ItemInfo->Rect.top = pItem->yItem; + ItemInfo->Rect.right = pItem->cxItem; // Do this for now...... + ItemInfo->Rect.bottom = pItem->cyItem; + ItemInfo->dxTab = pItem->dxTab; + ItemInfo->lpstr = pItem->lpstr.Buffer; + ItemInfo->maxBmpSize.cx = pItem->cxBmp; + ItemInfo->maxBmpSize.cy = pItem->cyBmp; + + ItemInfo->fMask = Save_Mask; + return TRUE; +} + +/*********************************************************************** + * MenuSetRosMenuItemInfo + * + * Set selected information about a menu item, need to set the mask bits. + */ +static BOOL FASTCALL +MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo) +{ + BOOL Ret; + + if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING && + ItemInfo->dwTypeData != NULL) + { + ItemInfo->cch = strlenW(ItemInfo->dwTypeData); + } ++ if (ItemInfo->hSubMenu) ++ { ++ if (!IsMenu(ItemInfo->hSubMenu)) ItemInfo->hSubMenu = NULL; ++ } + Ret = NtUserThunkedMenuItemInfo(Menu, Index, TRUE, FALSE, (LPMENUITEMINFOW)ItemInfo, NULL); + return Ret; +} + +/*********************************************************************** + * MenuCleanupRosMenuItemInfo + * + * Cleanup after use of MenuGet/SetRosMenuItemInfo + */ +static VOID FASTCALL +MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) +{ + if (ItemInfo->dwTypeData != NULL) + { + HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData); + ItemInfo->dwTypeData = NULL; + } +} + - DWORD FASTCALL - IntBuildMenuItemList(PMENU MenuObject, PVOID Buffer, ULONG nMax) - { - DWORD res = 0; - ROSMENUITEMINFO mii; - PVOID Buf; - PITEM CurItem = MenuObject->rgItems ? DesktopPtrToUser(MenuObject->rgItems) : NULL; - PWCHAR StrOut; - WCHAR NulByte; - - if (0 != nMax) - { - if (nMax < MenuObject->cItems * sizeof(ROSMENUITEMINFO)) - { - return 0; - } - StrOut = (PWCHAR)((char *) Buffer + MenuObject->cItems * sizeof(ROSMENUITEMINFO)); - nMax -= MenuObject->cItems * sizeof(ROSMENUITEMINFO); - Buf = Buffer; - mii.cbSize = sizeof(ROSMENUITEMINFO); - mii.fMask = 0; - NulByte = L'\0'; - - while (NULL != CurItem) - { - mii.cch = CurItem->lpstr.Length / sizeof(WCHAR); - mii.dwItemData = CurItem->dwItemData; - if (0 != CurItem->lpstr.Length) - { - mii.dwTypeData = StrOut; - } - else - { - mii.dwTypeData = NULL; - } - mii.fState = CurItem->fState; - mii.fType = CurItem->fType; - mii.wID = CurItem->wID; - mii.hbmpChecked = CurItem->hbmpChecked; - mii.hbmpItem = CurItem->hbmp; - mii.hbmpUnchecked = CurItem->hbmpUnchecked; - if (CurItem->spSubMenu) - { - PMENU pSubMenu = DesktopPtrToUser(CurItem->spSubMenu); - HMENU hSubMenu = UserHMGetHandle(pSubMenu); - mii.hSubMenu = hSubMenu; - } - else - mii.hSubMenu = NULL; - mii.Rect.left = CurItem->xItem; - mii.Rect.top = CurItem->yItem; - mii.Rect.right = CurItem->cxItem; // Do this for now...... - mii.Rect.bottom = CurItem->cyItem; - mii.dxTab = CurItem->dxTab; - mii.lpstr = CurItem->lpstr.Buffer; // Can be read from user side! - //mii.maxBmpSize.cx = CurItem->cxBmp; - //mii.maxBmpSize.cy = CurItem->cyBmp; - - RtlCopyMemory(Buf, &mii, sizeof(ROSMENUITEMINFO)); - Buf = (PVOID)((ULONG_PTR)Buf + sizeof(ROSMENUITEMINFO)); - - if (0 != CurItem->lpstr.Length && (nMax >= CurItem->lpstr.Length + sizeof(WCHAR))) - { - LPWSTR lpstr = CurItem->lpstr.Buffer ? DesktopPtrToUser(CurItem->lpstr.Buffer) : NULL; - if (lpstr) - { - /* Copy string */ - RtlCopyMemory(StrOut, lpstr, CurItem->lpstr.Length); - - StrOut += CurItem->lpstr.Length / sizeof(WCHAR); - RtlCopyMemory(StrOut, &NulByte, sizeof(WCHAR)); - StrOut++; - nMax -= CurItem->lpstr.Length + sizeof(WCHAR); - } - } - else if (0 != CurItem->lpstr.Length) - { - break; - } - - CurItem = CurItem->Next ? DesktopPtrToUser(CurItem->Next) : NULL; - res++; - } - } - else - { - while (NULL != CurItem) - { - res += sizeof(ROSMENUITEMINFO) + CurItem->lpstr.Length + sizeof(WCHAR); - CurItem = CurItem->Next ? DesktopPtrToUser(CurItem->Next) : NULL; - } - } - return res; - } - - /*********************************************************************** - * MenuGetAllRosMenuItemInfo - * - * Get full information about all menu items - */ - static INT FASTCALL - MenuGetAllRosMenuItemInfo(HMENU Menu, PROSMENUITEMINFO *ItemInfo) - { - DWORD BufSize; - PMENU pMenu; - - if (!(pMenu = ValidateHandle(Menu, TYPE_MENU))) return -1; - - BufSize = IntBuildMenuItemList(pMenu, (PVOID)1, 0); - if (BufSize == (DWORD) -1 || BufSize == 0) - { - return -1; - } - *ItemInfo = HeapAlloc(GetProcessHeap(), 0, BufSize); - if (NULL == *ItemInfo) - { - return -1; - } - - return IntBuildMenuItemList(pMenu, (PVOID)*ItemInfo, BufSize); - } - - /*********************************************************************** - * MenuCleanupAllRosMenuItemInfo - * - * Cleanup after use of MenuGetAllRosMenuItemInfo - */ - static VOID FASTCALL - MenuCleanupAllRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) - { - HeapFree(GetProcessHeap(), 0, ItemInfo); - } - +/*********************************************************************** + * MenuInitSysMenuPopup + * + * Grey the appropriate items in System menu. + */ +void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest ) +{ + BOOL gray; + UINT DefItem; + #if 0 + MENUITEMINFOW mii; + #endif + + gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE)); + EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = ((style & WS_MAXIMIZE) != 0); + EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE); + EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE); + EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE)); + EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = (clsStyle & CS_NOCLOSE) != 0; + + /* The menu item must keep its state if it's disabled */ + if(gray) + EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED); + + /* Set default menu item */ + if(style & WS_MINIMIZE) DefItem = SC_RESTORE; + else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE); + else DefItem = SC_CLOSE; +#if 0 + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask |= MIIM_STATE; + if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) && + (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE; +#endif + SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND); +} + +/****************************************************************************** + * + * UINT MenuGetStartOfNextColumn( + * PROSMENUINFO MenuInfo) + * + *****************************************************************************/ - static UINT MenuGetStartOfNextColumn( - PROSMENUINFO MenuInfo) ++ ++static UINT MENU_GetStartOfNextColumn( ++ HMENU hMenu ) +{ - PROSMENUITEMINFO MenuItems; ++ MENU *menu = MENU_GetMenu(hMenu); ++ PITEM pItem; + UINT i; + - i = MenuInfo->iItem; - if ( i == NO_SELECTED_ITEM ) - return i; ++ if(!menu) ++ return NO_SELECTED_ITEM; + - if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0) - return NO_SELECTED_ITEM; ++ i = menu->iItem + 1; ++ if( i == NO_SELECTED_ITEM ) ++ return i; + - for (i++ ; i < MenuInfo->cItems; i++) - if (0 != (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK))) - return i; ++ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; ++ if (!pItem) return NO_SELECTED_ITEM; ++ for( ; i < menu->cItems; ++i ) { ++ if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) ++ return i; ++ } + + return NO_SELECTED_ITEM; +} + +/****************************************************************************** + * + * UINT MenuGetStartOfPrevColumn( + * PROSMENUINFO MenuInfo) + * + *****************************************************************************/ - - static UINT FASTCALL MenuGetStartOfPrevColumn( - PROSMENUINFO MenuInfo) ++static UINT MENU_GetStartOfPrevColumn( ++ HMENU hMenu ) +{ - PROSMENUITEMINFO MenuItems; - UINT i; ++ MENU *menu = MENU_GetMenu(hMenu); ++ UINT i; ++ PITEM pItem; + - if (!MenuInfo->iItem || MenuInfo->iItem == NO_SELECTED_ITEM) - return NO_SELECTED_ITEM; ++ if( !menu ) ++ return NO_SELECTED_ITEM; + - if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &MenuItems) <= 0) - return NO_SELECTED_ITEM; ++ if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM ) ++ return NO_SELECTED_ITEM; ++ ++ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; ++ if (!pItem) return NO_SELECTED_ITEM; + + /* Find the start of the column */ - for (i = MenuInfo->iItem; - 0 != i && 0 == (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)); - --i) - { - ; /* empty */ - } + - if (i == 0) - { - MenuCleanupAllRosMenuItemInfo(MenuItems); - return NO_SELECTED_ITEM; - } ++ for(i = menu->iItem; i != 0 && ++ !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)); ++ --i); /* empty */ + - for (--i; 0 != i; --i) - if (MenuItems[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) - break; ++ if(i == 0) ++ return NO_SELECTED_ITEM; ++ ++ for(--i; i != 0; --i) { ++ if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) ++ break; ++ } + - MenuCleanupAllRosMenuItemInfo(MenuItems); + TRACE("ret %d.\n", i ); + + return i; +} + +/*********************************************************************** + * MenuLoadBitmaps + * + * Load the arrow bitmap. We can't do this from MenuInit since user32 + * can also be used (and thus initialized) from text-mode. + */ +static void FASTCALL +MenuLoadBitmaps(VOID) +{ + /* Load system buttons bitmaps */ + if (NULL == BmpSysMenu) + { + BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)); + } +} +/////////// Make gpsi OBMI via callback ////////////// +/*********************************************************************** + * get_arrow_bitmap + */ +HBITMAP get_arrow_bitmap(void) +{ + static HBITMAP arrow_bitmap; + + if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW)); + return arrow_bitmap; +} + +/*********************************************************************** + * get_down_arrow_bitmap DFCS_MENUARROWDOWN + */ +HBITMAP get_down_arrow_bitmap(void) +{ + static HBITMAP arrow_bitmap; + + if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW)); + return arrow_bitmap; +} + +/*********************************************************************** + * get_down_arrow_inactive_bitmap DFCS_MENUARROWDOWN | DFCS_INACTIVE + */ +HBITMAP get_down_arrow_inactive_bitmap(void) +{ + static HBITMAP arrow_bitmap; + + if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI)); + return arrow_bitmap; +} + +/*********************************************************************** + * get_up_arrow_bitmap DFCS_MENUARROWUP + */ +HBITMAP get_up_arrow_bitmap(void) +{ + static HBITMAP arrow_bitmap; + + if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW)); + return arrow_bitmap; +} + +/*********************************************************************** + * get_up_arrow_inactive_bitmap DFCS_MENUARROWUP | DFCS_INACTIVE + */ +static HBITMAP get_up_arrow_inactive_bitmap(void) +{ + static HBITMAP arrow_bitmap; + + if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI)); + return arrow_bitmap; +} +//////////////// +/*********************************************************************** + * MenuFindSubMenu + * + * Find a Sub menu. Return the position of the submenu, and modifies + * *hmenu in case it is found in another sub-menu. + * If the submenu cannot be found, NO_SELECTED_ITEM is returned. + */ +static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget ) +{ + PMENU menu, pSubMenu; + HMENU hSubMenu; + UINT i; + PITEM item; + + if (((*hmenu)==(HMENU)0xffff) ||(!(menu = MENU_GetMenu(*hmenu)))) + return NO_SELECTED_ITEM; + + item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - for (i = 0; i < menu->cItems; i++, item = item->Next ? DesktopPtrToUser(item->Next) : NULL)//item++) ++ for (i = 0; i < menu->cItems; i++, item++) + { + if (!item->spSubMenu) + continue; + else + { + pSubMenu = DesktopPtrToUser(item->spSubMenu); + hSubMenu = UserHMGetHandle(pSubMenu); + if (hSubMenu == hSubTarget) + { + return i; + } + else + { + HMENU hsubmenu = hSubMenu; + UINT pos = MenuFindSubMenu( &hsubmenu, hSubTarget ); + if (pos != NO_SELECTED_ITEM) + { + *hmenu = hsubmenu; + return pos; + } + } + } + } + return NO_SELECTED_ITEM; +} + +/*********************************************************************** + * MENU_AdjustMenuItemRect + * + * Adjust menu item rectangle according to scrolling state. + */ +static void +MENU_AdjustMenuItemRect(PROSMENUINFO menu, LPRECT rect) +{ + if (menu->dwArrowsOn) + { + UINT arrow_bitmap_height; + BITMAP bmp; + + GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp); + arrow_bitmap_height = bmp.bmHeight; + rect->top += arrow_bitmap_height - menu->iTop; + rect->bottom += arrow_bitmap_height - menu->iTop; + } +} + +/*********************************************************************** + * MenuDrawPopupGlyph + * + * Draws popup magic glyphs (can be found in system menu). + */ +static void FASTCALL +MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite) +{ + LOGFONTW lf; + HFONT hFont, hOldFont; + COLORREF clrsave; + INT bkmode; + TCHAR symbol; + switch (popupMagic) + { + case (INT_PTR) HBMMENU_POPUP_RESTORE: + symbol = '2'; + break; + case (INT_PTR) HBMMENU_POPUP_MINIMIZE: + symbol = '0'; + break; + case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: + symbol = '1'; + break; + case (INT_PTR) HBMMENU_POPUP_CLOSE: + symbol = 'r'; + break; + default: + ERR("Invalid popup magic bitmap %d\n", (int)popupMagic); + return; + } + ZeroMemory(&lf, sizeof(LOGFONTW)); + InflateRect(r, -2, -2); + lf.lfHeight = r->bottom - r->top; + lf.lfWidth = 0; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = DEFAULT_CHARSET; + lstrcpy(lf.lfFaceName, TEXT("Marlett")); + hFont = CreateFontIndirect(&lf); + /* save font and text color */ + hOldFont = SelectObject(dc, hFont); + clrsave = GetTextColor(dc); + bkmode = GetBkMode(dc); + /* set color and drawing mode */ + SetBkMode(dc, TRANSPARENT); + if (inactive) + { + /* draw shadow */ + if (!hilite) + { + SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + TextOut(dc, r->left + 1, r->top + 1, &symbol, 1); + } + } + SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT))); + /* draw selected symbol */ + TextOut(dc, r->left, r->top, &symbol, 1); + /* restore previous settings */ + SetTextColor(dc, clrsave); + SelectObject(dc, hOldFont); + SetBkMode(dc, bkmode); + DeleteObject(hFont); +} + +/*********************************************************************** + * MenuFindItemByKey + * + * Find the menu item selected by a key press. + * Return item id, -1 if none, -2 if we should close the menu. + */ - static UINT FASTCALL MenuFindItemByKey(HWND WndOwner, PROSMENUINFO MenuInfo, ++static UINT FASTCALL MENU_FindItemByKey(HWND WndOwner, HMENU hmenu, + WCHAR Key, BOOL ForceMenuChar) +{ - ROSMENUINFO SysMenuInfo; - PROSMENUITEMINFO Items, ItemInfo; + LRESULT MenuChar; - UINT i; + WORD Flags = 0; + - TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char) Key, Key, MenuInfo); ++ TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, hmenu ); + - if (NULL == MenuInfo || ! IsMenu(MenuInfo->Self)) - { - if (MenuGetRosMenuInfo(&SysMenuInfo, GetSystemMenu(WndOwner, FALSE))) - { - MenuInfo = &SysMenuInfo; - } - else - { - MenuInfo = NULL; - } - } ++ if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(WndOwner), 0); ++ if (hmenu) ++ { ++ MENU *menu = MENU_GetMenu( hmenu ); ++ ITEM *item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; + - if (NULL != MenuInfo) - { - if (MenuGetAllRosMenuItemInfo(MenuInfo->Self, &Items) <= 0) - { - return -1; - } - if ( !ForceMenuChar ) - { - ItemInfo = Items; - for (i = 0; i < MenuInfo->cItems; i++, ItemInfo++) - { - if ((ItemInfo->lpstr) && NULL != ItemInfo->dwTypeData) - { - WCHAR *p = (WCHAR *) ItemInfo->dwTypeData - 2; - do - { - p = strchrW (p + 2, '&'); - } - while (p != NULL && p [1] == '&'); - if (p && (toupperW(p[1]) == toupperW(Key))) return i; - } - } - } ++ if( !ForceMenuChar ) ++ { ++ UINT i; ++ BOOL cjk = GetSystemMetrics( SM_DBCSENABLED ); ++ ++ for (i = 0; i < menu->cItems; i++, item++) ++ { ++ LPWSTR text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL; ++ if( text) ++ { ++ const WCHAR *p = text - 2; ++ do ++ { ++ const WCHAR *q = p + 2; ++ p = strchrW (q, '&'); ++ if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */ ++ } ++ while (p != NULL && p [1] == '&'); ++ if (p && (toupperW(p[1]) == toupperW(Key))) return i; ++ } ++ } ++ } + - Flags |= MenuInfo->fFlags & MNF_POPUP ? MF_POPUP : 0; - Flags |= MenuInfo->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0; ++ Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0; ++ Flags |= menu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0; + + MenuChar = SendMessageW(WndOwner, WM_MENUCHAR, - MAKEWPARAM(Key, Flags), (LPARAM) MenuInfo->Self); ++ MAKEWPARAM(Key, Flags), (LPARAM) hmenu); + if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar); + if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2); + } + return (UINT)(-1); +} + +/*********************************************************************** + * MenuGetBitmapItemSize + * + * Get the size of a bitmap item. + */ +static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner) +{ + BITMAP bm; + HBITMAP bmp = lpitem->hbmpItem; + + size->cx = size->cy = 0; + + /* check if there is a magic menu item associated with this item */ + if (IS_MAGIC_BITMAP(bmp)) + { + switch((INT_PTR) bmp) + { + case (INT_PTR)HBMMENU_CALLBACK: + { + MEASUREITEMSTRUCT measItem; + measItem.CtlType = ODT_MENU; + measItem.CtlID = 0; + measItem.itemID = lpitem->wID; + measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left; + measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top; + measItem.itemData = lpitem->dwItemData; + SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem); + size->cx = measItem.itemWidth; + size->cy = measItem.itemHeight; + return; + } + break; + + case (INT_PTR) HBMMENU_SYSTEM: + if (0 != lpitem->dwItemData) + { + bmp = (HBITMAP) lpitem->dwItemData; + break; + } + /* fall through */ + case (INT_PTR) HBMMENU_MBAR_RESTORE: + case (INT_PTR) HBMMENU_MBAR_MINIMIZE: + case (INT_PTR) HBMMENU_MBAR_CLOSE: + case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D: + case (INT_PTR) HBMMENU_MBAR_CLOSE_D: + case (INT_PTR) HBMMENU_POPUP_CLOSE: + case (INT_PTR) HBMMENU_POPUP_RESTORE: + case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: + case (INT_PTR) HBMMENU_POPUP_MINIMIZE: + /* FIXME: Why we need to subtract these magic values? */ + /* to make them smaller than the menu bar? */ + size->cx = GetSystemMetrics(SM_CXSIZE) - 2; + size->cy = GetSystemMetrics(SM_CYSIZE) - 4; + return; + } + } + + if (GetObjectW(bmp, sizeof(BITMAP), &bm)) + { + size->cx = bm.bmWidth; + size->cy = bm.bmHeight; + } +} + +/*********************************************************************** + * MenuDrawBitmapItem + * + * Draw a bitmap item. + */ +static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect, + PROSMENUINFO MenuInfo, HWND WndOwner, UINT odaction, BOOL MenuBar) +{ + BITMAP bm; + DWORD rop; + HDC hdcMem; + HBITMAP bmp; + int w = rect->right - rect->left; + int h = rect->bottom - rect->top; + int bmp_xoffset = 0; + int left, top; + HBITMAP hbmToDraw = lpitem->hbmpItem; + bmp = hbmToDraw; + + /* Check if there is a magic menu item associated with this item */ + if (IS_MAGIC_BITMAP(hbmToDraw)) + { + UINT flags = 0; + RECT r; + + r = *rect; + switch ((INT_PTR)hbmToDraw) + { + case (INT_PTR)HBMMENU_SYSTEM: + if (lpitem->dwTypeData) + { + bmp = (HBITMAP)lpitem->dwTypeData; + if (!GetObjectW( bmp, sizeof(bm), &bm )) return; + } + else + { + if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)); + bmp = BmpSysMenu; + if (! GetObjectW(bmp, sizeof(bm), &bm)) return; + /* only use right half of the bitmap */ + bmp_xoffset = bm.bmWidth / 2; + bm.bmWidth -= bmp_xoffset; + } + goto got_bitmap; + case (INT_PTR)HBMMENU_MBAR_RESTORE: + flags = DFCS_CAPTIONRESTORE; + break; + case (INT_PTR)HBMMENU_MBAR_MINIMIZE: + r.right += 1; + flags = DFCS_CAPTIONMIN; + break; + case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D: + r.right += 1; + flags = DFCS_CAPTIONMIN | DFCS_INACTIVE; + break; + case (INT_PTR)HBMMENU_MBAR_CLOSE: + flags = DFCS_CAPTIONCLOSE; + break; + case (INT_PTR)HBMMENU_MBAR_CLOSE_D: + flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE; + break; + case (INT_PTR)HBMMENU_CALLBACK: + { + DRAWITEMSTRUCT drawItem; + POINT origorg; + drawItem.CtlType = ODT_MENU; + drawItem.CtlID = 0; + drawItem.itemID = lpitem->wID; + drawItem.itemAction = odaction; + drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0; + drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0; + drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0; + drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0; + drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0; + //drawItem.itemState |= (!(MenuInfo->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0; + //drawItem.itemState |= (MenuInfo->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0; + drawItem.hwndItem = (HWND)MenuInfo->Self; + drawItem.hDC = hdc; + drawItem.rcItem = *rect; + drawItem.itemData = lpitem->dwItemData; + /* some applications make this assumption on the DC's origin */ + SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg); + OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top); + SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem); + SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); + return; + } + break; + + case (INT_PTR) HBMMENU_POPUP_CLOSE: + case (INT_PTR) HBMMENU_POPUP_RESTORE: + case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: + case (INT_PTR) HBMMENU_POPUP_MINIMIZE: + MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE); + return; + } + InflateRect(&r, -1, -1); + if (0 != (lpitem->fState & MF_HILITE)) + { + flags |= DFCS_PUSHED; + } + DrawFrameControl(hdc, &r, DFC_CAPTION, flags); + return; + } + + if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return; + + got_bitmap: + hdcMem = CreateCompatibleDC( hdc ); + SelectObject( hdcMem, bmp ); + + /* handle fontsize > bitmap_height */ + top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top; + left=rect->left; + rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY; + if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem) + SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); + BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop ); + DeleteDC( hdcMem ); +} + +/*********************************************************************** + * MenuCalcItemSize + * + * Calculate the size of the menu item and store it in lpitem->rect. + */ +static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner, + INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp) +{ + WCHAR *p; + UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK ); + UINT arrow_bitmap_width; + BITMAP bm; + INT itemheight = 0; + + TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY); + + GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm ); + arrow_bitmap_width = bm.bmWidth; + + MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy ); + + SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY ); + + if (lpitem->fType & MF_OWNERDRAW) + { + MEASUREITEMSTRUCT mis; + mis.CtlType = ODT_MENU; + mis.CtlID = 0; + mis.itemID = lpitem->wID; + mis.itemData = lpitem->dwItemData; + mis.itemHeight = HIWORD( GetDialogBaseUnits()); + mis.itemWidth = 0; + SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis ); + /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average + * width of a menufont character to the width of an owner-drawn menu. + */ + lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx; + if (menuBar) { + /* under at least win95 you seem to be given a standard + height for the menu and the height value is ignored */ + lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE); + } else + lpitem->Rect.bottom += mis.itemHeight; + // Or this, + //Item->cxBmp = mis.itemWidth; + //Item->cyBmp = mis.itemHeight; + TRACE("id=%04lx size=%dx%d\n", + lpitem->wID, lpitem->Rect.right-lpitem->Rect.left, + lpitem->Rect.bottom-lpitem->Rect.top); + return; + } + + if (lpitem->fType & MF_SEPARATOR) + { + lpitem->Rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT; + if( !menuBar) + lpitem->Rect.right += arrow_bitmap_width/*check_bitmap_width*/ + MenuCharSize.cx; + return; + } + + lpitem->dxTab = 0; + + if (lpitem->hbmpItem) + { + SIZE size; + + if (!menuBar) { + MenuGetBitmapItemSize(lpitem, &size, hwndOwner ); + /* Keep the size of the bitmap in callback mode to be able + * to draw it correctly */ + lpitem->maxBmpSize = size; + MenuInfo->cxTextAlign = max(MenuInfo->cxTextAlign, size.cx); + MenuSetRosMenuInfo(MenuInfo); + lpitem->Rect.right += size.cx + 2; + itemheight = size.cy + 2; + + if( !(MenuInfo->dwStyle & MNS_NOCHECK)) + lpitem->Rect.right += 2 * check_bitmap_width; + lpitem->Rect.right += 4 + MenuCharSize.cx; + lpitem->dxTab = lpitem->Rect.right; + lpitem->Rect.right += arrow_bitmap_width;//check_bitmap_width; + } else /* hbmpItem & MenuBar */ { + MenuGetBitmapItemSize(lpitem, &size, hwndOwner ); + lpitem->Rect.right += size.cx; + if( lpitem->lpstr) lpitem->Rect.right += 2; + itemheight = size.cy; + + /* Special case: Minimize button doesn't have a space behind it. */ + if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE || + lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D) + lpitem->Rect.right -= 1; + } + } + else if (!menuBar) { + if( !(MenuInfo->dwStyle & MNS_NOCHECK)) + lpitem->Rect.right += check_bitmap_width; + lpitem->Rect.right += 4 + MenuCharSize.cx; + lpitem->dxTab = lpitem->Rect.right; + lpitem->Rect.right += check_bitmap_width; + } + + /* it must be a text item - unless it's the system menu */ + if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) { + HFONT hfontOld = NULL; + RECT rc = lpitem->Rect; + LONG txtheight, txtwidth; + + if ( lpitem->fState & MFS_DEFAULT ) { + hfontOld = SelectObject( hdc, hMenuFontBold ); + } + if (menuBar) { + txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc, + DT_SINGLELINE|DT_CALCRECT); + lpitem->Rect.right += rc.right - rc.left; + itemheight = max( max( itemheight, txtheight), + GetSystemMetrics( SM_CYMENU) - 1); + lpitem->Rect.right += 2 * MenuCharSize.cx; + } else { + if ((p = strchrW( lpitem->dwTypeData, '\t' )) != NULL) { + RECT tmprc = rc; + LONG tmpheight; + int n = (int)( p - lpitem->dwTypeData); + /* Item contains a tab (only meaningful in popup menus) */ + /* get text size before the tab */ + txtheight = DrawTextW( hdc, lpitem->dwTypeData, n, &rc, + DT_SINGLELINE|DT_CALCRECT); + txtwidth = rc.right - rc.left; + p += 1; /* advance past the Tab */ + /* get text size after the tab */ + tmpheight = DrawTextW( hdc, p, -1, &tmprc, + DT_SINGLELINE|DT_CALCRECT); + lpitem->dxTab += txtwidth; + txtheight = max( txtheight, tmpheight); + txtwidth += MenuCharSize.cx + /* space for the tab */ + tmprc.right - tmprc.left; /* space for the short cut */ + } else { + txtheight = DrawTextW( hdc, lpitem->dwTypeData, -1, &rc, + DT_SINGLELINE|DT_CALCRECT); + txtwidth = rc.right - rc.left; + lpitem->dxTab += txtwidth; + } + lpitem->Rect.right += 2 + txtwidth; + itemheight = max( itemheight, + max( txtheight + 2, MenuCharSize.cy + 4)); + } + if (hfontOld) SelectObject (hdc, hfontOld); + } else if( menuBar) { + itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1); + } + lpitem->Rect.bottom += itemheight; + TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom); +} + +/*********************************************************************** + * MENU_GetMaxPopupHeight + */ +static UINT +MENU_GetMaxPopupHeight(PROSMENUINFO lppop) +{ + if (lppop->cyMax) + return lppop->cyMax; + return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER); +} + +/*********************************************************************** + * MenuPopupMenuCalcSize + * + * Calculate the size of a popup menu. + */ +static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner) +{ + ROSMENUITEMINFO lpitem; + HDC hdc; + int start, i; + int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight; + BOOL textandbmp = FALSE; + + MenuInfo->cxMenu = MenuInfo->cyMenu = 0; + if (MenuInfo->cItems == 0) + { + MenuSetRosMenuInfo(MenuInfo); + return; + } + + hdc = GetDC(NULL); + SelectObject( hdc, hMenuFont ); + + start = 0; + maxX = 2 + 1; + + MenuInfo->cxTextAlign = 0; + + MenuInitRosMenuItemInfo(&lpitem); + while (start < MenuInfo->cItems) + { + orgX = maxX; + orgY = 2; + + maxTab = maxTabWidth = 0; + + /* Parse items until column break or end of menu */ + for (i = start; i < MenuInfo->cItems; i++) + { + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem)) + { + MenuCleanupRosMenuItemInfo(&lpitem); + MenuSetRosMenuInfo(MenuInfo); + return; + } + if (i != start && + (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break; + + if( lpitem.lpstr && lpitem.hbmpItem) textandbmp = TRUE; + + MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE, textandbmp); + if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem)) + { + MenuCleanupRosMenuItemInfo(&lpitem); + MenuSetRosMenuInfo(MenuInfo); + return; + } +// Not sure here,, The patch from wine removes this. +// if ((lpitem.fType & MF_MENUBARBREAK) != 0) +// { +// OrgX++; +// } + maxX = max(maxX, lpitem.Rect.right); + orgY = lpitem.Rect.bottom; + if ((lpitem.lpstr) && lpitem.dxTab ) + { + maxTab = max( maxTab, lpitem.dxTab ); + maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab); + } + } + + /* Finish the column (set all items to the largest width found) */ + maxX = max( maxX, maxTab + maxTabWidth ); + while (start < i) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem)) + { + lpitem.Rect.right = maxX; + if ((lpitem.lpstr) && 0 != lpitem.dxTab) + { + lpitem.dxTab = maxTab; + } + MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem); + } + start++; + } + MenuInfo->cyMenu = max(MenuInfo->cyMenu, orgY); + } + + MenuInfo->cxMenu = maxX; + /* if none of the items have both text and bitmap then + * the text and bitmaps are all aligned on the left. If there is at + * least one item with both text and bitmap then bitmaps are + * on the left and texts left aligned with the right hand side + * of the bitmaps */ + if( !textandbmp) MenuInfo->cxTextAlign = 0; + + /* space for 3d border */ + MenuInfo->cyMenu += MENU_BOTTOM_MARGIN; + MenuInfo->cxMenu += 2; + + /* Adjust popup height if it exceeds maximum */ + maxHeight = MENU_GetMaxPopupHeight(MenuInfo); + MenuInfo->iMaxTop = MenuInfo->cyMenu - MENU_TOP_MARGIN; + if (MenuInfo->cyMenu >= maxHeight) + { + MenuInfo->cyMenu = maxHeight; + MenuInfo->dwArrowsOn = 1; + } + else + { + MenuInfo->dwArrowsOn = 0; + } + + MenuCleanupRosMenuItemInfo(&lpitem); + MenuSetRosMenuInfo(MenuInfo); + ReleaseDC( 0, hdc ); +} + +/*********************************************************************** + * MenuMenuBarCalcSize + * + * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap + * height is off by 1 pixel which causes lengthy window relocations when + * active document window is maximized/restored. + * + * Calculate the size of the menu bar. + */ +static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect, + PROSMENUINFO MenuInfo, HWND hwndOwner ) +{ + ROSMENUITEMINFO ItemInfo; + int start, i, orgX, orgY, maxY, helpPos; + + if ((lprect == NULL) || (MenuInfo == NULL)) return; + if (MenuInfo->cItems == 0) return; - TRACE("left=%ld top=%ld right=%ld bottom=%ld\n", lprect->left, lprect->top, lprect->right, lprect->bottom); ++ TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect)); + MenuInfo->cxMenu = lprect->right - lprect->left; + MenuInfo->cyMenu = 0; + maxY = lprect->top + 1; + start = 0; + helpPos = -1; + + MenuInfo->cxTextAlign = 0; + + MenuInitRosMenuItemInfo(&ItemInfo); + while (start < MenuInfo->cItems) + { + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + orgX = lprect->left; + orgY = maxY; + + /* Parse items until line break or end of menu */ + for (i = start; i < MenuInfo->cItems; i++) + { + if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i; + if ((i != start) && + (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break; + + TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY); + MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE, FALSE); + if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + + if (ItemInfo.Rect.right > lprect->right) + { + if (i != start) break; + else ItemInfo.Rect.right = lprect->right; + } + maxY = max( maxY, ItemInfo.Rect.bottom ); + orgX = ItemInfo.Rect.right; + if (i + 1 < MenuInfo->cItems) + { + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } + } + +/* FIXME: Is this really needed? */ /*NO! it is not needed, why make the +HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */ +#if 0 + /* Finish the line (set all items to the largest height found) */ + while (start < i) + { + if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo)) + { + ItemInfo.Rect.bottom = maxY; + MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo); + } + start++; + } +#else + start = i; /* This works! */ +#endif + } + + lprect->bottom = maxY; + MenuInfo->cyMenu = lprect->bottom - lprect->top; + MenuSetRosMenuInfo(MenuInfo); + + if (helpPos != -1) + { + /* Flush right all items between the MF_RIGHTJUSTIFY and */ + /* the last item (if several lines, only move the last line) */ + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->cItems - 1, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + orgY = ItemInfo.Rect.top; + orgX = lprect->right; + for (i = MenuInfo->cItems - 1; helpPos <= i; i--) + { + if (i < helpPos) + { + break; /* done */ + } + if (ItemInfo.Rect.top != orgY) + { + break; /* Other line */ + } + if (orgX <= ItemInfo.Rect.right) + { + break; /* Too far right already */ + } + ItemInfo.Rect.left += orgX - ItemInfo.Rect.right; + ItemInfo.Rect.right = orgX; + orgX = ItemInfo.Rect.left; + MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo); + if (helpPos + 1 <= i && + ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } + } + + MenuCleanupRosMenuItemInfo(&ItemInfo); +} + +/*********************************************************************** + * MENU_DrawScrollArrows + * + * Draw scroll arrows. + */ +static void +MENU_DrawScrollArrows(PROSMENUINFO lppop, HDC hdc) +{ + HDC hdcMem = CreateCompatibleDC(hdc); + HBITMAP hOrigBitmap; + UINT arrow_bitmap_width, arrow_bitmap_height; + BITMAP bmp; + RECT rect; + + GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp); + arrow_bitmap_width = bmp.bmWidth; + arrow_bitmap_height = bmp.bmHeight; + + + if (lppop->iTop) + hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap()); + else + hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap()); + rect.left = 0; + rect.top = 0; + rect.right = lppop->cxMenu; + rect.bottom = arrow_bitmap_height; + FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU)); + BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, 0, + arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY); + rect.top = lppop->cyMenu - arrow_bitmap_height; + rect.bottom = lppop->cyMenu; + FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU)); + if (lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)) + SelectObject(hdcMem, get_down_arrow_bitmap()); + else + SelectObject(hdcMem, get_down_arrow_inactive_bitmap()); + BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, + lppop->cyMenu - arrow_bitmap_height, + arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY); + SelectObject(hdcMem, hOrigBitmap); + DeleteDC(hdcMem); +} + - +/*********************************************************************** + * MenuDrawMenuItem + * + * Draw a single menu item. + */ +static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc, + PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction) +{ + RECT rect; + PWCHAR Text; + BOOL flat_menu = FALSE; + int bkgnd; - PWND Wnd = ValidateHwnd(hWnd); ++ PWND Wnd = ValidateHwndNoErr(hWnd); + + if (!Wnd) + return; + + if (lpitem->fType & MF_SYSMENU) + { + if ( (Wnd->style & WS_MINIMIZE)) + { + UserGetInsideRectNC(Wnd, &rect); + UserDrawSysMenuButton(hWnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT)); + } + return; + } + + SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0); + bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU; + + /* Setup colors */ + + if (lpitem->fState & MF_HILITE) + { + if(menuBar && !flat_menu) { + SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT)); + SetBkColor(hdc, GetSysColor(COLOR_MENU)); + } else { + if (lpitem->fState & MF_GRAYED) + SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); + else + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); + } + } + else + { + if (lpitem->fState & MF_GRAYED) + SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); + else + SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) ); + SetBkColor( hdc, GetSysColor( bkgnd ) ); + } + ++ TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect)); + rect = lpitem->Rect; + MENU_AdjustMenuItemRect(MenuInfo, &rect); + + if (lpitem->fType & MF_OWNERDRAW) + { + /* + ** Experimentation under Windows reveals that an owner-drawn + ** menu is given the rectangle which includes the space it requested + ** in its response to WM_MEASUREITEM _plus_ width for a checkmark + ** and a popup-menu arrow. This is the value of lpitem->rect. + ** Windows will leave all drawing to the application except for + ** the popup-menu arrow. Windows always draws that itself, after + ** the menu owner has finished drawing. + */ + DRAWITEMSTRUCT dis; + + dis.CtlType = ODT_MENU; + dis.CtlID = 0; + dis.itemID = lpitem->wID; + dis.itemData = (DWORD)lpitem->dwItemData; + dis.itemState = 0; + if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED; + if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT; + if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED; + if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED; + if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED; + //if (!(MenuInfo->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL; + //if (MenuInfo->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE; + dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */ + dis.hwndItem = (HWND) MenuInfo->Self; + dis.hDC = hdc; + dis.rcItem = rect; + TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, " + "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd, + dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem, + dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, + dis.rcItem.bottom); + SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis); + /* Draw the popup-menu arrow */ + if (lpitem->hSubMenu) + { + RECT rectTemp; + CopyRect(&rectTemp, &rect); + rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK); + DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW); + } + return; + } + + if (menuBar && (lpitem->fType & MF_SEPARATOR)) return; + + if (lpitem->fState & MF_HILITE) + { + if (flat_menu) + { + InflateRect (&rect, -1, -1); + FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT)); + InflateRect (&rect, 1, 1); + FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + else + { + if(menuBar) + DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT); + else + FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT)); + } + } + else + FillRect( hdc, &rect, GetSysColorBrush(bkgnd) ); + + SetBkMode( hdc, TRANSPARENT ); + + /* vertical separator */ + if (!menuBar && (lpitem->fType & MF_MENUBARBREAK)) + { + HPEN oldPen; + RECT rc = rect; + + rc.left -= 3; + rc.top = 3; + rc.bottom = Height - 3; + if (flat_menu) + { + oldPen = SelectObject( hdc, GetStockObject(DC_PEN) ); + SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW)); + MoveToEx( hdc, rc.left, rc.top, NULL ); + LineTo( hdc, rc.left, rc.bottom ); + SelectObject( hdc, oldPen ); + } + else + DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT); + } + + /* horizontal separator */ + if (lpitem->fType & MF_SEPARATOR) + { + HPEN oldPen; + RECT rc = rect; + + rc.left++; + rc.right--; + rc.top += SEPARATOR_HEIGHT / 2; + if (flat_menu) + { + oldPen = SelectObject( hdc, GetStockObject(DC_PEN) ); + SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW)); + MoveToEx( hdc, rc.left, rc.top, NULL ); + LineTo( hdc, rc.right, rc.top ); + SelectObject( hdc, oldPen ); + } + else + DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP); + return; + } + +#if 0 + /* helper lines for debugging */ + /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */ + FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH)); + SelectObject(hdc, GetStockObject(DC_PEN)); + SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME)); + MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL); + LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2); +#endif + + if (!menuBar) + { + HBITMAP bm; + INT y = rect.top + rect.bottom; + RECT rc = rect; + BOOL checked = FALSE; + UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK ); + UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK ); + /* Draw the check mark + * + * FIXME: + * Custom checkmark bitmaps are monochrome but not always 1bpp. + */ + if( !(MenuInfo->dwStyle & MNS_NOCHECK)) { + bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked : + lpitem->hbmpUnchecked; + if (bm) /* we have a custom bitmap */ + { + HDC hdcMem = CreateCompatibleDC( hdc ); + + SelectObject( hdcMem, bm ); + BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2, + check_bitmap_width, check_bitmap_height, + hdcMem, 0, 0, SRCCOPY ); + DeleteDC( hdcMem ); + checked = TRUE; + } + else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */ + { + RECT r; + CopyRect(&r, &rect); + r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK); + DrawFrameControl( hdc, &r, DFC_MENU, + (lpitem->fType & MFT_RADIOCHECK) ? + DFCS_MENUBULLET : DFCS_MENUCHECK); + checked = TRUE; + } + } + if ( lpitem->hbmpItem ) + { + RECT bmpRect; + CopyRect(&bmpRect, &rect); + if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK)) + bmpRect.left += check_bitmap_width + 2; + if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP))) + { + bmpRect.right = bmpRect.left + lpitem->maxBmpSize.cx; + MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo, WndOwner, odaction, menuBar); + } + } + /* Draw the popup-menu arrow */ + if (lpitem->hSubMenu) + { + RECT rectTemp; + CopyRect(&rectTemp, &rect); + rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK); + DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW); + } + rect.left += 4; + if( !(MenuInfo->dwStyle & MNS_NOCHECK)) + rect.left += check_bitmap_width; + rect.right -= check_bitmap_width; + } + else if( lpitem->hbmpItem) + { /* Draw the bitmap */ + MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo, WndOwner, odaction, menuBar); + } + + /* process text if present */ + if (lpitem->lpstr) + { + register int i = 0; + HFONT hfontOld = 0; + + UINT uFormat = menuBar ? + DT_CENTER | DT_VCENTER | DT_SINGLELINE : + DT_LEFT | DT_VCENTER | DT_SINGLELINE; + + if((MenuInfo->dwStyle & MNS_CHECKORBMP)) - rect.left += max(0, MenuInfo->cxTextAlign - GetSystemMetrics(SM_CXMENUCHECK)); ++ rect.left += max(0, (int)(MenuInfo->cxTextAlign - GetSystemMetrics(SM_CXMENUCHECK))); + else + rect.left += MenuInfo->cxTextAlign; + + if ( lpitem->fState & MFS_DEFAULT ) + { + hfontOld = SelectObject(hdc, hMenuFontBold); + } + + if (menuBar) { + rect.left += MENU_BAR_ITEMS_SPACE / 2; + rect.right -= MENU_BAR_ITEMS_SPACE / 2; + } + + Text = (PWCHAR) lpitem->dwTypeData; + if(Text) + { + for (i = 0; L'\0' != Text[i]; i++) + if (Text[i] == L'\t' || Text[i] == L'\b') + break; + } + + if(lpitem->fState & MF_GRAYED) + { + if (!(lpitem->fState & MF_HILITE) ) + { + ++rect.left; ++rect.top; ++rect.right; ++rect.bottom; + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + DrawTextW( hdc, Text, i, &rect, uFormat ); + --rect.left; --rect.top; --rect.right; --rect.bottom; + } + SetTextColor(hdc, RGB(0x80, 0x80, 0x80)); + } + + DrawTextW( hdc, Text, i, &rect, uFormat); + + /* paint the shortcut text */ + if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */ + { + if (L'\t' == Text[i]) + { + rect.left = lpitem->dxTab; + uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE; + } + else + { + rect.right = lpitem->dxTab; + uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE; + } + + if (lpitem->fState & MF_GRAYED) + { + if (!(lpitem->fState & MF_HILITE) ) + { + ++rect.left; ++rect.top; ++rect.right; ++rect.bottom; + SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat); + --rect.left; --rect.top; --rect.right; --rect.bottom; + } + SetTextColor(hdc, RGB(0x80, 0x80, 0x80)); + } + DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat ); + } + + if (hfontOld) + SelectObject (hdc, hfontOld); + } +} + +/*********************************************************************** + * MenuDrawPopupMenu + * + * Paint a popup menu. + */ +static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu ) +{ + HBRUSH hPrevBrush = 0; + RECT rect; + + TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu); + + GetClientRect( hwnd, &rect ); + + if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) )) + && (SelectObject( hdc, hMenuFont))) + { + HPEN hPrevPen; + + Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom ); + + hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) ); + if ( hPrevPen ) + { + BOOL flat_menu = FALSE; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + + SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0); + if (flat_menu) + FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW)); + else + DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT); + + /* draw menu items */ ++ //TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle); + if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.cItems) + { + UINT u; + MenuInitRosMenuItemInfo(&ItemInfo); + + for (u = 0; u < MenuInfo.cItems; u++) + { + if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo)) + { + HWND WndOwner = MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL; + MenuDrawMenuItem(hwnd, &MenuInfo, WndOwner, hdc, &ItemInfo, + MenuInfo.cyMenu, FALSE, ODA_DRAWENTIRE); + } + } + + /* draw scroll arrows */ + if (MenuInfo.dwArrowsOn) + MENU_DrawScrollArrows(&MenuInfo, hdc); + + MenuSetRosMenuInfo(&MenuInfo); + MenuCleanupRosMenuItemInfo(&ItemInfo); + } + } else + { + SelectObject( hdc, hPrevBrush ); + } + } +} + +/*********************************************************************** + * MenuDrawMenuBar + * + * Paint a menu bar. Returns the height of the menu bar. + * called from [windows/nonclient.c] + */ +UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd, + BOOL suppress_draw) +{ + ROSMENUINFO lppop; + HFONT hfontOld = 0; + HMENU hMenu = GetMenu(hwnd); + + if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL) + { + return GetSystemMetrics(SM_CYMENU); + } + + if (suppress_draw) + { + hfontOld = SelectObject(hDC, hMenuFont); + + MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd); + + lprect->bottom = lprect->top + lppop.cyMenu; + + if (hfontOld) SelectObject( hDC, hfontOld); + return lppop.cyMenu; + } + else + return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL); +} + ++/*********************************************************************** ++ * MENU_InitPopup ++ * ++ * Popup menu initialization before WM_ENTERMENULOOP. ++ */ ++static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags ) ++{ ++ MENU *menu; ++ DWORD ex_style = 0; ++ ROSMENUINFO MenuInfo; ++ ++ TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu); ++ ++ if (!(menu = MENU_GetMenu( hmenu ))) return FALSE; ++ ++ /* store the owner for DrawItem */ ++ if (!IsWindow( hwndOwner )) ++ { ++ SetLastError( ERROR_INVALID_WINDOW_HANDLE ); ++ return FALSE; ++ } ++ MenuGetRosMenuInfo(&MenuInfo, menu->head.h); ++ //menu->hwndOwner = hwndOwner; ++ MenuInfo.spwndNotify = ValidateHwndNoErr( hwndOwner ); ++ ++ if (flags & TPM_LAYOUTRTL) ++ ex_style = WS_EX_LAYOUTRTL; ++ ++ /* NOTE: In Windows, top menu popup is not owned. */ ++ //menu->hWnd = CreateWindowExW( ex_style, WC_MENU, NULL, ++ MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL, ++ WS_POPUP, 0, 0, 0, 0, ++ hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE), ++ (LPVOID)hmenu ); ++ MenuSetRosMenuInfo(&MenuInfo); ++ if( !menu->hWnd ) return FALSE; ++ return TRUE; ++} + +/*********************************************************************** + * MenuShowPopup + * + * Display a popup menu. + */ +static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags, + INT x, INT y, INT xanchor, INT yanchor ) +{ + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + UINT width, height; + POINT pt; + HMONITOR monitor; + MONITORINFO info; - DWORD ex_style = 0; + + TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n", + hwndOwner, hmenu, id, x, y, xanchor, yanchor); + + if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE; + if (MenuInfo.iItem != NO_SELECTED_ITEM) + { + MenuInitRosMenuItemInfo(&ItemInfo); + if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo)) + { + ItemInfo.fMask |= MIIM_STATE; + ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT); + MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo); + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuInfo.iItem = NO_SELECTED_ITEM; + } + - /* store the owner for DrawItem */ - if (!IsWindow(hwndOwner)) - { - SetLastError( ERROR_INVALID_WINDOW_HANDLE ); - return FALSE; - } - MenuInfo.spwndNotify = ValidateHwndNoErr(hwndOwner); ++ //menu->dwArrowsOn = 0; ++ MenuInfo.dwArrowsOn = 0; + MenuSetRosMenuInfo(&MenuInfo); - + MenuPopupMenuCalcSize(&MenuInfo, hwndOwner); + + /* adjust popup menu pos so that it fits within the desktop */ + + width = MenuInfo.cxMenu + GetSystemMetrics(SM_CXBORDER); + height = MenuInfo.cyMenu + GetSystemMetrics(SM_CYBORDER); + + /* FIXME: should use item rect */ + pt.x = x; + pt.y = y; + monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST ); + info.cbSize = sizeof(info); + GetMonitorInfoW( monitor, &info ); + + if (flags & TPM_LAYOUTRTL) - { - ex_style = WS_EX_LAYOUTRTL; + flags ^= TPM_RIGHTALIGN; - } ++ + if( flags & TPM_RIGHTALIGN ) x -= width; + if( flags & TPM_CENTERALIGN ) x -= width / 2; + + if( flags & TPM_BOTTOMALIGN ) y -= height; + if( flags & TPM_VCENTERALIGN ) y -= height / 2; + + if( x + width > info.rcMonitor.right) + { + if( xanchor && x >= width - xanchor ) + x -= width - xanchor; + + if( x + width > info.rcMonitor.right) + x = info.rcMonitor.right - width; + } + if( x < info.rcMonitor.left ) x = info.rcMonitor.left; + + if( y + height > info.rcMonitor.bottom) + { + if( yanchor && y >= height + yanchor ) + y -= height + yanchor; + + if( y + height > info.rcMonitor.bottom) + y = info.rcMonitor.bottom - height; + } + if( y < info.rcMonitor.top ) y = info.rcMonitor.top; + - /* NOTE: In Windows, top menu popup is not owned. */ - MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL, - WS_POPUP, x, y, width, height, - hwndOwner, 0, (HINSTANCE) GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE), - (LPVOID) MenuInfo.Self); - if ( !MenuInfo.Wnd || ! MenuSetRosMenuInfo(&MenuInfo)) return FALSE; + if (!top_popup) { + top_popup = MenuInfo.Wnd; + top_popup_hmenu = hmenu; + } - - IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); - + /* Display the window */ + - SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, 0, 0, 0, 0, - SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); ++ SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, x, y, width, height, ++ SWP_SHOWWINDOW | SWP_NOACTIVATE); + UpdateWindow( MenuInfo.Wnd ); ++ ++ IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); ++ + return TRUE; +} + +/*********************************************************************** + * MENU_EnsureMenuItemVisible + */ +void +MENU_EnsureMenuItemVisible(PROSMENUINFO lppop, PROSMENUITEMINFO item, HDC hdc) +{ + if (lppop->dwArrowsOn) + { + //ITEM *item = &lppop->items[wIndex]; + UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop); + UINT nOldPos = lppop->iTop; + RECT rc; + UINT arrow_bitmap_height; + BITMAP bmp; + + GetClientRect(lppop->Wnd, &rc); + + GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp); + arrow_bitmap_height = bmp.bmHeight; + + rc.top += arrow_bitmap_height; + rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN; + + nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height; + if (item->Rect.bottom > lppop->iTop + nMaxHeight) + { + lppop->iTop = item->Rect.bottom - nMaxHeight; + ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc); + MENU_DrawScrollArrows(lppop, hdc); + } + else if (item->Rect.top - MENU_TOP_MARGIN < lppop->iTop) + { + lppop->iTop = item->Rect.top - MENU_TOP_MARGIN; + ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc); + MENU_DrawScrollArrows(lppop, hdc); + } + } +} + +/*********************************************************************** + * MenuSelectItem + */ +static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex, + BOOL sendMenuSelect, HMENU topmenu) +{ + ROSMENUITEMINFO ItemInfo; + ROSMENUINFO TopMenuInfo; + HDC hdc; + + TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect); + + if (!hmenu || !hmenu->cItems || !hmenu->Wnd) return; + if (hmenu->iItem == wIndex) return; + if (hmenu->fFlags & MNF_POPUP) hdc = GetDC(hmenu->Wnd); + else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW); + if (!top_popup) { + top_popup = hmenu->Wnd; + top_popup_hmenu = hmenu->Self; + } + + SelectObject( hdc, hMenuFont ); + + MenuInitRosMenuItemInfo(&ItemInfo); + + /* Clear previous highlighted item */ + if (hmenu->iItem != NO_SELECTED_ITEM) + { + if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo)) + { + ItemInfo.fMask |= MIIM_STATE; + ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT); + MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo); + } + //MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc); + MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo, + hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP), + ODA_SELECT); + } + + /* Highlight new item (if any) */ + hmenu->iItem = wIndex; + MenuSetRosMenuInfo(hmenu); + if (hmenu->iItem != NO_SELECTED_ITEM) + { + if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo)) + { + if (!(ItemInfo.fType & MF_SEPARATOR)) + { + ItemInfo.fMask |= MIIM_STATE; + ItemInfo.fState |= MF_HILITE; + MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo); + MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, + &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP), + ODA_SELECT); + } + if (sendMenuSelect) + { + WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID, + ItemInfo.fType | ItemInfo.fState | + (ItemInfo.hSubMenu ? MF_POPUP : 0) | + (hmenu->fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) ); + + SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self); + } + } + } + else if (sendMenuSelect) + { + if(topmenu) + { + int pos; + pos = MenuFindSubMenu(&topmenu, hmenu->Self); + if (pos != NO_SELECTED_ITEM) + { + if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu) + && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo)) + { + WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState | + (ItemInfo.hSubMenu ? MF_POPUP : 0) | + (TopMenuInfo.fFlags & MNF_SYSDESKMN ? MF_SYSMENU : 0 ) ); + + SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu); + } + } + } + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + ReleaseDC(hmenu->Wnd, hdc); +} + +/*********************************************************************** + * MenuMoveSelection + * + * Moves currently selected item according to the Offset parameter. + * If there is no selection then it should select the last item if + * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT. + */ +static void FASTCALL +MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset) +{ + INT i; + ROSMENUITEMINFO ItemInfo; + INT OrigPos; + + TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset); + + /* Prevent looping */ + if (0 == MenuInfo->cItems || 0 == Offset) + return; + else if (Offset < -1) + Offset = -1; + else if (Offset > 1) + Offset = 1; + + MenuInitRosMenuItemInfo(&ItemInfo); + + OrigPos = MenuInfo->iItem; + if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */ + { + OrigPos = 0; + i = -1; + } + else + { + i = MenuInfo->iItem; + } + + do + { + /* Step */ + i += Offset; + /* Clip and wrap around */ + if (i < 0) + { + i = MenuInfo->cItems - 1; + } + else if (i >= MenuInfo->cItems) + { + i = 0; + } + /* If this is a good candidate; */ + if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) && + 0 == (ItemInfo.fType & MF_SEPARATOR)) + { + MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL); + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + } while (i != OrigPos); + + /* Not found */ + MenuCleanupRosMenuItemInfo(&ItemInfo); +} + +#if 0 +LRESULT WINAPI +PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) +{ +#ifdef __REACTOS__ // Do this now, remove after Server side is fixed. + PWND pWnd; + PPOPUPMENU pPopupMenu; + - pWnd = ValidateHwnd(Wnd); ++ pWnd = ValidateHwndNoErr(Wnd); + if (pWnd) + { + if (!pWnd->fnid) + { + if (Message != WM_NCCREATE) + { + return DefWindowProcW(Wnd, Message, wParam, lParam); + } + NtUserSetWindowFNID(Wnd, FNID_MENU); + pPopupMenu = HeapAlloc( GetProcessHeap(), 0, sizeof(POPUPMENU) ); + pPopupMenu->spwndPopupMenu = pWnd; + SetWindowLongPtrW(Wnd, 0, (LONG_PTR)pPopupMenu); + } + else + { + if (pWnd->fnid != FNID_MENU) + { + ERR("Wrong window class for Menu!\n"); + return 0; + } + pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu; + } + } +#endif + + TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); + + switch(Message) + { + case WM_CREATE: + { + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + pPopupMenu->spmenu = ValidateHandle(cs->lpCreateParams, TYPE_MENU); + return 0; + } + + case WM_MOUSEACTIVATE: /* We don't want to be activated */ + return MA_NOACTIVATE; + + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(Wnd, &ps); + MenuDrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu->head.h); + EndPaint(Wnd, &ps); + return 0; + } + + case WM_PRINTCLIENT: + { + MenuDrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu->head.h); + return 0; + } + + case WM_ERASEBKGND: + return 1; + + case WM_DESTROY: + /* zero out global pointer in case resident popup window was destroyed. */ + if (Wnd == top_popup) + { + top_popup = NULL; + top_popup_hmenu = NULL; + } + break; + + case WM_NCDESTROY: + { + HeapFree( GetProcessHeap(), 0, pPopupMenu ); + SetWindowLongPtrW(Wnd, 0, 0); + NtUserSetWindowFNID(Wnd, FNID_DESTROY); + break; + } + + case WM_SHOWWINDOW: + if (0 != wParam) + { + if (!pPopupMenu || !pPopupMenu->spmenu) + { + OutputDebugStringA("no menu to display\n"); + } + } + /*else + { + pPopupMenu->spmenu = NULL; ///// WTF? + }*/ + break; + + case MM_SETMENUHANDLE: + { + PMENU pmenu = ValidateHandle((HMENU)wParam, TYPE_MENU); + if (!pmenu) + { + ERR("Bad Menu Handle\n"); + break; + } + pPopupMenu->spmenu = pmenu; + break; + } + + case MM_GETMENUHANDLE: + case MN_GETHMENU: + return (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? pPopupMenu->spmenu->head.h : NULL) : NULL); + + default: + return DefWindowProcW(Wnd, Message, wParam, lParam); + } + + return 0; +} +#endif + +LRESULT WINAPI +PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) +{ +#ifdef __REACTOS__ // Do this now, remove after Server side is fixed. + PWND pWnd; + + pWnd = ValidateHwnd(Wnd); + if (pWnd) + { + if (!pWnd->fnid) + { + if (Message != WM_NCCREATE) + { + return DefWindowProcW(Wnd, Message, wParam, lParam); + } + NtUserSetWindowFNID(Wnd, FNID_MENU); + } + else + { + if (pWnd->fnid != FNID_MENU) + { + ERR("Wrong window class for Menu!\n"); + return 0; + } + } + } +#endif + + TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); + + switch(Message) + { + case WM_CREATE: + { + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams); + return 0; + } + + case WM_MOUSEACTIVATE: /* We don't want to be activated */ + return MA_NOACTIVATE; + + case WM_PAINT: + { + PAINTSTRUCT ps; + BeginPaint(Wnd, &ps); + MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0)); + EndPaint(Wnd, &ps); + return 0; + } + + case WM_PRINTCLIENT: + { + MenuDrawPopupMenu( Wnd, (HDC)wParam, + (HMENU)GetWindowLongPtrW( Wnd, 0 ) ); + return 0; + } + + case WM_ERASEBKGND: + return 1; + + case WM_DESTROY: + /* zero out global pointer in case resident popup window was destroyed. */ + if (Wnd == top_popup) + { + top_popup = NULL; + top_popup_hmenu = NULL; + } + break; + ++#ifdef __REACTOS__ ++ case WM_NCDESTROY: ++ NtUserSetWindowFNID(Wnd, FNID_DESTROY); ++ break; ++#endif ++ + case WM_SHOWWINDOW: + if (0 != wParam) + { + if (0 == GetWindowLongPtrW(Wnd, 0)) + { + OutputDebugStringA("no menu to display\n"); + } + } + else + { + SetWindowLongPtrW(Wnd, 0, 0); + } + break; + + case MM_SETMENUHANDLE: + SetWindowLongPtrW(Wnd, 0, wParam); + break; + + case MM_GETMENUHANDLE: + case MN_GETHMENU: + return GetWindowLongPtrW(Wnd, 0); + + default: + return DefWindowProcW(Wnd, Message, wParam, lParam); + } + + return 0; +} + +// +// This breaks some test results. Should handle A2U if called! +// +LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) +{ + PWND pWnd; + + pWnd = ValidateHwnd(Wnd); + if (pWnd && !pWnd->fnid && Message != WM_NCCREATE) + { + return DefWindowProcA(Wnd, Message, wParam, lParam); + } + TRACE("YES! hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); + + switch(Message) + { + case WM_NCCREATE: + case WM_CREATE: + case WM_MOUSEACTIVATE: + case WM_PAINT: + case WM_PRINTCLIENT: + case WM_ERASEBKGND: + case WM_DESTROY: + case WM_NCDESTROY: + case WM_SHOWWINDOW: + case MM_SETMENUHANDLE: + case MM_GETMENUHANDLE: + case MN_GETHMENU: + return PopupMenuWndProcW(Wnd, Message, wParam, lParam); + + default: + return DefWindowProcA(Wnd, Message, wParam, lParam); + } + return 0; +} + +/********************************************************************** + * MENU_ParseResource + * + * Parse a standard menu resource and add items to the menu. + * Return a pointer to the end of the resource. + * + * NOTE: flags is equivalent to the mtOption field + */ +static LPCSTR MENU_ParseResource( LPCSTR res, HMENU hMenu) +{ + WORD flags, id = 0; + HMENU hSubMenu; + LPCWSTR str; + BOOL end = FALSE; + + do + { + flags = GET_WORD(res); + + /* remove MF_END flag before passing it to AppendMenu()! */ + end = (flags & MF_END); + if(end) flags ^= MF_END; + + res += sizeof(WORD); + if(!(flags & MF_POPUP)) + { + id = GET_WORD(res); + res += sizeof(WORD); + } + str = (LPCWSTR)res; + res += (strlenW(str) + 1) * sizeof(WCHAR); + + if (flags & MF_POPUP) + { + hSubMenu = CreatePopupMenu(); + if(!hSubMenu) return NULL; + if(!(res = MENU_ParseResource(res, hSubMenu))) return NULL; + AppendMenuW(hMenu, flags, (UINT_PTR)hSubMenu, (LPCWSTR)str); + } + else /* Not a popup */ + { + AppendMenuW(hMenu, flags, id, *(LPCWSTR)str ? (LPCWSTR)str : NULL); + } + } while(!end); + return res; +} + - +/********************************************************************** + * MENUEX_ParseResource + * + * Parse an extended menu resource and add items to the menu. + * Return a pointer to the end of the resource. + */ +static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu) +{ + WORD resinfo; + do + { + MENUITEMINFOW mii; + + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_STATE | MIIM_ID | MIIM_TYPE; + mii.fType = GET_DWORD(res); + res += sizeof(DWORD); + mii.fState = GET_DWORD(res); + res += sizeof(DWORD); + mii.wID = GET_DWORD(res); + res += sizeof(DWORD); + resinfo = GET_WORD(res); + res += sizeof(WORD); + /* Align the text on a word boundary. */ + res += (~((UINT_PTR)res - 1)) & 1; + mii.dwTypeData = (LPWSTR)res; + mii.cch = strlenW(mii.dwTypeData); + res += (1 + strlenW(mii.dwTypeData)) * sizeof(WCHAR); + /* Align the following fields on a dword boundary. */ + res += (~((UINT_PTR)res - 1)) & 3; + + TRACE("Menu item: [%08x,%08x,%04x,%04x,%S]\n", + mii.fType, mii.fState, mii.wID, resinfo, mii.dwTypeData); + + if (resinfo & 1) /* Pop-up? */ + { + /* DWORD helpid = GET_DWORD(res); FIXME: use this. */ + res += sizeof(DWORD); + mii.hSubMenu = CreatePopupMenu(); + if (!mii.hSubMenu) + { + ERR("CreatePopupMenu failed\n"); + return NULL; + } + + if (!(res = MENUEX_ParseResource(res, mii.hSubMenu))) + { + ERR("MENUEX_ParseResource failed\n"); + DestroyMenu(mii.hSubMenu); + return NULL; + } + mii.fMask |= MIIM_SUBMENU; + } + else if (!mii.dwTypeData[0] && !(mii.fType & MF_SEPARATOR)) + { + mii.fType |= MF_SEPARATOR; + } + InsertMenuItemW(hMenu, -1, MF_BYPOSITION, &mii); + } while (!(resinfo & MF_END)); + return res; +} + - NTSTATUS WINAPI - User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength) - { - HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU"); - LRESULT Result = (LRESULT)hmenu; - MENUINFO menuinfo = {0}; - MENUITEMINFOW info = {0}; - - // removing space for checkboxes from menu - menuinfo.cbSize = sizeof(menuinfo); - menuinfo.fMask = MIM_STYLE; - GetMenuInfo(hmenu, &menuinfo); - menuinfo.dwStyle |= MNS_CHECKORBMP; // test_menu_bmp_and_string MNS_CHECKORBMP - SetMenuInfo(hmenu, &menuinfo); - - // adding bitmaps to menu items - info.cbSize = sizeof(info); - info.fMask |= MIIM_BITMAP; - info.hbmpItem = HBMMENU_POPUP_MINIMIZE; - SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info); - info.hbmpItem = HBMMENU_POPUP_RESTORE; - SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info); - info.hbmpItem = HBMMENU_POPUP_MAXIMIZE; - SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info); - info.hbmpItem = HBMMENU_POPUP_CLOSE; - SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info); - - return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS)); - } - - - BOOL - MenuInit(VOID) - { - NONCLIENTMETRICSW ncm; - - /* get the menu font */ - if(!hMenuFont || !hMenuFontBold) - { - ncm.cbSize = sizeof(ncm); - if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) - { - ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n"); - return FALSE; - } - - hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont); - if(hMenuFont == NULL) - { - ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n"); - return FALSE; - } - - ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000); - hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont); - if(hMenuFontBold == NULL) - { - ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n"); - DeleteObject(hMenuFont); - hMenuFont = NULL; - return FALSE; - } - } - - return TRUE; - } - - VOID - MenuCleanup(VOID) - { - if (hMenuFont) - { - DeleteObject(hMenuFont); - hMenuFont = NULL; - } - - if (hMenuFontBold) - { - DeleteObject(hMenuFontBold); - hMenuFontBold = NULL; - } - } + +/*********************************************************************** + * DrawMenuBarTemp (USER32.@) + * + * UNDOCUMENTED !! + * + * called by W98SE desk.cpl Control Panel Applet + * + * Not 100% sure about the param names, but close. + * + * @implemented + */ +DWORD WINAPI +DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font) +{ + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + UINT i; + HFONT FontOld = NULL; + BOOL flat_menu = FALSE; + + SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0); + + if (NULL == Menu) + { + Menu = GetMenu(Wnd); + } + + if (NULL == Font) + { + Font = hMenuFont; + } + + if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu)) + { + return GetSystemMetrics(SM_CYMENU); + } + + TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font); + + FontOld = SelectObject(DC, Font); + + if (0 == MenuInfo.cyMenu) + { + MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd); + } + + Rect->bottom = Rect->top + MenuInfo.cyMenu; + + FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU)); + + SelectObject(DC, GetStockObject(DC_PEN)); + SetDCPenColor(DC, GetSysColor(COLOR_3DFACE)); + MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL); + LineTo(DC, Rect->right, Rect->bottom - 1); + + if (0 == MenuInfo.cItems) + { + SelectObject(DC, FontOld); + return GetSystemMetrics(SM_CYMENU); + } + + MenuInitRosMenuItemInfo(&ItemInfo); + for (i = 0; i < MenuInfo.cItems; i++) + { + if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo)) + { + MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo, + MenuInfo.cyMenu, TRUE, ODA_DRAWENTIRE); + } + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + + SelectObject(DC, FontOld); + + return MenuInfo.cyMenu; +} - #if 0 - static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags ) - { - POPUPMENU *menu; - DWORD ex_style = 0; - - TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu); + - if (!(menu = MENU_GetMenu( hmenu ))) return FALSE; - - /* store the owner for DrawItem */ - if (!IsWindow( hwndOwner )) - { - SetLastError( ERROR_INVALID_WINDOW_HANDLE ); - return FALSE; - } - menu->hwndOwner = hwndOwner; - - if (flags & TPM_LAYOUTRTL) - ex_style = WS_EX_LAYOUTRTL; + - /* NOTE: In Windows, top menu popup is not owned. */ - menu->hWnd = CreateWindowExW( ex_style, (LPCWSTR)POPUPMENU_CLASS_ATOM, NULL, - WS_POPUP, 0, 0, 0, 0, - hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE), - (LPVOID)hmenu ); - if( !menu->hWnd ) return FALSE; - return TRUE; - } - #endif +/*********************************************************************** + * MenuShowSubPopup + * + * Display the sub-menu of the selected item of this menu. + * Return the handle of the submenu, or menu if no submenu to display. + */ +static HMENU FASTCALL +MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags) +{ - extern void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect); + RECT Rect; + ROSMENUITEMINFO ItemInfo; + ROSMENUINFO SubMenuInfo; + HDC Dc; + HMENU Ret; + + TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst); + + if (NO_SELECTED_ITEM == MenuInfo->iItem) + { + return MenuInfo->Self; + } + + MenuInitRosMenuItemInfo(&ItemInfo); + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuInfo->Self; + } + if (0 == (ItemInfo.hSubMenu) || 0 != (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuInfo->Self; + } + + /* message must be sent before using item, + because nearly everything may be changed by the application ! */ + + /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ + if (0 == (Flags & TPM_NONOTIFY)) + { + SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu, + MAKELPARAM(MenuInfo->iItem, IS_SYSTEM_MENU(MenuInfo))); + } + + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return MenuInfo->Self; + } + Rect = ItemInfo.Rect; + + /* correct item if modified as a reaction to WM_INITMENUPOPUP message */ + if (0 == (ItemInfo.fState & MF_HILITE)) + { + if (0 != (MenuInfo->fFlags & MNF_POPUP)) + { + Dc = GetDC(MenuInfo->Wnd); + } + else + { + Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW); + } + + SelectObject(Dc, hMenuFont); + ItemInfo.fMask |= MIIM_STATE; + ItemInfo.fState |= MF_HILITE; + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo); + MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->cyMenu, + !(MenuInfo->fFlags & MNF_POPUP), ODA_DRAWENTIRE); + ReleaseDC(MenuInfo->Wnd, Dc); + } + + if (0 == ItemInfo.Rect.top && 0 == ItemInfo.Rect.left + && 0 == ItemInfo.Rect.bottom && 0 == ItemInfo.Rect.right) + { + ItemInfo.Rect = Rect; + } + + ItemInfo.fMask |= MIIM_STATE; + ItemInfo.fState |= MF_MOUSESELECT; + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo); + + if (IS_SYSTEM_MENU(MenuInfo)) + { + ERR("Right click on window bar and Draw system menu!\n"); + MenuInitSysMenuPopup(ItemInfo.hSubMenu, GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE), + GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU); + if (Flags & TPM_LAYOUTRTL) Rect.left; + NcGetSysPopupPos(MenuInfo->Wnd, &Rect); + Rect.top = Rect.bottom; + Rect.right = GetSystemMetrics(SM_CXSIZE); + Rect.bottom = GetSystemMetrics(SM_CYSIZE); + } + else + { + GetWindowRect(MenuInfo->Wnd, &Rect); + if (0 != (MenuInfo->fFlags & MNF_POPUP)) + { + RECT rc = ItemInfo.Rect; + + MENU_AdjustMenuItemRect(MenuInfo, &rc); + + if(Flags & TPM_LAYOUTRTL) + Rect.left += GetSystemMetrics(SM_CXBORDER); + else + Rect.left += ItemInfo.Rect.right- GetSystemMetrics(SM_CXBORDER); + Rect.top += rc.top - MENU_TOP_MARGIN;//3; + Rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER); + Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/ + - GetSystemMetrics(SM_CYBORDER); + } + else + { + if(Flags & TPM_LAYOUTRTL) + Rect.left += Rect.right - ItemInfo.Rect.left; + else + Rect.left += ItemInfo.Rect.left; + Rect.top += ItemInfo.Rect.bottom; + Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left; + Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top; + } + } + + /* use default alignment for submenus */ + Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN); + - //MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags ); ++ MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags ); + + MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->iItem, Flags, + Rect.left, Rect.top, Rect.right, Rect.bottom ); + if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu)) + { + MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT); + } + + Ret = ItemInfo.hSubMenu; + MenuCleanupRosMenuItemInfo(&ItemInfo); + + return Ret; +} + +/********************************************************************** + * MENU_EndMenu + * + * Calls EndMenu() if the hwnd parameter belongs to the menu owner + * + * Does the (menu stuff) of the default window handling of WM_CANCELMODE + */ +void MENU_EndMenu( HWND hwnd ) +{ - ROSMENUINFO MenuInfo; - BOOL Ret = FALSE; - if (top_popup_hmenu) - Ret = MenuGetRosMenuInfo(&MenuInfo, top_popup_hmenu); - if (Ret && hwnd == (MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL)) EndMenu(); ++ MENU *menu; ++ menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL; ++ if (menu && ( hwnd == menu->hWnd || hwnd == (menu->spwndNotify ? menu->spwndNotify->head.h : NULL)) ) ++ EndMenu(); +} + +/*********************************************************************** + * MenuHideSubPopups + * + * Hide the sub-popup menus of this menu. + */ +static void FASTCALL +MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo, + BOOL SendMenuSelect, UINT wFlags) +{ + ROSMENUINFO SubMenuInfo; + ROSMENUITEMINFO ItemInfo; + + TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect); + + if (NULL != MenuInfo && NULL != top_popup && NO_SELECTED_ITEM != MenuInfo->iItem) + { + MenuInitRosMenuItemInfo(&ItemInfo); + ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE; + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo) + || 0 == (ItemInfo.hSubMenu) + || 0 == (ItemInfo.fState & MF_MOUSESELECT)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return; + } + ItemInfo.fState &= ~MF_MOUSESELECT; + ItemInfo.fMask |= MIIM_STATE; + MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo); + if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu)) + { + MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags); + MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL); - DestroyWindow(SubMenuInfo.Wnd); - SubMenuInfo.Wnd = NULL; - MenuSetRosMenuInfo(&SubMenuInfo); + ++ DestroyWindow(SubMenuInfo.Wnd); ++ /* Native returns handle to destroyed window */ + if (!(wFlags & TPM_NONOTIFY)) + SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu, + MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) ); ++ //// ++ // Call WM_UNINITMENUPOPUP FIRST before destroy!! ++ // Fixes todo_wine User32 test menu.c line 2233 GetMenuBarInfo callback.... ++ // ++ SubMenuInfo.Wnd = NULL; ++ MenuSetRosMenuInfo(&SubMenuInfo); ++ //// + } + } +} + +/*********************************************************************** + * MenuSwitchTracking + * + * Helper function for menu navigation routines. + */ +static void FASTCALL +MenuSwitchTracking(MTRACKER* Mt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags) +{ + ROSMENUINFO TopMenuInfo; + + TRACE("%x menu=%x 0x%04x\n", Mt, PtMenuInfo->Self, Index); + + if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu) && + Mt->TopMenu != PtMenuInfo->Self && + 0 == ((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP)) + { + /* both are top level menus (system and menu-bar) */ + MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags); + MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL); + Mt->TopMenu = PtMenuInfo->Self; + } + else + { + MenuHideSubPopups(Mt->OwnerWnd, PtMenuInfo, FALSE, wFlags); + } + + MenuSelectItem(Mt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL); +} + +/*********************************************************************** + * MenuExecFocusedItem + * + * Execute a menu item (for instance when user pressed Enter). + * Return the wID of the executed item. Otherwise, -1 indicating + * that no menu item was executed, -2 if a popup is shown; + * Have to receive the flags for the TrackPopupMenu options to avoid + * sending unwanted message. + * + */ +static INT FASTCALL +MenuExecFocusedItem(MTRACKER *Mt, PROSMENUINFO MenuInfo, UINT Flags) +{ + ROSMENUITEMINFO ItemInfo; + UINT wID; + + TRACE("%p menu=%p\n", Mt, MenuInfo); + + if (0 == MenuInfo->cItems || NO_SELECTED_ITEM == MenuInfo->iItem) + { + return -1; + } + + MenuInitRosMenuItemInfo(&ItemInfo); + if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return -1; + } + + TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu); + + if (0 == (ItemInfo.hSubMenu)) + { + if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)) + && 0 == (ItemInfo.fType & MF_SEPARATOR)) + { + /* If TPM_RETURNCMD is set you return the id, but + do not send a message to the owner */ + if (0 == (Flags & TPM_RETURNCMD)) + { + if (0 != (MenuInfo->fFlags & MNF_SYSDESKMN)) + { + PostMessageW(Mt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID, + MAKELPARAM((SHORT) Mt->Pt.x, (SHORT) Mt->Pt.y)); + } + else + { + ROSMENUINFO topmenuI; + BOOL ret = MenuGetRosMenuInfo(&topmenuI, Mt->TopMenu); + DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0); + + if (dwStyle & MNS_NOTIFYBYPOS) + PostMessageW(Mt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->iItem, (LPARAM)MenuInfo->Self); + else + PostMessageW(Mt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0); + } + } + wID = ItemInfo.wID; + MenuCleanupRosMenuItemInfo(&ItemInfo); + return wID; + } + } + else + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, MenuInfo, TRUE, Flags); + return -2; + } + + return -1; +} + +/*********************************************************************** + * MenuButtonDown + * + * Return TRUE if we can go on with menu tracking. + */ +static BOOL FASTCALL - MenuButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags) ++MENU_ButtonDown(MTRACKER* Mt, HMENU PtMenu, UINT Flags) +{ + int Index; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO Item; + + TRACE("%x PtMenu=%p\n", Mt, PtMenu); + + if (NULL != PtMenu) + { + if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) + { + return FALSE; + } + if (IS_SYSTEM_MENU(&MenuInfo)) + { + Index = 0; + } + else + { + Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y); + } + MenuInitRosMenuItemInfo(&Item); + if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(PtMenu, Index, &Item)) + { + MenuCleanupRosMenuItemInfo(&Item); + return FALSE; + } + + if (!(Item.fType & MF_SEPARATOR) && + !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) ) + { + if (MenuInfo.iItem != Index) + { + MenuSwitchTracking(Mt, &MenuInfo, Index, Flags); + } + + /* If the popup menu is not already "popped" */ + if (0 == (Item.fState & MF_MOUSESELECT)) + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags); + } + } + + MenuCleanupRosMenuItemInfo(&Item); + + return TRUE; + } + + /* else the click was on the menu bar, finish the tracking */ + + return FALSE; +} + +/*********************************************************************** + * MenuButtonUp + * + * Return the value of MenuExecFocusedItem if + * the selected item was not a popup. Else open the popup. + * A -1 return value indicates that we go on with menu tracking. + * + */ +static INT FASTCALL - MenuButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags) ++MENU_ButtonUp(MTRACKER *Mt, HMENU PtMenu, UINT Flags) +{ + INT Id; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + + TRACE("%p hmenu=%x\n", Mt, PtMenu); + + if (NULL != PtMenu) + { + Id = 0; + if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) + { + return -1; + } + + if (! IS_SYSTEM_MENU(&MenuInfo)) + { + Id = NtUserMenuItemFromPoint(Mt->OwnerWnd, MenuInfo.Self, Mt->Pt.x, Mt->Pt.y); + } + MenuInitRosMenuItemInfo(&ItemInfo); + if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) && + MenuInfo.iItem == Id) + { + if (0 == (ItemInfo.hSubMenu)) + { + INT ExecutedMenuId = MenuExecFocusedItem(Mt, &MenuInfo, Flags); + MenuCleanupRosMenuItemInfo(&ItemInfo); + return (ExecutedMenuId < 0) ? -1 : ExecutedMenuId; + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + + /* If we are dealing with the top-level menu */ + /* and this is a click on an already "popped" item: */ + /* Stop the menu tracking and close the opened submenus */ + if (Mt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide) + { + MenuCleanupRosMenuItemInfo(&ItemInfo); + return 0; + } + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + MenuInfo.TimeToHide = TRUE; + MenuSetRosMenuInfo(&MenuInfo); + } + + return -1; +} + +/*********************************************************************** + * MenuPtMenu + * + * Walks menu chain trying to find a menu pt maps to. + */ +static HMENU FASTCALL - MenuPtMenu(HMENU Menu, POINT Pt) ++MENU_PtMenu(HMENU hMenu, POINT pt) +{ - extern LRESULT DefWndNCHitTest(HWND hWnd, POINT Point); - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - HMENU Ret = NULL; - INT Ht; ++ MENU *menu; ++ PITEM pItem; ++ HMENU ret = NULL; + - if (! MenuGetRosMenuInfo(&MenuInfo, Menu)) - { - return NULL; - } ++ menu = MENU_GetMenu( hMenu ); ++ if (!menu) return NULL; + + /* try subpopup first (if any) */ - if (NO_SELECTED_ITEM != MenuInfo.iItem) - { - MenuInitRosMenuItemInfo(&ItemInfo); - if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo) && - 0 != (ItemInfo.hSubMenu) && - 0 != (ItemInfo.fState & MF_MOUSESELECT)) - { - Ret = MenuPtMenu(ItemInfo.hSubMenu, Pt); - if (NULL != Ret) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return Ret; - } - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - } ++ if (menu->iItem != NO_SELECTED_ITEM) ++ { ++ pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; ++ if ( pItem ) pItem = &pItem[menu->iItem]; ++ if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT) ++ { ++ PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); ++ ret = MENU_PtMenu( UserHMGetHandle(pSubMenu), pt); ++ } ++ } + + /* check the current window (avoiding WM_HITTEST) */ - Ht = DefWndNCHitTest(MenuInfo.Wnd, Pt); - if (0 != (MenuInfo.fFlags & MNF_POPUP)) - { - if (HTNOWHERE != Ht && HTERROR != Ht) - { - Ret = Menu; - } - } - else if (HTSYSMENU == Ht) - { - Ret = NtUserGetSystemMenu(MenuInfo.Wnd, FALSE); - } - else if (HTMENU == Ht) - { - Ret = GetMenu(MenuInfo.Wnd); - } - - return Ret; ++ if (!ret) ++ { ++ INT ht = DefWndNCHitTest(menu->hWnd, pt); ++ if ( menu->fFlags & MNF_POPUP ) ++ { ++ if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu; ++ } ++ else if (ht == HTSYSMENU) ++ ret = NtUserGetSystemMenu(menu->hWnd, FALSE); ++ else if (ht == HTMENU) ++ ret = GetMenu( menu->hWnd ); ++ } ++ return ret; +} + +/*********************************************************************** + * MenuMouseMove + * + * Return TRUE if we can go on with menu tracking. + */ +static BOOL FASTCALL +MenuMouseMove(MTRACKER *Mt, HMENU PtMenu, UINT Flags) +{ + INT Index; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; + + if (NULL != PtMenu) + { + if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) + { + return TRUE; + } + if (IS_SYSTEM_MENU(&MenuInfo)) + { + Index = 0; + } + else + { + Index = NtUserMenuItemFromPoint(Mt->OwnerWnd, PtMenu, Mt->Pt.x, Mt->Pt.y); + } + } + else + { + Index = NO_SELECTED_ITEM; + } + + if (NO_SELECTED_ITEM == Index) + { + if (Mt->CurrentMenu == MenuInfo.Self || + MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)) + { + MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, + TRUE, Mt->TopMenu); + } + } + else if (MenuInfo.iItem != Index) + { + MenuInitRosMenuItemInfo(&ItemInfo); + if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) && + !(ItemInfo.fType & MF_SEPARATOR)) + { + MenuSwitchTracking(Mt, &MenuInfo, Index, Flags); + if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED))) + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, FALSE, Flags); + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + } + + return TRUE; +} + +/*********************************************************************** + * MenuGetSubPopup + * + * Return the handle of the selected sub-popup menu (if any). + */ - static HMENU FASTCALL - MenuGetSubPopup(HMENU Menu) ++static ++HMENU MENU_GetSubPopup( HMENU hmenu ) +{ - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; ++ MENU *menu; ++ ITEM *item; + - if (! MenuGetRosMenuInfo(&MenuInfo, Menu) - || NO_SELECTED_ITEM == MenuInfo.iItem) - { - return NULL; - } ++ menu = MENU_GetMenu( hmenu ); + - MenuInitRosMenuItemInfo(&ItemInfo); - if (! MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return NULL; - } - if (0 != (ItemInfo.hSubMenu) && 0 != (ItemInfo.fState & MF_MOUSESELECT)) ++ if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0; ++ ++ //item = &menu->rgItems[menu->iItem]; ++ item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; ++ if (item) item = &item[menu->iItem]; ++ if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT)) + { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return ItemInfo.hSubMenu; ++ PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu); ++ return UserHMGetHandle(pSubMenu); + } - - MenuCleanupRosMenuItemInfo(&ItemInfo); - return NULL; ++ return 0; +} + +/*********************************************************************** + * MenuDoNextMenu + * + * NOTE: WM_NEXTMENU documented in Win32 is a bit different. + */ +static LRESULT FASTCALL +MenuDoNextMenu(MTRACKER* Mt, UINT Vk, UINT wFlags) +{ + ROSMENUINFO TopMenuInfo; + ROSMENUINFO MenuInfo; + + if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu)) + { + return (LRESULT) FALSE; + } + + if ((VK_LEFT == Vk && 0 == TopMenuInfo.iItem) + || (VK_RIGHT == Vk && TopMenuInfo.iItem == TopMenuInfo.cItems - 1)) + { + MDINEXTMENU NextMenu; + HMENU NewMenu; + HWND NewWnd; + UINT Id = 0; + + NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(Mt->TopMenu, 0) : Mt->TopMenu; + NextMenu.hmenuNext = NULL; + NextMenu.hwndNext = NULL; + SendMessageW(Mt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu); + + TRACE("%p [%p] -> %p [%p]\n", + Mt->CurrentMenu, Mt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext ); + + if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext) + { + DWORD Style = GetWindowLongPtrW(Mt->OwnerWnd, GWL_STYLE); + NewWnd = Mt->OwnerWnd; + if (IS_SYSTEM_MENU(&TopMenuInfo)) + { + /* switch to the menu bar */ + + if (0 != (Style & WS_CHILD) + || NULL == (NewMenu = GetMenu(NewWnd))) + { + return FALSE; + } + + if (VK_LEFT == Vk) + { + if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu)) + { + return FALSE; + } + Id = MenuInfo.cItems - 1; + } + } + else if (0 != (Style & WS_SYSMENU)) + { + /* switch to the system menu */ + NewMenu = NtUserGetSystemMenu(NewWnd, FALSE); + } + else + { + return FALSE; + } + } + else /* application returned a new menu to switch to */ + { + NewMenu = NextMenu.hmenuNext; + NewWnd = NextMenu.hwndNext; + + if (IsMenu(NewMenu) && IsWindow(NewWnd)) + { + DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE); + + if (0 != (Style & WS_SYSMENU) + && GetSystemMenu(NewWnd, FALSE) == NewMenu) + { + /* get the real system menu */ + NewMenu = NtUserGetSystemMenu(NewWnd, FALSE); + } + else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu) + { + /* FIXME: Not sure what to do here; + * perhaps try to track NewMenu as a popup? */ + + WARN(" -- got confused.\n"); + return FALSE; + } + } + else + { + return FALSE; + } + } + + if (NewMenu != Mt->TopMenu) + { + MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, + FALSE, 0 ); + if (Mt->CurrentMenu != Mt->TopMenu) + { + MenuHideSubPopups(Mt->OwnerWnd, &TopMenuInfo, FALSE, wFlags); + } + } + + if (NewWnd != Mt->OwnerWnd) + { + Mt->OwnerWnd = NewWnd; + NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, Mt->OwnerWnd); // 1 + SetCapture(Mt->OwnerWnd); // 2 + } + + Mt->TopMenu = Mt->CurrentMenu = NewMenu; /* all subpopups are hidden */ + if (MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu)) + { + MenuSelectItem(Mt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0); + } + + return TRUE; + } + + return FALSE; +} + +/*********************************************************************** + * MenuSuspendPopup + * + * The idea is not to show the popup if the next input message is + * going to hide it anyway. + */ +static BOOL FASTCALL +MenuSuspendPopup(MTRACKER* Mt, UINT uMsg) +{ + MSG msg; + + msg.hwnd = Mt->OwnerWnd; + + PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG +// Mt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!! + + switch( uMsg ) + { + case WM_KEYDOWN: + PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE); + if( msg.message == WM_KEYUP || msg.message == WM_PAINT ) + { + PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE); + PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE); + if( msg.message == WM_KEYDOWN && + (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT)) + { + Mt->TrackFlags |= TF_SUSPENDPOPUP; + return TRUE; + } + } + break; + } + /* failures go through this */ + Mt->TrackFlags &= ~TF_SUSPENDPOPUP; + return FALSE; +} + +/*********************************************************************** + * MenuKeyEscape + * + * Handle a VK_ESCAPE key event in a menu. + */ +static BOOL FASTCALL +MenuKeyEscape(MTRACKER *Mt, UINT Flags) +{ + BOOL EndMenu = TRUE; + ROSMENUINFO MenuInfo; + HMENU MenuTmp, MenuPrev; + + if (Mt->CurrentMenu != Mt->TopMenu) + { + if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu) + && 0 != (MenuInfo.fFlags & MNF_POPUP)) + { + MenuPrev = MenuTmp = Mt->TopMenu; + + /* close topmost popup */ + while (MenuTmp != Mt->CurrentMenu) + { + MenuPrev = MenuTmp; - MenuTmp = MenuGetSubPopup(MenuPrev); ++ MenuTmp = MENU_GetSubPopup(MenuPrev); + } + + if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev)) + { + MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, TRUE, Flags); + } + Mt->CurrentMenu = MenuPrev; + EndMenu = FALSE; + } + } + + return EndMenu; +} + +/*********************************************************************** + * MenuKeyLeft + * + * Handle a VK_LEFT key event in a menu. + */ +static void FASTCALL +MenuKeyLeft(MTRACKER* Mt, UINT Flags) +{ + ROSMENUINFO MenuInfo; + ROSMENUINFO TopMenuInfo; + ROSMENUINFO PrevMenuInfo; + HMENU MenuTmp, MenuPrev; + UINT PrevCol; + + MenuPrev = MenuTmp = Mt->TopMenu; + - if (! MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)) - { - return; - } - + /* Try to move 1 column left (if possible) */ - if ( (PrevCol = MenuGetStartOfPrevColumn(&MenuInfo)) != NO_SELECTED_ITEM) ++ if ( (PrevCol = MENU_GetStartOfPrevColumn(Mt->CurrentMenu)) != NO_SELECTED_ITEM) + { + if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)) + { + MenuSelectItem(Mt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0); + } + return; + } + + /* close topmost popup */ + while (MenuTmp != Mt->CurrentMenu) + { + MenuPrev = MenuTmp; - MenuTmp = MenuGetSubPopup(MenuPrev); ++ MenuTmp = MENU_GetSubPopup(MenuPrev); + } + + if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev)) + { + return; + } + MenuHideSubPopups(Mt->OwnerWnd, &PrevMenuInfo, TRUE, Flags); + Mt->CurrentMenu = MenuPrev; + + if (! MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu)) + { + return; + } + if ((MenuPrev == Mt->TopMenu) && !(TopMenuInfo.fFlags & MNF_POPUP)) + { + /* move menu bar selection if no more popups are left */ + + if (!MenuDoNextMenu(Mt, VK_LEFT, Flags)) + { + MenuMoveSelection(Mt->OwnerWnd, &TopMenuInfo, ITEM_PREV); + } + + if (MenuPrev != MenuTmp || Mt->TrackFlags & TF_SUSPENDPOPUP) + { + /* A sublevel menu was displayed - display the next one + * unless there is another displacement coming up */ + + if (! MenuSuspendPopup(Mt, WM_KEYDOWN) + && MenuGetRosMenuInfo(&TopMenuInfo, Mt->TopMenu)) + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &TopMenuInfo, + TRUE, Flags); + } + } + } +} + +/*********************************************************************** + * MenuKeyRight + * + * Handle a VK_RIGHT key event in a menu. + */ +static void FASTCALL MenuKeyRight(MTRACKER *Mt, UINT Flags) +{ + HMENU hmenutmp; + ROSMENUINFO MenuInfo; + ROSMENUINFO CurrentMenuInfo; + UINT NextCol; + + TRACE("MenuKeyRight called, cur %p, top %p.\n", + Mt->CurrentMenu, Mt->TopMenu); + + if (! MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) return; + if ((MenuInfo.fFlags & MNF_POPUP) || (Mt->CurrentMenu != Mt->TopMenu)) + { + /* If already displaying a popup, try to display sub-popup */ + + hmenutmp = Mt->CurrentMenu; + if (MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu)) + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags); + } + + /* if subpopup was displayed then we are done */ + if (hmenutmp != Mt->CurrentMenu) return; + } + - if (! MenuGetRosMenuInfo(&CurrentMenuInfo, Mt->CurrentMenu)) - { - return; - } - + /* Check to see if there's another column */ - if ( (NextCol = MenuGetStartOfNextColumn(&CurrentMenuInfo)) != NO_SELECTED_ITEM) ++ if ( (NextCol = MENU_GetStartOfNextColumn(Mt->CurrentMenu)) != NO_SELECTED_ITEM) + { + TRACE("Going to %d.\n", NextCol); + if (MenuGetRosMenuInfo(&MenuInfo, Mt->CurrentMenu)) + { + MenuSelectItem(Mt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0); + } + return; + } + + if (!(MenuInfo.fFlags & MNF_POPUP)) /* menu bar tracking */ + { + if (Mt->CurrentMenu != Mt->TopMenu) + { + MenuHideSubPopups(Mt->OwnerWnd, &MenuInfo, FALSE, Flags); + hmenutmp = Mt->CurrentMenu = Mt->TopMenu; + } + else + { + hmenutmp = NULL; + } + + /* try to move to the next item */ + if ( !MenuDoNextMenu(Mt, VK_RIGHT, Flags)) + MenuMoveSelection(Mt->OwnerWnd, &MenuInfo, ITEM_NEXT); + + if ( hmenutmp || Mt->TrackFlags & TF_SUSPENDPOPUP ) + { + if (! MenuSuspendPopup(Mt, WM_KEYDOWN) + && MenuGetRosMenuInfo(&MenuInfo, Mt->TopMenu)) + { + Mt->CurrentMenu = MenuShowSubPopup(Mt->OwnerWnd, &MenuInfo, + TRUE, Flags); + } + } + } +} + +/*********************************************************************** + * MenuTrackMenu + * + * Menu tracking code. + */ +static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y, + HWND hwnd, const RECT *lprect ) +{ + MSG msg; + ROSMENUINFO MenuInfo; + ROSMENUITEMINFO ItemInfo; ++ PMENU menu; + BOOL fRemove; + INT executedMenuId = -1; + MTRACKER mt; + HWND capture_win; + BOOL enterIdleSent = FALSE; + + mt.TrackFlags = 0; + mt.CurrentMenu = hmenu; + mt.TopMenu = hmenu; + mt.OwnerWnd = hwnd; + mt.Pt.x = x; + mt.Pt.y = y; + + TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n", + hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0, + lprect ? lprect->right : 0, lprect ? lprect->bottom : 0); + + if (!IsMenu(hmenu)) + { - WARN("Invalid menu handle %p\n", hmenu); - SetLastError( ERROR_INVALID_MENU_HANDLE ); ++ WARN("Invalid menu handle %p\n", hmenu); // Error already set in IsMenu. + return FALSE; + } + - fEndMenu = FALSE; + if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) + { + return FALSE; + } + + if (wFlags & TPM_BUTTONDOWN) + { + /* Get the result in order to start the tracking or not */ - fRemove = MenuButtonDown( &mt, hmenu, wFlags ); ++ fRemove = MENU_ButtonDown( &mt, hmenu, wFlags ); + fEndMenu = !fRemove; + } + + if (wFlags & TF_ENDMENU) fEndMenu = TRUE; + + /* owner may not be visible when tracking a popup, so use the menu itself */ + capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd; + NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1 + SetCapture(capture_win); // 2 + - while (! fEndMenu) ++ while (!fEndMenu) + { + BOOL ErrorExit = FALSE; - PMENU menu = ValidateHandle(mt.CurrentMenu, TYPE_MENU); ++ menu = MENU_GetMenu( mt.CurrentMenu ); + if (!menu) /* sometimes happens if I do a window manager close */ + break; + + /* we have to keep the message in the queue until it's + * clear that menu loop is not over yet. */ + + for (;;) + { + if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) + { + if (!CallMsgFilterW( &msg, MSGF_MENU )) break; + /* remove the message from the queue */ + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + } + else + { + /* ReactOS Check */ - if (!ValidateHwnd(mt.OwnerWnd) || !ValidateHwnd(MenuInfo.Wnd)) ++ if (!ValidateHwndNoErr(mt.OwnerWnd) || !ValidateHwndNoErr(MenuInfo.Wnd)) + { + ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works. + break; + } + if (!enterIdleSent) + { + HWND win = MenuInfo.fFlags & MNF_POPUP ? MenuInfo.Wnd : NULL; + enterIdleSent = TRUE; + SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win); + } + WaitMessage(); + } + } + + if (ErrorExit) break; // Gracefully dropout. + + /* check if EndMenu() tried to cancel us, by posting this message */ + if (msg.message == WM_CANCELMODE) + { + /* we are now out of the loop */ + fEndMenu = TRUE; + + /* remove the message from the queue */ + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + + /* break out of internal loop, ala ESCAPE */ + break; + } + + TranslateMessage( &msg ); + mt.Pt = msg.pt; + + if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) ) + enterIdleSent=FALSE; + + fRemove = FALSE; + if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST)) + { + /* + * Use the mouse coordinates in lParam instead of those in the MSG + * struct to properly handle synthetic messages. They are already + * in screen coordinates. + */ + mt.Pt.x = (short)LOWORD(msg.lParam); + mt.Pt.y = (short)HIWORD(msg.lParam); + + /* Find a menu for this mouse event */ - hmenu = MenuPtMenu(mt.TopMenu, mt.Pt); ++ hmenu = MENU_PtMenu(mt.TopMenu, mt.Pt); + + switch(msg.message) + { + /* no WM_NC... messages in captured state */ + + case WM_RBUTTONDBLCLK: - ERR("WM_RBUTTONDBLCLK\n"); + case WM_RBUTTONDOWN: + if (!(wFlags & TPM_RIGHTBUTTON)) break; + /* fall through */ + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + /* If the message belongs to the menu, removes it from the queue */ + /* Else, end menu tracking */ - fRemove = MenuButtonDown(&mt, hmenu, wFlags); ++ fRemove = MENU_ButtonDown(&mt, hmenu, wFlags); + fEndMenu = !fRemove; + break; + + case WM_RBUTTONUP: + if (!(wFlags & TPM_RIGHTBUTTON)) break; + /* fall through */ + case WM_LBUTTONUP: + /* Check if a menu was selected by the mouse */ + if (hmenu) + { - executedMenuId = MenuButtonUp( &mt, hmenu, wFlags); ++ executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags); + TRACE("executedMenuId %d\n", executedMenuId); + + /* End the loop if executedMenuId is an item ID */ + /* or if the job was done (executedMenuId = 0). */ + fEndMenu = fRemove = (executedMenuId != -1); + } + /* No menu was selected by the mouse */ + /* if the function was called by TrackPopupMenu, continue + with the menu tracking. If not, stop it */ + else + fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE); + + break; + + case WM_MOUSEMOVE: + /* the selected menu item must be changed every time */ + /* the mouse moves. */ + + if (hmenu) + fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags ); + + } /* switch(msg.message) - mouse */ + } + else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) + { + fRemove = TRUE; /* Keyboard messages are always removed */ + switch(msg.message) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + switch(msg.wParam) + { + case VK_MENU: + case VK_F10: + fEndMenu = TRUE; + break; + + case VK_HOME: + case VK_END: + if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) + { - MenuSelectItem(mt.OwnerWnd, &MenuInfo, - NO_SELECTED_ITEM, FALSE, 0 ); - MenuMoveSelection(mt.OwnerWnd, &MenuInfo, - VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV); ++ MenuSelectItem(mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 ); ++ MenuMoveSelection(mt.OwnerWnd, &MenuInfo, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV); + } + break; + + case VK_UP: + case VK_DOWN: /* If on menu bar, pull-down the menu */ + if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) + { + if (!(MenuInfo.fFlags & MNF_POPUP)) + { + if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu)) + mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags); + } + else /* otherwise try to move selection */ - MenuMoveSelection(mt.OwnerWnd, &MenuInfo, - (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT ); ++ MenuMoveSelection(mt.OwnerWnd, &MenuInfo, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT ); + } + break; + + case VK_LEFT: + MenuKeyLeft( &mt, wFlags ); + break; + + case VK_RIGHT: + MenuKeyRight( &mt, wFlags ); + break; + + case VK_ESCAPE: + fEndMenu = MenuKeyEscape(&mt, wFlags); + break; + + case VK_F1: + { + HELPINFO hi; + hi.cbSize = sizeof(HELPINFO); + hi.iContextType = HELPINFO_MENUITEM; + if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) + { + if (MenuInfo.iItem == NO_SELECTED_ITEM) + hi.iCtrlId = 0; + else + { + MenuInitRosMenuItemInfo(&ItemInfo); + if (MenuGetRosMenuItemInfo(MenuInfo.Self, + MenuInfo.iItem, + &ItemInfo)) + { + hi.iCtrlId = ItemInfo.wID; + } + else + { + hi.iCtrlId = 0; + } + MenuCleanupRosMenuItemInfo(&ItemInfo); + } + } + hi.hItemHandle = hmenu; + hi.dwContextId = MenuInfo.dwContextHelpID; + hi.MousePos = msg.pt; + SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi); + break; + } + + default: + break; + } + break; /* WM_KEYDOWN */ + + case WM_CHAR: + case WM_SYSCHAR: + { + UINT pos; + + if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break; + if (msg.wParam == L'\r' || msg.wParam == L' ') + { + executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags); + fEndMenu = (executedMenuId != -2); + break; + } + + /* Hack to avoid control chars. */ + /* We will find a better way real soon... */ + if (msg.wParam < 32) break; + - pos = MenuFindItemByKey(mt.OwnerWnd, &MenuInfo, - LOWORD(msg.wParam), FALSE); ++ pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE); + if (pos == (UINT)-2) fEndMenu = TRUE; + else if (pos == (UINT)-1) MessageBeep(0); + else + { - MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos, - TRUE, 0); ++ MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos, TRUE, 0); + executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags); + fEndMenu = (executedMenuId != -2); + } + } + break; + } /* switch(msg.message) - kbd */ + } + else + { + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + DispatchMessageW( &msg ); + continue; + } + + if (!fEndMenu) fRemove = TRUE; + + /* finally remove message from the queue */ + + if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) ) + PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); + else mt.TrackFlags &= ~TF_SKIPREMOVE; + } + + NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL); + SetCapture(NULL); /* release the capture */ + + /* If dropdown is still painted and the close box is clicked on + then the menu will be destroyed as part of the DispatchMessage above. + This will then invalidate the menu handle in mt.hTopMenu. We should + check for this first. */ + if( IsMenu( mt.TopMenu ) ) + { + if (IsWindow(mt.OwnerWnd)) + { + if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu)) + { + MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags); + + if (MenuInfo.fFlags & MNF_POPUP) + { + IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); + DestroyWindow(MenuInfo.Wnd); + MenuInfo.Wnd = NULL; ++ MenuSetRosMenuInfo(&MenuInfo); + + if (!(wFlags & TPM_NONOTIFY)) + SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu, + MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) ); + } + MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 ); + } + + SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 ); + } + + /* Reset the variable for hiding menu */ + if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu)) + { + MenuInfo.TimeToHide = FALSE; + MenuSetRosMenuInfo(&MenuInfo); + } + } + + /* The return value is only used by TrackPopupMenu */ + if (!(wFlags & TPM_RETURNCMD)) return TRUE; + if (executedMenuId == -1) executedMenuId = 0; + return executedMenuId; +} + +/*********************************************************************** + * MenuInitTracking + */ +static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags) +{ + ROSMENUINFO MenuInfo; + + TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu); + + HideCaret(0); + + /* This makes the menus of applications built with Delphi work. + * It also enables menus to be displayed in more than one window, + * but there are some bugs left that need to be fixed in this case. + */ - if (MenuGetRosMenuInfo(&MenuInfo, hMenu)) ++ if (!bPopup && (MenuGetRosMenuInfo(&MenuInfo, hMenu))) + { + MenuInfo.Wnd = hWnd; + MenuSetRosMenuInfo(&MenuInfo); + } ++ //if (!bPopup) menu->hWnd = hWnd; ++ if (!top_popup) ++ { ++ top_popup = MenuInfo.Wnd;//menu->hWnd; ++ top_popup_hmenu = hMenu; ++ } ++ ++ fEndMenu = FALSE; + + /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */ + if (!(wFlags & TPM_NONOTIFY)) + SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 ); + + SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION ); + + if (!(wFlags & TPM_NONOTIFY)) + { + SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 ); + /* If an app changed/recreated menu bar entries in WM_INITMENU + * menu sizes will be recalculated once the menu created/shown. + */ + + if (!MenuInfo.cyMenu) + { + /* app changed/recreated menu bar entries in WM_INITMENU + Recalculate menu sizes else clicks will not work */ + SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | + SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED ); + + } + } + + IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART, + hWnd, + MenuInfo.fFlags & MNF_SYSDESKMN ? OBJID_SYSMENU : OBJID_MENU, + CHILDID_SELF, 0); + return TRUE; +} ++ +/*********************************************************************** + * MenuExitTracking + */ +static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup) +{ + TRACE("hwnd=%p\n", hWnd); + + IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0); + SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 ); + ShowCaret(0); + top_popup = 0; + top_popup_hmenu = NULL; + return TRUE; +} + +/*********************************************************************** + * MenuTrackMouseMenuBar + * + * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand(). + */ +VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt) +{ + HMENU hMenu = (ht == HTSYSMENU) ? NtUserGetSystemMenu( hWnd, FALSE) : GetMenu(hWnd); + UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON; + + TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y); + + if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL; + if (IsMenu(hMenu)) + { + /* map point to parent client coordinates */ + HWND Parent = GetAncestor(hWnd, GA_PARENT ); + if (Parent != GetDesktopWindow()) + { + ScreenToClient(Parent, &pt); + } + + MenuInitTracking(hWnd, hMenu, FALSE, wFlags); + MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL); + MenuExitTracking(hWnd, FALSE); + } +} + - +/*********************************************************************** + * MenuTrackKbdMenuBar + * + * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand(). + */ +VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar) +{ + UINT uItem = NO_SELECTED_ITEM; + HMENU hTrackMenu; + ROSMENUINFO MenuInfo; + UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON; + + TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar); + + /* find window that has a menu */ + + while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) & + (WS_CHILD | WS_POPUP)) != WS_CHILD)) + if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return; + + /* check if we have to track a system menu */ + + hTrackMenu = GetMenu( hwnd ); + if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' ) + { + if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return; + hTrackMenu = NtUserGetSystemMenu(hwnd, FALSE); + uItem = 0; + wParam |= HTSYSMENU; /* prevent item lookup */ + } + + if (!IsMenu( hTrackMenu )) return; + + MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags ); + ++ /* fetch the window menu again, it may have changed */ ++ hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd ); ++ + if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu)) + { + goto track_menu; + } + + if( wChar && wChar != ' ' ) + { - uItem = MenuFindItemByKey( hwnd, &MenuInfo, wChar, (wParam & HTSYSMENU) ); ++ uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) ); + if ( uItem >= (UINT)(-2) ) + { + if( uItem == (UINT)(-1) ) MessageBeep(0); + /* schedule end of menu tracking */ + wFlags |= TF_ENDMENU; + goto track_menu; + } + } + + MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 ); + + if (!(wParam & HTSYSMENU) || wChar == ' ') + { + if( uItem == NO_SELECTED_ITEM ) + MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT ); + else + PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 ); + } + +track_menu: + MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL ); + MenuExitTracking( hwnd, FALSE ); +} + +/********************************************************************** + * TrackPopupMenuEx (USER32.@) + */ - BOOL WINAPI TrackPopupMenuEx( HMENU Menu, UINT Flags, int x, int y, - HWND Wnd, LPTPMPARAMS Tpm) ++BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, int x, int y, ++ HWND hWnd, LPTPMPARAMS lpTpm) +{ + BOOL ret = FALSE; - ROSMENUINFO MenuInfo; ++ MENU *menu; ++ ++ TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n", ++ hMenu, wFlags, x, y, hWnd, lpTpm, ++ lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" ); + - if (!IsMenu(Menu)) ++ /* Parameter check */ ++ /* FIXME: this check is performed several times, here and in the called ++ functions. That could be optimized */ ++ if (!(menu = MENU_GetMenu( hMenu ))) + { + SetLastError( ERROR_INVALID_MENU_HANDLE ); + return FALSE; + } + - /* ReactOS Check */ - if (!ValidateHwnd(Wnd)) - { - /* invalid window see wine menu.c test_menu_trackpopupmenu line 3146 */ - return FALSE; - } - - MenuGetRosMenuInfo(&MenuInfo, Menu); - if (IsWindow(MenuInfo.Wnd)) ++ if (IsWindow(menu->hWnd)) + { + SetLastError( ERROR_POPUP_ALREADY_ACTIVE ); + return FALSE; + } + - MenuInitTracking(Wnd, Menu, TRUE, Flags); ++ if (MENU_InitPopup( hWnd, hMenu, wFlags )) ++ { ++ MenuInitTracking(hWnd, hMenu, TRUE, wFlags); ++ ++ /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ ++ if (!(wFlags & TPM_NONOTIFY)) ++ SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM) hMenu, 0); + - /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ - if (!(Flags & TPM_NONOTIFY)) - SendMessageW(Wnd, WM_INITMENUPOPUP, (WPARAM) Menu, 0); ++ if (MenuShowPopup(hWnd, hMenu, 0, wFlags, x, y, 0, 0 )) ++ ret = MenuTrackMenu(hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, ++ lpTpm ? &lpTpm->rcExclude : NULL); ++ MenuExitTracking(hWnd, TRUE); + - if (MenuShowPopup(Wnd, Menu, 0, Flags, x, y, 0, 0 )) - ret = MenuTrackMenu(Menu, Flags | TPM_POPUPMENU, 0, 0, Wnd, - Tpm ? &Tpm->rcExclude : NULL); - MenuExitTracking(Wnd, TRUE); ++ if (menu->hWnd) ++ { ++ ROSMENUINFO MenuInfo; ++ if (IsWindow( menu->hWnd )) // wine hack around this with their destroy function. ++ DestroyWindow( menu->hWnd ); // Fix wrong error return. ++ //menu->hWnd = 0; ++ MenuGetRosMenuInfo(&MenuInfo, menu->head.h); ++ MenuInfo.Wnd = 0; ++ MenuSetRosMenuInfo(&MenuInfo); ++ ++ if (!(wFlags & TPM_NONOTIFY)) ++ SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu, ++ MAKELPARAM(0, IS_SYSTEM_MENU(menu)) ); ++ } ++ } + return ret; +} + +/********************************************************************** + * TrackPopupMenu (USER32.@) + */ +BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y, + int Reserved, HWND Wnd, CONST RECT *Rect) +{ + return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL); +} + - /* - * From MSDN: - * The MFT_BITMAP, MFT_SEPARATOR, and MFT_STRING values cannot be combined - * with one another. Also MFT_OWNERDRAW. Set fMask to MIIM_TYPE to use fType. - * - * Windows 2K/XP: fType is used only if fMask has a value of MIIM_FTYPE. - * - * MIIM_TYPE: Retrieves or sets the fType and dwTypeData members. Windows - * 2K/XP: MIIM_TYPE is replaced by MIIM_BITMAP, MIIM_FTYPE, and MIIM_STRING. - * MFT_STRING is replaced by MIIM_STRING. - * (So, I guess we should use MIIM_STRING only for strings?) - * - * MIIM_FTYPE: Windows 2K/Windows XP: Retrieves or sets the fType member. - * - * Based on wine, SetMenuItemInfo_common: - * 1) set MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP any one with MIIM_TYPE, - * it will result in a error. - * 2) set menu mask to MIIM_FTYPE and MFT_BITMAP ftype it will result in a error. - * These conditions are addressed in Win32k IntSetMenuItemInfo. ++/********************************************************************** ++ * MENU_mnu2mnuii + * ++ * Uses flags, id and text ptr, passed by InsertMenu() and ++ * ModifyMenu() to setup a MenuItemInfo structure. + */ - static - BOOL - FASTCALL - MenuSetItemData( - LPMENUITEMINFOW mii, - UINT Flags, - UINT_PTR IDNewItem, - LPCWSTR NewItem, - BOOL Unicode) ++static void MENU_mnu2mnuii( UINT flags, UINT_PTR id, LPCWSTR str, LPMENUITEMINFOW pmii, BOOL Unicode) +{ - /* - * Let us assume MIIM_FTYPE is set and building a new menu item structure. - */ - if(Flags & MF_BITMAP) - { - mii->fMask |= MIIM_BITMAP; /* Use the new way of seting hbmpItem.*/ - mii->hbmpItem = (HBITMAP) NewItem; - - if (Flags & MF_HELP) - { - /* increase ident */ - mii->fType |= MF_HELP; - } - } - else if(Flags & MF_OWNERDRAW) - { - mii->fType |= MFT_OWNERDRAW; - mii->fMask |= MIIM_DATA; - mii->dwItemData = (DWORD_PTR) NewItem; - } - else if (Flags & MF_SEPARATOR) - { - mii->fType |= MFT_SEPARATOR; - if (!(Flags & (MF_GRAYED|MF_DISABLED))) - Flags |= MF_GRAYED|MF_DISABLED; - } - else /* Default action MF_STRING. */ - { - /* Item beginning with a backspace is a help item */ - if (NewItem != NULL) - { - if (Unicode) - { - if (*NewItem == '\b') - { - mii->fType |= MF_HELP; - NewItem++; - } - } - else - { - LPCSTR NewItemA = (LPCSTR) NewItem; - if (*NewItemA == '\b') - { - mii->fType |= MF_HELP; - NewItemA++; - NewItem = (LPCWSTR) NewItemA; - } - } - - if (Flags & MF_HELP) - mii->fType |= MF_HELP; - mii->fMask |= MIIM_STRING; - mii->fType |= MFT_STRING; /* Zero */ - mii->dwTypeData = (LPWSTR)NewItem; - if (Unicode) - mii->cch = (NULL == NewItem ? 0 : strlenW(NewItem)); - else - mii->cch = (NULL == NewItem ? 0 : strlen((LPCSTR)NewItem)); - } - else - { - mii->fType |= MFT_SEPARATOR; - if (!(Flags & (MF_GRAYED|MF_DISABLED))) - Flags |= MF_GRAYED|MF_DISABLED; - } - } - - if(Flags & MF_RIGHTJUSTIFY) /* Same as MF_HELP */ - { - mii->fType |= MFT_RIGHTJUSTIFY; - } - - if(Flags & MF_MENUBREAK) - { - mii->fType |= MFT_MENUBREAK; - } - else if(Flags & MF_MENUBARBREAK) - { - mii->fType |= MFT_MENUBARBREAK; - } - - if(Flags & MF_GRAYED || Flags & MF_DISABLED) - { - if (Flags & MF_GRAYED) - mii->fState |= MF_GRAYED; - - if (Flags & MF_DISABLED) - mii->fState |= MF_DISABLED; - - mii->fMask |= MIIM_STATE; - } - else if (Flags & MF_HILITE) - { - mii->fState |= MF_HILITE; - mii->fMask |= MIIM_STATE; - } - else /* default state */ - { - mii->fState |= MFS_ENABLED; - mii->fMask |= MIIM_STATE; - } - - if(Flags & MF_POPUP && IsMenu((HMENU)IDNewItem)) - { - mii->fMask |= MIIM_SUBMENU; - mii->hSubMenu = (HMENU)IDNewItem; - } - mii->fMask |= MIIM_ID; - mii->wID = (UINT)IDNewItem; - return TRUE; - } - - NTSTATUS WINAPI - User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength) - { - PLOADMENU_CALLBACK_ARGUMENTS Common; - LRESULT Result; - - Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments; - - Result = (LRESULT)LoadMenuW( Common->hModule, - IS_INTRESOURCE(Common->MenuName[0]) ? - MAKEINTRESOURCE(Common->MenuName[0]) : - (LPCWSTR)&Common->MenuName); - - return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS); ++ RtlZeroMemory( pmii, sizeof( MENUITEMINFOW)); ++ pmii->cbSize = sizeof( MENUITEMINFOW); ++ pmii->fMask = MIIM_STATE | MIIM_ID | MIIM_FTYPE; ++ /* setting bitmap clears text and vice versa */ ++ if( IS_STRING_ITEM(flags)) { ++ pmii->fMask |= MIIM_STRING | MIIM_BITMAP; ++ if( !str) ++ flags |= MF_SEPARATOR; ++ /* Item beginning with a backspace is a help item */ ++ /* FIXME: wrong place, this is only true in win16 */ ++ else ++ { ++ if (Unicode) ++ { ++ if (*str == '\b') ++ { ++ flags |= MF_HELP; ++ str++; ++ } ++ } ++ else ++ { ++ LPCSTR NewItemA = (LPCSTR) str; ++ if (*NewItemA == '\b') ++ { ++ flags |= MF_HELP; ++ NewItemA++; ++ str = (LPCWSTR) NewItemA; ++ } ++ TRACE("A cch %d\n",strlen(NewItemA)); ++ } ++ } ++ pmii->dwTypeData = (LPWSTR)str; ++ } else if( flags & MFT_BITMAP){ ++ pmii->fMask |= MIIM_BITMAP | MIIM_STRING; ++ pmii->hbmpItem = (HBITMAP)str; ++ } ++ if( flags & MF_OWNERDRAW){ ++ pmii->fMask |= MIIM_DATA; ++ pmii->dwItemData = (ULONG_PTR) str; ++ } ++ if( flags & MF_POPUP && MENU_GetMenu((HMENU)id)) { ++ pmii->fMask |= MIIM_SUBMENU; ++ pmii->hSubMenu = (HMENU)id; ++ } ++ if( flags & MF_SEPARATOR) flags |= MF_GRAYED | MF_DISABLED; ++ pmii->fState = flags & MENUITEMINFO_STATE_MASK & ~MFS_DEFAULT; ++ pmii->fType = flags & MENUITEMINFO_TYPE_MASK; ++ pmii->wID = (UINT)id; +} + +/********************************************************************** + * MENU_NormalizeMenuItemInfoStruct + * + * Helper for SetMenuItemInfo and InsertMenuItemInfo: + * check, copy and extend the MENUITEMINFO struct from the version that the application + * supplied to the version used by wine source. */ +static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in, + MENUITEMINFOW *pmii_out ) +{ + /* do we recognize the size? */ + if( !pmii_in || (pmii_in->cbSize != sizeof( MENUITEMINFOW) && + pmii_in->cbSize != sizeof( MENUITEMINFOW) - sizeof( pmii_in->hbmpItem)) ) { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + /* copy the fields that we have */ + memcpy( pmii_out, pmii_in, pmii_in->cbSize); + /* if the hbmpItem member is missing then extend */ + if( pmii_in->cbSize != sizeof( MENUITEMINFOW)) { + pmii_out->cbSize = sizeof( MENUITEMINFOW); + pmii_out->hbmpItem = NULL; + } + /* test for invalid bit combinations */ + if( (pmii_out->fMask & MIIM_TYPE && + pmii_out->fMask & (MIIM_STRING | MIIM_FTYPE | MIIM_BITMAP)) || + (pmii_out->fMask & MIIM_FTYPE && pmii_out->fType & MFT_BITMAP)) { + ERR("invalid combination of fMask bits used\n"); + /* this does not happen on Win9x/ME */ + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + /* convert old style (MIIM_TYPE) to the new and keep the old one too */ + if( pmii_out->fMask & MIIM_TYPE){ + pmii_out->fMask |= MIIM_FTYPE; + if( IS_STRING_ITEM(pmii_out->fType)){ + pmii_out->fMask |= MIIM_STRING; + } else if( (pmii_out->fType) & MFT_BITMAP){ + pmii_out->fMask |= MIIM_BITMAP; + pmii_out->hbmpItem = UlongToHandle(LOWORD(pmii_out->dwTypeData)); + } + } + if (pmii_out->fMask & MIIM_FTYPE ) + { + pmii_out->fType &= ~MENUITEMINFO_TYPE_MASK; + pmii_out->fType |= pmii_in->fType & MENUITEMINFO_TYPE_MASK; + } + if (pmii_out->fMask & MIIM_STATE) + /* Other menu items having MFS_DEFAULT are not converted + to normal items */ + pmii_out->fState = pmii_in->fState & MENUITEMINFO_STATE_MASK; + + return TRUE; +} + ++BOOL ++MenuInit(VOID) ++{ ++ NONCLIENTMETRICSW ncm; ++ ++ /* get the menu font */ ++ if(!hMenuFont || !hMenuFontBold) ++ { ++ ncm.cbSize = sizeof(ncm); ++ if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) ++ { ++ ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n"); ++ return FALSE; ++ } ++ ++ hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont); ++ if(hMenuFont == NULL) ++ { ++ ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n"); ++ return FALSE; ++ } ++ ++ ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000); ++ hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont); ++ if(hMenuFontBold == NULL) ++ { ++ ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n"); ++ DeleteObject(hMenuFont); ++ hMenuFont = NULL; ++ return FALSE; ++ } ++ } ++ ++ return TRUE; ++} ++ ++VOID ++MenuCleanup(VOID) ++{ ++ if (hMenuFont) ++ { ++ DeleteObject(hMenuFont); ++ hMenuFont = NULL; ++ } ++ ++ if (hMenuFontBold) ++ { ++ DeleteObject(hMenuFontBold); ++ hMenuFontBold = NULL; ++ } ++} ++ ++NTSTATUS WINAPI ++User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength) ++{ ++ HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU"); ++ LRESULT Result = (LRESULT)hmenu; ++ MENUINFO menuinfo = {0}; ++ MENUITEMINFOW info = {0}; ++ ++ // removing space for checkboxes from menu ++ menuinfo.cbSize = sizeof(menuinfo); ++ menuinfo.fMask = MIM_STYLE; ++ GetMenuInfo(hmenu, &menuinfo); ++ menuinfo.dwStyle |= MNS_CHECKORBMP; // test_menu_bmp_and_string MNS_CHECKORBMP ++ SetMenuInfo(hmenu, &menuinfo); ++ ++ // adding bitmaps to menu items ++ info.cbSize = sizeof(info); ++ info.fMask |= MIIM_BITMAP; ++ info.hbmpItem = HBMMENU_POPUP_MINIMIZE; ++ SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info); ++ info.hbmpItem = HBMMENU_POPUP_RESTORE; ++ SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info); ++ info.hbmpItem = HBMMENU_POPUP_MAXIMIZE; ++ SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info); ++ info.hbmpItem = HBMMENU_POPUP_CLOSE; ++ SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info); ++ ++ return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS)); ++} ++ ++NTSTATUS WINAPI ++User32CallLoadMenuFromKernel(PVOID Arguments, ULONG ArgumentLength) ++{ ++ PLOADMENU_CALLBACK_ARGUMENTS Common; ++ LRESULT Result; ++ ++ Common = (PLOADMENU_CALLBACK_ARGUMENTS) Arguments; ++ ++ Result = (LRESULT)LoadMenuW( Common->hModule, ++ IS_INTRESOURCE(Common->MenuName[0]) ? ++ MAKEINTRESOURCE(Common->MenuName[0]) : ++ (LPCWSTR)&Common->MenuName); ++ ++ return ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS); ++} ++ + +/* FUNCTIONS *****************************************************************/ + +/* + * @implemented + */ +BOOL WINAPI +AppendMenuA(HMENU hMenu, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCSTR lpNewItem) +{ - return(InsertMenuA(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem, lpNewItem)); ++ MENUITEMINFOW mii; ++ UNICODE_STRING UnicodeString; ++ BOOL res; ++ ++ RtlInitUnicodeString(&UnicodeString, 0); ++ ++ MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE); ++ ++ /* copy the text string, it wll be one or the other */ ++ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) ++ { ++ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) ++ { ++ SetLastError (ERROR_NOT_ENOUGH_MEMORY); ++ return FALSE; ++ } ++ mii.dwTypeData = UnicodeString.Buffer; ++ mii.cch = UnicodeString.Length / sizeof(WCHAR); ++ } ++ else ++ { ++ TRACE("AMA Handle bitmaps\n"); ++ } ++ ////// Answer a question, why a -1? To hunt for the end of the item list. Get it, to Append? ++ res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &UnicodeString); ++ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); ++ return res; +} + +/* + * @implemented + */ +BOOL WINAPI +AppendMenuW(HMENU hMenu, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCWSTR lpNewItem) +{ - return(InsertMenuW(hMenu, -1, uFlags | MF_BYPOSITION, uIDNewItem, lpNewItem)); ++ MENUITEMINFOW mii; ++ UNICODE_STRING MenuText; ++ BOOL res; ++ ++ RtlInitUnicodeString(&MenuText, 0); ++ ++ MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE); ++ ++ /* copy the text string, it wll be one or the other */ ++ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) ++ { ++ RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData); ++ mii.dwTypeData = MenuText.Buffer; ++ mii.cch = MenuText.Length / sizeof(WCHAR); ++ } ++ res = NtUserThunkedMenuItemInfo(hMenu, -1, TRUE, TRUE, &mii, &MenuText); ++ return res; +} + +/* + * @implemented + */ +DWORD WINAPI +CheckMenuItem(HMENU hmenu, + UINT uIDCheckItem, + UINT uCheck) +{ + PMENU pMenu; + PITEM item; + DWORD Ret; + + if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU))) + return -1; + + if (!(item = MENU_FindItem( &hmenu, &uIDCheckItem, uCheck ))) return -1; + + Ret = item->fState & MFS_CHECKED; + if ( Ret == (uCheck & MFS_CHECKED)) return Ret; // Already Checked... + + return NtUserCheckMenuItem(hmenu, uIDCheckItem, uCheck); +} + - +/* + * @implemented + */ +BOOL WINAPI +CheckMenuRadioItem(HMENU hMenu, + UINT first, + UINT last, + UINT check, + UINT bypos) +{ + BOOL done = FALSE; + UINT i; + PITEM mi_first = NULL, mi_check; + HMENU m_first, m_check; + MENUITEMINFOW mii; + mii.cbSize = sizeof( mii); + + for (i = first; i <= last; i++) + { + UINT pos = i; + + if (!mi_first) + { + m_first = hMenu; + mi_first = MENU_FindItem(&m_first, &pos, bypos); + if (!mi_first) continue; + mi_check = mi_first; + m_check = m_first; + } + else + { + m_check = hMenu; + mi_check = MENU_FindItem(&m_check, &pos, bypos); + if (!mi_check) continue; + } + + if (m_first != m_check) continue; + if (mi_check->fType == MFT_SEPARATOR) continue; + + if (i == check) + { + if (!(mi_check->fType & MFT_RADIOCHECK) || !(mi_check->fState & MFS_CHECKED)) + { + mii.fMask = MIIM_FTYPE | MIIM_STATE; + mii.fType = (mi_check->fType & MENUITEMINFO_TYPE_MASK) | MFT_RADIOCHECK; + mii.fState = (mi_check->fState & MII_STATE_MASK) | MFS_CHECKED; + NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL); + } + done = TRUE; + } + else + { + /* MSDN is wrong, Windows does not remove MFT_RADIOCHECK */ + if (mi_check->fState & MFS_CHECKED) + { + mii.fMask = MIIM_STATE; + mii.fState = (mi_check->fState & MII_STATE_MASK) & ~MFS_CHECKED; + NtUserThunkedMenuItemInfo(m_check, i, bypos, FALSE, &mii, NULL); + } + } + } + return done; +} + +/* + * @implemented + */ +HMENU WINAPI +CreateMenu(VOID) +{ + MenuLoadBitmaps(); + return NtUserxCreateMenu(); +} + +/* + * @implemented + */ +HMENU WINAPI +CreatePopupMenu(VOID) +{ + MenuLoadBitmaps(); + return NtUserxCreatePopupMenu(); +} + +/* + * @implemented + */ +BOOL WINAPI +DrawMenuBar(HWND hWnd) +{ + return NtUserxDrawMenuBar(hWnd); +} + +/* + * @implemented + */ +BOOL WINAPI +EnableMenuItem(HMENU hMenu, + UINT uIDEnableItem, + UINT uEnable) +{ + return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable); +} + +/* + * @implemented + */ +BOOL WINAPI +EndMenu(VOID) +{ + GUITHREADINFO guii; + guii.cbSize = sizeof(GUITHREADINFO); + if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner) + { + if (!fEndMenu && + top_popup && + guii.hwndMenuOwner != top_popup ) + { + ERR("Capture GUI pti hWnd does not match top_popup!\n"); + } + } + + /* if we are in the menu code, and it is active */ + if (!fEndMenu && top_popup) + { + /* terminate the menu handling code */ + fEndMenu = TRUE; + + /* needs to be posted to wakeup the internal menu handler */ + /* which will now terminate the menu, in the event that */ + /* the main window was minimized, or lost focus, so we */ + /* don't end up with an orphaned menu */ + PostMessageW( top_popup, WM_CANCELMODE, 0, 0); + } - return TRUE; ++ return fEndMenu; +} + +BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID, + UINT wHilite ) +{ + ROSMENUINFO MenuInfo; + TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite); + // Force bits to be set call server side.... + // This alone works and passes all the menu test_menu_hilitemenuitem tests. + if (!NtUserHiliteMenuItem(hWnd, hMenu, wItemID, wHilite)) return FALSE; + // Without the above call we fail 3 out of the wine failed todo tests, see CORE-7967 + // Now redraw menu. + if (MenuGetRosMenuInfo(&MenuInfo, hMenu)) + { + if (MenuInfo.iItem == wItemID) return TRUE; + MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 ); + MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 ); + } + return TRUE; // Always returns TRUE! +} + +/* + * @implemented + */ +HMENU WINAPI +GetMenu(HWND hWnd) +{ + PWND Wnd = ValidateHwnd(hWnd); + + if (!Wnd) + return NULL; + + return UlongToHandle(Wnd->IDMenu); +} + - +/* + * @implemented + */ +BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi ) +{ + BOOL Ret; + Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi); + // Reason to move to server side!!!!! + if (!Ret) return Ret; + // EL HAXZO!!! + pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu; + if (!idItem) + { + pmbi->fFocused = pmbi->fBarFocused; + } + + return TRUE; +} + +/* + * @implemented + */ +LONG WINAPI +GetMenuCheckMarkDimensions(VOID) +{ + return(MAKELONG(GetSystemMetrics(SM_CXMENUCHECK), + GetSystemMetrics(SM_CYMENUCHECK))); +} + +/* + * @implemented + */ +DWORD +WINAPI +GetMenuContextHelpId(HMENU hmenu) +{ + PMENU pMenu; + if ((pMenu = ValidateHandle(hmenu, TYPE_MENU))) + return pMenu->dwContextHelpId; + return 0; +} + +/* + * @implemented + */ +UINT WINAPI +GetMenuDefaultItem(HMENU hMenu, + UINT fByPos, + UINT gmdiFlags) +{ + PMENU pMenu; + DWORD gismc = 0; + if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU))) + return (UINT)-1; + + return IntGetMenuDefaultItem( pMenu, (BOOL)fByPos, gmdiFlags, &gismc); +} + +/* + * @implemented + */ +BOOL WINAPI +GetMenuInfo(HMENU hmenu, + LPMENUINFO lpcmi) +{ + PMENU pMenu; + + if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (!(pMenu = ValidateHandle(hmenu, TYPE_MENU))) + return FALSE; + + if (lpcmi->fMask & MIM_BACKGROUND) + lpcmi->hbrBack = pMenu->hbrBack; + + if (lpcmi->fMask & MIM_HELPID) + lpcmi->dwContextHelpID = pMenu->dwContextHelpId; + + if (lpcmi->fMask & MIM_MAXHEIGHT) + lpcmi->cyMax = pMenu->cyMax; + + if (lpcmi->fMask & MIM_MENUDATA) + lpcmi->dwMenuData = pMenu->dwMenuData; + + if (lpcmi->fMask & MIM_STYLE) + lpcmi->dwStyle = pMenu->fFlags & MNS_STYLE_MASK; + + return TRUE; +} + +/* + * @implemented + */ +int WINAPI +GetMenuItemCount(HMENU hmenu) +{ + PMENU pMenu; + if ((pMenu = ValidateHandle(hmenu, TYPE_MENU))) + return pMenu->cItems; + return -1; +} + +/* + * @implemented + */ +UINT WINAPI +GetMenuItemID(HMENU hMenu, + int nPos) +{ - PMENU pMenu; - PITEM pItem; - INT i = 0; - - if (!(pMenu = ValidateHandle(hMenu, TYPE_MENU))) - return -1; - - pItem = pMenu->rgItems ? DesktopPtrToUser(pMenu->rgItems) : NULL; - if ( nPos >= 0 ) - { - //pItem = &menu->rgItems[nPos]; or pItem[nPos]; after dptu. - while(pItem) // Do this for now. - { - if (i < (INT)pMenu->cItems) - { - if ( nPos == i && !pItem->spSubMenu) return pItem->wID; - } - pItem = pItem->Next ? DesktopPtrToUser(pItem->Next) : NULL; - i++; - } - } - return -1; ++ ITEM * lpmi; ++ if (!(lpmi = MENU_FindItem(&hMenu,(UINT*)&nPos,MF_BYPOSITION))) return -1; ++ if (lpmi->spSubMenu) return -1; ++ return lpmi->wID; +} + +/* + * @implemented + */ +BOOL WINAPI +GetMenuItemInfoA( + HMENU hmenu, + UINT item, + BOOL bypos, + LPMENUITEMINFOA lpmii) +{ + BOOL ret; + MENUITEMINFOA mii; + + if( lpmii->cbSize != sizeof( mii) && + lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) + { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + memcpy( &mii, lpmii, lpmii->cbSize); + mii.cbSize = sizeof( mii); + ret = GetMenuItemInfo_common (hmenu, + item, + bypos, + (LPMENUITEMINFOW)&mii, + FALSE); + mii.cbSize = lpmii->cbSize; + memcpy( lpmii, &mii, mii.cbSize); + return ret; +} + +/* + * @implemented + */ +BOOL WINAPI +GetMenuItemInfoW( + HMENU hMenu, + UINT Item, + BOOL bypos, + LPMENUITEMINFOW lpmii) +{ + BOOL ret; + MENUITEMINFOW mii; + if( lpmii->cbSize != sizeof( mii) && lpmii->cbSize != sizeof( mii) - sizeof ( mii.hbmpItem)) + { + SetLastError( ERROR_INVALID_PARAMETER); + return FALSE; + } + memcpy( &mii, lpmii, lpmii->cbSize); + mii.cbSize = sizeof( mii); + ret = GetMenuItemInfo_common (hMenu, Item, bypos, &mii, TRUE); + mii.cbSize = lpmii->cbSize; + memcpy( lpmii, &mii, mii.cbSize); + return ret; +} + +/* + * @implemented + */ +UINT +WINAPI +GetMenuState( + HMENU hMenu, + UINT uId, + UINT uFlags) +{ + PITEM pItem; ++ UINT Type = 0; + TRACE("(menu=%p, id=%04x, flags=%04x);\n", hMenu, uId, uFlags); + if (!(pItem = MENU_FindItem( &hMenu, &uId, uFlags ))) return -1; + ++ if (!pItem->Xlpstr && pItem->hbmp) Type = MFT_BITMAP; ++ + if (pItem->spSubMenu) + { + PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); + HMENU hsubmenu = UserHMGetHandle(pSubMenu); + if (!IsMenu(hsubmenu)) return (UINT)-1; - else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType) & 0xff); ++ else return (pSubMenu->cItems << 8) | ((pItem->fState|pItem->fType|Type) & 0xff); + } + else - return (pItem->fType | pItem->fState); ++ return (pItem->fType | pItem->fState | Type); +} + +/* + * @implemented + */ +int +WINAPI +GetMenuStringA( + HMENU hMenu, + UINT uIDItem, + LPSTR lpString, + int nMaxCount, + UINT uFlag) +{ - /* MENUITEMINFOA mii; - memset( &mii, 0, sizeof(mii) ); - mii.dwTypeData = lpString; - mii.fMask = MIIM_STRING; - mii.cbSize = sizeof(MENUITEMINFOA); - mii.cch = nMaxCount; - - if(!(GetMenuItemInfoA( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&mii))) - return 0; - else - return mii.cch; - */ + ITEM *item; + LPWSTR text; + ////// wine Code, seems to be faster. + TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag ); + + if (lpString && nMaxCount) lpString[0] = '\0'; + + if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag ))) + { + SetLastError( ERROR_MENU_ITEM_NOT_FOUND); + return 0; + } + + text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL; + + if (!text) return 0; + if (!lpString || !nMaxCount) return WideCharToMultiByte( CP_ACP, 0, text, -1, NULL, 0, NULL, NULL ); + if (!WideCharToMultiByte( CP_ACP, 0, text, -1, lpString, nMaxCount, NULL, NULL )) + lpString[nMaxCount-1] = 0; - ERR("returning %s\n", lpString); ++ TRACE("A returning %s\n", lpString); + return strlen(lpString); +} + +/* + * @implemented + */ +int +WINAPI +GetMenuStringW( + HMENU hMenu, + UINT uIDItem, + LPWSTR lpString, + int nMaxCount, + UINT uFlag) +{ - /* MENUITEMINFOW miiW; - memset( &miiW, 0, sizeof(miiW) ); - miiW.dwTypeData = lpString; - miiW.fMask = MIIM_STRING | MIIM_FTYPE; - miiW.fType = MFT_STRING; - miiW.cbSize = sizeof(MENUITEMINFOW); - miiW.cch = nMaxCount; - - if(!(GetMenuItemInfoW( hMenu, uIDItem, (BOOL)(MF_BYPOSITION & uFlag),&miiW))) - return 0; - else - return miiW.cch; - */ + ITEM *item; + LPWSTR text; + + TRACE("menu=%p item=%04x ptr=%p len=%d flags=%04x\n", hMenu, uIDItem, lpString, nMaxCount, uFlag ); + + if (lpString && nMaxCount) lpString[0] = '\0'; + + if (!(item = MENU_FindItem( &hMenu, &uIDItem, uFlag ))) + { + SetLastError( ERROR_MENU_ITEM_NOT_FOUND); + return 0; + } + + text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL; + + if (!lpString || !nMaxCount) return text ? strlenW(text) : 0; + if( !(text)) + { + lpString[0] = 0; + return 0; + } + lstrcpynW( lpString, text, nMaxCount ); - ERR("returning %S\n", lpString); ++ TRACE("W returning %S\n", lpString); + return strlenW(lpString); +} + +/* + * @implemented + */ +HMENU +WINAPI +GetSubMenu( + HMENU hMenu, + int nPos) +{ + PITEM pItem; + if (!(pItem = MENU_FindItem( &hMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL; + + if (pItem->spSubMenu) + { + PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); + HMENU hsubmenu = UserHMGetHandle(pSubMenu); + if (IsMenu(hsubmenu)) return hsubmenu; + } + return NULL; +} + +/* + * @implemented + */ +HMENU +WINAPI +GetSystemMenu( + HWND hWnd, + BOOL bRevert) +{ + HMENU TopMenu; + + TopMenu = NtUserGetSystemMenu(hWnd, bRevert); + + return NULL == TopMenu ? NULL : GetSubMenu(TopMenu, 0); +} + +/* + * @implemented + */ +BOOL +WINAPI +InsertMenuA( + HMENU hMenu, + UINT uPosition, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCSTR lpNewItem) +{ - MENUITEMINFOA mii; - memset( &mii, 0, sizeof(mii) ); - mii.cbSize = sizeof(MENUITEMINFOA); - mii.fMask = MIIM_FTYPE; - - MenuSetItemData((LPMENUITEMINFOW) &mii, - uFlags, - uIDNewItem, - (LPCWSTR) lpNewItem, - FALSE); - - return InsertMenuItemA(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii); ++ MENUITEMINFOW mii; ++ UNICODE_STRING UnicodeString; ++ BOOL res; ++ ++ RtlInitUnicodeString(&UnicodeString, 0); ++ ++ MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE); ++ ++ /* copy the text string, it wll be one or the other */ ++ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) ++ { ++ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) ++ { ++ SetLastError (ERROR_NOT_ENOUGH_MEMORY); ++ return FALSE; ++ } ++ mii.dwTypeData = UnicodeString.Buffer; ++ mii.cch = UnicodeString.Length / sizeof(WCHAR); ++ } ++ else ++ { ++ TRACE("Handle bitmaps\n"); ++ } ++ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &UnicodeString); ++ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); ++ return res; +} + +/* + * @implemented + */ +BOOL +WINAPI +InsertMenuItemA( + HMENU hMenu, + UINT uItem, + BOOL fByPosition, + LPCMENUITEMINFOA lpmii) +{ + MENUITEMINFOW mii; + UNICODE_STRING UnicodeString; + BOOL res; + + TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii); + + RtlInitUnicodeString(&UnicodeString, 0); + + if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; + + /* copy the text string */ + if (((mii.fMask & MIIM_STRING) || + ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING))) + && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) ) + { + if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) + { + SetLastError (ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + mii.dwTypeData = UnicodeString.Buffer; + mii.cch = UnicodeString.Length / sizeof(WCHAR); + } + else + { - UnicodeString.Buffer = NULL; ++ TRACE("Handle bitmaps\n"); + } + res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &UnicodeString); + if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); + return res; +} + +/* + * @implemented + */ +BOOL +WINAPI +InsertMenuItemW( + HMENU hMenu, + UINT uItem, + BOOL fByPosition, + LPCMENUITEMINFOW lpmii) +{ + MENUITEMINFOW mii; + UNICODE_STRING MenuText; + BOOL res = FALSE; + + /* while we could just pass 'lpmii' to win32k, we make a copy so that + if a bad user passes bad data, we crash his process instead of the + entire kernel */ + + TRACE("hmenu %p, item %04x, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii); + + RtlInitUnicodeString(&MenuText, 0); + + if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; + + /* copy the text string */ + if (((mii.fMask & MIIM_STRING) || + ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING))) + && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) ) + { + RtlInitUnicodeString(&MenuText, (PWSTR)lpmii->dwTypeData); + mii.dwTypeData = MenuText.Buffer; + mii.cch = MenuText.Length / sizeof(WCHAR); + } + res = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, TRUE, &mii, &MenuText); + return res; +} + +/* + * @implemented + */ +BOOL +WINAPI +InsertMenuW( + HMENU hMenu, + UINT uPosition, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCWSTR lpNewItem) +{ + MENUITEMINFOW mii; - memset( &mii, 0, sizeof(mii) ); - mii.cbSize = sizeof(MENUITEMINFOW); - mii.fMask = MIIM_FTYPE; ++ UNICODE_STRING MenuText; ++ BOOL res; + - MenuSetItemData( &mii, - uFlags, - uIDNewItem, - lpNewItem, - TRUE); ++ RtlInitUnicodeString(&MenuText, 0); ++ ++ MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE); + - return InsertMenuItemW(hMenu, uPosition, (BOOL)((MF_BYPOSITION & uFlags) > 0), &mii); ++ /* copy the text string, it wll be one or the other */ ++ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) ++ { ++ RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData); ++ mii.dwTypeData = MenuText.Buffer; ++ mii.cch = MenuText.Length / sizeof(WCHAR); ++ } ++ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), TRUE, &mii, &MenuText); ++ return res; +} + +/* + * @implemented + */ +BOOL +WINAPI +IsMenu( + HMENU Menu) +{ + if (ValidateHandle(Menu, TYPE_MENU)) return TRUE; + return FALSE; +} + +/* + * @implemented + */ +HMENU WINAPI +LoadMenuA(HINSTANCE hInstance, + LPCSTR lpMenuName) +{ + HANDLE Resource = FindResourceA(hInstance, lpMenuName, MAKEINTRESOURCEA(4)); + if (Resource == NULL) + { + return(NULL); + } + return(LoadMenuIndirectA((PVOID)LoadResource(hInstance, Resource))); +} + +/* + * @implemented + */ +HMENU WINAPI +LoadMenuIndirectA(CONST MENUTEMPLATE *lpMenuTemplate) +{ + return(LoadMenuIndirectW(lpMenuTemplate)); +} + +/* + * @implemented + */ +HMENU WINAPI +LoadMenuIndirectW(CONST MENUTEMPLATE *lpMenuTemplate) +{ + HMENU hMenu; + WORD version, offset; + LPCSTR p = (LPCSTR)lpMenuTemplate; + + version = GET_WORD(p); + p += sizeof(WORD); + + switch (version) + { + case 0: /* standard format is version of 0 */ + offset = GET_WORD(p); + p += sizeof(WORD) + offset; + if (!(hMenu = CreateMenu())) return 0; + if (!MENU_ParseResource(p, hMenu)) + { + DestroyMenu(hMenu); + return 0; + } + return hMenu; + case 1: /* extended format is version of 1 */ + offset = GET_WORD(p); + p += sizeof(WORD) + offset; + if (!(hMenu = CreateMenu())) return 0; + if (!MENUEX_ParseResource(p, hMenu)) + { + DestroyMenu( hMenu ); + return 0; + } + return hMenu; + default: + ERR("Menu template version %d not supported.\n", version); + return 0; + } +} + +/* + * @implemented + */ +HMENU WINAPI +LoadMenuW(HINSTANCE hInstance, + LPCWSTR lpMenuName) +{ + HANDLE Resource = FindResourceW(hInstance, lpMenuName, RT_MENU); + if (Resource == NULL) + { + return(NULL); + } + return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource))); +} + +/* + * @implemented + */ +int +WINAPI +MenuItemFromPoint( + HWND hWnd, + HMENU hMenu, + POINT ptScreen) +{ + return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y); +} + +/* + * @implemented + */ +BOOL +WINAPI +ModifyMenuA( - HMENU hMnu, ++ HMENU hMenu, + UINT uPosition, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCSTR lpNewItem) +{ - MENUITEMINFOA mii; - memset( &mii, 0, sizeof(mii) ); - mii.cbSize = sizeof(MENUITEMINFOA); - mii.fMask = MIIM_FTYPE; - - MenuSetItemData((LPMENUITEMINFOW) &mii, - uFlags, - uIDNewItem, - (LPCWSTR) lpNewItem, - FALSE); - - //if (mii.hSubMenu && (uFlags & MF_POPUP) && (mii.hSubMenu != (HMENU)uIDNewItem)) - // NtUserDestroyMenu( mii.hSubMenu ); /* ModifyMenu() spec */ - - return SetMenuItemInfoA( hMnu, - uPosition, - (BOOL)(MF_BYPOSITION & uFlags), - &mii); ++ MENUITEMINFOW mii; ++ UNICODE_STRING UnicodeString; ++ BOOL res; ++ ++ RtlInitUnicodeString(&UnicodeString, 0); ++ ++ MENU_mnu2mnuii( uFlags, uIDNewItem, (LPCWSTR)lpNewItem, &mii, FALSE); ++ ++ /* copy the text string, it wll be one or the other */ ++ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) ++ { ++ if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) ++ { ++ SetLastError (ERROR_NOT_ENOUGH_MEMORY); ++ return FALSE; ++ } ++ mii.dwTypeData = UnicodeString.Buffer; ++ mii.cch = UnicodeString.Length / sizeof(WCHAR); ++ } ++ else ++ { ++ TRACE("Handle bitmaps\n"); ++ } ++ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &UnicodeString); ++ if ( UnicodeString.Buffer ) RtlFreeUnicodeString ( &UnicodeString ); ++ return res; +} + +/* + * @implemented + */ +BOOL +WINAPI +ModifyMenuW( - HMENU hMnu, ++ HMENU hMenu, + UINT uPosition, + UINT uFlags, + UINT_PTR uIDNewItem, + LPCWSTR lpNewItem) +{ + MENUITEMINFOW mii; - memset ( &mii, 0, sizeof(mii) ); - mii.cbSize = sizeof(MENUITEMINFOW); - mii.fMask = MIIM_FTYPE; - - /* Init new data for this menu item */ - MenuSetItemData( &mii, - uFlags, - uIDNewItem, - lpNewItem, - TRUE); - - //if (mii.hSubMenu && (uFlags & MF_POPUP) && (mii.hSubMenu != (HMENU)uIDNewItem)) - // NtUserDestroyMenu( mii.hSubMenu ); /* ModifyMenu() spec */ - - return SetMenuItemInfoW( hMnu, - uPosition, - (BOOL)(MF_BYPOSITION & uFlags), - &mii); ++ UNICODE_STRING MenuText; ++ BOOL res; ++ ++ RtlInitUnicodeString(&MenuText, 0); ++ ++ MENU_mnu2mnuii( uFlags, uIDNewItem, lpNewItem, &mii, TRUE); ++ ++ /* copy the text string, it wll be one or the other */ ++ if (lpNewItem && mii.fMask & MIIM_STRING && !mii.hbmpItem && mii.dwTypeData) ++ { ++ RtlInitUnicodeString(&MenuText, (PWSTR)mii.dwTypeData); ++ mii.dwTypeData = MenuText.Buffer; ++ mii.cch = MenuText.Length / sizeof(WCHAR); ++ } ++ else ++ { ++ TRACE("Handle bitmaps\n"); ++ } ++ res = NtUserThunkedMenuItemInfo(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), FALSE, &mii, &MenuText); ++ return res; +} + +/* + * @implemented + */ +BOOL WINAPI +SetMenu(HWND hWnd, + HMENU hMenu) +{ + return NtUserSetMenu(hWnd, hMenu, TRUE); +} + +/* + * @implemented + */ +BOOL +WINAPI +SetMenuInfo( + HMENU hmenu, + LPCMENUINFO lpcmi) +{ + ROSMENUINFO mi; + BOOL res = FALSE; + + if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) + { + SetLastError(ERROR_INVALID_PARAMETER); + return res; + } + + memcpy(&mi, lpcmi, sizeof(MENUINFO)); + return NtUserThunkedMenuInfo(hmenu, (LPCMENUINFO)&mi); +} + +/* + * @implemented + */ +BOOL +WINAPI +SetMenuItemBitmaps( + HMENU hMenu, + UINT uPosition, + UINT uFlags, + HBITMAP hBitmapUnchecked, + HBITMAP hBitmapChecked) +{ + MENUITEMINFOW uItem; + memset ( &uItem, 0, sizeof(uItem) ); + uItem.cbSize = sizeof(MENUITEMINFOW); + uItem.fMask = MIIM_CHECKMARKS; + uItem.hbmpUnchecked = hBitmapUnchecked; + uItem.hbmpChecked = hBitmapChecked; + return SetMenuItemInfoW(hMenu, uPosition, (BOOL)(uFlags & MF_BYPOSITION), &uItem); +} + +/* + * @implemented + */ +BOOL +WINAPI +SetMenuItemInfoA( + HMENU hmenu, + UINT item, + BOOL bypos, + LPCMENUITEMINFOA lpmii) +{ + MENUITEMINFOW mii; + UNICODE_STRING UnicodeString; + BOOL Ret; + + TRACE("hmenu %p, item %u, by pos %d, info %p\n", hmenu, item, bypos, lpmii); + + RtlInitUnicodeString(&UnicodeString, 0); + + if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &mii )) return FALSE; +/* + * MIIM_STRING == good + * MIIM_TYPE & MFT_STRING == good + * MIIM_STRING & MFT_STRING == good + * MIIM_STRING & MFT_OWNERDRAW == good + */ + if (((mii.fMask & MIIM_STRING) || + ((mii.fMask & MIIM_TYPE) && (MENU_ITEM_TYPE(mii.fType) == MF_STRING))) + && mii.dwTypeData && !(GdiValidateHandle((HGDIOBJ)mii.dwTypeData)) ) + { + /* cch is ignored when the content of a menu item is set by calling SetMenuItemInfo. */ + if (!RtlCreateUnicodeStringFromAsciiz(&UnicodeString, (LPSTR)mii.dwTypeData)) + { + SetLastError (ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + mii.dwTypeData = UnicodeString.Buffer; + mii.cch = UnicodeString.Length / sizeof(WCHAR); + } + else + { + UnicodeString.Buffer = NULL; + } + Ret = NtUserThunkedMenuItemInfo(hmenu, item, bypos, FALSE, &mii, &UnicodeString); + if (UnicodeString.Buffer != NULL) RtlFreeUnicodeString(&UnicodeString); + return Ret; +} + +/* + * @implemented + */ +BOOL +WINAPI +SetMenuItemInfoW( + HMENU hMenu, + UINT uItem, + BOOL fByPosition, + LPCMENUITEMINFOW lpmii) +{ + MENUITEMINFOW MenuItemInfoW; + UNICODE_STRING UnicodeString; + BOOL Ret; + + TRACE("hmenu %p, item %u, by pos %d, info %p\n", hMenu, uItem, fByPosition, lpmii); + + RtlInitUnicodeString(&UnicodeString, 0); + + if (!MENU_NormalizeMenuItemInfoStruct( (const MENUITEMINFOW *)lpmii, &MenuItemInfoW )) return FALSE; + + if (((MenuItemInfoW.fMask & MIIM_STRING) || + ((MenuItemInfoW.fMask & MIIM_TYPE) && + (MENU_ITEM_TYPE(MenuItemInfoW.fType) == MF_STRING))) + && MenuItemInfoW.dwTypeData && !(GdiValidateHandle((HGDIOBJ)MenuItemInfoW.dwTypeData)) ) + { + RtlInitUnicodeString(&UnicodeString, (PCWSTR)MenuItemInfoW.dwTypeData); + MenuItemInfoW.cch = strlenW(MenuItemInfoW.dwTypeData); + } + Ret = NtUserThunkedMenuItemInfo(hMenu, uItem, fByPosition, FALSE, &MenuItemInfoW, &UnicodeString); + + return Ret; +} + +/* + * @implemented + */ +BOOL +WINAPI +SetSystemMenu ( + HWND hwnd, + HMENU hMenu) +{ + if(!hwnd) + { + SetLastError(ERROR_INVALID_WINDOW_HANDLE); + return FALSE; + } + if(!hMenu) + { + SetLastError(ERROR_INVALID_MENU_HANDLE); + return FALSE; + } + return NtUserSetSystemMenu(hwnd, hMenu); +} + +// +// Example for the Win32/User32 rewrite. +// Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24 +// +// +BOOL +WINAPI +NEWTrackPopupMenu( + HMENU Menu, + UINT Flags, + int x, + int y, + int Reserved, + HWND Wnd, + CONST RECT *Rect) +{ + return NtUserTrackPopupMenuEx( Menu, + Flags, + x, + y, + Wnd, + NULL); // LPTPMPARAMS is null +} + - - +/* + * @unimplemented + */ +BOOL +WINAPI +MenuWindowProcA( + HWND hWnd, + ULONG_PTR Result, + UINT Msg, + WPARAM wParam, + LPARAM lParam + ) +{ + if ( Msg < WM_USER) + { + LRESULT lResult; + lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam ); + if (Result) + { + Result = (ULONG_PTR)lResult; + return TRUE; + } + return FALSE; + } + return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE); - +} + +/* + * @unimplemented + */ +BOOL +WINAPI +MenuWindowProcW( + HWND hWnd, + ULONG_PTR Result, + UINT Msg, + WPARAM wParam, + LPARAM lParam + ) +{ + if ( Msg < WM_USER) + { + LRESULT lResult; + lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam ); + if (Result) + { + Result = (ULONG_PTR)lResult; + return TRUE; + } + return FALSE; + } + return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE); +} + +/* + * @implemented + */ +BOOL +WINAPI +ChangeMenuW( + HMENU hMenu, + UINT cmd, + LPCWSTR lpszNewItem, + UINT cmdInsert, + UINT flags) +{ + /* + FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert + for MF_DELETE. We should check the parameters for all others + MF_* actions also (anybody got a doc on ChangeMenu?). + */ + + switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT)) + { + case MF_APPEND : + return AppendMenuW(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem); + + case MF_DELETE : + return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE); + + case MF_CHANGE : + return ModifyMenuW(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem); + + case MF_REMOVE : + return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert, + flags &~ MF_REMOVE); + + default : /* MF_INSERT */ + return InsertMenuW(hMenu, cmd, flags, cmdInsert, lpszNewItem); + }; +} + +/* + * @implemented + */ +BOOL +WINAPI +ChangeMenuA( + HMENU hMenu, + UINT cmd, + LPCSTR lpszNewItem, + UINT cmdInsert, + UINT flags) +{ + /* + FIXME: Word passes the item id in 'cmd' and 0 or 0xffff as cmdInsert + for MF_DELETE. We should check the parameters for all others + MF_* actions also (anybody got a doc on ChangeMenu?). + */ + + switch(flags & (MF_APPEND | MF_DELETE | MF_CHANGE | MF_REMOVE | MF_INSERT)) + { + case MF_APPEND : + return AppendMenuA(hMenu, flags &~ MF_APPEND, cmdInsert, lpszNewItem); + + case MF_DELETE : + return DeleteMenu(hMenu, cmd, flags &~ MF_DELETE); + + case MF_CHANGE : + return ModifyMenuA(hMenu, cmd, flags &~ MF_CHANGE, cmdInsert, lpszNewItem); + + case MF_REMOVE : + return RemoveMenu(hMenu, flags & MF_BYPOSITION ? cmd : cmdInsert, + flags &~ MF_REMOVE); + + default : /* MF_INSERT */ + return InsertMenuA(hMenu, cmd, flags, cmdInsert, lpszNewItem); + }; +} +