I'm using the following code to call a TaskDialog.
[DllImport("ComCtl32", CharSet = CharSet.Unicode, PreserveSig = false)]
internal static extern void TaskDialogIndirect(
[In] ref TASKDIALOGCONFIG pTaskConfig,
[Out] out int pnButton,
[Out] out int pnRadioButton,
[Out] out bool pfVerificationFlagChecked);
However, I get the exception "Unable to find an entry point named 'TaskDialogIndirect' in DLL 'ComCtl32'."
I took this code. I am using Windows 7 x64 (RC).
What am I doing wrong?
Nothing except this is a vista feature
UPDATE:
This probem had to do with side by side assemblies: these functions are present only in comctl32.dll version 6, but, for compatibility reasons, Vista will load an earlier version unless you tell it otherwise. The approach most people (including me) have been taking is to use a manifest. This has proven to be tricky, and may not be the right solution anyway, especially if what you're writing is a library: you don't necessarily want to force the entire application to use common controls 6.
The right solution is to push a new activation context when calling one of the Vista-only APIs. The activation context will use the correct version of comctl32.dll while leaving the rest of the application alone, and no manifest is required.
Fortunately, this is easy to do.Some complete code that already exists MS Knowledgebase. The code from the article (KB 830033) does the trick as is.
Alternative Managed API:
A full wrapper for Vista's TaskDialog & TaskDialogIndirect can be found here:
http://code.msdn.microsoft.com/WindowsAPICodePack
For WPF use the following:
Download the 'VistaBridge Sample Library' from http://code.msdn.microsoft.com/VistaBridge once downloaded, open the project and then build it (if you want to look through all the code, examine the files in the \Library or \Interop folders). You can now take the DLL from VistaBridge\bin\debug\ and add a reference to it in your project, as well you must add a using statement for each of the different VistaBridge modules. For Example:
using Microsoft.SDK.Samples.VistaBridge.Interop or .Library or .Properties or .Services - Depending on your needs.
The VistaBridge project includes API's for many other Vista Features (such as the TaskDialog, Vista OpenFile and SaveFile Dialogs, and of course the Aero Glass Effects) to try these out, run the VistaBridge Project.
The use of Task Dialog requires version 6 of the Windows Common Controls DLL(ComCtl32.dll)! For compatibility reasons, applications don’t bind to this version by default. One way to bind to version 6 is to place a manifest file alongside your executable (named YourAppName.exe.manifest), with the following content:
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
This manifest can also be embedded as a Win32 resource inside your executable (with the name RT_MANIFEST and ID set to 1), if you don’t want to have the extra standalone file. Visual Studio can do this work for you, if you associate your manifest file in your project’s properties.
Based on almog.ori's answer (which got some orphaned links) I made a small change to the linked code, I puzzled around several days:
MS Knowledgebase helped (Archiv), Full Code with adoptions made by me:
using System.Runtime.InteropServices;
using System;
using System.Security;
using System.Security.Permissions;
using System.Collections;
using System.IO;
using System.Text;
namespace MyOfficeNetAddin
{
/// <devdoc>
/// This class is intended to use with the C# 'using' statement in
/// to activate an activation context for turning on visual theming at
/// the beginning of a scope, and have it automatically deactivated
/// when the scope is exited.
/// </devdoc>
[SuppressUnmanagedCodeSecurity]
internal class EnableThemingInScope : IDisposable
{
// Private data
private IntPtr cookie; // changed cookie from uint to IntPtr
private static ACTCTX enableThemingActivationContext;
private static IntPtr hActCtx;
private static bool contextCreationSucceeded = false;
public EnableThemingInScope(bool enable)
{
if (enable)
{
if (EnsureActivateContextCreated())
{
if (!ActivateActCtx(hActCtx, out cookie))
{
// Be sure cookie always zero if activation failed
cookie = IntPtr.Zero;
}
}
}
}
// Finalizer removed, that could cause Exceptions
// ~EnableThemingInScope()
// {
// Dispose(false);
// }
void IDisposable.Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (cookie != IntPtr.Zero)
{
if (DeactivateActCtx(0, cookie))
{
// deactivation succeeded...
cookie = IntPtr.Zero;
}
}
}
private bool EnsureActivateContextCreated()
{
lock (typeof(EnableThemingInScope))
{
if (!contextCreationSucceeded)
{
// Pull manifest from the .NET Framework install
// directory
string assemblyLoc = null;
FileIOPermission fiop = new FileIOPermission(PermissionState.None);
fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
fiop.Assert();
try
{
assemblyLoc = typeof(Object).Assembly.Location;
}
finally
{
CodeAccessPermission.RevertAssert();
}
string manifestLoc = null;
string installDir = null;
if (assemblyLoc != null)
{
installDir = Path.GetDirectoryName(assemblyLoc);
const string manifestName = "XPThemes.manifest";
manifestLoc = Path.Combine(installDir, manifestName);
}
if (manifestLoc != null && installDir != null)
{
enableThemingActivationContext = new ACTCTX();
enableThemingActivationContext.cbSize = Marshal.SizeOf(typeof(ACTCTX));
enableThemingActivationContext.lpSource = manifestLoc;
// Set the lpAssemblyDirectory to the install
// directory to prevent Win32 Side by Side from
// looking for comctl32 in the application
// directory, which could cause a bogus dll to be
// placed there and open a security hole.
enableThemingActivationContext.lpAssemblyDirectory = installDir;
enableThemingActivationContext.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;
// Note this will fail gracefully if file specified
// by manifestLoc doesn't exist.
hActCtx = CreateActCtx(ref enableThemingActivationContext);
contextCreationSucceeded = (hActCtx != new IntPtr(-1));
}
}
// If we return false, we'll try again on the next call into
// EnsureActivateContextCreated(), which is fine.
return contextCreationSucceeded;
}
}
// All the pinvoke goo...
[DllImport("Kernel32.dll")]
private extern static IntPtr CreateActCtx(ref ACTCTX actctx);
// changed from uint to IntPtr according to
// https://www.pinvoke.net/default.aspx/kernel32.ActiveActCtx
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);
// changed from uint to IntPtr according to
// https://www.pinvoke.net/default.aspx/kernel32.DeactivateActCtx
[DllImport("Kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeactivateActCtx(int dwFlags, IntPtr lpCookie);
private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;
private struct ACTCTX
{
public int cbSize;
public uint dwFlags;
public string lpSource;
public ushort wProcessorArchitecture;
public ushort wLangId;
public string lpAssemblyDirectory;
public string lpResourceName;
public string lpApplicationName;
}
}
}
I then used it that way:
using (new EnableThemingInScope(true))
{
// The call all this mucking about is here for.
VistaUnsafeNativeMethods.TaskDialogIndirect(ref config, out result, out radioButtonResult, out verificationFlagChecked);
}
in TaskDialogInterop.cs provided in WPF Task Dialog Wrapper on GitHub
For more info on possible SEHExceptions in the Finalizer of EnableThemingInScope see this Question on SO
Related
I've searched all over to try and find an answer to my predicament but cannot seem to find a valid answer. I'm trying to write some equivalent user32.dll code with Xlib instead so I can support Linux users. I'm of course running Linux, so I'm using Mono. Problem comes along when I cannot even grab a window handle from the Process class because it was never even implemented:
[MonoTODO]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
[MonitoringDescription ("The handle of the main window of the process.")]
public IntPtr MainWindowHandle {
get {
return((IntPtr)0);
}
}
(Source)
This is especially frustrating because there is seemingly no alternative. I'm trying to grab a window handle like so:
[DllImport("libX11")]
private static extern IntPtr XOpenDisplay(IntPtr display);
[DllImport("libX11")]
private static extern int XRaiseWindow(IntPtr display, IntPtr window);
private IntPtr ApplicationHandle;
private IntPtr Display;
private void TestXlib() {
Process process = Process.GetProcessById(myPid);
ApplicationHandle = process.MainWindowHandle;
Display = XOpenDisplay(IntPtr.Zero);
XRaiseWindow(Display, ApplicationHandle);
}
NOTE: In place of "myPid" is a proper process ID. Replace "myPid" with a valid process ID. Yes, I did make sure the replaced "myPid" was a valid process ID and my code didn't throw any errors indicating any process IDs I used as invalid.
This doesn't crash my application, but almost every time I call XRaiseWindow it prints:
X11 Error encountered:
Error: BadWindow (invalid Window parameter)
Request: 12 (0)
Resource ID: 0x0
Serial: 121
Hwnd: <null>
Control: <null>
This obviously occurs because Process.MainWindowHandle returns IntPtr.Zero. Is there no other way to get a window handle? Thanks in advance!
Yes, I know this was forever ago that I asked this, but I'm answering it now because I kept forgetting to answer it after I found the solution myself. I originally used #SushiHangover's solution but didn't really like it because I felt relying on an external program(xwininfo) was a hotfix and ultimately just added another dependency. Hopefully this helps other C# developers using Mono. This code was originally written for .NET Framework 2.0. It's not fancy and isn't really documented well. My solution was just to natively enumerate the windows using Xlib myself and return all windows whose title's match the described title.
In X11Wrapper.cs:
using System;
using System.Runtime.InteropServices;
namespace Program.PInvoke.Xlib {
public static class X11Wrapper {
public const string SOName = "libX11.so";
[DllImport(SOName)]
// See: https://tronche.com/gui/x/xlib/display/display-macros.html#DefaultRootWindow
public static extern IntPtr XDefaultRootWindow(IntPtr display);
[DllImport(SOName)]
// See: https://tronche.com/gui/x/xlib/window-information/XQueryTree.html
public static extern int XQueryTree(IntPtr display, IntPtr w,
out IntPtr root_return, out IntPtr parent_return,
out IntPtr[] children_return, out int nchildren_return);
[DllImport(SOName)]
// See: https://tronche.com/gui/x/xlib/ICC/client-to-window-manager/XFetchName.html
public static extern int XFetchName(IntPtr display, IntPtr w,
out string window_name_return);
}
}
In Linux.Utilities.cs:
using Program.PInvoke.Xlib;
namespace Program {
public static partial class Utilities {
public static bool IsUnix {
get {
return Environment.OSVersion.
Platform == PlatformID.Unix;
}
}
private static IntPtr[] FindChildWindows(IntPtr display, IntPtr window,
string title, ref List<IntPtr> windows) {
IntPtr rootWindow;
IntPtr parentWindow;
IntPtr[] childWindows = new IntPtr[0];
int childWindowsLength;
X11Wrapper.XQueryTree(display, window,
out rootWindow, out parentWindow,
out childWindows, out childWindowsLength);
childWindows = new IntPtr[childWindowsLength];
X11Wrapper.XQueryTree(display, window,
out rootWindow, out parentWindow,
out childWindows, out childWindowsLength);
string windowFetchedTitle;
X11Wrapper.XFetchName(display, window, out windowFetchedTitle);
if(title == windowFetchedTitle &&
!windows.Contains(window)) {
windows.Add(window);
}
for(int childWindowsIndexer = 0;
childWindowsIndexer < childWindows.Length;
childWindowsIndexer++) {
IntPtr childWindow = childWindows[childWindowsIndexer];
string childWindowFetchedTitle;
X11Wrapper.XFetchName(display, childWindow,
out childWindowFetchedTitle);
if(title == childWindowFetchedTitle &&
!windows.Contains(childWindow)) {
windows.Add(childWindow);
}
FindChildWindows(display, childWindow, title, ref windows);
}
windows.TrimExcess();
return windows.ToArray();
}
public static IntPtr[] FindWindows(IntPtr display, string title) {
List<IntPtr> windows = new List<IntPtr>();
return FindChildWindows(display,
X11Wrapper.XDefaultRootWindow(display),
title,
ref windows);
}
}
}
Footnote: I initially stated I wasn't a C developer(Things have changed since then and I've learned C) so I was hesitant to implement the functionality myself using interop. If you do end up using Xlib a lot more like I did then consider using tronche as an Xlib API reference. It is in C but I found it was pretty easy to translate to PInvokable functions and marshable structs in C#. Has some good notes to take into account too. Another good resource to help translation is directly using the source to find the definitions of the low level types to help find C# equivalents. Something like this should greatly aid you: http://refspecs.linuxbase.org/LSB_4.0.0/LSB-Desktop-generic/LSB-Desktop-generic/libx11-ddefs.html
I need to import SglW32.dll to my solution.
But I get:
AccessViolation exeption : Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.
I could not use just DllImport. In that case dll is not found.
This is whole example.
using System;
using System.Runtime.InteropServices;
namespace TestDllimport
{
class Program
{
static void Main(string[] args)
{
var a = new MyClass();
var result = a.getValue();
}
}
class FunctionLoader
{
[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string path);
[DllImport("Kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
public static Delegate LoadFunction<T>(string dllPath, string functionName)
{
var hModule = LoadLibrary(dllPath);
var functionAddress = GetProcAddress(hModule, functionName);
return Marshal.GetDelegateForFunctionPointer(functionAddress, typeof(T));
}
}
public class MyClass
{
//Define your path to dll.
//Get dll from: http://www.sg-lock.com/download/sglw32_v2_28.zip
private const string DLL_Path = #"C:\Users\admin123\Desktop\MyDlls\SglW32.dll";
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ulong SglAuthentA(IntPtr AuthentCode);
static MyClass()
{
sglAuthentA = (SglAuthentA)FunctionLoader.LoadFunction<SglAuthentA>(DLL_Path, "SglAuthentA");
}
static private SglAuthentA sglAuthentA;
unsafe public ulong getValue()
{
IntPtr d = new IntPtr(5);
var a1 = sglAuthentA(d); // Exception IS HERE !!!!!
return a1;
}
}
}
I am using load function to get dll from any path. After that I crate delegate from required function. In my case function is SglAuthentA.
This solution in working with one other dll, but not for SglW32.dll.
Product: http://www.sg-lock.com/us/
Required dll : http://www.sg-lock.com/download/sglw32_v2_28.zip
Manual: http://www.sg-lock.com/download/SG-Lock_Manual_Eng.pdf
Source 1: https://stackoverflow.com/a/8836228/2451446
EDIT: Solution thanks to Hans Passant answer and ja72 comment
See How to import dll
using System.Runtime.InteropServices;
namespace TestDllimport
{
class Program
{
static void Main(string[] args)
{
var testA = DllImportClass.SglAuthentA(new uint[] { 5, 6, 7 }, new uint[] { 5, 6, 7 }, new uint[] { 5, 6, 7 });
var testB = DllImportClass.SglAuthentB(new uint[] { 5, 6, 7 });
}
}
static class DllImportClass
{
[DllImport("SglW32.dll")]
public static extern uint SglAuthentA(uint[] AuthentCode0, uint[] AuthentCode1, uint[] AuthentCode2);
[DllImport("SglW32.dll")]
public static extern uint SglAuthentB(uint[] AuthentCode);
}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate ulong SglAuthentA(IntPtr AuthentCode);
The delegate declaration is not correct and does not match the api function signature. An ULONG in C is an uint in C#. An ULONG* in C is ambiguous, could be a ref uint or it could be a uint[]. Since you are supposed to pass a 48 byte authentication code, you know it is an array. Fix:
private delegate uint SglAuthentA(uint[] authentCode);
Be sure to pass the proper authentication code. It is not 5, the array must have 12 elements. If you don't have one then call the manufacturer to acquire one.
private const string DLL_Path = #"C:\Users\admin123\Desktop\MyDlls\SglW32.dll";
Do beware that this is not a workaround for not being able to use [DllImport]. Hardcoding the path is a problem, the file is not going to be present in that directory on the user's machine. The DLL itself does not have any dependencies that prevents it from loading, the only plausible reason for having trouble is you just forgetting to copy the DLL into the proper place. There is only one such place, the same directory as your EXE.
Fix this the right way, use Project > Add Existing Item > select the DLL. Select the added file in the Solution Explorer window. In the Properties window, change the Copy to Output Directory setting to "Copy if newer". Rebuild your project and note that you'll now get the DLL in your project's bin\Debug directory. Now [DllImport] will work.
A caution about the manual, it lists code samples in Visual Basic. Which is in general what you'd normally use as a guide on learning how to use the api. The code is however not VB.NET code, it is VB6 code. Where ever you see Long in the sample code, you should use uint or int instead.
Very sloppy, it casts a big question mark on the quality of the product. Something else they don't seem to address at all is how to get your own code secure. Very important when you use a dongle. Beware it is very trivial for anybody to reverse-engineer your authentication code. And worse, to decompile your program and remove the authentication check. You need to use an obfuscator.
To get stuck in straight away, a very basic example:
using System;
using System.Windows.Forms;
class test
{
static void Main()
{
Console.WriteLine("test");
MessageBox.Show("test");
}
}
If I compile this with default options (using csc at command line), as expected, it will compile to a console application. Also, because I imported System.Windows.Forms, it will also show a message box.
Now, if I use the option /target:winexe, which I think is the same as choosing Windows Application from within project options, as expected I will only see the Message Box and no console output.
(In fact, the moment it is launched from command line, I can issue the next command before the application has even completed).
So, my question is - I know that you can have "windows"/forms output from a console application, but is there anyway to show the console from a Windows application?
this one should work.
using System.Runtime.InteropServices;
private void Form1_Load(object sender, EventArgs e)
{
AllocConsole();
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
Perhaps this is over-simplistic...
Create a Windows Form project...
Then: Project Properties -> Application -> Output Type -> Console Application
Then can have Console and Forms running together, works for me
If you are not worrying about opening a console on-command, you can go into the properties for your project and change it to Console Application
.
This will still show your form as well as popping up a console window. You can't close the console window, but it works as an excellent temporary logger for debugging.
Just remember to turn it back off before you deploy the program.
You can call AttachConsole using pinvoke to get a console window attached to a WinForms project: http://www.csharp411.com/console-output-from-winforms-application/
You may also want to consider Log4net ( http://logging.apache.org/log4net/index.html ) for configuring log output in different configurations.
Create a Windows Forms Application, and change the output type to Console.
It will result in both a console and the form to open.
This worked for me, to pipe the output to a file.
Call the console with
cmd /c "C:\path\to\your\application.exe" > myfile.txt
Add this code to your application.
[DllImport("kernel32.dll")]
static extern bool AttachConsole(UInt32 dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool GetFileInformationByHandle(
SafeFileHandle hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
[DllImport("kernel32.dll")]
private static extern SafeFileHandle GetStdHandle(UInt32 nStdHandle);
[DllImport("kernel32.dll")]
private static extern bool SetStdHandle(UInt32 nStdHandle, SafeFileHandle hHandle);
[DllImport("kernel32.dll")]
private static extern bool DuplicateHandle(
IntPtr hSourceProcessHandle,
SafeFileHandle hSourceHandle,
IntPtr hTargetProcessHandle,
out SafeFileHandle lpTargetHandle,
UInt32 dwDesiredAccess,
Boolean bInheritHandle,
UInt32 dwOptions
);
private const UInt32 ATTACH_PARENT_PROCESS = 0xFFFFFFFF;
private const UInt32 STD_OUTPUT_HANDLE = 0xFFFFFFF5;
private const UInt32 STD_ERROR_HANDLE = 0xFFFFFFF4;
private const UInt32 DUPLICATE_SAME_ACCESS = 2;
struct BY_HANDLE_FILE_INFORMATION
{
public UInt32 FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public UInt32 VolumeSerialNumber;
public UInt32 FileSizeHigh;
public UInt32 FileSizeLow;
public UInt32 NumberOfLinks;
public UInt32 FileIndexHigh;
public UInt32 FileIndexLow;
}
static void InitConsoleHandles()
{
SafeFileHandle hStdOut, hStdErr, hStdOutDup, hStdErrDup;
BY_HANDLE_FILE_INFORMATION bhfi;
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
hStdErr = GetStdHandle(STD_ERROR_HANDLE);
// Get current process handle
IntPtr hProcess = Process.GetCurrentProcess().Handle;
// Duplicate Stdout handle to save initial value
DuplicateHandle(hProcess, hStdOut, hProcess, out hStdOutDup,
0, true, DUPLICATE_SAME_ACCESS);
// Duplicate Stderr handle to save initial value
DuplicateHandle(hProcess, hStdErr, hProcess, out hStdErrDup,
0, true, DUPLICATE_SAME_ACCESS);
// Attach to console window – this may modify the standard handles
AttachConsole(ATTACH_PARENT_PROCESS);
// Adjust the standard handles
if (GetFileInformationByHandle(GetStdHandle(STD_OUTPUT_HANDLE), out bhfi))
{
SetStdHandle(STD_OUTPUT_HANDLE, hStdOutDup);
}
else
{
SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
}
if (GetFileInformationByHandle(GetStdHandle(STD_ERROR_HANDLE), out bhfi))
{
SetStdHandle(STD_ERROR_HANDLE, hStdErrDup);
}
else
{
SetStdHandle(STD_ERROR_HANDLE, hStdErr);
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// initialize console handles
InitConsoleHandles();
if (args.Length != 0)
{
if (args[0].Equals("waitfordebugger"))
{
MessageBox.Show("Attach the debugger now");
}
if (args[0].Equals("version"))
{
#if DEBUG
String typeOfBuild = "d";
#else
String typeOfBuild = "r";
#endif
String output = typeOfBuild + Assembly.GetExecutingAssembly()
.GetName().Version.ToString();
//Just for the fun of it
Console.Write(output);
Console.Beep(4000, 100);
Console.Beep(2000, 100);
Console.Beep(1000, 100);
Console.Beep(8000, 100);
return;
}
}
}
I found this code here: http://www.csharp411.com/console-output-from-winforms-application/
I thought is was worthy to post it here as well.
There are basically two things that can happen here.
Console output
It is possible for a winforms program to attach itself to the console window that created it (or to a different console window, or indeed to a new console window if desired). Once attached to the console window Console.WriteLine() etc works as expected. One gotcha to this approach is that the program returns control to the console window immediately, and then carries on writing to it, so the user can also type away in the console window. You can use start with the /wait parameter to handle this I think.
Link to start Command syntax
Redirected console output
This is when someone pipes the output from your program somewhere else, eg.
yourapp > file.txt
Attaching to a console window in this case effectively ignores the piping. To make this work you can call Console.OpenStandardOutput() to get a handle to the stream that the output should be piped to. This only works if the output is piped, so if you want to handle both of the scenarios you need to open the standard output and write to it and attach to the console window. This does mean that the output is sent to the console window and to the pipe but its the best solution I could find. Below the code I use to do this.
// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
StreamWriter _stdOutWriter;
// this must be called early in the program
public GUIConsoleWriter()
{
// this needs to happen before attachconsole.
// If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
// I guess it probably does write somewhere, but nowhere I can find out about
var stdout = Console.OpenStandardOutput();
_stdOutWriter = new StreamWriter(stdout);
_stdOutWriter.AutoFlush = true;
AttachConsole(ATTACH_PARENT_PROCESS);
}
public void WriteLine(string line)
{
_stdOutWriter.WriteLine(line);
Console.WriteLine(line);
}
}
//From your application set the Console to write to your RichTextkBox
//object:
Console.SetOut(new RichTextBoxWriter(yourRichTextBox));
//To ensure that your RichTextBox object is scrolled down when its text is
//changed add this event:
private void yourRichTextBox_TextChanged(object sender, EventArgs e)
{
yourRichTextBox.SelectionStart = yourRichTextBox.Text.Length;
yourRichTextBox.ScrollToCaret();
}
public delegate void StringArgReturningVoidDelegate(string text);
public class RichTextBoxWriter : TextWriter
{
private readonly RichTextBox _richTextBox;
public RichTextBoxWriter(RichTextBox richTexttbox)
{
_richTextBox = richTexttbox;
}
public override void Write(char value)
{
SetText(value.ToString());
}
public override void Write(string value)
{
SetText(value);
}
public override void WriteLine(char value)
{
SetText(value + Environment.NewLine);
}
public override void WriteLine(string value)
{
SetText(value + Environment.NewLine);
}
public override Encoding Encoding => Encoding.ASCII;
//Write to your UI object in thread safe way:
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (_richTextBox.InvokeRequired)
{
var d = new StringArgReturningVoidDelegate(SetText);
_richTextBox.Invoke(d, text);
}
else
{
_richTextBox.Text += text;
}
}
}
Building on Chaz's answer, in .NET 5 there is a breaking change, so two modifications are required in the project file, i.e. changing OutputType and adding DisableWinExeOutputInference. Example:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0-windows10.0.17763.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<Platforms>AnyCPU;x64;x86</Platforms>
</PropertyGroup>
using System;
using System.Runtime.InteropServices;
namespace SomeProject
{
class GuiRedirect
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(StandardHandle nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetStdHandle(StandardHandle nStdHandle, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern FileType GetFileType(IntPtr handle);
private enum StandardHandle : uint
{
Input = unchecked((uint)-10),
Output = unchecked((uint)-11),
Error = unchecked((uint)-12)
}
private enum FileType : uint
{
Unknown = 0x0000,
Disk = 0x0001,
Char = 0x0002,
Pipe = 0x0003
}
private static bool IsRedirected(IntPtr handle)
{
FileType fileType = GetFileType(handle);
return (fileType == FileType.Disk) || (fileType == FileType.Pipe);
}
public static void Redirect()
{
if (IsRedirected(GetStdHandle(StandardHandle.Output)))
{
var initialiseOut = Console.Out;
}
bool errorRedirected = IsRedirected(GetStdHandle(StandardHandle.Error));
if (errorRedirected)
{
var initialiseError = Console.Error;
}
AttachConsole(-1);
if (!errorRedirected)
SetStdHandle(StandardHandle.Error, GetStdHandle(StandardHandle.Output));
}
}
Setting the output type as Console in the project properties will give you a Console application along with the form you created.
if what you want is simple debug output the following works for me. I am using VS 2022 programming in C#
add "using System.Diagnostics"
then
Debug.WriteLine("*****");
Debug.WriteLine(...);
Debug.WriteLine("");
THe output appears in the debug console of VS2022. There is a lot of stuff there so I use the Debug.WriteLine("*****") and Debug.WriteLine("") to help me find my output. You can also clear the debug output after start up.
I am still working but running under VS there is no output when running wo debugging
Why not just leave it as a Window Forms app, and create a simple form to mimic the Console. The form can be made to look just like the black-screened Console, and have it respond directly to key press.
Then, in the program.cs file, you decide whether you need to Run the main form or the ConsoleForm. For example, I use this approach to capture the command line arguments in the program.cs file. I create the ConsoleForm, initially hide it, then pass the command line strings to an AddCommand function in it, which displays the allowed commands. Finally, if the user gave the -h or -? command, I call the .Show on the ConsoleForm and when the user hits any key on it, I shut down the program. If the user doesn't give the -? command, I close the hidden ConsoleForm and Run the main form.
You can any time switch between type of applications, to console or windows. So, you will not write special logic to see the stdout. Also, when running application in debugger, you will see all the stdout in output window. You might also just add a breakpoint, and in breakpoint properties change "When Hit...", you can output any messages, and variables. Also you can check/uncheck "Continue execution", and your breakpoint will become square shaped. So, the breakpoint messages without changhing anything in the application in the debug output window.
I have to stop windows from going into sleep when my program is running.
And I don't only want to prevent the sleep-timer, I also want to cancel the sleep-event if I press the sleep-button or in any other way actively tell the computer to sleep. Therefore SetThreadExecutionState is not enough.
Or...I don't actually have to prevent the sleep completely, only delay it 5-10sec to allow my program to finish a task.
(I know that this is bad program behavior but it's only for personal use.)
After considering vim's answer
"Using PowerCreateRequest, PowerSetRequest, and PowerClearRequest
functions is the preferred method."
with the linked AvailabilityRequests.docx on msdn which is exhausting to get into it (too much to read), I have searched the web for a concrete example in c# that is based on the PowerCreateRequest and found http://go4answers.webhost4life.com/Example/problem-monitor-wakeup-service-windows7-12092.aspx [EDIT 2016 - isn't available anymore]
Copied and adapted it to my needs (PInvoke of CloseHandle copied from msdn):
using System.Runtime.InteropServices;
#region prevent screensaver, display dimming and automatically sleeping
POWER_REQUEST_CONTEXT _PowerRequestContext;
IntPtr _PowerRequest; //HANDLE
// Availability Request Functions
[DllImport("kernel32.dll")]
static extern IntPtr PowerCreateRequest(ref POWER_REQUEST_CONTEXT Context);
[DllImport("kernel32.dll")]
static extern bool PowerSetRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);
[DllImport("kernel32.dll")]
static extern bool PowerClearRequest(IntPtr PowerRequestHandle, PowerRequestType RequestType);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
internal static extern int CloseHandle(IntPtr hObject);
// Availablity Request Enumerations and Constants
enum PowerRequestType
{
PowerRequestDisplayRequired = 0,
PowerRequestSystemRequired,
PowerRequestAwayModeRequired,
PowerRequestMaximum
}
const int POWER_REQUEST_CONTEXT_VERSION = 0;
const int POWER_REQUEST_CONTEXT_SIMPLE_STRING = 0x1;
const int POWER_REQUEST_CONTEXT_DETAILED_STRING = 0x2;
// Availablity Request Structures
// Note: Windows defines the POWER_REQUEST_CONTEXT structure with an
// internal union of SimpleReasonString and Detailed information.
// To avoid runtime interop issues, this version of
// POWER_REQUEST_CONTEXT only supports SimpleReasonString.
// To use the detailed information,
// define the PowerCreateRequest function with the first
// parameter of type POWER_REQUEST_CONTEXT_DETAILED.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct POWER_REQUEST_CONTEXT
{
public UInt32 Version;
public UInt32 Flags;
[MarshalAs(UnmanagedType.LPWStr)]
public string
SimpleReasonString;
}
[StructLayout(LayoutKind.Sequential)]
public struct PowerRequestContextDetailedInformation
{
public IntPtr LocalizedReasonModule;
public UInt32 LocalizedReasonId;
public UInt32 ReasonStringCount;
[MarshalAs(UnmanagedType.LPWStr)]
public string[] ReasonStrings;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct POWER_REQUEST_CONTEXT_DETAILED
{
public UInt32 Version;
public UInt32 Flags;
public PowerRequestContextDetailedInformation DetailedInformation;
}
#endregion
/// <summary>
/// Prevent screensaver, display dimming and power saving. This function wraps PInvokes on Win32 API.
/// </summary>
/// <param name="enableConstantDisplayAndPower">True to get a constant display and power - False to clear the settings</param>
private void EnableConstantDisplayAndPower(bool enableConstantDisplayAndPower)
{
if (enableConstantDisplayAndPower)
{
// Set up the diagnostic string
_PowerRequestContext.Version = POWER_REQUEST_CONTEXT_VERSION;
_PowerRequestContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
_PowerRequestContext.SimpleReasonString = "Continuous measurement"; // your reason for changing the power settings;
// Create the request, get a handle
_PowerRequest = PowerCreateRequest(ref _PowerRequestContext);
// Set the request
PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
PowerSetRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);
}
else
{
// Clear the request
PowerClearRequest(_PowerRequest, PowerRequestType.PowerRequestSystemRequired);
PowerClearRequest(_PowerRequest, PowerRequestType.PowerRequestDisplayRequired);
CloseHandle(_PowerRequest);
}
}
I had a problem like this with a hardware device connected via usb. XP /Vista would sleep/hibernate right in the middle of ... Great you say, when it resumes it can just continue. If the hardware is still connected!!!
Users have the habit of pulling cables out whenever they feel like it.
You need to handle XP and Vista
Under XP trap the WM_POWERBROADCAST and look for the PBT_APMQUERYSUSPEND wparam.
// See if bit 1 is set, this means that you can send a deny while we are busy
if (message.LParam & 0x1)
{
// send the deny message
return BROADCAST_QUERY_DENY;
} // if
else
{
return TRUE;
} // else
Under Vista use SetThreadExecutionState like this
// try this for vista, it will fail on XP
if (SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED) == NULL)
{
// try XP variant as well just to make sure
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
} // if
and when you app has finished set it back to normal
// set state back to normal
SetThreadExecutionState(ES_CONTINUOUS);
Using PowerCreateRequest, PowerSetRequest, and PowerClearRequest functions is the preferred method. Details and sample code (C/C#) are inside http://msdn.microsoft.com/en-us/library/windows/hardware/gg463205.aspx
set wsc = CreateObject("WScript.Shell")
Do
WScript.Sleep (60*1000)
wsc.SendKeys ("{SCROLLLOCK 2}")
Loop
-put the above code in notepad and save the file as .vbs and double click the file
The same technique applies as for preventing the screensaver should be used. See Programmatically prevent Windows screensaver from starting.
Note that some security settings can override this (forcing computers to lock after a certain time is one).
If you need a display that should work until your application is running then try to set "ES_DISPLAY_REQUIRED" instead of away mode:
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
Once you are done with your application make sure to clear all other flags.
SetThreadExecutionState(ES_CONTINUOUS);
How about waking it back up if it goes to sleep?
http://www.enterprisenetworksandservers.com/monthly/art.php?1049
Below is my attempt using the modern power availability request API (supersedes SetThreadExecutionState), as suggested by vim.
I'm using a nice P/Invoke NuGet I came across, Vanara.PInvoke.Kernel32:
using Vanara.PInvoke;
using static Vanara.PInvoke.Kernel32;
// create request object
using var request = PowerCreateRequest(new REASON_CONTEXT("App FOO is working"));
if (request.IsInvalid)
{
throw new InvalidOperationException(
$"Could not create power availability request: {Win32Error.GetLastError()}");
}
// send request
if (!PowerSetRequest(request, POWER_REQUEST_TYPE.PowerRequestSystemRequired))
{
throw new InvalidOperationException(
$"Could not send power availability request: {Win32Error.GetLastError()}");
}
// do stuff that required the machine to be up
Console.WriteLine("Doing stuff...");
await Task.Delay(5000);
// clear request
if (!PowerClearRequest(request, POWER_REQUEST_TYPE.PowerRequestSystemRequired))
{
Console.WriteLine(
"WARNING: Could not clear power availability request: {0}",
Win32Error.GetLastError());
}
You can see your request in action by issuing powercfg /requests from an admin terminal.
To get stuck in straight away, a very basic example:
using System;
using System.Windows.Forms;
class test
{
static void Main()
{
Console.WriteLine("test");
MessageBox.Show("test");
}
}
If I compile this with default options (using csc at command line), as expected, it will compile to a console application. Also, because I imported System.Windows.Forms, it will also show a message box.
Now, if I use the option /target:winexe, which I think is the same as choosing Windows Application from within project options, as expected I will only see the Message Box and no console output.
(In fact, the moment it is launched from command line, I can issue the next command before the application has even completed).
So, my question is - I know that you can have "windows"/forms output from a console application, but is there anyway to show the console from a Windows application?
this one should work.
using System.Runtime.InteropServices;
private void Form1_Load(object sender, EventArgs e)
{
AllocConsole();
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
Perhaps this is over-simplistic...
Create a Windows Form project...
Then: Project Properties -> Application -> Output Type -> Console Application
Then can have Console and Forms running together, works for me
If you are not worrying about opening a console on-command, you can go into the properties for your project and change it to Console Application
.
This will still show your form as well as popping up a console window. You can't close the console window, but it works as an excellent temporary logger for debugging.
Just remember to turn it back off before you deploy the program.
You can call AttachConsole using pinvoke to get a console window attached to a WinForms project: http://www.csharp411.com/console-output-from-winforms-application/
You may also want to consider Log4net ( http://logging.apache.org/log4net/index.html ) for configuring log output in different configurations.
Create a Windows Forms Application, and change the output type to Console.
It will result in both a console and the form to open.
This worked for me, to pipe the output to a file.
Call the console with
cmd /c "C:\path\to\your\application.exe" > myfile.txt
Add this code to your application.
[DllImport("kernel32.dll")]
static extern bool AttachConsole(UInt32 dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool GetFileInformationByHandle(
SafeFileHandle hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
[DllImport("kernel32.dll")]
private static extern SafeFileHandle GetStdHandle(UInt32 nStdHandle);
[DllImport("kernel32.dll")]
private static extern bool SetStdHandle(UInt32 nStdHandle, SafeFileHandle hHandle);
[DllImport("kernel32.dll")]
private static extern bool DuplicateHandle(
IntPtr hSourceProcessHandle,
SafeFileHandle hSourceHandle,
IntPtr hTargetProcessHandle,
out SafeFileHandle lpTargetHandle,
UInt32 dwDesiredAccess,
Boolean bInheritHandle,
UInt32 dwOptions
);
private const UInt32 ATTACH_PARENT_PROCESS = 0xFFFFFFFF;
private const UInt32 STD_OUTPUT_HANDLE = 0xFFFFFFF5;
private const UInt32 STD_ERROR_HANDLE = 0xFFFFFFF4;
private const UInt32 DUPLICATE_SAME_ACCESS = 2;
struct BY_HANDLE_FILE_INFORMATION
{
public UInt32 FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public UInt32 VolumeSerialNumber;
public UInt32 FileSizeHigh;
public UInt32 FileSizeLow;
public UInt32 NumberOfLinks;
public UInt32 FileIndexHigh;
public UInt32 FileIndexLow;
}
static void InitConsoleHandles()
{
SafeFileHandle hStdOut, hStdErr, hStdOutDup, hStdErrDup;
BY_HANDLE_FILE_INFORMATION bhfi;
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
hStdErr = GetStdHandle(STD_ERROR_HANDLE);
// Get current process handle
IntPtr hProcess = Process.GetCurrentProcess().Handle;
// Duplicate Stdout handle to save initial value
DuplicateHandle(hProcess, hStdOut, hProcess, out hStdOutDup,
0, true, DUPLICATE_SAME_ACCESS);
// Duplicate Stderr handle to save initial value
DuplicateHandle(hProcess, hStdErr, hProcess, out hStdErrDup,
0, true, DUPLICATE_SAME_ACCESS);
// Attach to console window – this may modify the standard handles
AttachConsole(ATTACH_PARENT_PROCESS);
// Adjust the standard handles
if (GetFileInformationByHandle(GetStdHandle(STD_OUTPUT_HANDLE), out bhfi))
{
SetStdHandle(STD_OUTPUT_HANDLE, hStdOutDup);
}
else
{
SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
}
if (GetFileInformationByHandle(GetStdHandle(STD_ERROR_HANDLE), out bhfi))
{
SetStdHandle(STD_ERROR_HANDLE, hStdErrDup);
}
else
{
SetStdHandle(STD_ERROR_HANDLE, hStdErr);
}
}
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// initialize console handles
InitConsoleHandles();
if (args.Length != 0)
{
if (args[0].Equals("waitfordebugger"))
{
MessageBox.Show("Attach the debugger now");
}
if (args[0].Equals("version"))
{
#if DEBUG
String typeOfBuild = "d";
#else
String typeOfBuild = "r";
#endif
String output = typeOfBuild + Assembly.GetExecutingAssembly()
.GetName().Version.ToString();
//Just for the fun of it
Console.Write(output);
Console.Beep(4000, 100);
Console.Beep(2000, 100);
Console.Beep(1000, 100);
Console.Beep(8000, 100);
return;
}
}
}
I found this code here: http://www.csharp411.com/console-output-from-winforms-application/
I thought is was worthy to post it here as well.
There are basically two things that can happen here.
Console output
It is possible for a winforms program to attach itself to the console window that created it (or to a different console window, or indeed to a new console window if desired). Once attached to the console window Console.WriteLine() etc works as expected. One gotcha to this approach is that the program returns control to the console window immediately, and then carries on writing to it, so the user can also type away in the console window. You can use start with the /wait parameter to handle this I think.
Link to start Command syntax
Redirected console output
This is when someone pipes the output from your program somewhere else, eg.
yourapp > file.txt
Attaching to a console window in this case effectively ignores the piping. To make this work you can call Console.OpenStandardOutput() to get a handle to the stream that the output should be piped to. This only works if the output is piped, so if you want to handle both of the scenarios you need to open the standard output and write to it and attach to the console window. This does mean that the output is sent to the console window and to the pipe but its the best solution I could find. Below the code I use to do this.
// This always writes to the parent console window and also to a redirected stdout if there is one.
// It would be better to do the relevant thing (eg write to the redirected file if there is one, otherwise
// write to the console) but it doesn't seem possible.
public class GUIConsoleWriter : IConsoleWriter
{
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
private static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
StreamWriter _stdOutWriter;
// this must be called early in the program
public GUIConsoleWriter()
{
// this needs to happen before attachconsole.
// If the output is not redirected we still get a valid stream but it doesn't appear to write anywhere
// I guess it probably does write somewhere, but nowhere I can find out about
var stdout = Console.OpenStandardOutput();
_stdOutWriter = new StreamWriter(stdout);
_stdOutWriter.AutoFlush = true;
AttachConsole(ATTACH_PARENT_PROCESS);
}
public void WriteLine(string line)
{
_stdOutWriter.WriteLine(line);
Console.WriteLine(line);
}
}
//From your application set the Console to write to your RichTextkBox
//object:
Console.SetOut(new RichTextBoxWriter(yourRichTextBox));
//To ensure that your RichTextBox object is scrolled down when its text is
//changed add this event:
private void yourRichTextBox_TextChanged(object sender, EventArgs e)
{
yourRichTextBox.SelectionStart = yourRichTextBox.Text.Length;
yourRichTextBox.ScrollToCaret();
}
public delegate void StringArgReturningVoidDelegate(string text);
public class RichTextBoxWriter : TextWriter
{
private readonly RichTextBox _richTextBox;
public RichTextBoxWriter(RichTextBox richTexttbox)
{
_richTextBox = richTexttbox;
}
public override void Write(char value)
{
SetText(value.ToString());
}
public override void Write(string value)
{
SetText(value);
}
public override void WriteLine(char value)
{
SetText(value + Environment.NewLine);
}
public override void WriteLine(string value)
{
SetText(value + Environment.NewLine);
}
public override Encoding Encoding => Encoding.ASCII;
//Write to your UI object in thread safe way:
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (_richTextBox.InvokeRequired)
{
var d = new StringArgReturningVoidDelegate(SetText);
_richTextBox.Invoke(d, text);
}
else
{
_richTextBox.Text += text;
}
}
}
Building on Chaz's answer, in .NET 5 there is a breaking change, so two modifications are required in the project file, i.e. changing OutputType and adding DisableWinExeOutputInference. Example:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0-windows10.0.17763.0</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<Platforms>AnyCPU;x64;x86</Platforms>
</PropertyGroup>
using System;
using System.Runtime.InteropServices;
namespace SomeProject
{
class GuiRedirect
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AttachConsole(int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetStdHandle(StandardHandle nStdHandle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetStdHandle(StandardHandle nStdHandle, IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern FileType GetFileType(IntPtr handle);
private enum StandardHandle : uint
{
Input = unchecked((uint)-10),
Output = unchecked((uint)-11),
Error = unchecked((uint)-12)
}
private enum FileType : uint
{
Unknown = 0x0000,
Disk = 0x0001,
Char = 0x0002,
Pipe = 0x0003
}
private static bool IsRedirected(IntPtr handle)
{
FileType fileType = GetFileType(handle);
return (fileType == FileType.Disk) || (fileType == FileType.Pipe);
}
public static void Redirect()
{
if (IsRedirected(GetStdHandle(StandardHandle.Output)))
{
var initialiseOut = Console.Out;
}
bool errorRedirected = IsRedirected(GetStdHandle(StandardHandle.Error));
if (errorRedirected)
{
var initialiseError = Console.Error;
}
AttachConsole(-1);
if (!errorRedirected)
SetStdHandle(StandardHandle.Error, GetStdHandle(StandardHandle.Output));
}
}
Setting the output type as Console in the project properties will give you a Console application along with the form you created.
if what you want is simple debug output the following works for me. I am using VS 2022 programming in C#
add "using System.Diagnostics"
then
Debug.WriteLine("*****");
Debug.WriteLine(...);
Debug.WriteLine("");
THe output appears in the debug console of VS2022. There is a lot of stuff there so I use the Debug.WriteLine("*****") and Debug.WriteLine("") to help me find my output. You can also clear the debug output after start up.
I am still working but running under VS there is no output when running wo debugging
Why not just leave it as a Window Forms app, and create a simple form to mimic the Console. The form can be made to look just like the black-screened Console, and have it respond directly to key press.
Then, in the program.cs file, you decide whether you need to Run the main form or the ConsoleForm. For example, I use this approach to capture the command line arguments in the program.cs file. I create the ConsoleForm, initially hide it, then pass the command line strings to an AddCommand function in it, which displays the allowed commands. Finally, if the user gave the -h or -? command, I call the .Show on the ConsoleForm and when the user hits any key on it, I shut down the program. If the user doesn't give the -? command, I close the hidden ConsoleForm and Run the main form.
You can any time switch between type of applications, to console or windows. So, you will not write special logic to see the stdout. Also, when running application in debugger, you will see all the stdout in output window. You might also just add a breakpoint, and in breakpoint properties change "When Hit...", you can output any messages, and variables. Also you can check/uncheck "Continue execution", and your breakpoint will become square shaped. So, the breakpoint messages without changhing anything in the application in the debug output window.