Answered by:
Add-In: How to set environment variables to C# debugee

Question
-
Hi all,
I am using VS 2010 SP1 (10.0.40219.1 SP1Rel) and I would like to implement add-in that sets environment variables when I debug my C# projects. VS 2010 already provides such functionality for C++ projects (see http://msdn.microsoft.com/en-us/library/ms173406.aspx). I found that I could achieve this using VsDebugTargetInfo2.bstrEnv field and I built a demo add-in that actually works. Also I found that I have to implement quite a bit of code that deals with all possible prjStartAction/prjProjectType/activeConfiguration combinations, something I am trying to avoid.
Is it possible to intercept the starting_debugger_process and get/change the actual VsDebugTargetInfo2 instance that will be used to start the debugger?
Please advise in case I am trying to accomplish it in a wrong way or I am looking for something wrong/impossible.
Regards,
Mihail
Friday, September 16, 2011 2:58 PM
Answers
-
Hi Mihail,
The only want to 'intercept' the debug launch for a C# or VB .Net project, such that you could effectively control the parameters being passed to the DebugLaunch, would require implementing a project subtype (aka flavor).
Beneath the hood, the debugger is launched by the project system. Specifically, the project system invokes the debugger by calling IVsDebugger::DebugLaunch from it's implementation of IVsDebuggableProjectCfg::DebugLaunch.
If you are willing to create a project flavor, you'll need to derive a class from FlavoredProjectFactoryBase, and IVsProjectFlavorCfgProvider, and then override IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg, and pass back your own implementation of IVsProjectFlavorCfg.
int IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg) { ppFlavorCfg = new TweakedProjCfg(this, pBaseProjectCfg); return VSConstants.S_OK; }
Generally speaking, you'll likely need to implement an object that derives from IVsProjectFlavorCfg, IVsDebuggableProjectCfg, and IVsDebuggableProjectCfg2. For the most part, you can simply defer the various method implementations to invoke the original VB/C# project implementation of IVsDebuggableProjectCfg and just handle the DebugLaunch call directly. For example: (The following is copied from a prototype where I needed to debug a project using a specific process, but the concept is similar to what you would need to implement with your own project flavor.
internal class TweakedProjCfg : IVsProjectFlavorCfg, IVsDebuggableProjectCfg, IVsDebuggableProjectCfg2 { private TweakedProject _project; private IVsCfg _baseConfig; private IVsDebuggableProjectCfg _innerDebuggableProjCfg; public IVsDebuggableProjectCfg InnerDebuggableProjCfg { get { return _innerDebuggableProjCfg; } } internal TweakedProjCfg(TweakedProject project, IVsCfg baseConfig) { _project = project; _baseConfig = baseConfig; _innerDebuggableProjCfg = _baseConfig as IVsDebuggableProjectCfg; } public int Close() { return VSConstants.S_OK; } public int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) { ppCfg = IntPtr.Zero; // wire in our own IVsDebuggableProjectCfg/2 if (iidCfg.Equals(typeof(IVsDebuggableProjectCfg).GUID) && _innerDebuggableProjCfg != null ) ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg)); else if (iidCfg.Equals(typeof(IVsDebuggableProjectCfg2).GUID) && _innerDebuggableProjCfg != null) ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg2)); if (ppCfg != IntPtr.Zero) return VSConstants.S_OK; return VSConstants.E_NOINTERFACE; } #region IVsDebuggableProjectCfg public int DebugLaunch(uint grfLaunch) { ITweakDbgOptions tweakDbgOptions = (ITweakDbgOptions)_project; // todo: retrieve relevant project settings // return InnerDebuggableProjCfg.DebugLaunch(grfLaunch); // todo: call LaunchDebugTargets (use attach scenario?, as we want to debug this assembly // in the context of an already running service. // Populate the VsDebugTargetInfo2 struct to attach to specified process VsDebugTargetInfo2[] info = new VsDebugTargetInfo2[1]; info[0].cbSize = (uint)Marshal.SizeOf(info[0]); info[0].bstrExe = @"C:\Users\eddo\Documents\Visual Studio 2010\Projects\TweakDbgLaunch\FakeDbgSvc\FakeDbgSvc\bin\Debug\FakeDbgSvc.exe"; info[0].bstrOptions = ""; info[0].fSendToOutputWindow = 1; info[0].dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_AlreadyRunning; info[0].LaunchFlags = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_DetachOnStop | (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd; Process[] processes = Process.GetProcessesByName("FakeDbgSvc"); info[0].dwProcessId = (uint)processes[0].Id; info[0].dwDebugEngineCount = 1; // guidCOMPlusOnlyEng = {449EC4CC-30D2-4032-9256-EE18EB41B62B} Guid guidDbgEngine = VSConstants.CLSID_ComPlusOnlyDebugEngine; IntPtr pGuids = Marshal.AllocCoTaskMem(Marshal.SizeOf(guidDbgEngine)); Marshal.StructureToPtr(guidDbgEngine, pGuids, false); info[0].pDebugEngines = pGuids; IVsDebugger2 vsDbg = Package.GetGlobalService(typeof(IVsDebugger)) as IVsDebugger2; IntPtr pInfo = Marshal.AllocCoTaskMem((int)info[0].cbSize); Marshal.StructureToPtr(info[0], pInfo, false); int hr = vsDbg.LaunchDebugTargets2(1, pInfo); System.Threading.Thread.Sleep(5000); System.Diagnostics.Debug.WriteLine("Moving right along!!!"); return hr; } public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { return InnerDebuggableProjCfg.EnumOutputs(out ppIVsEnumOutputs); } public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { return InnerDebuggableProjCfg.OpenOutput(szOutputCanonicalName, out ppIVsOutput); } public int QueryDebugLaunch(uint grfLaunch, out int pfCanLaunch) { return InnerDebuggableProjCfg.QueryDebugLaunch(grfLaunch, out pfCanLaunch); } public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProjectCfg) { return InnerDebuggableProjCfg.get_BuildableProjectCfg(out ppIVsBuildableProjectCfg); } public int get_CanonicalName(out string pbstrCanonicalName) { return InnerDebuggableProjCfg.get_CanonicalName(out pbstrCanonicalName); } public int get_DisplayName(out string pbstrDisplayName) { return InnerDebuggableProjCfg.get_DisplayName(out pbstrDisplayName); } public int get_IsDebugOnly(out int pfIsDebugOnly) { return InnerDebuggableProjCfg.get_IsDebugOnly(out pfIsDebugOnly); } public int get_IsPackaged(out int pfIsPackaged) { return InnerDebuggableProjCfg.get_IsPackaged(out pfIsPackaged); } public int get_IsReleaseOnly(out int pfIsReleaseOnly) { return InnerDebuggableProjCfg.get_IsReleaseOnly(out pfIsReleaseOnly); } public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported) { return InnerDebuggableProjCfg.get_IsSpecifyingOutputSupported(out pfIsSpecifyingOutputSupported); } public int get_Platform(out Guid pguidPlatform) { return InnerDebuggableProjCfg.get_Platform(out pguidPlatform); } public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvider) { return InnerDebuggableProjCfg.get_ProjectCfgProvider(out ppIVsProjectCfgProvider); } public int get_RootURL(out string pbstrRootURL) { return InnerDebuggableProjCfg.get_RootURL(out pbstrRootURL); } public int get_TargetCodePage(out uint puiTargetCodePage) { return InnerDebuggableProjCfg.get_TargetCodePage(out puiTargetCodePage); } public int get_UpdateSequenceNumber(ULARGE_INTEGER[] puliUSN) { return InnerDebuggableProjCfg.get_UpdateSequenceNumber(puliUSN); } public int OnBeforeDebugLaunch(uint grfLaunch) { // todo: ensure host process/service is up and running here? Process[] processes = Process.GetProcessesByName("FakeDbgSvc"); if (processes.Length == 0) Process.Start(@"C:\Users\eddo\Documents\Visual Studio 2010\Projects\TweakDbgLaunch\FakeDbgSvc\FakeDbgSvc\bin\Debug\FakeDbgSvc.exe"); return VSConstants.S_OK; } #endregion }
It isn't exactly a trivial operation and requires implementing a package extension with the Visual Studio SDK. And even with this, you'll likely need to account for different project configurations/properties specified for the debugger settings.
Sincerely,
Ed Dore- Proposed as answer by Ed DoreMicrosoft employee Tuesday, September 20, 2011 9:22 PM
- Marked as answer by M.Slav Sunday, September 25, 2011 1:22 PM
Tuesday, September 20, 2011 9:22 PM
All replies
-
Hello ,
Thank you for your question.
I am trying to involve someone familiar with this topic to further look at this issue. There might be some time delay. Appreciate your patience.
Thank you for your understanding and support.
Yi
Yi Feng Li [MSFT]
MSDN Community Support | Feedback to us
Get or Request Code Sample from Microsoft
Please remember to mark the replies as answers if they help and unmark them if they provide no help.
Tuesday, September 20, 2011 10:12 AM -
Hi Mihail,
The only want to 'intercept' the debug launch for a C# or VB .Net project, such that you could effectively control the parameters being passed to the DebugLaunch, would require implementing a project subtype (aka flavor).
Beneath the hood, the debugger is launched by the project system. Specifically, the project system invokes the debugger by calling IVsDebugger::DebugLaunch from it's implementation of IVsDebuggableProjectCfg::DebugLaunch.
If you are willing to create a project flavor, you'll need to derive a class from FlavoredProjectFactoryBase, and IVsProjectFlavorCfgProvider, and then override IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg, and pass back your own implementation of IVsProjectFlavorCfg.
int IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg) { ppFlavorCfg = new TweakedProjCfg(this, pBaseProjectCfg); return VSConstants.S_OK; }
Generally speaking, you'll likely need to implement an object that derives from IVsProjectFlavorCfg, IVsDebuggableProjectCfg, and IVsDebuggableProjectCfg2. For the most part, you can simply defer the various method implementations to invoke the original VB/C# project implementation of IVsDebuggableProjectCfg and just handle the DebugLaunch call directly. For example: (The following is copied from a prototype where I needed to debug a project using a specific process, but the concept is similar to what you would need to implement with your own project flavor.
internal class TweakedProjCfg : IVsProjectFlavorCfg, IVsDebuggableProjectCfg, IVsDebuggableProjectCfg2 { private TweakedProject _project; private IVsCfg _baseConfig; private IVsDebuggableProjectCfg _innerDebuggableProjCfg; public IVsDebuggableProjectCfg InnerDebuggableProjCfg { get { return _innerDebuggableProjCfg; } } internal TweakedProjCfg(TweakedProject project, IVsCfg baseConfig) { _project = project; _baseConfig = baseConfig; _innerDebuggableProjCfg = _baseConfig as IVsDebuggableProjectCfg; } public int Close() { return VSConstants.S_OK; } public int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) { ppCfg = IntPtr.Zero; // wire in our own IVsDebuggableProjectCfg/2 if (iidCfg.Equals(typeof(IVsDebuggableProjectCfg).GUID) && _innerDebuggableProjCfg != null ) ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg)); else if (iidCfg.Equals(typeof(IVsDebuggableProjectCfg2).GUID) && _innerDebuggableProjCfg != null) ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg2)); if (ppCfg != IntPtr.Zero) return VSConstants.S_OK; return VSConstants.E_NOINTERFACE; } #region IVsDebuggableProjectCfg public int DebugLaunch(uint grfLaunch) { ITweakDbgOptions tweakDbgOptions = (ITweakDbgOptions)_project; // todo: retrieve relevant project settings // return InnerDebuggableProjCfg.DebugLaunch(grfLaunch); // todo: call LaunchDebugTargets (use attach scenario?, as we want to debug this assembly // in the context of an already running service. // Populate the VsDebugTargetInfo2 struct to attach to specified process VsDebugTargetInfo2[] info = new VsDebugTargetInfo2[1]; info[0].cbSize = (uint)Marshal.SizeOf(info[0]); info[0].bstrExe = @"C:\Users\eddo\Documents\Visual Studio 2010\Projects\TweakDbgLaunch\FakeDbgSvc\FakeDbgSvc\bin\Debug\FakeDbgSvc.exe"; info[0].bstrOptions = ""; info[0].fSendToOutputWindow = 1; info[0].dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_AlreadyRunning; info[0].LaunchFlags = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_DetachOnStop | (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd; Process[] processes = Process.GetProcessesByName("FakeDbgSvc"); info[0].dwProcessId = (uint)processes[0].Id; info[0].dwDebugEngineCount = 1; // guidCOMPlusOnlyEng = {449EC4CC-30D2-4032-9256-EE18EB41B62B} Guid guidDbgEngine = VSConstants.CLSID_ComPlusOnlyDebugEngine; IntPtr pGuids = Marshal.AllocCoTaskMem(Marshal.SizeOf(guidDbgEngine)); Marshal.StructureToPtr(guidDbgEngine, pGuids, false); info[0].pDebugEngines = pGuids; IVsDebugger2 vsDbg = Package.GetGlobalService(typeof(IVsDebugger)) as IVsDebugger2; IntPtr pInfo = Marshal.AllocCoTaskMem((int)info[0].cbSize); Marshal.StructureToPtr(info[0], pInfo, false); int hr = vsDbg.LaunchDebugTargets2(1, pInfo); System.Threading.Thread.Sleep(5000); System.Diagnostics.Debug.WriteLine("Moving right along!!!"); return hr; } public int EnumOutputs(out IVsEnumOutputs ppIVsEnumOutputs) { return InnerDebuggableProjCfg.EnumOutputs(out ppIVsEnumOutputs); } public int OpenOutput(string szOutputCanonicalName, out IVsOutput ppIVsOutput) { return InnerDebuggableProjCfg.OpenOutput(szOutputCanonicalName, out ppIVsOutput); } public int QueryDebugLaunch(uint grfLaunch, out int pfCanLaunch) { return InnerDebuggableProjCfg.QueryDebugLaunch(grfLaunch, out pfCanLaunch); } public int get_BuildableProjectCfg(out IVsBuildableProjectCfg ppIVsBuildableProjectCfg) { return InnerDebuggableProjCfg.get_BuildableProjectCfg(out ppIVsBuildableProjectCfg); } public int get_CanonicalName(out string pbstrCanonicalName) { return InnerDebuggableProjCfg.get_CanonicalName(out pbstrCanonicalName); } public int get_DisplayName(out string pbstrDisplayName) { return InnerDebuggableProjCfg.get_DisplayName(out pbstrDisplayName); } public int get_IsDebugOnly(out int pfIsDebugOnly) { return InnerDebuggableProjCfg.get_IsDebugOnly(out pfIsDebugOnly); } public int get_IsPackaged(out int pfIsPackaged) { return InnerDebuggableProjCfg.get_IsPackaged(out pfIsPackaged); } public int get_IsReleaseOnly(out int pfIsReleaseOnly) { return InnerDebuggableProjCfg.get_IsReleaseOnly(out pfIsReleaseOnly); } public int get_IsSpecifyingOutputSupported(out int pfIsSpecifyingOutputSupported) { return InnerDebuggableProjCfg.get_IsSpecifyingOutputSupported(out pfIsSpecifyingOutputSupported); } public int get_Platform(out Guid pguidPlatform) { return InnerDebuggableProjCfg.get_Platform(out pguidPlatform); } public int get_ProjectCfgProvider(out IVsProjectCfgProvider ppIVsProjectCfgProvider) { return InnerDebuggableProjCfg.get_ProjectCfgProvider(out ppIVsProjectCfgProvider); } public int get_RootURL(out string pbstrRootURL) { return InnerDebuggableProjCfg.get_RootURL(out pbstrRootURL); } public int get_TargetCodePage(out uint puiTargetCodePage) { return InnerDebuggableProjCfg.get_TargetCodePage(out puiTargetCodePage); } public int get_UpdateSequenceNumber(ULARGE_INTEGER[] puliUSN) { return InnerDebuggableProjCfg.get_UpdateSequenceNumber(puliUSN); } public int OnBeforeDebugLaunch(uint grfLaunch) { // todo: ensure host process/service is up and running here? Process[] processes = Process.GetProcessesByName("FakeDbgSvc"); if (processes.Length == 0) Process.Start(@"C:\Users\eddo\Documents\Visual Studio 2010\Projects\TweakDbgLaunch\FakeDbgSvc\FakeDbgSvc\bin\Debug\FakeDbgSvc.exe"); return VSConstants.S_OK; } #endregion }
It isn't exactly a trivial operation and requires implementing a package extension with the Visual Studio SDK. And even with this, you'll likely need to account for different project configurations/properties specified for the debugger settings.
Sincerely,
Ed Dore- Proposed as answer by Ed DoreMicrosoft employee Tuesday, September 20, 2011 9:22 PM
- Marked as answer by M.Slav Sunday, September 25, 2011 1:22 PM
Tuesday, September 20, 2011 9:22 PM -
Hello Ed,
Thank you very much for the detailed answer. I implemented a demo project subtype and it works great. I appreciate your help.
Regards,
Mihail
Sunday, September 25, 2011 1:22 PM