Пример #1
0
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;
}
Пример #2
0
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());
}
Пример #3
0
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];
}
Пример #4
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;
}
Пример #5
0
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());
}
Пример #6
0
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);
}
Пример #7
0
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;
}
Пример #8
0
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;
}
Пример #9
0
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;
}
Пример #10
0
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"";
}
Пример #11
0
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;
}
Пример #12
0
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);
	}
}
Пример #13
0
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);
}
Пример #14
0
// 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]);
}
Пример #15
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;
}
Пример #16
0
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;
}
Пример #17
0
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;
}
Пример #18
0
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;
}
Пример #19
0
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;
}
Пример #20
0
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;
}
Пример #21
0
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);
}
Пример #22
0
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);
}
Пример #23
0
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_;
}