Powershell 到 EXE 工具的建议

Powershell to EXE tool Advice

所以这是交易。由于许多...我们只是说不是 PowerShell 聪明的人,他们将使用我刚刚完成的极其复杂的应用程序,我需要能够将它打包到 exe 包装器中。

这不应该那么难

我能够成功使用 PS2EXE,除了 AD 的某些原因,它会抛出一大堆我无法摆脱的 AD 文本。在感到沮丧并继续前进之前尝试修复了几天。

然后,我发现了PowerGUI。我根本不能说我喜欢它。然而,它的编译器正是我想要的!除了 Exchange 2010 管理单元与此应用程序 .NET 4.5 不兼容之外。

我想非常清楚地表明我的脚本在多台不同的计算机上完美,但是一旦我使用这些工具中的任何一个,一切都会崩溃

一个 exe 是我能想到的最好的东西,它可以简化界面,并防止技术上的智力发育迟缓破坏一切,或者 运行 我会遇到每一个小错误,因为他们 不知何故 进入代码并输入一些内容并保存,现在 没有任何效果 这是 世界末日 他们有不知道发生了什么

如果你们知道有什么工具可以将其打包成一个 exe,或者有任何其他关于如何提供帮助的想法,我将非常感谢你们能提供给我的任何东西。

你从来没有让我失望过!

在我看来,如果您真的想要一个 EXE 文件,您应该编写一个 .NET 应用程序,嵌入 PowerShell CmdLets 并不难。

为了避免最终用户修改您的代码,我知道两个解决方案:

首先: 在用户计算机上将执行策略设置为 AllSigned 并签署您部署的脚本。您可以设法使用我们自己的证书(一点都不贵)或 public 证书(更贵)。此解决方案的缺点之一是它不会阻止用户看到代码。另一个很大的缺点是 PKI 和签名代码基础设施会浪费很多时间。

其次: 用于非交互式脚本(小心这是一种临时工作):

  1. 创建新用户帐户
  2. 只允许访问新帐户的脚本文件。
  3. 在 Windows 调度程序中设置一个任务,以 运行 该特定帐户下的 PowerShell 脚本文件。计划任务的权限允许用户进行读取和执行访问。然后将任务设置为"disabled".
  4. 每当脚本文件需要运行时,相应的任务由用户手动启动。

使用此解决方案还可以让您远程执行脚本。

当我遇到类似的部署问题时 - 1) 用户不知道 powershell 2) 我不希望他们必须了解诸如执行策略之类的东西,3) 如何开始 PS, 4 ) 等。我将它包装在一个批处理文件中。我还想确保有经验的 PS 用户仍然具有 PS 的功能,因此批处理文件确定它是否在 PS 下 运行ning 和 运行 在当前 PS 会话中(如果适用)。我从不担心用户会弄乱脚本 - 如果它 "just worked",他们会很高兴。所以无论用户喜欢Explorer,CMD.EXE,还是PS,他们都被容纳了。

我先写的批处理文件运行是一段powershell代码,用来判断批处理文件的进程是否是powershell进程的g运行dchild。如果是,则正在从 PS 调用批处理文件。还会检查执行策略,如果它足够宽松,则使用 Wscript.SendKeys 将击键发送到 PS 以在当前 PS 会话中获取脚本 运行ning。如果不是,则它使用 -ExecutionPolicy 参数启动一个新的 PS 会话,并将脚本作为命令行参数 (-Command) 传递。

这段 powershell 代码使用 return 代码与 .CMD 文件通信。对不起,它很神秘,但命令行参数的长度是有限的。这是代码:

set scr=       $mp=[diagnostics.process]::getcurrentprocess().id
set scr=%scr%; $pp=([wmi]\"win32_process.handle='$mp'\").parentprocessid
set scr=%scr%; $gp=([wmi]\"win32_process.handle='$pp'\").parentprocessid
set scr=%scr%; $ep=[int][microsoft.powershell.executionpolicy](get-executionpolicy)
set scr=%scr%; try {$pnp=1-[int](([wmi]\"win32_process.handle='$gp'\").Name -eq \"powershell.exe\")
set scr=%scr%; } catch {$pnp=1}
set scr=%scr%; $ev = (8 * $pnp + $ep) -band 0xB; %wo% pp: $pp gp: $gp ev: $ev; if ($ev -le 1) {
set scr=%scr%    %wo% Launching within existing powershell session...`n;
set scr=%scr%    $w=new-object -com wscript.shell;$null=$w.appactivate($gp);

set scr=%scr%;   $w.sendkeys(\"^&{{}`$st =cat "%me%";`$sc=`$st -join [char]10 -split 'rem PS script';
set scr=%scr%     `$script:myArgs = `\" %*`\";`$sb=[scriptblock]::create{(} `$sc[3]{)};. `$sb{}}~\")
set scr=%scr%; }
set scr=%scr%; exit $ev
powershell -noprofile -Command %scr%

%wo% 是允许调试这个 "checker script"。如果正在调试,则 %wo% 设置为 write-host。否则它被设置为定义一个 "null" 函数,然后调用 null 函数。 null 不执行任何操作,因此不会输出作为函数参数的消息。

注意调用 SendKeys 时的转义。 ^ 是 CMD.EXE 转义字符,SendKeys 和 PS.

一样有自己的转义机制

如果 运行 从 PS 你最终进入 PS 会话,感谢 SendKeys。否则批处理文件会这样做:

set scr=       ren function:prompt prompto
set scr=%scr%; function prompt{ 'myApp: '+(prompto)}
set scr=%scr%; $st= (cat %me%) -join \"`n\";
set scr=%scr%; $sx=($st -split 'rem PS script')
set scr=%scr%; $sc=$sx[3]
set scr=%scr%; %wo% myArgs: $myArgs script length: $sc.length 
set scr=%scr%; ^&{$script:myArgs=\"%*\"; iex $sc}

title MyApp
rem Change the number of lines on the console if currently set to 25
for /f "tokens=2" %%i in ('mode con^|findstr Lines:') do if %%i LEQ 25 (mode con lines=50&color 5F)
powershell -noexit -noprofile -command "%scr%"

这个"helper script"也不能太长。因此,帮助脚本读取原始 .CMD 文件,然后使用字符串 'rem PS script' 将其拆分。该字符串将同时出现在这个帮助程序脚本和批处理文件中(将批处理文件语句与 PS 语句分开)。在我的例子中,字符串也在批处理文件注释中,所以这就是使用 3 的索引的原因。

您的 PS 脚本可以定义函数或模块。您的 PS 脚本还可以输出一些介绍性信息,以向用户解释如何开始、如何获得帮助或任何您想要的内容。

除了使用 PS 命令行,您的 PS 脚本还可以创建它自己的交互环境(例如使用 Read-Host)。但是我不想这样做,因为它会阻止有经验的 PS 用户使用他们关于 PS 的知识。例如,如果您的脚本需要 username/password,有经验的 PS 用户可以使用 get-credential 创建要发送到您的脚本的凭据。