windows-nt/Source/XPSP1/NT/sdktools/linkd/linkd.c

1054 lines
27 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
linkd.c
Simple utility to manipulate name graftings at directories.
Author:
Felipe Cabrera (Cabrera) 271-Aug-1997
Revision History:
--*/
#define UNICODE
#define _UNICODE
#include <stdio.h>
#include <stdlib.h> // exit
#include <io.h> // _get_osfhandle
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntioapi.h>
#include <windows.h>
#include <locale.h> // setlocale
//
// Functions forward referenced.
//
void
ScanArgs (
int argc,
char **argv
);
void
__cdecl
printmessage (
DWORD messageID,
...
);
void
SzToWsz (
OUT WCHAR *Unicode,
IN char *Ansi
);
BOOL
MassageLinkValue (
IN LPCWSTR lpLinkName,
IN LPCWSTR lpLinkValue,
OUT PUNICODE_STRING NtLinkName,
OUT PUNICODE_STRING NtLinkValue,
OUT PUNICODE_STRING DosLinkValue
);
void
__cdecl
printmessage (
DWORD messageID,
...
);
void
__cdecl
DisplayMsg (
DWORD MsgNum,
...
);
int
FileIsConsole (
int fh
);
//
// I/O stream handles and variables.
//
HANDLE hInput;
HANDLE hOutput;
HANDLE hError;
#define STDIN 0
#define STDOUT 1
#define STDERR 2
BOOL ConsoleInput;
BOOL ConsoleOutput;
BOOL ConsoleError;
//
// Core control state vars
//
BOOL NeedHelp;
BOOL DoCreate;
BOOL DoDelete;
BOOL DoQuery;
BOOL DoEnumerate;
#include "linkdmsg.h"
TCHAR Buf[1024]; // for displaying stuff
//
// Main
//
void
__cdecl
main(
int argc,
char **argv
)
{
CHAR lBuf[16];
DWORD dwCodePage;
LANGID LangId;
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE Handle;
UNICODE_STRING UnicodeName;
UNICODE_STRING NtLinkName;
UNICODE_STRING NtLinkValue;
UNICODE_STRING DosLinkValue;
WCHAR FullPathLinkValue[ DOS_MAX_PATH_LENGTH+1 ];
IO_STATUS_BLOCK IoStatusBlock;
BOOL TranslationStatus;
PVOID FreeBuffer;
PVOID FreeBuffer2;
FILE_DISPOSITION_INFORMATION Disposition;
PREPARSE_DATA_BUFFER ReparseBufferHeader = NULL;
PCHAR ReparseBuffer = NULL;
ULONG ReparsePointTag = IO_REPARSE_TAG_RESERVED_ZERO;
USHORT ReparseDataLength = 0;
WCHAR WFileName[MAX_PATH + 2];
WCHAR WFileNameTwo[MAX_PATH + 2];
UCHAR Command[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
ULONG FsControlCode = 0;
ULONG CreateOptions = 0;
ULONG CreateDisposition = 0;
ULONG DesiredAccess = SYNCHRONIZE;
//
// Build up state vector in global booleans.
//
ScanArgs(argc, argv);
//
// printf( "argc = %d NeedHelp = %d DoCreate = %d DoDelete = %d DoQuery = %d DoEnumerate = %d\n",
// argc, NeedHelp, DoCreate, DoDelete, DoQuery, DoEnumerate );
//
//
// Since FormatMessage checks the current TEB's locale, and the Locale for
// CHCP is initialized when the message class is initialized, the TEB has to
// be updated after the code page is changed successfully.
//
// Why are we doing this, you ask. Well, the FE guys have plans to add
// more than one set of language resources to this module, but not all
// the possible resources. So this limited set is what they plan for.
// If FormatMessage can't find the right language, it falls back to
// something hopefully useful.
//
dwCodePage = GetConsoleOutputCP();
sprintf(lBuf, ".%d", dwCodePage);
switch( dwCodePage )
{
case 437:
LangId = MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US );
break;
case 932:
LangId = MAKELANGID( LANG_JAPANESE, SUBLANG_DEFAULT );
break;
case 949:
LangId = MAKELANGID( LANG_KOREAN, SUBLANG_KOREAN );
break;
case 936:
LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED );
break;
case 950:
LangId = MAKELANGID( LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL );
break;
default:
LangId = MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT );
lBuf[0] = '\0';
break;
}
SetThreadLocale( MAKELCID(LangId, SORT_DEFAULT) );
setlocale(LC_ALL, lBuf);
//
// Set the appropriate handles.
//
hInput = GetStdHandle(STD_INPUT_HANDLE);
ConsoleInput = FileIsConsole(STDIN) ? TRUE : FALSE;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
ConsoleOutput = FileIsConsole(STDOUT) ? TRUE : FALSE;
hError = GetStdHandle(STD_ERROR_HANDLE);
ConsoleError = FileIsConsole(STDERR) ? TRUE : FALSE;
//
// OK, we know the state of the command, do work
//
//
// printf( "The parameters specified were: [0]%s [1]%s\n", argv[0], argv[1] );
//
//
// If they asked for help, or did something that indicates they don't
// understand how the program works, print help and exit.
//
if (NeedHelp) {
printmessage( MSG_LINKD_HELP );
exit(1);
}
//
// The enumeration of all mount points is not yet supported.
//
if (DoEnumerate) {
printmessage( MSG_LINKD_HELP );
exit(1);
}
//
// The following three calls require, at least, to have the SourceName of the operation.
// Thus, we have one NT file name that we will operate on.
// Change the string to Unicode and store it locally. Use it latter to open the file.
//
SzToWsz( WFileName, argv[1] );
TranslationStatus = RtlDosPathNameToNtPathName_U(
WFileName,
&UnicodeName,
NULL,
NULL
);
if (!TranslationStatus) {
printmessage( MSG_LINKD_WRONG_NAME );
exit (1);
}
FreeBuffer = UnicodeName.Buffer;
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
//
// printf( "Transformed unicode str is %Z\n", &UnicodeName );
//
//
// Now go do the appropriate actions.
//
if (DoCreate) {
//
// Set the code of the FSCTL operation.
//
FsControlCode = FSCTL_SET_REPARSE_POINT;
//
// Set the open/create options for a directory.
//
CreateOptions = FILE_OPEN_REPARSE_POINT;
//
// Set the tag to mount point.
//
ReparsePointTag = IO_REPARSE_TAG_MOUNT_POINT;
//
// Open to set the reparse point.
//
DesiredAccess |= FILE_WRITE_DATA;
CreateDisposition = FILE_OPEN; // the file must be present
Status = NtCreateFile(
&Handle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL, // pallocationsize (none!)
FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
CreateDisposition,
CreateOptions,
NULL, // EA buffer (none!)
0
);
//
// Create a directory if you do not find it.
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
DesiredAccess = SYNCHRONIZE;
CreateDisposition = FILE_CREATE;
CreateOptions = FILE_DIRECTORY_FILE;
Status = NtCreateFile(
&Handle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL, // pallocationsize (none!)
FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
CreateDisposition,
CreateOptions,
NULL, // EA buffer (none!)
0
);
if (!NT_SUCCESS(Status)) {
printmessage( MSG_LINKD_CREATE_FAILED );
exit (1);
}
//
// Close the handle and re-open.
//
NtClose( Handle );
CreateOptions = FILE_OPEN_REPARSE_POINT;
DesiredAccess |= FILE_WRITE_DATA;
CreateDisposition = FILE_OPEN; // the file must be present
Status = NtCreateFile(
&Handle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
NULL, // pallocationsize (none!)
FILE_ATTRIBUTE_NORMAL, // attributes to be set if created
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
CreateDisposition,
CreateOptions,
NULL, // EA buffer (none!)
0
);
}
RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
if (!NT_SUCCESS(Status)) {
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
// printmessage( MSG_LINKD_OPEN_FAILED );
exit (1);
}
//
// Build the appropriate buffer for mount points and for symbolic links.
//
if ((ReparsePointTag == IO_REPARSE_TAG_MOUNT_POINT) ||
(ReparsePointTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
//
// The value of the mount point or of the symbolic link comes in argv[2].
//
SzToWsz( WFileName, argv[1] );
SzToWsz( WFileNameTwo, argv[2] );
//
// Innitialize the DosName buffer.
//
DosLinkValue.Buffer = FullPathLinkValue;
DosLinkValue.MaximumLength = sizeof( FullPathLinkValue );
DosLinkValue.Length = 0;
//
// Massage all the names.
//
if (!MassageLinkValue( WFileName, WFileNameTwo, &NtLinkName, &NtLinkValue, &DosLinkValue )) {
if (DosLinkValue.Length == 0) {
printmessage( MSG_LINKD_WRONG_NAME );
}
else {
printmessage( MSG_LINKD_PATH_NOT_FOUND );
}
RtlFreeUnicodeString( &NtLinkName );
RtlFreeUnicodeString( &NtLinkValue );
exit (1);
}
//
// printf( "NtLinkName %Z\n", &NtLinkName );
// printf( "NtLinkValue %Z\n", &NtLinkValue );
// printf( "DosLinkValue is %Z\n", &DosLinkValue );
// printf( "NtLinkValue.Length %d DosLinkValue.Length %d sizeof(UNICODE_NULL) %d\n",
// NtLinkValue.Length, DosLinkValue.Length, sizeof(UNICODE_NULL) );
//
RtlFreeUnicodeString( &NtLinkName );
//
// Set the reparse point with mount point or symbolic link tag and determine
// the appropriate length of the buffer.
//
//
// printf( "FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE %d\n",
// (FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) - REPARSE_DATA_BUFFER_HEADER_SIZE) );
//
ReparseDataLength = (USHORT)((FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) -
REPARSE_DATA_BUFFER_HEADER_SIZE) +
NtLinkValue.Length + sizeof(UNICODE_NULL) +
DosLinkValue.Length + sizeof(UNICODE_NULL));
//
// printf( "ReparseDataLength %d\n", ReparseDataLength );
//
//
// Allocate a buffer to set the reparse point.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)RtlAllocateHeap(
RtlProcessHeap(),
HEAP_ZERO_MEMORY,
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseDataLength
);
if (ReparseBufferHeader == NULL) {
NtClose( Handle );
RtlFreeUnicodeString( &NtLinkValue );
printmessage( MSG_LINKD_NO_MEMORY );
exit (1);
}
//
// Setting the buffer is common for both tags as their buffers have identical fields.
//
ReparseBufferHeader->ReparseDataLength = (USHORT)ReparseDataLength;
ReparseBufferHeader->Reserved = 0;
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameOffset = 0;
ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength = NtLinkValue.Length;
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameOffset = NtLinkValue.Length + sizeof( UNICODE_NULL );
ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength = DosLinkValue.Length;
RtlCopyMemory(
ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer,
NtLinkValue.Buffer,
NtLinkValue.Length
);
RtlCopyMemory(
(PCHAR)(ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer)+
NtLinkValue.Length + sizeof(UNICODE_NULL),
DosLinkValue.Buffer,
DosLinkValue.Length
);
RtlFreeUnicodeString( &NtLinkValue );
}
//
// Set the tag in common, once for all possible cases.
//
ReparseBufferHeader->ReparseTag = ReparsePointTag;
//
// Set the reparse point.
//
Status = NtFsControlFile(
Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FsControlCode,
ReparseBufferHeader,
REPARSE_DATA_BUFFER_HEADER_SIZE + ReparseBufferHeader->ReparseDataLength,
NULL, // no output buffer
0 // output buffer length
);
//
// Close the file.
//
NtClose( Handle );
if (!NT_SUCCESS(Status)) {
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_SET_OPERATION_FAILED, Buf);
// printmessage( MSG_LINKD_SET_OPERATION_FAILED );
exit (1);
}
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_CREATE_OPERATION_SUCCESS, Buf);
// printmessage( MSG_LINKD_CREATE_OPERATION_SUCCESS );
}
if (DoDelete) {
FILE_DISPOSITION_INFORMATION Disposition = {TRUE};
//
// Open the file for delete access.
// Inhibit the reparse behavior using FILE_OPEN_REPARSE_POINT.
// This will get a handle to the entity whether the appropriate filter is or not in place.
//
Status = NtOpenFile(
&Handle,
(ACCESS_MASK)DELETE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT
);
if (!NT_SUCCESS(Status)) {
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
// printmessage( MSG_LINKD_OPEN_FAILED );
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
exit(1);
}
RtlFreeHeap(RtlProcessHeap(), 0, FreeBuffer);
//
// Delete the file
//
Status = NtSetInformationFile(
Handle,
&IoStatusBlock,
&Disposition,
sizeof(Disposition),
FileDispositionInformation
);
NtClose(Handle);
if (!NT_SUCCESS(Status)) {
printmessage( MSG_LINKD_DELETE_OPERATION_FAILED );
exit(1);
}
printmessage( MSG_LINKD_DELETE_OPERATION_SUCCESS );
}
if (DoQuery) {
//
// Set the code of the FSCTL operation.
//
FsControlCode = FSCTL_GET_REPARSE_POINT;
//
// We do not specify whether it is a file or a directory to get either.
//
DesiredAccess = FILE_READ_DATA | SYNCHRONIZE;
CreateOptions = FILE_OPEN_REPARSE_POINT | FILE_SYNCHRONOUS_IO_NONALERT;
//
// Open the reparse point for query.
//
Status = NtOpenFile(
&Handle,
DesiredAccess,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
CreateOptions
);
//
// Free the name buffer as we are done with it.
//
RtlFreeHeap( RtlProcessHeap(), 0, FreeBuffer );
if (!NT_SUCCESS(Status)) {
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_OPEN_FAILED, Buf);
// printmessage( MSG_LINKD_OPEN_FAILED );
exit (1);
}
//
// Query the reparse point.
//
// We are use the approach of passing a buffer of well-known length:
// MAXIMUM_REPARSE_DATA_BUFFER_SIZE
//
// Allocate a buffer and get the information.
//
ReparseDataLength = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
ReparseBuffer = RtlAllocateHeap(
RtlProcessHeap(),
HEAP_ZERO_MEMORY,
ReparseDataLength
);
if (ReparseBuffer == NULL) {
printmessage( MSG_LINKD_NO_MEMORY );
exit (1);
}
//
// Now go and get the data.
//
Status = NtFsControlFile(
Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FsControlCode, // no input buffer
NULL, // input buffer length
0,
(PVOID)ReparseBuffer,
ReparseDataLength
);
//
// printf( "Status %x IoStatusBlock.Status %x IoStatusBlock.Information %x\n", Status, IoStatusBlock.Status, IoStatusBlock.Information );
//
if (!NT_SUCCESS(Status)) {
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_GET_OPERATION_FAILED, Buf);
NtClose( Handle );
RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
exit (1);
}
//
// Close the file and free the buffer.
//
NtClose( Handle );
//
// Display the buffer.
//
ReparseBufferHeader = (PREPARSE_DATA_BUFFER)ReparseBuffer;
if ((ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) ||
(ReparseBufferHeader->ReparseTag == IO_REPARSE_TAG_SYMBOLIC_LINK)) {
USHORT Offset = 0;
NtLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[Offset];
NtLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.SubstituteNameLength;
Offset = NtLinkValue.Length + sizeof(UNICODE_NULL);
DosLinkValue.Buffer = &ReparseBufferHeader->SymbolicLinkReparseBuffer.PathBuffer[Offset/sizeof(WCHAR)];
DosLinkValue.Length = ReparseBufferHeader->SymbolicLinkReparseBuffer.PrintNameLength;
//
// printf( "NtLinkValue.Length %d DosLinkValue.Length %d\n", NtLinkValue.Length, DosLinkValue.Length );
// printf( " NtLinkValue: %Z\n", &NtLinkValue );
// printf( "DosLinkValue: %Z\n", &DosLinkValue );
//
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_DISPLAY_NL_A, Buf);
swprintf(&Buf[0], TEXT("%s"), DosLinkValue.Buffer);
DisplayMsg(MSG_LINKD_DISPLAY_NL, Buf);
}
else {
SzToWsz( WFileName, argv[1] );
swprintf(&Buf[0], TEXT("%s"), WFileName);
DisplayMsg(MSG_LINKD_GET_OPERATION_FAILED, Buf);
}
//
// Free the buffer.
//
RtlFreeHeap( RtlProcessHeap(), 0, ReparseBufferHeader );
}
//
// Final exit point.
//
exit (0);
} // main
//
// Changing a file name to wide characters.
//
void
SzToWsz (
OUT WCHAR *Unicode,
IN char *Ansi
)
{
while (*Unicode++ = *Ansi++)
;
} // SzToWsz
//
// Name transformations to create a legitimate mount point or a symbolic link.
//
BOOL
MassageLinkValue(
IN LPCWSTR lpLinkName,
IN LPCWSTR lpLinkValue,
OUT PUNICODE_STRING NtLinkName,
OUT PUNICODE_STRING NtLinkValue,
OUT PUNICODE_STRING DosLinkValue
)
{
PWSTR FilePart;
PWSTR s, sBegin, sBackupLimit, sLinkName;
NTSTATUS Status;
USHORT nSaveNtNameLength;
ULONG nLevels;
//
// Initialize output variables to NULL
//
RtlInitUnicodeString( NtLinkName, NULL );
RtlInitUnicodeString( NtLinkValue, NULL );
//
// Translate link name into full NT path.
//
if (!RtlDosPathNameToNtPathName_U( lpLinkName,
NtLinkName,
&sLinkName,
NULL
)
) {
return FALSE;
}
//
// All done if no link value.
//
if (!ARGUMENT_PRESENT( lpLinkValue )) {
return TRUE;
}
//
// If the target is a device, do not allow the link.
//
if (RtlIsDosDeviceName_U( (PWSTR)lpLinkValue )) {
return FALSE;
}
//
// Convert to DOS path to full path, and get Nt representation
// of DOS path.
//
if (!RtlGetFullPathName_U( lpLinkValue,
DosLinkValue->MaximumLength,
DosLinkValue->Buffer,
NULL
)
) {
return FALSE;
}
DosLinkValue->Length = wcslen( DosLinkValue->Buffer ) * sizeof( WCHAR );
//
// Verify that the link value is a valid NT name.
//
if (!RtlDosPathNameToNtPathName_U( DosLinkValue->Buffer,
NtLinkValue,
NULL,
NULL
)
) {
return FALSE;
}
return TRUE;
} // MassageLinkValue
VOID
ScanArgs(
int argc,
char **argv
)
/*++
Routine Description:
ScanArgs - parse command line arguments, and set control flags
to reflect what we find.
Sets:
NeedHelp;
DoCreate;
DoDelete;
DoQuery;
DoEnumerate;
Arguments:
argc - count of command line args
argv - argument vector
Return Value:
--*/
{
int i;
NeedHelp = FALSE;
DoCreate = FALSE;
DoDelete = FALSE;
DoQuery = FALSE;
DoEnumerate = FALSE;
//
// The valid calls are:
//
// linkd sourceName valueName -- create a directory name grafting
// linkd sourceName /d -- delete a directory name grafting
// linkd sourceName -- to query the value of the name grafting
// linkd /? -- print help
// linkd -- to enumerate all the name graftings
//
if (argc > 3) {
NeedHelp = TRUE;
goto done;
}
if (argc == 1) {
DoEnumerate = TRUE;
goto done;
}
if (argc == 2) {
if ( (argv[1][0] == '/') &&
(argv[1][1] == '?') &&
(strlen(argv[1]) == 2) ) {
NeedHelp = TRUE;
goto done;
}
DoQuery = TRUE;
goto done;
}
if (argc == 3) {
if ( (argv[2][0] == '/') &&
((argv[2][1] == 'd') || (argv[2][1] == 'D')) &&
(strlen(argv[2]) == 2) ) {
DoDelete = TRUE;
goto done;
}
DoCreate = TRUE;
goto done;
}
done:
return;
} // ScanArgs
//
// Call FormatMessage and dump the result. All messages to Stdout
//
void
__cdecl
printmessage (
DWORD messageID,
...
)
{
unsigned short messagebuffer[4096];
va_list ap;
va_start(ap, messageID);
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, messageID, 0,
messagebuffer, 4095, &ap);
wprintf(messagebuffer);
va_end(ap);
} // printmessage
TCHAR DisplayBuffer[4096];
CHAR DisplayBuffer2[4096];
void
__cdecl
DisplayMsg (
DWORD MsgNum,
...
)
{
DWORD len, bytes_written;
BOOL success;
DWORD status;
va_list ap;
va_start(ap, MsgNum);
len = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, NULL, MsgNum, 0,
DisplayBuffer, 4096, &ap);
if (ConsoleOutput) {
success = WriteConsole(hOutput, (LPVOID)DisplayBuffer, len,
&bytes_written, NULL);
} else {
CharToOem(DisplayBuffer, DisplayBuffer2);
success = WriteFile(hOutput, (LPVOID)DisplayBuffer2, len,
&bytes_written, NULL);
}
if (!success || bytes_written != len) {
status = GetLastError();
}
va_end(ap);
} // DisplayMsg
int
FileIsConsole(int fh)
{
unsigned htype;
DWORD dwMode;
HANDLE hFile;
hFile = (HANDLE)_get_osfhandle(fh);
htype = GetFileType(hFile);
htype &= ~FILE_TYPE_REMOTE;
if (FILE_TYPE_CHAR == htype) {
switch (fh) {
case STDIN:
hFile = GetStdHandle(STD_INPUT_HANDLE);
break;
case STDOUT:
hFile = GetStdHandle(STD_OUTPUT_HANDLE);
break;
case STDERR:
hFile = GetStdHandle(STD_ERROR_HANDLE);
break;
}
if (GetConsoleMode(hFile, &dwMode)) {
return TRUE;
}
}
return FALSE;
} // FileIsConsole