Visual Studio Development Bookmark and Share   
 index > Visual Studio Extensibility > GetService question
 

GetService question

Hi there,

I am trying to implement a standalone application which, given the path to a Visual Studio solution file, will start up the Visual Studio DTE, open the solution within it and then get it to run through all the public methods in the solution and,using the "find symbol" functionality, seewhere each public method is called.

I am new to VS automation and extensibility, so please bear with me! I am coding in VB and, reading thru the VS SDK docs (boy is that a task!),I am at the point where I need to use a call to IserviceProvider.GetService for the SVsObjectSearch interface IVsObjectSearch so that I can perform the Symbol find functionality.

Anyone care to give me a few clues as to how I get the call to GetService to provide me with a useful interface instead of "Nothing"? As I say, I am new to this and all the examples I can find on the interweb are for plugins and add-ons and extensions which are running within VS and just call GetService direct. I assume that the context they are running in enables them to get something useful back. In my case I creating an instance of VS DTE from a stand alone app and this is, I believe, where my problem lies, since any call to GetService needs to be in the context of the created instance, only it ain't!

Any help gratefully taken!

TIA,

/\/\
Martyn_Bannister  Tuesday, September 22, 2009 7:38 AM

Hi, TIA

For the class Package in the Microsoft.VisualStudio.Shell.Package namespace has already implemented the IserviceProvider interface, so any class derived from the Package class can use the GetService method directly. Another class called component also has the method GetService, so any class derive from it such as UserControl canget the service.

If your class derived from neither of the class I metioned before, you have to use the Package.GetGlobalService method, and the class Package is in theMicrosoft.VisualStudio.Shellnamespace.
Hope this could help you!
Thanks
Chao

Chao Kuo  Wednesday, September 23, 2009 8:26 AM

Hi Chao,

Many thanks for your reply. The application that I am writing is a stand-alone application which creates an instance of Visual Studio and wants to query its loaded solutionfrom "outside", rather than a package which VS loads on startup and is therefore bound into the VS instance.

The way I see things, because my application is not a Package or a Component, I have no access to IServiceProvider as you describe.Similarly, my applicationhas no access to Package.GetGlobalService either?

Please correct me if I am wrong.

Below is some VBcode for a fictitious console app which hopefully demonstates what I am trying to do....

<DUMMYCODE>
Imports System.ComponentModel.Design
Imports System.IO
Imports EnvDTE ' C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE.dll
Imports Microsoft.VisualStudio.Shell.Interop ' Visual Studio SDK

Module Module1

Sub Main()

' Create an instance of Visual Studio

Dim VS2008Instance As EnvDTE.DTE = Nothing

VS2008Instance = DirectCast(CreateObject("VisualStudio.DTE.9.0"), EnvDTE.DTE)

Dim arg As String = "C:\WORK\TestSolution.sln"

Dim soln As Solution = VS2008Instance.Solution

' Open a solution within it

soln.Open(arg)

' Create the groundwork necessary to query the symbols

Dim objList As IVsObjectList = Nothing

Dim serviceProvider As IServiceProvider

' Somehow, we need to get a handle on IServiceProvider!!!!

Dim fs As IVsObjectSearch = _
CType(serviceProvider.GetService(GetType(SVsObjectSearch)), IVsObjectSearch)

If fs IsNot Nothing Then

Dim SCrit(0) As VSOBSEARCHCRITERIA

With SCrit(0)

.eSrchType = VSOBSEARCHTYPE.SO_ENTIREWORD

.grfOptions = CUInt(_VSOBSEARCHOPTIONS.VSOBSO_NONE)

.szName = "SomeFunctionName"

End With

Try

fs.Find(CUInt(__VSOBSEARCHFLAGS.VSOSF_EXPANDREFS), SCrit, objList)

Catch ex As Exception

' Apparently, Find will ALWAYS throw an exception, because of a bug.
'It still succeeds however!

End Try

End If

End Sub

End Module
</DUMMYCODE>

Martyn_Bannister  Wednesday, September 23, 2009 10:32 AM
Hi Martyn,

