718 lines
18 KiB
C
718 lines
18 KiB
C
/*++
|
|
|
|
Copyright (c) 1996-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dumpcd.c
|
|
|
|
Abstract:
|
|
|
|
This module dumps the contents of the
|
|
entrypoint tree and translation cache to a file.
|
|
|
|
Author:
|
|
|
|
Dave Hastings (daveh) creation-date 02-May-1996
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#ifdef CODEGEN_PROFILE
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <entrypt.h>
|
|
#include <coded.h>
|
|
|
|
extern PEPNODE intelRoot;
|
|
extern EPNODE _NIL;
|
|
|
|
ULONG ProfileFlags = 0;
|
|
|
|
BOOL
|
|
ProcessEntrypoint(
|
|
PENTRYPOINT Entrypoint
|
|
);
|
|
|
|
|
|
#define CODE_BUFFER_SIZE 8184
|
|
UCHAR IntelCodeBuffer[CODE_BUFFER_SIZE];
|
|
ULONG NativeCodeBuffer[CODE_BUFFER_SIZE];
|
|
|
|
#define STACK_DEPTH 200
|
|
|
|
ULONG DumpStack[STACK_DEPTH];
|
|
ULONG DumpStackTop;
|
|
|
|
#define STACK_RESET() DumpStackTop=0;
|
|
|
|
#define PUSH(x) { \
|
|
if (DumpStackTop == STACK_DEPTH-1) { \
|
|
CHAR ErrorString[80]; \
|
|
sprintf(ErrorString, "Error: Dump stack overflow\n"); \
|
|
OutputDebugString(ErrorString); \
|
|
goto Exit; \
|
|
} else { \
|
|
DumpStack[DumpStackTop] = x; \
|
|
DumpStackTop++; \
|
|
} \
|
|
}
|
|
|
|
#define POP(x) { \
|
|
if (DumpStackTop == 0) { \
|
|
CHAR ErrorString[80]; \
|
|
sprintf(ErrorString, "Error: Dump stack underflow\n"); \
|
|
OutputDebugString(ErrorString); \
|
|
goto Exit; \
|
|
} else { \
|
|
DumpStackTop--; \
|
|
x = DumpStack[DumpStackTop]; \
|
|
} \
|
|
}
|
|
|
|
|
|
BOOL CpuCodegenProfile = FALSE;
|
|
PCHAR CpuCodegenProfilePath = NULL;
|
|
HANDLE CpuCodegenProfileFile = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Code Description file state
|
|
//
|
|
ULONG CurrentFileLocation;
|
|
ULONG CodeDescriptionFlags = 0;
|
|
|
|
VOID
|
|
InitCodegenProfile(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets the configuration information from the registry
|
|
and creates the file to put the profile data into.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG RetVal;
|
|
DWORD KeyType;
|
|
DWORD ProfileEnabled;
|
|
DWORD BufferSize;
|
|
CHAR FileName[MAX_PATH];
|
|
LPTSTR CommandLine;
|
|
CODEDESCRIPTIONHEADER Header;
|
|
ULONG CommandLineLength;
|
|
ULONG BytesWritten;
|
|
BOOL Success;
|
|
HKEY Key;
|
|
|
|
//
|
|
// Find out if codegen profiling is enabled. If there is a problem
|
|
// with the value in the registry, we will be disabled by default.
|
|
//
|
|
|
|
RetVal = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
"System\\CurrentControlSet\\Control\\Wx86",
|
|
0,
|
|
KEY_READ,
|
|
&Key
|
|
);
|
|
|
|
BufferSize = sizeof(ProfileEnabled);
|
|
|
|
RetVal = RegQueryValueEx(
|
|
Key,
|
|
"CpuCodegenProfile",
|
|
NULL,
|
|
&KeyType,
|
|
(PVOID)&ProfileEnabled,
|
|
&BufferSize
|
|
);
|
|
|
|
if ((RetVal != ERROR_SUCCESS) || (KeyType != REG_DWORD)) {
|
|
OutputDebugString("Wx86Cpu: No CpuCodegenProfile value, or wrong type\n");
|
|
return;
|
|
}
|
|
|
|
CpuCodegenProfile = ProfileEnabled;
|
|
|
|
//
|
|
// Get the path to store the datafile to.
|
|
// First we get the size of the string (and verify that it is a string)
|
|
// Then we get the actual string.
|
|
//
|
|
BufferSize = 0;
|
|
RetVal = RegQueryValueEx(
|
|
Key,
|
|
"CpuCodegenProfilePath",
|
|
NULL,
|
|
&KeyType,
|
|
(PVOID)&ProfileEnabled,
|
|
&BufferSize
|
|
);
|
|
|
|
if ((RetVal != ERROR_MORE_DATA) || (KeyType != REG_SZ)) {
|
|
OutputDebugString("Wx86Cpu: Problem with CpuCodegenProfilePath\n");
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
CpuCodegenProfilePath = HeapAlloc(GetProcessHeap(), 0, BufferSize);
|
|
|
|
if (CpuCodegenProfilePath == NULL) {
|
|
OutputDebugString("Wx86Cpu: Can't allocate CpuCodegenProfilePath\n");
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
RetVal = RegQueryValueEx(
|
|
Key,
|
|
"CpuCodegenProfilePath",
|
|
NULL,
|
|
&KeyType,
|
|
CpuCodegenProfilePath,
|
|
&BufferSize
|
|
);
|
|
|
|
if ((RetVal != ERROR_SUCCESS) || (KeyType != REG_SZ)) {
|
|
//
|
|
// Something really bad just happened. Don't do the profiling
|
|
//
|
|
OutputDebugString("Wx86Cpu: Inexplicable problem with CpuCodegenProfilePath\n");
|
|
HeapFree(GetProcessHeap(), 0, CpuCodegenProfilePath);
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create file for the data
|
|
//
|
|
RetVal = GetTempFileName(CpuCodegenProfilePath, "prf", 0, FileName);
|
|
|
|
if (RetVal == 0) {
|
|
OutputDebugString("Wx86Cpu: GetTempFileName failed\n");
|
|
HeapFree(GetProcessHeap(), 0, CpuCodegenProfilePath);
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
CpuCodegenProfileFile = CreateFile(
|
|
FileName,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
TRUNCATE_EXISTING,
|
|
FILE_ATTRIBUTE_COMPRESSED,
|
|
NULL
|
|
);
|
|
|
|
if (CpuCodegenProfileFile == INVALID_HANDLE_VALUE) {
|
|
OutputDebugString("Wx86Cpu: Unable to create profile file\n");
|
|
HeapFree(GetProcessHeap(), 0, CpuCodegenProfilePath);
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Write the file header to the file
|
|
//
|
|
CommandLine = GetCommandLine();
|
|
CommandLineLength = strlen(CommandLine) + 1;
|
|
Header.CommandLineOffset = sizeof(CODEDESCRIPTIONHEADER);
|
|
Header.NextCodeDescriptionOffset = ((sizeof(CODEDESCRIPTIONHEADER) +
|
|
CommandLineLength) + 3) & ~3;
|
|
Header.DumpFileRev = CODEGEN_PROFILE_REV;
|
|
Header.StartTime = GetCurrentTime();
|
|
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
&Header,
|
|
sizeof(Header),
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
|
|
if (!Success || (BytesWritten != sizeof(Header))) {
|
|
OutputDebugString("Wx86Cpu: Failed to write profile header\n");
|
|
CloseHandle(CpuCodegenProfileFile);
|
|
HeapFree(GetProcessHeap(), 0, CpuCodegenProfilePath);
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
CommandLine,
|
|
CommandLineLength,
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
|
|
if (!Success || (BytesWritten != CommandLineLength)) {
|
|
OutputDebugString("Wx86Cpu: Failed to write profile header\n");
|
|
CloseHandle(CpuCodegenProfileFile);
|
|
HeapFree(GetProcessHeap(), 0, CpuCodegenProfilePath);
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set the file position for the first code description
|
|
//
|
|
CurrentFileLocation = SetFilePointer(
|
|
CpuCodegenProfileFile,
|
|
Header.NextCodeDescriptionOffset,
|
|
NULL,
|
|
FILE_BEGIN
|
|
);
|
|
|
|
if (CurrentFileLocation != Header.NextCodeDescriptionOffset) {
|
|
OutputDebugString("Wx86Cpu: failed to update file position\n");
|
|
CloseHandle(CpuCodegenProfileFile);
|
|
HeapFree(GetProcessHeap(), 0, CpuCodegenProfilePath);
|
|
CpuCodegenProfile = FALSE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
TerminateCodegenProfile(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function put in the terminating record and closes the file.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CODEDESCRIPTION CodeDescription;
|
|
BOOL Success;
|
|
ULONG BytesWritten;
|
|
CHAR ErrorString[80];
|
|
|
|
if (!CpuCodegenProfile) {
|
|
return;
|
|
}
|
|
CodeDescription.NextCodeDescriptionOffset = 0xFFFFFFFF;
|
|
CodeDescription.TypeTag = PROFILE_TAG_EOF;
|
|
CodeDescription.CreationTime = GetCurrentTime();
|
|
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
&CodeDescription,
|
|
sizeof(CODEDESCRIPTION),
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != sizeof(CODEDESCRIPTION))) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write termination record, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
}
|
|
|
|
CpuCodegenProfile = FALSE;
|
|
CloseHandle(CpuCodegenProfileFile);
|
|
|
|
}
|
|
|
|
VOID
|
|
DumpCodeDescriptions(
|
|
BOOL TCFlush
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dumps out the entrypoints, and the corresponding code
|
|
to a file in binary form.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PEPNODE NextEntrypoint;
|
|
EPNODE Entrypoint;
|
|
ULONG Epcount = 0;
|
|
|
|
if (!CpuCodegenProfile) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the root of the entrypoint tree
|
|
//
|
|
NextEntrypoint = intelRoot;
|
|
|
|
//
|
|
// Initialize stack
|
|
//
|
|
STACK_RESET();
|
|
PUSH(0);
|
|
|
|
//
|
|
// iterate over every entrypoint
|
|
//
|
|
while (NextEntrypoint != NULL) {
|
|
Entrypoint = *NextEntrypoint;
|
|
|
|
//
|
|
// Process the top level entrypoint
|
|
//
|
|
if (!ProcessEntrypoint(&Entrypoint.ep)){
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Process the sub entrypoints
|
|
//
|
|
while (Entrypoint.ep.SubEP) {
|
|
Entrypoint.ep = *Entrypoint.ep.SubEP;
|
|
|
|
//
|
|
// Write the sub-entrypoint to the file
|
|
//
|
|
if (!ProcessEntrypoint(&Entrypoint.ep)){
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set up for future iterations
|
|
//
|
|
if (Entrypoint.intelRight != &_NIL) {
|
|
PUSH((ULONG)Entrypoint.intelRight);
|
|
}
|
|
|
|
if (Entrypoint.intelLeft != &_NIL) {
|
|
PUSH((ULONG)Entrypoint.intelLeft);
|
|
}
|
|
|
|
POP((ULONG)NextEntrypoint);
|
|
}
|
|
|
|
Exit: ;
|
|
if (TCFlush) {
|
|
CODEDESCRIPTION CodeDescription;
|
|
ULONG NextCodeDescriptionOffset;
|
|
BOOL Success;
|
|
ULONG BytesWritten;
|
|
CHAR ErrorString[80];
|
|
|
|
NextCodeDescriptionOffset = (CurrentFileLocation + sizeof(CODEDESCRIPTION)) & ~3;
|
|
CodeDescription.TypeTag = PROFILE_TAG_TCFLUSH;
|
|
CodeDescription.NextCodeDescriptionOffset = NextCodeDescriptionOffset;
|
|
CodeDescription.CreationTime = GetCurrentTime();
|
|
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
&CodeDescription,
|
|
sizeof(CODEDESCRIPTION),
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != sizeof(CODEDESCRIPTION))) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write code description, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
return;
|
|
}
|
|
|
|
CurrentFileLocation = SetFilePointer(
|
|
CpuCodegenProfileFile,
|
|
NextCodeDescriptionOffset,
|
|
NULL,
|
|
FILE_BEGIN
|
|
);
|
|
|
|
if (CurrentFileLocation != (ULONG)NextCodeDescriptionOffset) {
|
|
sprintf(ErrorString, "Error: SetFilePointer didn't work\n");
|
|
OutputDebugString(ErrorString);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
ProcessEntrypoint(
|
|
PENTRYPOINT Entrypoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes the description for this entrypoint to the file.
|
|
|
|
Arguments:
|
|
|
|
Entrypoint -- Supplies the entrypoint to describe
|
|
File -- Supplies the file to write to
|
|
|
|
Return Value:
|
|
|
|
True for success, False for failure
|
|
|
|
--*/
|
|
{
|
|
ULONG NativeCodeLength, IntelCodeLength;
|
|
CODEDESCRIPTION CodeDescription;
|
|
ULONG NextCodeDescriptionOffset;
|
|
NTSTATUS Status;
|
|
BOOL Success;
|
|
ULONG BytesWritten;
|
|
CHAR ErrorString[80];
|
|
|
|
//
|
|
// Create the code description
|
|
//
|
|
NativeCodeLength = ((ULONG)Entrypoint->nativeEnd - (ULONG)Entrypoint->nativeStart + 4) & ~3;
|
|
IntelCodeLength = (ULONG)Entrypoint->intelEnd - (ULONG)Entrypoint->intelStart + 1;
|
|
CodeDescription.NativeCodeOffset = CurrentFileLocation + sizeof(CODEDESCRIPTION);
|
|
CodeDescription.IntelCodeOffset = CodeDescription.NativeCodeOffset + NativeCodeLength;
|
|
NextCodeDescriptionOffset = (CodeDescription.IntelCodeOffset +
|
|
IntelCodeLength + 3) & ~3;
|
|
CodeDescription.NextCodeDescriptionOffset = NextCodeDescriptionOffset;
|
|
CodeDescription.IntelAddress = (ULONG)Entrypoint->intelStart;
|
|
CodeDescription.NativeAddress = (ULONG)Entrypoint->nativeStart;
|
|
CodeDescription.SequenceNumber = Entrypoint->SequenceNumber;
|
|
CodeDescription.ExecutionCount = Entrypoint->ExecutionCount;
|
|
CodeDescription.IntelCodeSize = IntelCodeLength;
|
|
CodeDescription.NativeCodeSize = NativeCodeLength;
|
|
CodeDescription.TypeTag = PROFILE_TAG_CODEDESCRIPTION;
|
|
CodeDescription.CreationTime = Entrypoint->CreationTime;
|
|
|
|
//
|
|
// Verify that we can get all of the Intel and Native code
|
|
//
|
|
if (
|
|
(IntelCodeLength / sizeof(IntelCodeBuffer[1]) > CODE_BUFFER_SIZE) ||
|
|
(NativeCodeLength) && (NativeCodeLength / sizeof(NativeCodeBuffer[1]) > CODE_BUFFER_SIZE)
|
|
) {
|
|
sprintf(ErrorString, "Error: Code buffers not big enough:N %lx:I %lx\n", NativeCodeLength, IntelCodeLength);
|
|
OutputDebugString(ErrorString);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get the native code
|
|
//
|
|
if (NativeCodeLength) {
|
|
memcpy(NativeCodeBuffer, Entrypoint->nativeStart, NativeCodeLength);
|
|
}
|
|
|
|
//
|
|
// Get the Intel code
|
|
//
|
|
try {
|
|
memcpy(IntelCodeBuffer, Entrypoint->intelStart, IntelCodeLength);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Apparently the intel code is no longer there. This happens
|
|
// if a dll gets unloaded
|
|
//
|
|
IntelCodeLength = 0;
|
|
CodeDescription.IntelCodeSize = 0;
|
|
CodeDescription.IntelCodeOffset = CodeDescription.NativeCodeOffset + NativeCodeLength;
|
|
NextCodeDescriptionOffset = (CodeDescription.IntelCodeOffset +
|
|
IntelCodeLength + 3) & ~3;
|
|
CodeDescription.NextCodeDescriptionOffset = NextCodeDescriptionOffset;
|
|
}
|
|
|
|
//
|
|
// Write code description to disk
|
|
//
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
&CodeDescription,
|
|
sizeof(CODEDESCRIPTION),
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != sizeof(CODEDESCRIPTION))) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write code description, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Write Native code to disk
|
|
//
|
|
if (NativeCodeLength) {
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
NativeCodeBuffer,
|
|
NativeCodeLength,
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != NativeCodeLength)) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write native code, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write Intel code to disk
|
|
//
|
|
if (IntelCodeLength) {
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
IntelCodeBuffer,
|
|
IntelCodeLength,
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != IntelCodeLength)) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write native code, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
return FALSE;
|
|
}
|
|
}
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
IntelCodeBuffer,
|
|
IntelCodeLength,
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != IntelCodeLength)) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write native code, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Update file pointer position
|
|
//
|
|
CurrentFileLocation = SetFilePointer(
|
|
CpuCodegenProfileFile,
|
|
NextCodeDescriptionOffset,
|
|
NULL,
|
|
FILE_BEGIN
|
|
);
|
|
|
|
if (CurrentFileLocation != (ULONG)NextCodeDescriptionOffset) {
|
|
sprintf(ErrorString, "Error: SetFilePointer didn't work\n");
|
|
OutputDebugString(ErrorString);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
DumpAllocFailure(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds an allocation failure record to the profile dump.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CODEDESCRIPTION CodeDescription;
|
|
BOOL Success;
|
|
ULONG BytesWritten;
|
|
CHAR ErrorString[80];
|
|
ULONG NextCodeDescriptionOffset;
|
|
|
|
if (!CpuCodegenProfile) {
|
|
return;
|
|
}
|
|
NextCodeDescriptionOffset = CurrentFileLocation + sizeof(CODEDESCRIPTION);
|
|
CodeDescription.NextCodeDescriptionOffset = NextCodeDescriptionOffset;
|
|
CodeDescription.TypeTag = PROFILE_TAG_TCALLOCFAIL;
|
|
CodeDescription.CreationTime = GetCurrentTime();
|
|
|
|
Success = WriteFile(
|
|
CpuCodegenProfileFile,
|
|
&CodeDescription,
|
|
sizeof(CODEDESCRIPTION),
|
|
&BytesWritten,
|
|
NULL
|
|
);
|
|
if (!Success || (BytesWritten != sizeof(CODEDESCRIPTION))) {
|
|
sprintf(
|
|
ErrorString,
|
|
"Error: Could not write termination record, %lu\n",
|
|
ProxyGetLastError()
|
|
);
|
|
OutputDebugString(ErrorString);
|
|
}
|
|
|
|
//
|
|
// Update file pointer position
|
|
//
|
|
CurrentFileLocation = SetFilePointer(
|
|
CpuCodegenProfileFile,
|
|
NextCodeDescriptionOffset,
|
|
NULL,
|
|
FILE_BEGIN
|
|
);
|
|
|
|
if (CurrentFileLocation != (ULONG)NextCodeDescriptionOffset) {
|
|
sprintf(ErrorString, "Error: SetFilePointer didn't work\n");
|
|
OutputDebugString(ErrorString);
|
|
}
|
|
}
|
|
#endif |