【问题标题】:Simple DOM Manipulation Without Console Window or Dependencies没有控制台窗口或依赖项的简单 DOM 操作
【发布时间】:2016-08-31 08:33:58
【问题描述】:

我正在为用户编写一个打开文件的解决方案,该文件应该导航到某个网站并将用户的用户名插入到他们的登录表单中。 此文件需要由 citrix 会话中的用户访问

这应该非常简单,我发现了一种通过 Powershell 实现的方法:

$aduser = Get-ADUser $env:USERNAME -Properties EmailAddress
$emailaddress = $aduser.EmailAddress

$url = "https://website.org/loginpage.asp"
$ie = New-Object -comobject "InternetExplorer.Application"
$ie.visible = $true
$ie.Navigate($url)
WaitForPage 10
$ie.Document.GetElementById("USERID").Value = $emailaddress

这非常有效 - 它打开网页,并插入用户名(电子邮件地址)。

但是,当用户从他们的机器上运行它时,似乎不可能隐藏 CMD 窗口(如果从 .cmd.bat 运行)以及 Powershell 窗口。 -WindowStyle Hidden 只是缩短了窗口出现的时间长度 - 这不是一个可接受的解决方案。

所以我的下一个行动计划是在 c# 中重新创建上述代码并将其作为 exe 分发(因为这不太可能显示任何控制台窗口)。但是,我似乎在 C# 中找不到任何不依赖外部库的方法(例如 Selenium,它还需要安装驱动程序,这对我来说不是一个有效的选项)。

我想我的问题是 - 上面的 Powershell 脚本可以用 C# 重新创建吗?该脚本中的-comobject 是.NET 对象吗?如果是,我如何在C# 中利用它?


供参考 - 我目前正在调用.ps1 文件如下(在 CMD 文件中):

START Powershell.exe -WindowStyle Hidden -File \\file\Folder\SK\scripts\powershell\opensite.ps1

而且我还没有找到任何隐藏出现的控制台窗口的方法。我要么需要找到解决方案,要么需要一种在 C# 中实现相同功能的简单方法。

【问题讨论】:

  • 您可以将您的代码转换为 vbscript 或 jscript 并使用wscript.exe 运行它。瞧!没有控制台窗口。
  • @rojo 呃,作为一个 powershell 瘾君子,这看起来很落后,但我不得不承认它很简单,得到很好的支持,而且很容易。我认为值得回答。

标签: c# powershell batch-file jscript citrix


【解决方案1】:

正如我commented above,您可以通过wscript.exe 使用VBScript 或Jscript 来避免启动另一个控制台窗口。这是一个示例 Jscript 脚本,以批处理 + Jscript 混合形式编写。用 .bat 扩展名保存它,加盐调味,然后试一试。

@if (@CodeSection == @Batch) @then
@echo off & setlocal

wscript /e:JScript "%~f0"

goto :EOF
@end // end batch / begin JScript hybrid code

var user = WSH.CreateObject("ADSystemInfo"),
    email = GetObject("LDAP://" + user.UserName).EmailAddress,
    url = "https://website.org/loginpage.asp",
    ie = WSH.CreateObject('InternetExplorer.Application');

ie.visible = true;
ie.Navigate(url);
while (ie.readyState != 4) WSH.Sleep(25);

ie.document.getElementById('USERID').value = email;

if (ie.document.getElementById('password'))
    ie.document.getElementById('password').focus();

它实际上是一个多语言脚本,您可以使用 .bat 扩展名或 .js 来保存它。作为 .js,您可以双击它,它会启动(假设 .js 文件与 wscript.exe 关联,因为它们通常是默认的),根本没有任何控制台窗口。


如果您更喜欢 .exe 文件,可以很容易地将上面的脚本修改为可以自编译和链接 Jscript.NET 可执行文件的脚本。 (这个脚本仍然有一个 .bat 扩展名。)

@if (@CodeSection == @Batch) @then
@echo off & setlocal

for /f "delims=" %%I in ('dir /b /s "%windir%\microsoft.net\*jsc.exe"') do (
    if not exist "%~dpn0.exe" "%%~I" /nologo /target:winexe /out:"%~dpn0.exe" "%~f0"
)
"%~dpn0.exe"
goto :EOF
@end // end batch / begin JScript.NET hybrid code

