std::vector<std::wstring> GetFileList(const std::wstring& pattern, const bool fullPaths) { const std::wstring directory = (fullPaths ? GetDirPart(pattern) : L""); // may not pass an empty string to FindFirstFileEx UIETWASSERT(pattern.length() > 0); // string passed to FindFirstFileEx must not end in a backslash UIETWASSERT(pattern.back() != L'\\'); WIN32_FIND_DATA findData; HANDLE hFindFile = ::FindFirstFileExW(pattern.c_str(), FindExInfoBasic, &findData, FindExSearchNameMatch, NULL, 0); std::vector<std::wstring> result; if (hFindFile == INVALID_HANDLE_VALUE) { // If there are NO matching files, then FindFirstFileExW returns // INVALID_HANDLE_VALUE and the last error is ERROR_FILE_NOT_FOUND. UIETWASSERT(::GetLastError() == ERROR_FILE_NOT_FOUND); return result; } do { result.emplace_back(directory + findData.cFileName); } while (::FindNextFileW(hFindFile, &findData)); UIETWASSERT(::GetLastError() == ERROR_NO_MORE_FILES); ATLVERIFY(::FindClose(hFindFile)); return result; }
std::wstring StripExtensionFromPath(const std::wstring& path) { UIETWASSERT(path.size() > 0); const std::wstring ext = GetFileExt(path); UIETWASSERT(path.size() >= ext.size()); return path.substr(0, path.size() - ext.size()); }
std::wstring GetEditControlText(const HWND hEdit) { const int length = ::GetWindowTextLengthW(hEdit); std::vector<wchar_t> buffer(length + 1); // GetWindowText https://msdn.microsoft.com/en-us/library/windows/desktop/ms633520.aspx // If [GetWindowTextW] succeeds, the return value is the length, // in characters, of the copied string, not including the // terminating null character. // If the window has no title bar or text, // [or] if the title bar is empty, // or if the window or control handle is invalid, // the return value is zero. const int charsWritten = ::GetWindowTextW(hEdit, &buffer[0], static_cast<int>(buffer.size())); if (charsWritten == 0) { if (length > 0) debugLastError(); return L""; } UIETWASSERT(charsWritten == length); UIETWASSERT((charsWritten) < (int)buffer.size()); UIETWASSERT(buffer[charsWritten] == 0); // Double-verify that the buffer is null-terminated. buffer[buffer.size() - 1] = 0; return &buffer[0]; }
std::wstring AnsiToUnicode(const std::string& text) { // If the string is empty, then we can return early, and avoid // confusing return values (from MultiByteToWideChar) if (text.empty()) return L""; // Determine number of wide characters to be allocated for the // Unicode string. const int multiCharCount = RequiredNumberOfWideChars(text); std::vector<wchar_t> buffer(multiCharCount); // Convert to Unicode. const int multiToWideResult = ::MultiByteToWideChar( CP_ACP, 0, text.c_str(), static_cast<int>(text.size() + 1), &buffer[0], multiCharCount); if (multiToWideResult == 0) { // No reasonable way for MultiByteToWideChar to fail. debugLastError(); std::terminate(); } UIETWASSERT(multiToWideResult > 0); UIETWASSERT(buffer[multiToWideResult - 1] == 0); // Double-verify that the buffer is null-terminated. buffer[buffer.size() - 1] = 0; std::wstring result = &buffer[0]; return result; }
void SetClipboardText(const std::wstring& text) { if (!::OpenClipboard(::GetDesktopWindow())) return; ATLVERIFY(::EmptyClipboard()); const size_t length = (text.size() + 1) * sizeof(text[0]); HANDLE hmem = ::GlobalAlloc(GMEM_MOVEABLE, length); UIETWASSERT(hmem); // We are not hardened against OOM. void* const ptr = ::GlobalLock(hmem); UIETWASSERT(ptr != NULL); wcscpy_s(static_cast<wchar_t*>(ptr), (text.size() + 1), text.c_str()); UnlockGlobalMemory(hmem); if (::SetClipboardData(CF_UNICODETEXT, hmem) == NULL) { ATLVERIFY(!::GlobalFree(hmem)); ATLVERIFY(::CloseClipboard()); return; } ATLVERIFY(::CloseClipboard()); }
int DeleteFiles(const HWND hwnd, const std::vector<std::wstring>& paths) { UIETWASSERT(paths.size() > 0); std::vector<wchar_t> fileNames; for (const auto& path : paths) { // Push the file name and its NULL terminator onto the vector. fileNames.insert(fileNames.end(), path.c_str(), path.c_str() + path.size()); fileNames.emplace_back(0); } // Double null-terminate. fileNames.emplace_back(0); SHFILEOPSTRUCT fileOp = { hwnd, FO_DELETE, &fileNames[0], NULL, FOF_ALLOWUNDO | FOF_FILESONLY | FOF_NOCONFIRMATION, }; // Delete using the recycle bin. // TODO: IFileOperation? return ::SHFileOperationW(&fileOp); }
std::wstring CrackFilePart(const std::wstring& path) { const std::wstring filePart = GetFilePart(path); const std::wstring extension = GetFileExt(filePart); UIETWASSERT(filePart.size() >= extension.size()); if (!extension.empty()) return filePart.substr(0, filePart.size() - extension.size()); return filePart; }
std::wstring GetFilePart(const std::wstring& path) { UIETWASSERT(path.size() > 0); const size_t lastSlash = path.find_last_of(L'\\'); if (lastSlash != std::wstring::npos) return path.substr(lastSlash); // If there's no slash then the file part is the entire string. return path; }
std::wstring GetBuildTimeFromAddress(_In_ const void* const codeAddress) { // Get the base of the address reservation. This lets this // function be passed any function or global variable address // in a DLL or EXE. MEMORY_BASIC_INFORMATION memoryInfo; if (::VirtualQuery(codeAddress, &memoryInfo, sizeof(memoryInfo)) != sizeof(memoryInfo)) { UIETWASSERT(0); return L""; } const void* const ModuleHandle = memoryInfo.AllocationBase; // Walk the PE data structures to find the link time stamp. const IMAGE_DOS_HEADER* const DosHeader = reinterpret_cast<const IMAGE_DOS_HEADER*>(ModuleHandle); if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic) { UIETWASSERT(0); return L""; } const IMAGE_NT_HEADERS* const NTHeader = reinterpret_cast<const IMAGE_NT_HEADERS*>( reinterpret_cast<const char*>(DosHeader) + DosHeader->e_lfanew); if (IMAGE_NT_SIGNATURE != NTHeader->Signature) { UIETWASSERT(0); return L""; } // TimeDateStamp is 32 bits and time_t is 64 bits. That will have to be dealt // with when TimeDateStamp wraps in February 2106. const time_t timeDateStamp = NTHeader->FileHeader.TimeDateStamp; tm linkTime; gmtime_s(&linkTime, &timeDateStamp); // Print out the module information. The %.24s is necessary to trim // the new line character off of the date string returned by asctime(). // _wasctime_s requires a 26-character buffer. wchar_t ascTimeBuf[26]; _wasctime_s(ascTimeBuf, &linkTime); wchar_t buffer[100]; swprintf_s(buffer, L"%.24s GMT (%08lx)", ascTimeBuf, NTHeader->FileHeader.TimeDateStamp); // Return buffer+4 because we don't need the day of the week. return buffer + 4; }
std::wstring GetDirPart(const std::wstring& path) { UIETWASSERT(path.size() > 0); const size_t lastSlash = path.find_last_of(L'\\'); if (lastSlash != std::wstring::npos) { return path.substr(0, lastSlash+1); } // If there's no slash then there is no directory. return L""; }
std::wstring GetBuildTimeFromAddress(void* codeAddress) { // Get the base of the address reservation. This lets this // function be passed any function or global variable address // in a DLL or EXE. MEMORY_BASIC_INFORMATION memoryInfo; if (VirtualQuery(codeAddress, &memoryInfo, sizeof(memoryInfo)) != sizeof(memoryInfo)) { UIETWASSERT(0); return L""; } void* ModuleHandle = memoryInfo.AllocationBase; // Walk the PE data structures to find the link time stamp. IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle; if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic) { UIETWASSERT(0); return L""; } IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader + DosHeader->e_lfanew); if (IMAGE_NT_SIGNATURE != NTHeader->Signature) { UIETWASSERT(0); return L""; } tm linkTime = {}; gmtime_s(&linkTime, (time_t*)&NTHeader->FileHeader.TimeDateStamp); // Print out the module information. The %.24s is necessary to trim // the new line character off of the date string returned by asctime(). // _wasctime_s requires a 26-character buffer. wchar_t ascTimeBuf[26]; _wasctime_s(ascTimeBuf, &linkTime); wchar_t buffer[100]; swprintf_s(buffer, L"%.24s GMT (%08lx)", ascTimeBuf, NTHeader->FileHeader.TimeDateStamp); // Return buffer+4 because we don't need the day of the week. return buffer + 4; }
void CCPUFrequencyMonitor::StartThreads() { UIETWASSERT(!hExitEvent_); if (!hExitEvent_) { // Must do all this before creating the child threads. SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); numCPUs_ = systemInfo.dwNumberOfProcessors; // Clamp to the number of processors that can be identified by // SetThreadAffinityMask - 32 or 64. if (numCPUs_ > sizeof(DWORD_PTR) * 8) numCPUs_ = sizeof(DWORD_PTR) * 8; threads_.resize(numCPUs_); quit_ = false; workStartSemaphore_ = CreateSemaphore(nullptr, 0, numCPUs_, nullptr); resultsDoneSemaphore_ = CreateSemaphore(nullptr, 0, numCPUs_, nullptr); if (!workStartSemaphore_ || !resultsDoneSemaphore_) return; // Create numCPUs_ threads, set their affinity to individual CPU // cores, and raise their priority. This will mostly ensure that they // run promptly and on the desired CPU core. for (unsigned i = 0; i < numCPUs_; ++i) { threads_[i].pOwner = this; threads_[i].cpuNumber = i; HANDLE hThread = CreateThread(nullptr, 0x10000, StaticPerCPUSamplingThread, &threads_[i], 0, nullptr); if (hThread) { SetThreadAffinityMask(hThread, DWORD_PTR(1) << i); SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); } threads_[i].hThread = hThread; } // Get the initial frequency QPCElapsedTimer timer; // Run the test long enough so that the OS will ramp up the CPU to // full speed. startFrequency_ = MeasureFrequency(600); float testElapsed = static_cast<float>(timer.ElapsedSeconds()); ETWMark2F("Startup CPU frequency (MHz) and measurement time (s)", startFrequency_, testElapsed); // Once the monitor thread is created the other threads will start // being told to do measurements occasionally. hExitEvent_ = CreateEvent(nullptr, FALSE, FALSE, nullptr); hThread_ = CreateThread(NULL, 0, StaticMonitorThread, this, 0, NULL); } }
void CreateRegistryKey(const HKEY root, const std::wstring& subkey, const std::wstring& newKey) { HKEY key; if (::RegOpenKeyExW(root, subkey.c_str(), 0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) return; HKEY resultKey; // TODO: RegCreateKey is deprecated. const LONG createResult = ::RegCreateKeyW(key, newKey.c_str(), &resultKey); UIETWASSERT(createResult == ERROR_SUCCESS); if (createResult == ERROR_SUCCESS) { ATLVERIFY(::RegCloseKey(resultKey) == ERROR_SUCCESS); } ATLVERIFY(::RegCloseKey(key) == ERROR_SUCCESS); }
// Load a file and convert to a string. If the file contains // an embedded NUL then the resulting string will be truncated. std::wstring LoadFileAsText(const std::wstring& fileName) { if (!::PathFileExistsW(fileName.c_str())) return L""; std::ifstream f; f.open(fileName, std::ios_base::binary); if (!f) { outputPrintf(L"Failed to load file `%s` as text - f.open failed!\n", fileName.c_str()); return L""; } // Find the file length. f.seekg(0, std::ios_base::end); const std::streamoff len_int = f.tellg(); if (len_int == -1) { outputPrintf(L"Failed to load file `%s` as text - f.tellg failed!\n", fileName.c_str()); return L""; } const size_t length = static_cast<size_t>(len_int); f.seekg(0, std::ios_base::beg); // Allocate a buffer and read the file. std::vector<char> data(length + 2); f.read(&data[0], length); if (!f) { outputPrintf(L"Failed to load file `%s` as text - f.read failed!\n", fileName.c_str()); return L""; } // Add a multi-byte null terminator. data[length] = 0; data[length+1] = 0; const wchar_t bom = 0xFEFF; UIETWASSERT(data.size() > sizeof(bom)); if (memcmp(&bom, &data[0], sizeof(bom)) == 0) { // Assume UTF-16, strip bom, and return. return reinterpret_cast<const wchar_t*>(&data[sizeof(bom)]); } // If not-UTF-16 then convert from ANSI to wstring and return return AnsiToUnicode(&data[0]); }
// MultiByteToWideChar: https://msdn.microsoft.com/en-us/library/windows/desktop/dd319072.aspx // // Remarks: // As mentioned in the caution above, // the output buffer can easily be overrun // if this function is not first called with cchWideChar set to 0 // in order to obtain the required size. int RequiredNumberOfWideChars(const std::string& text) { static_assert(sizeof(std::string::value_type) == 1 == sizeof(text[0]), "bad assumptions!"); const int multiCharCount = ::MultiByteToWideChar(CP_ACP, 0, text.c_str(), static_cast<int>(text.size() + 1), NULL, 0); if (multiCharCount == 0) { // No reasonable way for MultiByteToWideChar to fail. debugLastError(); std::terminate(); } UIETWASSERT(multiCharCount > 0); return multiCharCount; }
static bool ControlOK(const HWND win) { if (!win) return false; if (!::IsWindowEnabled(win)) return false; if (!(::GetWindowLong(win, GWL_STYLE) & WS_TABSTOP)) return false; // You have to check for visibility of the parent window because during dialog // creation the parent window is invisible, which renders the child windows // all invisible - not good. const HWND parent = ::GetParent(win); UIETWASSERT(parent); if (!::IsWindowVisible(win) && ::IsWindowVisible(parent)) return false; return true; }
static HWND GetNextDlgItem(HWND win, bool Wrap) { HWND next = GetWindow(win, GW_HWNDNEXT); while (next != win && !ControlOK(next)) { if (next) next = GetWindow(next, GW_HWNDNEXT); else { if (Wrap) next = GetWindow(win, GW_HWNDFIRST); else return 0; } } UIETWASSERT(!Wrap || next); return next; }
static HWND GetNextDlgItem(const HWND win, const bool Wrap) { HWND next = ::GetWindow(win, GW_HWNDNEXT); while ((next != win) && (!::ControlOK(next))) { if (next) next = ::GetWindow(next, GW_HWNDNEXT); else { if (Wrap) next = ::GetWindow(win, GW_HWNDFIRST); else return 0; } } UIETWASSERT(!Wrap || next); return next; }
bool ChildProcess::Run(bool showCommand, std::wstring args) { UIETWASSERT(!hProcess_); if (showCommand) outputPrintf(L"%s\n", args.c_str()); SECURITY_ATTRIBUTES security = { sizeof(security), 0, TRUE }; hStdOutput_ = CreateFile(kPipeName, GENERIC_WRITE, 0, &security, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE); if (hStdOutput_ == INVALID_HANDLE_VALUE) return false; if (!DuplicateHandle(GetCurrentProcess(), hStdOutput_, GetCurrentProcess(), &hStdError_, 0, TRUE, DUPLICATE_SAME_ACCESS)) return false; STARTUPINFO startupInfo = {}; startupInfo.hStdOutput = hStdOutput_; startupInfo.hStdError = hStdError_; startupInfo.hStdInput = INVALID_HANDLE_VALUE; startupInfo.dwFlags = STARTF_USESTDHANDLES; PROCESS_INFORMATION processInfo = {}; DWORD flags = CREATE_NO_WINDOW; // Wacky CreateProcess rules say args has to be writable! std::vector<wchar_t> argsCopy(args.size() + 1); wcscpy_s(&argsCopy[0], argsCopy.size(), args.c_str()); BOOL success = CreateProcess(exePath_.c_str(), &argsCopy[0], NULL, NULL, TRUE, flags, NULL, NULL, &startupInfo, &processInfo); if (success) { CloseHandle(processInfo.hThread); hProcess_ = processInfo.hProcess; return true; } else { outputPrintf(L"Error %d starting %s, %s\n", (int)GetLastError(), exePath_.c_str(), args.c_str()); } return false; }
std::wstring GetClipboardText() { std::wstring result; if (!::OpenClipboard(::GetDesktopWindow())) return result; HANDLE hClip = ::GetClipboardData(CF_UNICODETEXT); if (!hClip) return result; void* const ptr = ::GlobalLock(hClip); UIETWASSERT(ptr != NULL); const size_t bytes = ::GlobalSize(hClip); PCWSTR const text = static_cast<PCWSTR>(ptr); result.insert(result.begin(), text, text + (bytes / sizeof(text[0]))); UnlockGlobalMemory(hClip); ATLVERIFY(::CloseClipboard()); return result; }
void SmartEnableWindow(HWND Win, BOOL Enable) { UIETWASSERT(Win); if (!Enable) { HWND hasfocus = GetFocus(); bool FocusProblem = false; HWND focuscopy; for (focuscopy = hasfocus; focuscopy; focuscopy = (GetParent)(focuscopy)) if (focuscopy == Win) FocusProblem = true; if (FocusProblem) { HWND nextctrl = GetNextDlgItem(Win, true); if (nextctrl) SetFocus(nextctrl); } } ::EnableWindow(Win, Enable); }
void SmartEnableWindow(const HWND Win, const BOOL Enable) { UIETWASSERT(Win); if (!Enable) { bool FocusProblem = false; for (HWND focuscopy = ::GetFocus(); focuscopy; focuscopy = ::GetParent(focuscopy)) { if (focuscopy == Win) FocusProblem = true; } if (FocusProblem) { HWND nextctrl = ::GetNextDlgItem(Win, true); if (nextctrl) ::SetFocus(nextctrl); } } ::EnableWindow(Win, Enable); }
void CWorkingSetMonitor::SampleWorkingSets() { CSingleLock locker(&processesLock_); if (processes_.empty() && !processAll_) return; // CreateToolhelp32Snapshot runs faster than EnumProcesses and // it returns the process name as well, thus avoiding a call to // EnumProcessModules to get the name. HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, TH32CS_SNAPPROCESS); if (!hSnapshot) return; PROCESSENTRY32W peInfo; peInfo.dwSize = sizeof(peInfo); BOOL nextProcess = Process32First(hSnapshot, &peInfo); // Allocate enough space to get the working set of most processes. // It will grow if needed. ULONG_PTR numEntries = 100000; const rsize_t bufferSizeNeeded = sizeof(PSAPI_WORKING_SET_INFORMATION) + (numEntries * sizeof(PSAPI_WORKING_SET_BLOCK)); std::vector<char> buffer(bufferSizeNeeded); PSAPI_WORKING_SET_INFORMATION* pwsBuffer = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(buffer.data()); ULONG_PTR totalWSPages = 0; // The PSS page count is stored as a multiple of PSSMultiplier. // This allows all the supported share counts, from 1 to 7, to be // divided out without loss of precision. That is, an unshared page // is recorded by adding 420. A page shared by seven processes (the // maximum recorded) is recorded by adding 420/7. const uint64_t PSSMultiplier = 420; // LCM of 1, 2, 3, 4, 5, 6, 7 uint64_t totalPSSPages = 0; ULONG_PTR totalPrivateWSPages = 0; // Iterate through the processes. while (nextProcess) { bool match = processAll_; for (const auto& name : processes_) { if (_wcsicmp(peInfo.szExeFile, name.c_str()) == 0) { match = true; } } if (match) { DWORD pid = peInfo.th32ProcessID; // Get a handle to the process. HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); ULONG_PTR wsPages = 0; uint64_t PSSPages = 0; ULONG_PTR privateWSPages = 0; if (NULL != hProcess) { bool success = true; if (bExpensiveWSMonitoring_) { if (!QueryWorkingSet(hProcess, &buffer[0], static_cast<DWORD>(buffer.size()))) { success = false; // Increase the buffer size based on the NumberOfEntries returned, // with some padding in case the working set is increasing. if (GetLastError() == ERROR_BAD_LENGTH) { numEntries = pwsBuffer->NumberOfEntries + pwsBuffer->NumberOfEntries / 4; buffer.resize(sizeof(PSAPI_WORKING_SET_INFORMATION) + numEntries * sizeof(PSAPI_WORKING_SET_BLOCK)); pwsBuffer = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(&buffer[0]); if (QueryWorkingSet(hProcess, &buffer[0], static_cast<DWORD>(buffer.size()))) { success = true; } } } if (success) { wsPages = pwsBuffer->NumberOfEntries; for (ULONG_PTR page = 0; page < wsPages; ++page) { if (!pwsBuffer->WorkingSetInfo[page].Shared) { ++privateWSPages; PSSPages += PSSMultiplier; } else { UIETWASSERT(pwsBuffer->WorkingSetInfo[page].ShareCount <= 7); PSSPages += PSSMultiplier / pwsBuffer->WorkingSetInfo[page].ShareCount; } } totalPSSPages += PSSPages; totalPrivateWSPages += privateWSPages; } } else { PROCESS_MEMORY_COUNTERS memoryCounters = {sizeof(memoryCounters)}; if (GetProcessMemoryInfo(hProcess, &memoryCounters, sizeof(memoryCounters))) { wsPages = memoryCounters.WorkingSetSize / 4096; } } if (success) { totalWSPages += wsPages; wchar_t process[MAX_PATH + 100]; swprintf_s(process, L"%s (%u)", peInfo.szExeFile, pid); ETWMarkWorkingSet(peInfo.szExeFile, process, counter_, static_cast<unsigned>(privateWSPages * 4), static_cast<unsigned>((PSSPages * 4) / PSSMultiplier), static_cast<unsigned>((wsPages * 4))); } CloseHandle(hProcess); } } nextProcess = Process32Next(hSnapshot, &peInfo); } CloseHandle(hSnapshot); ETWMarkWorkingSet(L"Total", L"", counter_, static_cast<unsigned>(totalPrivateWSPages * 4), static_cast<unsigned>((totalPSSPages * 4) / PSSMultiplier), static_cast<unsigned>(totalWSPages * 4)); ++counter_; }