How to run a ClickOnce application (.appref-ms) on a remote computer? - c#

I am trying to launch a ClickOnce application via an .appref-ms shortcut on a remote machine using WMI, but cannot succeed. The below code works fine if I try to run notepad.exe.
ManagementPath pm = new ManagementPath(#"\\server\root\cimv2:Win32_process");
ManagementClass processClass = new ManagementClass(pm);
//Get an input parameters object for this method
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
//Fill in input parameter values
inParams["CommandLine"] = #"C:\Documents and Settings\Start Menu\Programs\New\New App.appref-ms";
//Execute the method
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);

Try launching the .appref-ms shortcut via rundll32:
inParams["CommandLine"] = #"rundll32.exe dfshim.dll,ShOpenVerbShortcut ""C:\New App.appref-ms"";
Alternatively, instead of relying on the shortcut path, you could use the application deployment URL (you can see it by opening the .appref-ms file in a text editor):
inParams["CommandLine"] = #"rundll32.exe dfshim.dll,ShOpenVerbApplication http://github-windows.s3.amazonaws.com/GitHub.application";
Keep in mind that Win32_Process.Create cannot create interactive processes remotely.

What is the error that you are getting.
BTW, there seem to be two problems with the commandline path that you are using.
Start Menu folder IMO is in C:\Documents and Settings\"Usernamehere"\StartMenu.
I am not sure but I think that you can't run a program remotely which lies inside a user's profile. Try installing the program to some other location like your c:\program files and then try to call it.
Apart from that, if you mention the exact error you are getting, then it would be helpful to diagnose the problem.

Related

Unable to Execute Remote Process

