Suppress command window from started command-line application with .NET - c#

I have a module that needs to run a small .Net command-line program to check for updates. Everything is working great, however I am having trouble suppressing the Command Prompt output from being shown.
The app has it's own Windows Form that it pops up if it detected an update. Updating needs to run as a seperate app due to the fact that it requires a different execution context from the DLL it is launched from.
string path = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\" + AUTO_UPDATE_EXENAME;
updater.StartInfo.FileName = path;
updater.StartInfo.Arguments = AUTO_UPDATE_PARAMETERS;
updater.StartInfo.CreateNoWindow = false;
updater.StartInfo.UseShellExecute = false;
updater.StartInfo.RedirectStandardOutput = true;
updater.StartInfo.WorkingDirectory = path;
updater.Start();
I have tried most all of the different working combinations of CreateNoWindow, UseShellExecute, and RedirectStandardOutput and each of them results in that annoying black box popping up. The app does write to stdout but I only use that for debugging and the user shouldn't really see the text that it generates.
Supposedly CreateNoWindow and/or RedirectStandardOutput should prevent the box from popping up, but it does no matter how I set these variables.

Set the command-line application to a Winforms Application, but don't open a form when it executes, like you usually would.

You can hide the window on startup like this:
using System.Runtime.InteropServices;
namespace MyConsoleApp {
class Program {
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName,
string lpWindowName);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[STAThread()]
static void Main(string[] args)
{
Console.Title = "MyConsoleApp";
if (args.StartWith("-w"))
{
// hide the console window
setConsoleWindowVisibility(false, Console.Title);
// open your form
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run( new frmMain() );
}
// else don't do anything as the console window opens by default
}
public static void setConsoleWindowVisibility(bool visible, string title)
{
//Sometimes System.Windows.Forms.Application.ExecutablePath works
// for the caption depending on the system you are running under.
IntPtr hWnd = FindWindow(null, title);
if (hWnd != IntPtr.Zero)
{
if (!visible)
//Hide the window
ShowWindow(hWnd, 0); // 0 = SW_HIDE
else
//Show window again
ShowWindow(hWnd, 1); //1 = SW_SHOWNORMA
}
}
}
}
http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/ea8b0fd5-a660-46f9-9dcb-d525cc22dcbd

Here's an example code that interrogates the MAC on the active connection, this is a console application, no need to make this a Windows form...
public class TestARP
{
private StringBuilder sbRedirectedOutput = new StringBuilder();
public string OutputData
{
get { return this.sbRedirectedOutput.ToString(); }
}
// Asynchronous!
public void Run()
{
System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo();
ps.FileName = "arp";
ps.ErrorDialog = false;
ps.Arguments = "-a";
ps.CreateNoWindow = true; // comment this out
ps.UseShellExecute = false; // true
ps.RedirectStandardOutput = true; // false
ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; // comment this out
using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
{
proc.StartInfo = ps;
proc.Exited += new EventHandler(proc_Exited);
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.WaitForExit();
proc.BeginOutputReadLine(); // Comment this out
}
}
void proc_Exited(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended");
}
void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (e.Data != null) this.sbRedirectedOutput.Append(e.Data + Environment.NewLine);
}
}
Now, look at the Run method, that is in Asynchronous mode, and runs as a single console window - in fact a normal console application with no extra window popping up, notice the comments, if you were to change those lines, it becomes a synchronous process shelling out, very quickly, you will notice that this console will create another window with the output of the command arp. Because it is in Asynchronous mode, the output gets redirected to an event handler which stuffs the data into the StringBuilder instance for further processing...
Hope this helps,
Best regards,
Tom.

Related

Run the current application as Single Instance and show the previous instance

