Launching a RemoteApp programmatically without a system call to "mstsc" - c#

I've an RDP file that successfully start a RemoteApp.
remoteapplicationmode:i:1
remoteapplicationprogram:s:||application
remoteapplicationname:s:application.exe
remoteapplicationcmdline:s:
authentication level:i:2
gatewayusagemethod:i:2
gatewayprofileusagemethod:i:1
gatewaycredentialssource:i:0
full address:s:aaa.bbb.ccc.com
I tried to copy its settings into my C# objects:
AxMsRdpClient7NotSafeForScripting rc = new AxMsRdpClient7NotSafeForScripting();
rc.OnConnected += (_1, _2) => { rc.RemoteProgram2.ServerStartProgram("application.exe", "", "%HOMEDRIVE%" + "%HOMEPATH%", true, "", true); };
rc.RemoteProgram2.RemoteProgramMode = true;
rc.RemoteProgram2.RemoteApplicationProgram = "||application";
rc.RemoteProgram2.RemoteApplicationName = "application.exe";
rc.TransportSettings.GatewayUsageMethod = 1;
rc.TransportSettings.GatewayProfileUsageMethod = 1;
rc.TransportSettings.GatewayCredsSource = 0;
rc.Server = "aaa.bbb.ccc.com";
rc.UserName = "DOMAIN\\user";
rc.AdvancedSettings7.PublicMode = false;
rc.AdvancedSettings7.ClearTextPassword = "pass";
rc.AdvancedSettings7.AuthenticationLevel = 2;
rc.DesktopWidth = SystemInformation.VirtualScreen.Width;
rc.DesktopHeight = SystemInformation.VirtualScreen.Height;
rc.AdvancedSettings7.SmartSizing = true;
rc.Connect();
I've been searching everywhere but I wasn't able to find any example of how to launch a RemoteApp programmatically.
I've red this page, but it was not very helpfull. The client (a COM control) is connecting successfully, but it just displays a blue screen and no RemoteApp is launched.
Furthermore, I'm not sure that the right method to launch rc.RemoteProgram2.ServerStartProgram, because it takes paths as arguments, while in my RDP file no path is present!
Can anyone help me? I'm using the right objects to do what I want?
The server runs Windows Server 2008R2

