Simple DOM Manipulation Without Console Window or Dependencies - c#

I am writing a solution for users to open a file, and this file should navigate to a certain website and insert the user's username into the login form for them. This file needs to be accessed by users which are on a citrix session.
This should be extremely simple, and I have discovered a way of doing it via 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
This works perfectly - it opens the web page, and inserts the username (email address).
However, when a user runs this from their machine, it seems impossible to hide either the CMD window (if running from .cmd or .bat) as well as the Powershell window. -WindowStyle Hidden just reduced the length of time the window appears for - which is not an acceptable solution.
So my next plan of action was to recreate the above code in c# and distribute it as an exe (as this is unlikely to show any console windows). However, I can't seem to find any method of doing this in C# which does not depend on external libraries (e.g. Selenium, which also requires a driver to be installed which is not a valid option for me).
I guess my question is - can the above Powershell script be recreated in C#? Is the -comobject from that script a .NET object, and if so how can I harness that in C#?
For reference - I am currently calling the .ps1 file as follows (in a CMD file) :
START Powershell.exe -WindowStyle Hidden -File \\file\Folder\SK\scripts\powershell\opensite.ps1
And I have not found any way of actually hiding the console windows which appear. I either need to find a solution to this, or a simple way of implementing the same thing in C#.

As I commented above, you could use VBScript or Jscript via wscript.exe to avoid launching another console window. Here's an example Jscript script, written as a batch + Jscript hybrid. Save it with a .bat extension, salt to taste, and give it a shot.
#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();
It's actually a polyglot script, in that you can save it either with a .bat extension or .js. As .js, you can double-click it and it'll launch (assuming .js files are associated with wscript.exe, as they typically are by default) without any console window at all.
If you'd prefer an .exe file, the script above can be modified fairly easily into one that will self-compile and link a Jscript.NET executable. (This script still gets a .bat extension.)
#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();

You can certainly call COM objects in C#, as explained in this existing answer:
If the library is already registered, you can perform the following
steps to have Visual Studio generate an interop assembly for you:
Open to your Visual Studio project.
Right click on 'References' (right under the project in your Solution Explorer) and select 'Add Reference'.
Select the COM tab.
Select the Component you wish to interop with.
Select 'ok'.
This will be a class or set of C# classes that wrap all of the COM
interface stuff with a normal C# class. Then you just use it like any
other C# library. If the import of the reference worked well, you can
explore it like any other reference and the
methods/structs/classes/constants should show up in that namespace and
intellisense.
Alternatively, you could execute the PowerShell within C# using a Runspace and a Pipeline. See Runspace samples on MSDN (here's example 3 from the link):
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();
}
}
}
While the second approach is no doubt going to take longer, it may make sense if you want to keep the PowerShell code out of the executable so that you can more easily change it, without having to recompile every time.
Essentially, the exe could just be used to execute a given powershell script invisibly.

I was using the below C# until I properly understood rojo's answer. This method does work, but is not as simple/elegant as rojo's so I marked that as the answer.
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);
}
}
Note that the main method here is running 3 different solutions, one after each other. The different "versions" are each dependant on whether it makes more sense in the environment to use .bat or .ps1 or .js. I will be using the .js version, which works in this context and the executable generated by this C# code hides the resulting console window.

Related

What is the best way to check PowerShell Execution Policy in C#?

