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.
1344 lines
28 KiB
1344 lines
28 KiB
// Copyright (c) 2001 Microsoft Corporation
|
|
//
|
|
// File: common.cpp
|
|
//
|
|
// Synopsis: Commonly used functions
|
|
//
|
|
// History: 02/03/2001 JeffJon Created
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
bool
|
|
IsServiceInstalledHelper(const wchar_t* serviceName)
|
|
{
|
|
LOG_FUNCTION2(IsServiceInstalledHelper, serviceName);
|
|
ASSERT(serviceName);
|
|
|
|
// if we can open the service, then it is installed
|
|
|
|
bool result = false;
|
|
|
|
SC_HANDLE hsc =
|
|
::OpenSCManager(0, SERVICES_ACTIVE_DATABASE, GENERIC_READ);
|
|
|
|
if (hsc)
|
|
{
|
|
SC_HANDLE hs = ::OpenServiceW(hsc, serviceName, GENERIC_READ);
|
|
|
|
if (hs)
|
|
{
|
|
::CloseServiceHandle(hs);
|
|
result = true;
|
|
}
|
|
|
|
::CloseServiceHandle(hsc);
|
|
}
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
// Wait for a handle to become signalled, or a timeout to expire, or WM_QUIT
|
|
// to appear in the message queue. Pump the message queue while we wait.
|
|
//
|
|
// WARNING: UI should diable itself before calling any function that invokes
|
|
// this function, or functions calling this one should guard against
|
|
// re-entrance. Otherwise there will be a re-entrancy problem.
|
|
//
|
|
// e.g. command handler gets button clicked message, calls a func that calls
|
|
// this wait function, then user clicks the button again, command handler call
|
|
// a func that calls this one, and so on.
|
|
|
|
DWORD
|
|
MyWaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
|
|
{
|
|
LOG_FUNCTION(MyWaitForSendMessageThread);
|
|
ASSERT(hThread);
|
|
|
|
MSG msg;
|
|
DWORD dwRet;
|
|
DWORD dwEnd = GetTickCount() + dwTimeout;
|
|
bool quit = false;
|
|
|
|
// We will attempt to wait up to dwTimeout for the thread to
|
|
// terminate
|
|
|
|
do
|
|
{
|
|
dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
|
|
dwTimeout, QS_ALLEVENTS | QS_SENDMESSAGE );
|
|
|
|
if (dwRet == (WAIT_OBJECT_0 + 1))
|
|
{
|
|
// empty out the message queue. We call DispatchMessage to
|
|
// ensure that we still process the WM_PAINT messages.
|
|
// DANGER: Make sure that the CYS UI is completely disabled
|
|
// or there will be re-entrancy problems here
|
|
|
|
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
// Need to re-post this so that we know to close CYS
|
|
|
|
::PostMessage(msg.hwnd, WM_QUIT, 0, 0);
|
|
quit = true;
|
|
break;
|
|
}
|
|
::TranslateMessage(&msg);
|
|
::DispatchMessage(&msg);
|
|
}
|
|
|
|
// Calculate if we have any more time left in the timeout to
|
|
// wait on.
|
|
|
|
if (dwTimeout != INFINITE)
|
|
{
|
|
dwTimeout = dwEnd - GetTickCount();
|
|
if ((long)dwTimeout <= 0)
|
|
{
|
|
// No more time left, fail with WAIT_TIMEOUT
|
|
dwRet = WAIT_TIMEOUT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED
|
|
// The thread must have exited, so we are happy
|
|
//
|
|
// dwRet == WAIT_TIMEOUT
|
|
// The thread is taking too long to finish, so just
|
|
// return and let the caller kill it
|
|
|
|
} while (dwRet == (WAIT_OBJECT_0 + 1) && !quit);
|
|
|
|
return(dwRet);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CreateAndWaitForProcess(
|
|
const String& fullPath,
|
|
String& commandLine,
|
|
DWORD& exitCode,
|
|
bool minimize)
|
|
{
|
|
LOG_FUNCTION2(CreateAndWaitForProcess, fullPath);
|
|
LOG(commandLine);
|
|
ASSERT(!fullPath.empty());
|
|
|
|
exitCode = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
PROCESS_INFORMATION procInfo;
|
|
memset(&procInfo, 0, sizeof(procInfo));
|
|
|
|
STARTUPINFO startup;
|
|
memset(&startup, 0, sizeof(startup));
|
|
|
|
if (minimize)
|
|
{
|
|
LOG(L"Starting minimized");
|
|
startup.dwFlags = STARTF_USESHOWWINDOW;
|
|
startup.wShowWindow = SW_MINIMIZE;
|
|
}
|
|
|
|
LOG(L"Calling CreateProcess");
|
|
LOG(fullPath);
|
|
LOG(commandLine);
|
|
|
|
hr =
|
|
Win::CreateProcess(
|
|
fullPath,
|
|
commandLine,
|
|
0,
|
|
String(),
|
|
startup,
|
|
procInfo);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
ASSERT(procInfo.hProcess);
|
|
|
|
DWORD dwRet = MyWaitForSendMessageThread(procInfo.hProcess, INFINITE);
|
|
|
|
ASSERT(dwRet == WAIT_OBJECT_0);
|
|
|
|
hr = Win::GetExitCodeProcess(procInfo.hProcess, exitCode);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
Win::CloseHandle(procInfo.hThread);
|
|
Win::CloseHandle(procInfo.hProcess);
|
|
}
|
|
while (0);
|
|
|
|
LOG(String::format(L"exit code = %1!x!", exitCode));
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
MyCreateProcess(
|
|
const String& fullPath,
|
|
String& commandline)
|
|
{
|
|
LOG_FUNCTION2(MyCreateProcess, fullPath);
|
|
LOG(commandline);
|
|
ASSERT(!fullPath.empty());
|
|
|
|
HRESULT hr = S_OK;
|
|
do
|
|
{
|
|
PROCESS_INFORMATION procInfo;
|
|
memset(&procInfo, 0, sizeof(procInfo));
|
|
|
|
STARTUPINFO startup;
|
|
memset(&startup, 0, sizeof(startup));
|
|
|
|
|
|
LOG(L"Calling CreateProcess");
|
|
|
|
hr =
|
|
Win::CreateProcess(
|
|
fullPath,
|
|
commandline,
|
|
0,
|
|
String(),
|
|
startup,
|
|
procInfo);
|
|
BREAK_ON_FAILED_HRESULT(hr);
|
|
|
|
ASSERT(procInfo.hProcess);
|
|
|
|
Win::CloseHandle(procInfo.hThread);
|
|
Win::CloseHandle(procInfo.hProcess);
|
|
}
|
|
while (0);
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
bool
|
|
IsKeyValuePresent(RegistryKey& key, const String& valueKey)
|
|
{
|
|
LOG_FUNCTION(IsKeyValuePresent);
|
|
|
|
bool result = false;
|
|
|
|
do
|
|
{
|
|
|
|
String value;
|
|
HRESULT hr = key.GetValue(valueKey, value);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to read regkey %1 because: hr = %2!x!",
|
|
valueKey,
|
|
hr));
|
|
break;
|
|
}
|
|
|
|
if (!value.empty())
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
} while (false);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
bool
|
|
GetRegKeyValue(
|
|
const String& keyName,
|
|
const String& value,
|
|
String& resultString,
|
|
HKEY parentKey)
|
|
{
|
|
LOG_FUNCTION(GetRegKeyValue);
|
|
|
|
bool result = true;
|
|
|
|
do
|
|
{
|
|
HRESULT hr = S_OK;
|
|
RegistryKey key;
|
|
|
|
hr = key.Open(parentKey, keyName);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to open regkey %1 because: hr = %2!x!",
|
|
keyName.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
hr = key.GetValue(value, resultString);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to read regkey %1 because: hr = %2!x!",
|
|
value.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
LOG(String::format(
|
|
L"Value of key: %1",
|
|
resultString.c_str()));
|
|
|
|
} while (false);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
GetRegKeyValue(
|
|
const String& keyName,
|
|
const String& value,
|
|
DWORD& resultValue,
|
|
HKEY parentKey)
|
|
{
|
|
LOG_FUNCTION(GetRegKeyValue);
|
|
|
|
bool result = true;
|
|
|
|
do
|
|
{
|
|
HRESULT hr = S_OK;
|
|
RegistryKey key;
|
|
|
|
hr = key.Open(parentKey, keyName);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to open regkey %1 because: hr = %2!x!",
|
|
keyName.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
hr = key.GetValue(value, resultValue);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to read regkey %1 because: hr = %2!x!",
|
|
value.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
LOG(String::format(
|
|
L"Key value: %1!d!",
|
|
resultValue));
|
|
|
|
} while (false);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SetRegKeyValue(
|
|
const String& keyName,
|
|
const String& value,
|
|
const String& newString,
|
|
HKEY parentKey,
|
|
bool create
|
|
)
|
|
{
|
|
LOG_FUNCTION(SetRegKeyValue);
|
|
|
|
bool result = true;
|
|
|
|
do
|
|
{
|
|
HRESULT hr = S_OK;
|
|
RegistryKey key;
|
|
|
|
if (create)
|
|
{
|
|
hr = key.Create(parentKey, keyName);
|
|
}
|
|
else
|
|
{
|
|
hr = key.Open(parentKey, keyName, KEY_SET_VALUE);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to open regkey %1 because: hr = %2!x!",
|
|
keyName.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
hr = key.SetValue(value, newString);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to write regkey %1 because: hr = %2!x!",
|
|
value.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
SetRegKeyValue(
|
|
const String& keyName,
|
|
const String& value,
|
|
DWORD newValue,
|
|
HKEY parentKey,
|
|
bool create)
|
|
{
|
|
LOG_FUNCTION(SetRegKeyValue);
|
|
|
|
bool result = true;
|
|
|
|
do
|
|
{
|
|
HRESULT hr = S_OK;
|
|
RegistryKey key;
|
|
|
|
if (create)
|
|
{
|
|
hr = key.Create(parentKey, keyName);
|
|
}
|
|
else
|
|
{
|
|
hr = key.Open(parentKey, keyName, KEY_WRITE);
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to open regkey %1 because: hr = %2!x!",
|
|
keyName.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
hr = key.SetValue(value, newValue);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to write regkey %1 because: hr = %2!x!",
|
|
value.c_str(),
|
|
hr));
|
|
|
|
result = false;
|
|
|
|
break;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
HRESULT
|
|
VariantArrayToStringVector(VARIANT* variant, StringVector& stringList)
|
|
{
|
|
LOG_FUNCTION(VariantArrayToStringVector);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
stringList.clear();
|
|
|
|
do
|
|
{
|
|
ASSERT(variant);
|
|
|
|
LOG(String::format(
|
|
L"Variant type = 0x%1!x!",
|
|
V_VT(variant)));
|
|
|
|
if (V_VT(variant) == VT_EMPTY ||
|
|
V_VT(variant) == VT_NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ASSERT(V_VT(variant) == (VT_ARRAY | VT_BSTR));
|
|
|
|
|
|
SAFEARRAY* psa = V_ARRAY(variant);
|
|
|
|
ASSERT(psa);
|
|
ASSERT(psa != (SAFEARRAY*)-1);
|
|
|
|
if (!psa || psa == (SAFEARRAY*)-1)
|
|
{
|
|
LOG(L"variant not safe array");
|
|
break;
|
|
}
|
|
|
|
if (::SafeArrayGetDim(psa) != 1)
|
|
{
|
|
LOG(L"safe array: wrong number of dimensions");
|
|
break;
|
|
}
|
|
|
|
VARTYPE vt = VT_EMPTY;
|
|
hr = ::SafeArrayGetVartype(psa, &vt);
|
|
if (FAILED(hr) || vt != VT_BSTR)
|
|
{
|
|
LOG(L"safe array: wrong element type");
|
|
break;
|
|
}
|
|
|
|
long lower = 0;
|
|
long upper = 0;
|
|
|
|
hr = ::SafeArrayGetLBound(psa, 1, &lower);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(L"can't get lower bound");
|
|
break;
|
|
}
|
|
|
|
hr = ::SafeArrayGetUBound(psa, 1, &upper);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(L"can't get upper bound");
|
|
break;
|
|
}
|
|
|
|
for (long i = lower; i <= upper; ++i)
|
|
{
|
|
BSTR item;
|
|
hr = ::SafeArrayGetElement(psa, &i, &item);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(L"index %1!d! failed", i));
|
|
continue;
|
|
}
|
|
|
|
if (item)
|
|
{
|
|
stringList.push_back(String(item));
|
|
}
|
|
|
|
::SysFreeString(item);
|
|
}
|
|
|
|
}
|
|
while (0);
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
String
|
|
IPAddressToString(DWORD ipAddress)
|
|
{
|
|
String result = String::format(
|
|
L"%1!d!.%2!d!.%3!d!.%4!d!",
|
|
FIRST_IPADDRESS(ipAddress),
|
|
SECOND_IPADDRESS(ipAddress),
|
|
THIRD_IPADDRESS(ipAddress),
|
|
FOURTH_IPADDRESS(ipAddress));
|
|
|
|
return result;
|
|
}
|
|
|
|
// Will convert a string in the form of an IP address to
|
|
// a DWORD. A return value of INADDR_NONE means that we failed
|
|
// to do the conversion
|
|
|
|
DWORD
|
|
StringToIPAddress(const String& stringIPAddress)
|
|
{
|
|
DWORD result = INADDR_NONE;
|
|
|
|
// Convert the string to ansi so that we can use inet_addr
|
|
// to convert to an IP address DWORD
|
|
|
|
AnsiString ansi;
|
|
|
|
String::ConvertResult convertResult = stringIPAddress.convert(ansi);
|
|
|
|
if (String::CONVERT_SUCCESSFUL == convertResult)
|
|
{
|
|
// Convert the string to an address
|
|
|
|
result = inet_addr(ansi.c_str());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DWORD
|
|
ConvertIPAddressOrder(DWORD address)
|
|
{
|
|
DWORD result = 0;
|
|
result |= (address & 0xff) << 24;
|
|
result |= (address & 0xff00) << 8;
|
|
result |= (address >> 8) & 0x0000ff00;
|
|
result |= (address >> 24) & 0x000000ff;
|
|
|
|
return result;
|
|
}
|
|
|
|
// This function allocates an array of DWORDs and fills it with the IP addresses
|
|
// from the StringList. The caller must free the returned pointer using
|
|
// delete[]
|
|
DWORD*
|
|
StringIPListToDWORDArray(
|
|
const StringList& stringIPList,
|
|
DWORD& count)
|
|
{
|
|
// This is an exception throwing new so there is no
|
|
// reason to check for NULL
|
|
|
|
count = 0;
|
|
DWORD ipCount = static_cast<DWORD>(stringIPList.size());
|
|
DWORD* array = new DWORD[ipCount];
|
|
|
|
try
|
|
{
|
|
// Copy the forwarders addresses into the array
|
|
|
|
for (StringList::iterator itr = stringIPList.begin();
|
|
itr != stringIPList.end();
|
|
++itr)
|
|
{
|
|
if (!(*itr).empty())
|
|
{
|
|
DWORD newAddress = StringToIPAddress(*itr);
|
|
if (newAddress != INADDR_NONE)
|
|
{
|
|
array[count++] = newAddress;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (exception &e)
|
|
{
|
|
// NTRAID#NTBUG9-654260-2002/07/08-artm
|
|
// Avoid leaking "array" if encounter any exceptions based on
|
|
// std::exception. Among these are std::length_error and std::bad_alloc,
|
|
// but there could be others.
|
|
delete [] array;
|
|
|
|
// Don't really know how to handle exceptions at this level, so pass the buck.
|
|
throw e;
|
|
}
|
|
return array;
|
|
}
|
|
|
|
|
|
void
|
|
CreateInfFileText(
|
|
String& infFileText,
|
|
unsigned int windowTitleResourceID)
|
|
{
|
|
LOG_FUNCTION(CreateInfFileText);
|
|
|
|
infFileText = L"[Version]\n";
|
|
infFileText += L"Signature = \"$Windows NT$\"\n";
|
|
infFileText += L"[Components]\n";
|
|
infFileText += L"NetOC=netoc.dll,NetOcSetupProc,netoc.inf\n";
|
|
infFileText += L"[Global]\n";
|
|
infFileText += L"WindowTitle=";
|
|
infFileText += String::load(windowTitleResourceID, hResourceModuleHandle);
|
|
infFileText += L"\n";
|
|
infFileText += L"[Strings]\n";
|
|
infFileText += L";(empty)";
|
|
|
|
}
|
|
|
|
|
|
void
|
|
CreateUnattendFileText(
|
|
String& unattendFileText,
|
|
PCWSTR serviceName,
|
|
bool install)
|
|
{
|
|
LOG_FUNCTION(CreateUnattendFileText);
|
|
|
|
ASSERT(serviceName);
|
|
|
|
unattendFileText = L"[NetOptionalComponents]\n";
|
|
unattendFileText += serviceName;
|
|
|
|
if (install)
|
|
{
|
|
unattendFileText += L"=1";
|
|
}
|
|
else
|
|
{
|
|
unattendFileText += L"=0";
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
GetShellPath(
|
|
int folder,
|
|
String& shellPath,
|
|
HWND hwnd = 0)
|
|
{
|
|
LOG_FUNCTION(GetShellPath);
|
|
|
|
HRESULT hr = S_OK;
|
|
PWSTR path = 0;
|
|
|
|
do
|
|
{
|
|
path = new WCHAR[MAX_PATH];
|
|
ZeroMemory(path, sizeof(WCHAR) * MAX_PATH);
|
|
|
|
hr = ::SHGetFolderPath(
|
|
hwnd,
|
|
folder,
|
|
0,
|
|
SHGFP_TYPE_DEFAULT,
|
|
path);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(
|
|
String::format(
|
|
L"Failed to get shell path: hr = %1!x!",
|
|
hr));
|
|
|
|
break;
|
|
}
|
|
|
|
shellPath = path;
|
|
} while(false);
|
|
|
|
if (path)
|
|
{
|
|
delete[] path;
|
|
path = 0;
|
|
}
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// Opens the favorites folder and creates a favorite for
|
|
// the specified URL
|
|
|
|
HRESULT
|
|
AddURLToFavorites(HWND hwnd, const String& url, const String& fileName)
|
|
{
|
|
LOG_FUNCTION(AddURLToFavorites);
|
|
|
|
ASSERT(Win::IsWindow(hwnd));
|
|
ASSERT(!url.empty());
|
|
ASSERT(!fileName.empty());
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
String path;
|
|
|
|
hr = GetShellPath(
|
|
CSIDL_FAVORITES,
|
|
path);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to get favorites path: hr = %1!x!",
|
|
hr));
|
|
|
|
break;
|
|
}
|
|
|
|
// Create the favorites .url file
|
|
|
|
String fileContents = L"[InternetShortcut]\r\n";
|
|
fileContents += L"URL=";
|
|
fileContents += url;
|
|
fileContents += L"\r\n";
|
|
|
|
String fullPath = FS::AppendPath(path, fileName);
|
|
if (fullPath.empty())
|
|
{
|
|
LOG(L"Failed to append path");
|
|
|
|
hr = E_FAIL;
|
|
|
|
break;
|
|
}
|
|
|
|
HANDLE h = 0;
|
|
|
|
hr =
|
|
FS::CreateFile(
|
|
fullPath,
|
|
h,
|
|
GENERIC_WRITE,
|
|
0,
|
|
CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(
|
|
String::format(
|
|
L"Failed to create file: hr = %1!x!",
|
|
hr));
|
|
|
|
break;
|
|
}
|
|
|
|
// Write contents and end-of-file marker
|
|
|
|
hr = FS::Write(h, fileContents + L"\032");
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(
|
|
String::format(
|
|
L"Failed to write contents to file: hr = %1!x!",
|
|
hr));
|
|
|
|
break;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
void
|
|
LaunchMMCConsole(
|
|
const String& consoleFile,
|
|
String& alternatePath)
|
|
{
|
|
LOG_FUNCTION2(LaunchMMCConsole, consoleFile);
|
|
|
|
String fullPath = Win::GetSystemDirectory() + L"\\mmc.exe";
|
|
|
|
String commandLine = L"\"";
|
|
if (!alternatePath.empty())
|
|
{
|
|
LOG(String::format(L"alternatePath = %1", alternatePath.c_str()));
|
|
|
|
commandLine += alternatePath;
|
|
}
|
|
else
|
|
{
|
|
commandLine += Win::GetSystemDirectory();
|
|
}
|
|
|
|
commandLine += L"\\" + consoleFile + L"\"";
|
|
|
|
LOG(String::format(L"fullPath = %1", fullPath.c_str()));
|
|
LOG(String::format(L"commandLine = %1", commandLine.c_str()));
|
|
|
|
HRESULT unused = MyCreateProcess(fullPath, commandLine);
|
|
ASSERT(SUCCEEDED(unused));
|
|
|
|
LOG_HRESULT(unused);
|
|
}
|
|
|
|
void
|
|
LaunchMYS()
|
|
{
|
|
LOG_FUNCTION(LaunchMYS);
|
|
|
|
String fullPath =
|
|
Win::GetSystemDirectory() +
|
|
L"\\mshta.exe";
|
|
|
|
String commandLine = L"res://" +
|
|
Win::GetSystemDirectory() +
|
|
L"\\mys.dll/mys.hta";
|
|
|
|
LOG(String::format(
|
|
L"MYS path = %1",
|
|
fullPath.c_str()));
|
|
|
|
LOG(String::format(
|
|
L"MYS commandline = %1",
|
|
commandLine.c_str()));
|
|
|
|
HRESULT unused = MyCreateProcess(fullPath, commandLine);
|
|
ASSERT(SUCCEEDED(unused));
|
|
|
|
LOG_HRESULT(unused);
|
|
}
|
|
|
|
HRESULT
|
|
GetAllUsersStartMenu(String& startMenuPath)
|
|
{
|
|
LOG_FUNCTION(GetAllUsersStartMenu);
|
|
|
|
HRESULT hr = GetShellPath(
|
|
CSIDL_COMMON_STARTMENU,
|
|
startMenuPath);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to get the start menu path: hr = %1!x!",
|
|
hr));
|
|
}
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
GetAllUsersAdminTools(String& adminToolsPath)
|
|
{
|
|
LOG_FUNCTION(GetAllUsersAdminTools);
|
|
|
|
HRESULT hr = GetShellPath(
|
|
CSIDL_COMMON_ADMINTOOLS,
|
|
adminToolsPath);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to get the admin tools path: hr = %1!x!",
|
|
hr));
|
|
}
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CreateShortcut(
|
|
const String& shortcutPath,
|
|
const String& target,
|
|
const String& description)
|
|
{
|
|
LOG_FUNCTION(CreateShortcut);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
do
|
|
{
|
|
ASSERT(!shortcutPath.empty());
|
|
ASSERT(!target.empty());
|
|
|
|
if (shortcutPath.empty() ||
|
|
target.empty())
|
|
{
|
|
LOG(String::format(
|
|
L"A parameter was empty: shortcutPath = %1, target = %2",
|
|
shortcutPath.c_str(),
|
|
target.c_str()));
|
|
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
LOG(String::format(
|
|
L"shortcutPath = %1",
|
|
shortcutPath.c_str()));
|
|
|
|
LOG(String::format(
|
|
L"target = %1",
|
|
target.c_str()));
|
|
|
|
SmartInterface<IShellLink> shellLink;
|
|
|
|
hr = shellLink.AcquireViaCreateInstance(
|
|
CLSID_ShellLink,
|
|
0,
|
|
CLSCTX_INPROC_SERVER);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to CoCreate IShellLink: hr = %1!x!",
|
|
hr));
|
|
break;
|
|
}
|
|
|
|
hr = shellLink->SetPath(target.c_str());
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to set the target path: hr = %1!x!",
|
|
hr));
|
|
break;
|
|
}
|
|
|
|
if (!description.empty())
|
|
{
|
|
LOG(L"Setting description");
|
|
|
|
hr = shellLink->SetDescription(description.c_str());
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to set shortcut description: hr = 0x%1!x!",
|
|
hr));
|
|
}
|
|
}
|
|
|
|
SmartInterface<IPersistFile> persistFile;
|
|
|
|
hr = persistFile.AcquireViaQueryInterface(
|
|
shellLink,
|
|
IID_IPersistFile);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to QI for IPersistFile: hr = %1!x!",
|
|
hr));
|
|
break;
|
|
}
|
|
|
|
hr = persistFile->Save(shortcutPath.c_str(), FALSE);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to save the shortcut: hr = %1!x!",
|
|
hr));
|
|
break;
|
|
}
|
|
|
|
} while (false);
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
int
|
|
LinkIndexFromNotifyLPARAM(LPARAM lParam)
|
|
{
|
|
LOG_FUNCTION(LinkIndexFromNotifyLPARAM);
|
|
|
|
int result = 0;
|
|
|
|
do
|
|
{
|
|
ASSERT(lParam);
|
|
|
|
if (!lParam)
|
|
{
|
|
break;
|
|
}
|
|
|
|
NMLINK* linkHeader = reinterpret_cast<NMLINK*>(lParam);
|
|
if (!linkHeader)
|
|
{
|
|
LOG(L"Cast of notify link header failed");
|
|
break;
|
|
}
|
|
|
|
result = linkHeader->item.iLink;
|
|
} while (false);
|
|
|
|
LOG(String::format(
|
|
L"result = %1!d!",
|
|
result));
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void
|
|
ShowHelp(
|
|
const String& helpTopic)
|
|
{
|
|
LOG_FUNCTION2(
|
|
ShowHelp,
|
|
helpTopic.c_str());
|
|
|
|
// NOTE: I am not using Win::HtmlHelp here so that the help
|
|
// is actually running in a different process. This
|
|
// allows us to close down CYS without closing the help
|
|
// window.
|
|
|
|
String fullPath = Win::GetSystemWindowsDirectory() + L"\\hh.exe";
|
|
String commandLine = helpTopic;
|
|
|
|
HRESULT hr = MyCreateProcess(fullPath, commandLine);
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to open help: hr = 0x%1!x!",
|
|
hr));
|
|
}
|
|
}
|
|
|
|
void
|
|
OpenLogFile()
|
|
{
|
|
LOG_FUNCTION(OpenLogFile);
|
|
|
|
String fullPath = Win::GetSystemDirectory() + L"\\notepad.exe";
|
|
String commandLine = Win::GetWindowsDirectory();
|
|
commandLine += L"\\Debug\\";
|
|
commandLine += CYS_LOGFILE_NAME;
|
|
commandLine += L".log";
|
|
|
|
HRESULT hr = MyCreateProcess(fullPath, commandLine);
|
|
ASSERT(SUCCEEDED(hr));
|
|
}
|
|
|
|
bool
|
|
IsLogFilePresent()
|
|
{
|
|
LOG_FUNCTION(IsLogFilePresent);
|
|
|
|
bool result = false;
|
|
|
|
String logfile = Win::GetWindowsDirectory();
|
|
logfile += L"\\Debug\\";
|
|
logfile += CYS_LOGFILE_NAME;
|
|
logfile += L".log";
|
|
|
|
result = FS::FileExists(logfile);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
HRESULT
|
|
GetAdminToolsShortcutPath(
|
|
String& adminToolsShortcutPath,
|
|
const String& linkToAppend)
|
|
{
|
|
LOG_FUNCTION(GetAdminToolsShortcutPath);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
String adminToolsPath;
|
|
|
|
hr = GetAllUsersAdminTools(adminToolsPath);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (!linkToAppend.empty())
|
|
{
|
|
adminToolsShortcutPath =
|
|
FS::AppendPath(
|
|
adminToolsPath,
|
|
linkToAppend);
|
|
|
|
}
|
|
|
|
LOG(String::format(
|
|
L"Admin Tools Link = %1",
|
|
adminToolsShortcutPath.c_str()));
|
|
}
|
|
|
|
LOG_HRESULT(hr);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
AddShortcutToAdminTools(
|
|
const String& target,
|
|
unsigned int descriptionID,
|
|
unsigned int linkID)
|
|
{
|
|
LOG_FUNCTION2(
|
|
AddShortcutToAdminTools,
|
|
target);
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
String description = String::load(descriptionID);
|
|
String link = String::load(linkID);
|
|
|
|
do
|
|
{
|
|
String adminToolsLinkPath;
|
|
hr = GetAdminToolsShortcutPath(
|
|
adminToolsLinkPath,
|
|
link);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to GetAdminToolsShortcutPath: hr = 0x%1!x!",
|
|
hr));
|
|
}
|
|
else
|
|
{
|
|
hr = CreateShortcut(
|
|
adminToolsLinkPath,
|
|
target,
|
|
description);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
LOG(String::format(
|
|
L"Failed to create admin tools shortcut: hr = %1!x!",
|
|
hr));
|
|
}
|
|
}
|
|
|
|
} while (false);
|
|
|
|
LOG_HRESULT(hr);
|
|
return hr;
|
|
}
|
|
|
|
bool
|
|
IsSpecialShare(const SHARE_INFO_1& shareInfo)
|
|
{
|
|
LOG_FUNCTION2(
|
|
IsSpecialShare,
|
|
shareInfo.shi1_netname);
|
|
|
|
bool result = false;
|
|
|
|
if (shareInfo.shi1_type == STYPE_DISKTREE)
|
|
{
|
|
LOG(L"Share is of type STYPE_DISKTREE");
|
|
|
|
String shareName = shareInfo.shi1_netname;
|
|
|
|
if (shareName.icompare(CYS_SPECIAL_SHARE_SYSVOL) == 0 ||
|
|
shareName.icompare(CYS_SPECIAL_SHARE_NETLOGON) == 0 ||
|
|
shareName.icompare(CYS_SPECIAL_SHARE_PRINT) == 0)
|
|
{
|
|
LOG(L"Share has a special name");
|
|
|
|
result = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(L"Share is not of type STYPE_DISKTREE");
|
|
|
|
result = true;
|
|
}
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
IsNonSpecialSharePresent()
|
|
{
|
|
LOG_FUNCTION(IsNonSpecialSharePresent);
|
|
|
|
bool result = false;
|
|
|
|
SHARE_INFO_1* shareInfoArray = 0;
|
|
NET_API_STATUS shareResult = 0;
|
|
|
|
do
|
|
{
|
|
DWORD entriesRead = 0;
|
|
DWORD totalEntries = 0;
|
|
DWORD resumeHandle = 0;
|
|
|
|
shareResult = NetShareEnum(
|
|
0,
|
|
1,
|
|
reinterpret_cast<BYTE**>(&shareInfoArray),
|
|
static_cast<DWORD>(-1),
|
|
&entriesRead,
|
|
&totalEntries,
|
|
&resumeHandle);
|
|
|
|
if ((shareResult == ERROR_SUCCESS ||
|
|
shareResult == ERROR_MORE_DATA) &&
|
|
shareInfoArray)
|
|
{
|
|
for (
|
|
DWORD index = 0;
|
|
index < entriesRead;
|
|
++index)
|
|
{
|
|
// Look for only normal shares and ignore special shares
|
|
// like C$, ADMIN$, and IPC$
|
|
|
|
if (!IsSpecialShare(shareInfoArray[index]))
|
|
{
|
|
LOG(String::format(
|
|
L"Share found: %1",
|
|
shareInfoArray[index].shi1_netname));
|
|
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(String::format(
|
|
L"NetShareEnum failed: result = %1!x!",
|
|
shareResult));
|
|
}
|
|
|
|
if (shareInfoArray)
|
|
{
|
|
NetApiBufferFree(shareInfoArray);
|
|
shareInfoArray = 0;
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
break;
|
|
}
|
|
} while(shareResult == ERROR_MORE_DATA);
|
|
|
|
LOG_BOOL(result);
|
|
|
|
return result;
|
|
}
|
|
|