C++ 中隐藏DOS调用的命令行窗口
轉自:http://hi.baidu.com/jackyho2000/blog/item/b5c5fabdd3b4db0019d81fbb.html
?
我演示了一下在MFC程序中怎么應用DOS的dir的命令,可是我們遇到了需要解決的問題,首先就是文件dir.txt的殘留問題,其實這個問題很簡單,我們也可以用dos的del命令在操作后將dir.txt文件刪除,這樣的結果就是程序會兩次彈出窗口,這樣更加讓人無法接受了,現在我們的問題是,有沒有辦法隱藏彈出窗口?答案是有的,這點我在網上找了很久,都沒有找到解答,最后自己摸索出了一些方法,不知道還有沒有更好的方法,因為這些方法都有些缺點,比較惱火。
首先,我們可以用第二參數為SW_HIDE調用WinExec()這個windows的API,這時候又有個問題了,WinExec()是調用程序的,而DIR程序文件在哪里?呵呵,其實這點在有很多DOS使用經驗的人都知道,DOS命令在以前就分兩種,一種叫內部命令,一種叫外部命令。其中比較常用的比如dir,del都屬于內部命令,特點是直接加載進內存。而外部命令是可以在目錄中找到具體的文件的,當時就會常常遇到PATH設定不對導致的外部命令調用錯誤,而需要找到目錄去調用的情況。既然加載在內存里面,我們到哪里去找命令文件?答案我不知道,不過有個變通的方法。因為在windows中,DOS的實際調用都是用cmd程序,那么我們就用它來調用,具體方法是以/C為參數調用。比如我們要調用dir命令,那么具體方法如下:
WinExec("cmd /C dir");
理由可以參考help cmd。當然我們還是有以前那樣的問題,那就是dir后面跟具體目錄做參數的時候需要加引號,那么我們調用目錄的時候一般可以用下面的形式:
WinExec("cmd /c dir /"xxxxx/");
這種方法在直接調用的時候很好用,比如刪除文件的時候,直接一個WinExec("cmd /c del dir.txt");就可以了,但是也是有個問題,這是個太老的API了,所以根本沒有Unicode的版本,苦悶的Unicode版本程序因此無法較好的使用,在以CString為字符串調用的時候似乎只有兩種方法,一種是以ANSI方式編譯,一種就是通過Unicode到ANSI的轉換了,這樣的轉換很可能還會丟失中文信息。因此,個人推薦只在直接調用DOS命令的時候使用WinExec,而且也推薦直接調用的時候使用,因為WinExec只需要兩個參數,很容易調用。但是要在Unicode程序中以CString為參數調用怎么辦?當然,用MSDN中推薦的CreateProcess不會有任何問題,問題是,太復雜了。。。。。個人推薦另一個方案,ShellExecute。雖然比WinExec復雜一點,但是還可以接受。函數原型如下:
HINSTANCE ShellExecute(
HWND hwnd,
LPCTSTR lpOperation,
LPCTSTR lpFile,
LPCTSTR lpParameters,
LPCTSTR lpDirectory,
INT nShowCmd
);
這里,hwnd直接用窗口的句柄就可以了,lpOperation可以省略,默認為open,lpFile我們調用cmd,lpParameters我們加入參數,注意的是cmd要多個/C參數,目錄可以為空,nShowCmd為SW_HIDE以達到我們的目的,同上面的情況,調用ShellExecute的時候為以下形式:
ShellExecute(m_hWnd, NULL, _T(cmd), _T("/C dir /"xxxxxx/"");
比如在上一節(jié)的例子中,我們調用ShellExecute的方法就是下面這樣:
CString dir = _T("/C dir /"") + m_directory + _T("/" >dir.txt");
ShellExecute(m_hWnd, NULL, _T("cmd"), dir, NULL, SW_HIDE);
然后,改變后你會發(fā)現程序在第一次調用的時候不會有任何反應,一定要第二次點擊按鈕才能生效,原因可能會令人很困惑,有多線程編程經驗的人可能會反應過來,因為ShellExecute的調用實際上是新開了一個線程,那么所有關于多線程編程讓人郁悶苦惱煩躁的問題都完全適用。這里,問題在于,ShellExecute新開線程后直接返回了,不等dir調用完成,那么下面接著的打開文件根本就找不到文件,我給出一種解決方案,在打開文件的時候檢測一下,然后用Sleep休息200毫秒,再檢測,如此可以達到需要的要求:
while(!infile.is_open())
{
static int i = 0;
::Sleep(200);
infile.open("dir.txt");
++i;
if(i > 50)
{
MessageBox(_T("Error to create and open the file"));
exit(EXIT_FAILURE);
}
}
經過完善,上節(jié)中ReadFromDir函數完整結果如下:
std::string CTestDialogDlg::ReadFromDir()
{
UpdateData();
CString dir = _T("/C dir /"") + m_directory + _T("/" >dir.txt");
// _wsystem(dir);
std::string strFile,strTemp;
ShellExecute(m_hWnd, NULL, _T("cmd"), dir, NULL, SW_HIDE);
// WinExec(dir, SW_HIDE);
std::ifstream infile;
while(!infile.is_open())
{
static int i = 0;
::Sleep(200);
infile.open("dir.txt");
++i;
if(i > 50)
{
MessageBox(_T("Error to create and open the file"));
exit(EXIT_FAILURE);
}
}
while(std::getline(infile, strTemp))
{
strFile += strTemp + "/n";
}
infile.close();
WinExec("del dir.txt", SW_HIDE);
// _wsystem("del dir.txt");
return strFile;
}
經過如上改變,再使用程序,沒有看到源代碼的人,誰還知道你是用了DOS的DIR命令實現的呢?
這一節(jié)的主要內容是給廣大因為使用了DOS命令而導致程序運行效果不佳老彈出窗口的同志們信心,大膽的調用DOS命令吧,沒有人知道的,只要能簡單的完成任務,我們不擇手段,呵呵。
?
///
轉自:http://hi.baidu.com/notepad519/blog/item/0f126643545eae1673f05dc7.html
?
去除控件臺程序中的窗口顯示
總的來說都來改變程序的入口點來達到目的:
1.
#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup")
#include <iostream>
using namespace std;
int main(void)
{
?? cout<<"河北經貿大學"<<endl;
return 0;
}
在Cdm下 文件名.exe>>新的文件名.txt(格式后綴可變) 可以進行查看輸入內容。
2.
#pragma comment( linker, "/subsystem:windows" )
#include?? <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR???? lpCmdLine,
int?????? nCmdShow)
{
MessageBox(NULL,"顯示內容","標題",0);
???? return 0;
}
后記:一開始用的是AfxMessageBox 出現以后錯誤
'AfxMessageBox' : undeclared identifier
原因是:因為AfxMesageBox(?? )函數是
MFC類庫提供的函數,而Console程序又沒有提供支持?? MFC,
所以就不應該使用這個函數了。要實現用API函數
MessageBox(?? )來實現。
注意需要在添加?? #include?? <windows.h>
3、subsystem和可執(zhí)行文件的啟動
LINK的時候需要指定/subsystem,這個鏈接選項告訴Windows如何運行可執(zhí)行文件。
控制臺程序是/subsystem:"console"
其它程序一般都是/subsystem:"windows "
將 subsystem 選成"console"后,Windows在進入可執(zhí)行文件的代碼前(如mainCRTStartup),就會產生一個控制臺窗口。
如果選擇"windows",操作系統就不產生console窗口,該類型應用程序的窗口由用戶自己創(chuàng)建。
可執(zhí)行文件都有一個Entry Point,LINK時可以用/entry指定。缺省情況下,如果subsystem是“console”,Entry Point是 mainCRTStartup(ANSI)或wmainCRTStartuup(UNICODE),即:
/subsystem:"console" /entry:"mainCRTStartup" (ANSI)
/subsystem:"console" /entry:"wmainCRTStartuup" (UNICODE)
mainCRTStartup 或 wmainCRTStartuup 會調用main或wmain。
值得一提的是,在進入應用程序的Entry Point前,Windows的裝載器已經做過C變量的初始化,有初值的全局變量擁有了它們的初值,沒有初值的變量被設為0。
如果subsystem是“windows”,Entry Point是WinMain(ANSI)或wWinMain(UINCODE),即:
/subsystem:"windows" /entry:"WinMainCRTStartup" (ANSI)
/sbusystem:"windows" /entry:"wWinMainCRTStartup" (UINCODE)
WinMainCRTStartup 或 wWinMainCRTStartup 會調用 WinMain 或 wWinMain。
如果使用MFC框架,WinMain也會被埋藏在MFC庫中(APPMODUL.CPP):
extern "C" int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
"_t"是一個宏,對于ANSI版本,"_tWinMain"就是"WinMain";對于UINCODE版本,"_tWinMain"就是"wWinMain"。
全局C++對象的構造函數是在什么地方調用的?答案是在進入應用程序的Entry Point后,在調用main函數前的初始化操作中。所以MFC的theApp的構造函數是在_tWinMain之前調用的。
4、不顯示Console窗口的Console程序
在默認情況下/subsystem 和/entry開關是匹配的,也就是:
"console"對應"mainCRTStartup"或者"wmainCRTStartup"
"windows"對應"WinMain"或者"wWinMain"
我們可以通過手動修改的方法使他們不匹配。例如:
#include "windows.h"
#pragma comment( linker, "/subsystem:/"windows/" /entry:/"mainCRTStartup/"" ) // 設置入口地址
void main(void)
{
MessageBox(NULL, "hello", "Notice", MB_OK);
}
這個Console程序就不會顯示Console窗口。如果選/MLd的話,這個程序只需要鏈接LIBCD.LIB user32.lib kernel32.lib
總結
以上是生活随笔為你收集整理的C++ 中隐藏DOS调用的命令行窗口的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VC++ 隐藏控制台程序窗口
- 下一篇: 图片像素、英寸、厘米之间的单位换算