2015年3月19日 星期四

用 cmd line 執行 MFC 後的訊息顯示

一個卡了我不少時間的問題

最後終於找到一個可以解決的辦法,所以記錄一下

//----------------------------------------------------------------------

需求是有一個 MFC 的程式,並經由 cmd line 呼叫
而且在程式執行失敗時,可以顯示訊息到 cmd 當下的視窗中

PS. 接收命令參數使用下面的方法
CString strCommandLine(GetCommandLine());
CommandLineToArgvW(CString(GetCommandLine()), &nArgCount);

處理過程中所遇到的問題
1. 無法在當下的 cmd 中 printf 訊息
2. printf 出來的訊息位置不佳

第一點比較快解決,有兩種方法,我是選用第一個,較簡單
第一種:
AttachConsole(ATTACH_PARENT_PROCESS); //直接取得當下 process
_cprintf("程式執行失敗");

第二種方法是下面這個網址的做法
http://blog.csdn.net/panchunrong/article/details/8930603


第二個問題就真的卡很久了
先說明一下狀況
----------------------------------------------------------------------
C:\>MFC.exe

程式執行失敗            <-目標顯示狀況:在下面的 C:\> 之前顯示

C:\>
----------------------------------------------------------------------
C:\>MFC.exe

C:\>程式執行失敗        <-實際顯示狀況:在 C:\> 之後才顯示
----------------------------------------------------------------------

在思考時主要有兩個方向

  • 進程
  • Console 緩衝區字元處理

進程的思考就是要在 C:\> 出現之前就將訊息印出
不過似乎不是能夠輕易解決
所以最後是用「Console 緩衝區字元處理」解掉的
再配合一些小技巧 (e.g. 實現 "Enter" 輸入,清空一行緩衝區)
就能完美的解決問題

程式碼如下:
AttachConsole(ATTACH_PARENT_PROCESS); //綁定當下cmd窗口
HANDLE hHandle = GetStdHandle(STD_OUTPUT_HANDLE); //綁定 handle
CONSOLE_SCREEN_BUFFER_INFO    sbi; //窗口緩衝區信息
GetConsoleScreenBufferInfo(hHandle, &sbi); //讀取console內容
char *szLine;  /* buffer to hold the line read from the console */
szLine = (char *) malloc(sbi.dwSize.X);
COORD coordLine = {0, 0}; /* coordinates of where to read characters from */
DWORD dwCharsRead(0);
SHORT StartLine(0);
for(int i=sbi.dwCursorPosition.Y ; true ; i--)
{  
 coordLine.X = 0;
 coordLine.Y = i;
 //
 ReadConsoleOutputCharacterA(hHandle, szLine, sbi.dwSize.X, coordLine, &dwCharsRead); //讀取console緩衝區的一行內容
 int j = sbi.dwSize.X - 1;
 szLine[j--] = 0; /* null terminate */
 while (szLine[j] == ' ')
 {
  szLine[j--] = 0;
 }
 
 if (j==-1) //代表當下的行無任何字元
 {
  StartLine = i;
  break;
 }
}
 
for (int i=StartLine ; i<=sbi.dwCursorPosition.Y ; i++) //清空每一行(避免命令列路徑大於一行時)
{
 coordLine.Y = i;
 FillConsoleOutputCharacter(hHandle, _T(' '), sbi.dwSize.X, coordLine, &dwCharsRead);
}

coordLine.Y = StartLine+1;
SetConsoleCursorPosition(hHandle, coordLine);  //移動Console游標位置
_cwprintf(L"程式執行失敗\n");
keybd_event(VK_RETURN, 0 , 0, 0); //實現按下 "Enter" 鍵