In VS, make print-on-breakpoint use the console - visual-studio

I've made a "when hit, print a message" breakpoint in VS 2010. It works, but it only outputs to the VS "output" pane. Can I make it use my app's console window?
I've tried:
Debug.Listeners.Add(new ConsoleTraceListener());
As well as:
var writer = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(writer);

It is possible to print this message in your app console window, but for that you need to use the debugger evaluator:
Create a method that you would like to call from the debugger when the breakpoint is hit.
Place a breakpoint, but instead of providing just a text message use your method name in curly braces, eg. {CallFromDebugger()}
Have a look at this code:
static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
Console.WriteLine(i);
}
Console.ReadKey();
}
[Conditional("DEBUG")]
static void MessageFromDebugger(string message)
{
Console.WriteLine("I was called from the debugger evaluator: {0}", message);
}
If you place a breakpoint on line 5: Console.WriteLine(i); with When Hit... property set to: {MessageFromDebugger("message from address $ADDRESS")} you should see in your console window:
0
I was called from the debugger evaluator: message from address ConsoleApplication1.Program.Main(string[]) + 0x00000048
1
I was called from the debugger evaluator: message from address ConsoleApplication1.Program.Main(string[]) + 0x00000048
2
What's interesting is that you may pass arguments to your function that are valid in the calling scope as well as the special debugger variables (such as $ADDRESS, $PID, $CALLSTACK etc.). I observed though that special debugger variables are just placeholders and are replaced before submitting to your function, so remember to put them in double quotes, eg. {MessageFromDebugger(#"$CALLSTACK")}

Related

Execute C# code with side effects on breakpoint

I would like to use a breakpoint to execute code in Visual Studio 2017 and have the side effects of that code affect the program. It appears from Execute code when breakpoint is hit? that this was possible in a past version of Visual Studio.
But consider the code below.
int Counter = 0;
public bool Increment()
{
Counter++;
return false;
}
public void PrintNumbers()
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"Iteration {i}");
}
Debug.WriteLine("Counter: " + Counter);
}
I have set a breakpoint on the line that runs the "Iteration" as follows:
When this runs, it produces the following output:
Iteration 0
Iteration 1
Iteration 2
Counter: 0
This is a true technological marvel! Visual Studio does run the code -- if I change the "return false" in Increment() to "return true," the breakpoint will be hit. But it magically avoids side effects. That's great! But I want side effects. In other words, I really want an action option besides "log a message to output windows" that allows execution of code with side effects. Is there any way around this Visual Studio feature?
You might be wondering why I want to do this. I would like to make a conditional breakpoint dependent on another conditional breakpoint having been passed. (For example, I know that the Knight is talking funny after meeting the Princess, so I want to put a breakpoint on KnightTalks() that stops only if some code in KnightMeetsPrincess() has executed. I run into situations this all the time and get annoyed when I have to disable the KnightTalks breakpoint and reenable it after KnightMeetsPrincess, especially when there is a longer chain of events.) Based on Conditional breakpoint depends on other breakpoint, I think there is no built-in way to do this. But I could do something like it if I could execute arbitrary code -- for example, if I could set and get a pseudovariable. Or one could use code like the following:
public static class Bookmark
{
static Dictionary<string, int> HitCounts = new Dictionary<string, int>();
public static bool Set(string s)
{
if (!HitCounts.ContainsKey(s))
HitCounts[s] = 1;
else
HitCounts[s]++;
return false;
}
public static bool Set(string s, bool condition)
{
if (condition)
Set(s);
return false;
}
public static bool Get(string s)
{
return HitCounts.ContainsKey(s);
}
}
The idea of this code was that I would put a conditional breakpoint on KnightMeetsPrincess that called Bookmark.Set("KnightMeetsPrincess") and then on KnightTalks, a separate conditional breakpoint called Bookmark.Get("KnightMeetsPrincess"). Alas, this doesn't work because the code executes, but without side effects.
Any workarounds?

How do I pass line number to a method

