I have a WinForms project, and if the user want's a debug console, I allocate a console with AllocConsole().
All console output works normally with the target architecture set to "Any CPU", but when I change it to "x86" it doesn't output anything (Console.Read() still works as expected). If I open the EXE directly, the output works. It looks like Visual Studio redirects it into it's own "Output" window.
I also tried this answer, but it didn't work, I also tried Console.SetOut(GetStdHandle(-11)), which didn't work either.
Setting the target architecture to 'Any CPU' is no option for me.
So here are my two questions:
Why is this only the case when the target architecture is set to x86?
How can I output to my console when running inside of Visual Studio?
When "Enable native code debugging" is enabled, output from consoles crated with AllocConsole is redirected to the debug output window instead.
The reason this only happens in x86 and not AnyCPU is because you can only debug native code in an x86 application.
Note that this behavior only occurs with consoles created with AllocConsole. A console application's output is not redirected.
EDIT: The other reason for the console not outputting text is when you've written to the console before calling AllocConsole.
Regardless of the reason, this code will restore output if it was redirected, and reopen the console in case it's invalid. It uses the magic number 7 which is what the handle of stdout usually equals to.
using System;
using System.IO;
using System.Runtime.InteropServices;
public static class ConsoleHelper
{
public static void CreateConsole()
{
AllocConsole();
// stdout's handle seems to always be equal to 7
IntPtr defaultStdout = new IntPtr(7);
IntPtr currentStdout = GetStdHandle(StdOutputHandle);
if (currentStdout != defaultStdout)
// reset stdout
SetStdHandle(StdOutputHandle, defaultStdout);
// reopen stdout
TextWriter writer = new StreamWriter(Console.OpenStandardOutput())
{ AutoFlush = true };
Console.SetOut(writer);
}
// P/Invoke required:
private const UInt32 StdOutputHandle = 0xFFFFFFF5;
[DllImport("kernel32.dll")]
private static extern IntPtr GetStdHandle(UInt32 nStdHandle);
[DllImport("kernel32.dll")]
private static extern void SetStdHandle(UInt32 nStdHandle, IntPtr handle);
[DllImport("kernel32")]
static extern bool AllocConsole();
}
See How to detect if Console.In (stdin) has been redirected? for another way to detect if the console handles have been redirected.
None of the earlier answers worked well for me with VS2017 and Windows 10 (for instance they failed if launch app in debug mode).
Below you can find a little bit enhanced code. Idea is the same, but magic numbers are removed (Ceztko already mentioned that) and all necessary in\out streams are initialized.
This code works for me if create a new console (alwaysCreateNewConsole = true).
Attaching to console of parent process (alwaysCreateNewConsole = false) has several drawbacks. For example I was unable to completely mimic behavior of console app launched from cmd. And I'm not sure that it is possible at all.
And most important: after revision of Console class I reconsidered general idea of using Console class with manually created console. It works well (I hope) for most of the cases, but can bring a lot of pain in future.
static class WinConsole
{
static public void Initialize(bool alwaysCreateNewConsole = true)
{
bool consoleAttached = true;
if (alwaysCreateNewConsole
|| (AttachConsole(ATTACH_PARRENT) == 0
&& Marshal.GetLastWin32Error() != ERROR_ACCESS_DENIED))
{
consoleAttached = AllocConsole() != 0;
}
if (consoleAttached)
{
InitializeOutStream();
InitializeInStream();
}
}
private static void InitializeOutStream()
{
var fs = CreateFileStream("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, FileAccess.Write);
if (fs != null)
{
var writer = new StreamWriter(fs) { AutoFlush = true };
Console.SetOut(writer);
Console.SetError(writer);
}
}
private static void InitializeInStream()
{
var fs = CreateFileStream("CONIN$", GENERIC_READ, FILE_SHARE_READ, FileAccess.Read);
if (fs != null)
{
Console.SetIn(new StreamReader(fs));
}
}
private static FileStream CreateFileStream(string name, uint win32DesiredAccess, uint win32ShareMode,
FileAccess dotNetFileAccess)
{
var file = new SafeFileHandle(CreateFileW(name, win32DesiredAccess, win32ShareMode, IntPtr.Zero, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
if (!file.IsInvalid)
{
var fs = new FileStream(file, dotNetFileAccess);
return fs;
}
return null;
}
#region Win API Functions and Constants
[DllImport("kernel32.dll",
EntryPoint = "AllocConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();
[DllImport("kernel32.dll",
EntryPoint = "AttachConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern UInt32 AttachConsole(UInt32 dwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "CreateFileW",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr CreateFileW(
string lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile
);
private const UInt32 GENERIC_WRITE = 0x40000000;
private const UInt32 GENERIC_READ = 0x80000000;
private const UInt32 FILE_SHARE_READ = 0x00000001;
private const UInt32 FILE_SHARE_WRITE = 0x00000002;
private const UInt32 OPEN_EXISTING = 0x00000003;
private const UInt32 FILE_ATTRIBUTE_NORMAL = 0x80;
private const UInt32 ERROR_ACCESS_DENIED = 5;
private const UInt32 ATTACH_PARRENT = 0xFFFFFFFF;
#endregion
}
Following worked for me in vs 2015, none worked from other answers:
Source: https://social.msdn.microsoft.com/profile/dmitri567/?ws=usercard-mini
using System;
using System.Windows.Forms;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace WindowsApplication
{
static class Program
{
[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();
private const int STD_OUTPUT_HANDLE = -11;
private const int MY_CODE_PAGE = 437;
static void Main(string[] args)
{
Console.WriteLine("This text you can see in debug output window.");
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);
Console.WriteLine("This text you can see in console window.");
MessageBox.Show("Now I'm happy!");
}
}
}
I also had this problem. Every time I tried to debug my app, the console was blank. Strangely, launching the exe without the debugger worked fine.
I found that I had to Enable the Visual Studio hosting process from the project's Debug menu.
Stephen is correct that Enable native code debugging does redirect the console to the Output window. However, regardless of the native code debugging setting, I saw absolutely no output in either place until I enabled the Visual Studio hosting process.
This could have been the reason that merely disabling native code debugging did not solve your issue.
Just wanted to post the answer from Visual studio Developer community.
https://developercommunity.visualstudio.com/content/problem/12166/console-output-is-gone-in-vs2017-works-fine-when-d.html
Go to this link and look at the answer from Ramkumar Ramesh.
I have tested this code in VS 2017.
I spent one day to find this answer. Hope it helps you as well.
Edit--
As suggessted by Mike to include some description. I would like to suggesst some corrections in Zuniar answer. He tested with VS 2015. But that would not work in VS 2017.
Instead of GetStdHandle, Please use CreateFile reference from kernel32.dll
IntPtr stdHandle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, 0,
OPEN_EXISTING, 0, 0);
Before adding above code, please declare
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(string lpFileName, uint
dwDesiredAccess, uint dwShareMode, uint lpSecurityAttributes, uint
dwCreationDisposition, uint dwFlagsAndAttributes, uint hTemplateFile);
private const int MY_CODE_PAGE = 437;
private const uint GENERIC_WRITE = 0x40000000;
private const uint FILE_SHARE_WRITE = 0x2;
private const uint OPEN_EXISTING = 0x3;
i have taken this code from the given link.
Related
I am trying to get my C# script to output into ets2 so that it will drive for me (wasd). For testing I am using the space bar. I have tested the code in chrome and notepad, where it works and puts down a space. Would anyone know what is going wrong?
Update:
I wrote a little bit of test code for python using the keyboard module and I got it to work. Would it be possible to make "space" into a variable that I could change from C#?
Python Code:
import keyboard, time
time.sleep(5)
keyboard.press_and_release("space")
The Threads and Windows in Spy++:
I use the following code:
public const int WM_KEYDOWN = 0x0100;
const int VK_SPACE = 0x20;
static void Main(string[] args)
{
System.Threading.Thread.Sleep(2000); // gives user time to switch tabs
IntPtr programloc = WindowHelper.GetForegroundWindow();
// I also tried using (from Spy++) FindWindow("Euro Truck Simulator 2", "prism3d");
if (programloc == IntPtr.Zero) throw new SystemException();
WindowHelper.PostMessage(programloc, WM_KEYDOWN, VK_SPACE, 0);
}
and the following module WindowHelper (combination of multiple Stackoverflow and docs.microsoft pages):
class WindowHelper
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern IntPtr FindWindow(
string lpClassName,
string lpWindowName);
[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindos);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "GetForegroundWindow")]
public static extern IntPtr GetForegroundWindow();
}
Your code is exiting immediately. Is this intentional? Make a call to Console.Readline() at the end of main to block until you give the console input.
I have been able to find a (temporary) fix myself. This fix is however not very performance friendly. Anyone having better suggestions is welcome.
Using the keyboard function I described in my question, I made a python script which uses arguments. This python script is started by the C# script using the following code:
static public string run_python(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = #"C:\Users\Stijn\AppData\Local\Programs\Python\Python36\python.exe";
start.Arguments = string.Format("\"{0}\" \"{1}\"", cmd,args);
start.UseShellExecute = false;// Do not use OS shell
start.CreateNoWindow = true; // We don't need new window
start.RedirectStandardOutput = true;// Any output, generated by application will be redirected back
start.RedirectStandardError = true; // Any error in standard output will be redirected back (for example exceptions)
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd(); // Here are the exceptions from our Python script
string result = reader.ReadToEnd(); // Here is the result of StdOut(for example: print "test")
return result + stderr;
}
}
}
The python code consists of:
from keyboard import press_and_release
from sys import argv as args
for i in range(len(args)-1):
press_and_release(args[i+1])
Using the keyboard module as can be found at https://pypi.org/project/keyboard/
I currently use the following code in the old 32bit architecture windows:
[DllImport("kernel32.dll", EntryPoint = "SetConsoleMode", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint mode);
[DllImport("kernel32.dll", EntryPoint = "GetConsoleMode", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("user32.dll")]
public static extern bool EnableMenuItem(IntPtr hConsoleHandle, uint uIDEnableItem, uint uEnable);
[DllImport("user32.dll")]
public static extern IntPtr GetSystemMenu(IntPtr hSystemMenu, bool bRevert);
[DllImport("user32.dll")]
public static extern IntPtr RemoveMenu(IntPtr hSystemMenu, uint nPosition, uint wFlags);
This is the code, which uses the GetConsoleMode and SetConsoleMode:
if (!GetConsoleMode(consoleHandle, out consoleMode))
throw new IOException("Console setup error - failed to retrieve current ConsoleMode");
consoleMode &= ~Constants.ENABLE_QUICK_EDIT_MODE;
Constants.SetConsoleMode(consoleHandle, consoleMode)
I know, try to get the application running within a 64bit machine, but I get this error:
Type: System.IO.IOException
Message: Console setup error - failed to retrieve current ConsoleMode
I googled and checked that the dll on the 64-bit windows is also named kernel32.dll.. What am I missing here?
I'm not sure it's a good idea to assume that the console handle always has a value of 0x3, especially as there's a Windows API method to retrieve the standard handles.
The following seems to work for me when running as both a 32bit and 64bit executable (on 64bit Windows), and appears to be how .NET does it internally:
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int nStdHandle);
var consoleHandle = GetStdHandle(-10); // STD_INPUT_HANDLE
GetConsoleMode(consoleHandle, out var mode);
In a 32bit managed app I use Proecess.Start to load a 64bit .Net app.
Following that I PostMessage() from the 32bit app to the 64bit app using the MainWindowHandle.
The problem is that when the PostMessage() is called I get the error "A call to PInvoke function 'xxxx::PostMessage' has unbalanced the stack..."
After pressing continue everything works fine.
I am using VS 2017.
If I convert the 32bit app to 64bit no problem.
Also I don't get the problem when run the 32bit app outside the VS.
Both apps are in C#.
Can I just uncheck the PInvokeStackImbalance in MDA or is it something that I should "worry" about?
Thank you
Following is the code in a form to start a client app, dock its main form and set the handle of the current form to the client app.
private const int SEND_HANDLE = WM_USER + 101;
[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
private static extern bool PostMessage(IntPtr hwnd, uint Msg, long wParam, long lParam);
private void DoDock()
{
ProcessStartInfo psi = new ProcessStartInfo("test.exe");
psi.Arguments = "";
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
pDocked = Process.Start(psi);
while (hWndDocked == IntPtr.Zero)
{
pDocked.WaitForInputIdle(1000);
Thread.Sleep(100);
pDocked.Refresh();
if (pDocked.HasExited)
return;
hWndDocked = pDocked.MainWindowHandle; //cache the window handle
}
PostMessage(hWndDocked, SEND_HANDLE, this.Handle.ToInt32(), 0);
}
This normally means that your pinvoke declaration is wrong. This website lists the correct declaration as:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
How can I execute cmd command (webpack) from .NET Core - console app and preserve colors?
This is my current code:
static void Main(string[] args)
{
var dir = args.First();
//build:vendor -> webpack --config webpack.config.vendor.js --progress --color --display-error-details
Console.WriteLine("npm run build:vendor -- --env.prod".Execute(dir));
Console.ReadKey();
}
public static string Execute(this string cmd, string startDir)
{
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c {cmd}",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true,
WorkingDirectory = startDir
}
};
process.Start();
string result = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return result;
}
My output:
Expected output:
I see 2 problems:
when I run command
from cmd.exe, progress is displayed in percents but running command
from dotnet outputs strange characters ...
colors are missing
From your screenshot it looks like your tool uses ANSI escape codes to format output. Those are character sequences which are interpreted by commands instead of regular text, by terminals that support them.
Before Windows 10, cmd.exe did not support such codes, instead requiring using specific winapi functions to control console text colors and other attributes. Starting with Windows 10 it is supported, but has to be enabled.
For example, suppose you do this:
Console.WriteLine("\x1b[35mHello World\x1b[0m");
By default it will print some nonsense similar to your current output. Now let's enable support for ANSI escape codes. For that we will need pinvoke to winapi function SetConsoleMode:
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
And use some helper winapi functions:
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
Now we can enable required flag:
public class Program {
static void Main(string[] args) {
const int STD_OUTPUT_HANDLE = -11;
// get handle to console output
IntPtr hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != IntPtr.Zero) {
uint mode;
// get current mode
if (GetConsoleMode(hOut, out mode)) {
// add ENABLE_VIRTUAL_TERMINAL_PROCESSING flag which enables support for ANSI escape codes
mode |= 0x0004; // ENABLE_VIRTUAL_TERMINAL_PROCESSING flag
SetConsoleMode(hOut, mode);
}
}
Console.WriteLine("\x1b[35mHello World\x1b[0m");
}
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
}
If you run this on Windows 10, instead of some nonsense it should print "Hello World" in magentoo color to console output.
So, if you do this before writing redirected output - it should fix your problems.
Here is the code I wrote to open a process:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern UIntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(UIntPtr hObject);
private const uint PROCESS_QUERY_INFORMATION = 0x0400;
public static void processInfo() {
uint PID = 3144;
UIntPtr handle = UIntPtr.Zero;
handle = OpenProcess(PROCESS_QUERY_INFORMATION, false, PID);
Console.WriteLine(Marshal.GetLastWin32Error());
Console.WriteLine(handle);
if (!handle.Equals(UIntPtr.Zero)) {
CloseHandle(handle);
}
}
Marshal.GetLastWin32Error() returns error 1150 for any process. From MSDN:
"ERROR_OLD_WIN_VERSION: The specified program requires a newer version
of Windows."
I'm running this code in Windows 2008 R2 in Visual Studio 2015 Community Edition. Target Framework is set to ".NET Framework 4.5.2" in project settings.
Also, it seems that OpenProcess is still able to do its job because the returned handle is not zero. Should I be concerned about this error?
From the documentation:
If the function succeeds, the return value is an open handle to the
specified process.
If the function fails, the return value is NULL. To get extended error
information, call GetLastError.
Note that the only mention of calling GetLastError is if the function fails. That is indicated by the return value. Only check the error code when the function fails, it only has a meaningful value in that situation. Your mistake is that you check the error code unconditionally.
handle = OpenProcess(...);
if (handle == UIntPtr.Zero)
// only now call Marshal.GetLastWin32Error
Note also that it is pointless to assign handle twice. You wrote:
UIntPtr handle = UIntPtr.Zero;
handle = OpenProcess(...);
Surely the compiler warned that this was pointless, that the value assigned to handle was not used. Your code is somewhat akin to:
int i = 1;
i = 2;
I'm sure you'd never do this. Your code should be:
UIntPtr handle = OpenProcess(...);
I do not know what the problem with your code is, but here is a very simple implementation which I have tested working. Keep in mind you must run as administrator.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApp3
{
class Program
{
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
ProcessAccessFlags processAccess, bool bInheritHandle, int processId);
static void Main(string[] args)
{
Process proc = Process.GetProcessesByName("ac_client")[0];
var hProc = OpenProcess(ProcessAccessFlags.All, false, proc.Id);
}
}
}