If all you want to do is pragmatically launch a RemoteApp you already have an rdp file for, then just start it as a process:
System.Diagnostics.Process.Start(#"C:\Path_To_Rdp_File.rdp");

Related

AxMSTSCLib how to redirect camera from client to remote session?

I am using AxMSTSCLib to develop a Windows application for establishing RDP connections. But I don't know how to redirect a physical camera from client to remote RDP session.
I tried to add camerastoredirect:s:* and devicestoredirect:s:* in .rdp file to redirect camera, it worked, I can use the camera in remote desktop via RDP.
But I can't find the correct property in AxMSTSCLib library to enable this feature.
I've tried to redirect all possible/dynamic devices, but it didn't work at all. Here is the code:
public AxMSTSCLib.AxMsRdpClient7NotSafeForScripting oRemote;
oRemote = new AxMSTSCLib.AxMsRdpClient7NotSafeForScripting();
oRemote.AdvancedSettings7.RedirectDevices = true;
oRemote.AdvancedSettings7.RedirectPorts = true;
oRemote.AdvancedSettings7.RedirectPOSDevices = true;
MSTSCLib.IMsRdpClientNonScriptable5 ocx = (MSTSCLib.IMsRdpClientNonScriptable5)oRemote.GetOcx();
ocx.RedirectDynamicDevices = true;
for (int i = 0; i < ocx.DeviceCollection.DeviceCount; i++)
{
MSTSCLib.IMsRdpDevice device = ocx.DeviceCollection.get_DeviceByIndex((uint)i);
device.RedirectionState = true;
}
I found IMsRdpCameraRedirConfigCollection in newer API documents, need to upgrade IMsRdpClientNonScriptable from 5 to 7.
I tried to print its count before setting Redirected as true, like this:
MSTSCLib.IMsRdpClientNonScriptable7 ocx = (MSTSCLib.IMsRdpClientNonScriptable7)oRemote.GetOcx();
Logger.Log("[ocx.CameraRedirConfigCollection.Count]:" + (ocx.CameraRedirConfigCollection.Count).ToString());
for (int i = 0; i < ocx.CameraRedirConfigCollection.Count; i++)
{
MSTSCLib.IMsRdpCameraRedirConfig camera = ocx.CameraRedirConfigCollection.get_ByIndex((uint)i);
Logger.Log("[camera.FriendlyName]:" + camera.FriendlyName);
camera.Redirected = true;
}
Unfortunately, I got zero.
According to the Docs, it looks like I have to add the camera symbolic link to the collection using AddConfig method. I stuck at this point, how do I add a camera symbolic link to the IMsRdpCameraRedirConfigCollection? How to get camera symbolic link?
Why IMsRdpCameraRedirConfigCollection is empty in the beginning? Since you didn't Rescan connected camera devices.
MSTSCLib.IMsRdpClientNonScriptable7 ocx = (MSTSCLib.IMsRdpClientNonScriptable7)oRemote.GetOcx();
// enumerates connected camera devices
ocx.CameraRedirConfigCollection.Rescan();
for (int i = 0; i < ocx.CameraRedirConfigCollection.Count; i++)
{
MSTSCLib.IMsRdpCameraRedirConfig camera = ocx.CameraRedirConfigCollection.get_ByIndex((uint)i);
camera.Redirected = true;
}
That's it, all connected camera devices will be redirected to the remote desktop.

Updating a file fails before Process execution

I am starting a VBScript via C# application. Before starting, I programmatically change some content of the VBScript: Exchanging a placeholder keyword with a computer hostname.
The VBScript executes but uses the placeholder keyword instead of the computer hostname. However, when stopping the code right infront of Process.Start() I can see that the VBScript was successfully modified and saved, containing the computer hostname.
I also tried with adding a Thread.Sleep before execution, but it did not help.
It worked before, I updated some unrelated content on the program and now it fails to work for most of the time. When I use the debugger, sometimes the VBScript is loaded with the correct content. But the final program fails almost all the time.
Any suggesstions what this could be?
Here is the code of the C# process execution:
if (boEditStartupFileFirst)
{
string[] stFileContent = File.ReadAllLines(stFullPath);
for (int i = 0; i < stFileContent.Length; i++)
{
try
{
stFileContent[i] = stFileContent[i].Replace("NAME", stHostname);
}
catch { }
}
File.Delete(stFullPath);
StreamWriter swWriter = new StreamWriter(stFullPath, true);
for (int i = 0; i < stFileContent.Length; i++)
{
swWriter.WriteLine(stFileContent[i]);
}
swWriter.Close();
}
Process p = new Process();
p.StartInfo.FileName = stFullPath;
p.StartInfo.Arguments = stArgs;
p.Start();
Here is the content of the VBScript:
If Not WScript.Arguments.Named.Exists("elevate") Then
CreateObject("Shell.Application").ShellExecute WScript.FullName _
, WScript.ScriptFullName & " /elevate", "", "runas", 1
WScript.Quit
End If
Set WshShell = WScript.CreateObject("WScript.Shell")
WshShell.Run "regedit"
WScript.Sleep 500
WshShell.SendKeys "%DM"
WScript.Sleep 200
WshShell.SendKeys "NAME"
WScript.Sleep 200
WshShell.SendKeys "{ENTER}"
What the VBScript does: Open RegEdit and connect to remote computer.

Start a Process from a C# Application impersonating a specific User

I have a .NET c# application,
the business flow (short and to the point) is:
Users make a call to my app which authenticates them by windows authentication.
If the user is a "special user" (business logic part, E.g. some account admin), I impersonate to a "Master Account" in the active directory which has read / write permissions to a shared folder.
I then create folders and files with the impersonated user context --> This works.
But when I try to start a process (bcp.exe for those who care), I can't get it to work!
After many failing attempts , getting many error messages such as "access denied",
and trying to use almost all of the Process.ProcessStartInfo() attributes which should assist me to run a process as a different user, I decided to Post this as a question.
I've read many blogs suggesting the only way to do this is to use the win32 dll and call CreateProcessAsUser() method, but it's just to damn complicated, and I couldn't find any working sample of it.
bottom line question:
How can I start a Process (Process.Start) from a c# app while in impersonation context as the impersonated user?
My code:
private void ExecuteCommand(string backupSource, string backupFilename, string formatFilename)
{
// This works --> Here I'm under impersonated user context
// with read write permissions to the shared folder
if (!Directory.Exists(OutputPath))
{
Directory.CreateDirectory(OutputPath);
}
using (Process p = new Process())
{
p.StartInfo = GetProcessStartInfo(backupSource, backupFilename, formatFilename);
//Here I'm currently getting ""Access Denied" exception"
p.Start();
...
}
}
private ProcessStartInfo GetProcessStartInfo(string backupSource, string backupFilename, string formatFilename)
{
var result = new ProcessStartInfo();
result.UseShellExecute = false;
result.RedirectStandardOutput = true;
result.RedirectStandardError = true;
var file = Path.Combine(PathToExecutable, "bcp.exe");
// #"C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn\bcp.exe";
result.FileName = file;
result.WorkingDirectory = Path.GetDirectoryName(file);
result.LoadUserProfile = true;
result.Domain = "IMPERSONATED USER DOMAIN";
result.UserName = "IMPERSONATED USER NAME";
var ssPwd = new SecureString();
string password = "IMPERSONATED USER PASSWORD";
for (int x = 0; x < password.Length; x++)
{
ssPwd.AppendChar(password[x]);
}
result.Password = ssPwd;
var backupFullFilename = GetFullFileName(backupFilename);
StringBuilder arguments = new StringBuilder(backupSource);
var formatFullFilename = GetFullFileName(formatFilename);
FormatArguments(arguments, backupFullFilename, formatFullFilename);
var argumentsString = arguments.ToString();
result.Arguments = argumentsString;
return result;
}
Edit #1:
I was able to resolve the "Access is denied" exception, by adding the impersonating user to the administrators group on the machine which the application that starts the process runs on.
Now, I'm having a different issue, no exception but seems like the process isn't starting, or exiting right on start, I'm getting exit code 1073741502.
I've read I must use the native win32 api CreateProcessAsUser() instead of System.Diagnostics.Process.Start() but I'm not sure if that's true.
Ideas?
Assistance would be appreciated.

Mapped drivelabel windows 7 issue

I am going crazy with an issue with LABEL of mapping a drive to windows with windows 7 OS. Scenario;
We need to map the drive as soon as user logs in to the machine. That seems to be working fine with other os versions except windows 7. Steps for windows 7;
EXE (c# made by us) launched in windows 7
EXE has mapped drive correctly
User logged out
Logged in back
Again exe is trying to map drive (Its registered as a startup exe)
The mapped drive label becomes "Network Drive" (Not sure how)
We are setting the proper values in registry as well as shown in below figure;
Problem is here;
Issue is only occurring when we do logout and login. If we manually launch exe, it works fine...
I have also tried to DELETE all these registry before mapping driving assuming it might be cache or something but nothing helped..
We are using zMapDrive to map a drive;
//create struct data
structNetResource stNetRes = new structNetResource();
stNetRes.iScope = 2;
stNetRes.iType = RESOURCETYPE_DISK;
stNetRes.iDisplayType = 3;
stNetRes.iUsage = 1;
stNetRes.sRemoteName = ls_ShareName;
stNetRes.sLocalName = ls_Drive;
//prepare params
int iFlags = 0;
if (lf_SaveCredentials) { iFlags += CONNECT_CMD_SAVECRED; }
if (lf_Persistent) { iFlags += CONNECT_UPDATE_PROFILE; }
if (ls_PromptForCredentials) { iFlags += CONNECT_INTERACTIVE + CONNECT_PROMPT; }
if (psUsername == "") { psUsername = null; }
if (psPassword == "") { psPassword = null; }
//if force, unmap ready for new connection
if (lf_Force) { try { zUnMapDrive(true); } catch { } }
//call and return
int i = WNetAddConnection2A(ref stNetRes, psPassword, psUsername, iFlags);
if (i > 0) { throw new System.ComponentModel.Win32Exception(i); }
Maybe a simple powershell script, renaming network drive will work for you? You can then use Task Scheduler to run it every time a user logs in.
$Rename = New-Object -ComObject Shell.Application
$Net = New-Object -ComObject WScript.Network
# map the drive if the path doesn't exist
If (!(Test-Path Z:))
{
$Net.MapNetworkDrive("Z:", '\\SERVER_ADDRESS\Directory', $false, "user", "password")
}
# change the drive name
$Rename.NameSpace("Z:\").Self.Name = "MyNetDriveLabel"
From my experience, support for network mapped drives is somewhat buggy in Windows 7, so I use similar workaround on a few of our Win7 machines.

C# Windows Application - Linking a button to start a game

I believe I have been taking the right approach to this so far, but I would like to have a button start a video game on my computer.
So far, I have a button linking to a process:
void button2_Click(object sender, EventArgs e)
{
process1.Start();
}
this.process1.EnableRaisingEvents = true;
this.process1.StartInfo.Domain = "";
this.process1.StartInfo.FileName = "MBO\\marbleblast.exe";
this.process1.StartInfo.LoadUserProfile = false;
this.process1.StartInfo.Password = null;
this.process1.StartInfo.StandardErrorEncoding = null;
this.process1.StartInfo.StandardOutputEncoding = null;
this.process1.StartInfo.UserName = "";
this.process1.SynchronizingObject = this;
this.process1.Exited += new System.EventHandler(this.Process1Exited);
So, where-ever I place the EXE (the one I'm coding), it will launch the "marbleblast.exe" under the subfolder MBO relative to it's location.
It seems to be working and trying to launch the game, however, it says it cannot load files that are there. I tested the game without my launcher, and it worked. I believe it's trying to run the EXE, but not letting it use the other files inside of it's folder.
I'll give more details if needed.
How can I get the game to run normally?
try adding this
this.process1.StartInfo.WorkingDirectory= "MBO\\";
or something similar to set the Working Directory.
this.process1.StartInfo.WorkingDirectory= "MBO\";
There's sloppy programming in the game, it relies on the Environment.CurrentDirectory being set right. Which by default is the same directory as where the EXE is located. The upvoted answer repeats the mistake though. To make that statement actually fix the problem, you now rely on your CurrentDirectory being set right. If it is not set where you think it is then it still won't work.
The problem with the program's current directory is that it can be changed by software that you don't control. The classic example is OpenFileDialog with the RestoreDirectory property set to the default value of false. Etcetera.
Always program defensively and pass the full path name of files and directories. Like c:\mumble\foo.ext. To get that going, start with Assembly.GetEntryAssembly().Location, that's the path to your EXE. Then use the System.IO.Path class to generate path names from that. The correct always-works code is:
using System.IO;
using System.Reflection;
...
string myDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
string gameDir = Path.Combine(myDir, "MBO");
string gameExe = Path.Combine(gameDir, "marbleblast.exe");
process1.StartInfo.FileName = gameExe;
process1.StartInfo.WorkingDirectory = gameDir;
process1.SynchronizingObject = this;
process1.EnableRaisingEvents = true;
process1.Exited += new EventHandler(Process1Exited);
Set the WorkingDirectory property of the ProcessInfo to the correct directory.
I am Dobrakmato from MBForums. You simple need to add Working directory for Marble Blast.
this.process1.EnableRaisingEvents = true;
this.process1.StartInfo.Domain = "";
this.process1.StartInfo.FileName = "MBO\\marbleblast.exe";
this.process1.StartInfo.WorkingDirectory = "pathto marbleblast.exe directory";
this.process1.StartInfo.LoadUserProfile = false;
this.process1.StartInfo.Password = null;
this.process1.StartInfo.StandardErrorEncoding = null;
this.process1.StartInfo.StandardOutputEncoding = null;
this.process1.StartInfo.UserName = "";
this.process1.SynchronizingObject = this;
this.process1.Exited += new System.EventHandler(this.Process1Exited);

Categories