I am using Specflwo 2.4 inside my class with methods that do some actions while doing "feature steps" I would like to drop message to this console:
I tried to use :
ITraceListener traceListener and invoke methods
traceListener.WriteTestOutput and traceListener.WriteToolOutput
but these dumps the strings to the final test Output (but not to that window)
I tried pinvoke apporach - but there is problem with "access denied" occured - also will not pass Code Review
I tried adding Nunit/Xunit - dead end - not work, also not acceptable sollution to add yet anothe nuget....
sample code from Nuint - not working
NUnit.Framework.TestContext.Progress.WriteLine("NUnit.Framework.TestContext.Progress.WriteLine Some string");
I tried such a trick, also is not working:
using (var writer = new System.IO.StreamWriter(System.Console.OpenStandardOutput()))
writer.WriteLine("This will show up!"); // also is not working
CucumberMessages from SpecFlow/TechTalk.SpecFlow/CucumberMessages/CucumberMessageSender.cs - looks promising but I have to upgrade sepcflow and also usage is... annoying and closer to talking with Deamons/Ghosts not with Simlpe Output-Tests console
I tried also this
ConsoleTraceListener consoleTracer;
consoleTracer = new ConsoleTraceListener();
consoleTracer.Name = "mainConsoleTracer";
consoleTracer.WriteLine(" Starting output to trace listener .");
I also discovered that I can send strings to Debug Console (partial sollution)
Trace.WriteLine("\r\n Trace.WriteLine: Some String ");
Debugger.Log(0, "1", "Debugger.log message \r\n");
I tried sollutions from this forum: https://github.com/microsoft/vstest/issues/799
no success.
Any clues - looks liek Specflow is stealing all output from StdOut (Standard Output) and probbaly StdErr (Standard Error) and redirecting to something that is available after the test.
There was a trick also to use
Please advise.
Related
I have a C# class library that provides a number of interfaces that can be called from PowerShell scripts (.PS1) and Advanced Modules (.PSM1). I have a static method to write verbose and debug messages to the console using the System.Console class:
public class Write
{
public static void Verbose(string msg, string source)
{
if (Config.EnableVerbose)
{
ConsoleColor originalForeGroundColor = Console.ForegroundColor;
ConsoleColor originalBackGroundColor = Console.BackgroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write("VERBOSE: {0} {1}{2}", source, msg, Environment.NewLine);
Console.ForegroundColor = originalForeGroundColor;
Console.BackgroundColor = originalBackGroundColor;
}
}
}
However, when those messages are displayed in a PowerShell console, they cannot be captured using redirection, like with Out-File, >&0 or even with Start-Transcript.
I have read about_Redirection, and using the redirect modifiers does not capture the console output. For instance, using a PowerShell Advanced Function (aka Cmdlet) I have written:
Get-CommandTrace -ScriptBlock { Get-Resource } *> C:\Temp\capture.log
The Get-CommandTrace Cmdlet sets the $VerbosePreference = 'Continue' during the ScriptBlock execution, and does capture the verbose from Get-Resource output there. But does not capture the Console output from my C# library.
So, my question is simply: Can a C# class that is not a Cmdlet class, nor inherited class, be able to write output to the existing PowerShell runspace it is being called from?
Note:
This is not a complete answer, because it has severe limitations - though it may work for specific use cases.
The original form of this answer used since-deprecated PowerShell SDK method .CreateNestedPipeline(), which cannot be used anymore if you're writing your code against the PowerShellStandard library for cross-platform and cross-edition compatibility (code that should run in both Windows PowerShell and PowerShell Core, on all supported platforms).
Chris (the OP) himself found a compatible alternative, which the current form of this answer is based on.
The challenge is to write to the invoking pipeline's output streams (as you've observed writing via Console is unrelated to PowerShell's output streams and prints directly to the console, with no ability to capture or redirect such output in PowerShell).
While I you can obtain a reference to the invoking runspace, there is no way I know of to obtain a reference to the running pipeline.
Using the invoking runspace you can write to PowerShell's output streams via a new pipeline, but that comes with severe limitations:
You cannot write to the caller's success output stream (1) that way; that is, while you can call cmdlets that target the other streams, such as Write-Verbose, Write-Output does not work.
Capturing the output from this nested pipeline in a variable or sending it through the pipeline requires enclosing the method call in (...) (or $(...) or #(...)) in addition to applying the appropriate redirection to the success output stream (e.g., 4>&1 for the verbose stream).
See the code comments for details.
Add-Type -TypeDefinition #'
using System.Management.Automation;
public class Write
{
public static void Verbose(string msg)
{
using (PowerShell ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) {
// IMPORTANT: Use .AddScript(), not .AddCommand().
// Even though .AddCommand() + .AddParameter() is arguably a cleaner way to
// formulate the command, it results in output that cannot be captured.
ps.AddScript("Write-Output '" + msg.Replace("'", "''") + "'").Invoke();
}
}
}
'#
#"
$VerbosePreference = 'Continue'
# Regular output to the verbose stream.
Write-Verbose 'msg1'
# Verbose output via the custom type.
[Write]::Verbose('msg2')
# SUPPRESSING and REDIRECTING TO A FILE work.
[Write]::Verbose('msg3') 4> $null
[Write]::Verbose('msg4') 4> t.txt
# By default, REDIRECTING TO THE STANDARD OUTPUT STREAM (1)
# works only for the OUTSIDE, i.e. for CALLERS of this script.
[Write]::Verbose('msg5') 4>&1
# To REDIRECT TO THE STANDARD OUTPUT STREAM (1) in order to:
# * CAPTURE the result INSIDE your script
# * SEND THE RESULT THROUGH THE PIPELINE,
# additionally invoke the method call enclosed in (...) or $(...) or #(...)
$out = ([Write]::Verbose('msg6') 4>&1); "captured: [$out]"
([Write]::Verbose('msg7') 4>&1) | ForEach-Object { "piped: [$_]" }
While the answer above from mklement0 is a good one, it will not work if you attempt to use it with PowerShellCore or targeting .NetStandard 2.0 as the CreateNestedPipeline API is deprecated. (See this thread on the PowerShellStandard GitHub repo.)
So, instead, I have this working code:
Add-Type -TypeDefinition #'
using System.Management.Automation;
using System.Management.Automation.Runspaces;
public class Write
{
public static void Verbose(string msg)
{
using (PowerShell initialPowerShell = PowerShell.Create(RunspaceMode.CurrentRunspace))
{
initialPowerShell.Commands.AddScript("Write-Verbose " + msg.Replace("\"", "\"\"") + "\" -v");
initialPowerShell.Invoke();
}
}
}
'#
$VerbosePreference = 'Continue'
# Regular output to the verbose stream.
Write-Verbose 'msg1'
# Verbose output via the custom type.
# !! This can NOT be redirected from the outside.
[Write]::Verbose('msg2')
# !! SUPPRESSING or REDIRECTING TO A FILE only works
# !! when DIRECTLY APPLIED to the method call.
[Write]::Verbose('msg3') 4> $null
[Write]::Verbose('msg4') 4> t.txt
# !! REDIRECTING TO THE STANDARD OUTPUT STREAM (1) for the OUTSIDE works,
# !! but obviously it then merges with success output.
[Write]::Verbose('msg5') 4>&1
# !! To REDIRECT TO THE STANDARD OUTPUT STREAM (1) and capture the result
# !! INSIDE your script, invoke the method call in (...) or $(...) or #(...)
$out = ([Write]::Verbose('msg6') 4>&1)
"[$out]"
Which works with PowerShell 5.1 for Windows, PowerShell Core 6.2.2 for Windows and Linux (Ubuntu/Debian).
I will still leave mklement0 reply marked as the answer to the original question. I'm just adding another one based on research I had compiled over the past few days on migrating my class library to .NetStandard 2.0.
This seems trivial, but google only gives me powershell-related stuff and how to implement pipelines within programs.
I'm trying to consume input arguments from the (standard command processor cmd.exe for Windows 7, not powershell) command line pipeline on windows like 'more' does.
I'm trying to get this to work with two self-created .exe files:
randgen (creates a random number and writes it to the console)
wordwrite (the c# program that I want to consume this random number from the pipeline)
And using two common windows executables to test and model the behaviour:
echo (writes input to console)
more (displays output one screen at a time, can consume input from the pipeline)
Here's the current content of my Main function in wordwrite:
static void Main(string[] args)
{
string ouname = args[0];
DocWrite(ouname);
}
and the behaviour trying to naively pipe output from randgen into wordwrite:
D:\code>randgen | wordwrite
Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
at ADDoc.ADOUDoc.Main(String[] args)
(plus a popup telling me wordwrite.exe has stopped working)
Which is the behaviour of trying to consume a null array of arguments as input:
D:\code>wordwrite
Unhandled Exception: System.IndexOutOfRangeException: Index was outside the bounds of the array.
at ADDoc.ADOUDoc.Main(String[] args)
So the output of randgen isn't being read from the pipeline correctly as an argument to the wordwrite executable's args array (thanks #Peter Duniho for explaining the difference between stdin and how argument assignation works). Contrast that with the behaviour of the 'more' executable:
D:\code>randgen | more
56929227
That said, this 'pipeline-awareness' seems to be inherent to the 'more' executable itself, as another common windows executable, echo.exe, is just as pipeline-blind as wordwrite.exe:
D:\code>randgen | echo
ECHO is on.
D:\code>echo blah
blah
D:\code>echo
ECHO is on.
Here we can see that the attempt to use the pipeline is effectively the same as providing no arguments for echo.exe (contrasted in the middle with behaviour when an argument is provided).
Which suggests there is something I can add the the body of my Main function in wordwrite.exe to enable pipeline-consuming behaviour like the 'more' executable does. Does anyone know how this is done?
You can read data 'from the pipeline' (effectively, the stdin stream*) by using the Console.ReadLine() method, like so:
static void Main()
{
string ouname = Console.ReadLine();
DocWrite(ouname);
}
*The pipeline is a tool to direct the output of one process (stdout) to the input (stdin) of another one. So when you think of 'consuming input from the pipeline', think 'assign stdin to a variable that you then do stuff with'.
Reference:
C# Console receive input with pipe
I have the following code (simplified to show the problem):
var wdApp = new Application();
var wdDoc = wdApp.Documents.Open("C:\foo.docx");
wdApp.StatusBar = "Updating...";
var rng = wdDoc.Range(10, 10);
if ((bool)rng.Information(WdInformation.wdWithInTable))
{
}
//StatusBar value is gone...
What could be the reason?
How can I prevent it?
Do you know of other situations where this can happen?
Here screenshots of the problem
1 F10 (step over) later
Edit:
The provided code uses NetOffice and not the interop library from Microsoft directly, therefor the syntax is correct. You may notice in the provided screenshots that they are taken from a running application. Breakpoint, highlighting of current line of code executing, aswell as the actual result of the code in the word application on the right. Where at first there is the desired statusbar "Tabelle 8 von 17 wird neu erstellt." (Table 8 out of 17 is recreating) and at the next step my statusbar is gone and its the default stuff "165 von 8227 Wörtern" (165 out of 8227 words)
What could be the reason?
I believe this is to do with the library you are using. I tested your code but with the Word Interop library, and the only way I could get the status bar to reset was to manually click/type within the Word window.
How can I prevent it?
I would say take a look into the code base of library you are using. It is likely that it is doing something that is causing the behaviour. Unless there is a specific reason you are using NetOffice I would suggest switching to the either the standard Interop or VSTO.
Do you know of other situations where this can happen?
As above, I could only get the status bar to reset if I manually carried out some sort of input into the window.
I have been fighting with the Windows Event log for lots of hours with inconsistent behaviour during test of the log4net EventLogAppender and I realized, that the log4net code worked, but my windows event log was the one being unreasonable.
System
OS: Windows 8.1
C#: .Net 4.5, built to x64
Creating the error case
C#: Create Source1 in TestLog1
C#: Write to the log (Works)
Powershell: Removing the log using powershell
C# Create Source1 in TestLog2 (Different log)
C# Write to the log <= This shows no log entries in TestLog2!
I have made a complete step-by-step guide to recreate the problem:
1: Create a new source in a new log and write to it
Code executed:
EventLog.CreateEventSource(source: "TestSource1", logName: "TestLog1");
EventLog myLog = new EventLog();
myLog.Source = "TestSource1";
myLog.WriteEntry("This is a message");
List logs using powershell-command:
Get-EventLog -LogName *
This will correctly list all logs, including TestLog1 containing 1 log entry.
I can also get the log entries by using this powershell command:
GetEventLog -LogName "TestLog1"
This shows me the single log message in the log.
2: Delete the event log using powershell
Powershell command:
Remove-EventLog -LogName "TestLog1"
Listing all logs now shows, that the log has actually been deleted. Powershell command again:
Get-EventLog -LogName *
3: Create the source again, but in another log this time
Code executed:
EventLog.CreateEventSource(source: "TestSource1", logName: "TestLog2"); // New log name
EventLog myLog = new EventLog();
myLog.Source = "TestSource1";
myLog.WriteEntry("This is a message");
Result:
The log appears in powershell when listing all logs
The log does not contain any entry
Using Get-EventLog "TestLog2" throws and exception even though it appears in the log-list
Deleting the log in powershell using remove-eventlog -logName "TestLog2" somehow still works.
It seems that in some cases, the logs seems to exist, but in others it doesnt.
A: Is this a known bug or what is wrong with my scenario?
B: How can I clean up my existing mess if sources somehow still exist pointing at the old log? (If thats the case, that is)
EDIT: I even tried the following C# code to delete the source first and then the log, but the result is the same:
var source = "TestSource6";
var logName1 = "Testlog5";
var logName2 = "Testlog6";
EventLog.CreateEventSource(source: source, logName: logName1);
new EventLog() { Source = source }.WriteEntry("This is a message in log " + logName1);
EventLog.DeleteEventSource(source:source);
EventLog.Delete(logName:logName1);
EventLog.CreateEventSource(source: source, logName: logName2);
new EventLog() { Source = source }.WriteEntry("This is a message" + logName2);
Unfortunately you can't re-register an event source "back to back". It's one of the (many) reasons installers often ask to restart the computer.
From MSDN:
If a source has already been mapped to a log and you remap it to a new log, you must restart the computer for the changes to take effect.
EventLog.CreateEventSource Method (String, String)
For fixing the issue, I would recommend not deleting the event source unless the product is uninstalled. Just stop using Log1 and start using Log2, without deleting and recreating. When you go to use any log, you could use something similar to this:
if (!EventLog.SourceExists(source, log))
{
EventLog.CreateSource(source, log)
}
And simply leave the source where it is, until you uninstall the product. If you're using InstallShield, it should automatically detect a restart is required and ask the user to do so.
I've read several articles that tell you how to add text to the output window in visual studio from within an Add-On (specifically, a visual studio 2008 integration package, via the visual studio 2008 SDK 1.1), but no examples of how to read text from the output window. My goal is to parse text from the debug output window while debugging a certain application (TRACE output and possibly stdin/stdout). The IVsOutputWindowPane interface has no methods for reading in text from the output window. The documentation seems to imply that it is possible, but it doesn't provide an example:
http://msdn.microsoft.com/en-us/library/bb166236(VS.80).aspx
Quote: In addition, the OutputWindow and OutputWindowPane objects add some higher-level functionality to make it easier to enumerate the Output window panes and to retrieve text from the panes.
Preferably I'd like to be able to subscribe to an event that fires when a new line of text arrives, similar to a StreamReader's asynchronous reads.
It is possible, it is just a long winding path to get to it:
ServiceProvider -> IVsOutputWindow -> GetPane( debugwindow ) -> IVsUserData -> GetData( wpftextviewhost ) -> IWpfTextViewHost -> IWpfTextView -> TextBuffer -> Changed event.
Presuming you have a VS IServiceProvider from somewhere else (vsix extension/whatever, global service provider), and without any error checking, it looks like this:
IVsOutputWindow outWindow = ServiceProvider.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
Guid debugPaneGuid = VSConstants.GUID_OutWindowDebugPane;
IVsOutputWindowPane pane;
outWindow.GetPane(ref debugPaneGuid, out pane);
// from here up you'll find in lots of other stackoverflow answers,
// the stuff from here down is interesting to this question
IVsUserData userData = (IVsUserData)pane;
object o;
Guid guidViewHost = DefGuidList.guidIWpfTextViewHost;
userData.GetData(ref guidViewHost, out o);
IWpfTextViewHost viewHost = (IWpfTextViewHost)o;
IWpfTextView textView = viewHost.TextView;
textView.TextBuffer.Changed += YourTextChangedHandlerHere;
Your text changed handler will then get called every time the output window gets more data. you won't necessarily get it line by line, but you'll probably more likely than not get big chunks you'll need to deal with on your own.
It is highly likely that some of the above did not even exist in VS in 2010. But it exists now!
The default behavior (when you don’t set the listener explicitly) of VS is to display trace massages in the debugger output window, which you appreciate if you want a simple solution and do no other actions with the massages.
Unfortunately this is not your case. So you have to define a trace listener to send (and store) your trace massages where you then will be able to read them. The trace listener could be a file (for example XML) or you can create a custom listener by deriving a class from the base class TraceListener if you don't want to bother yourself with an additional file.
I don't know that what you ask is possible. But, you can register your add-in as a debugger for your application so that you get the output the trace messages. These are typically routed to OutputDebugString, and can be captured as described in this article: http://www.drdobbs.com/showArticle.jhtml?articleID=184410719. It does not give you the normal output, only debug, but it does not depend on the technology of the debugged application.
The solution on this page selects the text in order to read it. I'm hoping there's a better way.
Automatically stop Visual C++ 2008 build at first compile error?
Private Sub OutputWindowEvents_OnPaneUpdated(ByVal pPane As OutputWindowPane) Handles OutputWindowEvents.PaneUpdated
pPane.TextDocument.Selection.SelectAll()
Dim Context As String = pPane.TextDocument.Selection.Text
pPane.TextDocument.Selection.EndOfDocument()
End Sub