You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1261 lines
37 KiB
1261 lines
37 KiB
/********************************************************************
|
|
|
|
Copyright (c) 1999-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
util.cpp
|
|
|
|
Abstract:
|
|
utility functions implementation
|
|
|
|
Revision History:
|
|
DerekM created 05/01/99
|
|
|
|
********************************************************************/
|
|
|
|
#include "stdafx.h"
|
|
#include "util.h"
|
|
#include <ercommon.h>
|
|
#include <strsafe.h>
|
|
|
|
const WCHAR c_wszRKSetup[] = L"System\\Setup";
|
|
const WCHAR c_wszRVSetupNow[] = L"SystemSetupInProgress";
|
|
const WCHAR c_wszRVMiniSetupNow[] = L"MiniSetupInProgress";
|
|
const WCHAR c_wszRVOOBESetupNow[] = L"OobeInProgress";
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// tracing
|
|
|
|
#ifdef THIS_FILE
|
|
#undef THIS_FILE
|
|
#endif
|
|
static char __szTraceSourceFile[] = __FILE__;
|
|
#define THIS_FILE __szTraceSourceFile
|
|
|
|
HANDLE g_hPFPrivateHeap = NULL;
|
|
|
|
struct SLangCodepage
|
|
{
|
|
WORD wLanguage;
|
|
WORD wCodePage;
|
|
};
|
|
|
|
DWORD SetupIsInProgress(void)
|
|
{
|
|
DWORD retval = SIIP_NO_SETUP;
|
|
HRESULT hr;
|
|
HKEY hkey = NULL;
|
|
DWORD dw;
|
|
|
|
USE_TRACING("SetupIsInProgress");
|
|
|
|
TESTERR(hr, RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRKSetup, 0, KEY_READ,
|
|
&hkey));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DWORD cbData;
|
|
DWORD dwData;
|
|
|
|
// Are we in GUI mode setup?
|
|
cbData = sizeof(dwData);
|
|
dwData = 0;
|
|
dw = RegQueryValueExW(hkey, c_wszRVSetupNow, NULL, NULL,
|
|
(LPBYTE)&dwData, &cbData);
|
|
if (dw == ERROR_SUCCESS && dwData != 0)
|
|
{
|
|
retval = SIIP_GUI_SETUP;
|
|
|
|
// make certain this is not the OEM mini setup?
|
|
cbData = sizeof(dwData);
|
|
dwData = 0;
|
|
dw = RegQueryValueExW(hkey, c_wszRVMiniSetupNow, NULL, NULL,
|
|
(LPBYTE)&dwData, &cbData);
|
|
if (dw == ERROR_SUCCESS && dwData != 0)
|
|
retval=SIIP_OOBE_SETUP;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We are not in GUI mode, but it might be the OOBE movie
|
|
* or the activation that faulted, and we don't want the DW-UI
|
|
* then either as it will halt unattended setup.
|
|
*/
|
|
cbData = sizeof(dwData);
|
|
dwData = 0;
|
|
dw = RegQueryValueExW(hkey, c_wszRVOOBESetupNow, NULL, NULL,
|
|
(LPBYTE)&dwData, &cbData);
|
|
if (dw == ERROR_SUCCESS || dwData != 0)
|
|
retval=SIIP_OOBE_SETUP;
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
|
|
DBG_MSG(retval ? "Setup in progress" : "Setup not running");
|
|
return retval;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// string stuff
|
|
|
|
// ***************************************************************************
|
|
WCHAR *MyStrStrIW(const WCHAR *wcs1, const WCHAR *wcs2)
|
|
{
|
|
WCHAR *cp = (WCHAR *)wcs1;
|
|
WCHAR *s1, *s2;
|
|
|
|
while (*cp != '\0')
|
|
{
|
|
s1 = cp;
|
|
s2 = (WCHAR *) wcs2;
|
|
|
|
while (*s1 != '\0' && *s2 !='\0' && (towlower(*s1) - towlower(*s2)) == 0)
|
|
s1++, s2++;
|
|
|
|
if (*s2 == '\0')
|
|
return(cp);
|
|
|
|
cp++;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
CHAR *MyStrStrIA(const CHAR *cs1, const CHAR *cs2)
|
|
{
|
|
CHAR *cp = (CHAR *)cs1;
|
|
CHAR *s1, *s2;
|
|
|
|
while (*cp != '\0')
|
|
{
|
|
s1 = cp;
|
|
s2 = (CHAR *) cs2;
|
|
|
|
while (*s1 != '\0' && *s2 !='\0' && (tolower(*s1) - tolower(*s2)) == 0)
|
|
s1++, s2++;
|
|
|
|
if (*s2 == '\0')
|
|
return(cp);
|
|
|
|
cp++;
|
|
}
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// temp file stuff
|
|
|
|
// ***************************************************************************
|
|
BOOL DeleteTempDirAndFile(LPCWSTR wszPath, BOOL fFilePresent)
|
|
{
|
|
LPWSTR wszPathToDel = NULL, pwsz;
|
|
DWORD cchPath;
|
|
BOOL fRet = FALSE;
|
|
|
|
if (wszPath == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto done;
|
|
}
|
|
|
|
cchPath = wcslen(wszPath);
|
|
__try { wszPathToDel = (LPWSTR)_alloca((cchPath+1) * sizeof(WCHAR)); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszPathToDel = NULL; }
|
|
if (wszPathToDel == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto done;
|
|
}
|
|
|
|
StringCchCopyW(wszPathToDel, cchPath+1, wszPath);
|
|
|
|
// XXX can hang for up to 25m. In the case of a service, it could prevent its restart for 25m, thus creating a potential vulnerability.
|
|
// delete the actual file
|
|
if (fFilePresent)
|
|
{
|
|
if (!DeleteFileW(wszPathToDel))
|
|
{
|
|
int i=0;
|
|
|
|
while (i < 300)
|
|
{
|
|
if (!DeleteFileW(wszPathToDel))
|
|
{
|
|
Sleep(5000);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
i = 5000;
|
|
}
|
|
}
|
|
}
|
|
|
|
// next, delete the directory that we put it in
|
|
for(pwsz = wszPathToDel + cchPath - 1;
|
|
*pwsz != L'\\' && pwsz > wszPathToDel;
|
|
pwsz--);
|
|
if (*pwsz != L'\\' || pwsz <= wszPathToDel)
|
|
goto done;
|
|
}
|
|
else
|
|
{
|
|
pwsz = wszPathToDel + cchPath;
|
|
}
|
|
|
|
*pwsz = L'\0';
|
|
RemoveDirectoryW(wszPathToDel);
|
|
|
|
for(pwsz = pwsz - 1;
|
|
*pwsz != L'.' && pwsz > wszPathToDel;
|
|
pwsz--);
|
|
if (*pwsz == L'.' && pwsz > wszPathToDel)
|
|
{
|
|
*pwsz = L'\0';
|
|
DeleteFileW(wszPathToDel);
|
|
}
|
|
|
|
fRet = TRUE;
|
|
|
|
done:
|
|
return fRet;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
DWORD CreateTempDirAndFile(LPCWSTR wszTempDir, LPCWSTR wszName,
|
|
LPWSTR *pwszPath)
|
|
{
|
|
LPWSTR wszFilePath = NULL;
|
|
WCHAR *wszTemp = NULL;
|
|
DWORD cch = 0, cchDir = 0, iSuffix = 0, cSuffix = 0, cFilePathLength;
|
|
WCHAR wsz[1024];
|
|
BOOL fRet = FALSE;
|
|
|
|
if (pwszPath == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto done;
|
|
}
|
|
|
|
*pwszPath = NULL;
|
|
|
|
if (wszTempDir != NULL)
|
|
cch = wcslen(wszTempDir);
|
|
if (cch == 0)
|
|
{
|
|
cch = GetTempPathW(0, NULL);
|
|
if (cch == 0)
|
|
goto done;
|
|
}
|
|
|
|
// compute the size of the buffer for the string we're going
|
|
// to generate. The 20 includes the following:
|
|
// max size of the temp filename
|
|
// extra space for the NULL terminator.
|
|
cch += (16 + sizeofSTRW(c_wszDirSuffix));
|
|
if (wszName != NULL)
|
|
cch += wcslen(wszName);
|
|
|
|
// ok, so GetTempFileName likes to write MAX_PATH characters to the buffer,
|
|
// so make sure it's at least MAX_PATH in size...
|
|
cFilePathLength = cch = MyMax(cch, MAX_PATH + 1);
|
|
|
|
wszFilePath = (LPWSTR)MyAlloc(cch * sizeof(WCHAR));
|
|
if (wszFilePath == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto done;
|
|
}
|
|
|
|
if (wszTempDir != NULL && wszTempDir[0] != L'\0')
|
|
{
|
|
cch = wcslen(wszTempDir);
|
|
wszTemp = (LPWSTR)wszTempDir;
|
|
}
|
|
else
|
|
{
|
|
cch = GetTempPathW(cch, wszFilePath);
|
|
if (cch == 0)
|
|
goto done;
|
|
|
|
cch++;
|
|
|
|
// create the temp dir (in case it is not)
|
|
// ignoring the result (bug 526753)
|
|
CreateDirectoryW(wszFilePath, NULL);
|
|
|
|
__try { wszTemp = (WCHAR *)_alloca(cch * sizeof(WCHAR)); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszTemp = NULL; }
|
|
if (wszTemp == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
goto done;
|
|
}
|
|
|
|
StringCchCopyW(wszTemp, cch, wszFilePath);
|
|
}
|
|
cch = GetTempFileNameW(wszTemp, L"WER", 0, wszFilePath);
|
|
if (cch == 0)
|
|
goto done;
|
|
|
|
cch = wcslen(wszFilePath);
|
|
|
|
if (cch >= cFilePathLength)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
StringCchCopyW(&wszFilePath[cch], cFilePathLength - cch, c_wszDirSuffix);
|
|
|
|
// iSuffix points to the first digit of the '00' at the end of
|
|
// c_wszDirSuffix
|
|
iSuffix = cch + sizeofSTRW(c_wszDirSuffix) - 3;
|
|
cSuffix = 1;
|
|
do
|
|
{
|
|
fRet = CreateDirectoryW(wszFilePath, NULL);
|
|
if (fRet)
|
|
break;
|
|
|
|
wszFilePath[iSuffix] = L'0' + (WCHAR)(cSuffix / 10);
|
|
wszFilePath[iSuffix + 1] = L'0' + (WCHAR)(cSuffix % 10);
|
|
cSuffix++;
|
|
}
|
|
while (cSuffix <= 100);
|
|
|
|
// hmm, couldn't create the directory...
|
|
if (cSuffix > 100)
|
|
{
|
|
cchDir = cch;
|
|
cch = 0;
|
|
goto done;
|
|
}
|
|
|
|
|
|
cch += (sizeofSTRW(c_wszDirSuffix) - 1);
|
|
if (wszName != NULL && cch < cFilePathLength)
|
|
{
|
|
wszFilePath[cch++] = L'\\';
|
|
StringCchCopyW(&wszFilePath[cch], cFilePathLength - cch, wszName);
|
|
cch += wcslen(wszName);
|
|
}
|
|
|
|
*pwszPath = wszFilePath;
|
|
wszFilePath = NULL;
|
|
|
|
fRet = TRUE;
|
|
|
|
done:
|
|
if (wszFilePath != NULL)
|
|
{
|
|
if (cchDir > 0)
|
|
{
|
|
wszFilePath[cchDir] = L'\0';
|
|
DeleteFileW(wszFilePath);
|
|
}
|
|
MyFree(wszFilePath);
|
|
}
|
|
|
|
return cch;
|
|
}
|
|
|
|
BOOL
|
|
DeleteFullAndTriageMiniDumps(
|
|
LPCWSTR wszPath
|
|
)
|
|
//
|
|
// We create a FullMinidump file along with triage minidump in the same dir
|
|
// This routine cleans up both those files
|
|
//
|
|
{
|
|
LPWSTR wszFullMinidump = NULL;
|
|
DWORD cch;
|
|
BOOL fRet;
|
|
|
|
fRet = DeleteFileW(wszPath);
|
|
cch = wcslen(wszPath) + sizeofSTRW(c_wszHeapDumpSuffix);
|
|
__try { wszFullMinidump = (WCHAR *)_alloca(cch * sizeof(WCHAR)); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszFullMinidump = NULL; }
|
|
if (wszFullMinidump)
|
|
{
|
|
LPWSTR wszFileExt = NULL;
|
|
|
|
// Build Dump-with-heap path
|
|
StringCchCopyW(wszFullMinidump, cch, wszPath);
|
|
wszFileExt = wszFullMinidump + wcslen(wszFullMinidump) - sizeofSTRW(c_wszDumpSuffix) + 1;
|
|
if (!wcscmp(wszFileExt, c_wszDumpSuffix))
|
|
{
|
|
*wszFileExt = L'\0';
|
|
}
|
|
StringCchCatW(wszFullMinidump, cch, c_wszHeapDumpSuffix);
|
|
|
|
|
|
fRet = DeleteFileW(wszFullMinidump);
|
|
|
|
} else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// File mapping
|
|
|
|
// **************************************************************************
|
|
HRESULT OpenFileMapped(LPWSTR wszFile, LPVOID *ppvFile, DWORD *pcbFile)
|
|
{
|
|
USE_TRACING("OpenFileMapped");
|
|
|
|
HRESULT hr = NOERROR;
|
|
HANDLE hMMF = NULL;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
LPVOID pvFile = NULL;
|
|
DWORD cbFile = 0;
|
|
|
|
VALIDATEPARM(hr, (wszFile == NULL || ppvFile == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
*ppvFile = NULL;
|
|
if (pcbFile != NULL)
|
|
*pcbFile = 0;
|
|
|
|
hFile = CreateFileW(wszFile, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, 0, NULL);
|
|
TESTBOOL(hr, (hFile != INVALID_HANDLE_VALUE));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
cbFile = GetFileSize(hFile, NULL);
|
|
TESTBOOL(hr, (cbFile != (DWORD)-1));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
hMMF = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, cbFile, NULL);
|
|
TESTBOOL(hr, (hMMF != NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
pvFile = MapViewOfFile(hMMF, FILE_MAP_READ, 0, 0, 0);
|
|
TESTBOOL(hr, (pvFile != NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
*ppvFile = pvFile;
|
|
if (pcbFile != NULL)
|
|
*pcbFile = cbFile;
|
|
|
|
done:
|
|
if (hMMF != NULL)
|
|
CloseHandle(hMMF);
|
|
if (hFile != NULL)
|
|
CloseHandle(hFile);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// **************************************************************************
|
|
HRESULT DeleteTempFile(LPWSTR wszFile)
|
|
{
|
|
USE_TRACING("DeleteTempFile");
|
|
|
|
HRESULT hr = NOERROR;
|
|
WCHAR *pwsz;
|
|
|
|
if (wszFile == NULL)
|
|
return NOERROR;
|
|
|
|
// strip off the extension at the end (if it's not a .tmp)
|
|
for(pwsz = wszFile + wcslen(wszFile); *pwsz != L'.' && pwsz > wszFile; pwsz--);
|
|
if (pwsz > wszFile && _wcsicmp(pwsz, L".tmp") != 0)
|
|
*pwsz = L'\0';
|
|
|
|
if (DeleteFileW(wszFile) == FALSE)
|
|
hr = Err2HR(GetLastError());
|
|
|
|
// can do this even if the extension was a tmp since the value pointed to
|
|
// by pwsz is '.' if it's greater than wszFile...
|
|
if (pwsz > wszFile)
|
|
*pwsz = L'.';
|
|
|
|
return hr;
|
|
}
|
|
|
|
// **************************************************************************
|
|
HRESULT MyCallNamedPipe(LPCWSTR wszPipe, LPVOID pvIn, DWORD cbIn,
|
|
LPVOID pvOut, DWORD cbOut, DWORD *pcbRead,
|
|
DWORD dwWaitPipe, DWORD dwWaitRead)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
HANDLE hPipe = INVALID_HANDLE_VALUE;
|
|
HANDLE hev = NULL;
|
|
DWORD dwStart = GetTickCount(), dwNow, dw;
|
|
BOOL fRet;
|
|
|
|
USE_TRACING("MyCallNamedPipe");
|
|
|
|
VALIDATEPARM(hr, (wszPipe == NULL || pvIn == NULL || pvOut == NULL || pcbRead == NULL));
|
|
if (FAILED(hr))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
hr = E_INVALIDARG;
|
|
goto done;
|
|
}
|
|
|
|
*pcbRead = 0;
|
|
|
|
for(;;)
|
|
{
|
|
hPipe = CreateFileW(wszPipe, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED | SECURITY_IDENTIFICATION |
|
|
SECURITY_SQOS_PRESENT | SECURITY_CONTEXT_TRACKING,
|
|
NULL);
|
|
if (hPipe != INVALID_HANDLE_VALUE)
|
|
break;
|
|
|
|
// if we get ACCESS_DENIED to the above, then WaitNamedPipe will
|
|
// return SUCCESS, so we get stuck until the timeout expires. Better
|
|
// to just bail now.
|
|
if (GetLastError() == ERROR_ACCESS_DENIED)
|
|
goto done;
|
|
|
|
TESTBOOL(hr, WaitNamedPipeW(wszPipe, dwWaitPipe));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
dwNow = GetTickCount();
|
|
if (dwNow < dwStart)
|
|
dw = ((DWORD)-1 - dwStart) + dwNow;
|
|
else
|
|
dw = dwNow - dwStart;
|
|
if (dw >= dwWaitPipe)
|
|
dwWaitPipe = 0;
|
|
else
|
|
dwWaitPipe -= dw;
|
|
|
|
if (dwWaitPipe == 0)
|
|
{
|
|
SetLastError(ERROR_TIMEOUT);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
__try
|
|
{
|
|
OVERLAPPED ol;
|
|
DWORD dwMode = PIPE_READMODE_MESSAGE | PIPE_WAIT;
|
|
DWORD cbRead = 0;
|
|
|
|
// Default open is readmode byte stream- change to message mode.
|
|
TESTBOOL(hr, SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL))
|
|
if (FAILED(hr))
|
|
__leave;
|
|
|
|
// we need an event for the overlapped structure
|
|
hev = CreateEventW(NULL, TRUE, FALSE, NULL);
|
|
TESTBOOL(hr, (hev != NULL));
|
|
if (FAILED(hr))
|
|
__leave;
|
|
|
|
// populate the overlapped stuff
|
|
ZeroMemory(&ol, sizeof(ol));
|
|
ol.hEvent = hev;
|
|
|
|
fRet = TransactNamedPipe(hPipe, pvIn, cbIn, pvOut, cbOut, &cbRead,
|
|
&ol);
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
if (fRet)
|
|
{
|
|
SetEvent(hev);
|
|
}
|
|
else
|
|
{
|
|
hr = Err2HR(GetLastError());
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
dw = WaitForSingleObject(hev, dwWaitRead);
|
|
if (dw != WAIT_OBJECT_0)
|
|
{
|
|
hr = (dw == WAIT_TIMEOUT) ? Err2HR(WAIT_TIMEOUT) :
|
|
Err2HR(GetLastError());
|
|
__leave;
|
|
}
|
|
|
|
TESTBOOL(hr, GetOverlappedResult(hPipe, &ol, &cbRead, FALSE));
|
|
if (FAILED(hr))
|
|
__leave;
|
|
|
|
*pcbRead = cbRead;
|
|
|
|
hr = NOERROR;
|
|
|
|
}
|
|
__finally
|
|
{
|
|
}
|
|
|
|
done:
|
|
dw = GetLastError();
|
|
|
|
if (hPipe != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hPipe);
|
|
if (hev != NULL)
|
|
CloseHandle(hev);
|
|
|
|
SetLastError(dw);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Security stuff
|
|
|
|
// ***************************************************************************
|
|
#define MEMBER_ACCESS 1
|
|
BOOL IsUserAnAdmin(HANDLE hToken)
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
|
|
SECURITY_DESCRIPTOR *psdAdm = NULL;
|
|
GENERIC_MAPPING gm;
|
|
PRIVILEGE_SET *pPS;
|
|
HANDLE hTokenImp = NULL;
|
|
DWORD cbSD, cbPS, dwGranted = 0;
|
|
BYTE rgBuf[sizeof(PRIVILEGE_SET) + 3 * sizeof(LUID_AND_ATTRIBUTES)];
|
|
BOOL fRet = FALSE, fStatus;
|
|
PSID psidAdm = NULL;
|
|
PACL pACL = NULL;
|
|
HRESULT hr;
|
|
ULONG IsMember;
|
|
USE_TRACING("IsUserAnAdmin");
|
|
|
|
gm.GenericRead = GENERIC_READ;
|
|
gm.GenericWrite = GENERIC_WRITE;
|
|
gm.GenericExecute = GENERIC_EXECUTE;
|
|
gm.GenericAll = GENERIC_ALL;
|
|
pPS = (PRIVILEGE_SET *)rgBuf;
|
|
cbPS = sizeof(rgBuf);
|
|
|
|
// AccessCheck() reqires an impersonation token...
|
|
TESTBOOL(hr, DuplicateToken(hToken, SecurityImpersonation, &hTokenImp));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// construct a SID that contains the administrator's group.
|
|
TESTBOOL(hr, AllocateAndInitializeSid(&sia, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
|
|
0, &psidAdm));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
#if 0
|
|
// XXX - a simpler way??
|
|
if (CheckTokenMembership(hToken, psidAdm, &IsMember))
|
|
{
|
|
return IsMember;
|
|
}
|
|
#endif
|
|
|
|
cbSD = sizeof(SECURITY_DESCRIPTOR) + sizeof(ACCESS_ALLOWED_ACE) +
|
|
sizeof(ACL) + 3 * GetLengthSid(psidAdm);
|
|
|
|
__try { psdAdm = (SECURITY_DESCRIPTOR *)_alloca(cbSD); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { psdAdm = NULL; }
|
|
if (psdAdm == NULL)
|
|
goto done;
|
|
|
|
ZeroMemory(psdAdm, cbSD);
|
|
pACL = (PACL)(psdAdm + 1);
|
|
|
|
TESTBOOL(hr, InitializeSecurityDescriptor(psdAdm, SECURITY_DESCRIPTOR_REVISION));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
TESTBOOL(hr, SetSecurityDescriptorOwner(psdAdm, psidAdm, FALSE));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
TESTBOOL(hr, SetSecurityDescriptorGroup(psdAdm, psidAdm, FALSE));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
TESTBOOL(hr, InitializeAcl(pACL, cbSD - sizeof(SECURITY_DESCRIPTOR), ACL_REVISION));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
TESTBOOL(hr, AddAccessAllowedAce(pACL, ACL_REVISION, MEMBER_ACCESS, psidAdm));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
TESTBOOL(hr, SetSecurityDescriptorDacl(psdAdm, TRUE, pACL, FALSE));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
TESTBOOL(hr, AccessCheck(psdAdm, hTokenImp, MEMBER_ACCESS, &gm, pPS, &cbPS,
|
|
&dwGranted, &fStatus));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
fRet = (fStatus && dwGranted == MEMBER_ACCESS);
|
|
|
|
done:
|
|
if (psidAdm != NULL)
|
|
FreeSid(psidAdm);
|
|
if (hTokenImp != NULL)
|
|
CloseHandle(hTokenImp);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
// ***************************************************************************
|
|
BOOL AllocSD(SECURITY_DESCRIPTOR *psd, DWORD dwOLs, DWORD dwAd, DWORD dwWA)
|
|
{
|
|
SID_IDENTIFIER_AUTHORITY siaCreate = SECURITY_CREATOR_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY siaNT = SECURITY_NT_AUTHORITY;
|
|
DWORD cb, dw;
|
|
PACL pacl = NULL;
|
|
PSID psidOwner = NULL;
|
|
PSID psidLS = NULL;
|
|
PSID psidWorld = NULL;
|
|
PSID psidAnon = NULL;
|
|
PSID psidAdm = NULL;
|
|
BOOL fRet = FALSE;
|
|
|
|
if (psd == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto done;
|
|
}
|
|
|
|
fRet = InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
|
|
// get the SID for local system acct
|
|
fRet = AllocateAndInitializeSid(&siaNT, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0,
|
|
0, 0, 0, 0, 0, &psidLS);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
// get the SID for the creator
|
|
fRet = AllocateAndInitializeSid(&siaCreate, 1, SECURITY_CREATOR_OWNER_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &psidOwner);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
cb = sizeof(ACL) + GetLengthSid(psidLS) + GetLengthSid(psidOwner) +
|
|
2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD));
|
|
|
|
// if we have an access mask to apply for the administrators group, then
|
|
// we need it's SID.
|
|
if (dwAd != 0)
|
|
{
|
|
// get the SID for the local administrators group
|
|
fRet = AllocateAndInitializeSid(&siaNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
|
|
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
|
|
0, &psidAdm);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
cb += (GetLengthSid(psidAdm) + sizeof(ACCESS_ALLOWED_ACE) -
|
|
sizeof(DWORD));
|
|
}
|
|
|
|
// if we have an access mask to apply for world / anonymous, then we need
|
|
// their SIDs
|
|
if (dwWA != 0)
|
|
{
|
|
// get the SID for the world (everyone)
|
|
fRet = AllocateAndInitializeSid(&siaNT, 1, SECURITY_ANONYMOUS_LOGON_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &psidWorld);
|
|
|
|
|
|
// get the SID for the anonymous users acct
|
|
fRet = AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID,
|
|
0, 0, 0, 0, 0, 0, 0, &psidAnon);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
cb += GetLengthSid(psidWorld) + GetLengthSid(psidAnon) +
|
|
2 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD));
|
|
}
|
|
|
|
// make the DACL
|
|
pacl = (PACL)MyAlloc(cb);
|
|
if (pacl == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
fRet = FALSE;
|
|
goto done;
|
|
}
|
|
|
|
fRet = InitializeAcl(pacl, cb, ACL_REVISION);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
fRet = AddAccessAllowedAce(pacl, ACL_REVISION, dwOLs, psidOwner);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
fRet = AddAccessAllowedAce(pacl, ACL_REVISION, dwOLs, psidLS);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
// if we have an administrator access mask, then apply it
|
|
if (dwAd != 0)
|
|
{
|
|
fRet = AddAccessAllowedAce(pacl, ACL_REVISION, dwAd, psidAdm);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
}
|
|
|
|
// if we have a world / anonymous access mask, then apply it
|
|
if (dwWA != 0)
|
|
{
|
|
fRet = AddAccessAllowedAce(pacl, ACL_REVISION, dwWA, psidWorld);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
fRet = AddAccessAllowedAce(pacl, ACL_REVISION, dwWA, psidAnon);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
}
|
|
|
|
// set the SD dacl
|
|
fRet = SetSecurityDescriptorDacl(psd, TRUE, pacl, FALSE);
|
|
if (fRet == FALSE)
|
|
goto done;
|
|
|
|
pacl = NULL;
|
|
|
|
done:
|
|
dw = GetLastError();
|
|
|
|
if (psidLS != NULL)
|
|
FreeSid(psidLS);
|
|
if (psidWorld != NULL)
|
|
FreeSid(psidWorld);
|
|
if (psidAnon != NULL)
|
|
FreeSid(psidAnon);
|
|
if (psidAdm != NULL)
|
|
FreeSid(psidAdm);
|
|
if (psidOwner != NULL)
|
|
FreeSid(psidOwner);
|
|
if (pacl != NULL)
|
|
MyFree(pacl);
|
|
|
|
SetLastError(dw);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void FreeSD(SECURITY_DESCRIPTOR *psd)
|
|
{
|
|
PSID psid = NULL;
|
|
PACL pacl = NULL;
|
|
BOOL f, f2;
|
|
|
|
if (psd == NULL)
|
|
return;
|
|
|
|
if (GetSecurityDescriptorDacl(psd, &f, &pacl, &f2) && pacl != NULL)
|
|
MyFree(pacl);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Registry stuff
|
|
|
|
// **************************************************************************
|
|
HRESULT OpenRegKey(HKEY hkeyMain, LPCWSTR wszSubKey, DWORD dwOpt,
|
|
HKEY *phkey)
|
|
{
|
|
USE_TRACING("OpenRegKey");
|
|
|
|
HRESULT hr = NOERROR;
|
|
REGSAM samDesired;
|
|
DWORD dwErr;
|
|
|
|
VALIDATEPARM(hr, (hkeyMain == NULL || wszSubKey == NULL || phkey == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
*phkey = NULL;
|
|
|
|
samDesired = ((dwOpt & orkWantWrite) != 0) ? KEY_ALL_ACCESS : KEY_READ;
|
|
samDesired |= ((dwOpt & orkUseWOW64) != 0) ? KEY_WOW64_32KEY : KEY_WOW64_64KEY;
|
|
|
|
// first try calling RegCreateKeyEx to make sure we create the key if
|
|
// it doesn't exist
|
|
TESTERR(hr, RegCreateKeyExW(hkeyMain, wszSubKey, 0, NULL, 0, samDesired,
|
|
NULL, phkey, NULL));
|
|
if (FAILED(hr))
|
|
{
|
|
// ok, that didn't work, so try opening the key instead
|
|
TESTERR(hr, RegOpenKeyExW(hkeyMain, wszSubKey, 0, samDesired, phkey));
|
|
}
|
|
|
|
ErrorTrace(0, "OpenRegKey = [%S], %s", wszSubKey, FAILED(hr) ? "fail": "success");
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
// **************************************************************************
|
|
HRESULT ReadRegEntry(HKEY hkey, LPCWSTR wszValName, DWORD *pdwType,
|
|
PBYTE pbBuffer, DWORD *pcbBuffer, PBYTE pbDefault,
|
|
DWORD cbDefault)
|
|
{
|
|
USE_TRACING("ReadRegEntry");
|
|
|
|
HRESULT hr = NOERROR;
|
|
DWORD dwErr;
|
|
|
|
VALIDATEPARM(hr, (hkey == NULL || wszValName == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// ErrorTrace(0, "ReadRegEntry = %S", wszValName);
|
|
|
|
dwErr = RegQueryValueExW(hkey, wszValName, 0, pdwType, pbBuffer,
|
|
pcbBuffer);
|
|
VALIDATEEXPR(hr, (dwErr != ERROR_PATH_NOT_FOUND &&
|
|
dwErr != ERROR_FILE_NOT_FOUND &&
|
|
dwErr != ERROR_SUCCESS), Err2HR(dwErr));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
if (dwErr != ERROR_SUCCESS && pbDefault != NULL)
|
|
{
|
|
VALIDATEPARM(hr, (pcbBuffer == NULL && pbBuffer != NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// if the receiving buffer is NULL, just return the error that
|
|
// RegQueryValueEx gave us cuz the user doesn't really want the
|
|
// value anyway
|
|
VALIDATEEXPR(hr, (pcbBuffer == NULL), Err2HR(dwErr));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
if (pbBuffer == NULL)
|
|
{
|
|
*pcbBuffer = cbDefault;
|
|
hr = NOERROR;
|
|
goto done;
|
|
}
|
|
else if (cbDefault > *pcbBuffer)
|
|
{
|
|
*pcbBuffer = cbDefault;
|
|
hr = Err2HR(ERROR_MORE_DATA);
|
|
goto done;
|
|
}
|
|
|
|
CopyMemory(pbBuffer, pbDefault, cbDefault);
|
|
*pcbBuffer = cbDefault;
|
|
if (pdwType != NULL)
|
|
*pdwType = REG_BINARY;
|
|
|
|
hr = NOERROR;
|
|
goto done;
|
|
}
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
// **************************************************************************
|
|
HRESULT ReadRegEntry(HKEY *rghkey, DWORD cKeys, LPCWSTR wszValName,
|
|
DWORD *pdwType, PBYTE pbBuffer, DWORD *pcbBuffer,
|
|
PBYTE pbDefault, DWORD cbDefault, DWORD *piKey)
|
|
{
|
|
USE_TRACING("ReadRegEntryPolicy");
|
|
|
|
HRESULT hr = NOERROR;
|
|
DWORD dwErr=ERROR_SUCCESS, i;
|
|
|
|
VALIDATEPARM(hr, (rghkey == NULL || wszValName == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// ErrorTrace(0, "ReadRegEntryPolicy = %S", wszValName);
|
|
|
|
for(i = 0; i < cKeys; i++)
|
|
{
|
|
dwErr = RegQueryValueExW(rghkey[i], wszValName, 0, pdwType, pbBuffer,
|
|
pcbBuffer);
|
|
VALIDATEEXPR(hr, (dwErr != ERROR_PATH_NOT_FOUND &&
|
|
dwErr != ERROR_FILE_NOT_FOUND &&
|
|
dwErr != ERROR_SUCCESS), Err2HR(dwErr));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
if (dwErr == ERROR_SUCCESS)
|
|
{
|
|
if (piKey != NULL)
|
|
*piKey = i;
|
|
|
|
// ErrorTrace(0, " found value [0x%x] in %s", (DWORD*) *pbDefault, i?"registry" : "policy");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dwErr != ERROR_SUCCESS && pbDefault != NULL)
|
|
{
|
|
VALIDATEPARM(hr, (pcbBuffer == NULL && pbBuffer != NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// if the receiving buffer is NULL, just return the error that
|
|
// RegQueryValueEx gave us cuz the user doesn't really want the
|
|
// value anyway
|
|
VALIDATEEXPR(hr, (pcbBuffer == NULL), Err2HR(dwErr));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
if (pbBuffer == NULL)
|
|
{
|
|
*pcbBuffer = cbDefault;
|
|
hr = NOERROR;
|
|
goto done;
|
|
}
|
|
else if (cbDefault > *pcbBuffer)
|
|
{
|
|
*pcbBuffer = cbDefault;
|
|
hr = Err2HR(ERROR_MORE_DATA);
|
|
goto done;
|
|
}
|
|
|
|
CopyMemory(pbBuffer, pbDefault, cbDefault);
|
|
*pcbBuffer = cbDefault;
|
|
if (pdwType != NULL)
|
|
*pdwType = REG_BINARY;
|
|
|
|
if (piKey != NULL)
|
|
*piKey = cKeys;
|
|
|
|
hr = NOERROR;
|
|
// ErrorTrace(0, " not found, applying default [0x%x]", (DWORD*) *pbDefault);
|
|
goto done;
|
|
}
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// version info stuff
|
|
|
|
// **************************************************************************
|
|
DWORD IsMicrosoftApp(LPWSTR wszAppPath, PBYTE pbAppInfo, DWORD cbAppInfo)
|
|
{
|
|
USE_TRACING("IsMicrosoftApp");
|
|
|
|
SLangCodepage *plc;
|
|
HRESULT hr = NOERROR;
|
|
LPWSTR pwszName, pwszNameK32, wszModK32;
|
|
WCHAR wszQueryString[128];
|
|
DWORD cbFVI, cbFVIK32, dwJunk, dwRet = 0;
|
|
PBYTE pbFVI = NULL, pbFVIK32 = NULL;
|
|
UINT cb, cbVerInfo, i, cchNeed, cch;
|
|
|
|
VALIDATEPARM(hr, (wszAppPath == NULL &&
|
|
(pbAppInfo == NULL || cbAppInfo == 0)));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
if (pbAppInfo == NULL)
|
|
{
|
|
// dwJunk is a useful parameter. Gotta pass it in so the function call
|
|
// set it to 0. Gee this would make a great (tho non-efficient)
|
|
// way to set DWORDs to 0. Much better than saying dwJunk = 0 by itself.
|
|
cbFVI = GetFileVersionInfoSizeW(wszAppPath, &dwJunk);
|
|
TESTBOOL(hr, (cbFVI != 0))
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace(0, " failed to find module \'%s\', hr=", wszAppPath, hr);
|
|
// if it fails, assume the file doesn't have any version info &
|
|
// return S_FALSE
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
// alloca only throws exceptions so gotta catch 'em here....
|
|
__try { pbFVI = (PBYTE)_alloca(cbFVI); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { pbFVI = NULL; }
|
|
VALIDATEEXPR(hr, (pbFVI == NULL), E_OUTOFMEMORY);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
cb = cbFVI;
|
|
TESTBOOL(hr, GetFileVersionInfoW(wszAppPath, 0, cbFVI, (LPVOID *)pbFVI));
|
|
if (FAILED(hr))
|
|
{
|
|
// if it fails, assume the file doesn't have any version info &
|
|
// return S_FALSE
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pbFVI = pbAppInfo;
|
|
cbFVI = cbAppInfo;
|
|
}
|
|
|
|
// get the info for kernel32.dll
|
|
cchNeed = GetSystemDirectoryW(NULL, 0);
|
|
if (cchNeed == 0)
|
|
goto done;
|
|
|
|
cchNeed += (sizeofSTRW(L"\\kernel32.dll") + 1);
|
|
__try { wszModK32 = (LPWSTR)_alloca(cchNeed * sizeof(WCHAR)); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszModK32 = NULL; }
|
|
VALIDATEEXPR(hr, (wszModK32 == NULL), E_OUTOFMEMORY);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// get the info for kernel32.dll
|
|
cch = GetSystemDirectoryW(wszModK32, cchNeed);
|
|
if (cch == 0)
|
|
goto done;
|
|
if (*(wszModK32 + cch - 1) == L'\\')
|
|
*(wszModK32 + cch - 1) = L'\0';
|
|
StringCchCatW(wszModK32, cchNeed, L"\\kernel32.dll");
|
|
|
|
|
|
// dwJunk is a useful parameter. Gotta pass it in so the function call
|
|
// set it to 0. Gee this would make a great (tho non-efficient)
|
|
// way to set DWORDs to 0. Much better than saying dwJunk = 0 by itself.
|
|
cbFVIK32 = GetFileVersionInfoSizeW(wszModK32, &dwJunk);
|
|
TESTBOOL(hr, (cbFVIK32 != 0));
|
|
if (FAILED(hr))
|
|
{
|
|
ErrorTrace(0, " failed to find module \'%s\', hr=", wszAppPath, hr);
|
|
// if it fails, assume the file doesn't have any version info &
|
|
// return S_FALSE
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
// alloca only throws exceptions so gotta catch 'em here....
|
|
__try { pbFVIK32 = (PBYTE)_alloca(cbFVIK32); }
|
|
__except(EXCEPTION_STACK_OVERFLOW == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { pbFVIK32 = NULL; }
|
|
VALIDATEEXPR(hr, (pbFVIK32 == NULL), E_OUTOFMEMORY);
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
cb = cbFVI;
|
|
TESTBOOL(hr, GetFileVersionInfoW(wszModK32, 0, cbFVIK32, (LPVOID *)pbFVIK32));
|
|
if (FAILED(hr))
|
|
{
|
|
// if it fails, assume the file doesn't have any version info &
|
|
// return S_FALSE
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
// Ok, since we can have any number of languages in the module, gotta
|
|
// grep thru all of them & see if the company name field includes
|
|
// 'Microsoft'.
|
|
TESTBOOL(hr, VerQueryValueW(pbFVI, L"\\VarFileInfo\\Translation",
|
|
(LPVOID *)&plc, &cbVerInfo));
|
|
if (FAILED(hr))
|
|
{
|
|
// if it fails, assume the file doesn't have any version info &
|
|
// return S_FALSE
|
|
hr = S_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
// Read the file description for each language and code page.
|
|
for(i = 0; i < (cbVerInfo / sizeof(SLangCodepage)); i++)
|
|
{
|
|
StringCchPrintfW(wszQueryString, sizeof(wszQueryString)/sizeof(WCHAR),
|
|
L"\\StringFileInfo\\%04x%04x\\CompanyName",
|
|
plc[i].wLanguage, plc[i].wCodePage);
|
|
|
|
// Retrieve file description for language and code page "i".
|
|
TESTBOOL(hr, VerQueryValueW(pbFVI, wszQueryString,
|
|
(LPVOID *)&pwszName, &cb));
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
// see if the string contains the word 'Microsoft'
|
|
if (MyStrStrIW(pwszName, L"Microsoft") != NULL)
|
|
{
|
|
dwRet |= APP_MSAPP;
|
|
goto doneCompany;
|
|
}
|
|
|
|
// ok, didn't match the word 'Microsoft', so instead, see if it matches
|
|
// the string in kernel32.dll
|
|
TESTBOOL(hr, VerQueryValueW(pbFVIK32, wszQueryString,
|
|
(LPVOID *)&pwszNameK32, &cb));
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
if (CompareStringW(MAKELCID(plc[i].wLanguage, SORT_DEFAULT),
|
|
NORM_IGNORECASE | NORM_IGNOREKANATYPE |
|
|
NORM_IGNOREWIDTH | SORT_STRINGSORT,
|
|
pwszName, -1, pwszNameK32, -1) == CSTR_EQUAL)
|
|
dwRet |= APP_MSAPP;
|
|
else
|
|
continue;
|
|
|
|
doneCompany:
|
|
StringCchPrintfW(wszQueryString, sizeof(wszQueryString)/sizeof(WCHAR),
|
|
L"\\StringFileInfo\\%04x%04x\\ProductName",
|
|
plc[i].wLanguage, plc[i].wCodePage);
|
|
|
|
// Retrieve file description for language and code page "i".
|
|
TESTBOOL(hr, VerQueryValueW(pbFVI, wszQueryString,
|
|
(LPVOID *)&pwszName, &cb));
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
// see if the string contains the words 'Microsoft® Windows®'
|
|
if (MyStrStrIW(pwszName, L"Microsoft® Windows®") != NULL)
|
|
{
|
|
dwRet |= APP_WINCOMP;
|
|
break;
|
|
}
|
|
|
|
// ok, didn't match the words 'Microsoft® Windows®', so instead, see if
|
|
// it matches the string in kernel32.dll
|
|
TESTBOOL(hr, VerQueryValueW(pbFVIK32, wszQueryString,
|
|
(LPVOID *)&pwszNameK32, &cb));
|
|
if (FAILED(hr))
|
|
continue;
|
|
|
|
if (CompareStringW(MAKELCID(plc[i].wLanguage, SORT_DEFAULT),
|
|
NORM_IGNORECASE | NORM_IGNOREKANATYPE |
|
|
NORM_IGNOREWIDTH | SORT_STRINGSORT,
|
|
pwszName, -1, pwszNameK32, -1) == CSTR_EQUAL)
|
|
{
|
|
dwRet |= APP_WINCOMP;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
hr = S_FALSE;
|
|
|
|
done:
|
|
ErrorTrace(0, "results for module \'%S\', dwRet=%d", wszAppPath, dwRet);
|
|
return dwRet;
|
|
}
|
|
|