【MFC】Windows 执行bat批处理并获取其执行结果

编程入门 行业动态 更新时间:2024-10-19 01:28:15

【MFC】Windows 执行bat<a href=https://www.elefans.com/category/jswz/34/1768936.html style=批处理并获取其执行结果"/>

【MFC】Windows 执行bat批处理并获取其执行结果

01、目录

文章目录

      • 01、目录
      • 02、此文背景故事
      • 03、CreatePipe 与 CreateProcess
        • 3.1 CreatePipe
        • 3.2 CreateProcess
      • 04、程序小天地
      • 05、小结

02、此文背景故事

Cain 小熊是深圳市XXXXXX科技有限公司的一名职工,有一天,Beck老大叫Cain 小熊去办公室。

Beck老大:Cain 小熊,我们XXX版块的编译器还需要一个功能:支持输出编译结果,失败或者成功都会有执行的结果,你应该能办到吧?

Cain 小熊:当然可以(不敢说不可以)

Beck老大: 我们当前这里,前段时间你是用的bat批处理执行生成的,你要好好想想怎么把bat的结果输出出来,多余的信息我不想要,只需要核心编译:预处理、编译、汇编、链接这个过程的结果。

Cain 小熊: 放心,Beck老大,一定完成任务。

Beck老大: 嗯嗯,不错,那你先去忙吧!

出了办公室,Cain 小熊就四处找资料,怎么把批处理的结果得到,那个可是另起的一个线程。

直到他看见一篇bLog:管道技术实现重定向,他心中渐渐有了一丝希望,然后就参照管道技术实现重定向,做着做着,最后实现了此功能,现在Cain 小熊把他的体会与实践写出来,希望能够帮助更多的人。

下面根据上面文章浅谈一下两个重要的Window API。

03、CreatePipe 与 CreateProcess

3.1 CreatePipe

管 道(Pipe)实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另 一进程就可以从管道的另一端将其读取出来。匿名管道(Anonymous Pipes)是在父进程和子进程间单向传输数据的一种未命名的管道,只能在本地计算机中使用,而不可用于网络间的通信。

匿名管道: 未命名管道,只能用于本机间使用,父进程与子进程间使用。

第一:匿名管道只能实现本地进程之间的通信,不能实现跨网络之间的进程间的通信。
第二:匿名管道只能实现父进程和子进程之间的通信,而不能实现任意两个本地进程之间的通信。

创建管道函数原型:

BOOL WINAPI CreatePipe(_Out_    PHANDLE               hReadPipe,_Out_    PHANDLE               hWritePipe,_In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,_In_     DWORD                 nSize
);

Return Value:

如果函数成功,返回值不为零。
如果函数失败,返回值为零。要获取扩展错误信息,请调用GetLastError。

Remarks:
CreatePipe创建管道,将指定的管道大小分配给存储缓冲区。 CreatePipe还会在随后的ReadFile和WriteFile函数调用中创建该进程用于读取和写入缓冲区的句柄。
要从管道读取,一个进程在调用ReadFile函数时使用读取句柄。当以下任一条件为真时,ReadFile返回:写操作在管道的写入端完成,请求的字节数已被读取或发生错误。
当一个进程使用WriteFile写入匿名管道时,写操作在所有字节都被写入之前不会完成。如果在写入所有字节之前管道缓冲区已满,则WriteFile不会返回,直到另一个进程或线程使用ReadFile来创建更多的缓冲区空间。
使用具有唯一名称的命名管道实现匿名管道。因此,您经常可以将匿名管道的句柄传递给需要命名管道句柄的函数。
如果CreatePipe失败,输出参数的内容是不确定的。在这个事件中,不应该假设他们的内容。
要释放管道使用的资源,应用程序应该不再需要关闭句柄,这可以通过调用CloseHandle函数或与实例句柄关联的进程结束。请注意,管道的一个实例可能有多个与之关联的句柄。当命名管道的实例的最后一个句柄关闭时,管道的实例总是被删除。

3.2 CreateProcess

创建一个新进程及其主线程。新进程在调用进程的安全上下文中运行。

如果调用进程模拟另一个用户,则新进程使用调用进程的令牌,而不是模拟令牌。若要在模拟令牌表示的用户的安全上下文中运行新进程,请使用CreateProcessAsUser或CreateProcessWithLogonW功能。

微软Win32 文档:

