2009年4月2日星期四

WriteFile,WriteConsole和_tprintf的一些差别
















WriteFile,WriteConsole和_tprintf的一些差别

这几个API都是用于输出的,但都有各自的特点:
1. _tprintf的实现利用了WriteFile和WideCharToMultiByte。
2. WriteFile只接受字节流,没有UNICODE的概念,WriteFile可以实现重定向。
3. WriteConsole有两个版本WriteConsoleA和WriteConsoleW, 可以分别接收ANSI和UNICODE字符串,不支持重定向。


看下面这个简单的程序,编译后再用eXeScope分析一下Import

    1 #include <stdio.h>
    2 
    3 void wmain(int argc, wchar_t* argv[])
    4 {
    5     wprintf(L"Hello World\n");
    6 }

看了eXeScope的输出后,发现wprintf的实现利用了WideCharToMultiByte和WriteFile等API,在写入文件前首先是将UNICODE转换成多字节的。


然后本人试着直接调用API进行输出,分别使用了API WriteConsole和WriteFile
 
    1 #include <windows.h>
    2 
    3 #pragma comment(lib, "User32")
    4 #pragma comment(lib, "Kernel32")
    5 #pragma comment(linker, "/Entry:wmain")
    6 #pragma comment(linker, "/Merge:.rdata=.text")
    7 
    8 void *operator new[](unsigned int size){
    9     return HeapAlloc (GetProcessHeap(), NULL, size);
   10 }
   11 
   12 void operator delete[] (void* memblock){
   13     HeapFree (GetProcessHeap(), NULL, memblock);
   14 }
   15 
   16 bool WINAPI StdOut( wchar_t* lpString)
   17 {
   18     HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
   19     DWORD numOfCharsWritten=0;
   20     WriteConsole(hStdOut, lpString, lstrlen(lpString), &numOfCharsWritten, NULL);
   21 
   22     DWORD dwNum = WideCharToMultiByte(CP_OEMCP,NULL,lpString, -1, NULL, 0, NULL, false);
   23     char *pText = new char[dwNum];
   24     WideCharToMultiByte(CP_OEMCP,NULL,lpString,-1,pText,dwNum,NULL,false);
   25     BOOL bRet = WriteFile(hStdOut, pText, dwNum, &numOfCharsWritten, NULL);
   26     delete[] pText;
   27     return bRet;
   28 }
   29 
   30 void wmain()
   31 {
   32     wchar_t *pChar = L"Hello World!\n";
   33     StdOut(pChar);
   34 }

命令行编译 cl client.cpp /DUNICODE /D_UNICODE /LINK /ALIGN:16
程序执行结果为:
Hello World!
Hello World!


下面的程序Parent.cpp用于输出重定向


    1 #include "stdafx.h"
    2 #include <windows.h>
    3 #include <tchar.h>
    4 #include <stdio.h>
    5 
    6 HANDLE SpawnAndRedirect(LPCTSTR commandLine, HANDLE *hStdOutputReadPipe, LPCTSTR lpCurrentDirectory);
    7 
    8 int _tmain()
    9 {
   10     HANDLE hOutput, hProcess;
   11     hProcess = SpawnAndRedirect(_T("client.exe"), &hOutput, NULL);
   12     if (!hProcess){
   13         _tprintf(_T("Failed to spawn the thread\n"));
   14         return 1;
   15     }
   16 
   17     char buffer[129];
   18     DWORD read;
   19     while(ReadFile(hOutput, buffer, 128, &read, NULL))
   20     {
   21         buffer[read] = '\0';
   22         printf("%s\n", buffer);
   23     }
   24 
   25     CloseHandle(hOutput);
   26     CloseHandle(hProcess);
   27 
   28     return 0;
   29 }
   30 
   31 
   32 // The following function is a shortened variant of Q190351 - HOWTO: Spawn Console Processes with Redirected Standard Handles
   33 // There is no special magic here, and this version doesn't address issues like:
   34 // - redirecting Input handle
   35 // - spawning 16-bits process 
   36 // - command-line limitations (unsafe 1024-char buffer)
   37 // So you might want to use more advanced versions such as the ones you can find on CodeProject
   38 HANDLE SpawnAndRedirect(LPCTSTR commandLine, HANDLE *hStdOutputReadPipe, LPCTSTR lpCurrentDirectory)
   39 {
   40     HANDLE hStdOutputWritePipe, hStdOutput, hStdError;
   41     CreatePipe(hStdOutputReadPipe, &hStdOutputWritePipe, NULL, 0);  // create a non-inheritable pipe
   42     DuplicateHandle(GetCurrentProcess(), hStdOutputWritePipe,
   43         GetCurrentProcess(), &hStdOutput,                           // duplicate the "write" end as inheritable stdout
   44         0, TRUE, DUPLICATE_SAME_ACCESS);
   45     DuplicateHandle(GetCurrentProcess(), hStdOutput,
   46         GetCurrentProcess(), &hStdError,                            // duplicate stdout as inheritable stderr
   47         0, TRUE, DUPLICATE_SAME_ACCESS);
   48     CloseHandle(hStdOutputWritePipe);                               // no longer need the non-inheritable "write" end of the pipe
   49 
   50     PROCESS_INFORMATION pi;
   51     STARTUPINFO si;
   52     ZeroMemory(&si, sizeof(STARTUPINFO));
   53     si.cb = sizeof(STARTUPINFO);
   54     si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
   55     si.hStdInput  = GetStdHandle(STD_INPUT_HANDLE); // (this is bad on a GUI app)
   56     si.hStdOutput = hStdOutput;
   57     si.hStdError  = hStdError;
   58     si.wShowWindow = SW_HIDE;                       // IMPORTANT: hide subprocess console window
   59     TCHAR commandLineCopy[1024];                    // CreateProcess requires a modifiable buffer
   60     _tcscpy(commandLineCopy, commandLine);
   61     if (!CreateProcess(    NULL, commandLineCopy, NULL, NULL, TRUE,
   62         CREATE_NEW_CONSOLE, NULL, lpCurrentDirectory, &si, &pi))
   63     {
   64         CloseHandle(hStdOutput);
   65         CloseHandle(hStdError);
   66         CloseHandle(*hStdOutputReadPipe);
   67         *hStdOutputReadPipe = INVALID_HANDLE_VALUE;
   68         return NULL;
   69     }
   70 
   71     CloseHandle(pi.hThread);
   72     CloseHandle(hStdOutput);
   73     CloseHandle(hStdError);
   74     return pi.hProcess;
   75 }


调用Parent,这个程序会创建子进程Client,并且通过pipe将Client的输出定向为Parent的输出,程序的执行结果为
Hello World!
在Client中WriteConsole的输出没有重定向过来!





没有评论:

发表评论