如何在 Visual Studio 中 "Start Debugging" 时 运行 自定义代码并附加到调试器
How to run custom code when "Start Debugging" in Visual Studio and attach to debugger
VS Code 似乎有一个名为 Launch.json 的文件来自定义当您 运行 您的应用程序进行调试时发生的情况。
我们如何处理 Visual Studio?
我的目标是 运行 docker-compose 没有 VS Docker 工具,然后在容器启动后,告诉 VS 附加到容器。
虽然我的问题并不特定于那种情况,但它也可能是 运行 与调试器场景的另一种自定义方式。
理想情况下,我可以 运行 一些命令来构建项目,运行 项目,然后告诉 VS 附加到它并将该逻辑与 VS 相关联。
Right-click 解决方案资源管理器中的项目; select "Properties"。
选择 "Debugging" 节点。填写 "Command"、"Command Arguments"(如果需要),并将 "Attach" 设置为 true。
如果你想用脚本附加到进程调试,你可以考虑使用powershell。
或者据我所知,我们可以自定义代码附加到进程。
Attach debugger in C# to another process
但是如果非要用到VS的IDE调试功能,我们常用远程调试工具:https://msdn.microsoft.com/library/y7f5zaaa.aspx.
根据 Vlad 和 Jack 的回答,我提出了以下解决方案。
为了在我按下 运行 按钮时拥有我自己的代码 运行,我设置了一个空白的命令行项目和自定义启动设置。
{
"profiles": {
"Build": {
"commandName": "Executable",
"executablePath": "powershell",
"commandLineArgs": ".\DebugRun.ps1",
"workingDirectory": "."
}
}
}
每当我按下 运行 时,它都会使用 PowerShell 运行 DebugRun.ps1
脚本。
这是我在 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
此脚本执行以下操作:
- 通过 docker-compose
调出我的 docker 容器
- 获取容器 ID、IP 等
- 在容器内启动远程调试器实例
- 使用自定义可执行文件 (RemoteDebugAttach) 附加到容器内的
dotnet.exe
进程
- 打开浏览器浏览容器提供的网站
- 等待任何按键关闭基础设施
- 使用自定义可执行文件从进程分离
- 在 docker 容器上终止远程调试器
- 关闭容器
自定义可执行文件来自一个单独的解决方案,我刚刚在其中构建了一些命令行应用程序。
我使用此 source 中的代码在机器上获取所有 VS 实例 运行ning。
并将其与此代码结合起来附加:
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];
}
}
以及要分离的以下内容:
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];
}
}
我宁愿在 PowerShell 中使用此代码,因为它更容易复制到其他项目并进行编辑。虽然在 PowerShell 中,我根本无法获得正确的代码来执行,即使在使用反射将某些代码应用于 COM 对象时也是如此。
我希望它能帮助一些想要将自己的自定义流程构建到 VS 中的人。
谢谢弗拉德和杰克。
VS Code 似乎有一个名为 Launch.json 的文件来自定义当您 运行 您的应用程序进行调试时发生的情况。 我们如何处理 Visual Studio? 我的目标是 运行 docker-compose 没有 VS Docker 工具,然后在容器启动后,告诉 VS 附加到容器。
虽然我的问题并不特定于那种情况,但它也可能是 运行 与调试器场景的另一种自定义方式。 理想情况下,我可以 运行 一些命令来构建项目,运行 项目,然后告诉 VS 附加到它并将该逻辑与 VS 相关联。
Right-click 解决方案资源管理器中的项目; select "Properties"。 选择 "Debugging" 节点。填写 "Command"、"Command Arguments"(如果需要),并将 "Attach" 设置为 true。
如果你想用脚本附加到进程调试,你可以考虑使用powershell。
或者据我所知,我们可以自定义代码附加到进程。
Attach debugger in C# to another process
但是如果非要用到VS的IDE调试功能,我们常用远程调试工具:https://msdn.microsoft.com/library/y7f5zaaa.aspx.
根据 Vlad 和 Jack 的回答,我提出了以下解决方案。 为了在我按下 运行 按钮时拥有我自己的代码 运行,我设置了一个空白的命令行项目和自定义启动设置。
{
"profiles": {
"Build": {
"commandName": "Executable",
"executablePath": "powershell",
"commandLineArgs": ".\DebugRun.ps1",
"workingDirectory": "."
}
}
}
每当我按下 运行 时,它都会使用 PowerShell 运行 DebugRun.ps1
脚本。
这是我在 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
此脚本执行以下操作:
- 通过 docker-compose 调出我的 docker 容器
- 获取容器 ID、IP 等
- 在容器内启动远程调试器实例
- 使用自定义可执行文件 (RemoteDebugAttach) 附加到容器内的
dotnet.exe
进程 - 打开浏览器浏览容器提供的网站
- 等待任何按键关闭基础设施
- 使用自定义可执行文件从进程分离
- 在 docker 容器上终止远程调试器
- 关闭容器
自定义可执行文件来自一个单独的解决方案,我刚刚在其中构建了一些命令行应用程序。 我使用此 source 中的代码在机器上获取所有 VS 实例 运行ning。 并将其与此代码结合起来附加:
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];
}
}
以及要分离的以下内容:
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];
}
}
我宁愿在 PowerShell 中使用此代码,因为它更容易复制到其他项目并进行编辑。虽然在 PowerShell 中,我根本无法获得正确的代码来执行,即使在使用反射将某些代码应用于 COM 对象时也是如此。
我希望它能帮助一些想要将自己的自定义流程构建到 VS 中的人。 谢谢弗拉德和杰克。