代码适用于 CLR 项目,但不适用于 Win32

Posted

技术标签:

【中文标题】代码适用于 CLR 项目,但不适用于 Win32【英文标题】:Code works in CLR project but not Win32 【发布时间】:2015-12-22 22:02:31 【问题描述】:

我正在尝试运行

std::string exec(const char* cmd) 
    std::shared_ptr<FILE> pipe(_popen(cmd, "r"), _pclose);
    if (!pipe) return "ERROR";
    char buffer[128];
    std::string result = "";
    while (!feof(pipe.get())) 
         if (fgets(buffer, 128, pipe.get()) != NULL)
            result += buffer;
    
    return result;


string domainNameList = exec("powershell.exe -Command \"$computers = Get-WsusComputer -All; $computers | foreach-object Write-Output $_.FullDomainName; \"");

在我的纯 CLR 应用程序中似乎可以正常工作,但是当我尝试在 Win32 应用程序中执行此操作时。我收到回复说

foreach-object is not recognized as a batch command

我不确定为什么管道会破坏一个字符串而不是另一个,但我希望它可以在我的 Win32 应用程序中工作,以便我可以链接必要的 DLL。

【问题讨论】:

你能提供这个函数的示例调用吗? @andlabs 我有字符串 exec(...) 函数,我从 main 调用最后一行。 domainNameList = exec("powershell.exe -Command... 对,因为我认为该错误来自 powershell 本身...您是否使用上面在 exec() 的 CLR 实现中发布的完全相同的代码?还是使用 exec() 的不同实现? 【参考方案1】:

正如documentation 所说:

_popen 函数创建一个管道并使用指定的字符串 command 异步执行命令处理器的衍生副本。

因此,在这种情况下,您的 Win32 代码实际上是在启动 cmd.exe 的一个实例,然后将您的字符串传递给它。

|cmd.exe 有特殊意义。因为管道涉及将命令输入cmd.exe,它实际上最终将| 解释为command redirection operator(即使它在输入字符串的引号内)。它获取左侧命令的输出并将其发送到右侧命令的输入。因此,就您而言,您实际上是在尝试执行命令:

powershell.exe -Command "$computers = Get-WsusComputer -All; $computers

并将其输出发送到此命令的输入:

foreach-object Write-Output $_.FullDomainName; "

这就是为什么您会收到关于 foreach-object 是未知命令的错误。

要执行您正在尝试的操作,请尝试转义 | 字符,以便在解析期间将其视为普通字符:

exec("powershell.exe -Command \"$computers = Get-WsusComputer -All; $computers ^| foreach-object Write-Output $_.FullDomainName; \"");

查看此页面了解更多详情:

Syntax : Escape Characters, Delimiters and Quotes

转义字符

^  Escape character.

在命令符号前添加转义字符可以将其视为普通文本。 当管道或重定向任何这些字符时,您应该使用转义字符作为前缀:&amp; \ &lt; &gt; ^ |

 e.g.  ^\  ^&  ^|  ^>  ^<  ^^ 

或者,考虑直接使用CreateProcess() 而不是_popen()。您可以使用命令字符串作为命令行参数而不是管道输入来运行cmd.exe,然后您不必转义引号内的| 字符(但您必须自己转义额外的引号):

std::basic_string<TCHAR> cmd = TEXT("cmd.exe /C \"powershell.exe -Command \"\"$computers = Get-WsusComputer -All; $computers | foreach-object Write-Output $_.FullDomainName; \"\"\"");
CreateProcess(NULL, const_cast<TCHAR*>(cmd.c_str()), ...);

要读取输出,您可以告诉CreateProcess() 将启动进程的STDOUT 句柄从CreatePipe() 重定向到匿名管道,然后您可以使用ReadFile() 从中读取:

Creating a Child Process with Redirected Input and Output

至于为什么在 CLR 中一切正常,我不知道。我只能推测 CLR 使用的命令处理器可能与 cmd.exe 不同,并且它以不同的方式解析您的命令字符串。

【讨论】:

以上是关于代码适用于 CLR 项目,但不适用于 Win32的主要内容,如果未能解决你的问题,请参考以下文章

Pygame:这个 spritesheet 代码适用于另一个项目,但不适用于这个项目。为啥?

代码适用于嵌入式Apache Tomcat 8但不适用于9。有什么改变?

PayPal IPN 适用于单个购物车项目,但不适用于多个购物车项目

jQuery 适用于 jsFiddle 但不适用于 localhost

VBS 脚本适用于 XP 32 位但不适用于 7 64 位

通过c#UWP应用程序中的Win运行时组件的c ++ dll适用于Arm而不适用于x86 / x64