调用cmd来执行响应的命令,windows实际上也给了一些接口,但是有些在执行某些命令的时候,却不能够执行,比如 winsat。
system
这个命令使用 VS 的同学一定不陌生
当我们想要执行某个命令或打开某个程序时
注:该函数是阻塞的
WinExec
1
|
WinExec("ipconfig", SW_SHOWNORMAL); //第二个参数表示显示cmd命令框
|
注:该函数是非阻塞的,也就是说,当输入的命令需要长时间来执行时,程序并不会等待执行结果,而是直接往下运行
ShellExecute
上述两种执行cmd的方法比较少用。
先看这个这个函数原型,共有六个参数
1
2
3
4
5
6
7
8
|
HINSTANCE ShellExecuteA(
HWND hwnd, //指定父窗口,一般为NULL
LPCSTR lpOperation, //打开方式
LPCSTR lpFile, //要打开的文件,要执行的程序
LPCSTR lpParameters, //参数
LPCSTR lpDirectory, //缺省目录,一般为NULL
INT nShowCmd //命令框打开方式
);
|
各个参数含义:
- HWND hwnd 指定父窗口句柄(通常为NULL)
- LPCSTR lpOperation 指定动作,表示打开Filename的方式:
open 正常打开
runas 以管理员身份打开
print 打印Filename指定的文件
explore 表示浏览由FileName参数指定的文件夹
find 从lpDirectory指定的目录开始搜索
edit 启动编辑器并打开文档进行编辑
- LPCSTR lpFile, 表示要打开的文件,比如cmd.exe,calc(计算器)
- LPCSTR lpParameters, 打开程序所执行的参数
- LPCSTR lpDirectory, 缺省目录,一般为NULL
- INT nShowCmd 命令框打开方式
SW_HIDE(0)隐藏窗口并激活另一个窗口。
SW_MAXIMIZE(3)最大化指定的窗口。
SW_MINIMIZE(6)最小化指定的窗口并激活z顺序中的下一个顶级窗口。
SW_RESTORE(9)激活并显示窗口。如果窗口最小化或最大化,Windows会将其恢复到原始大小和位置。应用程序应在还原最小化窗口时指定此标志。
SW_SHOW(5)激活窗口并以当前大小和位置显示它。
SW_SHOWDEFAULT(10)根据启动应用程序的程序传递给CreateProcess函数的STARTUPINFO结构中指定的SW_标志设置show状态。应用程序应该使用此标志调用ShowWindow来设置其主窗口的初始显示状态。
SW_SHOWMAXIMIZED(3)激活窗口并将其显示为最大化窗口。
SW_SHOWMINIMIZED(2)激活窗口并将其显示为最小化窗口。
SW_SHOWMINNOACTIVE(7)将窗口显示为最小化窗口。活动窗口保持活动状态。
SW_SHOWNA(8)以当前状态显示窗口。活动窗口保持活动状态。
SW_SHOWNOACTIVATE(4)显示最近大小和位置的窗口。活动窗口保持活动状态。
SW_SHOWNORMAL(1)激活并显示一个窗口。如果窗口最小化或最大化,Windows会将其恢复到原始大小和位置。应用程序应在首次显示窗口时指定此标志。
- 最常用的几个:SW_SHOWNORMAL(正常显示)、SW_HIDE(隐藏)
例子:
1
|
ShellExecute(NULL, "runas", "cmd", "/c ipconfig >> D:\\disk.txt", NULL, SW_SHOWNORMAL);
|
含义:以管理员身份运行cmd,执行ipconfg命令,将输出重定向到C:\disk.txt中,命令框正常显示
注:和WinExec一样,该函数也是非阻塞的
阻塞式调用ShellExecute
1
2
3
4
5
6
7
8
9
10
11
12
|
SHELLEXECUTEINFO ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = "runas";
ShExecInfo.lpFile = "cmd";
ShExecInfo.lpParameters = "/c winsat disk >> D:\\disk.txt";
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_HIDE;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
|
每个参数的含义和ShellExecute都一样。其中上述命令参数中 /c:
- /c 是执行完命令后关闭命令窗口。
- /k 是执行完命令后不关闭命令窗口。
但上述方法却不是万能的,有些命令在cmd中可以执行,但如果按照上述方法打开cmd就不能执行,比如 winsat,会出现winsat不是内部命令或可执行文件 的错误。那是因为应用程序发生了重定向
原因链接
使用CreatProcess(最好使用这个)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
bool RunCmdAndOutPutRedirect(const std::string &outPutFile, const std::string &cmd, bool wait = false)
{
cout<< "outPutFile:" << outPutFile << " cmd:" << cmd << " wait:" << wait;
STARTUPINFOA si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE handle = CreateFileA(outPutFile.c_str(),
FILE_APPEND_DATA,
FILE_SHARE_WRITE | FILE_SHARE_READ,
&sa,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (!handle)
{
cout << "CreateFile failed:" << GetLastError();
return false;
}
memset(&si, 0, sizeof(STARTUPINFO));
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdInput = NULL;
si.hStdError = NULL;
si.hStdOutput = handle;
if (!CreateProcessA(NULL,
(LPSTR)cmd.c_str(),
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi))
{
cout << "CreateProcess failed:" << GetLastError();
return false;
}
if (wait)
{
WaitForSingleObject(pi.hProcess, INFINITE);
}
cout << "WaitForSingleObject finish return:" << GetLastError();
CloseHandle(handle);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return true;
}
void Task()
{
PVOID oldValue = NULL;
if (Wow64DisableWow64FsRedirection(&oldValue))
{
string filePath = "D:\\diak.txt";
string cmd = "winsat disk";
RunCmdAndOutPutRedirect(filePath, cmd, true); //true表示阻塞调用
}
else
{
LOG_WARN << "重定向失败:" << GetLastError();
}
if (Wow64RevertWow64FsRedirection(oldValue) == FALSE)
{
return;
}
}
|
如何调用winsat(解决应用程序重定向问题)
需要调用windows的两个函数:
Wow64DisableWow64FsRedirection()
Wow64RevertWow64FsRedirection()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
PVOID oldValue = NULL;
if (Wow64DisableWow64FsRedirection(&oldValue))
{
//ShellExecute(NULL, "runas", "cmd", "/c winsat disk >> D:\\disk.txt", NULL, SW_SHOWNORMAL);
//执行cmd命令
SHELLEXECUTEINFO ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = "runas";
ShExecInfo.lpFile = "cmd";
ShExecInfo.lpParameters = "/k winsat disk >> D:\\disk.txt";
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_HIDE;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
}
if (Wow64RevertWow64FsRedirection(oldValue) == FALSE)
{
return;
}
|
就很好的解决了重定向的问题。
Wow64DisableWow64FsRedirection() 主要是为了关闭重定向
Wow64RevertWow64FsRedirection() 主要是为了恢复重定向
这两个函数一定要成对出现,关闭完成任务后一定要记得恢复。要不然会对其他的产生一定的影响。
任务计划程序
实际上,调用cmd还有另外一种方法,就是使用任务计划程序。这种方法也可以解决上面重定向的问题,但使用起来会比较麻烦一些。