none
Access DTE from Extender Vs 2005 problems

    Question

  • Hi,

    I have implemented my IExtenderProvider to display some custom properties of my projects and this is working fine with Visual Studio 2008, but when try to use my extender with Visual Studio 2005 i found that i could not acces DTE root object.
    public bool CanExtend(string category, string name, Object obj)
    {
    //This allways return null in Visual Studio 2005
    _dte = (DTE)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(DTE));

    Any ideas how could i access DTE object from here?
    Regards,
    José
    • Edited by pepone.onrez Tuesday, May 12, 2009 6:16 PM format
    Tuesday, May 12, 2009 6:13 PM

Answers

  • If you can reference and retrieve those properties off the Project object, you should be able to retrieve the Project.DTE property as well.


    Ed Dore
    • Marked as answer by pepone.onrez Monday, May 18, 2009 7:49 PM
    Monday, May 18, 2009 6:15 PM
    Moderator

All replies

  • Need a litte more background in this. Is your ExtenderProvider implemented in a VS Package?

    Note, the Package.GetGlobalService is a static "global service provider", but it needs to be initialized. Meaning, a managed package leverating the Package class would first have to be loaded.

    You don't mention when this is invoked, or how it's implemented (add-in, package, simple COM server). That will have some bearing as to why you're getting a null back on that call.

    Sincerely,

    Ed Dore
    Wednesday, May 13, 2009 9:22 PM
    Moderator
  • Hi Ed,

    My ExtenderProvider is implemented in a C# library that is register for COM usage, i also need to register the extender in windows registry so it is loaded
    by Visual Studio.

    So as far as i undertand COM that is not too much, my dll exports a COM type that Visual Studio load by is GUID and ProgramId.

    #if VS90
        [GuidAttribute("D1F0ACA3-749A-4aae-AC7F-5CFF2A620867"), ProgId("My.VisualStudio.ExtenderProvider")]
    #else
        [GuidAttribute("90C07B62-C9A2-434b-8F4D-061DF63873E6"), ProgId("My.VisualStudio.ExtenderProvider")]
    #endif
        public class ExtenderProvider : Object, EnvDTE.IExtenderProvider
        {
    //....
    I have added the conditional because i link the plugin for Visual Studio 2008 and 2005 with the correspoding VCProject and VCProjectEngine dlls.

    But the GlobalService seems that is some how not correct initialized when i use Visual Studio 2005,

    I finally work arround this problem by using the following code.

            public static DTE getCurrentDTE()
            {
                EnvDTE.DTE dte = (EnvDTE.DTE)Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(EnvDTE.DTE));
                if(dte != null)
                {
                    return dte;
                }
                System.Runtime.InteropServices.ComTypes.IRunningObjectTable rot;
                int uret = GetRunningObjectTable(0, out rot);
                if(uret == 0)
                {
                    System.Runtime.InteropServices.ComTypes.IEnumMoniker enumMon;
                    rot.EnumRunning(out enumMon);
                    if(enumMon != null)
                    {
                        // object limit
                        const int numrows = 5000;
                        IMoniker[] mons = new IMoniker[numrows];
                        IntPtr fetched = IntPtr.Zero;
                        enumMon.Next(numrows, mons, fetched);
                        // i'm creating binding to monikers.
                        string name;
                        IBindCtx ctx;
                        uret = CreateBindCtx(0, out ctx);
                        // ROT name of _DTE using id process
                        System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
                        string dteName = "VisualStudio.DTE";
                        for(int i = 0; mons[i] != null; ++i)
                        {
                            // i take the name
                            mons[i].GetDisplayName(ctx, null, out name);
                            // found?
                            if(name.IndexOf(dteName) != -1)
                            {
                                object temp;
                                rot.GetObject(mons[i], out temp);
                                dte = (EnvDTE.DTE)temp;
                                if(dte.MainWindow.HWnd == currentProcess.MainWindowHandle.ToInt32())
                                {
                                    break;
                                }
                            }
    
                        }
                    }
                }
                return dte;
            }
    
            [DllImport("ole32.dll")]
            public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
    
            [DllImport("ole32.dll")]
            public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
    So when my ExtenderProvider CanExtend is called i call this getCurrentDTE , whith vs 2008 it allways gets DTE from GetGlobalService, but for vs 2005 not and the other work around works.

    But maybe is a better way to get this working on both versions.

    Regards,
    Jose
    Wednesday, May 13, 2009 9:35 PM
  • Hi Jose,

    You may be able to retrieve it off of one of the arguments passed into GetExtender? Can you defer retrieval of the DTE interface until GetExtender is called? What exactly are you attempting to extend?


    Ed Dore
    Thursday, May 14, 2009 1:50 AM
    Moderator
  • Hi Ed,

    I think that i really need to do this in CanExtend to enabled/disable the extender depending on a project global property. My addin has commands to enable/ disabled the extender and it sets a project global property that idicate this, in my CanExtend implementation i read this project property to know if i should extend.

    Maybe i missurdestanding how CanExtend should be implemented? 

    When i first develop this only on vs 2008 i was getting the project from UIHierarchyItem selected items but this begin to not work inside CanExtend when test with vs2005 and was related to the dte problem.

    I also get similar problems in the context of UITypeEditor i have build to edit my extender properties, while i do the same inside EditValue implementation.

    So seems that there are some different behaviours in this between vs 2008 and vs 2005.



    Regards,
    José
    Thursday, May 14, 2009 2:21 AM
  • Per my earlier response, you will see a null value come back from Package.GetGlobalService, if a managed (MPF based) package has not already been loaded and sited. So you should not rely upon this function, unless your code happens to be a managed MPF based package. I suspect that in the VS 2008 scenario you are getting lucky, in that an MPF package just happened to be loaded and initialized before your CanExtend was executed.

    Is your extender implemented in an add-in? Or is it simply a stand-alone COM assembly? What specific object(s) are you attempting to extend here? Whe CanExtend is invoked, you may be able to retrieve the DTE interface through the object passed as the ExtendeeObject.

    Sincerely,


    Ed Dore
    Thursday, May 14, 2009 8:18 PM
    Moderator
  • Hi Ed,

    I have the extender in a stand-alone COM assembly, i trying to extend Project object browser for c++ and c# project categories, I hava also and add-in in a
    separate assembley, maybe it could be better to put both in a single assembley?

    >Whe CanExtend is invoked, you may be able to retrieve the DTE interface through the object passed as the ExtendeeObject.

    How can i do that? 

    Regards,
    José
    Thursday, May 14, 2009 8:28 PM
  • Hi Ed,

    Finally i have moved the extender to the same assembley of the addin , and i get the DTE reference from a static method in my Addin, as the extender not need to work without the addin this works fine for me, so i think i finally go to use this aproach.

    Regards,
    José
    Thursday, May 14, 2009 11:22 PM
  • That was going to be my 'other' recommendation, after you mentioned you had an add-in already. But I'm still thinking you can probably retrieve the DTE interface from the ExtendeeObject. For example, if you are extending an EnvDTE.Project object, you would simply cast the ExtendeeObject to (EnvDTE.Project), then you could get at the DTE interface throught the DTE property on the EnvDTE.Project interface.

    But I'm still not quite sure what you are actually extending here. I don't know what the Project object browser C++ is. If you post the CATID's you're actually using, that might clue me in to what specific objects you are attempting to extend.

    Sincerely,
    Ed Dore
    Friday, May 15, 2009 5:09 PM
    Moderator
  • Hi Ed,

    i check for this two categories:
    private bool isProjectCategory(string category)
    {
        if(category.ToUpper() == vcprojCATIDS.vcprojCATIDProjectNode.ToUpper() ||
           category.ToUpper() == VSLangProj.PrjBrowseObjectCATID.prjCATIDCSharpProjectBrowseObject.ToUpper())
          {
              return true;
          }
          return false;
    }
    In CanExtend call this operation to check is the correct category, and then i get the project by reading the ProjectProperty and search for the EnvDTE.Project in the DTE.Solution.Projects

    public bool CanExtend(string category, string name, Object obj)
    {
        Project project = getProjectFromExtender(obj);
        if(project == null)
        {
    	return false;
        }
        if(!Util.isSliceBuilderEnabled(project))
        {
    	return false;
        }
    
        if(name.ToUpper() == extensionName.ToUpper() && isProjectCategory(category))
        {
    	return true;
        }
        return false;
    }
    
    
    private Project getProjectFromExtender(object obj)
    {
        //
        // Get project properties to search Project object in DTE.Solution.Projects
        // FullPath should containt the project file full path for c# project.
        //
        PropertyDescriptor fullPathPropertyDesc = TypeDescriptor.GetProperties(obj)["FullPath"];
        PropertyDescriptor namePropertyDesc = TypeDescriptor.GetProperties(obj)["FileName"];
        if(fullPathPropertyDesc == null || namePropertyDesc == null)
        {
    	//
    	// VC++ projects not define a property named FullPath they define ProjectFile
    	//
    	namePropertyDesc = TypeDescriptor.GetProperties(obj)["ProjectFile"];
    	if(namePropertyDesc == null)
    	{
    	    return null;
    	}
        }
        string fullPath = "";
        if(fullPathPropertyDesc != null)
        {
    	fullPath = fullPathPropertyDesc.GetValue(obj) as string;
        }
        string fileName = namePropertyDesc.GetValue(obj) as string;
        
        if(!String.IsNullOrEmpty(fullPath))
        {
    	fullPath = Path.Combine(fullPath, fileName);
        }
        else
        {
    	fullPath = fileName;
        }
    
        // find corresponding EnvDTE.Project object by its full path
        Project project = null;
        foreach(Project p in _dte.Solution.Projects)
        {
    	if(p.FileName.Equals(fullPath, StringComparison.CurrentCultureIgnoreCase))
    	{
    	    project = p;
    	    break;
    	}
        }
        return project;
    }
    It will be good if i can get the EnvDTE.Project object more directly
    Friday, May 15, 2009 5:20 PM
  • If you can reference and retrieve those properties off the Project object, you should be able to retrieve the Project.DTE property as well.


    Ed Dore
    • Marked as answer by pepone.onrez Monday, May 18, 2009 7:49 PM
    Monday, May 18, 2009 6:15 PM
    Moderator