import System;
import System.Diagnostics;

try {
    var wshShell:Object = new ActiveXObject("Wscript.Shell"),
        user:Object = new ActiveXObject("ADSystemInfo"),
        email:String = GetObject("LDAP://" + user.UserName).EmailAddress,
        url:String = "https://website.org/loginpage.asp",
        ie:Object = new ActiveXObject('InternetExplorer.Application');
}
catch(e:Exception) { System.Environment.Exit(1); }

ie.visible = true;
ie.Navigate(url);

// force IE window to the foreground and give it focus
var proc:System.Diagnostics.Process[] = System.Diagnostics.Process.GetProcesses();
for (var i:Int16 = proc.length, hwnd:IntPtr = IntPtr(ie.hwnd); i--;) {
    if (proc[i].MainWindowHandle === hwnd && wshShell.AppActivate(proc[i].Id)) break;
}

while (ie.readyState != 4) System.Threading.Thread.Sleep(25);
ie.document.getElementById('USERID').value = email;
if (ie.document.getElementById('password'))
    ie.document.getElementById('password').focus();

【讨论】:

  • 嗨 rojo,很好的回答,感谢您的帮助。不幸的是,在运行脚本时这仍然会显示一个控制台窗口,所以我无法使用它!不过,我找到了另一种解决方案,我将很快发布。
  • @Bassie 我认为你错了。我使用 .js 扩展名保存的第一个解决方案在由 wscript 处理时没有显示控制台窗口。看,cscript 中的“c”代表 console;而wscript 中的“w”表示您错了。对于第二种解决方案,生成的 .exe 也不会根据 target:winexe 开关显示任何控制台窗口——而且它表面上是您要部署的 .exe,而不是创建它的 .bat。
  • 我现在明白了——我没有完全理解“通过wscript 运行”的意思,因为我认为这是我的默认设置,但似乎我的默认设置为cscript,这就是为什么我得到了控制台窗口。再次运行您的第二个解决方案后,我看到生成了.exe - 您认为您可以向我指出与此 exe 生成相关的任何阅读材料吗?我以前从未见过这种方法,这似乎是一种非常有用的创建独立 exe 的方法(我相信你已经注意到我当前的解决方案需要将脚本保存在静态位置,因此并不完全独立)
  • @Bassie 除了编译器的/help 开关之外,我不知道有任何阅读材料。如果您导航到%windir%\Microsoft.NET\Framework 然后选择一个.NET 版本,您会发现csc.exe 用于编译和链接C# 项目; jsc.exe 用于 JScript.NET 项目,vbc.exe 用于 VB.NET 项目。 Visual Studio 中的所有各种编译选项开关和切换只是这些控制台应用程序的精美 GUI。关于批处理+JScript.NET混合格式,只是one of these的变种。
【解决方案2】:

You can certainly call COM objects in C#, as explained in this existing answer:

如果库已经注册,可以执行以下操作 让 Visual Studio 为您生成互操作程序集的步骤:

  • 打开您的 Visual Studio 项目。
  • 右键单击“参考”(在解决方案资源管理器中的项目下方)并选择“添加参考”。
  • 选择 COM 选项卡。
  • 选择您希望与之互操作的组件。
  • 选择“确定”。

这将是一个或一组包含所有 COM 的 C# 类 与普通 C# 类接口的东西。然后你就像任何一样使用它 其他 C# 库。如果引用的导入运行良好,您可以 像任何其他参考一样探索它 方法/结构/类/常量应该出现在该命名空间中,并且 智能感知。

或者,您可以使用运行空间和管道在 C# 中执行 PowerShell。 See Runspace samples on MSDN(这里是链接中的示例 3):

namespace Microsoft.Samples.PowerShell.Runspaces
{
  using System;
  using System.Collections;
  using System.Management.Automation;
  using System.Management.Automation.Runspaces;
  using PowerShell = System.Management.Automation.PowerShell;

