process.mainwindowhandle always returning zero - c#

I am trying to start another program from my code and set it to be the size of the screen. No matter what I try, I can't seem to get the correct handle of the window, so SetWindowPos() fails. This is on a Raspberry Pi, and the window I am trying to resize is LXTerminal running a Python script.
Code:
Process p = Process.Start ("bash", b.command);
p.WaitForInputIdle (100);
Process[] procs = Process.GetProcessesByName ("/usr/bin/lxterminal");
p.Refresh ();
IntPtr handle = procs [0].MainWindowHandle;
Console.WriteLine (handle);
SetWindowPos (handle, 0, 0, 0, 1920, 1080, 0x0040);
This always prints '0' for the handle, and "SetWindowPos (nil) (nil) to [0,0x1920,1080] 64" after the SetWindowPos has been run.
b.command is equal to -c "lxterminal -e 'python3 ../../../../GameV1.py'" (but only in this case.) This code for lxterminal is an example and the program may launch other processes.

You are waiting for input idle in a maximum amount of time of 0.1s. I sincerely doubt that the process is going to both start AND have it's main message loop created in such short manner.
I suggest you use the parameterless overload instead, so that you'll wait until the process's message loop has entered an idle state - which is when its window should've been created:
p.WaitForInputIdle()
Quoting the documentation:
A process is said to be in an idle state when it is waiting for messages inside of a message loop. This state is useful, for example, when your application needs to wait for a starting process to finish creating its main window before the application communicates with that window.

Related

Moving the current process window to the front

I am calling a thread every hour to take a screenshot.
but the issue is with activating the window or moving the window to the front.
if there are other apps in the front then while taking the screenshot they appear in the front.
Whats going on?
<Runtime.InteropServices.DllImport("user32.dll")>
Private Function SetForegroundWindow(ByVal hWnd As IntPtr) As Integer
End Function
<Runtime.InteropServices.DllImport("user32.dll")>
Private Function ShowWindow(ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As IntPtr
End Function
Public Sub TakeScreenshot()
Dim thread As New Threading.Thread(AddressOf TakeScreenShotThread)
thread.Start()
End Sub
Public Function TakeScreenShotThread() As Integer
Dim proc As Process = Process.GetCurrentProcess
Call SetForegroundWindow(proc.MainWindowHandle)
Call ShowWindow(proc.MainWindowHandle, 5)
'GIVE IT TIME TO DISPLAY THE APP AND ACTIVATE WINDOW
Dim t = Threading.Tasks.Task.Run(Async Function()
Await Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(200))
Return 1
End Function)
t.Wait()
'SAVE SCREENSHOT IMAGE CODE HERE
End Function
Unfortunately(or if one thinks from the user's point of view fortunately) SetForegroundWindow is not an unconditional "make this window the foremost/active one" command.
It has a set of restrictions:
The system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:
The process is the foreground process.
The process was started by the foreground process.
The process received the last input event.
There is no foreground process.
The process is being debugged.
The foreground process is not a Modern Application or the Start Screen.
The foreground is not locked (see LockSetForegroundWindow).
The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
No menus are active.
So while there are some not recommended ways to overcome this restriction, under normal circumstances, unless one of those conditions is true, SetForegroundWindow won't work.
P.S.: Also, recommended reading - https://devblogs.microsoft.com/oldnewthing/20090220-00/?p=19083
P.S.1: While this restriction is probably the reason why the original solution fails, I'd also like to point that there is a chance that proc.MainWindowHandle can be IntPtr.Zero or be a stale handle (from some starting window or etc.) and that you do not check return values from the called WIN API functions, that is both very important and may actually assist in troubleshooting the issue...

IntPtr always Zero C#

I'm starting internet explorer process. the problem is that it always return zero in p.MainWindowHandle.
my objective is to get mainwindowHandler and minimize that particular window which is just started. the same code is working with chrome browser. but in internet explorer it's not working.
my code is below.
Process p = Process.Start("IEXPLORE.EXE", "www.google.com");
ShowWindow(p.MainWindowHandle, 2);
ShowWindow is a method resize window.
Among the many other reasons (see comments to the question), you have to wait for process to create the main window:
// Process is IDisposable, wrap it into using
using (Process p = Process.Start("IEXPLORE.EXE", "www.google.com")) {
...
// wait till the process creates the main window
p.WaitForInputIdle();
// Main window (if any) has been created, so we can ask for its handle
ShowWindow(p.MainWindowHandle, 2);
}

Don't display wait cursor when console app is launching

I've developed a small console application that essentially pings a url, records the result, restarts the phone if required and then schedules itself to run again.
The client is complaining about the "washing machine" icon being displayed (albeit for less than a second) every time the application launches.
I'm hiding the wait cursor in the first line of my main method but is there any way of preventing the wait cursor from displaying at all?
static void Main()
{
//Hide cursor
IntPtr hOldCursor = SetCursor(IntPtr.Zero);
//Ensure EventLog table is ready
PrepareDatabase();
tapi = new Tapi();
tapi.TAPI_Open();
//Ping specified URL and restart phone if required.
PingRestart();
tapi.TAPI_Close();
//Set the application to run again after the ping interval has passed
SystemTime systemTime = new SystemTime(DateTime.Now.AddMilliseconds(RegistryAccess.PingInterval));
CeRunAppAtTime(System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase, ref systemTime);
}
There is no way to disable the wait cursor, as it appears before the application runs so the application can't block it.
The only way is to code it in C or Assembly, as those languages start up very fast in comparison to .NET executables. But still, an AntiVirus program could block it for a while before it executes.

What is the different between API functions AllocConsole and AttachConsole(-1)?

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.

How to continue executing other processes in command line window after breaking a running process using C#

I have written C# command window application. I'm running bunch of processes on command line inside the main(). For e.g.
void main()
{
process p1 = new process()
set p1 properties
p1.start()
-->p1.StandardInput.WriteLine("start /WAIT cmd.exe /c BUILD -cZP");
}
This line will execute some program in a new command window. While executing that last line I will break this execution using ctrl+c and return the control to execution of the main program.
loop through to output to the execution window.
p1.StandardInput.WriteLine("Done some action");
p1.WaitForExit();
p1.Close();
The above three lines are not executed. The question is p1 never closes to execute following lines which I have in my program.
process p2 = new process()
...
p2.waitforExit()
p2.close.
Any insight for above challenges will be great. thx.
If I understand you correctly (which I admit I may not be understanding you), I believe the problem is that when you press CTRL-C to break into process p1, you are actually killing that process. Then you are trying to send text to the standard input for the process, which has just been killed. Since the process is no longer available to take your input, the main program hangs. That's my best guess.

Categories

Resources