Starting process and storing handle to window - c#

Would someone mind helping me out with a problem I've been stuck on for a bit? I'm using C# and trying to start a couple processes, and later move those windows to separate monitors.
So far this was the main idea:
Process p1 = Process.Start(#"1.pptx");
Process p2 = Process.Start(#"2.pptx");
SetWindowPos(p1.MainWindowHandle, -1,
0,
0,
100,
100,
SWP_SHOWWINDOW);
SetWindowPos(p2.MainWindowHandle, -1,
200,
200,
100,
100,
SWP_SHOWWINDOW);
But after trying a bunch of different things, I haven't been able to get it to work. Could anyone give me some pointers?
As a side note which is confusing me, if I print those to process IDs (p1, p2), and then run this code:
Process[] processlist = Process.GetProcesses();
foreach (Process process in processlist)
{
if (!String.IsNullOrEmpty(process.MainWindowTitle))
{
Console.WriteLine("Process: {0} ID: {1} Window title: {2}", process.ProcessName, process.Id, process.MainWindowTitle);
}
}
those process IDs don't exist. I know there must be something simple I'm missing...?
UPDATE: The reason for the problem above is that for some reason the MainWindowTitle didn't have a value, so it wasn't printing the pid.

When you use Process.Start to open a document this is handled by the shell. The shell looks in the file association registry and takes whatever steps are needed to open the document.
This may involve creating a new process but equally may not. Office applications will typically reuse already open processes to open new documents. That's what is happening here.
And when this does happen, when no new process is started, the shell returns 0 for the new process handle. That's reflected back to the .net Process object. It explains why you have no main window handle.
So fundamentally your basic approach is flawed. Using Process.Start will not yield window handles for these documents. You'll have to find another way to locate these windows. For instance EnumWindows or a CBT hook. Or perhaps COM automation is the right solution.
As an aside it seems that you did not check for errors when you called SetWindowPos. That would have helped you work this out more quickly. Always check return values when calling Win32 functions.

For those who are still looking for an answer, this is what I did to get it working.
First, use EnumWindows to get a handle to each open window before starting any new process. Then start your process, then check all windows again making sure they're visible and have window text. If you have only 1 new process, chances are that's your new window. If not, I've tried it 3 times before failing. So far, the code has worked great.
Here is the code for helper functions/Win32 API calls.
public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumedWindow lpEnumFunc, ArrayList lParam);
public static ArrayList GetWindows()
{
ArrayList windowHandles = new ArrayList();
EnumedWindow callBackPtr = GetWindowHandle;
EnumWindows(callBackPtr, windowHandles);
return windowHandles;
}
private static bool GetWindowHandle(IntPtr windowHandle, ArrayList windowHandles)
{
windowHandles.Add(windowHandle);
return true;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
const int SWP_SHOWWINDOW = 0x0040;
[DllImport("user32.dll", EntryPoint = "SetWindowPos", SetLastError = true)]
public static extern Boolean SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
My main logic then went something like this (adjust as necessary):
List<IntPtr> alreadyOpenWindows = new List<IntPtr>();
foreach (IntPtr ip in GetWindows())
{
alreadyOpenWindows.Add(ip);
}
Process.Start("your command here");
System.Threading.Thread.Sleep(1000);
foreach (IntPtr ip in GetWindows())
{
// To consider it a new window, it must be visible, it must not have been open previously, and it must have window text length > 0
if (IsWindowVisible(ip) && alreadyOpenWindows.Contains(ip) == false)
{
StringBuilder windowText = new StringBuilder();
windowText.Length = 256;
GetWindowText(ip, windowText, windowText.Length);
if (windowText.Length > 0)
{
numNewWindows++;
handle = ip;
// break if your confident there will only be one new window opened
}
}
}
// Check numNewWindows if you'd like
if (handle != IntPtr.Zero)
{
SetWindowPos(handle, -1,
this.GetScreen().WorkingArea.X,
this.GetScreen().WorkingArea.Y,
this.GetScreen().WorkingArea.Width,
this.GetScreen().WorkingArea.Height,
SWP_SHOWWINDOW);
}

Related

Excel Interop disable Msgbox without disabling all VBA

I'm trying to automate an excel file which has VBA in it. This VBA is protected so I can't access it.
Here is what I need the automated script to do.
Open the workbook
Click / dismiss any Msgbox's (Stuck part)
Enter a cell and let the workbook's vba do it's thing
So I have found I can open the book without popups by using:
var app = new Excel.Application();
app.DisplayAlerts = false;
app.Visible = false;
app.EnableEvents = false;
app.Workbooks.Open(#"path...");
But then the VBA within the book is also disabled so I can't do step 3 above.
How can I just disable all Msg box's then re-enable them at the end?
The technique that can be used is:
Run the Excel code in a function on another thread. This is because there are many things that Excel can put up to block the execution, such as Msgboxes and other dialogs from Excel, and if you do not control the Excel code-behind, then you should wish to abort that Task on a timeout basis.
In your main thread, just check for the completion of the task, and add a timeout too.
I made the WindowHandler as a separate class with the winAPI functions from user32.dll etc from examples here: Close window via SendMessage AND here: FindWindow Function Codes
class WindowHandler {
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const UInt32 WM_CLOSE = 0x0010;
public static void CloseWindow(IntPtr hwnd) {
SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
public static IntPtr FindWindow(string windowName) {
var hWnd = FindWindowByCaption(IntPtr.Zero, windowName);
return hWnd;
}
public static void CloseMsgBox() {
CloseWindow(FindWindow("Microsoft Excel"));
}
}
So now the code execution looks crudely like:
// The OpenExcel Action would actually be all the Excel code encapsulated into one function to run in a separate thread
Task t = Task.Run(OpenExcel);
// Be aware that Excel can have many different popups or VBA issues which may cause execution to stall.
TimeSpan timeLimit = new TimeSpan(0, 0, 10); // 10 secs or acceptable time limit for Excel
DateTime startTime = DateTime.Now;
while (!t.IsCompleted) {
if (DateTime.Now - startTime > timeLimit)
break; //or do other exception routine, if Excel execution is taking an unacceptable amount of time!
WindowHandler.CloseMsgBox(); //close any Msgboxes
Thread.Sleep(200);
}

How do I queue processes on C#? [duplicate]

How do I suspend a whole process (like the Process Explorer does when I click Suspend) in C#.
I'm starting the Process with Process.Start, and on a certain event, I want to suspend the process to be able to do some investigation on a "snapshot" of it.
Here's my suggestion:
[Flags]
public enum ThreadAccess : int
{
TERMINATE = (0x0001),
SUSPEND_RESUME = (0x0002),
GET_CONTEXT = (0x0008),
SET_CONTEXT = (0x0010),
SET_INFORMATION = (0x0020),
QUERY_INFORMATION = (0x0040),
SET_THREAD_TOKEN = (0x0080),
IMPERSONATE = (0x0100),
DIRECT_IMPERSONATION = (0x0200)
}
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
[DllImport("kernel32", CharSet = CharSet.Auto,SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
private static void SuspendProcess(int pid)
{
var process = Process.GetProcessById(pid); // throws exception if process does not exist
foreach (ProcessThread pT in process.Threads)
{
IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
if (pOpenThread == IntPtr.Zero)
{
continue;
}
SuspendThread(pOpenThread);
CloseHandle(pOpenThread);
}
}
public static void ResumeProcess(int pid)
{
var process = Process.GetProcessById(pid);
if (process.ProcessName == string.Empty)
return;
foreach (ProcessThread pT in process.Threads)
{
IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);
if (pOpenThread == IntPtr.Zero)
{
continue;
}
var suspendCount = 0;
do
{
suspendCount = ResumeThread(pOpenThread);
} while (suspendCount > 0);
CloseHandle(pOpenThread);
}
}
Thanks to Magnus
After including the Flags, I modified the code a bit to be an extension method in my project. I could now use
var process = Process.GetProcessById(param.PId);
process.Suspend();
Here is the code for those who might be interested.
public static class ProcessExtension
{
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
public static void Suspend(this Process process)
{
foreach (ProcessThread thread in process.Threads)
{
var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
break;
}
SuspendThread(pOpenThread);
}
}
public static void Resume(this Process process)
{
foreach (ProcessThread thread in process.Threads)
{
var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
break;
}
ResumeThread(pOpenThread);
}
}
}
I have a utility done which I use to generally suspend/kill/list a process. Full source is on Git
So really, what the other answer's are showing is suspending thread's in the process, there is no way to really suspend the process (i.e. in one call)....
A bit of a different solution would be to actually debug the target process which you are starting, see Mike Stall's blog for some advice how to implement this from a managed context.
If you implement a debugger, you will be able to scan memory or what other snap-shotting you would like.
However, I would like to point out, that technically, there is now way to really do this. Even if you do debugbreak a target debuggee process, another process on your system may inject a thread and will be given some ability to execute code regardless of the state of the target process (even let's say if it's hit a breakpoint due to an access violation), if you have all thread's suspended up to a super high suspend count, are currently at a break point in the main process thread and any other such presumed-frozen status, it is still possible for the system to inject another thread into that process and execute some instructions. You could also go through the trouble of modifying or replacing all of the entry point's the kernel usually calls and so on, but you've now entered the viscous arm's race of MALWARE ;)...
In any case, using the managed interfaces for debugging seems' a fair amount easier than p/invoke'ng a lot of native API call's which will do a poor job of emulating what you probably really want to be doing... using debug api's ;)
See this CodeProject article for the win32 basics : http://www.codeproject.com/KB/threads/pausep.aspx. This sample code makes use of the ToolHelp32 library from the SDK, so I would recommend turning this sample code into an unmanaged C++/CLI library with a simple interface like "SuspendProcess(uint processID).
Process.Start will return you a Process object, from which you can get the process id, and then pass this to your new library based on the above.
Dave
[DllImport("ntdll.dll", PreserveSig = false)]
public static extern void NtSuspendProcess(IntPtr processHandle);
static IntPtr handle;
string p = "";
foreach (Process item in Process.GetProcesses())
{
if (item.ProcessName == "GammaVPN")
{
p = item.ProcessName;
handle = item.Handle;
NtSuspendProcess(handle);
}
}
Console.WriteLine(p);
Console.WriteLine("done");

compare a string against a process name

i want to do this... IF notepad is in the foreground it opens the calculator... if another program is open does nothing... the notepad is oepn manualy... "Start, Notepad"... i have this code to "see" if notepad is open... dont know how to continue D: i know i have to use
if (switch == 0)
{
if (SOMETHING == "Notepad")
{
var switch = 1 //so it doesnt enters in a loop
OPEN CALCULATOR //irrelevant, i may use another part of otrher code that is already working
}
}
the "switch" variable is going to be 0 from the beginning of the code, so that is going to work (hope)
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
Process GetActiveProcess()
{
IntPtr hwnd = GetForegroundWindow();
uint pid;
GetWindowThreadProcessId(hwnd, out pid);
Process p = Process.GetProcessById((int)pid);
return p;
}
the problem is that i dont know what to put on "SOMETHING" to use the rest of the code, and Where or How to use the If...
You can do:
Process[] notePadProcesses = Process.GetProcessesByName("notepad.exe");
IntPtr activeWindowHandle = GetForegroundWindow();
if (notePadProcesses != null && notePadProcesses.Length > 0
&& notePadProcesses.Any(p=>p.MainWindowHandle == activeWindowHandle))
{
// notepad is open in the foreground.
switch = 1;
// OPEN Calculator or whatever you need to.
}
else
{
// notepad is either not open, or not open in the foreground.
}
basically we use the C# friendly Process class to find all the open notepad processes.
Then find if it is an active process and go from there.
please be careful using activewindow logic, because many a time, they result in race conditions where by the time you determine a process is active and try to do something, it may no longer be an active process. tread carefully.

Find handle of a ActiveX user control inside IE

How can I programatically find the handle of a user control in a webpage running on IE?
I'm able to find it using Spy++ but since the handle keeps changing I'm stuck.
I've been trying using FindWindow() but no luck :( I also wonder if I am doing something wrong or it simply only work for Windows...
Thanks in advance,
Zubrowka
I had a similar problem finding a PDF ActiveX Control inside a IE control in WPF.
To overcome the problem I used the EnumChildWindows API to find the correct child window and thus get its handle.
I'll include as much code as I can.
private static IntPtr FindPdfControlWindow(IntPtr parentHandle)
{
IntPtr result = IntPtr.Zero;
IntPtr matchPointer = IntPtr.Zero;
try
{
//allocate unmanaged memory for the result of the callback delegate
matchPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
Marshal.WriteIntPtr(matchPointer, IntPtr.Zero)
//instantiate the delegate and pass to the API
NativeMethods.EnumWindowProc windowChecker = CheckForPdfControlWindow;
if (!NativeMethods.EnumChildWindows(parentHandle,
windowChecker,
matchPointer))
}
finally
{
if (matchPointer != IntPtr.Zero) Marshal.FreeHGlobal(matchPointer);
}
return result;
}
private static bool CheckForPdfControlWindow(IntPtr handle,
IntPtr matchPointer)
{
int captionLength = NativeMehtods.GetWindowTextLength(handle);
if (captionLength > 0)
{
StringBuilder buffer = new StringBuilder(captionLength + 1);
NativeMethods.GetWindowText(handle, buffer, buffer.Capacity);
if (buffer.ToString().Contains("Adobe"))
{
Marhsal.WriteIntPtr(matchPointer, handle)
return false;
}
}
return true;
}
private static class NativeMethods
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EnumChildWindows(IntPtr window,
EnumWindowProc callback,
IntPtr i);
internal delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSer.Auto)]
internal static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowText(IntPtr hWnd,
StringBuilder lpString,
int nMaxCount);
}
transcribed in a rush so I hope it is both helpful and accurate.
If the ActiveX control is windowed, then you can query its IOleWindow interface to get the window handle.
Before you query interfaces from the ActiveX, you need to review the page's HTML to find a way to identify the activex in the document, such as element id.