I just implemented this code that is guarding the Single Instance of the Application, in order to not run the application twice.
Now I am wondering how I can show the original Application process that is already running.
Here is my code in the program class:
static class Program
{
[STAThread]
static void Main()
{
const string appName = "MyappName";
bool createdNew;
mutex = new Mutex(true, appName, out createdNew);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form form = new Form1();
if (!createdNew)
{
form.Show(); <<=========================== NOT WORKING
form.Visible = true; <<===================== None
form.TopMost = true; <<===================== of
form.BringToFront(); <<===================== these working!
form.WindowState = FormWindowState.Maximized;
return;
}
Application.Run(form);
} private static Mutex mutex = null;
}
I propose you a different method, using a combination of the System.Threading.Mutex class and UIAutomation AutomationElement class.
A Mutex can be, as you already know, a simple string. You can assign an application a Mutex in the form of a GUID, but it can be anything else.
Let's assume this is the current Application Mutex:
string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";
Note:
Use the "Global\" Prefix to define the scope of the Mutex. If no prefix is specified, the "Local\" prefix is assumed and used instead. This will prevent a single instance of the process when multiple desktops are active or Terminal Services is running on the server.
If we want to verify whether another running Process has already registered the same Mutex, we try to register our Mutex and if it fails, another instance of our Application is already running.
We let the user know that the Application supports only a single instance, then switch to the running process, showing its interface and finally exit the duplicate Application, disposing of the Mutex.
The method to activate a previous instance of the Application may vary based on the type of the Application, but only some details change.
We can use Process..GetProcesses() to retrieve a list of the running processes and verify if one of them has the same details as ours.
Here, you have a windowed Application (it has an UI), so it's already possible to filter the list, excluding those processes that do not have a MainWindowHandle.
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
To identify the right one, we could test if the Process.ProcessName is the same.
But this name is tied to the executable name. If the file name changes (someone changes it for some reason), we will never identify the Process this way.
One possible way to identify the right Process is to test the Process.MainModule.FileVersionInfo.ProductName and check whether it's the same.
When found, it's possible to bring the original Application to front with an AutomationElement created using the MainWindowHandle of the identified Process.
The AutomationElement can automate different Patterns (sort of controls that provide automation functionalities for UI elements).
A WindowPattern allows to control a window-base control (the Platform is irrelevant, could be a WinForms' Form or a WPF's Window).
AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);
To use the UIAutomation functionalities, you have to add these refereneces in your Project:
- UIAutomationClient
- UIAutomationTypes
UPDATE:
Since the Application's Form might be hidden, Process.GetProcesses() will not find it's Window handle, thus AutomationElement.FromHandle() cannot be used to identify the Form Window.
A possible workaround, without dismissing the UIAutomation "pattern", is to register an Automation event, using Automation.AddAutomationEventHandler, which allows to receive a notification when an UI Automation events occurs, such as a new Window is about to be shown (a Program is run).
The event is registerd only if the Application needs to run as Single Instance. When the event is raised, the new Process AutomationElement Name (the Windows Title Text) is compared to the current and, if it's the same, the hidden Form will un-hide and show itself in Normal state.
As a fail-safe measure, we present an information MessageBox. The MessageBox caption has the same caption as the Application MainForm.
(Tested with a Form with its WindowsState set to Minimized and its Visible property set to false).
After the orginal Process has been brought to front, we just neeed to close the current thread and release the resources we created (mainly the Mutex, in this case).
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;
static class Program
{
static Mutex mutex = null;
[STAThread]
static void Main()
{
Application.ThreadExit += ThreadOnExit;
string applicationMutex = #"Global\BcFFcd23-3456-6543-Fc44abcd1234";
mutex = new Mutex(true, applicationMutex);
bool singleInstance = mutex.WaitOne(0, false);
if (!singleInstance)
{
string appProductName = Process.GetCurrentProcess().MainModule.FileVersionInfo.ProductName;
Process[] windowedProcesses =
Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();
foreach (Process process in windowedProcesses.Where(p => p.MainModule.FileVersionInfo.ProductName == appProductName))
{
if (process.Id != Process.GetCurrentProcess().Id)
{
AutomationElement wElement = AutomationElement.FromHandle(process.MainWindowHandle);
if (wElement.Current.IsOffscreen)
{
WindowPattern wPattern = wElement.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
#if DEBUG
WindowInteractionState state = wPattern.Current.WindowInteractionState;
Debug.Assert(!(state == WindowInteractionState.NotResponding), "The application is not responding");
Debug.Assert(!(state == WindowInteractionState.BlockedByModalWindow), "Main Window blocked by a Modal Window");
#endif
wPattern.SetWindowVisualState(WindowVisualState.Normal);
break;
}
}
}
Thread.Sleep(200);
MessageBox.Show("Application already running", "MyApplicationName",
MessageBoxButtons.OK, MessageBoxIcon.Information,
MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
}
if (SingleInstance) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyAppMainForm());
}
else {
Application.ExitThread();
}
}
private static void ThreadOnExit(object s, EventArgs e)
{
mutex.Dispose();
Application.ThreadExit -= ThreadOnExit;
Application.Exit();
}
}
In the Application MainForm constructor:
(this is used in case the Application's Main Window is hidden when a new instance is run, hence the procedure in Program.cs cannot find its handle)
public partial class MyAppMainForm : Form
{
public MyAppMainForm()
{
InitializeComponent();
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement,
TreeScope.Subtree, (uiElm, evt) =>
{
AutomationElement element = uiElm as AutomationElement;
string windowText = element.Current.Name;
if (element.Current.ProcessId != Process.GetCurrentProcess().Id && windowText == this.Text)
{
this.BeginInvoke(new MethodInvoker(() =>
{
this.WindowState = FormWindowState.Normal;
this.Show();
}));
}
});
}
}
Run Only One time :
static class Program
{
[STAThread]
static void Main()
{
bool createdNew = true;
using (Mutex mutex = new Mutex(true, "samplename", out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new Form1());
}
else
{
ProcessUtils.SetFocusToPreviousInstance("samplename");
}
}
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
}
private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
}
}
ProcessUtils :
public static class ProcessUtils
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
const int SW_RESTORE = 9;
[DllImport("user32.dll")]
static extern IntPtr GetLastActivePopup(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool IsWindowEnabled(IntPtr hWnd);
public static void SetFocusToPreviousInstance(string windowCaption)
{
IntPtr hWnd = FindWindow(null, windowCaption);
if (hWnd != null)
{
IntPtr hPopupWnd = GetLastActivePopup(hWnd);
if (hPopupWnd != null && IsWindowEnabled(hPopupWnd))
{
hWnd = hPopupWnd;
}
SetForegroundWindow(hWnd);
if (IsIconic(hWnd))
{
ShowWindow(hWnd, SW_RESTORE);
}
}
}
}
Normal Run :
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
If you are still looking for an answer. There is a good example here that
uses windows messages to restore the previous instance. It work even if the first instance is minimized contrary to FindWindow witch does not work in that case.

