I am trying to build a VSIX extension which Starts debugger in currently selected project and runs a method within that project.
I am able, using reflection to instantiate and call the method. However I would like my extension to start the debugger so that I can use breakpoints etc.
I have been able to build the projects by accessing the dte object, but cant see how I launch the debugger and run code.
foreach (Project project in dte.Solution.Projects)
{
dte.Solution.SolutionBuild.BuildProject("Debug", project.UniqueName);
}
I did something similar in the past. The code is in VB but it's very simple and you can easily convert it to C#.
Your code to build the project is OK. But if you have more Platforms available, such as "x86" and "Any CPU", the SolutionBuild.BuildProject takes a random platform. So it's better to explicitly specify it, like:
dte.Solution.SolutionBuild.BuildProject("Debug|Any CPU", project.UniqueName);
You can get the current configuration name and platform from:
Dim solutionConfiguration As SolutionConfiguration2 = CType(dte.Solution.SolutionBuild.ActiveConfiguration, SolutionConfiguration2)
Then you cannot start debugging particular method. DTE can only start to debug the startup object, e.g. Main. Exactly as if you press F5. If you want to start any particular method, it is more difficult.
You wrote, you can instantiate and call the method with reflection. This is OK. But remember, you cannot debug the process itself. This is how Windows works. It means, Visual Studio process can only debug ANOTHER process. So you cannot instantiate your compiled assembly in VSIX process (which is in fact a VS process).
You must create a new process and load your assembly in it. I don't have a code how to do it. Just for your information, the path of the compiled assembly can be retrieved like this:
Private Shared Function GetAssemblyPath(proj As EnvDTE.Project) As String
Dim fullPath As String = proj.Properties.Item("FullPath").Value.ToString()
Dim outputPath As String = proj.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString()
Dim outputDir As String = Path.Combine(fullPath, outputPath)
Dim outputFileName As String = proj.Properties.Item("OutputFileName").Value.ToString()
Dim assemblyPath As String = Path.Combine(outputDir, outputFileName)
Return assemblyPath
End Function
When you have the process and its ID, you must attach Visual Studio to this process:
Friend Sub AttachDebuggerToProcess(processId As Integer)
Dim dte As DTE2 = TryCast(Package.GetGlobalService(GetType(SDTE)), DTE2)
If dte Is Nothing Then
Return
End If
Dim proc As Process2 = Nothing
For Each process As Process In dte.Debugger.LocalProcesses
If process.ProcessID = processId Then
proc = process
Exit For
End If
Next
If proc Is Nothing Then
' report error
Return
End If
Try
If Not proc.IsBeingDebugged Then
proc.Attach()
End If
Catch exx As Exception
End Try
End Sub
And only now you can call your method with reflection. If there's a breakpoint set, it will be hit.
Related
In my Visual Studio Extension, I need to detect whether a C++ project is managed or unmanaged code.
Previously, I had a satisfactory method, described in this posting in an MSDN forum.
In that example, it was necessary to get the ManagedExtensions property of the active configuration.
Sub Macro1()
Dim objProject As EnvDTE.Project
Dim objConfiguration As EnvDTE.Configuration
Dim objProperty As EnvDTE.Property
For Each objProject In DTE.Solution.Projects
objConfiguration = objProject.ConfigurationManager.ActiveConfiguration()
objProperty = objConfiguration.Properties.Item("ManagedExtensions")
System.Windows.Forms.MessageBox.Show(objProject.Name & " (" & objConfiguration.ConfigurationName & ") ManagedExtensions: " & objProperty.Value.ToString)
Next
End Sub
Unfortunately, this method is no longer working for me.
For unmanaged projects, I get an exception trying to fetch the ActiveConfiguration.
For managed projects, I can get the ActiveConfiguration, but the ManagedExtensions property is not available. In fact, I think that the properties collection is empty.
Is there a new way to recognize an unmanaged C++ project?
You can get the ManagedExtensions property via the VCConfiguration object, with code something like
Private Enum compileAsManagedOptions
managedNotSet = 0
managedAssembly = 1
managedAssemblyPure = 2
managedAssemblySafe = 3
managedAssemblyOldSyntax = 4
End Enum
Dim VCProj As Object 'VCProject
Dim VCConfig As Object 'VCConfiguration
Dim VCManagedOption As compileAsManagedOptions = compileAsManagedOptions.managedAssemblyPure
VCProj = prj.Object
If VCProj IsNot Nothing Then
VCConfig = VCProj.Configurations.Item(1)
If VCConfig IsNot Nothing Then
VCManagedOption = VCConfig.ManagedExtensions
End If
End If
where prj is the Envdte.Project object.
This code is only executed if I already know that it is a C++ project, based on the project kind.
I defined the variables as object, so that I don't have to add a reference to
Microsoft.VisualStudio.VCProject.dll
to my package, because this DLL will only be present if support for C++ projects has been installed.
I've been tasked with making a change to a legacy VB6 Winform app. What I found is that this app was unnecessarily split up into multiple DLLs (some of the DLL were simply a couple of classes). So, I'm working on consolidating some of the DLLs into the main program but I've run into a problem that I could use some help on.
One of the dlls contained a class called CTest(Test.cls). The main program used it in the following lines of code. strProgId is a string naming another DLL.
Dim objTest As CTest
Set objTest = CreateTestObject(strProgId)
Public Function CreateTestObject(strProgId As String) As Object
10 On Error GoTo ErrorHandler
20 Set CreateTestObject = CreateObject(strProgId)
30 Exit Function
ErrorHandler:
40 UpdateErrorInfo "CreateTestObject", "Globals", strProgId
50 HandleError
End Function
Here are the contents of CTest
Option Explicit
Private m_strName As String
Private m_strDescription As String
Private m_cnnADO As ADODB.Connection
Public Property Get Name() As String
10 Name = m_strName
End Property
Public Property Let Name(strNewName As String)
10 m_strName = strNewName
End Property
Public Property Get Connection() As ADODB.Connection
10 Set Connection = m_cnnADO
End Property
Public Property Set Connection(cnnADO As ADODB.Connection)
10 Set m_cnnADO = cnnADO
End Property
Public Property Get Description() As String
10 Description = m_strDescription
End Property
Public Property Let Description(strNewDescription As String)
10 m_strDescription = strNewDescription
End Property
Public Function Run(ByVal strSTMType As String, _
instInstruments As CInstruments, objResults As CTestResults) As Boolean
End Function
If CTest is still part of a DLL and I have a reference to it in the Main Program, it gets through the CreateTestObject line without an error. If I bring in the class into the main program it throws a type mismatch error.
Any help is appreciated, thank you in advance.
CreateObject will only work with publicly visible COM classes. Therefore, because you've brought CTest into your main program, CreateObject will no longer work and will raise errors just like you describe.
Either
Create the object via Set obj = New CTest
Or just leave the class in a separate DLL? Are you sure there's no other side effects of it being in a separate DLL? No other app using it?
I just solved this one after a day and a half. In my case I invoke the dll twice. The first time it worked and the second time it threw the error above. I have several projects open and each has its' own compatibility setting. For some unexplained reason the second reference to the common dll had compatibility set off. By setting the correct path in the version compatability and setting it to binary compatibility the problem cleared up.
If you're bringing CTest into your main program directly, then you don't need the CreateObject call - just instantiate it the normal way, now that it's part of your program, and it should work fine.
I want to notify the user of the macro if something went wrong during the execution of the macro. I was wondering if it would be possible to add an item to the Visual Studio error list?
It is possible to do so from within an AddIn (like here), but I would like to do the same thing from a macro.
Edit
To further clarify what i want to achive, here is the sample from the Samples macro library (Alt+F8 -> Samples -> Utilities -> SaveView())
Sub SaveView()
Dim name As String
name = InputBox("Enter the name you want to save as:", "Save window layout")
If (name = "") Then
MsgBox("Empty string, enter a valid name.")
Else
DTE.WindowConfigurations.Add(name)
End If
End Sub
Instead of the MsgBox("...") alert I want to put the error into the VS error list.
You can add an item in the Task List easily from your macro. Just use the AddTaskToList method from that article and change m_objDTE to DTE. I've tried it and it worked.
However, adding the item in Error List, is probably impossible. You need to call VS services, see how adding an error is done in an add-in. I created a macro from this code and it didn't work. In general, VS services don't work in macros. I was able to create ErrorListProvider successfully. I could access it's methods and properties. But calling ErrorListProvider.Task.Add caused COM exception. If you want to play with it, several notes:
As described in the article, you need to get 4 assemblies out of the GAC e.g. to c:\dlls\ directory. Since Macros IDE doesn't allow you to browse when you Add Reference, you need to copy these dlls into ...\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies directory (change the 10.0 to your VS version). Then, when you Add Reference in Macros IDE, you should see the assemblies.
The GetService function always returned Nothing. Add the following field to the class:
Private serviceProvider As IServiceProvider = New Microsoft.VisualStudio.Shell.ServiceProvider(CType(DTE, Microsoft.VisualStudio.OLE.Interop.IServiceProvider))
and in GetService function change line:
objService = Microsoft.VisualStudio.Shell.Package.GetGlobalService(serviceType)
to
objService = serviceProvider.GetService(serviceType)
As I wrote, everything seems OK then but ErrorListProvider.Task.Add fails.
I think that for your situation outputting something to your own output pane would be more suitable. The error list is generally used for errors within the project the user is working on, not for errors caused by running macros. Especially when someone says it can't be done. :)
Outputting to your own output pane is pretty easy:
DTE.Windows.Item(Constants.vsWindowKindOutput).Activate()
Dim panes As OutputWindowPanes = window.OutputWindowPanes
Dim my_pane As OutputWindowPane
Try
my_pane = panes.Item("SaveView")
Catch exception As System.ArgumentException
my_pane = panes.Add("SaveView")
End Try
my_pane.Activate()
my_pane.OutputString("Empty string, enter a valid name." + vbCrLf)
Hope this helps.
Cheers,
Sebastiaan
Is this not what you want?
HOWTO: Add an error with navigation to the Error List from a Visual Studio add-in
http://www.mztools.com/articles/2008/MZ2008022.aspx
I want to count how many times i built on VS.NET .
I have a plan. If i run svn commit when i build to project, i would have build count on revision number :)
I didn't write any macro which can execute a command. And i don't know visual studio is allowing to prebuild event for this kind of request.
You can use pre/post build events.
In your project properties, go to the Build events tab - there is space for both pre build and post build commands.
See this blog post on how incrementing build numbers can be done.
Yes this is possible.
Open up the Macros IDE
Open the file called environmentEvents
This file has the various DTE event objects declared and you can easily add a handler like so.
Private Sub PostBuild() Handles BuildEvents.OnBuildDone
' Your code here
End Sub
Also can use:
Dim WithEvents myTimer As Timers.Timer
Sub CustomBuild()
DTE.ExecuteCommand("Build.BuildAll")
myTimer = New Timers.Timer
myTimer.Interval = 0.05
myTimer.Start()
End Sub
Sub myTimer_Elapsed(ByVal ee As Object, ByVal dd As Timers.ElapsedEventArgs) Handles myTimer.Elapsed
If DTE.Solution.SolutionBuild.BuildState <> vsBuildState.vsBuildStateInProgress Then
myTimer.Stop()
End If
If DTE.Solution.SolutionBuild.BuildState <> vsBuildState.vsBuildStateInProgress And DTE.Solution.SolutionBuild.LastBuildInfo <> 1 Then
... build was successful ...
End If
End Sub
How to get all the functions you have in a code file in Visual Studio using VS macros?
I`m using Visual Studio 2008.
Also I need to get whether function is private protected or public. For now I know I can just parse the code and check it on my own, but I want to make it in a proper way and think vs macros environment should allow know all info about functions.
See HOWTO: Navigate the code elements of a file from a Visual Studio .NET macro or add-in
An maybe HOWTO: Navigate the files of a solution from a Visual Studio .NET macro or add-in would be interesting for you.
Getting function accessibility is easy. Following the first article, you have CodeElement object. If it is of type CodeFunction, you can cast it to CodeFunction (or also to CodeFunction2) type. The CodeFunction contains many properties including Access which is what you need. I have modified ShowCodeElement from this article so it only shows functions and also displays their accessibility:
Private Sub ShowCodeElement(ByVal objCodeElement As CodeElement)
Dim objCodeNamespace As EnvDTE.CodeNamespace
Dim objCodeType As EnvDTE.CodeType
Dim objCodeFunction As EnvDTE.CodeFunction
If TypeOf objCodeElement Is EnvDTE.CodeNamespace Then
objCodeNamespace = CType(objCodeElement, EnvDTE.CodeNamespace)
ShowCodeElements(objCodeNamespace.Members)
ElseIf TypeOf objCodeElement Is EnvDTE.CodeType Then
objCodeType = CType(objCodeElement, EnvDTE.CodeType)
ShowCodeElements(objCodeType.Members)
ElseIf TypeOf objCodeElement Is EnvDTE.CodeFunction Then
Try
Dim msg As String = objCodeElement.FullName & vbCrLf
Dim cd As EnvDTE.CodeFunction = DirectCast(objCodeElement, CodeFunction)
Select Case cd.Access
Case vsCMAccess.vsCMAccessDefault
msg &= "Not explicitly specified. It is Public in VB and private in C#."
Case Else
msg &= cd.Access.ToString
End Select
MsgBox(msg)
Catch ex As System.Exception
' Ignore
End Try
End If
End Sub
Change it and execute ShowFileCodeModel macro then.