You can only marshal the automation interfaces (which ultimately derive from IDispatch) across the process boundary. IServiceProvider, IVsObjectSearch etc are not marshallable across processes. This code will need to run in-process to DevEnv.exe.

Sincerely,
Ed Dore
Ed Dore  Wednesday, September 23, 2009 5:32 PM

Hi, Martyn

Yes, Ed Dore was right, this will make your applicationaccross the process boundary and you should run your application in-process to DevEnv. Why can not you use add-in instead, you are able to use the Service in anadd-in.

ServiceProvider sp = new ServiceProvider((IOleServiceProvider)_applicationObject);
 IVsObjectSearch fs = sp.GetService(typeof(SVsObjectSearch).GUID) as IVsObjectSearch;

Hope this could help
Thanks
Chao
Chao Kuo  Thursday, September 24, 2009 3:30 AM

We are changing the issue type to “General Discussion�because you have not followed up with the necessary information. If you have more time to look at the issue and provide more information, please feel free to change the issue type back to “Question�by opening the Options list at the top of the post window, and changing the type. If the issue is resolved, we will appreciate it if you can share the solution so that the answer can be found and used by other community members having similar questions.

Thank you!
Chao

Chao Kuo  Tuesday, September 29, 2009 9:55 AM
Hi Chao,

I didn't want to use it as an Add-In because the concept of the application is that it stands alone and separate from VS Devenv.exe. In this way, the app SHOULD be able to open multiple solutions, one after the other, and do its processing on each one. If I use an Add-In from within VS, I am limited to the solution that VS currently has loaded.

Hmmm... this is looking like it won't work!

Rgds,

/\/\
Martyn_Bannister  Wednesday, September 30, 2009 12:38 PM
Hi Martyn,
You can give a try on below approach.
Once DTE object is ready with solution, call below API to get the projects
EnvDTE.Projects VCProjects = (EnvDTE.Projects) DTE.GetObject("VCProjects");

Then loop through projects and get the CodeModel and CodeElements.

Let me know if it works. If you want details, Let me know.

Thanks & Regards
PKR

Vic Vega  Thursday, October 01, 2009 6:36 AM
Hi PKR,

Many thanks for your response. The DTE.GetObject call works for both "VCProjects" (which returns an empty list) and "VBProjects", which returns the correct list of two (in my test solution), mainly because they are VB Projects I suspect!!!!

Do you thinkI might be able to do a "GetObject" to hook into the service provider? I will look into that, but if you happen to know the object I would be looking for, I'd be grateful if you can let me know!

Rgds,

/\/\
Martyn_Bannister  Thursday, October 01, 2009 7:53 AM
Hi, Martyn
I think you should consider the words by Ed Dore, you needs is across the process bounday, Ed Dore is the developer of VSX in Microsoft, I think there is few body that have more acknowlege about VSX.
Thanks
Chao
Chao Kuo  Friday, October 02, 2009 9:52 AM
Hi Chao, Hi Ed,

Don't mean to offend. Don't know Ed personally. I take your word for it that Ed's answer is definitive and that what iI want to do cannot be done.

Rgds,

/\/\
Martyn_Bannister  Friday, October 02, 2009 10:47 AM
No offense taken Martyn.

As a point of clarification, I actually work on the Visual Studio Extensibility support team at Microsoft. I'm not a developer, though I do seem to write a lot of code :-)

You should be able to retrieve any interface that is derived from IDispatch. But many of the core IDE services are actually derived from IUnknown and there is no marshalling support that would allow you to successfully retrieve these from another process.

One additional approach you could consider is writing either an addin or package that extends the IDE, such that you can then invoke the functionality you are looking for from your external app. Meaning, you could author an addin that supported a specific command, and then invoke that command via the EnvDTE automation service. For example: the _EnvDTE.ExecuteCommand.

Or you could proffer an automation (IDispatch based) service from a VSPackage and retrieve it via _EnvDTE.GetObject. While you cannot retrieve/access services like SVsObjectSearch, there are certainly ways to work around such limitations. With VS Extensibility, there is always more than one way to skin a cat. For example, the EnvDTE.Find interface :-)