In Visual Stuido 2013, working in C# (.Net 4.5), how can I pass a line number to a method call. I recall in C there was a #pragma lineNumber to do this, but searching on those terms brings up nothing.
I want to write a method something like this:
// unchecked code:
private void printResetStopwatch(int lineNumber)
{
stopwatch.stop();
System.Console.WriteLine(stopwatch.Elapsed.ToString() + " at line " + lineNumber.ToString();
}
and I would call it something like
printResetStopwatch(#pragma lineNumber);
if #pragma was the answer.
The way to do this is to attribute a parameter on the method with the CallerLineNumberAttribute and provide it with a default value. C# will then fill it in with the line number of the caller
void Method(string message, [CallerLineNumber] int lineNumber = 0) {
...
}
Method("foo"); // C# will insert the line number here
Note that there are actually a set of related attributes here that might interest you. Here is a sample
public void TraceMessage(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
Full Documentation: http://msdn.microsoft.com/en-us/library/hh534540.aspx
Note: This requires the C# 5.0 compiler which is included in VS 2013.

D: executeShell on Windows to run another program not returning immediately

I'm using D as a scripting language for Windows 7 console stuff to automate boring tasks. One of my scripts (open.exe) is supposed to allow me to open stuff from the command line without me having to specify which program I use (I have a configuration file with this stuff). Now, I use executeShell to do this, and call something like start [name of program I want to use] [name of input file]. If I do this directly from the shell, it returns immediately, but if I do it using my D script, it doesn't return until the program that it opens is closed. What should I do to allow it to return immediately?
For reference purposes, this is the business logic of my script (the main method just does some argument parsing for piping purposes):
immutable path = "some//path//going//to//config//file.conf";
void process(string input) {
string extension = split(input,".")[1]; //get file extension from input
auto config = File(path,"r"); auto found = false;
while (!config.eof()){
auto line = chomp(config.readln());
if (line[0]!='#') { //skip comment lines
auto divided = split(line, ":");
if (divided[0] == extension) {
found = true;
auto command = "start " ~ divided[1] ~ " " ~ input;
auto result = executeShell(command);
//test for error code and output if necessary
writeln(result.output);
}
}
}
if (!found)
writeln("ERROR: Don't know how to open " ~ input);
}
From the top of the std.process documentation:
Execute and wait for completion, collect output - executeShell
The Windows start program spawns a process and exits immediately. D's executeShell does something else. If you'd like to spawn another program, use the appropriate functions: spawnProcess or spawnShell.

Calling functions while in debug mode in VC++ (Immediate Window)

I wonder can I call functions during the debug mode in VC++? Assume that I have a function to which I set a break point at, when the execution stops at that point during debugging, can I call other functions and see their results before proceeding to the next line of code?
I believe you can. I think its called Immediate Window. I use VS2010 Ultimate, so I don't know if it exists in your version.
Ctrl + Alt + I
But this only prints output for when the function returns a value. Also, it may not work in some cases.
Let's say you have :
#include <iostream>
int number = 10; //global
void setNumber(int n);
int main()
{
std::cout<<std::endl; //breakpoint 1 here
setNumber(4);
std::cout<<std::endl; //breakpoint 2 here
}
int getNumberSquared()
{
return number * number;
}
void setNumber(int n)
{
number = n;
}
when you encounter breakpoint 1, press the shortcut and type:
getNumberSquared()
The output will be 100
After encountering breakpoint 2, do the same thing and the output will be 16
Visual studio has the option to jump to a specific statement (right click + set next statement or ctrl+shift+F10), but be aware when doing so. A function call requires registries to be valid, which will most likely not be if you jump across classes or out of scope.

Open a file in Visual Studio at a specific line number

I have a utility (grep) that gives me a list of filenames and a line numbers. After I have determined that devenv is the correct program to open a file, I would like to ensure that it is opened at the indicated line number. In emacs, this would be:
emacs +140 filename.c
I have found nothing like this for Visual Studio (devenv). The closest I have found is:
devenv /Command "Edit.Goto 140" filename.c
However, this makes a separate instance of devenv for each such file. I would rather have something that uses an existing instance.
These variations re-use an existing devenv, but don't go to the indicated line:
devenv /Command "Edit.Goto 140" /Edit filename.c
devenv /Command /Edit filename.c "Edit.Goto 140"
I thought that using multiple "/Command" arguments might do it, but I probably don't have the right one because I either get errors or no response at all (other than opening an empty devenv).
I could write a special macro for devenv, but I would like this utility to be used by others that don't have that macro. And I'm not clear on how to invoke that macro with the "/Command" option.
Any ideas?
Well, it doesn't appear that there is a way to do this as I wanted. Since it looks like I'll need to have dedicated code to start up Visual Studio, I've decided to use EnvDTE as shown below. Hopefully this will help somebody else.
#include "stdafx.h"
//-----------------------------------------------------------------------
// This code is blatently stolen from http://benbuck.com/archives/13
//
// This is from the blog of somebody called "BenBuck" for which there
// seems to be no information.
//-----------------------------------------------------------------------
// import EnvDTE
#pragma warning(disable : 4278)
#pragma warning(disable : 4146)
#import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids
#pragma warning(default : 4146)
#pragma warning(default : 4278)
bool visual_studio_open_file(char const *filename, unsigned int line)
{
HRESULT result;
CLSID clsid;
result = ::CLSIDFromProgID(L"VisualStudio.DTE", &clsid);
if (FAILED(result))
return false;
CComPtr<IUnknown> punk;
result = ::GetActiveObject(clsid, NULL, &punk);
if (FAILED(result))
return false;
CComPtr<EnvDTE::_DTE> DTE;
DTE = punk;
CComPtr<EnvDTE::ItemOperations> item_ops;
result = DTE->get_ItemOperations(&item_ops);
if (FAILED(result))
return false;
CComBSTR bstrFileName(filename);
CComBSTR bstrKind(EnvDTE::vsViewKindTextView);
CComPtr<EnvDTE::Window> window;
result = item_ops->OpenFile(bstrFileName, bstrKind, &window);
if (FAILED(result))
return false;
CComPtr<EnvDTE::Document> doc;
result = DTE->get_ActiveDocument(&doc);
if (FAILED(result))
return false;
CComPtr<IDispatch> selection_dispatch;
result = doc->get_Selection(&selection_dispatch);
if (FAILED(result))
return false;
CComPtr<EnvDTE::TextSelection> selection;
result = selection_dispatch->QueryInterface(&selection);
if (FAILED(result))
return false;
result = selection->GotoLine(line, TRUE);
if (FAILED(result))
return false;
return true;
}
With VS2008 SP1, you can use the following command line to open a file at a specific line in an existing instance :
devenv /edit FILE_PATH /command "edit.goto FILE_LINE"
Source
Elaborating on Harold question and answer, I adapted the C++ solution (that I first adopted) to C#. It is much simpler (that is my first C# program!). One just need to create a project, add references to "envDTE" and "envDTE80" and drop the following code:
using System;
using System.Collections.Generic;
using System.Text;
namespace openStudioFileLine
{
class Program
{
[STAThread]
static void Main(string[] args)
{
try
{
String filename = args[0];
int fileline;
int.TryParse(args[1], out fileline);
EnvDTE80.DTE2 dte2;
dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");
dte2.MainWindow.Activate();
EnvDTE.Window w = dte2.ItemOperations.OpenFile(filename, EnvDTE.Constants.vsViewKindTextView);
((EnvDTE.TextSelection)dte2.ActiveDocument.Selection).GotoLine(fileline, true);
}
catch (Exception e)
{
Console.Write(e.Message);
}
}
}
}
One then just calls openStudioFileLine path_to_file numberOfLine.
Hope that may help !
Based on reder answer I have published repository with source, here is binary(.net2.0)
I also add support for multiple VS versions
usage: <version> <file path> <line number>
Visual Studio version value
VisualStudio 2002 2
VisualStudio 2003 3
VisualStudio 2005 5
VisualStudio 2008 8
VisualStudio 2010 10
VisualStudio 2012 12
VisualStudio 2013 13
Example using from GrepWin:
VisualStudioFileOpenTool.exe 12 %path% %line%
Pretty old thread, but it got me started so here's another example. This AutoHotkey function opens a file, and puts the cursor on a particular rowand column.
; http://msdn.microsoft.com/en-us/library/envdte.textselection.aspx
; http://msdn.microsoft.com/en-us/library/envdte.textselection.movetodisplaycolumn.aspx
VST_Goto(Filename, Row:=1, Col:=1) {
DTE := ComObjActive("VisualStudio.DTE.12.0")
DTE.ExecuteCommand("File.OpenFile", Filename)
DTE.ActiveDocument.Selection.MoveToDisplayColumn(Row, Col)
}
Call with:
VST_Goto("C:\Palabra\.NET\Addin\EscDoc\EscDoc.cs", 328, 40)
You could translate it pretty much line by line to VBScript or JScript.
Here is Python variation of Harold's solution:
import sys
import win32com.client
filename = sys.argv[1]
line = int(sys.argv[2])
column = int(sys.argv[3])
dte = win32com.client.GetActiveObject("VisualStudio.DTE")
dte.MainWindow.Activate
dte.ItemOperations.OpenFile(filename)
dte.ActiveDocument.Selection.MoveToLineAndOffset(line, column+1)
It shows how to go to specified line + column.
Here is VBS variation of Harold's solution: link to .vbs script.
open-in-msvs.vbs full-path-to-file line column
Windows supports VBScript natively - no need for compilation or any additional interpreters.
These C# dependencies on project references are completely unecessary. Indeed much of the code here is overly verbose. All you need is this.
using System.Reflection;
using System.Runtime.InteropServices;
private static void OpenFileAtLine(string file, int line) {
object vs = Marshal.GetActiveObject("VisualStudio.DTE");
object ops = vs.GetType().InvokeMember("ItemOperations", BindingFlags.GetProperty, null, vs, null);
object window = ops.GetType().InvokeMember("OpenFile", BindingFlags.InvokeMethod, null, ops, new object[] { file });
object selection = window.GetType().InvokeMember("Selection", BindingFlags.GetProperty, null, window, null);
selection.GetType().InvokeMember("GotoLine", BindingFlags.InvokeMethod, null, selection, new object[] { line, true });
}
Simples eh?
This is my working C# solution for Visual Studio 2017 (15.9.7)
For other versions of VS just change the version number (i.e. "VisualStudio.DTE.14.0")
todo:
Add Reference->Search 'envdte'->Check Checkbox for envdte->Click OK
using EnvDTE;
private static void OpenFileAtLine(string file, int line)
{
DTE dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.15.0");
dte.MainWindow.Visible = true;
dte.ExecuteCommand("File.OpenFile", file);
dte.ExecuteCommand("Edit.GoTo", line.ToString());
}
For reference here is the ENVDE written in C# (using O2 Platform inside VisualStudio to get a reference to the live DTE object)
var visualStudio = new API_VisualStudio_2010();
var vsDTE = visualStudio.VsAddIn.VS_Dte;
//var document = (Document)vsDTE.ActiveDocument;
//var window = (Window)document.Windows.first();
var textSelection = (TextSelection)vsDTE.ActiveDocument.Selection;
var selectedLine = 1;
20.loop(100,()=>{
textSelection.GotoLine(selectedLine++);
textSelection.SelectLine();
});
return textSelection;
This code does a little animation where 20 lines are selected (with a 100ms interval)
The correct wingrep command line syntax to force a new instance and jump to a line number is:
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" $F /command "edit.goto $L"
Replace the studio version number with the correct version for your setup.
The version posted by #Mungo64 worked for me, but of course the version number is always changing, so I made a version that automatically searches until we find it.
Add Reference->Search 'envdte'->Check Checkbox for envdte->Click OK
//using EnvDTE; //I didn't use the using directive as it causes ambiguity in another module I'm using.
private static void OpenFileAtLine(string file, int line)
{
//The number needs to be rolled to the next version each time a new version of visual studio is used...
EnvDTE.DTE dte = null;
for (int i = 25; i > 8; i--) {
try
{
dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE." + i.ToString() + ".0");
}
catch (Exception ex)
{
//don't care... just keep bashing head against wall until success
}
}
//the following line works fine for visual studio 2019:
//EnvDTE.DTE dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE.16.0");
dte.MainWindow.Visible = true;
dte.ExecuteCommand("File.OpenFile", file);
dte.ExecuteCommand("Edit.GoTo", line.ToString());
}
I can't figure out a way to do this with straight command-line options. It looks like you will have to write a macro for it. Supposedly, you can invoke them like so.
devenv /command "Macros.MyMacros.Module1.OpenFavoriteFiles"
So, you can probably create a macro that takes a filename and a line number, then opens the file and jumps to the proper place. But, I don't know that you can specify a same-instance flag somewhere, or not.
I was about to ask this question because when you get the "yellow screen of death" when debugging a web application, you want to quickly go to the file and line that it gives you in the stacktrace e.g:
[ContractException: Precondition failed: session != null]
System.Diagnostics.Contracts.__ContractsRuntime.TriggerFailure(ContractFailureKind kind, String msg, String userMessage, String conditionTxt, Exception inner) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\CustomErrorsPageController.cs:0
System.Diagnostics.Contracts.__ContractsRuntime.ReportFailure(ContractFailureKind kind, String msg, String conditionTxt, Exception inner) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\CustomErrorsPageController.cs:0
System.Diagnostics.Contracts.__ContractsRuntime.Requires(Boolean condition, String msg, String conditionTxt) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\CustomErrorsPageController.cs:0
IAS_UI.Web.IAS_Session..ctor(HttpSessionStateBase session) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Web\IAS_Session.cs:15
IAS_UI.Controllers.ServiceUserController..ctor() in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\ServiceUserController.cs:41
Say I want to go to ServiceUserController.cs at line 41. Usually I would open Visual Studio and do it manually but then I wrote a little Autohotkey script which does it.
To open it, you will highlight the filename and line number e.g. ServiceUserController.cs:41 and thereafter press your shortcut Alt + v. Here is the code for it:
$!v::
if (NOT ProcessExists("devenv.exe"))
{
MsgBox, % "Visual Studio is not loaded"
}
else
{
IfWinExist, Microsoft Visual Studio
{
ToolTip, Opening Visual Studio...
c := GetClip()
if (NOT c) {
MsgBox, % "No text selected"
}
else
{
WinActivate ; now activate visual studio
Sleep, 50
; for now assume that there is only one instance of visual studio - handling of multiple instances comes in later
arr := StringSplitF(c, ":")
if (arr.MaxIndex() <> 2) {
MsgBox, % "Text: '" . c . "' is invalid."
}
else {
fileName := arr[1]
lineNumber := arr[2]
; give focus to the "Find" box
SendInput, ^d
; delete the contents of the "Find" box
SendInput, {Home}
SendInput, +{End}
SendInput, {Delete}
; input *** >of FILENAME *** into the "Find" box
SendInput, >of{Space}
SendInput, % fileName
; select the first entry in the drop down list
SendInput, {Down}
SendInput, {Enter}
; lineNumber := 12 remove later
; open the go to line dialog
SendInput, ^g
Sleep, 20
; send the file number and press enter
SendInput, % lineNumber
SendInput {Enter}
}
}
ToolTip
}
}
return
You will want to paste the following "utility functions" before it:
GetClip()
{
ClipSaved := ClipboardAll
Clipboard=
Sleep, 30
Send ^c
ClipWait, 2
Sleep, 30
Gc := Clipboard
Clipboard := ClipSaved
ClipSaved=
return Gc
}
ProcessExists(procName)
{
Process, Exist, %procName%
return (ErrorLevel != 0)
}
StringSplitF(str, delimeters)
{
Arr := Object()
Loop, parse, str, %delimeters%,
{
Arr.Insert(A_LoopField)
}
return Arr
}
Using this command works for me, as long as Visual Studio is NOT open already.
"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" /edit "ABSOLUTEFILEPATH_FILENAME.CPP" /command "Edit.GoTo 164"
If it is already open, then sometimes it works and goes to the right line, but then it just stops working and I have never figured out why. Looks like Microsoft is aware of the issue but have said they "Will Not Fix" it, unless more people complain. So if it's still an issue I'd suggest commenting here: https://connect.microsoft.com/VisualStudio/Feedback/Details/1128717
Slightly simplified version of the answer from OnceUponATimeInTheWest:
using System.Runtime.InteropServices;
private static void OpenFileAtLine(string file, int line) {
dynamic vs = Marshal.GetActiveObject("VisualStudio.DTE");
dynamic window = vs.ItemOperations.OpenFile(path);
window.Selection.GotoLine(line, true);
}
It uses dynamics instead of Reflection to make the code a bit shorter and more readable.

Resources