MS Visual Studio add-in for DSS services debugging – the value of a tool

Programs in DSS environment are called services. They are not standard windows executables and could run only under DSS application server. They also differ significantly from conventional programs in terms of ideology. But still they are programs. And no matter how thorough you unit test them, or if three quarters of code is occupied with code contract assertions, you are to debug them.

Conventional standalone application debugging process under MS Visual Studio is good and old. Average application (solution in VS terminology) could contain several (depending on architecture) components (projects in VS), but they all share single control flow (more or less). The project owning application’s single entry point is designated as “start up” and is launched under debugger when you press F5 key. No matter what component of the application you are going to debug, the entry point and “start up” project usually do not change. That is not the case with DSS services debugging.

A DSS solution also consists of several components-services. The similarity ends here. Each service is a self sufficient isolated program with its own control flow. To produce some meaningful results it interacts closely with other services. So called “manifest” files describes such bundles of services. This setup poses multiple usability problems for debugging:

  1. Each service-project has its own entry point. You have to state, which project you’d like to debug.
  2. Each VS project could contain several services. You have to state, which service inside the project exactly you’d like to debug.
  3. Each service could run in different setups, requiring starting of different manifests. You have to state, which service manifest you’d like to debug.

If the first bullet “only” requires you to constantly reset “start up” flag between projects, others are much more involved. They imply modifying debug command line (by opening project properties, selecting debug tab and manually editing debug command line parameters field to point to needed service manifest). And only then pressing F5. Each time you change a debug target. An alternate approach would be to start target manifest externally and to attach VS debugger to it. Both options are very cumbersome, ergo unbearable, which becomes obvious after a couple of hours of debugging.

A colleague of mine proposed a simple solution – to write a VS add-in allowing to launch DSS manifests under debugger directly from VS. User selects manifest file to debug from the project tree and starts debugging from the context menu. We wrote it as a VSIX package. The code for its single meaningful class is presented below, complete add-in project could be found on GitHubThe code creates a new command object, checks each time if the context menu was called for manifest file, if so makes the command menu item visible. The command itself starts selected manifest within DSS server with VS debugger attached.

DebugDssManifest

DebugDssManifestVsPackagePackage.cs

// This attribute is needed to let the shell know that this package exposes some menus.
[ProvideMenuResource("Menus.ctmenu", 1)]
//Guid VSConstants.UICONTEXT_SolutionExists
[ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}")]
[Guid(GuidList.guidDebugDssManifestVsPackagePkgString)]
public sealed class DebugDssManifestVsPackage : Package {
    const string MANIFEST_EXTENSION = ".manifest.xml";
    const string DSS_HOST_PATH = @"C:\MRDS4\bin\DssHost32.exe";
    const string DSS_HOST_SECURITY_FILE_PATH = @"C:\MRDS4\brumba\config\DssHost32.security";
    const string DSS_HOST_ARGS = @"/port:50000 /tcpport:50001 /manifest:{0} /s:{1}";

    string _selectedManifest;
    IVsDebugger _debugger;
    DTE2 _ide;

    protected override void Initialize() {
        base.Initialize();

        _debugger = GetService(typeof(IVsDebugger)) as IVsDebugger;
        _ide = (DTE2)GetService(typeof(DTE));
            
        var menuCommandID = new CommandID(GuidList.guidDebugDssManifestVsPackageCmdSet, (int)PkgCmdIDList.cmdidMyCommand);
        var menuItem = new OleMenuCommand((_, __) => DebugManifest(), menuCommandID);
        menuItem.BeforeQueryStatus += MenuItemOnBeforeQueryStatus;
        (GetService(typeof(IMenuCommandService)) as OleMenuCommandService).AddCommand(menuItem);
    }

    private void MenuItemOnBeforeQueryStatus(object sender, EventArgs eventArgs) {            
        var selectedItems = (_ide.Windows.Item(EnvDTE.Constants.vsWindowKindSolutionExplorer).Object as UIHierarchy).SelectedItems as UIHierarchyItem[];

        var itemIsManifest = selectedItems.Length == 1 && selectedItems[0].Name.ToLower().EndsWith(MANIFEST_EXTENSION);
        (sender as OleMenuCommand).Visible = itemIsManifest;

        if (itemIsManifest)           
            _selectedManifest = (selectedItems[0].Object as ProjectItem).Properties.Item("FullPath").Value as string;
    }

    private void DebugManifest() {
        var info = new VsDebugTargetInfo {
            bstrExe = DSS_HOST_PATH,
            bstrArg = string.Format(DSS_HOST_ARGS, _selectedManifest, DSS_HOST_SECURITY_FILE_PATH),
            dlo = DEBUG_LAUNCH_OPERATION.DLO_CreateProcess,
            clsidCustom = VSConstants.CLSID_ComPlusOnlyDebugEngine //Set the managed debugger
            //clsidCustom = new Guid("{3B476D35-A401-11D2-AAD4-00C04F990171}") //Set the unmanged debugger
        };
        info.cbSize = (uint) Marshal.SizeOf(info);

        var ptr = Marshal.AllocCoTaskMem((int) info.cbSize);
        try {
            Marshal.StructureToPtr(info, ptr, false);
            var res = _debugger.LaunchDebugTargets(1, ptr);
            if (res != VSConstants.S_OK)
                (GetService(typeof (IVsUIShell)) as IVsUIShell).ReportErrorInfo(res);
        }
        finally {
            Marshal.FreeCoTaskMem(ptr);
        }
    }
}

listing

However, my main point is not to show how to write VSIX add-in starting VS debugger: the code above is there only to illustrate the amount of work. I aim to vindicate (in thousandth time?) the value of good tools, and that they are worth the effort. The idea of this add-in did not hit me at the very start of the project, so I had had enough time to thoroughly enjoy “manual” procedure of DSS services debugging. Despite that, if not an external nudge, I would have never made myself to learn new “unneeded and dull” technology (VS add-ins). And then it took one day to make sense of VSIX packages and implement one. And probably saved me hours of time and a lot of gray hair afterwards. The debugging process have smoothed tremendously and became indistinguishable from conventional application debugging now. And the last but not the least, colleagues like me more since then because good tools make you swear so much less.

Leave a Reply

Your email address will not be published. Required fields are marked *