Sincerely,
Ed Dore
Ed Dore  Saturday, October 03, 2009 8:23 AM
OK Ed, many thanks for that.

Looks like my best bet is therefore to get my functionality working in an Add-In before thinking about exposing it. Thats what I'll work towards now. Thanks again for your help.

Rgds,

/\/\
Martyn_Bannister  Monday, October 05, 2009 7:21 AM
Well Ed, I must admit that this has got me tearing my hair out (and I don't have too much of that left!)

Have now succeeded in writing an addin which gets an IVsObjectSearch interface using GetService(GetType(SVsObjectSaerch)). I can supply it with an VSOBSEARCHCRITERIA structure and perform a Find,  passing an IVsObjectList by reference. What I can't now work out is how to then access the result!

The IVsObjectList returns as nothing, even though the output appears in the FindSymbolResults window, but I cannot find a way of accessing it from there either. Sorry to be a pest, but can you point me in the right direction Ed?

Rgds,

/\/\
Martyn_Bannister  Monday, October 05, 2009 10:35 AM
Hi Martyn,

Stop pulling your hair out, or you'll end up like me :-)

If the info appears in the FindSymbolResults window, I think it'll be possible to retrieve the info. I'm just not sure if we can do that through the existing automation interfaces. I'll try poking around with this tomorrow and post back here.

Sincerely,
Ed Dore
Ed Dore  Friday, October 09, 2009 4:39 AM
Hi Ed,

Many thanks for your time. I appreciate it.

I am now totally hairless. Not only could I not access the FindSymbolResults content, when I gave up on that, I started to investigate the IVsFindSymbol interface, which <QUOTE> replaces IVsObjectSearch </QUOTE>

Now I know that all Microsoft documentation is ONLY useful when you ALREADY KNOW THE SUBJECT BACKWARDS, but I can't for the life of me work out how IVsFindSymbol is meant to be used either! It has the "DoSearch" method, which sounds incredibily useful, but again the docs give no indication as to how to access the results which, you've guessed it, end up in the Find Symbol Reuslts window! Can it be that all this functionality is only there to populate this window so that the user can visually scan it? If so, then what a waste, since you could just execute  the Find Symbol command. If you are able to access the results of the search, I would have thought a paragraph or two in the docuementation on how to do that wouldn't have gone amiss.

<sob>

/\/\

Martyn_Bannister  Friday, October 09, 2009 8:45 AM
Hi Martyn,

I think I've hit on a pretty good solution you can execute from your automation client, instead of having to use an addin.

You can use the EnvDTE.Find2 interface to execute your search, and then retrieve the results from the results ToolWindow. I tested this out with the following macros. The macros run in an external process, so this should be easily converted to run in your original app. Note, after calling TextSelection.SelectAll(), the TextSelection.Text property will contain the contents of the results window.

 

' Returns a "Miscellany" OutputWindowPane
Function GetMiscDumpPane() As OutputWindowPane
   Dim ow As OutputWindow = DTE.Windows.Item(Constants.vsWindowKindOutput).Object
   Dim pane As OutputWindowPane
   Try
      pane = ow.OutputWindowPanes.Item("Miscellany")
   Catch ex As Exception
      pane = ow.OutputWindowPanes.Add(
"Miscellany")
   End Try
   Return pane
End Function

 

Sub TestSearch()
   ' Clear the results toolwindow
   Dim resultsWindow As Window = DTE.Windows.Item(Constants.vsWindowKindFindResults1)
   Dim txtSel As TextSelection = resultsWindow.Selection()
   txtSel.Delete()

   ' Do a search
   Dim find2 As Find2 = DTE.Find
   find2.MatchCase =
True
   find2.FindWhat = "Microsoft"
   find2.Target = vsFindTarget.vsFindTargetSolution
   find2.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
   find2.Action = vsFindAction.vsFindActionFindAll
   find2.ResultsLocation = vsFindResultsLocation.vsFindResults1
   find2.WaitForFindToComplete =
True
   DTE.Find.Execute()

   ' Select text in the results window
   txtSel.SelectAll()

   ' Display retrieved selection Text in our custom Output Window Pane
   Dim outputPane = GetMiscDumpPane()
   outputPane.Clear()
   outputPane.OutputString(txtSel.Text)
End Sub

Cheers,
Ed Dore
Ed Dore  Friday, October 09, 2009 6:17 PM
Hi Ed,

Many thanks for all your help. I almost hate to say this, because you have put in a lot of effort on my behalf, for which I am very grateful, but........

your code, while working fine in itself, doesn't actually do what I want! Your code uses the find2 object to do a straight text search. What I need to do is a symbol search.  The reason a straight text search doesn't meet my needs is that it will pick up all occurrences of the search string, but what I need to do is find references to functions. If I use the straight text search I need to check that the string found isn't inside a comment, isn't a substring of  a larger string, is actually a function call, rather than a string which looks like one etc. etc.

If I can access the result of a FindSymbol search, I don't need to do any of this malarkey! In addition, I believe the Symbols search gets over the problem of function calls to overloaded functions etc.

I can perform the symbol search that I want.
The search populates the FindSymbolResults window.
I can get a handle on the output window --- "DTE.Windows.Item(EnvDTE.Constants.vsWindowKindFindSymbolResults)"

I thought I could use your "TextSelection" method to access the contents, but NO!

In the immediate window...

? resultswindow
{System.__ComObject}
    System.__ComObject: {System.__ComObject}
    AutoHides: True
    Caption: "Find Symbol Results - 1 match found"
    Collection: {System.__ComObject}
    ContextAttributes: {System.__ComObject}
    Document: Nothing
    DocumentData: In order to evaluate an indexed property, the property must be qualified and the arguments must be explicitly supplied by the user.
    DTE: {System.__ComObject}
    Height: 192
    HWnd: 0
    IsFloating: False
    Kind: "Tool"
    Left: 111
    Linkable: True
    LinkedWindowFrame: Nothing
    LinkedWindows: Nothing
    Object: Nothing
    ObjectKind: "{68487888-204A-11D3-87EB-00C04F7971A5}"
    Project: Nothing
    ProjectItem: Nothing
    Selection: Nothing
    Top: 597
    Type: vsWindowTypeToolWindow {15}
    Visible: True
    Width: 1228
    WindowState: {"Error HRESULT E_FAIL has been returned from a call to a COM component."}

As you can see, the "Selection" property is Nothing, the "Object" property is Nothing. so I have no way, or so it seems, of accessing the contents.  What I really need is not the Find2 object, but the FindSymbol2 object! Or a way of getting at the FindSymbolResults window contents.

AAARRGGGHHH!!!

More hair pulling needed!

Rgds,

/\/\

Martyn_Bannister  Monday, October 12, 2009 12:04 PM
My fault, I didn't realize which toolwindow you needed here.

That "Find Symbol Results" toolwindow is an entirely different beast. It's actually an IVsLiteTreeList based creature. Unfortunately, this puts us back into the mode of having to find a way to export the data back out of processes, as there is no automation support on this particular toolwindow.

I'll have to poke around some more and see what all can be done to extract the info.



Ed Dore
Ed Dore  Monday, October 12, 2009 6:33 PM
Hi Ed,

I guessed it might be something to do with the IVsLiteTree, but can't ifnd anything in the docs to help me further with that either. Many thanks for your continuing help, persistence and patience!!!

/\/\
Martyn_Bannister  Tuesday, October 13, 2009 7:55 AM

You can use google to search for other answers

Custom Search

More Threads

• IVsPersistDocData2.SetDocDataReadOnly - seems to make Cider sick
• DDEX Provider and Retrieve Data command
• VS2005 August CTP / VS SDK Sept CTP interop assembly mismatch
• Macro for renaming fields (global variables) crashs, when it tries to rename.
• Problem loading add-in: Error message: The parameter is incorrect
• VS 2005 Package: Getting the DTE interface (unmanaged C++)
• ProjectNode.GetProjectProperty / ProjectNode.SetProjectProperty not working
• VS 2010 ctp image and message evaluation is about to expire
• How to move the methods with comments to a existed region
• Simple operations with IVsHierarchy