The complex solution below is justified by the need of bringing a browser window to front. It is working ~90% of the time. The problem is the 10%, when it doesn't.
I have an application that is running on a different desktop than the user's active one (it is a screensaver).
I also have a windows service that receives events from the screensaver. This service then does the following:
Impersonates the currently logged in user and starts a helper application with a URL in the command line arguments.
The helper application is started by CreateProcessAsUser - this is also the justification for the helper, I need to use ShellExec, so a separate process have to be used.
This helper application does the following:
Waits until the user's current desktop becomes active. It does a while loop with some sleep until then.
Then it finds out the user's default browser
Starts the default browser using ShellExec (Process.Start in C#), and passes the browser some command line arguments and the URL.
The actual command line invoked by the helper application is this:
cmd /C start "" C:\PathToBrowser\Browser.exe URL -someargument
Up to this point everything is working except one important thing: The browser is not brought to front in all possible cases.
Is there anything further than this, that I could do with these browsers to force them to come to front? My problem is this:
Let's say I start Chrome from command line. Chrome will just send a message to the already running instance, and quit. So I can't rely on the PID and the hWnd of the process I started, it will not be the same as the one actually showing the webpage.
Any help would be much appreciated.
Thanks to cubrr for the help, his idea worked with some extension from my part. First of all, I have to find out the Title of the webpage that will be displayed within the browser. After this I have to use EnumWindows to find the newly opened browser window, and call SetForegroundWindow on it.
My solution is based on these other sources:
How to use EnumWindows to find a certain window by partial title.
Get the title from a webpage.
Bring to forward window when minimized
The solution suggested by cubrr, using FindWindow (you have to know the exact window title to be able to use this):
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool IsIconic(IntPtr handle);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr handle, int nCmdShow);
void Main()
{
const int SW_RESTORE = 9;
var hWnd = FindWindow(null, "Google - Google Chrome");
if (IsIconic(hWnd))
ShowWindow(hWnd, SW_RESTORE);
SetForegroundWindow(hWnd);
}
Here is the final code I ended up using:
public class MyClass
{
private const int SW_RESTORE = 9;
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
[DllImport("user32.dll")]
private static extern bool IsIconic(IntPtr handle);
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
public static string GetWebPageTitle(string url)
{
// Create a request to the url
HttpWebRequest request = HttpWebRequest.Create(url) as HttpWebRequest;
// If the request wasn't an HTTP request (like a file), ignore it
if (request == null) return null;
// Use the user's credentials
request.UseDefaultCredentials = true;
// Obtain a response from the server, if there was an error, return nothing
HttpWebResponse response = null;
try { response = request.GetResponse() as HttpWebResponse; }
catch (WebException) { return null; }
// Regular expression for an HTML title
string regex = #"(?<=<title.*>)([\s\S]*)(?=</title>)";
// If the correct HTML header exists for HTML text, continue
if (new List<string>(response.Headers.AllKeys).Contains("Content-Type"))
if (response.Headers["Content-Type"].StartsWith("text/html"))
{
// Download the page
WebClient web = new WebClient();
web.UseDefaultCredentials = true;
string page = web.DownloadString(url);
// Extract the title
Regex ex = new Regex(regex, RegexOptions.IgnoreCase);
return ex.Match(page).Value.Trim();
}
// Not a valid HTML page
return null;
}
public static void BringToFront(string title)
{
try
{
if (!String.IsNullOrEmpty(title))
{
IEnumerable<IntPtr> listPtr = null;
// Wait until the browser is started - it may take some time
// Maximum wait is (200 + some) * 100 milliseconds > 20 seconds
int retryCount = 100;
do
{
listPtr = FindWindowsWithText(title);
if (listPtr == null || listPtr.Count() == 0)
{
Thread.Sleep(200);
}
} while (--retryCount > 0 || listPtr == null || listPtr.Count() == 0);
if (listPtr == null)
return;
foreach (var hWnd in listPtr)
{
if (IsIconic(hWnd))
ShowWindow(hWnd, SW_RESTORE);
SetForegroundWindow(hWnd);
}
}
}
catch (Exception)
{
// If it fails at least we tried
}
}
public static string GetWindowText(IntPtr hWnd)
{
int size = GetWindowTextLength(hWnd);
if (size++ > 0)
{
var builder = new StringBuilder(size);
GetWindowText(hWnd, builder, builder.Capacity);
return builder.ToString();
}
return String.Empty;
}
public static IEnumerable<IntPtr> FindWindowsWithText(string titleText)
{
IntPtr found = IntPtr.Zero;
List<IntPtr> windows = new List<IntPtr>();
EnumWindows(delegate(IntPtr wnd, IntPtr param)
{
if (GetWindowText(wnd).Contains(titleText))
{
windows.Add(wnd);
}
return true;
}, IntPtr.Zero);
return windows;
}
[STAThread]
public static int Main(string[] args)
{
try
{
if (args.Count() == 0)
return 0;
// ...
// Wait until the user's desktop is inactive (outside the scope of this solution)
// ...
String url = args[0];
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
// ...
// Get the path to the default browser from registry, and create a StartupInfo object with it.
// ...
process.StartInfo = startInfo;
process.Start();
try
{
process.WaitForInputIdle();
}
catch (InvalidOperationException)
{
// if the process exited then it passed the URL on to the other browser process.
}
String title = GetWebPageTitle(url);
if (!String.IsNullOrEmpty(title))
{
BringToFront(title);
}
return 0;
}
catch (System.Exception ex)
{
return -1;
}
}
}
Related
I have a listener service where I can send a command to it, but with this service, it's unable to send a command to a command prompt running ethereuem's geth. Is there a way to forcibly get the keyboard strokes through?
I notice that I have other code that can find a command prompt by name or id and able to bring that window to the front, but when I attempt to do that with the command prompt that is running geth, it can't seem to bring that window to the front. I hope that bit of information may help.
namespace Listener
{
class Program
{
static void Main(string[] args)
{
using (var listener = new HttpListener())
{
listener.Prefixes.Add("http://localhost:8081/mytest/");
listener.Start();
string command = string.Empty;
for (; ; )
{
Console.WriteLine("Listening...");
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
// TODO: read and parse the JSON data from 'request.InputStream'
using (StreamReader reader = new StreamReader(request.InputStream))
{
// Would prefer string[] result = reader.ReadAllLines();
string[] result = reader.ReadToEnd().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var s in result)
{
command = s;
}
}
// send command to other geth window
sendKeystroke(command);
using (HttpListenerResponse response = context.Response)
{
// returning some test results
// TODO: return the results in JSON format
string responseString = "<HTML><BODY>Hello, world!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
using (var output = response.OutputStream)
{
output.Write(buffer, 0, buffer.Length);
}
}
}
}
}
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
private static void sendToOpenCmd(string command)
{
int processId = 13420;
//processId = int.Parse(13420);
System.Diagnostics.Process proc = (from n in System.Diagnostics.Process.GetProcesses()
where n.ProcessName == "geth"
//where n.Id == processId
select n).FirstOrDefault();
if (proc == null)
{
//MessageBox.Show("No such process.");
Console.WriteLine("No such process.");
}
else
{
SetForegroundWindow(proc.MainWindowHandle);
SendKeys.SendWait(command + "{enter}");
Console.WriteLine("Sent! " + command + " " + DateTime.Now.ToString());
}
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
public static void sendKeystroke(string command)
{
const uint WM_KEYDOWN = 0x100;
const uint WM_SYSCOMMAND = 0x018;
const uint SC_CLOSE = 0x053;
IntPtr WindowToFind = FindWindow(null, "geth");
//ushort[] result = command.Where(i => ushort.TryParse(i, out short s)).Select(ushort.Parse);
//ushort[] result = command.Where(i => { ushort r = 0; return ushort.TryParse(i, out r); }).Select(ushort.Parse);
ushort result;
ushort.TryParse(command, out result);
IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)result), (IntPtr)0);
//IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
}
}
}
ohhh there seems to be three process id's that windows has on that command prompt running geth. i'm not sure why there's three. i tried all three, and one of them worked for me. so i can't call the process by name of "geth", that doesn't seem to be the correct window or process to send the keystrokes to. hopefully this helps someone else!
There are lots of answers, try this-that, but nothing works. Access denied.
We are starting an application on the server, and automating it to do certain Quick tasks. Of course I can start it... (but it cannot run hidden, it must run in a real-desktop mode).
I've tried all manner of different elevation/impersonation techniques. Yes I've selected the IIS-Interact with desktop box. In the web.config I've got the impersonation flag...
Here is relevant code with some commented out attempts:
private const int WM_CLOSE = 16;
private const int BN_CLICKED = 245;
private const int LB_GETTEXT = 0x0189;
private const int LB_GETTEXTLEN = 0x018A;
private const int WM_SETTEXT = 0X000C;
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
WindowsImpersonationContext impersonationContext;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("advapi32.dll")]
public static extern int LogonUserA(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogonType,
int dwLogonProvider,
ref IntPtr phToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetProcessWindowStation();
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr GetThreadDesktop(int dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern int GetCurrentThreadId();
public void findOurProcess(string filePath)
{
IntPtr hwnd = IntPtr.Zero;
IntPtr hwnd_select = IntPtr.Zero;
IntPtr hwndChild = IntPtr.Zero;
DateTime timer;
TimeSpan diff;
int processid;
string username = "Programmer";
clsImpersonate cls = new clsImpersonate();
try
{
IntPtr token = cls.ImpersonateUser(username, Environment.MachineName, "RoboMan");
using (WindowsImpersonationContext impersonatedUser = WindowsIdentity.Impersonate(token))
{
//Process process = new Process();
//ProcessStartInfo info = new ProcessStartInfo();
//info.FileName = fileName;
//info.Arguments = argument;
//process.StartInfo = info;
//process.Start();
//if (impersonateValidUser("Programmer", "", "Roboman"))
//if (impersonateValidUser(username, "DESKTOP", "Roboman"))
//{
ProcessStartInfo psi = new ProcessStartInfo("OUR PROCESS");
//psi.UserName = username;
//psi.Domain = Environment.MachineName;
//psi.Password = new System.Security.SecureString();
//psi.Password.AppendChar('R');
//psi.Password.AppendChar('o');
//psi.Password.AppendChar('b');
//psi.Password.AppendChar('o');
//psi.Password.AppendChar('m');
//psi.Password.AppendChar('a');
//psi.Password.AppendChar('n');
psi.Arguments = "-batch";
psi.WorkingDirectory = "OUR DIRECTORY";
psi.UseShellExecute = false;
//myProcess.StartInfo.CreateNoWindow = true; //Maybe?
//myProcess.Start();
//The following security adjustments are necessary to give the new
//process sufficient permission to run in the service's window station
//and desktop. This uses classes from the AsproLock library also from
//Asprosys.
//IntPtr hWinSta = GetProcessWindowStation();
//WindowStationSecurity ws = new WindowStationSecurity(hWinSta,
// System.Security.AccessControl.AccessControlSections.Access);
////ws.AddAccessRule(new WindowStationAccessRule(username,
// // WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
//ws.AddAccessRule(new WindowStationAccessRule(username,
// WindowStationRights.CreateDesktop, System.Security.AccessControl.AccessControlType.Allow));
//ws.AcceptChanges();
//IntPtr hDesk = GetThreadDesktop(GetCurrentThreadId());
//DesktopSecurity ds = new DesktopSecurity(hDesk,
// System.Security.AccessControl.AccessControlSections.Access);
//ds.AddAccessRule(new DesktopAccessRule(username,
// DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
//ds.AcceptChanges();
using (Process process = Process.Start(psi))
{
processid = process.Id;
}
the cls.ImpersonateUser The above attempts to run an elevated section of code as another user. But fails. You can see I've attempted to use this version as well. ImpersonateValidUser Example
The AsProSys code would also throw an Access-denied exception right on the ws.AcceptChanges();
WebServers run as Windows Services. And Windows services are by default prohibited from accessing the Desktop as of Windows Vista.
In addition to the general Service Limitations, webservers are also customary run in the most limited userrights possible. Readaccess to it's programm and content directory is about the best they get. They are always on, so they are highly vulnerable to hacking.
As I understand currently you try to start a Dekstop applciation from a Webserver. And that is pretty much a no-go. If that did work, I would first wonder how quickly I can uninstall it. And then how I did not manage to limits right in the first place to prevent this. For every admin that will ever have to run your Webpage: Stop trying to do that!
Instead just have a Helper application that is normally installed on the Windows. Have it start automatically via the TaskSheduler on user login. and have it and the WebServer communicate via Pipes, the Loopback device or similar IPC ways that are acceptable for a WebServer.
I am facing issue while automating a file upload process through chrome browser.
I have handled the Choose file windows popup. I use the following batch file to log off from remote desktop connnection .
for /f "skip=1 tokens=3" %%s in ('query user %USERNAME%') do (
%windir%\System32\tscon.exe %%s /dest:console
)
I have already disabled every setting under Administrative Templates > Windows Components > Remote Desktop Session Host > Session Time Limits in Windows Group Policy.
I have used below code for handling the windows file upload dialog
var title = "Open";
var fileTextBoxName = "File name:";
var openButtonName = "File name:";
//File Upload Window
AutomationElement desktopObject = AutomationElement.RootElement;
PropertyCondition popUpWindowNameCondition = new PropertyCondition(AutomationElement.NameProperty, title.Trim());
AutomationElement popUpWindow = desktopObject.FindFirst(TreeScope.Subtree, popUpWindowNameCondition);
if (popUpWindow == null) throw new Exception("File Upload dialog window not found");
// FileTextBox
PropertyCondition fileTextBoxCondition = new PropertyCondition(AutomationElement.NameProperty, fileTextBoxName.Trim());
AutomationElement fileTextBox = popUpWindow.FindFirst(TreeScope.Subtree,
new AndCondition(fileTextBoxCondition,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)));
if (fileTextBox == null) throw new Exception("File textbox not found");
try
{
SetForegroundWindow(popUpWindow, 0);
}
catch (Exception ex)
{
}
Thread.Sleep(2000);
try
{
fileTextBox.SetFocus();
}
catch (Exception ex)
{
}
ValuePattern etb = fileTextBox.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
etb.SetValue(extractedFields["UploadFilePath"].ToString());
Thread.Sleep(2000);
PropertyCondition buttonCondition = new PropertyCondition(AutomationElement.NameProperty, "Open");
AutomationElement buttonElement = popUpWindow.FindFirst(TreeScope.Descendants, buttonCondition);
if (buttonElement == null) throw new Exception("Button not found");
try
{
AutomationElement button = buttonElement;
InvokePattern buttonPattern = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
buttonPattern.Invoke();
}
catch (Exception ex)
{
SendKeys.SendWait("{Enter}");
}
static int RETRY_LIMIT = 3;
public static bool SetForegroundWindow(AutomationElement elm, uint retries, int time = 2000)
{
try
{
if (retries < RETRY_LIMIT)
{
// Using Win32 to set foreground window because
// AutomationElement.SetFocus() is unreliable
// Get handle to the element
IntPtr other = FindWindow(null, elm.Current.Name);
// Get the Process ID for the element we are trying to
// set as the foreground element
int other_id = GetWindowThreadProcessId(
other, IntPtr.Zero);
// Get the Process ID for the current process
int this_id = GetWindowThreadProcessId(
Process.GetCurrentProcess().Handle, IntPtr.Zero);
// Attach the current process's input to that of the
// given element. We have to do this otherwise the
// WM_SETFOCUS message will be ignored by the element.
bool success =
AttachThreadInput(this_id, other_id, true);
// Make the Win32 call
IntPtr previous = SetForegroundWindow(other);
if (IntPtr.Zero.Equals(previous))
{
// Trigger re-try
throw new Exception(
"SetForegroundWindow failed");
}
return true;
}
// Exceeded retry limit, failed!
return false;
}
catch
{
retries++;
Thread.Sleep(time);
return SetForegroundWindow(elm, retries);
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int GetWindowThreadProcessId(IntPtr hWnd, IntPtr lpdwProcessId);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SetForegroundWindow(IntPtr hWnd);
The code works perfectly after disconnecting from RDP using above bat. But the issue starts afters approx 12 hours. There is no error logged in the log file. (I have removed the logs from below code for keeping it small). The code finds the open dialog box and even the file textbox. The log says it has entered the file path. And even the open button is clicked . But actually the file is not selected . The website throws a alert prompting for selecting the file on pressing the upload button. The issue is only resolved when i reconnect to RDP and use the above bat to disconnect from RDP. I want to know is there any possibility for the code to work without reconnecting to RDP after every 12 hours ?
I have created a web application that will call a window based application using:
#region Process
public static string shell_exec(string path, string args)
{
var p = new ProcessStartInfo();
// Redirect the output stream of the child process.
p.UseShellExecute = false;
p.LoadUserProfile = true;
p.RedirectStandardOutput = true;
p.FileName = path;
p.Arguments = args;
p.WindowStyle = ProcessWindowStyle.Normal;
var pc = Process.Start(p);
WindowHelper.BringProcessToFront(pc);
string output = pc.StandardOutput.ReadToEnd();
pc.WaitForExit();
return output;
}
#endregion
this is published using IIS Express server, the process.start() calls the application and shows in the top most (obviously because i set the property topmost true) but not in the taskbar (didn't activate the load event or not focused)
I also used bringprocesstofront function but didn't do anything:
const int SW_RESTORE = 9;
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
public static void BringProcessToFront(Process process)
{
IntPtr handle = process.MainWindowHandle;
if (IsIconic(handle))
{
ShowWindow(handle, SW_RESTORE);
}
SetForegroundWindow(handle);
}
am i missing something in my code?
The external process im calling is an application that will register a persons fingerprint (using DPFP digital persona, creating it in window based unless there is for web or javascript)
I'm trying to open a console from a winform application from a button click. I'm doing this via the code below.
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
[DllImport("kernel32.dll")]
static extern Boolean FreeConsole();
private void button1_Click(object sender, EventArgs e)
{
int userInput = 0;
AllocConsole();
do
{
Console.Clear();
Console.WriteLine("Hello World");
Console.WriteLine("Select 1 or 2");
int.TryParse(Console.ReadLine(), out userInput);
} while (userInput != 1 && userInput != 2);
FreeConsole();
}
The first time the console is opened, it works fine. Attempting to open the console a second time will work fine, but once Console.Clear(); is called, I get:
An unhandled exception of type 'System.IO.IOException' occurred in mscorlib.dll
Additional information: The handle is invalid.
The same exception will be thrown on Console.WriteLine(); and Console.ReadLine();.
I've tried the proposed solutions from this, this, and this, but I end up with the same "handle is invalid." error on Console.Clear();.
Some additional code I've tried:
[DllImport("kernel32.dll",
EntryPoint = "GetStdHandle",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll",
EntryPoint = "AllocConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();
[DllImport("kernel32.dll")]
static extern Boolean FreeConsole();
private const int STD_OUTPUT_HANDLE = -11;
private const int MY_CODE_PAGE = 437;
private void button1_Click(object sender, EventArgs e)
{
int userInput = 0;
AllocConsole();
IntPtr stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE);
StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
standardOutput.AutoFlush = true;
Console.SetOut(standardOutput);
Stream streamIn = Console.OpenStandardInput();
TextReader readerIn = new StreamReader(streamIn);
Console.SetIn(readerIn);
do
{
Console.Clear();
Console.WriteLine("Hello World");
Console.WriteLine("Select 1 or 2");
int.TryParse(Console.ReadLine(), out userInput);
} while (userInput != 1 && userInput != 2);
FreeConsole();
}
This seems to be ok for Console.WriteLine(); and Console.ReadLine(); but I still can't get around the exception being thrown by Console.Clear();. Can anyone tell me if I'm missing something?
I doubt it is possible to attach/detach to console multiple times. As far as I understand the code of private static IntPtr ConsoleInputHandle (the same goes for ConsoleOutputHandle), the handle is initialized once on first usage, so when you detach the process from console and re-attach it once again the handle is invalid.
So imho it is not feasible to use Console class as you want (nor do I know any live examples of programs working both as console and Win32 app - the most applications I've seen provide two versions of .exe).
If you really need Windows console I guess you can try to provide your own console wrapper above Windows API. If you need only the looks of console you can go with rendering of your own "console-like" window.