Calling a Windows Forms Application via C# API - c#

I have a windows form application written in C# that allows me to point to a folder of images and parse those images into a easily view able format for a user. This is a stand alone application and it works fine. I want to extend this application so that instead of it parsing a folder of images, I can hand it a prebuilt data set, with all of the images and meta data preset by an external application via an API.
I have been able to compile the application into a class library and access the classes and structs to actually build the data set without issue, the problem I am having now is launching the application externally with the data set I have built.
For context, I am writing a tool that will allow the user to call this windows form application from Spotfire. Inside of spotfire I am parsing a DataTable object and building the data set from the information I have. Once this data set is built I need to be able to launch the application as a stand-alone process instead of calling the forms explicitly inside of Spotfire. (This is due to a limitation of GUI threads in Spotfire and we can't call a multi threaded process in a single threaded application as well as we want to keep the Spotfire GUI responsive which can't be done if we call the forms directly)
I know I can launch the exe standalone using Process.Start(), however this doesn't let me pass my information to teh application. How can I build this application to allow me to pass information to it? I've been trying to google examples of how to do this and keep coming up empty handed as people will reference ASP.net or things that are over my head.
Thank you in advance!
EDIT: An example of an application that handles this really well is below. We use DPlot Jr to create graphs externally. The dplot.dll exposes the following function:
[System.Runtime.InteropServices.DllImport("dplotlib64.dll")]
public static extern int DPlot_Plot8(
ref DPLOT d, double[] x, double[] y, string cmds);
which I can then use in my code
docNum = dplot.DPlot_Plot8(ref dPlot, X, Y, cmds);
docNums.Add(docNum);
calling this function in this way actually launches the dplot application and passes the object I've built "dPlot" along with the X and Y data in order to plot the information. I would like to build something like this in my windows form application in order to be able to launch it easily from an external application. Unfortunately I don't know how this function works inside the .dll
EDIT2: I have been able to modify the runtime via the commandline as suggested by Aybe. In my desktop application I have created a conditonal in the main program like so.
if (args.Length == 0 && false)
{
Application.Run(new frmWaveFormViewer());
}
else
{
DataSet dataSet = new DataSet();
//dataSet.LoadMetaData(args[0]);
dataSet.LoadMetaData(#"C:\Users\a0273881\AppData\Local\Temp\tmp1141.tmp");
Application.Run(new frmWaveFormViewer(dataSet));
}
the user can then call the forms externally by using the following...
DataSet dataSet = new DataSet(dpList);
dataSet.PrintDatasetMetadataToFile(String.Format(#"C:\Spotfire\DataSetTesting_{0}.csv", DateTime.Now.ToString("yyyyMMdd_HHmmss")));
string args = dataSet.GetMetaData();
ProcessStartInfo starter = new ProcessStartInfo();
starter.FileName = #"C:\Users\a0273881\Desktop\WaveFormViewer.exe";
starter.Arguments = args;
Process.Start(starter);
However, this is not easy to use for other developers.
I am starting to look into WCF, can anyone provide good resources on WCF for dummies? I'm currently reading through: http://www.codemag.com/article/0705041

I have been able to spawn a NamedPipeServer on the application when it is launched. The named pipeserver names itself tagged with the name of the application + the process id that it spawns with. The process number is logged to a ini file in the users appdata folder. The process can be started with an optional command line argument to effect the value of the "callingApplication" which is the INI Header. This way, we can create multiple instances of the application from different callers without interfering and ensuring connection to the correct named pipe.
static void Main(string[] args)
{
string callingApplication = "None";
if (args.Length != 0)
{
callingApplication = args[0];
}
int pId = Process.GetCurrentProcess().Id;
PipeServer.StartPipeServer(pId, callingApplication);
// do things
PipeServer.StopPipeServer();
}
The PipeClient side is accessed through public functions available in a API static class. Functions that connect through the pipe are all housed in a seperate PipeClient class. These functions require a spawned process id in order to connect to the correct pipe. These are api functions to either launch or return the needed pipe from an api
public static class API
{
public static int SendCommand(int aKey, ...)
{
try
{
PipeClient.StartCommandSendClient(input, aKey);
}
catch (TimeoutException)
{
// returns error codes based on exceptions
}
return 0;
}
}
So with this I've managed to create a link between two applications. All that is really required past this is custom implementation of methods exposed through the API class which have custom client methods to call as well. All of this together is pretty simple to call from my calling app...
int aKey = API.GetKey("callingAppName");
API.SendCommand(aKey, "[Reset()][AddPoint(arg1, arg2, arg3)]");

Related

Error when calling two fullTrust apps on the load of the page

I have a UWP app that does the recording and calling functionality. For this I have added two FullTrust Apps using Desktop bridge application. When I call just one fullTrust app everything works perfectly, but when I call two FullTrust apps by passing parameters (of the FullTrust apps to be started) the the first app that was started behaves incorrectly. For these two FullTrust apps I have used two different Appservice names declared in the Package.Manifest file of the Windows Packaging Project.
I have noticed that whenever I switch the position of the Fulltrust app call the last application that is called always remains active(has the priority of the Appservice connection) even if both has different app service names.
Here is the code I have added when user opens a page in UWP that starts Win32 app and background App
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("Win32");
}
if (ApiInformation.IsApiContractPresent("Windows.ApplicationModel.FullTrustAppContract", 1, 0))
{
await FullTrustProcessLauncher.LaunchFullTrustProcessForCurrentAppAsync("Background");
}
In the above code, the first app that is Started calls my Win32.exe and second background.exe.
How can I call these two apps independently? whenever we want to start them and close whenever required or may be in future I would like to start two apps at the same time but also I may need to close any app whenever required. Can anyone tel me how can I handle the correct communication path when calling two fullTrust apps at the same time?
How can I call these two apps independently?
For launching multiple desktop app, we suggest to make Launcher app to manage multiple apps, then call LaunchFullTrustProcessForCurrentAppAsync(string parameterGroupId) and pass GroupId parameter. And at first we need add the group in the desktop bridge appxmanifes file.
<Extensions>
<desktop:Extension Category="windows.fullTrustProcess" Executable="Launcher\Launcher.exe">
<desktop:FullTrustProcess>
<desktop:ParameterGroup GroupId="Background" Parameters="/background" />
<desktop:ParameterGroup GroupId="Win32" Parameters="/win32" />
</desktop:FullTrustProcess>
</desktop:Extension>
</Extensions>
Then use the Launcher to start all apps with parameter
static void Main(string[] args)
{
// determine the package root, based on own location
string result = Assembly.GetExecutingAssembly().Location;
int index = result.LastIndexOf("\\");
string rootPath = $"{result.Substring(0, index)}\\..\\";
// process object to keep track of your child process
Process newProcess = null;
if (args.Length > 2)
{
// launch process based on parameter
switch (args[2])
{
case "/background":
newProcess = Process.Start(rootPath + #"FullTrust_Background\FullTrust_Background.exe");
break;
case "/win32":
newProcess = Process.Start(rootPath + #"FullTrust_Win32\FullTrust_Win32.exe");
break;
}
}
}
For more detail please refer this tutorial.

Same machine, different app domains, same GUIDs coming up

I'm fairly certain I'm either doing something wrong, or understanding something wrong. It's hard to give a piece of code to show my problem, so I'm going to try explaining my scenario, with the outcome.
I'm starting up several instances of a DLL, in the same console application, but in it's own app domain. I then generated a Guid.NewGuid() that I assign to a class in the instance, and set the application's folder to a new folder. This works great so far. I can see everything works great, and my instances are seperated. However... when I started changing my app's folder to the same name as the unique GUID generated for that class I started picking up anomolies.
It works fine, when I instantiate the new instances slowly, but when I hammer new ones in, the application started picking up data in its folder, when it started up. After some investigation, I found that its because that folder already exist, due to that GUID already being instantiated. On further investigation, I can see that the machine takes a bit of a pause, and then continues to generated the new instances, all with the same GUID.
I understand that the GUID generating algorithm uses the MAC as part of it, but I was under the impression that even if the same machine, at the same exact moment generates two GUIDs, it would still be unique.
Am I correct in that statement? Where am I wrong?
Code :
Guid guid = Guid.NewGuid();
string myFolder = Path.Combine(baseFolder, guid.ToString());
AppDomain ad = AppDomain.CurrentDomain;
Console.WriteLine($"{ad.Id} - {guid.ToString()}");
string newHiveDll = Path.Combine(myFolder, "HiveDriveLibrary.dll");
if (!Directory.Exists(myFolder))
{
Directory.CreateDirectory(myFolder);
}
if (!File.Exists(newHiveDll))
{
File.Copy(hiveDll, newHiveDll);
}
Directory.SetCurrentDirectory(myFolder);
var client = ServiceHelper.CreateServiceClient(serviceURL);
ElementConfig config = new ElementConfig();
ElementConfig fromFile = ElementConfigManager.GetElementConfig();
if (fromFile == null)
{
config.ElementGUID = guid;
config.LocalServiceURL = serviceURL;
config.RegisterURL = registerServiceURL;
}
else
{
config = fromFile;
}
Directory.SetCurrentDirectory is a thin wrapper atop the Kernel 32 function SetCurrentDirectory.
Unfortunately, the .NET documentation writers didn't choose to copy the warning from the native function:
Multithreaded applications and shared library code should not use the SetCurrentDirectory function and should avoid using relative path names. The current directory state written by the SetCurrentDirectory function is stored as a global variable in each process, therefore multithreaded applications cannot reliably use this value without possible data corruption from other threads that may also be reading or setting this value
It's your reliance on this function that's creating the appearance that multiple threads have magically selected exactly the same GUID value.

Count the number of times the Program has been launched

How can I get the number of times a program has previously run in c# without keeping a file and tallying. Is there a Application class or something in c# to check the count.
Please give a detailed explantion as i know nothing about it.This is A windows console application not windows forms.
You can do that my creating an Entry in the Registry. And another way is by using an Application Settings.
But I prefer Application Settings because it has less task to do.
See HERE: Creating an Application Settings.
Tutorial From Youtube
Recent versions of Windows automatically maintain this information in the registry under HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist.
The data is obfuscated with ROT13, but that's easy to "decrypt". A free utility (with source code) is available and can serve as your starting point.
You could send a message to a database or webservice every time the program starts up (assuming there's a network connection).
You could keep a count on some form of hardware thet's not a standard storage device (therefore not technically being a file).
You could make a registry entry that you keep the count in (if you ignore the fact that the registry entry is, at some level, persisted into a file somewhere).
You could just have a file somewhere that keeps track of the count. Not sure why you're so opposed to this one in the first place....
If you are running a Winforms application, the you can easily use the Application Settings. Right click on your Solution Name --> Properties --> Settings Tab. More info and tutorial here.
Then, every time your program starts, increment this setting and save it.
Ref: Count the number of times the Program has been launched
In my knowledge Windows does not keep this information for you. You would have to tally the value somewhere (file, database, registry setting).
Better way is Application Settings as:
Create setting in app.config and then use it as:
Properties.Settings.Default.FirstUserSetting = "abc";
then, you usually do this in the Closing event handler of the main form. The following statement to Save settings method.
Properties.Settings.Default.Save();
Implementation using Registry:
static string AppRegyPath = "Software\\Cheeso\\ApplicationName";
static string rvn_Runs = "Runs";
private Microsoft.Win32.RegistryKey _appCuKey;
public Microsoft.Win32.RegistryKey AppCuKey
{
get
{
if (_appCuKey == null)
{
_appCuKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(AppRegyPath, true);
if (_appCuKey == null)
_appCuKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(AppRegyPath);
}
return _appCuKey;
}
set { _appCuKey = null; }
}
public int UpdateRunCount()
{
int x = (Int32)AppCuKey.GetValue(rvn_Runs, 0);
x++;
AppCuKey.SetValue(rvn_Runs, x);
return x;
}
If it's a WinForms app, you can hook the Form's OnClosing event to run UpdateCount.
Then Check tutorial to Read, write and delete from registry with C#

Access commandline arguments to WPF application in separate appdomain

Is there a clean way to access the commandline arguments passed as part of an AppDomain.ExecuteAssembly call that starts a WPF application?
I'm spinning up a WPF application in a separate app domain and passing arguments to the application like so:
AppDomain moduleDomain = AppDomain.CreateDomain("Friendly Name");
moduleDomain.ExecuteAssembly(path, new[] { "arg1", "arg2" });
There's a work-around to access these arguments, since both Environment.GetCommandLineArgs() and StartupEventArgs return the commandline arguments for the original application, not the one spun up with ExecuteAssembly().
I would like to access the arguments passed to the WPF application without having to manually define a Main method, preferably using StartupEventArgs. Is there a way to do so?
Starting the WPF application in a separate process works, but has performance penalties and complicates debugging.
Tigran's comment lead me to a solution that I'm happy with, using AppDomain.SetData instead of using command line arguments. The basic outline looks like this:
AppDomain moduleDomain = AppDomain.CreateDomain("Friendly Name");
moduleDomain.SetData("arg1", "arg1Value");
moduleDomain.SetData("arg2", "arg2Value");
moduleDomain.ExecuteAssembly(path);
Then, to access the 'arguments' in the WPF app:
string arg1Value = AppDomain.CurrentDomain.GetData("arg1");
string arg2Value = AppDomain.CurrentDomain.GetData("arg2");
This works well for my use case.

How to grab parent process standard output?

I'm writing an utility (http://reg2run.sf.net) which in case execution without arguments works as windows application (shows OpenFileDialog, etc), otherwise - as console application.
So, in first case I don't want to show a console window, that's why project is Windows Application.
But in second - I need to show it, and it's created with
if (ptrNew == IntPtr.Zero)
{
ptrNew = GetStdHandle(-11);
}
if (!AllocConsole())
{
throw new ExternalCallException("AllocConsole");
}
ptrNew = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero);
if (!SetStdHandle(-11, ptrNew))
{
throw new ExternalCallException("SetStdHandle");
}
StreamWriter newOut = new StreamWriter(Console.OpenStandardOutput());
newOut.AutoFlush = true;
Console.SetOut(newOut);
Console.SetError(newOut);
And what I want - is to grab parent process standard output and use it, if it exists (in case execution via cmd.exe or Far Manager). How can I do it?
I tried
static Process GetParentProc()
{
int pidParent = 0;
int pidCurrent = Process.GetCurrentProcess().Id;
IntPtr hSnapshot = CreateToolhelp32Snapshot(2, 0);
if (hSnapshot == IntPtr.Zero)
{
return null;
}
PROCESSENTRY32 oProcInfo = new PROCESSENTRY32();
oProcInfo.dwSize = (uint)Marshal.SizeOf(typeof(PROCESSENTRY32));
if (!Process32First(hSnapshot, ref oProcInfo))
{
return null;
}
do
{
if (pidCurrent == oProcInfo.th32ProcessID)
{
pidParent = (int)oProcInfo.th32ParentProcessID;
}
}
while (pidParent == 0 && Process32Next(hSnapshot, ref oProcInfo));
if (pidParent > 0)
{
return Process.GetProcessById(pidParent);
}
else
{
return null;
}
and
StreamWriter newOut = GetParentProc().StandardInput;
but got InvalidOperationException: StandardIn has not been redirected. Because of
GetParentProc().StartInfo.RedirectStandardOutput = false
There are several approaches for applications that need to choose whether to act as console or GUI applications, depending on context, on Windows:
Have two separate applications, and have one conditionally start the other.
A variant of the above strategy, have two applications, one called 'app.com' (i.e. just rename a console EXE with COM extension) and the other called 'app.exe', so that command-line invocations will find app.com first. Because of ancient DOS compatibility, .COM executables are found before .EXEs. (This in configurable in Windows; see the PATHEXT environment variable.)
The rxvt/Cygwin technique, which is one I haven't really seen documented anywhere else.
Let me go into a little bit of detail about how rxvt on Cygwin works. Rxvt is a terminal emulator that normally runs on the X Window system. Because of the limitations of the Win32 console, Cygwin packages it as a more fully-featured console, with support for things like lots of lines of history, dynamic resizing, per-instance configurable fonts and colour themes, non-application-freezing mouse select and copy, etc. In order to run natively on Windows, rxvt shipped with Cygwin includes a tiny X11 wrapper library for Win32. Rxvt on Windows is actually a console application for compatibility reasons with existing native Win32 executables, but most of the time you never see the console; you just see the rxvt terminal emulator window itself.
The way it works is specifically implemented in rxvt/W11/wrap/wrap.c in the rxvt source tree, in the function called hideConsole(). Basically, it opens up its console (with a CreateFile("CONOUT$" ...)), and checks to see if the cursor position is at (0,0) (using GetConsoleScreenBufferInfo() on the console handle).
If it is, then it infers that it has been started as a standalone application, rather than from a console parent application, and thus it knows the OS has created a dedicated Win32 console for the process. It proceeds to hide this console window, but it has to find it first. It uses SetConsoleTitle to set the console window's caption to a unique value based on the name of the application and the current thread ID. It then uses FindWindow to find this window's handle (periodically Sleeping for a few ms if necessary for the title to change, because the console windows are actually controlled by a different process entirely in Windows). When it eventually finds the window handle, it hides it with ShowWindowAsync, passing in SW_HIDE.
Using this approach, you can write an application that:
if started from a console parent, it can continue to use this console
if started as an application, it can optionally choose whether or not to hide the console
The only downside is a very brief flash of a console window at application startup.
You can always the following P/Invoke method:
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
const int ATTACH_PARENT_PROCESS = -1;

Categories