【问题标题】:How to wait for ShellExecute to run?如何等待 ShellExecute 运行?
【发布时间】:2020-06-20 09:32:07
【问题描述】:

我已经设法在 VC++ 中使用 ShellExecute 来启动一个文档。 现在我希望运行一个接收一些参数的命令行工具,并在后台运行(隐藏,而不是最小化)并让它阻止我的程序流,这样我就可以等待它完成。 如何更改命令行:

ShellExecute(NULL,"open",FULL_PATH_TO_CMD_LINE_TOOL,ARGUMENTS,NULL,SW_HIDE);

问题是,我有将 html 转换为 pdf 的工具,我希望一旦工具完成,也就是 pdf 准备就绪,有另一个 ShellExecute 来查看它。

【问题讨论】:

    标签: visual-c++ shellexecute


    【解决方案1】:

    有一个CodeProject article 显示如何使用ShellExecuteEx 而不是ShellExecute

    SHELLEXECUTEINFO ShExecInfo = {0};
    ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    ShExecInfo.hwnd = NULL;
    ShExecInfo.lpVerb = NULL;
    ShExecInfo.lpFile = "c:\\MyProgram.exe";        
    ShExecInfo.lpParameters = "";   
    ShExecInfo.lpDirectory = NULL;
    ShExecInfo.nShow = SW_SHOW;
    ShExecInfo.hInstApp = NULL; 
    ShellExecuteEx(&ShExecInfo);
    WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
    CloseHandle(ShExecInfo.hProcess);
    

    关键点是标志SEE_MASK_NOCLOSEPROCESS,其中as MSDN says

    用于表示 hProcess 成员接收进程句柄。此句柄通常用于允许应用程序找出使用ShellExecuteEx 创建的进程何时终止

    另外,请注意:

    调用应用程序负责在不再需要时关闭句柄。

    【讨论】:

    【解决方案2】:

    您也可以使用 CreateProcess 代替 ShellExecute/ShellExecuteEx。此函数包括一个 cmd.exe 包装选项、返回退出代码和返回标准输出。 (包括可能并不完美)。

    注意:在我的使用中,我知道必须有标准输出结果,但 PeekedNamePipe 函数并不总是在第一次尝试时返回字节数,因此存在循环。也许,有人可以弄清楚这一点并发布修订版?另外,也许应该生成一个替代版本,分别返回 stderr?

    #include <stdio.h>
    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <Shellapi.h>
    
    
    /*
    Note: 
        The exitCode for a "Cmd Process" is not the exitCode
        for a sub process launched from it!  That can be retrieved
        via the errorlevel variable in the command line like so:
        set errorlevel=&[launch command]&echo.&echo exitCode=%errorlevel%&echo.
        The stdOut vector will then contain the exitCode on a seperate line
    */
    BOOL executeCommandLine( const CStringW &command,
                             DWORD &exitCode,
                             const BOOL asCmdProcess=FALSE,
                             std::vector<CStringW> *stdOutLines=NULL )
    {
        // Init return values
        BOOL bSuccess = FALSE;
        exitCode = 0;
        if( stdOutLines ) stdOutLines->clear();
    
        // Optionally prepend cmd.exe to command line to execute
        CStringW cmdLine( (asCmdProcess ? L"cmd.exe /C " : L"" ) +
                          command );
    
        // Create a pipe for the redirection of the STDOUT 
        // of a child process. 
        HANDLE g_hChildStd_OUT_Rd = NULL;
        HANDLE g_hChildStd_OUT_Wr = NULL;
        SECURITY_ATTRIBUTES saAttr; 
        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
        saAttr.bInheritHandle = TRUE; 
        saAttr.lpSecurityDescriptor = NULL; 
        bSuccess = CreatePipe( &g_hChildStd_OUT_Rd, 
                               &g_hChildStd_OUT_Wr, &saAttr, 0);
        if( !bSuccess ) return bSuccess;         
        bSuccess = SetHandleInformation( g_hChildStd_OUT_Rd, 
                                         HANDLE_FLAG_INHERIT, 0 );
        if( !bSuccess ) return bSuccess;         
    
        // Setup the child process to use the STDOUT redirection
        PROCESS_INFORMATION piProcInfo; 
        STARTUPINFO siStartInfo;    
        ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
        ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
        siStartInfo.cb = sizeof(STARTUPINFO); 
        siStartInfo.hStdError = g_hChildStd_OUT_Wr;
        siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
        siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
    
        // Execute a synchronous child process & get exit code
        bSuccess = CreateProcess( NULL, 
          cmdLine.GetBuffer(),  // command line 
          NULL,                 // process security attributes 
          NULL,                 // primary thread security attributes 
          TRUE,                 // handles are inherited 
          0,                    // creation flags 
          NULL,                 // use parent's environment 
          NULL,                 // use parent's current directory 
          &siStartInfo,         // STARTUPINFO pointer 
          &piProcInfo );        // receives PROCESS_INFORMATION    
        if( !bSuccess ) return bSuccess;         
        WaitForSingleObject( piProcInfo.hProcess, (DWORD)(-1L) );
        GetExitCodeProcess( piProcInfo.hProcess, &exitCode );   
        CloseHandle( piProcInfo.hProcess );
        CloseHandle( piProcInfo.hThread );
    
        // Return if the caller is not requesting the stdout results
        if( !stdOutLines ) return TRUE;
    
        // Read the data written to the pipe
        DWORD bytesInPipe = 0;
        while( bytesInPipe==0 ){
            bSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL, 
                                      &bytesInPipe, NULL );
            if( !bSuccess ) return bSuccess;
        }
        if( bytesInPipe == 0 ) return TRUE; 
        DWORD dwRead; 
        CHAR *pipeContents = new CHAR[ bytesInPipe ];    
        bSuccess = ReadFile( g_hChildStd_OUT_Rd, pipeContents, 
                             bytesInPipe, &dwRead, NULL);
        if( !bSuccess || dwRead == 0 ) return FALSE; 
    
        // Split the data into lines and add them to the return vector
        std::stringstream stream( pipeContents );
        std::string str;
        while( getline( stream, str ) ) 
            stdOutLines->push_back( CStringW( str.c_str() ) );
    
        return TRUE;
    }
    

    【讨论】:

      【解决方案3】:

      使用ShellExecuteEx 有时在使用COM 的情况下不起作用,因此必须考虑以下注意事项。

      因为 ShellExecuteEx 可以将执行委托给 Shell 扩展 (数据源、上下文菜单处理程序、动词实现) 使用组件对象模型 (COM) 激活,COM 应该是 在调用 ShellExecuteEx 之前初始化。一些外壳扩展 需要 COM 单线程单元 (STA) 类型。在这种情况下, COM 应该被初始化,如下所示:

      CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
      

      在某些情况下,ShellExecuteEx 不使用其中之一 类型的 Shell 扩展,这些实例不需要 COM 被初始化。尽管如此,最好的做法是始终 在使用这个函数之前初始化 COM。

      更多来自 MSDN 这里 https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecuteexa

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-02
        • 1970-01-01
        • 1970-01-01
        • 2019-05-08
        • 2014-02-06
        相关资源
        最近更新 更多