c# Getting WinForm to acknowledge Process.StartInfo.WindowStyle when launched programmatically

Microsoft Paint (mspaint.exe) will launch minimized with no problems. When launching a windows form I wrote in c# (myWinForm.exe) the Process.StartInfo.WindowStyle command gets ignored (always launched as normal window). Comments in code below detail this.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
LaunchProcess("mspaint.exe");
LaunchProcess("myWinForm.exe"); // this process will not acknowledge the StartInfo.WindowStyle command (always normal window)
}
private void LaunchProcess(string filename)
{
Process myProcess = new Process();
myProcess.StartInfo.FileName = filename;
myProcess.StartInfo.WindowStyle = ProcessWindowStyle.Minimized; // how do I get a WinForm I wrote to acknowledge this line?
myProcess.Start();
}
}
How do I configure myWinForm to acknowledge ProcessWindowStyle when called from the Process.Start() command?
This must be taken care of in the program you launch, it is not automatic. The information is available from the Process.GetCurrentProcess().StartInfo property. It WindowState property contains the requested window state. Modify the project's Program.cs file similar to this:
using System.Diagnostics;
...
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var main = new Form1();
switch (Process.GetCurrentProcess().StartInfo.WindowStyle) {
case ProcessWindowStyle.Minimized: main.WindowState = FormWindowState.Minimized; break;
case ProcessWindowStyle.Maximized: main.WindowState = FormWindowState.Maximized; break;
}
Application.Run(main);
}

Waiting for commands to be complete

