I am building an internal development tool to manage different processes commonly used in our development environment. The tool shows the list of the monitored processes, indicating their running state and allows to start or stop each process.
I'd like to add the functionality of attaching a debugger to a monitored process from my tool instead of going in Debug -> Attach to process in Visual Studio and finding the process.
My goal is to have something like Debugger.Launch() that would show a list of the available Visual Studio. I can't use Debugger.Launch(), because it launches the debugger on the process that makes the call. I would need something like Debugger.Launch(processId).
How do I achieve this functionality?
A solution could be to implement a command in each monitored process to call Debugger.Launch() when the command is received from the monitoring tool, but I would prefer something that does not require to modify the code of the monitored processes.
Side question:
When using Debugger.Launch(), instances of Visual Studio that already have a debugger attached are not listed. Visual Studio is not limited to one attached debugger, you can attach on multiple process when using Debug → Attach to process.
How do I bypass this limitation when using Debugger.Launch() or an alternative?
A coworker ended up with a solution using DTE, and I posted the code on PasteBin.
The methods of interest are AttachVisualStudioToProcess and TryGetVsInstance
Source Code
public static void AttachVisualStudioToProcess(Process visualStudioProcess, Process applicationProcess)
{
_DTE visualStudioInstance;
if (TryGetVsInstance(visualStudioProcess.Id, out visualStudioInstance))
{
//Find the process you want the Visual Studio instance to attach to...
DTEProcess processToAttachTo = visualStudioInstance.Debugger.LocalProcesses.Cast<DTEProcess>().FirstOrDefault(process => process.ProcessID == applicationProcess.Id);
// Attach to the process.
if (processToAttachTo != null)
{
processToAttachTo.Attach();
ShowWindow((int)visualStudioProcess.MainWindowHandle, 3);
SetForegroundWindow(visualStudioProcess.MainWindowHandle);
}
else
{
throw new InvalidOperationException("Visual Studio process cannot find specified application '" + applicationProcess.Id + "'");
}
}
}
private static bool TryGetVsInstance(int processId, out _DTE instance)
{
IntPtr numFetched = IntPtr.Zero;
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
IMoniker[] monikers = new IMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
if (runningObjectVal is _DTE && runningObjectName.StartsWith("!VisualStudio"))
{
int currentProcessId = int.Parse(runningObjectName.Split(':')[1]);
if (currentProcessId == processId)
{
instance = (_DTE)runningObjectVal;
return true;
}
}
}
instance = null;
return false;
}
WinDbg does the chain debugging for native code by default. If you want to launch another instance of Visual Studio, check Launch the Debugger Automatically on MSDN:
To automate the existing debugger, use Marshal.GetActiveObject to get the current EnvDTE.Debugger then let it attach to the process you just created.
Sometimes, you may need to debug the startup code for an application that is launched by another process. Examples include services and custom setup actions. In these scenarios, you can have the debugger launch and automatically attach when your application starts.
To setup an application to launch the debugger automatically
Start the Registry Editor (regedit).
In the Registry Editor, open the HKEY_LOCAL_MACHINE folder.
Navigate to HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\currentversion\image file execution options.
In the Image File Execution Options folder, locate the name of the application you want to debug, such as myapp.exe. If you cannot find the application you want to debug:
a. Right-click the Image File Execution Options folder, and on the shortcut menu, click New Key.
b. Right-click the new key, and on the shortcut menu, click Rename.
c. Edit the key name to the name of your application; myapp.exe, in this example.
Right-click the myapp.exe folder, and on the shortcut menu, click New String Value.
Right-click the new string value, and on the shortcut menu, click Rename.
Change the name to debugger.
Right-click the new string value, and on the shortcut menu, click Modify.
The Edit String dialog box appears.
In the Value data box, type vsjitdebugger.exe.
Click OK.
From the Registry menu, click Exit.
The directory containing vsjitdebugger.exe must be in your system path. To add it to the system path, follow these steps:
a. Open the Control Panel in Classic view, and double-click System.
b. Click Advanced System Settings.
c. In System Properties, click the Advanced tab.
d. On the Advanced tab, click Environment Variables.
e. In the Environment Variables dialog box, under System variables, select Path, then click the Edit button.
f. In the Edit System Variable dialog box, add the directory to the Variable value box. Use a semicolon to separate it from other entries in the list.
g. Click OK to close the Edit System Variable dialog box.
h. Click OK to close the Environment Variables dialog box.
i. Click OK to close the System Properties dialog box.
Now, use any method to start your application. Visual Studio will start and load the application.
Here is some information about how you can programmatically attach the debugger to multiple processes:
Attach to locally running processes
Attach to remotely running processes
Related
I am writing an extension to Visual studio 2012 using VSPackage. I need to add a context menu entry to Test Explorer and on click of this menu item, I need to get the selected unit test(s). I tried to add an item using
((CommandBars)DTE.CommandBars)["Test Window Context Menu"].Controls.Add(Type: MsoControlType.msoControlButton);
and adding an event handler by subscribing to the event
DTE.Events.CommandBarEvents[command].Click
I succeeded in adding an item to Context menu but the Click event handler never gets fired. MSDN said, I needed to set the OnAction property of the command to a valid string value for the Click event handler to get fired. It didn't work either.
Then, I figured out I needed to add a command through the VSCT file in a VSPackage. However, I am not able to find the Test Window Context menu so that I can attach the command to it. Also, I need to get all the unit tests (TestCase objects) listed in the Test Explorer.
Any help is greatly appreciated!
Usually these are the files I look for Visual Studio shell GUIDs or command, context menu, group, etc IDs:
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Common\Inc\stdidcmd.h
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VSSDK\VisualStudioIntegration\Common\Inc\vsshlids.h
Actually they are included in the top of your newly created .vsct file (<Extern href="vsshlids.h" />). I guess you've already checked them. I did a quick search, but what I found for "Test" is just a test ribbon and a test dialog. Probably now that you're looking for. It might be still useful for someone finding this post.
You might also want try it brute force style:
Search your Program Files (x86)\Visual Studio [VERSION] for regexp: ^#define.*TEST.*$
This shall give you the defines containing TEST.
Also you might want to consider asking Microsoft directly.
I wrote some exploratory code to loop over commands in that context menu. I also played around with registering a priority command target and seeing what group GUID and command ID I got. The GUID for that context menu appears to be {1e198c22-5980-4e7e-92f3-f73168d1fb63}. You can probably use that to add a command via the .vsct file without using the DTE.CommandBars to add it dynamically.
Here's my experiment code which lists the GUID and command ID of the commands currently in that context menu, in case it helps anyone.
var bars = ((Microsoft.VisualStudio.CommandBars.CommandBars)DTE.CommandBars);
var teContextMenu = bars["Test Window Context Menu"];
var ctls = teContextMenu.Controls;
foreach (var ctl in ctls)
{
var cmdCtl = ctl as Microsoft.VisualStudio.CommandBars.CommandBarControl;
string guid; int id;
DTE.Commands.CommandInfo(ctl, out guid, out id);
Debug.WriteLine($"{cmdCtl?.accName} {guid} {id}");
}
This article on command routing was helpful to me:
https://learn.microsoft.com/en-us/visualstudio/extensibility/internals/command-routing-algorithm
My experimental priority command target, where I set a breakpoint to see what GUID and command IDs were sent, is registered as follows. The TestCommandInterceptor class is a bare-bones implementation of IOleCommandTarget.
var cmdService = GetService(typeof(SVsRegisterPriorityCommandTarget)) as IVsRegisterPriorityCommandTarget;
var target = new TestCommandInterceptor();
cmdService.RegisterPriorityCommandTarget(0, target, out _testCmdInterceptorRegistrationCookie);
I would still like to know the answer to the second part of this question about how to determine the selected tests.
I used the VS 2010 SDK to create and show a custom ToolWindowPane with a WPF control as content. I create a new instance and show it each time a Tool menu item is clicked (the ProvideToolWindow attribute has MultiInstances = true).
When the user attaches the debugger (e.g., hits F5 while in C# project) my ToolWindowPane suddenly hides. I'd like to make sure my tool window is always visible while open, no matter what context the user is in. Is there a way I can enforce that?
I've tried using the ProvideToolWindowVisibility attribute but that automatically shows a new instance of my tool window rather than keeping a remaining one open.
For VS 2010 SDK Microsoft added a new flag __VSCREATETOOLWIN2.CTW_fDocumentLikeTool
You can use this way:
public override void OnToolWindowCreated()
{
IVsWindowFrame windowFrame = Frame as IVsWindowFrame;
object varFlags;
windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_CreateToolWinFlags, out varFlags);
int flags = (int)varFlags | (int)__VSCREATETOOLWIN2.CTW_fDocumentLikeTool;
windowFrame.SetProperty((int)__VSFPROPID.VSFPROPID_CreateToolWinFlags, flags);
}
This way Tool Window persist open at "Document Well" when you go Debugging
However I have to say this give us some problems when debugging projects, avoiding us to open code files while debugging, like if Visual Studio document management was 'block', there are not so much information for this new flag...
So we preferred to hook to EnvDTE.DebuggerEvents and show the ToolWindow if hide when a debugging session start...
(our ToolWindow has MultiInstances = false)
Implement QueryShowTool
public:
int QueryShowTool(Guid % rguidPersistenceSlot, System::UInt32 dwId, [Runtime::InteropServices::Out] int % pfShowTool);
Enables the VSPackage to control whether to show or hide the tool
window. The shell calls this method when the user switches views or
contexts, for example Design, Debugging, Full Screen.
See https://learn.microsoft.com/en-us/visualstudio/extensibility/opening-a-dynamic-tool-window?view=vs-2017
I have a command line executable I built which is published on the network via ClickOnce. The main use of this tool is through Visual Studio as an external tool. When I set this up in Visual Studio I am able to set the command path to the shortcut under roaming data for my profile.
However, Visual Studio resolves this to a path such as:
C:\Users\ME\AppData\Local\Apps\2.0\CGR50YPV.W5E\RXBXM176.HH8\crea..tion_f423fce0316e1dfa_0001.0000_adecafbe6c6acba3\MyAppp.exe
So what happens is if I launch the exe and grab a new version, Visual Studio is still pointing at the old version (as indicated above). I can fix this by re-pointing the command value of my external tool to the shortcut of my exe, but this is a bit frustrating to deal with.
How can I make this work without having to update my command path every time?
You shouldn't access a ClickOnce application via the exe file. If you're going to do that, just xcopy the \bin folder of the application to the other machine. If you want to use the update features, you should always invoke the ClickOnce application by using the shortcut or by invoking the link to the deployment manifest on the webserver. (The deployment manifest is the application file). You can do a process.start on that link.
[edit -- add new info]
Ohhhhh, so you're accessing the shortcut in the folder under the user's profile? Am I getting that? Instead of looking for that one, can you point to the shortcut on the start menu? It will add one automatically when the user installs the application, if the application is online/offline. The shortcut is added to the start menu to the location of the Publishing Company / Product Name using those fields from the Options dialog.
I do this by setting the assembly information to the same values, and retrieving the assembly information programmatically. I always set the assembly description to be identical to the product name, and the assembly company to be the same as the publishing company. Then I can do this:
Assembly code = Assembly.GetExecutingAssembly();
string company = string.Empty;
string description = string.Empty;
if (Attribute.IsDefined(code, typeof(AssemblyCompanyAttribute)))
{
AssemblyCompanyAttribute ascompany =
(AssemblyCompanyAttribute)Attribute.GetCustomAttribute(code,
typeof(AssemblyCompanyAttribute));
company = ascompany.Company;
}
if (Attribute.IsDefined(code, typeof(AssemblyDescriptionAttribute)))
{
AssemblyDescriptionAttribute asdescription =
(AssemblyDescriptionAttribute)Attribute.GetCustomAttribute(code,
typeof(AssemblyDescriptionAttribute));
description = asdescription.Description;
}
if (company != string.Empty && description != string.Empty)
{
string shortcutName =
string.Concat(Environment.GetFolderPath(Environment.SpecialFolder.Programs),
\", company, "\\", description, ".appref-ms");
}
(Sorry, I can't figure out how to make the code format prettier and show the indents properly, but you get the idea.)
I tried the following steps in order to debug a particular custom timer ( installed and activated):
Copied the both .dll and .pdb files in the GAC.
Restarted the timer services.
Attached both w3wp and OWSTimer.exe processes.
But the debugging is still not taking place. The debugger placed is empty circle which displays this message:
The breakpoint will not currently be hit. No symbols have been loaded for this document.
The OWSTimer is shown in a diff username. Does It needs to be run from my account?
Why debugging is not working?
Debugging Timer Jobs can be hard... The steps you took sound about right, but you can also do some more:
Timer Jobs run in OWSTimer.exe - you only need to attach to that one
Restart the timer service. For good measure throw in a restart, deploy, restart, iisreset ;-)
Did you do a DEBUG Build or RELEASE build?
Make sure you actually RUN your timer job (as in trigger it)
If your breakpoints are still not hit, do something ugly: use Debugger.Launch() or Debugger.Break() in your code or an assertion which will always fails: System.Diagnostics.Trace.Assert(false);
And then there is MSDN for the rescue.
Try loading debug symbols manually and see what it says:
To display the Modules window in break mode or in run mod
On the Debug menu, choose Windows, and then click Modules.
By default, the Modules window sorts modules by load order. However,
you can choose to sort by any column.
In the Modules window, you can see which modules have debugging
symbols loaded. This information appears in the Symbol Status column.
If the status says Skipped loading Cannot find or open the PDB file,
or Loading disabled by include/exclude setting, you can direct the
debugger to download symbols from the Microsoft public symbol servers
or to load symbols from a symbol directory on your computer. For more
information, see How to: Use a Symbol Server and How to: Specify
Symbol Locations and Loading Behavior.
To load symbols manually
In the Modules window, right-click a module for which symbols have not
loaded.
Point to Load Symbols From and then click Microsoft Symbol Servers or
Symbol Path.
copied from MSDN
You can also try to delete Visual Studio cache just to be sure (from command prompt):
del /Q %LOCALAPPDATA%\Microsoft\WebsiteCache
del /Q %LOCALAPPDATA%\Temp\VWDWebCache
del /Q %LOCALAPPDATA%\Microsoft\Team Foundation\1.0\Cache
Just adding to moontear's post.
I had the same loading debug symbols issue until I added in this code to the first line of my Execute method.
public override void Execute(Guid contentDbId)
{
// If in debug mode, trigger a false assertion to give time
// to attach the debugger to the OWSTIMER.EXE process.
#if (DEBUG)
System.Diagnostics.Trace.Assert(false);
#endif
...
Check to make sure your regional settings are correct - append /_layouts/15/regionalsetng.aspx to the CA URL. If you have the wrong time zone, your job may be scheduled for a time in the past. This has hung me up more than once. If this is the case, set the correct time zone (using the url above), stop and start the timer service (either services tool or open command line - net stop sptimerv4 then net start sptimerv4). Then attach to OWSTIMER and debug.
On the Start menu, point to Administrative Tools, and then click Services.
In the Services window, make sure the SharePoint 2010 Timer service is started.
Open the Visual Studio 2010 project that contains your timer job.
Set a breakpoint in the Execute method of your job definition class.
On the Debug menu, click Attach to Process.
In the Attach to Process dialog box, c
If the Attach Security Warning dialog box is displayed, click Attach.
In the SharePoint Central Administration Web site, click Monitoring and then click Review job definitions.
Click the name of your job, and then click Run Now on the Edit Timer Job page.
Verify that the Visual Studio 2010 debugger stops execution on your breakpoint.lick OWSTIMER.EXE, and then click Attach.
I am looking for a good plugin showing Windows Explorer context menu directly from editor window in Eclipse. Does anybody know such plugin?
I'm a little late to the game with this answer, however since I found this article when trying to find a solution to this i'll post it here. There's an answer over at http://www.eclipsezone.com/eclipse/forums/t77655.html that solves this simply.
under Window -> External Tools -> External Tools Configuration
(1) Create a new Program (select Program in the tree)
(2) name it shell (or whatever you want)
(3) set the location to ${env_var:SystemRoot}\explorer.exe
(4) set the arguments to /select,${resource_loc}
(5) run it
for me it appears up in the tool bar at the top in it the little external tool run (run with a toolbox)
simple, effective and doesn't require any installation especially when all i really needed was to have a file focused, and rapidly get to the windows folder that contains it.
For people who don't want to install Aptana (It's kinda huge), here are a few plugins for a windows context menu in eclipse(and more):
contextmenu
Basic
Eclipse Navigator Extension
Basic + copy path
StartExplorer
Only opens explorer, but also does it on selected text (if it's a path) and has custom commands.
Some more info on Eclipse explorer menu's after trying them:
Failed to install (Some error with osgi)
Has 2 Eclipse context menu's:
Copy path (full, file, parent)
Show Context Menu (it's the basic version though, some of the context menu items that I can see in real Explorer don't show up here)
Has 1 Eclipse context menu (StartExplorer) with submenu's:
Show in File manager
Start Shell here
Open file with default application
Copy resource path to clipboard
Custom commands, which you can set in preferences and default ones:
Edit in notepad
echo to temp file
So, although (3) StartExplorer doesn't really have a context menu and everything sits in a submenu, the custom commands dominates in my opinion. It should allow a contextmenu through it (command to be found) or achieve what you want by cloning the behavior you want from your context menu.
It also seems like the code has been updated more recently than the others (and it supports multiple platforms)
For my custom paste I am not using the Paste from eclipse , I have created a new context menu Paste Objects by adding a new command .
I have added the handler : PasteObjectsHandler for the command which extends AbstractHandler .
Command
<command
categoryId="org.eclipse.ui.category.edit"
description="%pasteobjectscommand.description_xmsg"
id="com.test.pasteobjectscommand"
name="%pasteobjectscommand.name_xtit">
</command>
Handler
<handler
class="com.test.PasteObjectsHandler"
commandId=" com.test.pasteobjectscommand ">
</handler>
public class PasteObjectsHandler extends AbstractHandler {
#Override
public Object execute(ExecutionEvent event) {
Clipboard clipBoard = new Clipboard(Display.getDefault());
LocalTransfer instance = LocalTransfer.getInstance();
IResource clipboardData = (IResource) clipBoard.getContents(instance);
}
}
And in the handler I try to access the clipboard in the execute method . And I get null here .
I have written a plug-in that can open the Windows Explorer context menu:
ContextMenuPlugin
I wrote it a long time ago, but I still maintain it.
I will add EasyShell plugin for Eclipse, it has that functionality and more.
Have a look at that:
https://anb0s.github.io/EasyShell/
Aptana, it will give you context menu.