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 和签名代码基础设施会浪费很多时间。
其次: 用于非交互式脚本(小心这是一种临时工作):
- 创建新用户帐户
- 只允许访问新帐户的脚本文件。
- 在 Windows 调度程序中设置一个任务,以 运行 该特定帐户下的 PowerShell 脚本文件。计划任务的权限允许用户进行读取和执行访问。然后将任务设置为"disabled".
- 每当脚本文件需要运行时,相应的任务由用户手动启动。
使用此解决方案还可以让您远程执行脚本。
当我遇到类似的部署问题时 - 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 创建要发送到您的脚本的凭据。
所以这是交易。由于许多...我们只是说不是 PowerShell 聪明的人,他们将使用我刚刚完成的极其复杂的应用程序,我需要能够将它打包到 exe 包装器中。
这不应该那么难
我能够成功使用 PS2EXE
,除了 AD 的某些原因,它会抛出一大堆我无法摆脱的 AD 文本。在感到沮丧并继续前进之前尝试修复了几天。
然后,我发现了PowerGUI
。我根本不能说我喜欢它。然而,它的编译器正是我想要的!除了 Exchange 2010 管理单元与此应用程序 .NET 4.5
不兼容之外。
我想非常清楚地表明我的脚本在多台不同的计算机上完美,但是一旦我使用这些工具中的任何一个,一切都会崩溃。
一个 exe 是我能想到的最好的东西,它可以简化界面,并防止技术上的智力发育迟缓破坏一切,或者 运行 我会遇到每一个小错误,因为他们 不知何故 进入代码并输入一些内容并保存,现在 没有任何效果 这是 世界末日 他们有不知道发生了什么。
如果你们知道有什么工具可以将其打包成一个 exe,或者有任何其他关于如何提供帮助的想法,我将非常感谢你们能提供给我的任何东西。
你从来没有让我失望过!
在我看来,如果您真的想要一个 EXE 文件,您应该编写一个 .NET 应用程序,嵌入 PowerShell CmdLets 并不难。
为了避免最终用户修改您的代码,我知道两个解决方案:
首先: 在用户计算机上将执行策略设置为 AllSigned
并签署您部署的脚本。您可以设法使用我们自己的证书(一点都不贵)或 public 证书(更贵)。此解决方案的缺点之一是它不会阻止用户看到代码。另一个很大的缺点是 PKI 和签名代码基础设施会浪费很多时间。
其次: 用于非交互式脚本(小心这是一种临时工作):
- 创建新用户帐户
- 只允许访问新帐户的脚本文件。
- 在 Windows 调度程序中设置一个任务,以 运行 该特定帐户下的 PowerShell 脚本文件。计划任务的权限允许用户进行读取和执行访问。然后将任务设置为"disabled".
- 每当脚本文件需要运行时,相应的任务由用户手动启动。
使用此解决方案还可以让您远程执行脚本。
当我遇到类似的部署问题时 - 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 创建要发送到您的脚本的凭据。