I am working with a winform that runs a cmd in the background, redirecting input and output asynchronously.
Currently, the winform iterating through an array of commands, writing each to the cmd via the StreamWriter the StandardInput is redirected to. How can I force the loop to wait until the present command is complete in the cmd before writing the next line in?
EDIT: I took out all of my actual project code, and replaced it with this, a stripped down version of what I'm trying to do, only including components of my project relevant to my question.
public partial class Form1 : Form
{
public delegate void WriteToConsoleMethod(string text);
Process _process;
string[] _commands =
{
"echo hello world",
"echo my name is T.K.",
"echo Here is a list of commands"
};
public Form1()
{
InitializeComponent();
ProcessStartInfo processStartInfo = new ProcessStartInfo("cmd")
{
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
_process = Process.Start(processStartInfo);
_process.OutputDataReceived += new DataReceivedEventHandler(new DataReceivedEventHandler(DataReceived_EventHandler));
_process.ErrorDataReceived += new DataReceivedEventHandler(new DataReceivedEventHandler(DataReceived_EventHandler));
_process.BeginErrorReadLine();
_process.BeginOutputReadLine();
}
private void DataReceived_EventHandler(object sender, DataReceivedEventArgs e)
{
IAsyncResult result = this.BeginInvoke(new WriteToConsoleMethod(writeToConsole), new object[] { e.Data + Environment.NewLine });
this.EndInvoke(result);
}
private void writeToConsole(string output)
{
txtbxConsole.AppendText(output);
}
private void btnBegin_Click(object sender, EventArgs e)
{
foreach (string command in _commands)
{
_process.StandardInput.WriteLine(command);
// I want a way to pause here until the cmd has finished processing the command.
}
}
}
I don't think there is anything built-in that will support that. However you could send your own special command and then wait until you see this in the output for example ,
something like :
const string Separator= "---Command Completed--\xE3\xE2\xE1\xE0\xE3";
// Has to be something that won't occur in normal output.
volatile bool finished = false;
private void button1_Click(object sender, EventArgs e)
{
foreach (string command in _commands)
Run(command);
}
private void writeToConsole(string output)
{
if (output.IndexOf(Separator) >= 0)
finished = true;
else
richTextBox1.AppendText(output);
}
private void Run(string command)
{
finished = false;
_process.StandardInput.WriteLine(command);
_process.StandardInput.WriteLine("#echo " + Seperator);
while (!finished)
{
Application.DoEvents();
System.Threading.Thread.Sleep(100);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
finished = true;
}
Assuming you are using System.Diagnostics.Process, then you probably need something like
ProcessStartInfo pi = new ProcessStartInfo(cmd);
pi.Arguments = ...
pi.WorkingDirectory = ...
Process myProcess = Process.Start(pi);
myProcess.WaitForExit();
I ended up solving this problem by wrapping my interaction with the command prompt into a separate class and instead of maintaining one prompt for all of the actions, I started up another prompt for each call. Then I take advantage of WaitForExit() to synchronize my threads.
After each command, I write in an exit command to close the process. I scan the output for exit calls, and when I find one, I use the context of that line to save the workspace so that the prompt for the next command will be made from the same working directory. I also had to hook up a DataRecievedEventHandler to parse out the header and exit calls before forwarding the EventHandlers to the winform.
The thing that's nagging me about this solution is that if the output of whatever process I'm running prints out exit, output scanner will behave as though it found the original exit. I employed the same solution sgmoore had in his answer - I write in exit [UNIQUE STRING] to the prompt, and scan the output for that, but I'm sure that's far from best practice.

How to send keys instead of characters to a process?

System.Diagnostics.Process exposes a StreamWriter named StandardInput, which accepts only characters as far as I know.
But I need to send keystrokes as well, and some keystrokes don't map well to characters.
What should I do?
You are mixing input streams with control signals. A console process has a default input stream which you can control with the StandardInput, as you already know. But Ctrl-C and Ctrl-Break are not characters sent to the process through this stream, but instead they are instead control signals that the process receives using the registered signal handlers, see CTRL+C and CTRL+BREAK Signals:
By default, when a console window has
the keyboard focus, CTRL+C or
CTRL+BREAK is treated as a signal
(SIGINT or SIGBREAK) and not as
keyboard input.
To send fake signals to a process you can use GenerateConsoleCtrlEvent and send either CTRL_C_EVENT or CTRL_BREAK_EVENT. This API has no .Net equivalent, so you have to PInvoke it.
To use it from .NET you simply need to include the function definition:
const int CTRL_C_EVENT = 0;
const int CTRL_BREAK_EVENT = 1;
[DllImport("kernel32.dll")]
static extern bool GenerateConsoleCtrlEvent(
uint dwCtrlEvent,
uint dwProcessGroupId);
There's an input Simulator found here on Codeplex which may do just the job for you.
I am working on a sample code and will post back here shortly, bear in mind the Input Simulator is similar to what was found in the link supplied by Remus...
Edit: I have found that there is a limitation with this, you can definitely get away with the typical System.Windows.Forms.SendKeys.Send method, it does work effectively! , but, the process must have
No redirections of the streams
Cannot be hidden window (this is where it will fail, since the window's handle is nowhere to be seen, no way of bringing it to the foreground to make it active!)
A window showing the process for this to be effective!
In your case, it's a matter of finding the window, set it active via pinvoke 'SetForegroundWindow', and send the sequences ^{BREAK} which sends the Ctrl+Break signal to the process which does work very well (especially if the process is a command line program/batch file). Here's an article on CodeProject that does this exactly and mirrors the SendKeys...I have yet to paste some code into this to demonstrate ....
Edit#2: Actually I am quite surprised...as this code will show (proof of concept)...it is using:
InputSimulator (as mentioned previously)
A windows form that consists of a button, when the form is loaded it automatically runs the class. Upon clicking the button, it posts a ctrl-break to the hidden process
The output stream is indeed redirected and is a hidden window.
The weird thing, is the output is being captured but does not show the results in the debug window, in real-time that is, it is buffered (I guess) until the process terminates, the whole output is shown...
I cheated a bit on the FindWindow API call, because I knew the window's title was and was somehow, able to bring it to the foreground, and using the InputSimulator to send the keystrokes to it...or use the traditional plain old SendKeys function...the reason I had the Thread.Sleep is to ensure that the keystrokes are sent in order to be 'pushed into the keyboard queue of the "active foreground window", which despite that, is hidden'
I used the 'netstat -e 5' command to loop forever, refreshing the results every 5 seconds until it receives a 'Ctrl+C' to break the infinite loop.
public partial class Form1 : Form
{
private TestNetStat netStat = new TestNetStat();
public Form1()
{
InitializeComponent();
using (BackgroundWorker bgWorker = new BackgroundWorker())
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
bgWorker.RunWorkerAsync();
}
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("BGWORKER ENDED!");
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
netStat.Run();
}
void btnPost_Click(object sender, EventArgs e)
{
netStat.PostCtrlC();
System.Diagnostics.Debug.WriteLine(string.Format("[{0}] - {1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), this.netStat.OutputData.Replace(Environment.NewLine, "")));
}
}
public class TestNetStat
{
private StringBuilder sbRedirectedOutput = new StringBuilder();
//
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32")]
public static extern int SetForegroundWindow(IntPtr hwnd);
public string OutputData
{
get { return this.sbRedirectedOutput.ToString(); }
}
public void PostCtrlC()
{
IntPtr ptr = FindWindow(null, #"C:\Windows\System32\netstat.exe");
if (ptr != null)
{
SetForegroundWindow(ptr);
Thread.Sleep(1000);
WindowsInput.InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.CANCEL);
// SendKeys.Send("^{BREAK}");
Thread.Sleep(1000);
}
}
public void Run()
{
System.Diagnostics.ProcessStartInfo ps = new System.Diagnostics.ProcessStartInfo();
ps.FileName = "netstat";
ps.ErrorDialog = false;
ps.Arguments = "-e 5";
ps.CreateNoWindow = true;
ps.UseShellExecute = false;
ps.RedirectStandardOutput = true;
ps.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
using (System.Diagnostics.Process proc = new System.Diagnostics.Process())
{
proc.StartInfo = ps;
proc.EnableRaisingEvents = true;
proc.Exited += new EventHandler(proc_Exited);
proc.OutputDataReceived += new System.Diagnostics.DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
}
void proc_Exited(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("proc_Exited: Process Ended");
}
void proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.sbRedirectedOutput.Append(e.Data + Environment.NewLine);
System.Diagnostics.Debug.WriteLine("proc_OutputDataReceived: Data: " + e.Data);
}
}
}
Nitpicky aside, I know that the netStat is running off the 'BackgroundWorker' thread, and I directly invoked the 'PostCtrlC' method from the main GUI thread...this is pedantic as a proof-of-concept code, but it does show that it needs to implement 'ISynchronizeInvoke' to make it thread-safe, that aside...it does indeed work.
Have you seen this great tool - AutoIt. This is a scripting tool. To send a backspace you would use Send("{BACKSPACE}")
This is a great tool and it can help in automating many manual clicks/double-clicks/etc.
Is this relevant to your question ?
If you have a Windows Forms window that you can send the keys to, then SendKeys might be an appropriate solution.
For pressing backspace and Ctrl+C, that should be
SendKeys.Send("{BACKSPACE}^C");

Problem with SendMessage

I am developing an application using C# having similar functionality of copy,paste as in Windows.
I have added menu items and linked with respective applications.
Please look at the following image for getting more idea.
Items added to shell menu http://softwaregenius.net/myimages/menu.jpg
Like we select multiple items in windows explorer, you need to select multiple files and/or folders and then select OS Util->FastCopy. A form is opened as shown below
Form shown on FastCopy http://softwaregenius.net/myimages/fastcopy1.jpg
The application is working perfectly. The major problem here is that after selecting the files all these files are opening up within there respective softwares. That is if i selected word document then the filename is added to FastCopy form but the is also opening up within Word also.
When i investigate i found that this problem is due to SendMessage. I have to use PostMessage instead of SendMessage. But when i do so the application is not working.
Below is my Main function coding in C# 2005
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE92}");
[STAThread]
static void Main(string[] args)
{
string fileName = "";
if (args.Length > 0)
{
fileName = args[0];
}
if (mutex.WaitOne(TimeSpan.Zero, true))
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
frmFastCopy frm = new frmFastCopy();
frm.AddItemToList(fileName);
Application.Run(frm);
}
else
{
//The following message is sent just to show up the form
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
//Send the filename
SendFileName(fileName);
}
}
static void SendFileName(string s)
{
Win32.CopyDataStruct cds = new Win32.CopyDataStruct();
cds.cbData = (s.Length + 1) * 2;
cds.lpData = Win32.LocalAlloc(0x40, cds.cbData);
Marshal.Copy(s.ToCharArray(), 0, cds.lpData, s.Length);
cds.dwData = (IntPtr)1;
Win32.SendMessage((IntPtr)NativeMethods.HWND_BROADCAST, Win32.WM_COPYDATA, IntPtr.Zero, ref cds);
//NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, Win32.WM_COPYDATA, cds.lpData, IntPtr.Zero);
}
}
}
Below is the copy for WndProc and other code from within the Form
public partial class frmFastCopy : Form
{
delegate void AddItemToListDelegate(string itm);
public frmFastCopy()
{
InitializeComponent();
}
public void AddItemToList(string itm)
{
if (lvFilesAndFolders.InvokeRequired)
{
AddItemToListDelegate m = new AddItemToListDelegate(AddItemToList);
this.Invoke(m, new object[] { itm });
}
else
{
lvFilesAndFolders.Items.Add(itm);
}
}
protected override void WndProc(ref Message m)
{
if (m.Msg==NativeMethods.WM_SHOWME)
{
ShowMe();
}
if (m.Msg==Win32.WM_COPYDATA)
{
//string s = Marshal.PtrToStringUni(m.LParam);
MessageBox.Show("Got message");
Win32.CopyDataStruct st = (Win32.CopyDataStruct)Marshal.PtrToStructure(m.LParam, typeof(Win32.CopyDataStruct));
string strData = Marshal.PtrToStringUni(st.lpData);
AddItemToList(strData);
}
base.WndProc(ref m);
}
private void ShowMe()
{
this.Show();
if (WindowState == FormWindowState.Minimized)
{
WindowState = FormWindowState.Normal;
}
// get our current "TopMost" value (ours will always be false though)
bool top = TopMost;
// make our form jump to the top of everything
TopMost = true;
// set it back to whatever it was
TopMost = top;
}
Here is the NativeCode class
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
I know you guys are genius. Could someone tell me where should i make changes to that the selected files should be opened or rather how i should use postmessage.
Thanks for sharing your valuable time.
Regards
Irfan
Please look at my comment (I wonder why you don't use the Clipboard class here). But ignoring that: Why do you broadcast the message?
Can you locate your application (by name, window class, whatever) and only send the message to your own application?
To elaborate on the message handling:
You say regarding HWND_BROADCAST in the comments below:
Thats nothing but the global handle to
my application.
No, it's not. It is a special value that tells Windows "this message is for all applications". You are sending a WM_SHOWME to all applications. Which is why I asked why you would want to do that?
Please see this post on the old new things blog regarding message broadcasts.

Categories