When you run Get-ExecutionPolicy in PowerShell, it gets the effective execution policy. I need to know the best way to get that information in C#. I don't need to know how to change it like many other questions about PowerShell Execution Policy, I just need to know how to get it in C#.
Note:
PowerShell execution policies apply only on Windows.
With respect to Windows, the answer below covers both PowerShell editions.
It can be inferred from the docs that boxdog pointed to in a comment, but to spell it out:
using System;
using System.Management.Automation;
namespace demo
{
class ConsoleApp {
static void Main() {
using (var ps = PowerShell.Create()) {
var effectivePolicy = ps.AddCommand("Get-ExecutionPolicy").Invoke()[0].ToString();
ps.Commands.Clear();
Console.WriteLine("Effective execution policy: " + effectivePolicy);
}
}
}
}
Note:
The above assumes that you're using the PowerShell SDK - see this answer for the appropriate NuGet package to add to your project.
If you're using a PowerShell (Core) 7+ SDK, additional considerations apply:
On Unix-like platforms, execution policies fundamentally do not apply (Unrestricted is reported, though in effect it is Bypass), so the following applies to Windows only:
The LocalMachine scope of any - by definition install-on-demand - PowerShell (Core) 7+ version does not apply; only - if defined - the CurrentUser and GPO-based policies (which preempt the former) do.
On Windows:
In the absence of a relevant execution policy being defined, Restricted is the default, which categorically prevents execution of script files (.ps1).
If your application needs to execute .ps1 files when hosting the PowerShell SDK, for predictable behavior it is best to set the execution policy, for the current process only, as shown in this answer.
The most elegant solution would probably be to get the ExecutionPolicy registry key in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell. For this solution to work, your program needs to be running on the same architecture (x64 or x86) as the operating system it's running on or it won't be able to see the registry key. Code to do this would look something like this:
using Microsoft.Win32
...
string executionPolicy = Registry.GetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell", "ExecutionPolicy", null)?.ToString();
If for any reason you can't do the first solution, the second way I would recommend is by using the System.Management.Automation.PowerShell NuGet package. This method would look something like this:
using(var ps = PowerShell.Create()){
ps.AddScript("Get-ExecutionPolicy");
Collection<PSObject> output = ps.Invoke();
Console.WriteLine($"Execution policy is {output[0]}")
}
If you really don't want to add an extra NuGet package to your project, there is another, but quite a bit messier way of doing this using System.Diagnostics.Process and it's output stream. It would look something like this:
var procInfo = new ProcessStartInfo("powershell.exe", "-Command \"Get-ExecutionPolicy\"")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true
};
var proc = new Process
{
StartInfo = procInfo
};
proc.OutputDataReceived += Proc_OutputDataReceived;
proc.Start();
proc.BeginOutputReadLine();
Console.ReadLine();
...
private static void Proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(e.Data))
Console.WriteLine($"Execution policy is {e.Data}");
}

Launching C# program from another C# program

