1507 lines
41 KiB
C++
1507 lines
41 KiB
C++
/*++
|
|
|
|
Copyright(c) 1999-2002 Microsoft Corporation
|
|
|
|
--*/
|
|
|
|
|
|
#include "pch.cpp"
|
|
|
|
#include "platform.h"
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Win32LiveSystemProvider.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
Win32LiveSystemProvider::Win32LiveSystemProvider(ULONG PlatformId,
|
|
ULONG BuildNumber)
|
|
{
|
|
m_PlatformId = PlatformId;
|
|
m_BuildNumber = BuildNumber;
|
|
|
|
m_PsApi = NULL;
|
|
m_EnumProcessModules = NULL;
|
|
m_GetModuleFileNameExW = NULL;
|
|
|
|
m_Kernel32 = NULL;
|
|
m_OpenThread = NULL;
|
|
m_Thread32First = NULL;
|
|
m_Thread32Next = NULL;
|
|
m_Module32First = NULL;
|
|
m_Module32Next = NULL;
|
|
m_Module32FirstW = NULL;
|
|
m_Module32NextW = NULL;
|
|
m_CreateToolhelp32Snapshot = NULL;
|
|
m_GetLongPathNameA = NULL;
|
|
m_GetLongPathNameW = NULL;
|
|
m_GetProcessTimes = NULL;
|
|
}
|
|
|
|
Win32LiveSystemProvider::~Win32LiveSystemProvider(void)
|
|
{
|
|
if (m_PsApi) {
|
|
FreeLibrary(m_PsApi);
|
|
}
|
|
if (m_Kernel32) {
|
|
FreeLibrary(m_Kernel32);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::Initialize(void)
|
|
{
|
|
m_PsApi = LoadLibrary("psapi.dll");
|
|
if (m_PsApi) {
|
|
m_EnumProcessModules = (ENUM_PROCESS_MODULES)
|
|
GetProcAddress(m_PsApi, "EnumProcessModules");
|
|
m_GetModuleFileNameExW = (GET_MODULE_FILE_NAME_EX_W)
|
|
GetProcAddress(m_PsApi, "GetModuleFileNameExW");
|
|
}
|
|
|
|
m_Kernel32 = LoadLibrary("kernel32.dll");
|
|
if (m_Kernel32) {
|
|
m_OpenThread = (OPEN_THREAD)
|
|
GetProcAddress(m_Kernel32, "OpenThread");
|
|
m_Thread32First = (THREAD32_FIRST)
|
|
GetProcAddress(m_Kernel32, "Thread32First");
|
|
m_Thread32Next = (THREAD32_NEXT)
|
|
GetProcAddress(m_Kernel32, "Thread32Next");
|
|
m_Module32First = (MODULE32_FIRST)
|
|
GetProcAddress(m_Kernel32, "Module32First");
|
|
m_Module32Next = (MODULE32_NEXT)
|
|
GetProcAddress(m_Kernel32, "Module32Next");
|
|
m_Module32FirstW = (MODULE32_FIRST)
|
|
GetProcAddress(m_Kernel32, "Module32FirstW");
|
|
m_Module32NextW = (MODULE32_NEXT)
|
|
GetProcAddress(m_Kernel32, "Module32NextW");
|
|
m_CreateToolhelp32Snapshot = (CREATE_TOOLHELP32_SNAPSHOT)
|
|
GetProcAddress(m_Kernel32, "CreateToolhelp32Snapshot");
|
|
m_GetLongPathNameA = (GET_LONG_PATH_NAME_A)
|
|
GetProcAddress(m_Kernel32, "GetLongPathNameA");
|
|
m_GetLongPathNameW = (GET_LONG_PATH_NAME_W)
|
|
GetProcAddress(m_Kernel32, "GetLongPathNameW");
|
|
m_GetProcessTimes = (GET_PROCESS_TIMES)
|
|
GetProcAddress(m_Kernel32, "GetProcessTimes");
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::Release(void)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetCurrentTimeDate(OUT PULONG TimeDate)
|
|
{
|
|
FILETIME FileTime;
|
|
|
|
GetSystemTimeAsFileTime(&FileTime);
|
|
*TimeDate = FileTimeToTimeDate(&FileTime);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetCpuType(OUT PULONG Type,
|
|
OUT PBOOL BackingStore)
|
|
{
|
|
SYSTEM_INFO SysInfo;
|
|
|
|
GetSystemInfo(&SysInfo);
|
|
*Type = GenProcArchToImageMachine(SysInfo.wProcessorArchitecture);
|
|
if (*Type == IMAGE_FILE_MACHINE_UNKNOWN) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
#ifdef DUMP_BACKING_STORE
|
|
*BackingStore = TRUE;
|
|
#else
|
|
*BackingStore = FALSE;
|
|
#endif
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#if defined(i386)
|
|
|
|
BOOL
|
|
X86CpuId(
|
|
IN ULONG32 SubFunction,
|
|
OUT PULONG32 EaxRegister, OPTIONAL
|
|
OUT PULONG32 EbxRegister, OPTIONAL
|
|
OUT PULONG32 EcxRegister, OPTIONAL
|
|
OUT PULONG32 EdxRegister OPTIONAL
|
|
)
|
|
{
|
|
BOOL Succ;
|
|
ULONG32 _Eax;
|
|
ULONG32 _Ebx;
|
|
ULONG32 _Ecx;
|
|
ULONG32 _Edx;
|
|
|
|
__try {
|
|
__asm {
|
|
mov eax, SubFunction
|
|
|
|
__emit 0x0F
|
|
__emit 0xA2 ;; CPUID
|
|
|
|
mov _Eax, eax
|
|
mov _Ebx, ebx
|
|
mov _Ecx, ecx
|
|
mov _Edx, edx
|
|
}
|
|
|
|
if ( EaxRegister ) {
|
|
*EaxRegister = _Eax;
|
|
}
|
|
|
|
if ( EbxRegister ) {
|
|
*EbxRegister = _Ebx;
|
|
}
|
|
|
|
if ( EcxRegister ) {
|
|
*EcxRegister = _Ecx;
|
|
}
|
|
|
|
if ( EdxRegister ) {
|
|
*EdxRegister = _Edx;
|
|
}
|
|
|
|
Succ = TRUE;
|
|
}
|
|
|
|
__except ( EXCEPTION_EXECUTE_HANDLER ) {
|
|
|
|
Succ = FALSE;
|
|
}
|
|
|
|
return Succ;
|
|
}
|
|
|
|
VOID
|
|
GetCpuInformation(
|
|
PCPU_INFORMATION Cpu
|
|
)
|
|
{
|
|
BOOL Succ;
|
|
|
|
//
|
|
// Get the VendorID
|
|
//
|
|
|
|
Succ = X86CpuId ( CPUID_VENDOR_ID,
|
|
NULL,
|
|
&Cpu->X86CpuInfo.VendorId [0],
|
|
&Cpu->X86CpuInfo.VendorId [2],
|
|
&Cpu->X86CpuInfo.VendorId [1]
|
|
);
|
|
|
|
if ( !Succ ) {
|
|
|
|
//
|
|
// CPUID is not supported on this processor.
|
|
//
|
|
|
|
ZeroMemory (&Cpu->X86CpuInfo, sizeof (Cpu->X86CpuInfo));
|
|
}
|
|
|
|
//
|
|
// Get the feature information.
|
|
//
|
|
|
|
Succ = X86CpuId ( CPUID_VERSION_FEATURES,
|
|
&Cpu->X86CpuInfo.VersionInformation,
|
|
NULL,
|
|
NULL,
|
|
&Cpu->X86CpuInfo.FeatureInformation
|
|
);
|
|
|
|
if ( !Succ ) {
|
|
Cpu->X86CpuInfo.VersionInformation = 0;
|
|
Cpu->X86CpuInfo.FeatureInformation = 0;
|
|
}
|
|
|
|
//
|
|
// Get the AMD specific information if this is an AMD processor.
|
|
//
|
|
|
|
if ( Cpu->X86CpuInfo.VendorId [0] == AMD_VENDOR_ID_0 &&
|
|
Cpu->X86CpuInfo.VendorId [1] == AMD_VENDOR_ID_1 &&
|
|
Cpu->X86CpuInfo.VendorId [2] == AMD_VENDOR_ID_2 ) {
|
|
|
|
Succ = X86CpuId ( CPUID_AMD_EXTENDED_FEATURES,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Cpu->X86CpuInfo.AMDExtendedCpuFeatures
|
|
);
|
|
|
|
if ( !Succ ) {
|
|
Cpu->X86CpuInfo.AMDExtendedCpuFeatures = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
#else // #if defined(i386)
|
|
|
|
VOID
|
|
GetCpuInformation(
|
|
PCPU_INFORMATION Cpu
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get CPU information for non-X86 platform using the
|
|
IsProcessorFeaturePresent() API call.
|
|
|
|
Arguments:
|
|
|
|
Cpu - A buffer where the processor feature information will be copied.
|
|
Note: we copy the processor features as a set of bits or'd together.
|
|
Also, we only allow for the first 128 processor feature flags.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
DWORD j;
|
|
|
|
for (i = 0; i < ARRAY_COUNT (Cpu->OtherCpuInfo.ProcessorFeatures); i++) {
|
|
|
|
Cpu->OtherCpuInfo.ProcessorFeatures[i] = 0;
|
|
for (j = 0; j < 64; j++) {
|
|
if (IsProcessorFeaturePresent ( j + i * 64 )) {
|
|
Cpu->OtherCpuInfo.ProcessorFeatures[i] |= 1 << j;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // #if defined(i386)
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetCpuInfo(OUT PUSHORT Architecture,
|
|
OUT PUSHORT Level,
|
|
OUT PUSHORT Revision,
|
|
OUT PUCHAR NumberOfProcessors,
|
|
OUT PCPU_INFORMATION Info)
|
|
{
|
|
SYSTEM_INFO SysInfo;
|
|
|
|
GetSystemInfo(&SysInfo);
|
|
|
|
*Architecture = SysInfo.wProcessorArchitecture;
|
|
*Level = SysInfo.wProcessorLevel;
|
|
*Revision = SysInfo.wProcessorRevision;
|
|
*NumberOfProcessors = (UCHAR)SysInfo.dwNumberOfProcessors;
|
|
GetCpuInformation(Info);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::GetContextSizes(OUT PULONG Size,
|
|
OUT PULONG RegScanOffset,
|
|
OUT PULONG RegScanCount)
|
|
{
|
|
*Size = sizeof(CONTEXT);
|
|
|
|
#ifdef _X86_
|
|
// X86 has two sizes of context.
|
|
switch(m_PlatformId) {
|
|
case VER_PLATFORM_WIN32_NT:
|
|
if (m_BuildNumber < NT_BUILD_WIN2K) {
|
|
*Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
|
}
|
|
break;
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
if (m_BuildNumber <= 1998) {
|
|
*Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
|
}
|
|
break;
|
|
default:
|
|
*Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
// Default reg scan.
|
|
*RegScanOffset = -1;
|
|
*RegScanCount = -1;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::GetPointerSize(OUT PULONG Size)
|
|
{
|
|
*Size = sizeof(PVOID);
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::GetPageSize(OUT PULONG Size)
|
|
{
|
|
*Size = PAGE_SIZE;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::GetFunctionTableSizes(OUT PULONG TableSize,
|
|
OUT PULONG EntrySize)
|
|
{
|
|
#if defined(_IA64_) || defined(_AMD64_)
|
|
*TableSize = sizeof(DYNAMIC_FUNCTION_TABLE);
|
|
*EntrySize = sizeof(RUNTIME_FUNCTION);
|
|
#else
|
|
*TableSize = 0;
|
|
*EntrySize = 0;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::GetInstructionWindowSize(OUT PULONG Size)
|
|
{
|
|
// Default window.
|
|
*Size = -1;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetOsInfo(OUT PULONG PlatformId,
|
|
OUT PULONG Major,
|
|
OUT PULONG Minor,
|
|
OUT PULONG BuildNumber,
|
|
OUT PUSHORT ProductType,
|
|
OUT PUSHORT SuiteMask)
|
|
{
|
|
OSVERSIONINFOEXA OsInfo;
|
|
|
|
// Try first with the EX struct.
|
|
OsInfo.dwOSVersionInfoSize = sizeof(OsInfo);
|
|
|
|
if (!GetVersionExA((LPOSVERSIONINFO)&OsInfo)) {
|
|
// EX struct didn't work, try with the basic struct.
|
|
ZeroMemory(&OsInfo, sizeof(OsInfo));
|
|
OsInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
|
if (!GetVersionExA((LPOSVERSIONINFO)&OsInfo)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
}
|
|
|
|
*PlatformId = OsInfo.dwPlatformId;
|
|
*Major = OsInfo.dwMajorVersion;
|
|
*Minor = OsInfo.dwMinorVersion;
|
|
*BuildNumber = OsInfo.dwBuildNumber;
|
|
*ProductType = OsInfo.wProductType;
|
|
*SuiteMask = OsInfo.wSuiteMask;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetOsCsdString(OUT PWSTR Buffer,
|
|
IN ULONG BufferChars)
|
|
{
|
|
OSVERSIONINFOW OsInfoW;
|
|
|
|
// Try first with the Unicode struct.
|
|
OsInfoW.dwOSVersionInfoSize = sizeof(OsInfoW);
|
|
if (GetVersionExW(&OsInfoW)) {
|
|
// Got it.
|
|
GenStrCopyNW(Buffer, OsInfoW.szCSDVersion, BufferChars);
|
|
return S_OK;
|
|
}
|
|
|
|
OSVERSIONINFOA OsInfoA;
|
|
|
|
// Unicode struct didn't work, try with the ANSI struct.
|
|
OsInfoA.dwOSVersionInfoSize = sizeof(OsInfoA);
|
|
if (!GetVersionExA(&OsInfoA)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
if (!MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
OsInfoA.szCSDVersion,
|
|
-1,
|
|
Buffer,
|
|
BufferChars)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::OpenMapping(IN PCWSTR FilePath,
|
|
OUT PULONG Size,
|
|
OUT PWSTR LongPath,
|
|
IN ULONG LongPathChars,
|
|
OUT PVOID* ViewRet)
|
|
{
|
|
HRESULT Status;
|
|
HANDLE File;
|
|
HANDLE Mapping;
|
|
PVOID View;
|
|
DWORD Chars;
|
|
|
|
//
|
|
// The module may be loaded with a short name. Open
|
|
// the mapping with the name given, but also determine
|
|
// the long name if possible. This is done here as
|
|
// the ANSI/Unicode issues are already being handled here.
|
|
//
|
|
|
|
File = CreateFileW(FilePath,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if ( File == NULL || File == INVALID_HANDLE_VALUE ) {
|
|
|
|
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
|
|
|
|
// We're on an OS that doesn't support Unicode
|
|
// file operations. Convert to ANSI and see if
|
|
// that helps.
|
|
|
|
CHAR FilePathA [ MAX_PATH + 10 ];
|
|
|
|
if (WideCharToMultiByte (CP_ACP,
|
|
0,
|
|
FilePath,
|
|
-1,
|
|
FilePathA,
|
|
sizeof (FilePathA),
|
|
0,
|
|
0
|
|
) > 0) {
|
|
|
|
File = CreateFileA(FilePathA,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
0,
|
|
NULL);
|
|
if (File != INVALID_HANDLE_VALUE) {
|
|
if (!m_GetLongPathNameA) {
|
|
Chars = 0;
|
|
} else {
|
|
Chars = m_GetLongPathNameA(FilePathA, FilePathA,
|
|
ARRAY_COUNT(FilePathA));
|
|
}
|
|
if (Chars == 0 || Chars >= ARRAY_COUNT(FilePathA) ||
|
|
MultiByteToWideChar(CP_ACP, 0, FilePathA, -1,
|
|
LongPath, LongPathChars) == 0) {
|
|
// Couldn't get the long path, just use the
|
|
// given path.
|
|
GenStrCopyNW(LongPath, FilePath, LongPathChars);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( File == NULL || File == INVALID_HANDLE_VALUE ) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
} else {
|
|
if (!m_GetLongPathNameW) {
|
|
Chars = 0;
|
|
} else {
|
|
Chars = m_GetLongPathNameW(FilePath, LongPath, LongPathChars);
|
|
}
|
|
if (Chars == 0 || Chars >= LongPathChars) {
|
|
// Couldn't get the long path, just use the given path.
|
|
GenStrCopyNW(LongPath, FilePath, LongPathChars);
|
|
}
|
|
}
|
|
|
|
*Size = GetFileSize(File, NULL);
|
|
if (*Size == -1) {
|
|
::CloseHandle( File );
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
Mapping = CreateFileMapping(File,
|
|
NULL,
|
|
PAGE_READONLY,
|
|
0,
|
|
0,
|
|
NULL);
|
|
if (!Mapping) {
|
|
::CloseHandle(File);
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
View = MapViewOfFile(Mapping,
|
|
FILE_MAP_READ,
|
|
0,
|
|
0,
|
|
0);
|
|
|
|
if (!View) {
|
|
Status = WIN32_LAST_STATUS();
|
|
} else {
|
|
Status = S_OK;
|
|
}
|
|
|
|
::CloseHandle(Mapping);
|
|
::CloseHandle(File);
|
|
|
|
*ViewRet = View;
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::CloseMapping(PVOID Mapping)
|
|
{
|
|
UnmapViewOfFile(Mapping);
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetImageHeaderInfo(IN HANDLE Process,
|
|
IN PCWSTR FilePath,
|
|
IN ULONG64 ImageBase,
|
|
OUT PULONG Size,
|
|
OUT PULONG CheckSum,
|
|
OUT PULONG TimeDateStamp)
|
|
{
|
|
UCHAR HeaderBuffer[512];
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
IMAGE_NT_HEADERS64 Generic;
|
|
SIZE_T Done;
|
|
|
|
if (!ReadProcessMemory(Process, (PVOID)(ULONG_PTR)ImageBase,
|
|
HeaderBuffer, sizeof(HeaderBuffer), &Done)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
if (Done < sizeof(HeaderBuffer)) {
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
|
|
NtHeaders = GenImageNtHeader(HeaderBuffer, &Generic);
|
|
if (!NtHeaders) {
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_DLL);
|
|
}
|
|
|
|
*Size = Generic.OptionalHeader.SizeOfImage;
|
|
*CheckSum = Generic.OptionalHeader.CheckSum;
|
|
*TimeDateStamp = Generic.FileHeader.TimeDateStamp;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetImageVersionInfo(IN HANDLE Process,
|
|
IN PCWSTR FilePath,
|
|
IN ULONG64 ImageBase,
|
|
OUT VS_FIXEDFILEINFO* Info)
|
|
{
|
|
HRESULT Status;
|
|
BOOL Succ;
|
|
ULONG Unused;
|
|
ULONG Size;
|
|
UINT VerSize;
|
|
PVOID VersionBlock;
|
|
PVOID VersionData;
|
|
CHAR FilePathA [ MAX_PATH + 10 ];
|
|
BOOL UseAnsi = FALSE;
|
|
|
|
//
|
|
// Get the version information.
|
|
//
|
|
|
|
Size = GetFileVersionInfoSizeW (FilePath, &Unused);
|
|
|
|
if (Size == 0 &&
|
|
GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
|
|
|
|
// We're on an OS that doesn't support Unicode
|
|
// file operations. Convert to ANSI and see if
|
|
// that helps.
|
|
|
|
if (!WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
FilePath,
|
|
-1,
|
|
FilePathA,
|
|
sizeof (FilePathA),
|
|
0,
|
|
0
|
|
)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
Size = GetFileVersionInfoSizeA(FilePathA, &Unused);
|
|
UseAnsi = TRUE;
|
|
}
|
|
|
|
if (!Size) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
VersionBlock = HeapAlloc(GetProcessHeap(), 0, Size);
|
|
if (!VersionBlock) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if (UseAnsi) {
|
|
Succ = GetFileVersionInfoA(FilePathA,
|
|
0,
|
|
Size,
|
|
VersionBlock);
|
|
} else {
|
|
Succ = GetFileVersionInfoW(FilePath,
|
|
0,
|
|
Size,
|
|
VersionBlock);
|
|
}
|
|
|
|
if (Succ) {
|
|
//
|
|
// Get the VS_FIXEDFILEINFO from the image.
|
|
//
|
|
|
|
Succ = VerQueryValue(VersionBlock,
|
|
"\\",
|
|
&VersionData,
|
|
&VerSize);
|
|
|
|
if ( Succ && (VerSize == sizeof (VS_FIXEDFILEINFO)) ) {
|
|
CopyMemory(Info, VersionData, sizeof(*Info));
|
|
} else {
|
|
Succ = FALSE;
|
|
}
|
|
}
|
|
|
|
if (Succ) {
|
|
Status = S_OK;
|
|
} else {
|
|
Status = WIN32_LAST_STATUS();
|
|
}
|
|
|
|
HeapFree(GetProcessHeap(), 0, VersionBlock);
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetImageDebugRecord(IN HANDLE Process,
|
|
IN PCWSTR FilePath,
|
|
IN ULONG64 ImageBase,
|
|
IN ULONG RecordType,
|
|
OUT OPTIONAL PVOID Data,
|
|
IN OUT PULONG DataLen)
|
|
{
|
|
// We can rely on the default processing.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumImageDataSections(IN HANDLE Process,
|
|
IN PCWSTR FilePath,
|
|
IN ULONG64 ImageBase,
|
|
IN MiniDumpProviderCallbacks*
|
|
Callback)
|
|
{
|
|
// We can rely on the default processing.
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::OpenThread(IN ULONG DesiredAccess,
|
|
IN BOOL InheritHandle,
|
|
IN ULONG ThreadId,
|
|
OUT PHANDLE Handle)
|
|
{
|
|
if (!m_OpenThread) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
*Handle = m_OpenThread(DesiredAccess, InheritHandle, ThreadId);
|
|
return *Handle ? S_OK : WIN32_LAST_STATUS();
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::CloseThread(IN HANDLE Handle)
|
|
{
|
|
::CloseHandle(Handle);
|
|
}
|
|
|
|
ULONG
|
|
Win32LiveSystemProvider::GetCurrentThreadId(void)
|
|
{
|
|
return ::GetCurrentThreadId();
|
|
}
|
|
|
|
ULONG
|
|
Win32LiveSystemProvider::SuspendThread(IN HANDLE Thread)
|
|
{
|
|
return ::SuspendThread(Thread);
|
|
}
|
|
|
|
ULONG
|
|
Win32LiveSystemProvider::ResumeThread(IN HANDLE Thread)
|
|
{
|
|
return ::ResumeThread(Thread);
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetThreadContext(IN HANDLE Thread,
|
|
OUT PVOID Context,
|
|
IN ULONG ContextSize,
|
|
OUT PULONG64 CurrentPc,
|
|
OUT PULONG64 CurrentStack,
|
|
OUT PULONG64 CurrentStore)
|
|
{
|
|
CONTEXT StackContext;
|
|
BOOL Succ;
|
|
|
|
if (ContextSize > sizeof(StackContext)) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
// Always call GetThreadContext on the CONTEXT structure
|
|
// on the stack as CONTEXTs have strict alignment requirements
|
|
// and the raw buffer coming in may not obey them.
|
|
StackContext.ContextFlags = ALL_REGISTERS;
|
|
|
|
Succ = ::GetThreadContext(Thread, &StackContext);
|
|
|
|
if (Succ) {
|
|
|
|
memcpy(Context, &StackContext, ContextSize);
|
|
*CurrentPc = PROGRAM_COUNTER(&StackContext);
|
|
*CurrentStack = STACK_POINTER(&StackContext);
|
|
#ifdef DUMP_BACKING_STORE
|
|
*CurrentStore = BSTORE_POINTER(&StackContext);
|
|
#endif
|
|
|
|
return S_OK;
|
|
|
|
} else {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetProcessTimes(IN HANDLE Process,
|
|
OUT LPFILETIME Create,
|
|
OUT LPFILETIME User,
|
|
OUT LPFILETIME Kernel)
|
|
{
|
|
if (!m_GetProcessTimes) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
FILETIME Exit;
|
|
|
|
if (!m_GetProcessTimes(Process, Create, &Exit, User, Kernel)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::ReadVirtual(IN HANDLE Process,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Request,
|
|
OUT PULONG Done)
|
|
{
|
|
// ReadProcessMemory will fail if any part of the
|
|
// region to read does not have read access. This
|
|
// routine attempts to read the largest valid prefix
|
|
// so it has to break up reads on page boundaries.
|
|
|
|
HRESULT Status = S_OK;
|
|
SIZE_T TotalBytesRead = 0;
|
|
SIZE_T Read;
|
|
ULONG ReadSize;
|
|
|
|
while (Request > 0) {
|
|
|
|
// Calculate bytes to read and don't let read cross
|
|
// a page boundary.
|
|
ReadSize = PAGE_SIZE - (ULONG)(Offset & (PAGE_SIZE - 1));
|
|
ReadSize = min(Request, ReadSize);
|
|
|
|
if (!ReadProcessMemory(Process, (PVOID)(ULONG_PTR)Offset,
|
|
Buffer, ReadSize, &Read)) {
|
|
if (TotalBytesRead == 0) {
|
|
// If we haven't read anything indicate failure.
|
|
Status = WIN32_LAST_STATUS();
|
|
}
|
|
break;
|
|
}
|
|
|
|
TotalBytesRead += Read;
|
|
Offset += Read;
|
|
Buffer = (PVOID)((PUCHAR)Buffer + Read);
|
|
Request -= (ULONG)Read;
|
|
}
|
|
|
|
*Done = (ULONG)TotalBytesRead;
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::ReadAllVirtual(IN HANDLE Process,
|
|
IN ULONG64 Offset,
|
|
OUT PVOID Buffer,
|
|
IN ULONG Request)
|
|
{
|
|
HRESULT Status;
|
|
ULONG Done;
|
|
|
|
if ((Status = ReadVirtual(Process, Offset, Buffer, Request,
|
|
&Done)) != S_OK)
|
|
{
|
|
return Status;
|
|
}
|
|
if (Done != Request)
|
|
{
|
|
return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::QueryVirtual(IN HANDLE Process,
|
|
IN ULONG64 Offset,
|
|
OUT PULONG64 Base,
|
|
OUT PULONG64 Size,
|
|
OUT PULONG Protect,
|
|
OUT PULONG State,
|
|
OUT PULONG Type)
|
|
{
|
|
MEMORY_BASIC_INFORMATION Info;
|
|
|
|
if (!VirtualQueryEx(Process, (PVOID)(ULONG_PTR)Offset,
|
|
&Info, sizeof(Info))) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
*Base = (LONG_PTR)Info.BaseAddress;
|
|
*Size = Info.RegionSize;
|
|
*Protect = Info.Protect;
|
|
*State = Info.State;
|
|
*Type = Info.Type;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::StartProcessEnum(IN HANDLE Process,
|
|
IN ULONG ProcessId)
|
|
{
|
|
ULONG SnapFlags;
|
|
|
|
if (!m_CreateToolhelp32Snapshot) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// Toolhelp on older NT builds uses an in-process enumeration
|
|
// of modules so don't use it to keep everything out of process.
|
|
// On other platforms it's the only option.
|
|
//
|
|
|
|
SnapFlags = TH32CS_SNAPTHREAD;
|
|
if (m_PlatformId == VER_PLATFORM_WIN32_NT) {
|
|
if (m_BuildNumber >= NT_BUILD_TH_MODULES) {
|
|
m_AnsiModules = FALSE;
|
|
SnapFlags |= TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32;
|
|
}
|
|
} else {
|
|
m_AnsiModules = TRUE;
|
|
SnapFlags |= TH32CS_SNAPMODULE;
|
|
}
|
|
|
|
m_ThSnap = m_CreateToolhelp32Snapshot(SnapFlags, ProcessId);
|
|
if (m_ThSnap == INVALID_HANDLE_VALUE) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
m_ProcessHandle = Process;
|
|
m_ProcessId = ProcessId;
|
|
m_ThreadIndex = 0;
|
|
m_ModuleIndex = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumThreads(OUT PULONG ThreadId)
|
|
{
|
|
HRESULT Status;
|
|
THREADENTRY32 ThreadInfo;
|
|
|
|
ThreadInfo.dwSize = sizeof(ThreadInfo);
|
|
|
|
if (m_ThreadIndex == 0) {
|
|
Status = ProcessThread32First(m_ThSnap, m_ProcessId, &ThreadInfo);
|
|
} else {
|
|
Status = ProcessThread32Next(m_ThSnap, m_ProcessId, &ThreadInfo);
|
|
}
|
|
|
|
if (Status == S_OK) {
|
|
*ThreadId = ThreadInfo.th32ThreadID;
|
|
m_ThreadIndex++;
|
|
return S_OK;
|
|
} else {
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumModules(OUT PULONG64 Base,
|
|
OUT PWSTR Path,
|
|
IN ULONG PathChars)
|
|
{
|
|
BOOL Succ;
|
|
|
|
if (m_AnsiModules) {
|
|
|
|
if (!m_Module32First || !m_Module32Next) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
MODULEENTRY32 ModuleInfo;
|
|
|
|
ModuleInfo.dwSize = sizeof(ModuleInfo);
|
|
|
|
if (m_ModuleIndex == 0) {
|
|
Succ = m_Module32First(m_ThSnap, &ModuleInfo);
|
|
} else {
|
|
// Win9x seems to require that this module ID be saved
|
|
// between calls so stick it back in to keep Win9x happy.
|
|
ModuleInfo.th32ModuleID = m_LastModuleId;
|
|
Succ = m_Module32Next(m_ThSnap, &ModuleInfo);
|
|
}
|
|
|
|
if (Succ) {
|
|
m_ModuleIndex++;
|
|
*Base = (LONG_PTR)ModuleInfo.modBaseAddr;
|
|
m_LastModuleId = ModuleInfo.th32ModuleID;
|
|
if (!MultiByteToWideChar(CP_ACP,
|
|
0,
|
|
ModuleInfo.szExePath,
|
|
-1,
|
|
Path,
|
|
PathChars)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
return S_OK;
|
|
} else {
|
|
return S_FALSE;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!m_Module32FirstW || !m_Module32NextW) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
MODULEENTRY32W ModuleInfo;
|
|
|
|
ModuleInfo.dwSize = sizeof(ModuleInfo);
|
|
|
|
if (m_ModuleIndex == 0) {
|
|
Succ = m_Module32FirstW(m_ThSnap, &ModuleInfo);
|
|
} else {
|
|
Succ = m_Module32NextW(m_ThSnap, &ModuleInfo);
|
|
}
|
|
|
|
if (Succ) {
|
|
m_ModuleIndex++;
|
|
*Base = (LONG_PTR)ModuleInfo.modBaseAddr;
|
|
|
|
//
|
|
// The basic LdrQueryProcessModule API that toolhelp uses
|
|
// always returns ANSI strings for module paths. This
|
|
// means that even if you use the wide toolhelp calls
|
|
// you still lose Unicode information because the original
|
|
// Unicode path was converted to ANSI and then back to Unicode.
|
|
// To avoid this problem, always try and look up the true
|
|
// Unicode path first. This doesn't work for 32-bit modules
|
|
// in WOW64, though, so if there's a failure just use the
|
|
// incoming string.
|
|
//
|
|
|
|
if (!m_GetModuleFileNameExW ||
|
|
!m_GetModuleFileNameExW(m_ProcessHandle,
|
|
ModuleInfo.hModule,
|
|
Path,
|
|
PathChars)) {
|
|
GenStrCopyNW(Path, ModuleInfo.szExePath, PathChars);
|
|
}
|
|
return S_OK;
|
|
} else {
|
|
return S_FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumFunctionTables(OUT PULONG64 MinAddress,
|
|
OUT PULONG64 MaxAddress,
|
|
OUT PULONG64 BaseAddress,
|
|
OUT PULONG EntryCount,
|
|
OUT PVOID RawTable,
|
|
IN ULONG RawTableSize,
|
|
OUT PVOID* RawEntryHandle)
|
|
{
|
|
// Basic Win32 doesn't have function tables.
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumFunctionTableEntries(IN PVOID RawTable,
|
|
IN ULONG RawTableSize,
|
|
IN PVOID RawEntryHandle,
|
|
OUT PVOID RawEntries,
|
|
IN ULONG RawEntriesSize)
|
|
{
|
|
// Basic Win32 doesn't have function tables.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumFunctionTableEntryMemory(IN ULONG64 TableBase,
|
|
IN PVOID RawEntries,
|
|
IN ULONG Index,
|
|
OUT PULONG64 Start,
|
|
OUT PULONG Size)
|
|
{
|
|
// Basic Win32 doesn't have function tables.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumUnloadedModules(OUT PWSTR Path,
|
|
IN ULONG PathChars,
|
|
OUT PULONG64 BaseOfModule,
|
|
OUT PULONG SizeOfModule,
|
|
OUT PULONG CheckSum,
|
|
OUT PULONG TimeDateStamp)
|
|
{
|
|
// Basic Win32 doesn't have unloaded modules.
|
|
return S_FALSE;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::FinishProcessEnum(void)
|
|
{
|
|
::CloseHandle(m_ThSnap);
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::StartHandleEnum(IN HANDLE Process,
|
|
IN ULONG ProcessId,
|
|
OUT PULONG Count)
|
|
{
|
|
// Basic Win32 doesn't have handle data queries.
|
|
*Count = 0;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumHandles(OUT PULONG64 Handle,
|
|
OUT PULONG Attributes,
|
|
OUT PULONG GrantedAccess,
|
|
OUT PULONG HandleCount,
|
|
OUT PULONG PointerCount,
|
|
OUT PWSTR TypeName,
|
|
IN ULONG TypeNameChars,
|
|
OUT PWSTR ObjectName,
|
|
IN ULONG ObjectNameChars)
|
|
{
|
|
// Basic Win32 doesn't have handle data queries.
|
|
return S_FALSE;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::FinishHandleEnum(void)
|
|
{
|
|
// Basic Win32 doesn't have handle data queries.
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumPebMemory(IN HANDLE Process,
|
|
IN ULONG64 PebOffset,
|
|
IN ULONG PebSize,
|
|
IN MiniDumpProviderCallbacks* Callback)
|
|
{
|
|
// Basic Win32 doesn't have a defined PEB.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::EnumTebMemory(IN HANDLE Process,
|
|
IN HANDLE Thread,
|
|
IN ULONG64 TebOffset,
|
|
IN ULONG TebSize,
|
|
IN MiniDumpProviderCallbacks* Callback)
|
|
{
|
|
// Basic Win32 doesn't have a defined TEB beyond
|
|
// the TIB. The TIB can reference fiber data but
|
|
// that's NT-specific.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::GetCorDataAccess(IN PWSTR AccessDllName,
|
|
IN struct ICorDataAccessServices*
|
|
Services,
|
|
OUT struct ICorDataAccess**
|
|
Access)
|
|
{
|
|
HRESULT Status;
|
|
|
|
m_CorDll = ::LoadLibraryW(AccessDllName);
|
|
if (!m_CorDll) {
|
|
char DllPathA[MAX_PATH];
|
|
|
|
if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED ||
|
|
!WideCharToMultiByte(CP_ACP, 0,
|
|
AccessDllName, -1,
|
|
DllPathA, sizeof(DllPathA),
|
|
0, 0) ||
|
|
!(m_CorDll = ::LoadLibraryA(DllPathA))) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
}
|
|
|
|
PFN_CreateCorDataAccess Entry = (PFN_CreateCorDataAccess)
|
|
GetProcAddress(m_CorDll, "CreateCorDataAccess");
|
|
if (!Entry)
|
|
{
|
|
Status = WIN32_LAST_STATUS();
|
|
FreeLibrary(m_CorDll);
|
|
return Status;
|
|
}
|
|
|
|
if ((Status = Entry(__uuidof(ICorDataAccess), Services,
|
|
(void**)Access)) != S_OK)
|
|
{
|
|
FreeLibrary(m_CorDll);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
void
|
|
Win32LiveSystemProvider::ReleaseCorDataAccess(IN struct ICorDataAccess*
|
|
Access)
|
|
{
|
|
Access->Release();
|
|
::FreeLibrary(m_CorDll);
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::ProcessThread32Next(IN HANDLE Snapshot,
|
|
IN ULONG ProcessId,
|
|
OUT THREADENTRY32* ThreadInfo)
|
|
{
|
|
BOOL Succ;
|
|
|
|
if (!m_Thread32Next) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
//
|
|
// NB: Toolhelp says nothing about the order of the threads will be
|
|
// returned in (i.e., if they are grouped by process or not). If they
|
|
// are groupled by process -- which they emperically seem to be -- there
|
|
// is a more efficient algorithm than simple brute force.
|
|
//
|
|
|
|
do {
|
|
ThreadInfo->dwSize = sizeof (*ThreadInfo);
|
|
Succ = m_Thread32Next(Snapshot, ThreadInfo);
|
|
} while (Succ && ThreadInfo->th32OwnerProcessID != ProcessId);
|
|
|
|
return Succ ? S_OK : WIN32_LAST_STATUS();
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::ProcessThread32First(IN HANDLE Snapshot,
|
|
IN ULONG ProcessId,
|
|
OUT THREADENTRY32* ThreadInfo)
|
|
{
|
|
HRESULT Status;
|
|
BOOL Succ;
|
|
|
|
if (!m_Thread32First) {
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
ThreadInfo->dwSize = sizeof (*ThreadInfo);
|
|
Succ = m_Thread32First(Snapshot, ThreadInfo);
|
|
Status = Succ ? S_OK : WIN32_LAST_STATUS();
|
|
if (Succ && ThreadInfo->th32OwnerProcessID != ProcessId) {
|
|
Status = ProcessThread32Next (Snapshot, ProcessId, ThreadInfo);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
HRESULT
|
|
Win32LiveSystemProvider::TibGetThreadInfo(IN HANDLE Process,
|
|
IN ULONG64 TibBase,
|
|
OUT PULONG64 StackBase,
|
|
OUT PULONG64 StackLimit,
|
|
OUT PULONG64 StoreBase,
|
|
OUT PULONG64 StoreLimit)
|
|
{
|
|
#ifdef _WIN32_WCE
|
|
return E_NOTIMPL;
|
|
#else
|
|
TEB Teb;
|
|
HRESULT Status;
|
|
|
|
#if defined (DUMP_BACKING_STORE)
|
|
|
|
if ((Status = ReadAllVirtual(Process,
|
|
TibBase,
|
|
&Teb,
|
|
sizeof(Teb))) != S_OK) {
|
|
return Status;
|
|
}
|
|
|
|
*StoreBase = BSTORE_BASE(&Teb);
|
|
*StoreLimit = BSTORE_LIMIT(&Teb);
|
|
|
|
#else
|
|
|
|
if ((Status = ReadAllVirtual(Process,
|
|
TibBase,
|
|
&Teb,
|
|
sizeof(Teb.NtTib))) != S_OK) {
|
|
return Status;
|
|
}
|
|
|
|
*StoreBase = 0;
|
|
*StoreLimit = 0;
|
|
|
|
#endif
|
|
|
|
*StackBase = (LONG_PTR)Teb.NtTib.StackBase;
|
|
*StackLimit = (LONG_PTR)Teb.NtTib.StackLimit;
|
|
|
|
return S_OK;
|
|
#endif // #ifdef _WIN32_WCE
|
|
}
|
|
|
|
HRESULT
|
|
MiniDumpCreateLiveSystemProvider
|
|
(OUT MiniDumpSystemProvider** Prov)
|
|
{
|
|
HRESULT Status;
|
|
OSVERSIONINFO OsInfo;
|
|
Win32LiveSystemProvider* Obj;
|
|
|
|
OsInfo.dwOSVersionInfoSize = sizeof(OsInfo);
|
|
if (!GetVersionEx(&OsInfo)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
switch(OsInfo.dwPlatformId) {
|
|
case VER_PLATFORM_WIN32_NT:
|
|
Obj = NewNtWin32LiveSystemProvider(OsInfo.dwBuildNumber);
|
|
break;
|
|
case VER_PLATFORM_WIN32_WINDOWS:
|
|
Obj = NewWin9xWin32LiveSystemProvider(OsInfo.dwBuildNumber);
|
|
break;
|
|
case VER_PLATFORM_WIN32_CE:
|
|
Obj = NewWinCeWin32LiveSystemProvider(OsInfo.dwBuildNumber);
|
|
break;
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
if (!Obj) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
if ((Status = Obj->Initialize()) != S_OK) {
|
|
Obj->Release();
|
|
return Status;
|
|
}
|
|
|
|
*Prov = (MiniDumpSystemProvider*)Obj;
|
|
return S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Win32FileOutputProvider.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
class Win32FileOutputProvider
|
|
{
|
|
public:
|
|
Win32FileOutputProvider(HANDLE Handle);
|
|
virtual void Release(void);
|
|
|
|
virtual HRESULT SupportsStreaming(void);
|
|
virtual HRESULT Start(IN ULONG64 MaxSize);
|
|
virtual HRESULT Seek(IN ULONG How,
|
|
IN LONG64 Amount,
|
|
OUT OPTIONAL PULONG64 NewOffset);
|
|
virtual HRESULT WriteAll(IN PVOID Buffer,
|
|
IN ULONG Request);
|
|
virtual void Finish(void);
|
|
|
|
protected:
|
|
HANDLE m_Handle;
|
|
};
|
|
|
|
Win32FileOutputProvider::Win32FileOutputProvider(HANDLE Handle)
|
|
{
|
|
m_Handle = Handle;
|
|
}
|
|
|
|
void
|
|
Win32FileOutputProvider::Release(void)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
HRESULT
|
|
Win32FileOutputProvider::SupportsStreaming(void)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32FileOutputProvider::Start(IN ULONG64 MaxSize)
|
|
{
|
|
// Nothing to do.
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32FileOutputProvider::Seek(IN ULONG How,
|
|
IN LONG64 Amount,
|
|
OUT OPTIONAL PULONG64 NewOffset)
|
|
{
|
|
ULONG Ret;
|
|
LONG High;
|
|
|
|
High = (LONG)(Amount >> 32);
|
|
Ret = SetFilePointer(m_Handle, (LONG)Amount, &High, How);
|
|
if (Ret == INVALID_SET_FILE_POINTER &&
|
|
GetLastError()) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
|
|
if (NewOffset) {
|
|
*NewOffset = ((ULONG64)High << 32) | Ret;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
Win32FileOutputProvider::WriteAll(IN PVOID Buffer,
|
|
IN ULONG Request)
|
|
{
|
|
ULONG Done;
|
|
|
|
if (!WriteFile(m_Handle, Buffer, Request, &Done, NULL)) {
|
|
return WIN32_LAST_STATUS();
|
|
}
|
|
if (Done != Request) {
|
|
return HRESULT_FROM_WIN32(ERROR_WRITE_FAULT);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void
|
|
Win32FileOutputProvider::Finish(void)
|
|
{
|
|
// Nothing to do.
|
|
}
|
|
|
|
HRESULT
|
|
MiniDumpCreateFileOutputProvider
|
|
(IN HANDLE FileHandle,
|
|
OUT MiniDumpOutputProvider** Prov)
|
|
{
|
|
Win32FileOutputProvider* Obj =
|
|
new Win32FileOutputProvider(FileHandle);
|
|
if (!Obj) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*Prov = (MiniDumpOutputProvider*)Obj;
|
|
return S_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// Win32LiveAllocationProvider.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
class Win32LiveAllocationProvider : public MiniDumpAllocationProvider
|
|
{
|
|
public:
|
|
virtual void Release(void);
|
|
virtual PVOID Alloc(ULONG Size);
|
|
virtual PVOID Realloc(PVOID Mem, ULONG NewSize);
|
|
virtual void Free(PVOID Mem);
|
|
};
|
|
|
|
void
|
|
Win32LiveAllocationProvider::Release(void)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
PVOID
|
|
Win32LiveAllocationProvider::Alloc(ULONG Size)
|
|
{
|
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
|
|
}
|
|
|
|
PVOID
|
|
Win32LiveAllocationProvider::Realloc(PVOID Mem, ULONG NewSize)
|
|
{
|
|
return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Mem, NewSize);
|
|
}
|
|
|
|
void
|
|
Win32LiveAllocationProvider::Free(PVOID Mem)
|
|
{
|
|
if (Mem) {
|
|
HeapFree(GetProcessHeap(), 0, Mem);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
MiniDumpCreateLiveAllocationProvider
|
|
(OUT MiniDumpAllocationProvider** Prov)
|
|
{
|
|
Win32LiveAllocationProvider* Obj =
|
|
new Win32LiveAllocationProvider;
|
|
if (!Obj) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*Prov = (MiniDumpAllocationProvider*)Obj;
|
|
return S_OK;
|
|
}
|