|
|
/*++
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
|