Due to me having knowledge of launching apps I am aware that you have multiple ways of launching an application in C# .NET, but I'm running into a issue that occurs when attempting to launch a SDL2 application.
I have attempted the following using the Process class to:
Start the .exe file of the build.
Start the application using "cmd.exe /K" or "cmd.exe /c" followed by "exec" or "call" or "start" followed by "{path to file}" or "{path to batch file to launch the application}". Launching the application via a batch file and CMD works fine. But, whenever I attempt to even launch the application (even in a new instance of Command-Prompt launched from cmd.exe /? start cmd.exe ?params) it will yield no result.
What I can observe is that the application tries to open. It takes forever to launch into the Window mode (starting the 3D environment). After a timeout it will either, render a couple of frames of a blank window before closing or close immediately after opening the window.
So my question is, does anyone have succesfully made a launcher application for a SDL app written in C# .NET? Or knows a way to debug this behaviour? Because unfortunately, the app does not send out a error message and since SDL safely closes the application I can't observe a crash either.
Edit #1
I'm not doing anything fancy with parameters as there shouldn't be any. I already have another one functioning that launches a normal C# application as my launcher requires to open 2 programs. 1 SLD application, 1 COM:VBA controlling application.
Given:
string audioSpectrumProgram = "AudioSpectrum.exe";
string audioSpectrumBatchProgram = "AudioSpectrum.bat";
private void BtnLaunchPPTApp_OnClick()
{
//Powerpoint controlling application
pVBAApp = Process.Start(presenterProgram, $"\"{this.path}\" {this.audioFormatParams[0]} {((this.ckboxGenerate.Checked) ? "--create" : "")} lang={this.languageCodesParams[this.cboxLanguage.SelectedIndex]}");
}
Method 1:
private void BtnLaunchSDLApp_OnClick()
{
pVizualizer = Process.Start(audioSpectrumProgram); //file launched from local path (is correct)
}
Method 2:
pVizualizer = Process.Start(audioSpectrumBatchProgram); //file launched from local path (is correct)
Method 3:
ProcessStartInfo info = new ProcessStartInfo("cmd.exe");
FileInfo spectrumFileInfo = new FileInfo(audioSpectrumProgram);
if (spectrumFileInfo.Exists)
info.Arguments = $"/c \"{spectrumFileInfo.FullName}\"";
pVizualizer = Process.Start(info);
Method 4:
based on senario of method 3. You don't have to parse arguments using ProcessStartInfo.
pVizualizer = Process.Start($"cmd.exe /K call \"{spectrumFileInfo.FullName}\"") //to observe what happens to the application
Edit #2
Not affected by changing the UseShellExecute to true or false
private void btnOpenVisualizer_Click(object sender, EventArgs e)
{
FileInfo spectrumFileInfo = new FileInfo(audioSpectrumProgram);
ProcessStartInfo info = new ProcessStartInfo(spectrumFileInfo.FullName);
info.UseShellExecute = true;
pVizualizer = new Process();
pVizualizer.StartInfo = info;
pVizualizer.EnableRaisingEvents = true;
pVizualizer.Exited += new EventHandler(myProcess_Exited);
pVizualizer.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine(
$"Exit time : {pVizualizer.ExitTime}\n" +
$"Exit code : {pVizualizer.ExitCode}\n"
);
}
A general way of analyzing startup issues is to use SysInternals Process Monitor.
Record the application that is not starting up properly. Use a filter for your application. Then go through all items which don't have SUCCESS in the result column. Typically you want to do that bottom-up, since the last error is the one stopping your application from loading.
Like this you'll find common startup issues like:
missing DLLs or other dependencies
old DLLs or DLLs loaded from wrong location (e.g. registered COM components)
wrong working directory, e.g. access to non-existent config files
Ok For Future reference:
Pathing to the files can be correct and everything might be in order but if you are using DLLs for imports. Change the process's working directory.
The project will run, libs can "sometimes" be found but can cause a weird unknown bug like this one. So the most optimal way of running another C# instance with SDL or any other kind of library:
private void RunSDLProgram()
{
FileInfo spectrumFileInfo = new FileInfo("pathToFile.exe");
ProcessStartInfo info = new ProcessStartInfo(spectrumFileInfo.FullName);
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.UseShellExecute = false;
info.WorkingDirectory = spectrumFileInfo.DirectoryName;
pVizualizer = new Process();
pVizualizer.StartInfo = info;
pVizualizer.EnableRaisingEvents = true;
pVizualizer.Exited += new EventHandler(myProcess_Exited);
pVizualizer.Start();
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine(
$"Exit time : {pVizualizer.ExitTime}\n" +
$"Exit code : {pVizualizer.ExitCode}\n" +
$"output : {pVizualizer.StandardOutput}\n" +
$"err : {pVizualizer.StandardError}\n"
);
}
Running a batch file will look at it's own directory and makes all references local, but it won't alter the working directory. (already had my suspicions about changing the work directory but I didn't see a way to call 2 opperations in process.start("cmd.exe");)

$global: is set in a PowerShell session but not in the System.Management.Automation.PowerShell instance

I'm moving the execution of a PowerShell script (StartBackup.ps1) that we would normally run in a standalone PowerShell session into a C# application. The script executes normally directly in PowerShell, imports modules/DLLs, calls into other scripts and sets a ton of variables.
In the C# application, I have:
using (PowerShell powerShell = PowerShell.Create())
{
powerShell.AddCommand("Set-ExecutionPolicy");
powerShell.AddParameter("Scope", "Process");
powerShell.AddParameter("ExecutionPolicy", "RemoteSigned");
powerShell.AddCommand("Set-Location");
powerShell.AddParameter("Path", "E:\\BackupTools");
powerShell.AddCommand("E:\\BackupTools\\StartBackup.ps1", false);
powerShell.AddParameter("Type", "Closed");
Collection<PSObject> results = powerShell.Invoke();
foreach (var resultItem in results)
{
...
}
}
The above runs just fine up until the point where $global: stuff gets set, and that's where it starts to throw errors. All of those values are null/empty.
I added a couple of powerShell.AddCommands to check whether or not those values are set after the script executes, and they are indeed all null in the PowerShell instance. In the standalone shell they're all set just fine.
What is the issue here? Why is the PowerShell instance different from an actual shell?
EDIT: The intention is not to just fire-and-forget the script. The intention is to have it do its job and then continue working with whatever artifacts it leaves behind in the PowerShell instance just as I normally would if this was powershell.exe.
If you want to just execute an existing PowerShell script, the simplest way would be to use the Process class. You can build the command line and run it.
The C# PowerShell Class is required if you want to build your script itself in your C# code.
Also, your AddCommand will chain the commands. Is that your requirement ?
MSDN post
Call AddCommand() methods to add this content to the execution pipeline.
using (PowerShell PowerShellInstance = PowerShell.Create())
{
// use "AddScript" to add the contents of a script file to the end of the execution pipeline.
// use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
PowerShellInstance.AddScript("param($param1) $d = get-date; $s = 'test string value'; " +
"$d; $s; $param1; get-service");
// use "AddParameter" to add a single parameter to the last command/script on the pipeline.
PowerShellInstance.AddParameter("param1", "parameter 1 value!");
}

Run Executable Without WScript

I have the following problem:
When a user logs on over citrix, the logon script writes to a network file
Once the file has been written to, notify the local machine so that it can perform some operations
Once this is done, restart the session (I know how to do this part, my difficulty is with monitoring a network file from a locally stored FileWatcher and then running a local exe)
I plan to achieve this by:
Registering an ActiveEventScriptConsumer on the machine, which will monitor a file and then run an executable once it is modified
The first actions the exe takes is to sleep for 5 minutes, and then perform the actions I need it to do
Constraints
The issue I have is that ActiveEventScriptConsuemrs can't use the Wscript object, meaning I can't run an exe with:
objShell.Run("C:\MyProgram.exe")
Is there some other way of running an executable from vbscript which can be done from an EventConsumer? Alternatively, am I able to register this subscription where the script run uses something other than VBScript?
I would include the Wait in my logons script, but I can't delay the end of this because this causes my group policies to run 5 minutes after logon!
I tried having a look at running a batch file from the VBS, but this also uses WScript.
The executable is checking for something which occurs when a (Citrix) session begins, so I can't use regular windows logon as a trigger because sometimes the user will log onto a session even though the machine itself has been logged on for some time.
Any help is much appreciated!
Event Subscription Code:
$Computername = $env:COMPUTERNAME
$query = #"
SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'CIM_DataFile' AND TargetInstance.Name='C:\\test\\filewatching\\tester.txt'
"#
$instanceFilter = ([WMICLASS]"\\$Computername\root\subscription:__EventFilter").CreateInstance()
$instanceFilter.QueryLanguage = 'WQL'
$instanceFilter.Query = $query
$instanceFilter.Name = 'EventFilterNameHere'
$instanceFilter.EventNameSpace = 'root/CIMV2'
$result = $instanceFilter.Put()
$script =
#"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("c:\test\filewatching\Log.log", 8, True)
objFile.WriteLine "1"
Dim objShell
Set objShell = WScript.CreateObject( "WScript.Shell" )
objShell.Run("C:\MyProgran.exe")
Set objShell = Nothing
objFile.WriteLine "2"
objFile.Close
"#
$instanceConsumer = ([wmiclass]"\\$Computername\root\subscription:ActiveScriptEventConsumer").CreateInstance()
$instanceConsumer.Name = 'ConsumerNameHere'
$instanceConsumer.ScriptingEngine = 'VBScript'
$instanceConsumer.ScriptFilename = ''
$instanceConsumer.ScriptText = $script
$instanceConsumer.Put()
[object]$Filter = (Get-WMIObject -Computername $Computername -Namespace root\Subscription -Class __EventFilter | Sort Name)
[object]$Consumer = (Get-WMIObject -Computername $Computername -Namespace root\Subscription -Class __EventConsumer | Sort Name)
$instanceBinding = ([wmiclass]"\\$Computername\root\subscription:__FilterToConsumerBinding").CreateInstance()
$instanceBinding.Filter = $Filter
$instanceBinding.Consumer = $Consumer
$instanceBinding.Put()
This is not possible using this type of event consumer. This is because the ActiveScriptEventConsumer doesn't allow any screen interaction except for basic message boxes:
Docs
When WMI is run as a service, scripts run by ActiveScriptEventConsumer do not generate screen output. Scripts that use MsgBox do run, but they do not display information on the screen. Running the WMI service as an executable file is not supported, but WMI allows scripts that use the MsgBox function to display output or accept user input. None of the methods provided by the WScript object can be used because ActiveScriptEventConsumer does not use Windows Script Host (WSH).
Another option would be to use CSCript, which only uses the command line, but this also doesn't support visual activity (only command-line), so for my purposes this will not work.

I'd like to run a command over ssh from a windows box running using c#

Note that this has to be on a windows box as I am using c# to access information about windows
(I need information from both a windows box and a linux box, plus I think that making a program/script that runs without gui and accesses windows from a linux box without user intervention would be more difficult, if this is not true please tell me, I would love to do get this running on *nix with only the part that access windows info running on windows).
There is a nice c# api to get this information from windows, on *nix its simple enough to run a command and parse the output to my needs.
There doesn't seem to much decent advice about using ssh from c# out there, sharpSSH and Granados seem to have not been updated for years, are they decent? should I be possible worried about security issues?
(the info I'm retrieving isn't sensitive but if the ssh implementation might have unpatched security flaws(if they haven't been updated for years) I'm worried about someone stealing my credentials.
Are there any other decent c# ssh libraries. If the command output is simple should I just run plink/putty(is it difficult to run a windows cmd shell for plink, and capture output(and is there a way to do it without the shell popping up)?
P.S. while the commercial library seems nice I prefer something free (as in cost and free in source if possible).
Sample Code
There are several commercial SSH client libraries for C#. Following code shows how to connect to a *nix box, run a command and read the response using our Rebex SSH Shell.
// create client, connect and log in
Ssh client = new Ssh();
client.Connect(hostname);
client.Login(username, password);
// run the 'uname' command to retrieve OS info
string systemName = client.RunCommand("uname -a");
// display the output
Console.WriteLine("OS info: {0}", systemName);
client.Disconnect();
For advanced scenarios (such as interactive commands) see SSH Shell Tutorial.
References & Stability
You might be already using Rebex SSH core library without knowing about it. The Rebex SFTP (which uses this SSH lib as a transport layer) is used by Microsoft in several products including Expression Web and Visual Studio 2010. The Rebex SSH Shell is 'just' another layer on top of it (most notable addition is a terminal emulator).
You can download a trial from http://www.rebex.net/ssh-shell.net/download.aspx. Support forum uses engine very similar to this site and runs on http://forum.rebex.net/
Disclaimer: I am involved in development of Rebex SSH
It is quite easy to call plink without the shell popping up.
The trick to not show a window is to set ProcessStartInfo.CreateNoWindow = true.
Add some error handling to this and you're done.
--- PlinkWrapper.cs ---
using System.Collections.Generic;
using System.Diagnostics;
namespace Stackoverflow_Test
{
public class PlinkWrapper
{
private string host { get; set; }
/// <summary>
/// Initializes the <see cref="PlinkWrapper"/>
/// Assumes the key for the user is already loaded in PageAnt.
/// </summary>
/// <param name="host">The host, on format user#host</param>
public PlinkWrapper(string host)
{
this.host = host;
}
/// <summary>
/// Runs a command and returns the output lines in a List<string>.
/// </summary>
/// <param name="command">The command to execute</param>
/// <returns></returns>
public List<string> RunCommand(string command)
{
List<string> result = new List<string>();
ProcessStartInfo startInfo = new ProcessStartInfo("plink.exe");
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.CreateNoWindow = true;
startInfo.Arguments = host + " " + command;
using (Process p = new Process())
{
p.StartInfo = startInfo;
p.Start();
while (p.StandardOutput.Peek() >= 0)
{
result.Add(p.StandardOutput.ReadLine());
}
p.WaitForExit();
}
return result;
}
}
}
--- END PlinkWrapper.cs ---
Call it like
PlinkWrapper plink = new PlinkWrapper("albin#mazarin");
foreach (var str in plink.RunCommand("pwd"))
Console.WriteLine("> " + str);
and the output will be like
> /home/albin
The nice thing with plink is that it is well proven and integrates well with pageant.
I used SharpSsh lib to make an asynchronous directory sync program between linux and windows boxes (i choosed sftp for secure file tranfer). Remained unchanged for years doesn't mean it is unsecure.
it is really easy to use:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tamir.SharpSsh;
namespace sftpex
{
class Program
{
static void Main(string[] args)
{
try
{
SshExec exec = new SshExec(ipAddress, username, password);
Console.Write("Connecting...");
exec.Connect();
Console.WriteLine("OK");
if (exec.Connected)
Console.WriteLine(exec.Cipher);
while (true)
{
Console.Write("Enter a command to execute ['Enter' to cancel]: ");
string command = Console.ReadLine();
if (command == "") break;
string output = exec.RunCommand(command);
string[] m = output.Split('\n');
for(int i=0; i<m.Length; i++)
Console.WriteLine(m[i]);
}
Console.Write("Disconnecting...");
exec.Close();
Console.WriteLine("OK");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
If you're not averse to interop with C libs, I believe OpenSSH is one of the best libraries available for the job.
I used SharpSSH in the past to execute commands on a Linux box. There are quite a few bugs, and I had to modify the code to fix some of them, but eventually it kinda worked...
There is a commercial software IP*Works SSH which can do the job.

Categories

Resources