I already know that I can open and close an exist text file by notepad in c#
Like this:
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
// open by notepad
Process.Start("notepad.exe", #"myfile.txt");
// automatically save this file by notepad
// kill notepad process
Process[] proc = Process.GetProcessesByName("notepad");
proc[0].Kill();
}
}
But I need to automatically save this file by notepad before I close this file. I tried to send keystrokes ctrl+s but in vain.
Is there .net code for this? Thanks.
You could do something like this i guess
SendKeys.SendWait Method (String)
Sends the given keys to the active application, and then waits for the
messages to be processed.
Remarks
Use SendWait to send keystrokes or combinations of keystrokes to the
active application and wait for the keystroke messages to be
processed. You can use this method to send keystrokes to an
application and wait for any processes that are started by the
keystrokes to be completed. This can be important if the other
application must finish before your application can continue.
Imports
[DllImport("User32.dll")]
public static extern Int32 SetForegroundWindow(int hWnd);
Code
var process = Process.Start("notepad.exe", #"myfile.txt"));
process.WaitForInputIdle();
var handle = process.MainWindowHandle;
SetForegroundWindow(handle);
// if the window is still in the foreground
SendKeys.SendWait("^(s)"); // Ctrl+S
If your need is to modify file, better way to do that is open, modify and save text file contents with operations which are currently present in System.IO.File-class. When file is opened in code, you can i.e change it's encodings with classes found in System.Text-namespace (System.Text.Encoding).
You can start notepad.exe process, but after that there is not very much to do.
Related
A Program I have to work with opens a CMD-window with a message after processing some data.
The message is something like "finished processing" and I don't have access to the code behind the other Programm.
The goals I want to achieve is:
Get the message from the CMD-window and write it to a text box (or similar) in my own programm.
Close the CMD-window
I managed to attach the process to my programm using Process[] localByName = Process.GetProcessesByName("cmd"); but I can't manage to get the output.
Thank you for your help.
Edit:
To elaborate a bit further about the circumstances of the problem:
I start the other programm with a command line command. I pass some parameters and after a bit, the other programm opens a cmd window with the message.
If i open the other programm normally, then the message is displayed within the application.
None of the parameters give me the option to manipulate the output and i am not able to pipeline the output to another file.
As for why i have to do this: I was given the task to add quality of life improvements to the other programm and using the command line is the easiest way to access the functionality of the other programm. The other solution available would be to reverse engeneer the processing of the data.
The Process class has a StandardOutput property of type StreamReader that you can use to read the output of the process. Using your example:
Process[] localByName = Process.GetProcessesByName("cmd");
if (localByName.Length > 0)
{
var cmdProcess = localByName[0];
var reader = cmdProcess.StandardOutput;
var output = reader.ReadToEnd();
Console.WriteLine($"The output from the cmd process is: {output}");
}
Once you are done with your message processing, you can use the CloseMainWindow() method of the Process class to close the cmd window:
cmdProcess.CloseMainWindow();
This answer is assuming that you want to capture the output of cmd after it has been attached to your process.
My application is System Tray Application, using C#, .NET 4.0.
I'm trying to display many PDF files at a time and each PDF should split screen with other window, that I determine by ProcessName, that's all.
The difficulty is that I need to wait until user closes that window (.WaitForExit() method), because later I'm deleting PDF file. And here problem comes.
In first thread everything goes fine but the problem is when i try to show second PDF file window processList[0] THOUGH MoveWindow function returns true, and handle to that window is also correct only pdf window is resizing, the other window fails.
In main method the only thread that I'm creating (I call this piece of code couple of times, everytime user want to see pdf file):
Thread pdfThread = new Thread(() => ShowPdfFile(fullPath));
pdfThread.Start();
Then (simplified code)
public static void ShowPdfFile(string fileName)
{
try
{
Process pdfProcess = Process.Start(fileName);
Thread.Sleep(500);
string windowTitle = GetActiveWindowTitle();
IntPtr pdfHandle = (IntPtr)FindWindow(null, windowTitle);
MoveWindow(pdfHandle, 0, 0, 0, 0, true);
Process[] processList = Process.GetProcessesByName("someProcess");
MoveWindow(processList[0].MainWindowHandle, 0, 0, 0, 0, true);
pdfProcess.WaitForExit();
MoveWindow(processList[0].MainWindowHandle, 0, 0, max, max, true);
}
catch (Exception ex)
{
LogToFile(ex);
}
finally
{
try
{
File.Delete(fileName);
}
catch
{
LogToFile("Cannot delete file");
}
}
UPDATE: Well, I was debugging it whole day but just now i note that on second thread it doesn't wait on line pdfProcess.WaitForExit();
What should I change to force thread to wait for that exact pdfProcess exit?
There is a basic problem to your usage:
You start the external PDF application for each file. However this doesn't ensure that you have more than one process.
For example Acrobat reader only starts a single process. Additional files will just be "added" as new windows to the first process (You can check this by trying to manually open Acrobat reader twice --> won't work).
So in short: If you cannot control which PDF reader is used (and can ensure that you have a single process for EACH file) your approach will not work.
Note: Theoretically it would be possible to wait until the user closes the "reader window" that contains the specific file. However I strongly advise against this:
Looking up a window in a different process is very error prone (unless the process is explicitly designed in such a way...)
The solution would again depend on the reader application (you cannot be sure that Acrobat and Nitro use similar architecture to just name two readers)
Principally, if this feature is very important, you should consider to buy a PDF viewer component that allows to show the PDF's as windows of your own process.
Edit
The reason that the second thread isn't waiting is that the functionality to "add" a file to the first process uses a temporary process:
Temporary process is started for new file
Temporary process checks if the application is already running
Temporary process notifies the first process to open the specific file
Temporary process shuts down.
So waiting for that process will return almost immediately since the process in question already has stopped (or will stop in just a couple of milliseconds).
Daniel is right, however I found a solution! Here, maybe someone will use it.
Small comment: We must use winapi solutions instead of .WaitForExit().
The most important part is while loop, that waits for close pdf window.
Remember that FindWindow() and IsWindow() methods are from user32.dll (winapi).
This code run process based on it's paths, then gets it's handle and wait for it's closure.
Process pdfProcess = new Process();
pdfProcess.StartInfo.FileName = filePath;
if (pdfProcess.Start())
{
Thread.Sleep(500);
Process[] processlist = Process.GetProcesses();
string windowTitle = string.Empty;
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle) && process.MainWindowTitle.Contains(fileName))
{
windowTitle = process.MainWindowTitle;
}
}
IntPtr pdfHandle = FindWindow(null, windowTitle);
while (IsWindow(pdfHandle) && userExitedApp == false)
Thread.Sleep(100);
}
Multiple threads could utilize one method one after the other. Perhaps this video will help
https://www.youtube.com/watch?v=XhGXh9Z5GTw&feature=em-upload_owner
So long story short, I am trying to automate some things when my computer boots up. I thought I'd write an C# console application to do this and then add it to a schedule task in windows to be executed on bootup. My problem is with one program, it requires a password and has no options to open via the command line. Thus it must be entered manually. My thought was to retrieve my password from a KeePass database and use SendKeys to enter the password and login to the program. The problem I'm having is the time it takes to load; I have no way of detecting when the GUI interface has loaded and is ready for my SendKeys. Is there any way to detect this? I'm assuming all I have to work with is the "Process" class since thats what I used to run the program. Also note that when I run the executable using Process.Start(), the program creates another process for logging in, but it has no associated window that I can see using Windows API calls.
Okay that was long, I can re-cap...
Problem:
From C# detecting when a third party program has loaded (i.e. the splash screen is gone and GUI is ready for user interaction - meaning I can't just rely on if the Process is running or not).
Also, no command line options for the third party program, or I would just run it with the password as an argument.
Goal:
To use SendKeys in order to automate entering a password, but my program must wait for the third party application to finish loading.
Notes:
Using C# .NET 3.5 Console Application
NOT detecting load for my own form but a third party otherwise this would be easy (i.e. form_loaded event...)
Thank you for looking at my question, if you want any more details or anything let me know.
UPDATE:
Problem solved!
The two answers I received combined to give me the solution I wanted. So if anyone comes across this later, here is what I did to make it work.
So this program automates a login for some client software that you must login to. My problem was that the software offered not option or documentation for command line prameters which many other programs offer so you can login with a keyfile or something. This program also disabled copy and paste so the password HAS to be typed in manually, which is a big pain if you use passwords like I do, long complicated ones with no patterns. So I wrote this program for my benefit as well others at work; I just schedule it to run at logon to my windows machine and it opens the client software and performs login automatically.
//
// IMPORTANT Windows API imports....
//
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint procId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
// When I get to this point in my code, I already had the password and window title...
string password = "password";
string title = "window title";
// This gets a handle to the window I want where "title" is the text in the title
// bar of the window as a string.
// This is a Windows API function that must be imported by DLLImport
// I had to get the handle this way because all I knew about the third party
// window was the title, not the process name or anything...
IntPtr hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, title);
// Now that I have a handle to the login window I used another windows API
// call to get the process ID.
// This windows API call gives me the Process ID as an out parameter and returns
// the thread ID of the window. I don't use the thread it, but maybe you can...
uint loginWindowProcId;
uint loginWindowThreadId = GetWindowThreadProcessId(hWnd, out loginWindowProcId);
// now I can just use .NET to find the Process for me...
Process loginWindowProcess = null;
if (0 != loginWindowProcId)
{
// get the process object
loginWindowProcess = Process.GetProcessById((int)loginWindowProcId);
// This right here is why I wanted the Process structure. It takes a
// little while for the client software to load and be ready. So here
// you wait for the window to be idle so you know it has loaded and can
// receive user input, or in this case keys from "SendKeys".
loginWindowProcess.WaitForInputIdle();
// I use yet another windows API call to make sure that the login window
// is currently in the foreground. This ensures that the keys are sent
// to the right window. Use the handle that we started with.
SetForegroundWindow(hWnd);
// Now send the password to the window. In my case, the user name is
// always there from my windows credentials. So normally I would type in the
// password and press ENTER to login. But here I'll use SendKeys to mimic my
// behavior.
SendKeys.SendWait(password); // send password string
SendKeys.SendWait("{ENTER}"); // send ENTER key
// Now the client should be logging in for you! : )
// IMPORTANT NOTE
// If you are using a console application like I am, you must add a reference to
// System.Windows.Forms to your project and put "using System.Windows.Forms;" in
// your code. This is required to use the "SendKeys" function.
//
// Also this code is just for my testing (quick and dirty), you will want to write
// more checks and catch errors and such. You should probably give the
// WaitForInputIdle a timeout etc...
}
You can check with Process.WaitForInputIdle after you start a process, and wait until is fully started, here is the simple example :
http://msdn.microsoft.com/en-us/library/xb73d10t%28v=vs.71%29.aspx
Try looking at that: http://www.acoolsip.com/a-cool-blog/science-and-technology/151-c-sending-commands-to-independent-windows.html
you can check if the window is shown (using the link) and then sending messages (also on the link)
I have a C# winform application that during its work opens another Winform process. The other process has UI of its own.
When I close the parent application, I want the other application to be closed automatically.
How can I achieve that?
Thanks
If you are using Process.Process there is the CloseMainWindow method. If you keep a reference to the object you can use it later.
Here's the relevant page in the MSDN
and the relevant code:
// Close process by sending a close message to its main window.
myProcess.CloseMainWindow();
// Free resources associated with process.
myProcess.Close();
There are several different options. I would suggest that you have your application keep track of the processes that it starts:
private Stack<Process> _startedProcesses = new Stack<Process>();
private void StartChildProcess(string fileName)
{
Process newProcess = new Process();
newProcess.StartInfo = new ProcessStartInfo(fileName); ;
newProcess.Start();
_startedProcesses.Push(newProcess);
}
When the application closes, you can call a method that will close all started child processes that are still running. You can use this either with the Kill method or by calling the CloseMainWindow and Close methods. CloseMainWindow/Close will perform a more graceful close (if you start Notepad and there are unsaved changes, Kill will lose them, CloseMainWindow/Close will make notepad ask if you want to save):
private void CloseStartedProcesses()
{
while (_startedProcesses.Count > 0)
{
Process process = _startedProcesses.Pop();
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
The most graceful way to do this is probably to send a window message to the main from of the other process. You can get the handle of this main form simply using the Process.MainWindow.Handle property (I assume you are using the Process class, and then just use the PostMessage Win API call to send a message with a custom ID to the main window of this "child" process. Then, the message loop of the other process can easily detect this message (by overriding the WndProc method) and perform a proper shutdown accordingly. An alternative would be to send the standard WM_CLOSE method, which would mean you would just have to unload the application from the handler of the Form.Closed event, but may perhaps allow you less control (over whether to cancel the shutdown in certain situations).
Could you please explain me, what is the different between API functions AllocConsole and AttachConsole(-1) ? I mean if AttachConsole gets ATTACH_PARENT_PROCESS(DWORD)-1.
Well, the fundamental difference is:
AllocConsole() will create a new console (and attach to it)
AttachConsole( ATTACH_PARENT_PROCESS /* -1 */) will not create a new console, it will attach to the existing console of the parent process.
In the first case you get a whole new console window, in the second case, you use an existing console window.
Of course, if you're already attached to a console (ie., you're a console mode program launched from cmd.exe) there's not much difference - you'll get an error with either API.
Also note that just because you detach from a console doesn't mean the detached console will be useful - for example, if you're a console process launched from a cmd window, that window essentially blocks until your process ends.
Some code to play with:
int main( int argc, char* argv[])
{
int ch;
BOOL bResult;
printf( "default console\n");
ch = getchar();
bResult = FreeConsole();
bResult = AllocConsole();
printf( "AllocConsole()\n");
ch = getchar();
bResult = FreeConsole();
bResult = AttachConsole( ATTACH_PARENT_PROCESS);
printf( "AttachConsole( ATTACH_PARENT_PROCESS)\n");
ch = getchar();
return 0;
}
I don't think there's a function called CreateConsole, but there's AllocConsole.
Assuming that's what you meant, I think the difference is that AttachConsole(ATTACH_PARENT_PROCESS) can return ERROR_INVALID_HANDLE if the parent process doesn't have a console.
Try running this code from both a command prompt and Start -> Run:
#include <windows.h>
#pragma comment ( lib, "user32.lib" )
int main()
{
BOOL b;
char msg[1024];
b = FreeConsole();
sprintf(msg, "%d", b);
MessageBox(NULL, msg, "FreeConsole", 0);
b = AttachConsole(ATTACH_PARENT_PROCESS);
sprintf(msg, "%d", b);
MessageBox(NULL, msg, "AttachConsole", 0);
return 0;
}
When run from a command prompt, two message boxes containing a 1 are displayed, meaning both calls succeeded. When run from Start -> Run, the first box contains 1 and the second contains 0, meaning that only the first call succeeded. The second one fails because explorer.exe (which is the parent of a process launched from Start -> Run) doesn't have a console.
On Windows 7, when you execute cmd.exe, CreateProcess will have the CREATE_NEW_CONSOLE flag, which will allocate a new console rather than being attached to the parent console (which is default behaviour when the PE header contains Subsystem = 3 i.e. IMAGE_SUBSYSTEM_WINDOWS_CUI indicating that it is a console application). This means that AllocConsole will be called before the .exe image entry point in the current process address space.
AllocConsole creates a new conhost.exe instance, which paints the GUI window, handles the mouse and keyboard events and maintains and writes to the the input buffer and maintains and reads from the screen buffer, and when there is a keyboard event, it updates the input buffer. AllocConsole also sets the stdin handle in the ParameterBlock in the cmd.exe process PEB to the console input buffer and the stdout and stderr to the the console pseudohandles, and sets the ConsoleHandle in the ParameterBlockto the PID of theconhost.exe` instance that it is attached to.
cmd.exe is a console application written in C, like diskpart.exe or setx.exe, which displays the command prompt to the screen buffer (stdout of cmd.exe) and reads a command from the stdin of cmd.exe and interprets the key press events to display to stdout as well as determine what command to call and what to display to stdout as a result (which might be a file in the handle in the PEB and not sent to conhost). A command itself has an stdin of either nothing, the read side of an anonymous pipe, a file or the con file.
cmd.exe calls functions like WriteFile which will call WriteConsole (which is an alias of WriteConsoleA) if it is writing to a standard handle (otherwise it calls NtWriteFile). This will initiate an ALPC call to conhost.exe PID in ParameterBlock->ConsoleHandle, passing the console pseudohandle and the buffer to write the result to (or the buffer to read from).
When you execute cmd /c from inside cmd.exe, it creates a child cmd.exe, but does not supply CREATE_NEW_CONSOLE, which results in the child cmd.exe attaching to the same visible console i.e. conhost.exe instance and AttachConsole is called before the entry point. This is not the case with a child created with admin /c (admin version of cmd.exe) if done in a non-elevated cmd.exe, which needs to have a new conhost.exe instance because it has different privileges. Similarly, when starting a program, start supplies CREATE_NEW_CONSOLE, which opens a new conhost.exe for its child process, but call and specifying the program filename + extension as a raw command do not open another console window, but attach to the parent. cmd /c diskpart creates a cmd.exe child that attaches to the console that the parent is attached to and then that child creates its own child diskpart.exe, which attaches to the console that the parent is attached to.
When you execute a GUI application, it is not allocated or attached to a console. If you use AllocConsole within the application, it will create a new conhost.exe console window (which can be hidden by the application, i.e. blender has window > toggle system console, and it does this by getting a handle to the conhost.exe console window and then using using ShowWindow with SW_HIDE on it), but will not create a child process, because it is the console window of the current process, so now there will be 2 windows. You can also instead attach to a console (using AttachConsole) belonging to a process of pid. AttachConsole(-1) attaches to the parent pid. This will use the conhost that that process is attached to.
You cannot AttachConsole if you are already attached to a console without using FreeConsole and you can't AttachConsole to connect to the parent's console if you create the process with DETACHED_PROCESS. CREATE_NO_WINDOW has the same effect as DETACHED_PROCESS in that it does not attach to or allocate a console for the console application but does allow it to attach to the parent console.
It has been a while since I used the winapi, but I looked up the MSDN documentation and I was not able to find the CreateConsole API function. So my guess is that CreateConsole is legacy stuff and has been replaced by AttachConsole. So there is probably no difference, but CreateConsole has probably been deprecated.