I am trying to run a few commands on a remote machine using Win32_Process, but I can't get it to work.
This is what I tried first:
var processClass = new ManagementClass(#"\\server.domain.co.uk\root\cimv2:Win32_Process");
var inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = #"echo. 2>C:\users\user.name\desktop\EmptyFile.txt";
inParams["CurrentDirectory"] = #"C:\windows\system32";
var outParams = processClass.InvokeMethod("Create", inParams, null);
But nothing happens. I also tried running this locally at root\cimv2:Win32_Process, but again there was no effect. I was able to get it working locally when calling notepad.exe instead of the command line, but this does not work on the remote computer.
How can I work out what is going wrong with this?
In outParams, which is a System.Management.ManagementBaseObject, I can see that ClassPath contains the value Evaluation timed out - could this be a clue as to why it isn't working?
After reading around this and running some tests, it seems that similar to those started from CScript, these processes cannot be made interactive:
You can use Win32_Process.Create to execute a script or application on a remote computer. However, for security reasons, the process cannot be interactive. When Win32_Process.Create is called on the local computer, the process can be interactive.
I am still a bit confused, because the code I ran above did not appear in the task manager, yet the article goes on to say:
The remote process has no user interface but it is listed in the Task Manager
Either way, this suits my needs as I am running a shell command - however it is worth noting that executing cmd commands in this way requires that they are formatted as:
cmd /c <command-goes-here>
In my case, the command needs to be run from a specific location, so I need to pass:
cmd /c cd <path-to-executable> && <command-goes-here>

Starting a process

As part of SharePoint automation testing, I am trying to open Internet Explorer as another user by using the System.Diagnostics.Process . Here is the following C# code
System.Diagnostics.Process p = new System.Diagnostics.Process();
// Domain and User Name:
p.StartInfo.Domain = "MYDOMAIN";
p.StartInfo.UserName = "myusername";
// Command to execute and arguments:
p.StartInfo.FileName = "C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe";
p.StartInfo.Arguments = "http://url/AllItems.aspx";
// Build the SecureString password...
System.String rawPassword = "thepassword";
System.Security.SecureString encPassword = new System.Security.SecureString();
foreach (System.Char c in rawPassword)
{
encPassword.AppendChar(c);
}
p.StartInfo.Password = encPassword;
// The UseShellExecute flag must be turned off in order to supply a password:
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = false;
p.Start();
When I run this automated test Visual Studio returns informing me that the test was successful, however Internet Explorer does not open.
Is there something in my code I am missing in order for a window to appear? There is no iexplore process running prior to the test being run.
putting double quotes around the file path (since it contains spaces) may help:
p.StartInfo.FileName = "\"C:\\Program Files (x86)\\Internet Explorer\\iexplore.exe\""
^^ ^^
In addition, if your intention is to start this from a service process or dll running in a service such as "SharePoint", then this code will probably not launch the process in the desktop desired. You'll need to set the desktop to "winsta0\\default" in the startup info.
To run a process the worker process should have high privileges and this is not an ideal case in any web application. If your purpose is to use IE for unit testing then I would consider using something like WaitIN. If your purpose is for application logic to access a URL and do something then consider using HttpWebRequest. If you still need a process to be started then create a Windows Service and then expose a web call so in Share Point you can just make a call and your Windows Service can run on local account or some other high privilege account.
Hope this helps and please provide the scenario why you want to start the IE and that can give you a better answer in the forum.

Why is my application failing when launched remotely?

I have a Windows application that sits on four different servers, its purpose is to look through a directory, find .wav files and copy them to a remote location. It works perfectly when ran from directory it's in on the server, but I don't like going to four different servers to execute the application. So below, I have a console app that is really just telling the user to store the IDs for the wav files in the right location so the four applications can see them, then executes the various programs. The problem is all four apps crash immediately after launch, I think it has something to do with how I'm executing the command, but I can't figure it out. Any ideas?
class Program
{
static void Main()
{
Console.SetWindowSize(90, 35);
Console.WriteLine(#"Place the ID of the interaction(s) you wish to recover in \\SERVER\c$\IDKeys.txt." + Environment.NewLine);
Console.WriteLine("Press Enter to find interactions.");
Console.ReadLine();
exeCmd();
}
static void exeCmd()
{
string[] executable =
{
#"\\Server1\C$\App\findwavs.exe",
#"\\Server2\C$\App\findwavs.exe",
#"\\Server3\C$\App\findwavs.exe",
#"\\Server4\C$\App\findwavs.exe"
};
foreach (string exe in executable)
{
Process proc = new Process();
proc.StartInfo.FileName = exe;
proc.Start();
}
}
}
The code you have executes the executable on the local machine.
Using #"\\Server1\C$\App\findwavs.exe does not mean that the executable will run on Server1, it just means that your local machine will look in this network path to obtain the executable file, but will run it locally.
You can use PsExec to execute an application on a remote machine.
Consider the following code that uses PsExec to run an executable on a remote machine:
string application = "c:\\app\\findwavs.exe";
string arguments = ""; //use this if you want to pass any command line arguments to the findwavs.exe executable
string location_of_psexec = "c:\\pstools\\psexec.exe"; //location of Psexec.exe on local machine
string remote_machine = "Server1"; //name of remote machine
Process process = new Process();
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.FileName = location_of_psexec;
process.StartInfo.Arguments = string.Format("\\\\{0} -c \"{1}\" \"{2}\"", remote_machine, application, arguments);
This code assumes that the application path (e.g. "c:\app\findwavs.exe") is relative to your local machine, not the remote machine.
If you want to use a path relative to the remote machine, simply remove the -c from the value of process.StartInfo.Arguments
UPDATE:
Quoting from the above reference:
If you omit a user name, the process will run in the context of your account on the remote system, but will not have access to network resources (because it is impersonating). Specify a valid user name in the Domain\User syntax if the remote process requires access to network resources or to run in a different account. Note that the password and command are encrypted in transit to the remote system.
To make you application able to access network resources, use the -u and -p switches to supply a username and a password.
Running your code would be equivalent to navigating to \\Server1\C$\App\findwavs.exe in Explorer and double clicking on the exe. What I mean by this is that it will run the program on the machine that told it to run, not where the exe is stored.
The easiest way I can see to accomplish the task you want to perform would be to use PsExec.

Running remote exe on remote device

I'm stuck with an issue that may not be easily solvable, but I'm hoping someone will be able to help. I am attempting to run an exe to install some software (SCCM client) on a remote device.
So what is happening is that I am running the program from my PC (Host1) to connect to a remote device (Host2) and instruct that device to run an exe from a server.
I have been using standard remote execution WMI code with no luck:
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", Host2), connOptions);
manScope.Connect();
if(manScope.IsConnected)
{
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = #sCommand;
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
}
else
{
MessageBox.Show("An error occurerd while attempting to connect to WMI.");
}
The problem I'm running into is executing sCommand when the exe is on a server. So when the parameter is "\\server\share\program.exe" nothing happens. When the parameter is "c:\Folder\program.exe" it works great. These devices we are targeting unfortunately have Admin$ and C$ disabled, and do not have the exe on their hard drive.
I am at a loss unfortunately - is it possible to use the Win32_Process.Create method to run a UNC exe, or is it possible to copy the exe or even the folder it is in to the Host device when Admin$ and C$ are disabled? I am trying to avoid psexec, and I'm honestly wondering if I'd run into the same issue using it anyways.
Can't you deploy a custom Windows Service application on the remote machine? This way you'll just have to communicate with the service using whatever channel you want and run the executable from there.

How to verify a remote DSN entry?

I am creating a verification utility that check various parts of an application to ensure they are configured correctly. One of the things I need to check is that the DSN entry in a Web.config file is a functional DSN, and that the DSN is pointing to the correct SQL server and database.
Currently you can run the utility in one of two modes, local or remote. My problem occurs when I am trying to verify a remote systems DSN on my local computer. I know why it doesnt work, I don't have the same DSN on my local computer. So what I need to figure out is how to either retrieve the DSN connection information from a remote machine to which I have administrative access, or to get the remote machine to verify it for me.
Any suggestions? Thanks!
EDIT
So apparenty the DSN info is stored in the registry too!
oRegConn = new ConnectionOptions();
oRegConn.Username = username;
oRegConn.Password = password;
scope = new ManagementScope(#"//" + servername + #"/root/default", oRegConn);
registry = new ManagementClass(scope, new ManagementPath("StdRegProv"), null);
inParams = registry.GetMethodParameters("GetStringValue");
inParams["sSubKeyName"] = "SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources\\" +
dsnName;
inParams["sValueName"] = "Server";
outParams = registry.InvokeMethod("GetStringValue", inParams, null);
server = outParams["sValue"].ToString();
Well I would say that its not really a valid check if you run it on another system (yours). The firewall rules could be different, the driver versions might be off, and a million more things. The first thing I would say is if this is a web application, build in a page that can run these checks, like a status page. If this is an application that you cant do that, you can use WMI to run a remote process and gather the results. Other than that your last option might be to do a self hosting WCF service or a windows service that you connect to for querying info. This isnt too uncommon, and I have done it many times using WMI.
In that case, you may want to investigate PSExec (by Sysinternals). You can run your utility on a remote machine in "local" mode, as long as you have Administrative access to that machine.
Your "DSN Verification" part would have to be a separate executable/command, which can be executed by your main utility using syntax similar to the following:
psexec \\REMOTE-Machine <DSN Verification Utility>.exe
The output can be captured within C# by redirecting stdout/stderr.
Forgive me if I'm mistaken, but wouldn't just opening a connection to remote database server let you know if your connection string/DSN is okay?
So the DSN conn values are stored in the registry as plaintext, which is what I needed.
oRegConn = new ConnectionOptions();
oRegConn.Username = username;
oRegConn.Password = password;
scope = new ManagementScope(#"//" + servername + #"/root/default", oRegConn);
registry = new ManagementClass(scope, new ManagementPath("StdRegProv"), null);
inParams = registry.GetMethodParameters("GetStringValue");
inParams["sSubKeyName"] = "SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources\\" +
dsnName;
inParams["sValueName"] = "Server";
outParams = registry.InvokeMethod("GetStringValue", inParams, null);
server = outParams["sValue"].ToString();

Categories