Get the handle and write to the console that launched our process

How could I write to the standard output of some already open console?
I find the console I need with this piece of code:
IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
The problem is how to get the standard output handle pointer (stdHandle) of this process.
I would then want something like:
SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
Encoding encoding = Encoding.ASCII;
StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
standardOutput.AutoFlush = true;
Console.SetOut(standardOutput);
Code in C++ using windows API is OK - I can use pInvoke.
Effectively what I would like is to write text to an already open console window not spawned by my process (and it is the one that was in foreground when launching my process through command line - but my process is a WinApp so the console does not attach the std).
Can the standard output be redirected after the process has been created?
PS: I read about some COM file that can be used to do this, so this means that there is a programmatic way ...
Thanks!
I finally figured out how to attach transparently to a console if it is the foreground window while launching the windows app.
Don't ask me why STD_ERROR_HANDLE must be passed instead of STD_OUTPUT_HANDLE, but it simply works, probably because the standard error can be shared.
N.B.: the console can accept user input while displaying you app messages inside, but it is a bit confusing to use it while the stderr is outputting from you app.
With this snippet of code if you launch you app from a console window with at least one parameter it will attach Console.Write to it, and if you launch the app with the parameter /debug then it will attach even the Debug.Write to the console.
Call Cleanup() before exiting you app to free the console and send an Enter keypress to release the last line so the console is usable as before starting the app.
PS. You cannto use output redirection with this method ie.: yourapp.exe > file.txt because
you will get an empty file. And dont even try myapp.exe > file.txt 2>&1 because you will crash the app (redirecting error to output means we are trying to attach to a nonshared buffer).
Here is the code:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "GetStdHandle",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "AllocConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
private const int STD_OUTPUT_HANDLE = -11;
private const int STD_ERROR_HANDLE = -12;
private static bool _consoleAttached = false;
private static IntPtr consoleWindow;
[STAThread]
static void Main()
{
args = new List<string>(Environment.GetCommandLineArgs());
int prId;
consoleWindow = GetForegroundWindow();
GetWindowThreadProcessId(consoleWindow, out prId);
Process process = Process.GetProcessById(prId);
if (args.Count > 1 && process.ProcessName == "cmd")
{
if (AttachConsole((uint)prId)) {
_consoleAttached = true;
IntPtr stdHandle = GetStdHandle(STD_ERROR_HANDLE); // must be error dunno why
SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
Encoding encoding = Encoding.ASCII;
StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
standardOutput.AutoFlush = true;
Console.SetOut(standardOutput);
if (args.Contains("/debug")) Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
Console.WriteLine(Application.ProductName + " was launched from a console window and will redirect output to it.");
}
}
// ... do whatever, use console.writeline or debug.writeline
// if you started the app with /debug from a console
Cleanup();
}
private static void Cleanup() {
try
{
if (_consoleAttached)
{
SetForegroundWindow(consoleWindow);
SendKeys.SendWait("{ENTER}");
FreeConsole();
}
}
}
If the intention is to write to the parent console, if any, you can use the AttachConsole function with the ATTACH_PARENT_PROCESS argument. (see msdn attachconsole)
ATTACH_PARENT_PROCESS (DWORD)-1 : Use the console of the parent of the current process
And if you do need to check the parent process, you might use the CreateToolhelp32Snapshot and get the parent process thru the th32ParentProcessID member of the PROCESSENTRY32 structure.
If you just want to write to the console that's used by some other app, then you can use the following - you'll need to use P/Invoke to do the first step:
AttachConsole(pid) to attach to that console - if your process is already associated with a console, you'll have to FreeConsole first, since a process can be associated with only one console at a time.
Now that you're attached, get the console output handle using CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, ... ) - might be able to do this part in managed code.
Now that you've got the HANDLE, wrap it up in managed code - this part you already know.
Having said that, even though you can do this, it's not necessarily a good idea to do so. There's nothing to stop the original process from writing to the console while you are doing likewise, and the output from both getting mixed-up, depending on how the processes are doing buffering. If you want to do something like notify the user of something regardless of which window is active, there may be a better way of doing that.
A system process is uniquely identified on the system by its process identifier. Like many Windows resources, a process is also identified by its handle, which might not be unique on the computer. A handle is the generic term for an identifier of a resource. The operating system persists the process handle, which is accessed through the Process.Handle property of the Process component, even when the process has exited. Thus, you can get the process's administrative information, such as the Process.ExitCode (usually either zero for success or a nonzero error code) and the Process.ExitTime. Handles are an extremely valuable resource, so leaking handles is more virulent than leaking memory.
This is not the exact answer to ur questions , but it helps u to understand the basic thing actually.

Categories