  /// <summary>
  /// This class contains the Main entry point for this host application.
  /// </summary>
  internal class Runspace03
  {
    /// <summary>
    /// This sample shows how to use the PowerShell class to run a
    /// script that retrieves process information for the list of 
    /// process names passed to the script. It shows how to pass input 
    /// objects to a script and how to retrieve error objects as well 
    /// as the output objects.
    /// </summary>
    /// <param name="args">Parameter not used.</param>
    /// <remarks>
    /// This sample demonstrates the following:
    /// 1. Creating a PowerSHell object to run a script.
    /// 2. Adding a script to the pipeline of the PowerShell object.
    /// 3. Passing input objects to the script from the calling program.
    /// 4. Running the script synchronously.
    /// 5. Using PSObject objects to extract and display properties from 
    ///    the objects returned by the script.
    /// 6. Retrieving and displaying error records that were generated
    ///    when the script was run.
    /// </remarks>
    private static void Main(string[] args)
    {
      // Define a list of processes to look for.
      string[] processNames = new string[] 
      {
        "lsass", "nosuchprocess", "services", "nosuchprocess2" 
      };

      // The script to run to get these processes. Input passed
      // to the script will be available in the $input variable.
      string script = "$input | get-process -name {$_}";

      // Create a PowerShell object. Creating this object takes care of 
      // building all of the other data structures needed to run the script.
      using (PowerShell powershell = PowerShell.Create())
      {
        powershell.AddScript(script);

        Console.WriteLine("Process              HandleCount");
        Console.WriteLine("--------------------------------");

        // Invoke the script synchronously and display the   
        // ProcessName and HandleCount properties of the 
        // objects that are returned.
        foreach (PSObject result in powershell.Invoke(processNames))
        {
          Console.WriteLine(
                            "{0,-20} {1}",
                            result.Members["ProcessName"].Value,
                            result.Members["HandleCount"].Value);
        }

        // Process any error records that were generated while running 
        //  the script.
        Console.WriteLine("\nThe following non-terminating errors occurred:\n");
        PSDataCollection<ErrorRecord> errors = powershell.Streams.Error;
        if (errors != null && errors.Count > 0)
        {
          foreach (ErrorRecord err in errors)
          {
            System.Console.WriteLine("    error: {0}", err.ToString());
          }
        }
      }

      System.Console.WriteLine("\nHit any key to exit...");
      System.Console.ReadKey();
    }
  }
}

虽然第二种方法无疑会花费更长的时间,但如果您希望将 PowerShell 代码保留在可执行文件之外,这样您就可以更轻松地对其进行更改,而不必每次都重新编译。

从本质上讲,该 exe 可以用于不可见地执行给定的 powershell 脚本。

【讨论】:

  • 感谢您的帮助。您是否知道我在 PowerShell 脚本中使用的 COM 对象叫什么?在 PowerShell 中,它似乎被称为 InternetExplorer.Application,但是当我在 Visual Studio 的 COM 引用选项卡中搜索它时,我找不到它 - 我能得到的最接近的是 Microsoft Internet Controls,这似乎不正确。再次感谢
  • @Bassie 恐怕我不知道。 This page on code project 似乎暗示 Microsoft Internet Controls 是您需要的参考资料之一,但它可能比我或您预期的要多。 The answers to this question 建议使用第三方库。另请查看 rojo 对您的问题的评论;这是一个很好的选择,因为wscript.exe 已经包含在操作系统中。
【解决方案3】:

在我正确理解 rojo 的答案之前,我一直在使用下面的 C#。这种方法确实有效,但不像 rojo 那样简单/优雅,所以我将其标记为答案。

static class Program
{
    private static void Main()
    {
        var startInfo = new ProcessStartInfo(@"\\file\administration\Unused\Apps\opencascade\Alternate.bat")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            CreateNoWindow = true
        };
        Process.Start(startInfo);

        var startInfo = new ProcessStartInfo("Wscript.exe", @"\\file\administration\Unused\Apps\opencascade\Alternate.js")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            CreateNoWindow = true
        };
        Process.Start(startInfo);

        var startInfo = new ProcessStartInfo("Powershell.exe",
            @"\\file\administration\Unused\Apps\opencascade\opencascade.ps1")
        {
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            CreateNoWindow = true
        };
        Process.Start(startInfo);
    }
}

请注意,这里的主要方法是运行 3 个不同的解决方案,一个接一个。不同的“版本”分别取决于在环境中使用.bat.ps1.js 是否更有意义。我将使用.js 版本,该版本在此上下文中工作,并且此 C# 代码生成的可执行文件隐藏了生成的控制台窗口。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-02
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多