BOOL CreateProcessA(LPCSTR                lpApplicationName,LPSTR                 lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL                  bInheritHandles,DWORD                 dwCreationFlags,LPVOID                lpEnvironment,LPCSTR                lpCurrentDirectory,LPSTARTUPINFOA        lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation
);
BOOL WINAPI CreateProcess(_In_opt_    LPCTSTR               lpApplicationName,_Inout_opt_ LPTSTR                lpCommandLine,_In_opt_    LPSECURITY_ATTRIBUTES lpProcessAttributes,_In_opt_    LPSECURITY_ATTRIBUTES lpThreadAttributes,_In_        BOOL                  bInheritHandles,_In_        DWORD                 dwCreationFlags,_In_opt_    LPVOID                lpEnvironment,_In_opt_    LPCTSTR               lpCurrentDirectory,_In_        LPSTARTUPINFO         lpStartupInfo,_Out_       LPPROCESS_INFORMATION lpProcessInformation
);

Return Value:

如果函数成功,则返回值为非零。
如果函数失败,则返回值为零。若要获取扩展错误信息,请调用GetLastError

注意,函数在进程完成初始化之前返回。如果无法找到所需的DLL或无法初始化,则进程将终止。若要获取进程的终止状态,请调用GetExitCodeProcess.

Remarks:

进程被分配一个进程标识符。标识符在进程终止之前是有效的。它可以用于标识进程,或在OpenProcess函数打开进程的句柄。进程中的初始线程也被分配一个线程标识符。可以在OpenThread函数打开线程的句柄。标识符在线程终止之前是有效的,并且可以用来唯一地标识系统中的线程。属性中返回这些标识符。PROCESS_INFORMATION结构。

04、程序小天地

根据上述需求,我将中间步骤封装为一个函数,下面贴出源程序:

CString CMainFrame::ExecuteBat(CString strCmd)
{SECURITY_ATTRIBUTES sa;HANDLE hRead,hWrite;sa.nLength = sizeof(SECURITY_ATTRIBUTES);sa.lpSecurityDescriptor = NULL;sa.bInheritHandle = TRUE;if (!CreatePipe(&hRead,&hWrite,&sa,0)){MessageBox("Error on CreatePipe()!");return NULL;}STARTUPINFO si={sizeof(si)};PROCESS_INFORMATION pi;si.hStdError = hWrite;si.hStdOutput = hWrite;si.wShowWindow = SW_HIDE;si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;TCHAR* cmdline=StringToChar(strCmd);if (!CreateProcess(NULL,cmdline,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)){MessageBox("Error on CreateProcess()!");return NULL;}CloseHandle(hWrite);char buffer[4096];memset(buffer,0,4096);CString output;DWORD byteRead;while(true){if (ReadFile(hRead,buffer,4095,&byteRead,NULL) == NULL){break;}output += buffer;}return output;
}

上面程序中还有一个外部程序,源代码如下:

TCHAR* CMainFrame::StringToChar(CString& str)
{int len=str.GetLength();TCHAR* tr=str.GetBuffer(len);str.ReleaseBuffer();return tr; 
}

这个函数的作用是讲CString格式的命令转化为TCHAR格式

这段程序确实可以返回其bat文件的所有执行结果,但是,Beck老大说了,只要核心的那一段的结果。
OK,我们在写入bat的时候就将无用的(不需要返回的)命令前面加上@符号。
此处是Dos编程的知识,语句前加上@,告诉编译器,@后面的结果(命令)不需要返回显示了。

这样子,我们将得到的output返回出来,就是核心的返回结果啦。

到此为止,我们继续让程序美化,显示为跟VS编译器一样的编译界面。

CString strInfo = ExecuteBat(XXXXX.bat);
m_ErrPane.m_ErrDlg.ShowInfo("===============生成:项目:XXXX,配置:Debug,win32,正在编译.....===============");
m_ErrPane.m_ErrDlg.ShowInfo("\r\n[InFo:\r\n");
if(成功条件满足)
{m_ErrPane.m_ErrDlg.ShowInfo(strInfo);m_ErrPane.m_ErrDlg.ShowInfo("================生成:编译成功!================");
}else{m_ErrPane.m_ErrDlg.ShowInfo(strInfo);m_ErrPane.m_ErrDlg.ShowInfo("================生成:编译失败!================",RGB(255,0,0)); //显示为红色字体
}

OK,想必这份程序可以在Beck老大那里蒙混过关了吧!哈哈哈哈

05、小结

此文是与实际问题相关的一篇总结,或许知识点不多,只有两个API,但是,如果正巧你也遇到同样的问题,这篇文章一定可以给你启发式的效果。
如果文章中有什么地方不够完美,请大家指出,博主会及时纠正的。

好啦,后面再一起讨论关于MFC的一些小问题吧,此文就到此结束。

版权声明:转载请注明出处,谢谢!

更多推荐

【MFC】Windows 执行bat批处理并获取其执行结果

本文发布于:2024-02-27 05:39:44,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1705384.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:批处理   并获   取其   MFC   Windows

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!