暂停程序,直到 powershell 脚本完成

Posted

技术标签:

【中文标题】暂停程序,直到 powershell 脚本完成【英文标题】:Pause program until a powershell script finishes 【发布时间】:2018-01-22 18:39:34 【问题描述】:

我正在用 C++ 编写一个应用程序,它需要从 Windows 10 中获取已安装软件包的列表。我找到了一种通过 Powershell 脚本将其转储到文本文件中的方法。

我像这样启动脚本:

system("start powershell.exe -windowstyle hidden Set-ExecutionPolicy RemoteSigned \n");
system("start powershell.exe -windowstyle hidden .//package_list_save.ps1");

package_list_save.ps1 是创建 .txt 文件的脚本。这是 package_list_save.ps1 的内容:

Start-Transcript -Path ts.txt -Force
Get-AppxPackage
Stop-transcript

我的问题是,在 Powershell 脚本有机会创建文本文件之前,应用程序的 C++ 部分似乎会继续运行,导致我的其余函数失败,因为它们依赖于包列表在文本文件中。以下是行为不正确的部分:

void loadPackageList(string PATH) 

    string temp;//a string to be used for any temporary holding of values

    ifstream fs;//create an in-file-stream
    fs.open(PATH);

    //skip 17 lines (all the junk up front) 
    for (int i = 0; i < 17; ++i) 
        getline(fs, temp);
    

    //clear out temp just in case.
    temp = "";

    string currentLine;
    string name;
    string packageName;
    bool isFramework;

    while (getline(fs, currentLine)) 

        //read: name
        if ((currentLine.length() > 4) && (currentLine.substr(0, 4) == "Name")) 

            //get the name
            name = currentLine.substr(20);

            //skip four more lines
            for (int i = 0; i < 4; ++i) 
                getline(fs, temp);
            

            //grab the fullPackageName line
            getline(fs, currentLine);

            packageName = currentLine.substr(20);

            //skip a line
            getline(fs, temp);

            //get the isFramework line
            getline(fs, temp);

            if (temp.substr(20) == "False") 
                isFramework = false;
             else 
                isFramework = true;
            

            //store all the relevent details of the package as a tuple
            tuple<string, string, bool>packageData;
            get<0>(packageData) = name;
            get<1>(packageData) = packageName;
            get<2>(packageData) = isFramework;

            packages.push_back(packageData);
        
    

        //DEBUG:
        for (tuple<string, string, bool> tp: packages) 
            clog << "Name: "          << get<0>(tp) << endl;
            clog << "  PackageName: " << get<1>(tp) << endl;
            clog << "  IsFramework: " << get<2>(tp) << endl;
        
 

到目前为止,我已经尝试过一些事情,比如只有在检测到文本文件时才会终止的 while 循环:

while (!isFileExists("thing.txt")) 

但这要么导致它完全忽略 while 循环(随后仍然导致失败),要么永远卡住。

我尝试过使用这样的检测方法(取自here):

inline bool exists_test1 (const std::string& name) 
    if (FILE *file = fopen(name.c_str(), "r")) 
        fclose(file);
        return true;
     else 
        return false;
       

但我似乎仍然无法让它按照我想要的方式运行。有没有更好的方法让我暂停我的 C++ 代码,直到 Powershell 完成?

编辑:这是一个最小的、可验证的示例:

#include <fstream>
#include <iostream>
#include <windows.h>

using namespace std;

int main() 

    ofstream fs;
    fs.open("package_list_save.ps1");
    fs << "Start-Transcript -Path ts.txt -Force" << endl;
    fs << "Get-AppxPackage" << endl;
    fs << "Stop-transcript" << endl;
    fs.close();

    system("start powershell.exe -windowstyle hidden Set-ExecutionPolicy RemoteSigned \n");
    system("start powershell.exe -windowstyle hidden .//package_list_save.ps1");

    ifstream ifs;
    ifs.open("ts.txt");

    string currentLine;

    while (getline(ifs, currentLine)) 
        cout << currentLine << endl;
    

    cout << "Done." << endl;

在第一次运行时,除了“完成”之外没有任何输出,但在第二次运行时(假设你不删除 ts.txt”有很多输出。我需要能够在第一次运行,而不是让用户运行我的应用程序两次。

【问题讨论】:

“似乎无法让它按照我想要的方式运行” - 它的行为如何 “应用程序的 C++ 部分将在 Powershell 脚本有机会创建文本文件之前继续运行,从而导致我的其余功能失败”。当我更多地考虑它时,也可能是 .txt 文件已创建,但 powershell 脚本在另外 50 毫秒左右没有填充实际文本。 @Aaron 您如何从应用程序中调用您的 powershell 脚本?使用 system() 应该阻塞(在同一个线程中)。 脚本是从什么开始的?另一个程序?您是否尝试过以独占访问权限打开文件? @Aaron 那些system() 调用和while() 循环在同一个线程中运行?请您提供minimal reproducible example。 【参考方案1】:

出了什么问题

system 运行开始 (doc link)。开始运行 powershell 并退出,允许system 返回。程序在 powershell 运行时继续。

虚拟示例:

int main() 
    system("start cmd");
    std::cout << "Done.";
 

在您看到它之前退出程序并终止命令提示符。但是

int main() 
    system("cmd");
    std::cout << "Done.";
 

等待命令提示符关闭后再继续。

解决方案 1

去掉开头。基于命令行中的“-windowstyle hidden”,这可能会在控制台输出中抛出一堆你不想要的废话,但对于概念验证很有用。

system("powershell.exe -windowstyle hidden Set-ExecutionPolicy  RemoteSigned \n");
system("powershell.exe -windowstyle hidden .//package_list_save.ps1");

解决方案 2

不要使用system。 Powershell 建议询问者的目标操作系统是 Windows,因此我们可以使用 Windows 系统功能。在这种情况下,我将 CreateProcess (doc link) 并在 hProcess 句柄上调用 WaitForSingleObject (doc link),该句柄存储在 PROCESS_INFORMATION 中,作为 lpProcessInformation 提供给 CreateProcess

此处使用详情:Using CreateProcess() to exit/close an opened Application

【讨论】:

我明白了。所以基本上“开始”是一个启动其他东西的命令。因此,一旦 start 完成启动 powershell 它就存在,允许 C++ 继续运行? @Aaron 你明白了。 好吧,酷。我使用 system 的原因是它看起来更简单,因为我的应用程序专门针对 Windows 10 机器。 如果你想要简单,只需使用解决方案 1 并删除“开始”。但请注意:两个系统调用将是两个独立的 powershell。我不太了解powershell,无法确定第一个powershell中设置的参数会持续到第二个。

以上是关于暂停程序,直到 powershell 脚本完成的主要内容,如果未能解决你的问题,请参考以下文章

Powershell表单 - 使用按钮暂停循环

暂停js脚本直到事件发生

C# - 如何暂停应用程序直到计时器完成?

powershell 暂停脚本

PowerShell初探

win7怎样运行powershell脚本