mirror of https://github.com/tongzx/nt5src
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.
1326 lines
37 KiB
1326 lines
37 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
perfsprc.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Author:
|
|
|
|
Bob Watson (a-robw) Aug 95
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <wchar.h>
|
|
#include <winperf.h>
|
|
#include <ntprfctr.h>
|
|
#include <perfutil.h>
|
|
#include "perfsprc.h"
|
|
#include "procmsg.h"
|
|
|
|
#include "dataheap.h"
|
|
|
|
|
|
// bit field definitions for collect function flags
|
|
#define POS_READ_SYS_PROCESS_DATA ((DWORD)0x00010000)
|
|
#define POS_READ_PROCESS_VM_DATA ((DWORD)0x00020000)
|
|
#define POS_READ_JOB_OBJECT_DATA ((DWORD)0x00040000)
|
|
#define POS_READ_JOB_DETAIL_DATA ((DWORD)0x00080000)
|
|
#define POS_READ_HEAP_DATA ((DWORD)0x00100000)
|
|
|
|
#define POS_COLLECT_PROCESS_DATA ((DWORD)0x00010001)
|
|
#define POS_COLLECT_THREAD_DATA ((DWORD)0x00010003)
|
|
#define POS_COLLECT_EXPROCESS_DATA ((DWORD)0x00030004)
|
|
#define POS_COLLECT_IMAGE_DATA ((DWORD)0x0003000C)
|
|
#define POS_COLLECT_LONG_IMAGE_DATA ((DWORD)0x00030014)
|
|
#define POS_COLLECT_THREAD_DETAILS_DATA ((DWORD)0x00030024)
|
|
#define POS_COLLECT_JOB_OBJECT_DATA ((DWORD)0x00050040)
|
|
#define POS_COLLECT_JOB_DETAIL_DATA ((DWORD)0x000D00C1)
|
|
#define POS_COLLECT_HEAP_DATA ((DWORD)0x00110101)
|
|
|
|
#define POS_COLLECT_FUNCTION_MASK ((DWORD)0x000001FF)
|
|
|
|
#define POS_COLLECT_GLOBAL_DATA ((DWORD)0x001501C3)
|
|
#define POS_COLLECT_GLOBAL_NO_HEAP ((DWORD)0x000500C3)
|
|
#define POS_COLLECT_FOREIGN_DATA ((DWORD)0)
|
|
#define POS_COLLECT_COSTLY_DATA ((DWORD)0x0003003C)
|
|
|
|
// global variables to this DLL
|
|
|
|
HANDLE ThisDLLHandle = NULL;
|
|
HANDLE hEventLog = NULL;
|
|
LPWSTR wszTotal = NULL;
|
|
HANDLE hLibHeap = NULL;
|
|
|
|
LPBYTE pProcessBuffer = NULL;
|
|
PPROCESS_VA_INFO pProcessVaInfo = NULL;
|
|
PUNICODE_STRING pusLocalProcessNameBuffer = NULL;
|
|
|
|
LARGE_INTEGER PerfTime = {0,0};
|
|
|
|
const WCHAR IDLE_PROCESS[] = L"Idle";
|
|
const WCHAR SYSTEM_PROCESS[] = L"System";
|
|
|
|
const WCHAR szPerflibSubKey[] = L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
|
|
const WCHAR szPerfProcSubKey[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\PerfProc\\Performance";
|
|
|
|
const WCHAR szDisplayHeapPerfObject[] = L"DisplayHeapPerfObject";
|
|
const WCHAR szProcessNameFormat[] = L"ProcessNameFormat";
|
|
const WCHAR szThreadNameFormat[] = L"ThreadNameFormat";
|
|
|
|
BOOL PerfSprc_DisplayHeapPerfObject = FALSE;
|
|
DWORD PerfSprc_dwProcessNameFormat = NAME_FORMAT_DEFAULT;
|
|
DWORD PerfSprc_dwThreadNameFormat = NAME_FORMAT_DEFAULT;
|
|
|
|
extern DWORD bOpenJobErrorLogged;
|
|
|
|
//
|
|
// Value to decide if process names should be collected from:
|
|
// the SystemProcessInfo structure (fastest)
|
|
// -- or --
|
|
// the process's image file (slower, but shows Unicode filenames)
|
|
//
|
|
LONG lProcessNameCollectionMethod = PNCM_NOT_DEFINED;
|
|
|
|
// variables local to this module
|
|
|
|
static POS_FUNCTION_INFO posDataFuncInfo[] = {
|
|
{PROCESS_OBJECT_TITLE_INDEX, POS_COLLECT_PROCESS_DATA, 0, CollectProcessObjectData},
|
|
{THREAD_OBJECT_TITLE_INDEX, POS_COLLECT_THREAD_DATA, 0, CollectThreadObjectData},
|
|
{EXPROCESS_OBJECT_TITLE_INDEX, POS_COLLECT_EXPROCESS_DATA, 0, CollectExProcessObjectData},
|
|
{IMAGE_OBJECT_TITLE_INDEX, POS_COLLECT_IMAGE_DATA, 0, CollectImageObjectData},
|
|
{LONG_IMAGE_OBJECT_TITLE_INDEX, POS_COLLECT_LONG_IMAGE_DATA,0, CollectLongImageObjectData},
|
|
{THREAD_DETAILS_OBJECT_TITLE_INDEX, POS_COLLECT_THREAD_DETAILS_DATA, 0, CollectThreadDetailsObjectData},
|
|
{JOB_OBJECT_TITLE_INDEX, POS_COLLECT_JOB_OBJECT_DATA, 0, CollectJobObjectData},
|
|
{JOB_DETAILS_OBJECT_TITLE_INDEX, POS_COLLECT_JOB_DETAIL_DATA, 0, CollectJobDetailData},
|
|
{HEAP_OBJECT_TITLE_INDEX, POS_COLLECT_HEAP_DATA, 0, CollectHeapObjectData}
|
|
};
|
|
|
|
#define POS_NUM_FUNCS (sizeof(posDataFuncInfo) / sizeof(posDataFuncInfo[1]))
|
|
|
|
static BOOL bInitOk = FALSE;
|
|
static DWORD dwOpenCount = 0;
|
|
static DWORD ProcessBufSize = LARGE_BUFFER_SIZE;
|
|
|
|
PM_OPEN_PROC OpenSysProcessObject;
|
|
PM_COLLECT_PROC CollecSysProcessObjectData;
|
|
PM_CLOSE_PROC CloseSysProcessObject;
|
|
|
|
__inline
|
|
VOID
|
|
PerfpQuerySystemTime(
|
|
IN PLARGE_INTEGER SystemTime
|
|
)
|
|
{
|
|
do {
|
|
SystemTime->HighPart = USER_SHARED_DATA->SystemTime.High1Time;
|
|
SystemTime->LowPart = USER_SHARED_DATA->SystemTime.LowPart;
|
|
} while (SystemTime->HighPart != USER_SHARED_DATA->SystemTime.High2Time);
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
GetProcessExeName(
|
|
HANDLE hProcessID,
|
|
PUNICODE_STRING pusName
|
|
)
|
|
{
|
|
HANDLE hProcess;
|
|
OBJECT_ATTRIBUTES obProcess;
|
|
CLIENT_ID ClientId;
|
|
PROCESS_BASIC_INFORMATION BasicInfo;
|
|
NTSTATUS Status;
|
|
PPEB Peb;
|
|
PPEB_LDR_DATA Ldr;
|
|
PLIST_ENTRY LdrHead;
|
|
PLIST_ENTRY LdrNext;
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
LDR_DATA_TABLE_ENTRY LdrEntryData;
|
|
BOOL bReturn;
|
|
WCHAR wszDllName[MAX_PATH];
|
|
|
|
// open process for reading
|
|
// get handle to process
|
|
|
|
ClientId.UniqueThread = (HANDLE)NULL;
|
|
ClientId.UniqueProcess = hProcessID;
|
|
|
|
InitializeObjectAttributes(
|
|
&obProcess,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenProcess(
|
|
&hProcess,
|
|
(ACCESS_MASK)PROCESS_ALL_ACCESS,
|
|
&obProcess,
|
|
&ClientId);
|
|
|
|
if (! NT_SUCCESS(Status)){
|
|
// unable to open the process,
|
|
return FALSE;
|
|
}
|
|
|
|
// Get the process information
|
|
|
|
Status = NtQueryInformationProcess(
|
|
hProcess,
|
|
ProcessBasicInformation,
|
|
&BasicInfo,
|
|
sizeof(BasicInfo),
|
|
NULL
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SetLastError( RtlNtStatusToDosError( Status ) );
|
|
bReturn = FALSE;
|
|
} else {
|
|
Peb = BasicInfo.PebBaseAddress;
|
|
|
|
//
|
|
// get the loader information block
|
|
//
|
|
// Ldr = Peb->Ldr
|
|
//
|
|
|
|
if (!ReadProcessMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL)) {
|
|
// unable to read loader information
|
|
bReturn = FALSE;
|
|
} else {
|
|
LdrHead = &Ldr->InMemoryOrderModuleList;
|
|
|
|
//
|
|
// get the first memory block listed. this is the .EXE in NT
|
|
//
|
|
if (!ReadProcessMemory(hProcess, &LdrHead->Flink, &LdrNext, sizeof(LdrNext), NULL)) {
|
|
// unable to read memory link
|
|
bReturn = FALSE;
|
|
} else {
|
|
LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
|
|
|
if (!ReadProcessMemory(hProcess, LdrEntry, &LdrEntryData, sizeof(LdrEntryData), NULL)) {
|
|
// unable to read image header
|
|
bReturn = FALSE;
|
|
} else {
|
|
if (!ReadProcessMemory(hProcess,
|
|
LdrEntryData.BaseDllName.Buffer,
|
|
(LPVOID)&wszDllName[0],
|
|
sizeof(wszDllName), NULL)) {
|
|
// unable to read DLL buffer
|
|
bReturn = FALSE;
|
|
} else {
|
|
// copy the short name to the caller's buffer
|
|
RtlInitUnicodeString (
|
|
pusName,
|
|
wszDllName);
|
|
SetLastError(ERROR_SUCCESS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NtClose (hProcess);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
LONG
|
|
GetProcessNameColMeth (
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hPerflibKey;
|
|
OBJECT_ATTRIBUTES oaPerflibKey;
|
|
UNICODE_STRING PerflibSubKeyString;
|
|
UNICODE_STRING NameInfoValueString;
|
|
LONG lReturn = PNCM_SYSTEM_INFO;
|
|
PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
|
|
DWORD dwBufLen;
|
|
DWORD dwRetBufLen;
|
|
PDWORD pdwValue;
|
|
|
|
RtlInitUnicodeString (
|
|
&PerflibSubKeyString,
|
|
szPerflibSubKey);
|
|
|
|
InitializeObjectAttributes(
|
|
&oaPerflibKey,
|
|
&PerflibSubKeyString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey(
|
|
&hPerflibKey,
|
|
MAXIMUM_ALLOWED,
|
|
&oaPerflibKey
|
|
);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
// registry key opened, now read value.
|
|
// allocate enough room for the structure, - the last
|
|
// UCHAR in the struct, but + the data buffer (a dword)
|
|
|
|
dwBufLen = sizeof(KEY_VALUE_PARTIAL_INFORMATION) -
|
|
sizeof(UCHAR) + sizeof (DWORD);
|
|
|
|
pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ALLOCMEM (
|
|
hLibHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
dwBufLen);
|
|
|
|
if (pKeyInfo != NULL) {
|
|
// initialize value name string
|
|
RtlInitUnicodeString (
|
|
&NameInfoValueString,
|
|
(LPCWSTR)L"CollectUnicodeProcessNames");
|
|
|
|
dwRetBufLen = 0;
|
|
Status = NtQueryValueKey (
|
|
hPerflibKey,
|
|
&NameInfoValueString,
|
|
KeyValuePartialInformation,
|
|
(PVOID)pKeyInfo,
|
|
dwBufLen,
|
|
&dwRetBufLen);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// check value of return data buffer
|
|
pdwValue = (PDWORD)&pKeyInfo->Data[0];
|
|
if (*pdwValue == PNCM_MODULE_FILE) {
|
|
lReturn = PNCM_MODULE_FILE;
|
|
} else {
|
|
// all other values will cause this routine to return
|
|
// the default value of PNCM_SYSTEM_INFO;
|
|
}
|
|
}
|
|
|
|
FREEMEM (hLibHeap, 0, pKeyInfo);
|
|
}
|
|
// close handle
|
|
NtClose (hPerflibKey);
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
VOID
|
|
PerfProcGlobalSettings (
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
HANDLE hPerfProcKey;
|
|
OBJECT_ATTRIBUTES oaPerfProcKey;
|
|
UNICODE_STRING PerfProcSubKeyString;
|
|
UNICODE_STRING NameInfoValueString;
|
|
PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
|
|
DWORD dwBufLen;
|
|
DWORD dwRetBufLen;
|
|
PDWORD pdwValue;
|
|
|
|
PerfpQuerySystemTime(&PerfTime);
|
|
|
|
RtlInitUnicodeString (
|
|
&PerfProcSubKeyString,
|
|
szPerfProcSubKey);
|
|
|
|
InitializeObjectAttributes(
|
|
&oaPerfProcKey,
|
|
&PerfProcSubKeyString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenKey(
|
|
&hPerfProcKey,
|
|
MAXIMUM_ALLOWED,
|
|
&oaPerfProcKey
|
|
);
|
|
|
|
if (NT_SUCCESS (Status)) {
|
|
// registry key opened, now read value.
|
|
// allocate enough room for the structure, - the last
|
|
// UCHAR in the struct, but + the data buffer (a dword)
|
|
|
|
dwBufLen = sizeof(KEY_VALUE_PARTIAL_INFORMATION) -
|
|
sizeof(UCHAR) + sizeof (DWORD);
|
|
|
|
pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ALLOCMEM (
|
|
hLibHeap,
|
|
HEAP_ZERO_MEMORY,
|
|
dwBufLen);
|
|
|
|
if (pKeyInfo != NULL) {
|
|
// initialize value name string
|
|
RtlInitUnicodeString (
|
|
&NameInfoValueString,
|
|
szDisplayHeapPerfObject);
|
|
|
|
dwRetBufLen = 0;
|
|
Status = NtQueryValueKey (
|
|
hPerfProcKey,
|
|
&NameInfoValueString,
|
|
KeyValuePartialInformation,
|
|
(PVOID)pKeyInfo,
|
|
dwBufLen,
|
|
&dwRetBufLen);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// check value of return data buffer
|
|
pdwValue = (PDWORD)&pKeyInfo->Data[0];
|
|
if (*pdwValue == 1) {
|
|
PerfSprc_DisplayHeapPerfObject = TRUE;
|
|
} else {
|
|
// all other values will cause this routine to return
|
|
// the default value of FALSE
|
|
}
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
&NameInfoValueString,
|
|
szProcessNameFormat);
|
|
dwRetBufLen = 0;
|
|
Status = NtQueryValueKey(
|
|
hPerfProcKey,
|
|
&NameInfoValueString,
|
|
KeyValuePartialInformation,
|
|
(PVOID)pKeyInfo,
|
|
dwBufLen,
|
|
&dwRetBufLen);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
pdwValue = (PDWORD) &pKeyInfo->Data[0];
|
|
PerfSprc_dwProcessNameFormat = *pdwValue;
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
&NameInfoValueString,
|
|
szThreadNameFormat);
|
|
dwRetBufLen = 0;
|
|
Status = NtQueryValueKey(
|
|
hPerfProcKey,
|
|
&NameInfoValueString,
|
|
KeyValuePartialInformation,
|
|
(PVOID)pKeyInfo,
|
|
dwBufLen,
|
|
&dwRetBufLen);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
pdwValue = (PDWORD) &pKeyInfo->Data[0];
|
|
PerfSprc_dwThreadNameFormat = *pdwValue;
|
|
}
|
|
|
|
FREEMEM (hLibHeap, 0, pKeyInfo);
|
|
}
|
|
// close handle
|
|
NtClose (hPerfProcKey);
|
|
}
|
|
if ((PerfSprc_dwProcessNameFormat < NAME_FORMAT_BLANK) ||
|
|
(PerfSprc_dwProcessNameFormat > NAME_FORMAT_ID))
|
|
PerfSprc_dwProcessNameFormat = NAME_FORMAT_DEFAULT;
|
|
if ((PerfSprc_dwThreadNameFormat < NAME_FORMAT_BLANK) ||
|
|
(PerfSprc_dwThreadNameFormat > NAME_FORMAT_ID))
|
|
PerfSprc_dwThreadNameFormat = NAME_FORMAT_DEFAULT;
|
|
}
|
|
|
|
BOOL
|
|
DllProcessAttach (
|
|
IN HANDLE DllHandle
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
perform any initialization function that apply to all object
|
|
modules
|
|
|
|
--*/
|
|
{
|
|
BOOL bReturn = TRUE;
|
|
WCHAR wszTempBuffer[512];
|
|
LONG lStatus;
|
|
DWORD dwBufferSize;
|
|
|
|
UNREFERENCED_PARAMETER (DllHandle);
|
|
|
|
// open handle to the event log
|
|
if (hEventLog == NULL) {
|
|
hEventLog = MonOpenEventLog((LPWSTR)L"PerfProc");
|
|
|
|
// create the local heap
|
|
hLibHeap = HeapCreate (0, 1, 0);
|
|
|
|
if (hLibHeap == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (lProcessNameCollectionMethod == PNCM_NOT_DEFINED) {
|
|
// get desired process name collection method as defined in the
|
|
// registry
|
|
lProcessNameCollectionMethod = GetProcessNameColMeth ();
|
|
}
|
|
}
|
|
|
|
lStatus = GetPerflibKeyValue (
|
|
szTotalValue,
|
|
REG_SZ,
|
|
sizeof(wszTempBuffer),
|
|
(LPVOID)&wszTempBuffer[0],
|
|
DEFAULT_TOTAL_STRING_LEN,
|
|
(LPVOID)&szDefaultTotalString[0]);
|
|
|
|
if (lStatus == ERROR_SUCCESS) {
|
|
// then a string was returned in the temp buffer
|
|
dwBufferSize = lstrlenW (wszTempBuffer) + 1;
|
|
dwBufferSize *= sizeof (WCHAR);
|
|
wszTotal = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
|
|
if (wszTotal == NULL) {
|
|
// unable to allocate buffer so use static buffer
|
|
wszTotal = (LPWSTR)&szDefaultTotalString[0];
|
|
} else {
|
|
memcpy (wszTotal, wszTempBuffer, dwBufferSize);
|
|
}
|
|
} else {
|
|
// unable to get string from registry so just use static buffer
|
|
wszTotal = (LPWSTR)&szDefaultTotalString[0];
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
DllProcessDetach (
|
|
IN HANDLE DllHandle
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER (DllHandle);
|
|
|
|
if (dwOpenCount > 0) {
|
|
// the Library is being unloaded before it was
|
|
// closed so close it now as this is the last
|
|
// chance to do it before the library is tossed.
|
|
// if the value of dwOpenCount is > 1, set it to
|
|
// one to insure everything will be closed when
|
|
// the close function is called.
|
|
if (dwOpenCount > 1) dwOpenCount = 1;
|
|
CloseSysProcessObject();
|
|
}
|
|
|
|
if ((wszTotal != NULL) && (wszTotal != &szDefaultTotalString[0])) {
|
|
FREEMEM (hLibHeap, 0, wszTotal);
|
|
wszTotal = NULL;
|
|
}
|
|
|
|
if (HeapDestroy (hLibHeap)) hLibHeap = NULL;
|
|
|
|
if (hEventLog != NULL) {
|
|
MonCloseEventLog ();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
__stdcall
|
|
DllInit(
|
|
IN HANDLE DLLHandle,
|
|
IN DWORD Reason,
|
|
IN LPVOID ReservedAndUnused
|
|
)
|
|
{
|
|
ReservedAndUnused;
|
|
|
|
// this will prevent the DLL from getting
|
|
// the DLL_THREAD_* messages
|
|
DisableThreadLibraryCalls (DLLHandle);
|
|
|
|
switch(Reason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
return DllProcessAttach (DLLHandle);
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
return DllProcessDetach (DLLHandle);
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
case DLL_THREAD_DETACH:
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
PUNICODE_STRING
|
|
GetProcessSlowName (
|
|
PSYSTEM_PROCESS_INFORMATION pProcess
|
|
)
|
|
/*++
|
|
|
|
GetProcessSlowName
|
|
|
|
Inputs:
|
|
PSYSTEM_PROCESS_INFORMATION pProcess
|
|
|
|
address of System Process Information data structure.
|
|
|
|
Outputs:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Pointer to an initialized Unicode string (created by this routine)
|
|
that contains the short name of the process image or a numeric ID
|
|
if no name is found.
|
|
|
|
If unable to allocate memory for structure, then NULL is returned.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR pPeriod;
|
|
PWCHAR pThisChar;
|
|
|
|
WORD wStringSize;
|
|
|
|
WORD wThisChar;
|
|
WORD wLength;
|
|
|
|
// this routine assumes that the allocated memory has been zero'd
|
|
|
|
if (pusLocalProcessNameBuffer == NULL) {
|
|
// allocate Unicode String Structure and adjacent buffer first
|
|
wLength = MAX_INSTANCE_NAME * sizeof(WCHAR);
|
|
if (pProcess->ImageName.Length > 0) {
|
|
if (wLength < pProcess->ImageName.Length) {
|
|
wLength = pProcess->ImageName.Length;
|
|
}
|
|
}
|
|
wStringSize = sizeof(UNICODE_STRING) + wLength + 64 + (WORD) sizeof(UNICODE_NULL);
|
|
|
|
pusLocalProcessNameBuffer =
|
|
ALLOCMEM (hLibHeap,
|
|
HEAP_ZERO_MEMORY, // this will only 0 the buffer the first time!
|
|
(DWORD)wStringSize);
|
|
if (pusLocalProcessNameBuffer == NULL) {
|
|
return NULL;
|
|
} else {
|
|
pusLocalProcessNameBuffer->MaximumLength = (WORD)(wStringSize - (WORD)(sizeof (UNICODE_STRING)));
|
|
}
|
|
}
|
|
else {
|
|
wStringSize = pusLocalProcessNameBuffer->MaximumLength;
|
|
}
|
|
pusLocalProcessNameBuffer->Length = 0;
|
|
pusLocalProcessNameBuffer->Buffer = (PWCHAR)&pusLocalProcessNameBuffer[1];
|
|
|
|
memset ( // buffer must be zero'd so we'll have a NULL Term
|
|
pusLocalProcessNameBuffer->Buffer, 0,
|
|
(DWORD)pusLocalProcessNameBuffer->MaximumLength);
|
|
|
|
// get the process name from the image file
|
|
|
|
GetProcessExeName (pProcess->UniqueProcessId, pusLocalProcessNameBuffer);
|
|
|
|
if (pusLocalProcessNameBuffer->Length > 0) { // some name has been defined
|
|
|
|
pPeriod = (PWCHAR)pusLocalProcessNameBuffer->Buffer;
|
|
pThisChar = (PWCHAR)pusLocalProcessNameBuffer->Buffer;
|
|
wThisChar = 0;
|
|
|
|
//
|
|
// go from beginning to end and find last backslash and
|
|
// last period in name
|
|
//
|
|
|
|
while (*pThisChar != 0) { // go until null
|
|
if (*pThisChar == L'.') {
|
|
pPeriod = pThisChar;
|
|
}
|
|
pThisChar++; // point to next char
|
|
wThisChar += sizeof(WCHAR);
|
|
if (wThisChar >= pusLocalProcessNameBuffer->Length) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if pPeriod is still pointing to the beginning of the
|
|
// string, then no period was found
|
|
|
|
if (pPeriod == (PWCHAR)pusLocalProcessNameBuffer->Buffer) {
|
|
pPeriod = pThisChar; // set to end of string;
|
|
} else {
|
|
// if a period was found, then see if the extension is
|
|
// .EXE, if so leave it, if not, then use end of string
|
|
// (i.e. include extension in name)
|
|
|
|
if (lstrcmpiW(pPeriod, (LPCWSTR)L".EXE") != 0) {
|
|
pPeriod = pThisChar;
|
|
}
|
|
}
|
|
|
|
// copy characters between period (or end of string) and
|
|
// slash (or start of string) to make image name
|
|
|
|
wStringSize = (WORD)((PCHAR)pPeriod - (PCHAR)pusLocalProcessNameBuffer->Buffer);
|
|
wLength = pusLocalProcessNameBuffer->MaximumLength - sizeof(UNICODE_NULL);
|
|
if (wStringSize >= wLength) {
|
|
wStringSize = wLength;
|
|
}
|
|
*pPeriod = 0; // null terminate buffer
|
|
if ((PerfSprc_dwProcessNameFormat == NAME_FORMAT_ID) &&
|
|
(wStringSize < (wLength - 10))) {
|
|
ULONG Length;
|
|
Length = PerfIntegerToWString(
|
|
HandleToUlong(pProcess->UniqueProcessId),
|
|
10,
|
|
(pusLocalProcessNameBuffer->MaximumLength - wStringSize)
|
|
/ sizeof(WCHAR),
|
|
pPeriod+1);
|
|
if (Length > 0)
|
|
*pPeriod = L'_';
|
|
wStringSize += (WORD) (Length * sizeof(WCHAR));
|
|
}
|
|
pusLocalProcessNameBuffer->Length = wStringSize; // adjust length
|
|
|
|
} else { // no name defined so use Process #
|
|
|
|
// check to see if this is a system process and give it
|
|
// a name
|
|
|
|
switch (HandleToUlong(pProcess->UniqueProcessId)) {
|
|
case IDLE_PROCESS_ID:
|
|
RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
|
|
(LPWSTR)IDLE_PROCESS);
|
|
break;
|
|
|
|
case SYSTEM_PROCESS_ID:
|
|
RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
|
|
(LPWSTR)SYSTEM_PROCESS);
|
|
break;
|
|
|
|
// if the id is not a system process, then use the id as the name
|
|
|
|
default:
|
|
// try accessing via the "regular" interface
|
|
return (GetProcessShortName (pProcess));
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return pusLocalProcessNameBuffer;
|
|
}
|
|
|
|
PUNICODE_STRING
|
|
GetProcessShortName (
|
|
PSYSTEM_PROCESS_INFORMATION pProcess
|
|
)
|
|
/*++
|
|
|
|
GetProcessShortName
|
|
|
|
Inputs:
|
|
PSYSTEM_PROCESS_INFORMATION pProcess
|
|
|
|
address of System Process Information data structure.
|
|
|
|
Outputs:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Pointer to an initialized Unicode string (created by this routine)
|
|
that contains the short name of the process image or a numeric ID
|
|
if no name is found.
|
|
|
|
If unable to allocate memory for structure, then NULL is returned.
|
|
|
|
--*/
|
|
{
|
|
PWCHAR pSlash;
|
|
PWCHAR pPeriod;
|
|
PWCHAR pThisChar;
|
|
|
|
WORD wStringSize;
|
|
|
|
WORD wThisChar;
|
|
ULONG ProcessId;
|
|
WORD wLength;
|
|
|
|
// this routine assumes that the allocated memory has been zero'd
|
|
|
|
if (pusLocalProcessNameBuffer == NULL) {
|
|
// allocate Unicode String Structure and adjacent buffer first
|
|
wLength = MAX_INSTANCE_NAME * sizeof(WCHAR);
|
|
if (pProcess->ImageName.Length > 0) {
|
|
if (wLength < pProcess->ImageName.Length) {
|
|
wLength = pProcess->ImageName.Length;
|
|
}
|
|
}
|
|
wStringSize = sizeof(UNICODE_STRING) + wLength + 64 + (WORD) sizeof(UNICODE_NULL);
|
|
|
|
pusLocalProcessNameBuffer =
|
|
ALLOCMEM (hLibHeap,
|
|
HEAP_ZERO_MEMORY, // this will only 0 the buffer the first time!
|
|
(DWORD)wStringSize);
|
|
|
|
if (pusLocalProcessNameBuffer == NULL) {
|
|
return NULL;
|
|
} else {
|
|
pusLocalProcessNameBuffer->MaximumLength = (WORD)(wStringSize - (WORD)sizeof (UNICODE_STRING));
|
|
}
|
|
}
|
|
else {
|
|
wStringSize = pusLocalProcessNameBuffer->MaximumLength;
|
|
}
|
|
pusLocalProcessNameBuffer->Length = 0;
|
|
pusLocalProcessNameBuffer->Buffer = (PWCHAR)&pusLocalProcessNameBuffer[1];
|
|
|
|
memset ( // buffer must be zero'd so we'll have a NULL Term
|
|
pusLocalProcessNameBuffer->Buffer, 0,
|
|
(DWORD)pusLocalProcessNameBuffer->MaximumLength);
|
|
|
|
ProcessId = HandleToUlong(pProcess->UniqueProcessId);
|
|
if (pProcess->ImageName.Buffer) { // some name has been defined
|
|
|
|
pSlash = (PWCHAR)pProcess->ImageName.Buffer;
|
|
pPeriod = (PWCHAR)pProcess->ImageName.Buffer;
|
|
pThisChar = (PWCHAR)pProcess->ImageName.Buffer;
|
|
wThisChar = 0;
|
|
|
|
//
|
|
// go from beginning to end and find last backslash and
|
|
// last period in name
|
|
//
|
|
|
|
while (*pThisChar != 0) { // go until null
|
|
if (*pThisChar == L'\\') {
|
|
pSlash = pThisChar;
|
|
} else if (*pThisChar == L'.') {
|
|
pPeriod = pThisChar;
|
|
}
|
|
pThisChar++; // point to next char
|
|
wThisChar += sizeof(WCHAR);
|
|
if (wThisChar >= pProcess->ImageName.Length) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if pPeriod is still pointing to the beginning of the
|
|
// string, then no period was found
|
|
|
|
if (pPeriod == (PWCHAR)pProcess->ImageName.Buffer) {
|
|
pPeriod = pThisChar; // set to end of string;
|
|
} else {
|
|
// if a period was found, then see if the extension is
|
|
// .EXE, if so leave it, if not, then use end of string
|
|
// (i.e. include extension in name)
|
|
|
|
if (lstrcmpiW(pPeriod, (LPCWSTR)L".EXE") != 0) {
|
|
pPeriod = pThisChar;
|
|
}
|
|
}
|
|
|
|
if (*pSlash == L'\\') { // if pSlash is pointing to a slash, then
|
|
pSlash++; // point to character next to slash
|
|
}
|
|
|
|
// copy characters between period (or end of string) and
|
|
// slash (or start of string) to make image name
|
|
|
|
wStringSize = (WORD)((PCHAR)pPeriod - (PCHAR)pSlash); // size in bytes
|
|
wLength = pusLocalProcessNameBuffer->MaximumLength - sizeof(UNICODE_NULL);
|
|
if (wStringSize >= wLength) {
|
|
wStringSize = wLength;
|
|
}
|
|
|
|
memcpy (pusLocalProcessNameBuffer->Buffer, pSlash, wStringSize);
|
|
|
|
// null terminate is
|
|
// not necessary because allocated memory is zero-init'd
|
|
pPeriod = (PWCHAR) ((PCHAR) pusLocalProcessNameBuffer->Buffer + wStringSize);
|
|
if (PerfSprc_dwProcessNameFormat == NAME_FORMAT_ID) {
|
|
ULONG Length;
|
|
Length = PerfIntegerToWString(
|
|
ProcessId,
|
|
10,
|
|
(pusLocalProcessNameBuffer->MaximumLength - wStringSize)
|
|
/ sizeof(WCHAR),
|
|
pPeriod+1);
|
|
if (Length > 0)
|
|
*pPeriod = L'_';
|
|
wStringSize += (WORD) (Length * sizeof(WCHAR));
|
|
}
|
|
pusLocalProcessNameBuffer->Length = wStringSize;
|
|
} else { // no name defined so use Process #
|
|
|
|
// check to see if this is a system process and give it
|
|
// a name
|
|
|
|
switch (ProcessId) {
|
|
case IDLE_PROCESS_ID:
|
|
RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
|
|
(LPWSTR)IDLE_PROCESS);
|
|
break;
|
|
|
|
case SYSTEM_PROCESS_ID:
|
|
RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
|
|
(LPWSTR)SYSTEM_PROCESS);
|
|
break;
|
|
|
|
// if the id is not a system process, then use the id as the name
|
|
|
|
default:
|
|
RtlIntegerToUnicodeString (ProcessId,
|
|
10,
|
|
pusLocalProcessNameBuffer);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return pusLocalProcessNameBuffer;
|
|
}
|
|
|
|
#pragma warning (disable : 4706)
|
|
|
|
DWORD
|
|
GetSystemProcessData (
|
|
)
|
|
{
|
|
DWORD dwReturnedBufferSize;
|
|
NTSTATUS Status;
|
|
DWORD WinError;
|
|
PVOID pBuffer;
|
|
|
|
//
|
|
// Get process data from system.
|
|
// if bGotProcessInfo is TRUE, that means we have the process
|
|
// info. collected earlier when we are checking for costly
|
|
// object types.
|
|
//
|
|
if (pProcessBuffer == NULL) {
|
|
// allocate a new block
|
|
pProcessBuffer = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY,
|
|
ProcessBufSize);
|
|
if (pProcessBuffer == NULL) {
|
|
return ERROR_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
PerfpQuerySystemTime(&PerfTime);
|
|
while( (Status = NtQuerySystemInformation(
|
|
SystemProcessInformation,
|
|
pProcessBuffer,
|
|
ProcessBufSize,
|
|
&dwReturnedBufferSize)) == STATUS_INFO_LENGTH_MISMATCH ) {
|
|
// expand buffer & retry
|
|
ProcessBufSize += INCREMENT_BUFFER_SIZE;
|
|
|
|
pBuffer = pProcessBuffer;
|
|
if ( !(pProcessBuffer = REALLOCMEM(hLibHeap, 0,
|
|
pProcessBuffer,
|
|
ProcessBufSize)) ) {
|
|
FREEMEM(hLibHeap, 0, pBuffer);
|
|
return (ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
// convert to win32 error
|
|
WinError = (DWORD)RtlNtStatusToDosError(Status);
|
|
}
|
|
else {
|
|
WinError = ERROR_SUCCESS;
|
|
}
|
|
|
|
return (WinError);
|
|
|
|
}
|
|
#pragma warning (default : 4706)
|
|
|
|
DWORD APIENTRY
|
|
OpenSysProcessObject (
|
|
LPWSTR lpDeviceNames
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will initialize the data structures used to pass
|
|
data back to the registry
|
|
|
|
Arguments:
|
|
|
|
Pointer to object ID of each device to be opened (PerfGen)
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD status, dwSize;
|
|
HKEY hKey;
|
|
|
|
UNREFERENCED_PARAMETER (lpDeviceNames);
|
|
|
|
if (dwOpenCount == 0) {
|
|
// clear the job object open error flag
|
|
bOpenJobErrorLogged = FALSE;
|
|
PerfProcGlobalSettings();
|
|
}
|
|
|
|
dwOpenCount++;
|
|
|
|
bInitOk = TRUE;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD APIENTRY
|
|
CollectSysProcessObjectData (
|
|
IN LPWSTR lpValueName,
|
|
IN OUT LPVOID *lppData,
|
|
IN OUT LPDWORD lpcbTotalBytes,
|
|
IN OUT LPDWORD lpNumObjectTypes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will return the data for the processor object
|
|
|
|
Arguments:
|
|
|
|
IN LPWSTR lpValueName
|
|
pointer to a wide character string passed by registry.
|
|
|
|
IN OUT LPVOID *lppData
|
|
IN: pointer to the address of the buffer to receive the completed
|
|
PerfDataBlock and subordinate structures. This routine will
|
|
append its data to the buffer starting at the point referenced
|
|
by *lppData.
|
|
OUT: points to the first byte after the data structure added by this
|
|
routine. This routine updated the value at lppdata after appending
|
|
its data.
|
|
|
|
IN OUT LPDWORD lpcbTotalBytes
|
|
IN: the address of the DWORD that tells the size in bytes of the
|
|
buffer referenced by the lppData argument
|
|
OUT: the number of bytes added by this routine is writted to the
|
|
DWORD pointed to by this argument
|
|
|
|
IN OUT LPDWORD NumObjectTypes
|
|
IN: the address of the DWORD to receive the number of objects added
|
|
by this routine
|
|
OUT: the number of objects added by this routine is writted to the
|
|
DWORD pointed to by this argument
|
|
|
|
Returns:
|
|
|
|
0 if successful, else Win 32 error code of failure
|
|
|
|
--*/
|
|
{
|
|
LONG lReturn = ERROR_SUCCESS;
|
|
|
|
NTSTATUS status;
|
|
|
|
// build bit mask of functions to call
|
|
|
|
DWORD dwQueryType;
|
|
DWORD FunctionCallMask = 0;
|
|
DWORD FunctionIndex;
|
|
|
|
DWORD dwNumObjectsFromFunction;
|
|
DWORD dwOrigBuffSize;
|
|
DWORD dwByteSize;
|
|
|
|
if (!bInitOk) {
|
|
ReportEvent (hEventLog,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
PERFPROC_NOT_OPEN,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
*lpcbTotalBytes = (DWORD) 0;
|
|
*lpNumObjectTypes = (DWORD) 0;
|
|
lReturn = ERROR_SUCCESS;
|
|
goto COLLECT_BAIL_OUT;
|
|
}
|
|
|
|
dwQueryType = GetQueryType (lpValueName);
|
|
|
|
switch (dwQueryType) {
|
|
case QUERY_ITEMS:
|
|
for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
|
|
if (IsNumberInUnicodeList (
|
|
posDataFuncInfo[FunctionIndex].dwObjectId, lpValueName)) {
|
|
FunctionCallMask |=
|
|
posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case QUERY_GLOBAL:
|
|
// only return the HEAP data in a global query if it's enabled
|
|
// if they ask for it specifically, then it's OK
|
|
if (PerfSprc_DisplayHeapPerfObject) {
|
|
FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
|
|
} else {
|
|
// filter out the heap perf object
|
|
FunctionCallMask = POS_COLLECT_GLOBAL_NO_HEAP;
|
|
}
|
|
break;
|
|
|
|
case QUERY_FOREIGN:
|
|
FunctionCallMask = POS_COLLECT_FOREIGN_DATA;
|
|
break;
|
|
|
|
case QUERY_COSTLY:
|
|
FunctionCallMask = POS_COLLECT_COSTLY_DATA;
|
|
break;
|
|
|
|
default:
|
|
FunctionCallMask = POS_COLLECT_COSTLY_DATA;
|
|
break;
|
|
}
|
|
|
|
// collect data from system
|
|
if (FunctionCallMask & POS_READ_SYS_PROCESS_DATA) {
|
|
status = GetSystemProcessData ();
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
ReportEvent (hEventLog,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
PERFPROC_UNABLE_QUERY_PROCESS_INFO,
|
|
NULL,
|
|
0,
|
|
sizeof(DWORD),
|
|
NULL,
|
|
(LPVOID)&status);
|
|
}
|
|
} else {
|
|
status = ERROR_SUCCESS;
|
|
}
|
|
|
|
// collect data from system
|
|
if ((status == ERROR_SUCCESS) &&
|
|
(pProcessBuffer != NULL) &&
|
|
(FunctionCallMask & POS_READ_PROCESS_VM_DATA)) {
|
|
pProcessVaInfo = GetSystemVaData (
|
|
(PSYSTEM_PROCESS_INFORMATION)pProcessBuffer);
|
|
// call function
|
|
|
|
if (pProcessVaInfo == NULL) {
|
|
ReportEvent (hEventLog,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0,
|
|
PERFPROC_UNABLE_QUERY_VM_INFO,
|
|
NULL,
|
|
0,
|
|
sizeof(DWORD),
|
|
NULL,
|
|
(LPVOID)&status);
|
|
// zero buffer
|
|
}
|
|
} else {
|
|
// zero buffer
|
|
}
|
|
|
|
// collect data
|
|
*lpNumObjectTypes = 0;
|
|
dwOrigBuffSize = dwByteSize = *lpcbTotalBytes;
|
|
*lpcbTotalBytes = 0;
|
|
|
|
// remove query bits
|
|
FunctionCallMask &= POS_COLLECT_FUNCTION_MASK;
|
|
|
|
for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
|
|
if ((posDataFuncInfo[FunctionIndex].dwCollectFunctionBit & FunctionCallMask) ==
|
|
(posDataFuncInfo[FunctionIndex].dwCollectFunctionBit & POS_COLLECT_FUNCTION_MASK)) {
|
|
dwNumObjectsFromFunction = 0;
|
|
lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) (
|
|
lppData,
|
|
&dwByteSize,
|
|
&dwNumObjectsFromFunction);
|
|
|
|
if (lReturn == ERROR_SUCCESS) {
|
|
*lpNumObjectTypes += dwNumObjectsFromFunction;
|
|
*lpcbTotalBytes += dwByteSize;
|
|
dwOrigBuffSize -= dwByteSize;
|
|
dwByteSize = dwOrigBuffSize;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// this list of data must be freed after use
|
|
if (pProcessVaInfo != NULL) {
|
|
|
|
FreeSystemVaData (pProcessVaInfo);
|
|
pProcessVaInfo = NULL;
|
|
|
|
}
|
|
|
|
|
|
// *lppData is updated by each function
|
|
// *lpcbTotalBytes is updated after each successful function
|
|
// *lpNumObjects is updated after each successful function
|
|
|
|
COLLECT_BAIL_OUT:
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
DWORD APIENTRY
|
|
CloseSysProcessObject (
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the open handles to the Signal Gen counters.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = ERROR_SUCCESS;
|
|
PVOID buffer;
|
|
|
|
if (--dwOpenCount == 0) {
|
|
if (hLibHeap != NULL) {
|
|
// close
|
|
if (pProcessBuffer != NULL) {
|
|
buffer = pProcessBuffer;
|
|
pProcessBuffer = NULL;
|
|
FREEMEM (hLibHeap, 0, buffer);
|
|
}
|
|
|
|
if (pusLocalProcessNameBuffer != NULL) {
|
|
buffer = pusLocalProcessNameBuffer;
|
|
pusLocalProcessNameBuffer = NULL;
|
|
FREEMEM (hLibHeap, 0, buffer);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
|
|
}
|
|
|
|
const CHAR PerfpIntegerWChars[] = {L'0', L'1', L'2', L'3', L'4', L'5',
|
|
L'6', L'7', L'8', L'9', L'A', L'B',
|
|
L'C', L'D', L'E', L'F'};
|
|
|
|
ULONG
|
|
PerfIntegerToWString(
|
|
IN ULONG Value,
|
|
IN ULONG Base,
|
|
IN LONG OutputLength,
|
|
OUT LPWSTR String
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
WCHAR Result[ 33 ], *s;
|
|
ULONG Shift, Mask, Digit, Length;
|
|
|
|
Shift = 0;
|
|
switch( Base ) {
|
|
case 16: Shift = 4; break;
|
|
case 8: Shift = 3; break;
|
|
case 2: Shift = 1; break;
|
|
|
|
case 0: Base = 10;
|
|
case 10: Shift = 0; break;
|
|
default: Base = 10; Shift = 0; // Default to 10
|
|
}
|
|
|
|
if (Shift != 0) {
|
|
Mask = 0xF >> (4 - Shift);
|
|
}
|
|
|
|
s = &Result[ 32 ];
|
|
*s = L'\0';
|
|
do {
|
|
if (Shift != 0) {
|
|
Digit = Value & Mask;
|
|
Value >>= Shift;
|
|
}
|
|
else {
|
|
Digit = Value % Base;
|
|
Value = Value / Base;
|
|
}
|
|
|
|
*--s = PerfpIntegerWChars[ Digit ];
|
|
} while (Value != 0);
|
|
|
|
Length = (ULONG) (&Result[ 32 ] - s);
|
|
if (OutputLength < 0) {
|
|
OutputLength = -OutputLength;
|
|
while ((LONG)Length < OutputLength) {
|
|
*--s = L'0';
|
|
Length++;
|
|
}
|
|
}
|
|
|
|
if ((LONG)Length > OutputLength) {
|
|
return( 0 );
|
|
}
|
|
else {
|
|
try {
|
|
RtlMoveMemory( String, s, Length*sizeof(WCHAR) );
|
|
|
|
if ((LONG)Length < OutputLength) {
|
|
String[ Length ] = L'\0';
|
|
}
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
return( 0 );
|
|
}
|
|
|
|
return( Length );
|
|
}
|
|
}
|