// 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(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 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 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(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(&shareInfoArray), static_cast(-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; }