3

VS Code seems to have a file called Launch.json to customize what happens when you run your app for debugging. How do we go about that for Visual Studio? My goals is to run docker-compose without the VS Docker Tools, and then after the containers are up, telling VS to attach to the container.

My question is not specific to that case though, it could just as well be another custom way of Run with debugger scenario. Ideally I could just run some commands to build the project, run the project, and then tell VS to attach to it and associate that logic with VS.

Swimburger
  • 6,681
  • 6
  • 36
  • 63
  • Could you get useful information from VIad's suggestion? Or you want to run certain commands with Pre/Post Build even in your project property? – Jack Zhai Apr 06 '18 at 06:54
  • @JackZhai-MSFT, I'm still trying to do it but it's challenging with Docker because I have to run multiple commands. 1 Docker compose up, 2 run remote debugger, 3 somehow attach to that remote debugger running inside docker instance. Currently, I'm trying to point to a PowerShell script to have more flexibility. – Swimburger Apr 06 '18 at 14:27
  • I still don't know how to make VS attach to that remote process via script though – Swimburger Apr 06 '18 at 14:27
  • @JackZhai-MSFT, I posted the solution I came up with here. I was really hoping there was an easier way to control VS instances though. A PowerShell module for this would be pretty awesome. – Swimburger Apr 12 '18 at 18:12

3 Answers3

3

Right-click the project in your solution explorer; select "Properties". Choose "Debugging" node. Fill in "Command", "Command Arguments" (if needed), and set "Attach" to true.

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
  • How do you do this? There is already a "$(TargetPath)" content in the "Command" place. That represents the produced executable. How do you run a custom command before launching the executable, please? – yO_ Nov 02 '18 at 12:24
3

Based on the answers of Vlad and Jack, I came up with the following solution. To have my own code run when I press the Run button, I set up a blank Command Line project with custom launch settings.

{
  "profiles": {
    "Build": {
      "commandName": "Executable",
      "executablePath": "powershell",
      "commandLineArgs": ".\\DebugRun.ps1",
      "workingDirectory": "."
    }
  }
}

Whenever I press Run, it will run the DebugRun.ps1 script using PowerShell. Here's what I put in the DebugRun.ps1.

docker-compose -f "./docker-compose.debug.yml" --no-ansi up -d --force-recreate --build

Start-Sleep -Seconds 5

$appId = ((docker ps --filter "ancestor=employeemapapp:debug")[1] -split " ")[0]
$apiId = ((docker ps --filter "ancestor=employeemapapi:debug")[1] -split " ")[0]

$appIp = (docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $appId)
$apiIp = (docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $apiId)

docker exec -d $appId C:\\remote_debugger\\x64\\msvsmon.exe /noauth /anyuser /silent /nostatus /noclrwarn /nosecuritywarn /nofirewallwarn /nowowwarn /timeout:214748364
docker exec -d $apiId C:\\remote_debugger\\x64\\msvsmon.exe /noauth /anyuser /silent /nostatus /noclrwarn /nosecuritywarn /nofirewallwarn /nowowwarn /timeout:214748364

$appTarget = $appId + ":4022"
$apiTarget = $apiId + ":4022"

#Parameters
## 1: Solution Name is used to run the code only to the VS instance that has the Solution open
## 2: Transportation method for remote debugger
## 3: Target is the hostname/IP/... to the target where remote debugging is running
## 4: The process name you want to attach tos
./RemoteDebugAttach.exe "EmployeeMap.sln" "Remote (no authentication)" $appTarget "dotnet.exe"
./RemoteDebugAttach.exe "EmployeeMap.sln" "Remote (no authentication)" $apiTarget "dotnet.exe"

Write-Host "Api:" $apiIp
Write-Host "App:" $appIp
$apiUrl = "http://$apiIp/api/employees"
$appUrl = "http://$appIp"

start $apiUrl
start $appUrl

Read-Host "Press any key to quit"

./RemoteDebugDetach.exe EmployeeMap.sln "dotnet.exe"

docker exec -d $appId C:\\remote_debugger\\x64\\utils\\KillProcess.exe msvsmon.exe
docker exec -d $apiId C:\\remote_debugger\\x64\\utils\\KillProcess.exe msvsmon.exe

