I have a windows forms tool in VB that i have been working on for a while now.
Now i would like to be able to access all of the controls and return values through the command prompt so that i am able to play with it through Azure and basically make the application a black box.
So this is how i went about thinking i should do it.
1 - In my project, i created a second solution, a C# Windows Command line framework.
2 - i than added the following script to that second project in order to run the win forms
using EnabledTest;
using System;
using System.Windows.Forms;
namespace Command_lineStartup
{
internal class Program
{
private static frmMain MainForm;
[STAThread]
private static void Main(string[] args)
{
if (args.Length > 0)
{
// Command line given, display console
}
else
{
AllocConsole();
ConsoleMain(args);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(MainForm = new frmMain());
GUI();
}
}
private static void ConsoleMain(string[] args)
{
Console.WriteLine("Command line = {0}", Environment.CommandLine);
for (int ix = 0; ix < args.Length; ++ix)
Console.WriteLine("Argument{0} = {1}", ix + 1, args[ix]);
// Console.ReadLine();
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AllocConsole();
public static void GUI()
{
Console.WriteLine("Testing version 1 :");
Console.WriteLine("Enter Project File path to open, Project must be a .mmp file");
string Path = Console.ReadLine();
MainForm.LoadProject(Path);
}
}
}
However, I do not think this is the right way. When i run the console application through CMD using C:\TFS\Enabled Test\Command-lineStartup\bin\Debug\Command-lineStartup.exe"
I does not behave how i want it to work.
So my question is.
Am i going about this the right way? if so what am i doing wrong here
is there an easier way?
So i found a way in the end. When running the application through CMD, the arguments that are supplied can be accessed using
Dim cla As String() = Environment.GetCommandLineArgs()
then for each of the arguments provided u can do something with it.
For example,
If cla.Length > 1 Then
'cla(0) is the executable path.
'cla(1) is the Path to the project
If Not IsIDE() Then WCLicenseIsLicensed("Application", True)
Me.Text = Application.ProductName
mblnLoaded = True
LoadProject(cla(1))
TreeVieuwSystem.Nodes(cla(2)).Expand()
TreeVieuwSystem.SelectedNode = TreeVieuwSystem.Nodes(cla(2)).Nodes.Find(cla(2) & "\" & cla(3), True).First
NodeSelected()
If cla(2) = "Test Plans" Then
TheWindowThatAllowsYouToEditTheObject.RunTestPlan()
ElseIf cla(2) = "Tests" Then
TheWindowThatAllowsYouToEditTheObject.RunTest(False)
End If
Else
Related
I'm currently learning C#, and I'm trying to make a program that puts an already open instance of it on top but makes it pass the arguments it was given to the open instance before closing.
I understand how to make it close if an instance of it is running already, but is there any way to pass arguments to an already running instance?
The code:
using System;
using System.Windows.Forms;
using System.Web;
namespace foo
{
public static class Program
{
[STAThread]
public static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form form = new Form();
TextBox label = new TextBox();
label.Location = new System.Drawing.Point(0, 0);
label.Size = new System.Drawing.Size(500, 50);
if (args.Length == 0)
{
label.Text = "no args";
} else
{
args[0] = args[0].Substring(4);
if (args[0] == "") {
label.Text = "args are empty";
} else
{
label.Text = HttpUtility.UrlDecode(args[0]);
}
}
form.Controls.Add(label);
Application.Run(form);
}
}
}
If the program is already running, then the short answer is "no", at least - not as exe arguments; you do have some limited options, however:
if your program started it, it can redirect stdin/stdout and use that as a comms channel
otherwise, you're in IPC territory, so: sockets, named pipes, or similar as a comms channel (perhaps using gRPC, WCF or similar so you don't have to deal with all the details)
I am trying to code a program which is executed when a file is right clicked in windows, and then a context menu feature named 'Move to' executes a file in the windows registry HKEY ClASSES. It ought to parse in "%1" as argument when it executes, so that my program knows where the file is located. However, when I compile my single .cs file, the FolderBrowserDialog won't show. I am suspecting that it is because I haven't initialized some kind of form before I call it. Is it possible in some way to choose a folder from a single c# file without including Forms?
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
public class MoveTo : Form
{
public static string current_file_path;
public static string new_file_path;
public static string file_name;
public static void Main(string[] args){
if (args.Length > 0)
{
current_file_path = (string) args[0];
file_name = (string) current_file_path.Replace(Path.GetDirectoryName(Environment.GetCommandLineArgs()[1]), "");
var browser = new FolderBrowserDialog();
if (browser.ShowDialog()==DialogResult.OK)
{
new_file_path = browser.SelectedPath + file_name;
}else
{
Environment.Exit(1);
}
try
{
File.Move(current_file_path, new_file_path);
}catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}
If you bypass the argument check and try to show the FBD in a debugger, with this exact code, you will see System.Threading.ThreadStateException: 'Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.'
As per the error message, this exception won't be raised if no debugger is attached. Put an [STAThread] attribute on your Main method, like you normally see in any windows forms app:
[STAThread]
public static void Main(string[] args)
{
...
I also recommend you add an else for your outer if, to show an error if no arguments are passed (otherwise your app will exit silently
I have an application that is both gui and console.
Console: It executes from a windows schedule to do some automated tasks, so its called with an argument
GUI: Used for entering config parameters, a much nicer way for the user to do this than console.
All this works great. its primarily a console app, the console is hidden if its opened with no arguments and the configuration form is shown.
Problem:
If I open it FROM the console with NO arguments, the console is hidden and the form is shown.
how can i detect what or where i opened the app from, if it was opened from windows then hide the console, if it was opened from console then leave the console shown.
If you really want to know "where" your application has been started you have to know what is your parent process. In order to know your parent process you can read the solution of How to get parent process in .NET in managed way
Then you can for example check if your parent process name is explorer(windows) to open your application as a GUI.
sample code based on the solution provided in How to get parent process in .NET in managed way
namespace ConsoleApp1
{
public static class ProcessExtensions
{
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(Process.GetCurrentProcess().Parent().ProcessName);
Console.ReadKey();
}
}
}
This code will outputs:
debug in visual studio: devenv
start from windows: explorer
start from cmd: cmd
start from powershell console: powershell
...
One way to do this is to separate your cli version and gui version into 2 executable (like 7z do with 7z.exe a command line tool and 7zG the Gui version)
You could have 3 projects in visual studio:
MyApp.Console (console app)
MyApp.WindowsGui (winform/wpf app)
MyApp.Logic (all the logic)
Console and WindowsGui have a reference to your Logic project
This will give you cleaner code as each "Frontend" project will handle only their purpose (handling GUI or console stuff) and your Logic are callable by both frontends
I am unclear as to what you're trying to achieve. From my understanding, the application will launch as a console application regardless of having arguments or not. To prevent it from disappearing, you can utilize a Boolean to prevent the window from closing while the user is inputting configuration. For example (syntax may not be 100% at DialogResult):
using System;
using System.Windows.Forms;
// Allows you to access the static objects of Console
// without having to repeatedly type Console.Something.
using static System.Console;
static bool configured = false;
static bool showForm = false;
static void Main(string[] args) {
showForm = args.Length < 1;
if (showForm) {
WriteLine("The application needs to be configured.");
using (ConfigForm config = new ConfigForm()) {
if (config.ShowDialog() == DialogResult.OK) {
showForm = false;
configured = true;
// Set your configured arguments here.
}
}
}
// Prevents the console from closing.
while (showForm)
ReadKey();
// Do your processing in this condition.
if (!showForm && configured)
WriteLine("Thanks for playing. Press any key to exit.");
else // Retry or exit in this one.
WriteLine("An error occurred. Press any key to exit.");
ReadKey();
}
If your application is set as a console application then it will launch the console window by default. Now, if you need to show and hide your console at different times, you can look into this post where the accepted answer provides a proper way to utilize Windows API to achieve this without having to perform some shady logic to find the window by title or identity.
using System.Runtime.InteropServices;
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_HIDE = 0;
const int SW_SHOW = 5;
var handle = GetConsoleWindow();
// Hide
ShowWindow(handle, SW_HIDE);
// Show
ShowWindow(handle, SW_SHOW);
If this doesn't solve what you need, feel free to be more thorough in your post and include some code to give more definition to your issue. I am unable to comment and ask questions so I gave a basic solution. If you have any questions, feel free to ask.
This might help:
using System;
using System.Diagnostics;
static class IsRanFromConsole
{
private static readonly string[] consoleNames = {
"cmd", "bash", "ash", "dash", "ksh", "zsh", "csh",
"tcsh", "ch", "eshell", "fish", "psh", "pwsh", "rc",
"sash", "scsh", "powershell", "tcc"
};
private static bool isCache = false;
private static bool isConsole;
public static bool IsConsole()
{
if (!isCache)
{
string parentProc = Process.GetCurrentProcess().Parent().ProcessName;
isConsole = Array.IndexOf(consoleNames, parentProc) > -1;
}
return isConsole;
}
}
Usage:
Console.WriteLine(IsRanFromConsole.IsConsole());
For the .Parent() function, you need to add this code.
I have an unusual situation here.
Problem
I'm using Visual studio (VS) to write scripts to use in-game in the game Space Engineers.
The problem is that you only use a portion of the code from the file in-game. (I.E, Ctrl+A wont do). So selecting the correct portion is tedious.
I want to streamline the process of copying the desired code in VS and pasting it in Space Engineers.
The idea is to trim all unnecessary white space (there's a character limit) and copy to clipboard when pressing run in VS.
Where I'm at
I've found that you can make your own build configuration and use the "Pre-build event command line" to run something custom. The idea is to make a simple console application that does what I described above. But I don't know how to get the correct file to send to said application.
Am I on the right track? How do I send the desired file to the trimming application? Is there a better way?
Edit:
This is what I had in mind when I said "simple console application".
It does everything I needed it to do (trimming white-space and adding a portion of the code to clipboard). Only thing missing is that I have to specify the file name I want it to use. Which isn't important, it would just be nice.
using System;
using System.Windows.Forms;
namespace TrimFileToClipboard
{
class Program
{
[STAThread()]
static void Main(string[] args)
{
string startString = (args.Length > 1) ? "#region " + args[1] : "#region in-game";
string line;
string trimmed = "";
bool read = false;
int depth = 0;
System.IO.StreamReader file = new System.IO.StreamReader(args[0]);
while ((line = file.ReadLine()) != null)
{
if (!read && line.Contains(startString)) read = true;
else if (read && line.Contains("#region")) depth++;
else if (read && line.Contains("#endregion"))
{
if (depth == 0) break;
else if (depth < 0)
{
Console.WriteLine("There's something wrong with your #regions. Please edit the file.");
Console.ReadLine();
Environment.Exit(0);
}
else depth--;
}
else if (read) trimmed += line.Trim() + "\n";
}
file.Close();
Clipboard.SetText(trimmed);
}
}
}
It can be used by adding
"<path>\TrimFileToClipboard.exe" "$(ProjectDir)<classname>.cs"
to Pre-build event command line, in the project properties/Build events. Where <path> is the path to the application above and <classname> is the file you want to process.
Maybe I should post this part as an answer but I don't know if it's a decent approach or an ugly hack.
Instead of coping the code to the clipboard, I save it directly inside the game as saved workshop script with this simple C# console application.
The SE script I edit using VS has the comments \\script-begin and \\script-end to tell the application where to look for the actual code that needs to be in the programmable block.
After the execution the script will be available at the local workshop. It makes it very easy to work with the SE scripts, whenever I make a change using VS, I run the console application again and the script will be updated inside the game.
internal class Program
{
private static void Main(string[] args)
{
String[] InputLines, outputLines;
Int32 scriptBegin = 0, scriptEnd = 0;
String scriptName = args[0];
String inputPath = "C:\\Users\\hfand\\source\\repos\\se-scripts\\" + scriptName + ".cs";
if (File.Exists(inputPath))
{
InputLines = File.ReadAllLines(inputPath);
for (int i = 0; i < InputLines.Length; i++)
{
if (InputLines[i].Contains("script-begin"))
{
scriptBegin = i + 1;
}
if (InputLines[i].Contains("script-end"))
{
scriptEnd = i - 1;
}
}
outputLines = new List<string>(InputLines).GetRange(scriptBegin, scriptEnd - scriptBegin + 1).ToArray();
for (int i = 0; i < outputLines.Length; i++)
{
if (outputLines[i].Length >= 8)
{
outputLines[i] = outputLines[i].Substring(8);
}
}
String outputPath = "C:\\Users\\hfand\\AppData\\Roaming\\SpaceEngineers\\IngameScripts\\local\\" + scriptName;
if (Directory.Exists(outputPath))
{
File.WriteAllLines(outputPath + "\\Script.cs", outputLines);
}
else
{
Directory.CreateDirectory(outputPath);
File.WriteAllLines(outputPath + "\\Script.cs", outputLines);
}
Console.WriteLine(scriptName + " sincronizado");
}
else
{
Console.WriteLine("Arquivo \"" + inputPath + "\" não encontrado");
}
}
}
Here is an example of how the code in VS should look like
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using VRageMath;
using VRage.Game;
using Sandbox.ModAPI.Interfaces;
using Sandbox.ModAPI.Ingame;
using Sandbox.Game.EntityComponents;
using VRage.Game.Components;
using VRage.Collections;
using VRage.Game.ObjectBuilders.Definitions;
using VRage.Game.ModAPI.Ingame;
using SpaceEngineers.Game.ModAPI.Ingame;
namespace BlankScript
{
public class Program : MyGridProgram
{
//script-begin
public Program()
{
}
public void Save()
{
}
public void Main(string argument, UpdateType updateSource)
{
}
//script-end
}
}
You can write a C# command with my Visual Commander extension that gets active file path in Visual Studio as DTE.ActiveWindow.Document.FullName and then runs your file.ReadLine() loop over it and calls Clipboard.SetText(trimmed) at the end. See for example Copy current file, line, method sample code.
Let's say I have a windows service called "MyService" and an executable called "MyEXE"
Is it possible (from within "MyService") to start several instances of "MyEXE" running in seperate application domains in parallel?
I would apprecaiate if some one can also provide a small sample using .net.
As long as it is a managed program then, yes, you can run it in its own AppDomain. You'll need a thread to run the code, AppDomain.ExecuteAssembly() is the handy one that automatically starts running the Main() method of that program. Here's an example that uses two console mode applications:
using System;
using System.Threading;
using System.IO;
namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
string exePath = #"c:\projects\consoleapplication2\bin\debug\consoleapplication2.exe";
for (int ix = 0; ix < 10; ++ix) {
var setup = new AppDomainSetup();
setup.ApplicationBase = Path.GetDirectoryName(exePath);
var ad = AppDomain.CreateDomain(string.Format("Domain #{0}", ix + 1), null, setup);
var t = new Thread(() => {
ad.ExecuteAssembly(exePath);
AppDomain.Unload(ad);
});
t.Start();
}
Console.ReadLine();
}
}
}
And the one that's ran 10 times:
using System;
namespace ConsoleApplication2 {
class Program {
static void Main(string[] args) {
Console.WriteLine("Hello from {0}", AppDomain.CurrentDomain.FriendlyName);
}
}
}
One thing I didn't count on and stuck under a table a bit, the AppDomainSetup.ApplicationBase property didn't work as I expected. I had to pass the full path of the EXE to ExecuteAssembly() instead of just passing "consoleapplication2.exe". That was odd.