Is there a way to write to console / command prompt / powershell (like Console.WriteLine()) or anything similar in UWP apps?
If console is unavailable is there a proper alternative that I can use instead to write to the screen large amounts of text?
I, of course, can make a XAML control and output to it, but it doesn't seem to be convenient in comparison to simple Console.WriteLine().
There is also very old discussion on WPF console, but nothing seems to work from there (at least, I couldn't find Project-Properties-Application tab-Output Type-Console Application and Trace.WriteLine("text") is unavailable).
You can use Debug.WriteLine method from System.Diagnostics namespace
MSDN Link
When you start debugging your application those messages will be displayed in the Output Window (Standard VS shortcut is Ctrl+Alt+O, ReSharper shortcut is Ctrl+W, O)
Starting with RS4 (the release coming out mid-2018) you can build command - line apps with UWP or output info to the command line. The pre-release SDK is already available and you can watch a Channel 9 video.
You can use the LoggingChannel class. to create ETW trace events.
The cool thing with LoggingChannel is you can do sophisticated traces (and use advanced tools like PerfView, etc.), but you can also have a simple equivalent of Debug.WriteLine in terms of simplicity with the LoggingChannel.LogMessage Method
public void LogMessage(String eventString)
or
public void LogMessage(String eventString, LoggingLevel level)
This has numerous advantages over Debug.WriteLine:
it's much faster, you can log millions of messages easily, while Debug.WriteLine is dog slow (based on the archaic Windows' OutputDebugString function).
it doesn't block the sender nor the receiver.
each channel is identified by its own guid, while with Debug.WriteLine you get all traces from everywhere, everyone, it's a bit messy to find your own ones.
you can use a trace level (Critical, Error, Information, Verbose, Warning)
you can use PerfView (if you really want to) or Device Portal or any other ETW tool.
So, to send some traces, just add this:
// somewhere in your initialization code, like in `App` constructor
private readonly static LoggingChannel _channel = new LoggingChannel("MyApp",
new LoggingChannelOptions(),
new Guid("01234567-01234-01234-01234-012345678901")); // change this guid, it's yours!
....
// everywhere in your code. add simple string traces like this
_channel.LogMessage("hello from UWP!");
....
Now, if you want a simple way to display those traces on your local machine, beyond using PerfView or other ETW tools, you can use a free open source GUI tool I write called WpfTraceSpy available here: https://github.com/smourier/TraceSpy#wpftracespy or here is a sample .NET Framework Console app that will output all traces and their level to the console:
using System;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Tracing; // you need to add the Microsoft.Diagnostics.Tracing.TraceEvent nuget package
using Microsoft.Diagnostics.Tracing.Session;
namespace TraceTest
{
class Program
{
static void Main()
{
// create a real time user mode session
using (var session = new TraceEventSession("MySession"))
{
// use UWP logging channel provider
session.EnableProvider(new Guid("01234567-01234-01234-01234-012345678901")); // use the same guid as for your LoggingChannel
session.Source.AllEvents += Source_AllEvents;
// Set up Ctrl-C to stop the session
Console.CancelKeyPress += (object s, ConsoleCancelEventArgs a) => session.Stop();
session.Source.Process(); // Listen (forever) for events
}
}
private static void Source_AllEvents(TraceEvent obj)
{
// note: this is for the LoggingChannel.LogMessage Method only! you may crash with other providers or methods
var len = (int)(ushort)Marshal.ReadInt16(obj.DataStart);
var stringMessage = Marshal.PtrToStringUni(obj.DataStart + 2, len / 2);
// Output the event text message. You could filter using level.
// TraceEvent also contains a lot of useful informations (timing, process, etc.)
Console.WriteLine(obj.Level + ":" + stringMessage);
}
}
}
I just want to also add that debug.writeline seems to best work on the main thread so if async/await is used use something like Device.BeginInvokeOnMainThread(() => Debug.WriteLine(response)); to print to the console
Related
Is there a way to write to console / command prompt / powershell (like Console.WriteLine()) or anything similar in UWP apps?
If console is unavailable is there a proper alternative that I can use instead to write to the screen large amounts of text?
I, of course, can make a XAML control and output to it, but it doesn't seem to be convenient in comparison to simple Console.WriteLine().
There is also very old discussion on WPF console, but nothing seems to work from there (at least, I couldn't find Project-Properties-Application tab-Output Type-Console Application and Trace.WriteLine("text") is unavailable).
You can use Debug.WriteLine method from System.Diagnostics namespace
MSDN Link
When you start debugging your application those messages will be displayed in the Output Window (Standard VS shortcut is Ctrl+Alt+O, ReSharper shortcut is Ctrl+W, O)
Starting with RS4 (the release coming out mid-2018) you can build command - line apps with UWP or output info to the command line. The pre-release SDK is already available and you can watch a Channel 9 video.
You can use the LoggingChannel class. to create ETW trace events.
The cool thing with LoggingChannel is you can do sophisticated traces (and use advanced tools like PerfView, etc.), but you can also have a simple equivalent of Debug.WriteLine in terms of simplicity with the LoggingChannel.LogMessage Method
public void LogMessage(String eventString)
or
public void LogMessage(String eventString, LoggingLevel level)
This has numerous advantages over Debug.WriteLine:
it's much faster, you can log millions of messages easily, while Debug.WriteLine is dog slow (based on the archaic Windows' OutputDebugString function).
it doesn't block the sender nor the receiver.
each channel is identified by its own guid, while with Debug.WriteLine you get all traces from everywhere, everyone, it's a bit messy to find your own ones.
you can use a trace level (Critical, Error, Information, Verbose, Warning)
you can use PerfView (if you really want to) or Device Portal or any other ETW tool.
So, to send some traces, just add this:
// somewhere in your initialization code, like in `App` constructor
private readonly static LoggingChannel _channel = new LoggingChannel("MyApp",
new LoggingChannelOptions(),
new Guid("01234567-01234-01234-01234-012345678901")); // change this guid, it's yours!
....
// everywhere in your code. add simple string traces like this
_channel.LogMessage("hello from UWP!");
....
Now, if you want a simple way to display those traces on your local machine, beyond using PerfView or other ETW tools, you can use a free open source GUI tool I write called WpfTraceSpy available here: https://github.com/smourier/TraceSpy#wpftracespy or here is a sample .NET Framework Console app that will output all traces and their level to the console:
using System;
using System.Runtime.InteropServices;
using Microsoft.Diagnostics.Tracing; // you need to add the Microsoft.Diagnostics.Tracing.TraceEvent nuget package
using Microsoft.Diagnostics.Tracing.Session;
namespace TraceTest
{
class Program
{
static void Main()
{
// create a real time user mode session
using (var session = new TraceEventSession("MySession"))
{
// use UWP logging channel provider
session.EnableProvider(new Guid("01234567-01234-01234-01234-012345678901")); // use the same guid as for your LoggingChannel
session.Source.AllEvents += Source_AllEvents;
// Set up Ctrl-C to stop the session
Console.CancelKeyPress += (object s, ConsoleCancelEventArgs a) => session.Stop();
session.Source.Process(); // Listen (forever) for events
}
}
private static void Source_AllEvents(TraceEvent obj)
{
// note: this is for the LoggingChannel.LogMessage Method only! you may crash with other providers or methods
var len = (int)(ushort)Marshal.ReadInt16(obj.DataStart);
var stringMessage = Marshal.PtrToStringUni(obj.DataStart + 2, len / 2);
// Output the event text message. You could filter using level.
// TraceEvent also contains a lot of useful informations (timing, process, etc.)
Console.WriteLine(obj.Level + ":" + stringMessage);
}
}
}
I just want to also add that debug.writeline seems to best work on the main thread so if async/await is used use something like Device.BeginInvokeOnMainThread(() => Debug.WriteLine(response)); to print to the console
I have a C# application which in turn loads a C or C++ dll (which in turn loads other C/C++ dlls). Within the C# application I use a log4net logger to capture all the output into a series of log files. My application runs as a windows service so there is no console/output window for normal printfs or output written into stdout/stderr to go to.
Is there a way to setup the C# application to direct stdout/stderr (from the DLLs) and turn each line into a log4net output. Or is there some way within the C/C++ DLL to connect the stdout/stderr streams to the log4net output?
I found some solution (here : http://bytes.com/topic/c-sharp/answers/822341-dllimport-stdout-gets-eaten) that indicated I needed to put a call into my C DLL like this : setvbuf(stdout, NULL, _IONBF, 0); Though, I don't know what that does, it doesn't do what I want. I assume I'd also need a similar line for stderr. In either case, google seemed to think those lines simply take care of buffering and not redirection into log4net.
I assume I need some sort of function override which snags the console writes (from a loaded DLL in another language) and converts them into mLog.InfoFormat("{0}", consoleString); sorts of calls. I'm new to c# and not even sure what terms to google in order to find such an override (if its even possible).
Not sure if this complicates the problem, but my C# application is multithreaded and some of the DLLs have multiple threads as well. I assume that just means I need a lock of some sort inside the method that handles the console output and writes it into the log4net framework(maybe) or maybe the normal serialization of log4net will handle it for me.
Turns out those did the trick once I figured out how to use them. I setup two named pipes(or two ends of the same pipe?). One I connected to stdout and had it do a log message in log4net of whatever came through the pipe.
internal static void InfoLogWriter(Object threadContext)
{
mLog.Info("InfoLogWriterthread started");
int id = Process.GetCurrentProcess().Id; // make this instance unique
var serverPipe = new NamedPipeServerStream("consoleRedirect" + id, PipeDirection.In, 1);
NamedPipeClientStream clientPipe = new NamedPipeClientStream(".", "consoleRedirect" + id, PipeDirection.Out, PipeOptions.WriteThrough);
mLog.Info("Connecting Client Pipe.");
clientPipe.Connect();
mLog.Info("Connected Client Pipe, redirecting stdout");
HandleRef hr11 = new HandleRef(clientPipe, clientPipe.SafePipeHandle.DangerousGetHandle());
SetStdHandle(-11, hr11.Handle); // redirect stdout to my pipe
mLog.Info("Redirection of stdout complete.");
mLog.Info("Waiting for console connection");
serverPipe.WaitForConnection(); //blocking
mLog.Info("Console connection made.");
using (var stm = new StreamReader(serverPipe))
{
while (serverPipe.IsConnected)
{
try
{
string txt = stm.ReadLine();
if (!string.IsNullOrEmpty(txt))
mLog.InfoFormat("DLL MESSAGE : {0}", txt);
}
catch (IOException)
{
break; // normal disconnect
}
}
}
mLog.Info("Console connection broken. Thread Stopping.");
}
Also have a function to push all that to another thread so it doesn't block my main thread when it hits the various blocking calls.
internal static void RedirectConsole()
{
mLog.Info("RedirectConsole called.");
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(InfoLogWriter));
// TODO enqueue and item for error messages too.
}
I'm having trouble with it disconnecting and have to reconnect the pipes, but I'll figure out a reconnect solution. I'm guessing that happens when DLLs get swapped back out of memory or perhaps when I need to read but there isn't anything currently ready to be read? I've also got to setup another pair to snag stderr and redirect it as well, using Error logs for that one. Probably want to get rid of the magic number (-11) and use the normal enums as well (STD_ERROR_HANDLE, etc)
If I have these sprinkled throughout my code:
MessageBox.Show("See a format exception yet? #1");//todo: remove
(there are 7 of these, numbered from 1..7, most of which display (1,2,5,6,7))
I end up with one err msg ("Exception: Cannot find table 0 Location: frmFunction.SetPlatypus")
If I comment out all of those, I end up with a different err msg ("Exception: FormatException Location frmFunction.getDuckbillRecord")
How could this be? Shouldn't the existence/display of such an information msg have no effect on the way the code executes/the path it takes, etc.?
Note: getDuckbillRecord() is where all of the MessageBoxes are.
UPDATE
Using RT's suggestions as motivation, I came up with this:
public static StringBuilder LogMsgs = new StringBuilder();
public static void ExceptionHandler(Exception ex, string location)
{
try
{
LogMsgs.Append(string.Format("{0}\r\n", ex.Message)); //TODO: Comment out before deploying?
DateTime dt = DateTime.Now;
string timeAsStr = string.Format("{0}_{1}_{2}_{3}.txt", dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
using (StreamWriter file = new StreamWriter(timeAsStr))
{
file.WriteLine(LogMsgs.ToString());
}
. . .
//in the problematic code, replacing the MessageBox.Show() calls:
TTBT.LogMsgs.Append("Made it to location 1 in frmOverFunction.GetDuckbillRecord()\r\n");
...and it did help - the exception is reached right after the first of 7 "code waypoints," so there's something rotten in that particular Denmark, apparently.
UPDATE 2
After the rare experience of being able to run the app without crashing somewhere, I realized I also need that file-writing code in the main form's OnClosing() event - being in the global exception handler had worked for awhile - I wasn't expecting a clean termination of the app to ever occur again, I guess.
I'd strongly encourage you NOT to use MessageBox.Show(...) to log/trace/debug errors, because popping-up a dialog can introduce many subtle bugs and changes in behavior, focus, etc., especially if your UI flow/state is important.
Instead, use System.Diagnostics.Trace for writing tracing code (which you can optionally compile into your production/release code) or System.Diagnostics.Debug to write debug messages which get removed when you build your release version.
For example:
public void DoSomething(int a, int b)
{
Trace.TraceInformation("Starting to do something: a = {0}, b = {1}",
a, b);
try
{
int c = a / b;
}
catch (DivideByZeroException e)
{
Debug.WriteLine("OH NO ... 'b' WAS ZERO!!!! RUN AWAY!!!!");
throw e;
}
Trace.TraceInformation("Done doing something");
}
In the example above, the debug output will be available in debug builds, but will be removed in release builds. Trace output will be available in debug and retail bugs (unless you turn off the TRACE build parameter) but introduces no measurable perf impact unless you hook-up a listener and start writing trace output to a slow medium like spinning disks, ticker-tape printers, etc. ;)
When you run code with Trace and/or Debug builds with Debug messages in Visual Studio, the messages are written to the Output pane so you can watch what's going on inside your app as it runs.
If you want to, you can also write a stand-alone trace listener that listens for Trace and/or Debug messages and log them to a file/database or display them in a console window (or GUI if you want to get fancy).
You can also use tools like log4net which enables you to write log/trace messages to a variety of file formats, databases, etc.
Can we work together to come up with something that works for control-c, control-break, log off, window X button pressed, etc?
Here is what I have so far:
class Program
{
private static ConsoleEventHandlerDelegate consoleHandler;
delegate bool ConsoleEventHandlerDelegate(CtrlTypes eventCode);
static void Main(string[] args)
{
consoleHandler = new ConsoleEventHandlerDelegate(ConsoleCtrlCheck);
SetConsoleCtrlHandler(consoleHandler, true);
System.Diagnostics.Process.GetCurrentProcess().Exited
+= delegate(object sender, EventArgs e)
{
GeneralManager.Stop();
};
Console.CancelKeyPress += delegate(object sender,
ConsoleCancelEventArgs e)
{
e.Cancel = false;
GeneralManager.Stop();
};
GeneralManager.Start();
}
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
switch (ctrlType)
{
case CtrlTypes.CTRL_C_EVENT:
Console.WriteLine("CTRL+C received!");
GeneralManager.Stop();
break;
case CtrlTypes.CTRL_BREAK_EVENT:
isclosing = true;
Console.WriteLine("CTRL+BREAK received!");
GeneralManager.Stop();
break;
case CtrlTypes.CTRL_CLOSE_EVENT:
Console.WriteLine("Program being closed!");
GeneralManager.Stop();
break;
case CtrlTypes.CTRL_LOGOFF_EVENT:
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
Console.WriteLine("User is logging off!");
GeneralManager.Stop();
break;
}
return true;
}
#region unmanaged
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleEventHandlerDelegate
handlerProc, bool add);
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
#endregion
}
Two problems:
In the Managed Control-Break handler, if we set e.Cancel = true it fails with an exception for .Net4. This is noted in the MSDN article with no work-around: http://msdn.microsoft.com/en-us/library/system.consolecanceleventargs.cancel.aspx
I don't know how to cancel the close in the ConsoleCtrlCheck. I get a second or two to do some cleanup, but I'd rather cancel and make sure it all gets done properly.
UPDATE:
Thanks for the replies. Upvoted both. Will wait to see if anyone can come up with a reply that directly solves what I asked for, otherwise will accept one of the "use NT services" answers.
I need to wait for pending user requests to complete, disconnect them cleanly, run a few queries on the database to reflect the change(s) in state and so forth. It's a TCP server.
Then don't run it as a Console or any other kind of Client app.
Just run it as a Windows (NT) Service and the only events you'll have to worry about are Power loss and a stop signal.
Use a UPS and make sure you can close in a reasonable timespan.
I have not tried to do this kind of thing with a console app, but you may do better with a Windows Forms (or WCF app). They will give you a FormClosing event which is cancellable. Alternately, use a Windows Service if you are writing a network service, it provides an interface to cleanly stop your application.
If you are really keen on a console app, perhaps a try {} finally {} clause around all your code or something more exotic like a critical finaliser may allow you to run clean up code. But this is really not the right tool for the job.
And there are cases which you cannot prevent you app being closed, eg: power failure, or Task Manager kill command (and if an app didn't close via the X, Task Manager is the first tool I'd reach for).
So, code your service application such that all client requests are logged to a transaction log (like SQL server does). If you are unexpectedly interrupted (by whatever circumstance) anything which has happened up until that point is in the log. When your service next starts, replay that log.
One of your things to log will be "I was shutdown cleanly at time T". If you restart and don't find that item at the end of your log, you know something went wrong, and you can take whatever action is required.
If you need to know what your service is doing, use one of the many logging frameworks to pipe events to a second app, which just displays activity.
I spent couple hours looking at this and as I don't have time now to build a working code; as while it's probably short, getting it right would take a while. I'll just give you link to the various stuff that's needed to get this done:
http://pastebin.com/EzX3ezrf
Summarizing the lessons from the code in the paste:
Need a message pump to handle some/all of WM_QUERYENDSESSION, WM_ENDSESSION, CTRL_SHUTDOWN_EVENT (in c# SystemEvents.SessionEnding may cover some/all of these)
Easiest way to get a message pump is to make it a hidden form/window app, but I recall it's possible to build as a console app and add a message pump also. I didn't include that code in the paste though.
"If an application must block a potential system shutdown, it can call the ShutdownBlockReasonCreate function"
As AllocConsole is used to create the console, you need to use SetConsoleCtrlHandler and use ExitThread(1) in the handler. This is a "hack" that kills off the thread that would close the console otherwise. It's used in FarManager. see interf.cpp for example
You need to also initialize and clean up the console when using AllocConsole.
Pressing CTRL+C is reported to mess up the input. I'm not sure if FarManager is handling this scenario. There's some code in the CTRL_BREAK_EVENT handler in interf.cpp that I'm not sure what it does.
FarManager also handles WM_POWERBROADCAST, probably to do with suspending
If all that isn't enough (should be), you can also add the console into another process and IPC your messages to it like shown here. Why does closing a console that was started with AllocConsole cause my whole application to exit? Can I change this behavior?
RMTool can be used to simulate logoff/shutdown messages for testing: http://download.microsoft.com/download/d/2/5/d2522ce4-a441-459d-8302-be8f3321823c/LogoToolsv1.0.msi
MSDN has some C# code also at microsoft.win32.systemevents.sessionending.aspx
and microsoft.win32.systemevents.aspx (hidden form example)
The mischel.com/pubs/consoledotnet/consoledotnet.zip has a sample winTest project with AllocConsole being used and some of the events handled.
In my application I have a lot of Console#WriteLine or Console#Write calls. This are so many, that the application is really slower than necessary.
So I'm looking for an easy way to remove or at least disable all Console#WriteLine and Console#Write calls.
What's the easiest way to achieve that?
Here's a even quicker quick fix I implemented.
...
static int Main(String[] args)
{
...
#if !DEBUG
Console.SetOut(TextWriter.Null);
Console.SetError(TextWriter.Null);
#endif
...
}
HTH
Maybe Find&Replace function in any code editor?
For example find all
Console.WriteLine
and replace with
//Console.WriteLine
To switch off Console.WriteLine() output in runtime temporarily use
// backup the previous output handler connected to Console
TextWriter backupOut = Console.Out;
// activate a null handle
Console.SetOut(TextWriter.Null);
// this console output will be invisible
Console.WriteLine("Hidden output on Console.");
// restore the previous handle
Console.SetOut(backupOut);
// this console output will be visible
Console.WriteLine("Showing output on Console.");
Similarly, you can disable program Debug.WriteLine() output in runtime, also while compiling in Debug mode, by using a dummy TextWriterTraceListener, like so
using System.Diagnostics;
using System.IO;
//...
public class MyDummyListener: TextWriterTraceListener
{ }
// ......
[STAThread]
static void Main()
{
// Back up the old one
DefaultTraceListener[] debuglisteners = {(DefaultTraceListener) Debug.Listeners[0]};
// Plug in dummy listener
TextWriterTraceListener[] dummylisteners = {new MyDummyListener()};
Debug.Listeners.Clear();
Debug.Listeners.AddRange(dummylisteners);
// this is an invisible debug message
Debug.WriteLine("This one is invisible");
// ..
// to activate output again, plug in the previous listener
Debug.Listeners.Clear();
Debug.Listeners.AddRange(debuglisteners);
Debug.WriteLine("This one is visible again");
// ....
}
Note: I tested above code in .NET Framework 4.7.2 in Debug mode, using the Winforms platform.
If they are non-essential (logging) then you should have used System.Diagnostics.Debug.Print() to start with.
Luckily WriteLine() is compatible with Debug.Print() so that's an easy S&R. And fix some usings maybe.
Replacing Console.Write() might be a little trickier.
To be complete: The Debug.Print() statements can be turned on/off with a checkbox in project|Properties.
Use a simple, non regex, Find/Replace dialog, and replace every Console.WriteLine( by Debug.WriteLine(.
You can then keep the possibility to track what was previously outputted directly to the console window, still optimizing performance in release mode.
In Visual Studio, use the Tools|Macros|Record temporary macro option to record a macro that does a Find of "Console.Write", and deletes the line. That is:
Ctrl+F to find "Console.Write"
then Ctrl+L to delete the line.
Save the macro and then run it against every file in the project that has the offending lines.
Should take about two minutes.
I would suggest, however, that you back up your source first. Just in case.