docker-compose -f "./docker-compose.debug.yml" --no-ansi down 

This script does the following:

  • Bring up my docker containers via docker-compose
  • Grab the container id's, ip's, etc
  • Start the remote debugger instance inside of the container
  • Attach to the dotnet.exe processes inside of the container using a custom executable (RemoteDebugAttach)
  • Open the browsers to browse to the websites served from the containers
  • Wait for any keypress to bring down infrastructure
  • Detach from processes using custom executable
  • Kill remote debugger on docker container
  • Bring down containers

The custom executables come from a separate solution where I just built some Command Line Applications. I used the code from this source to get all VS instances running on the machine. And combined it with this code to attach:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        string solutionName = Ask(args, 0, "Solution name?");
        string transportName = Ask(args, 1, "Transport name?");
        string target = Ask(args, 2, "Target machine?");
        string processName = Ask(args, 3, "Process Name?");


        var instances = Msdev.GetIDEInstances(true);
        var dte = (DTE2)instances.Find(d => d.Solution.FullName.EndsWith(solutionName, StringComparison.InvariantCultureIgnoreCase));
        var debugger = dte.Debugger as Debugger2;
        var transports = debugger.Transports;
        Transport transport = null;
        foreach(Transport loopTransport in transports)
        {
            if(loopTransport.Name.Equals(transportName, StringComparison.InvariantCultureIgnoreCase)) // "Remote (no authentication)")
            {
                transport = loopTransport;
                break;
            }
        }

        Processes processes = debugger.GetProcesses(transport, target); // "172.24.50.15:4022");
        foreach(Process process in processes)
        {
            if(process.Name.EndsWith(processName, StringComparison.InvariantCultureIgnoreCase))
            {
                process.Attach();
            }
        }
    }

    static string Ask(string[] args, int index, string question)
    {
        if(args.Length <= index)
        {
            Console.WriteLine(question);
            return Console.ReadLine();
        }

        return args[index];
    }
}

And the following to Detach:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        string solutionName = Ask(args, 0, "Solution name");
        string processName = Ask(args, 1, "Process Name?");

        var instances = Msdev.GetIDEInstances(true);

        var dte = (DTE2)instances.Find(d => d.Solution.FullName.EndsWith(solutionName, StringComparison.InvariantCultureIgnoreCase));
        var debugger = dte.Debugger as Debugger2;

        Processes processes = debugger.DebuggedProcesses;
        foreach (Process2 process in processes)
        {
            if (process.Name.EndsWith(processName, StringComparison.InvariantCultureIgnoreCase))
            {
                process.Detach(false);
            }
        }
    }

    static string Ask(string[] args, int index, string question)
    {
        if (args.Length <= index)
        {
            Console.WriteLine(question);
            return Console.ReadLine();
        }

        return args[index];
    }
}

I would have preferred to have this code in PowerShell because it would be easier to copy to other project + edit. Though in PowerShell, I was simply not able to get the right code to execute, even when using reflection to apply certain code the COM objects.

I hope it helps some people who want to have their own custom flow build into VS. Thank you Vlad and Jack.

Swimburger
  • 6,681
  • 6
  • 36
  • 63
1

If you want to attach to process debugging with script, you could think about using the powershell.

Or as far as I know, we could custom the code to attach to process.

Attach debugger in C# to another process

But if you have to use the VS IDE debugging feature, we often use the remote debugging tool: https://msdn.microsoft.com/library/y7f5zaaa.aspx.

Jack Zhai
  • 6,230
  • 1
  • 12
  • 20
  • Hi Jack, thank you. I have a script ready that launches my docker container, and starts a remote debugger server on the container. I see the SO post you referenced only applies to local processes, but it may be a good enough starting point for me to figure it out :) – Swimburger Apr 11 '18 at 04:37
  • I can get the VS instance through `[System.Runtime.InteropServices.Marshal]::GetActiveObject("VisualStudio.DTE.15.0")`, but I can't cast it to `DTE2`, which means I can't access the `Debugger2.Transports` for remote debugging script. I'd prefer to stay in PowerShell, but I think I'll have to write a console app for it – Swimburger Apr 11 '18 at 17:14