c# - 在设计期间在 VS(10) 中调试 C# .NET 自定义组件/控件
问题描述
我在其他地方看到了很多问题(因此)和人们尝试使用MesssageBox.Show
(包括一开始的我自己)调试组件的主题。这绝对是一个糟糕的做法。原因是,功能在设计时喜欢Debug.Write
或Console.Write
不工作。所以你做下一个最好的事情来输出一个字符串。
另一点是我以前从未真正看过组件。当我写一个程序时,我通常直接将它作为代码/类来做,并在运行时对其进行测试。
如果您对我写的内容感兴趣(或困惑或两者兼而有之),如果我错了,请随时纠正我和/或添加您的建议。
我的问题实际上应该是:“在设计时调试组件的最佳方法是什么? ”。但是堆栈溢出不喜欢这样的问题。
或者只是针对以下问题:“设计时调试/控制台输出的问题”。
我首先尝试的是:MS docs guide to debug components at design time
我使用与此问题中提到的相同的做法在设计时调试组件。
断点有效,但我仍然没有使用控制台/调试文本获得任何输出文本:
在正常的运行时调试会话中,控制台和/或调试输出被传递到 VS 窗格。只是要注意调试窗格会输出任何内容。当然,在这种情况下,您应该使用Debug而不是Console。但是既然没用,我还是试着直接在控制台输出一些东西。
经过一些更深入的测试后,我得出的结论是MS Docs中的粒子是垃圾,我放弃了,因为:
Debug.WriteLine
- 每次都需要花费太多时间和精力来连接其他会话。
- 它杂乱无章
- 它实际上并不像在运行时那样执行相同的调试例程。
- 未处理的错误会导致设计人员停用导致重新启动(两个)VS 会话的组件。
- 如前所述,我没有找到为什么我没有得到调试/控制台输出的线索。
这进一步使我得出以下结论:
对我来说,在关键点在控制台中输出一些东西基本上就足够了。非常烦人的是,我无法删除组件并再次插入它们,因为 Visual Studio 在内部某处卡住了。那将是我接下来要解决的问题。
将 VS 进程附加到另一个 VS 实例绝对没有必要且逻辑错误。VS 的会话已经对组件执行了此操作。并在 appdata 中生成一个临时 exe。
自定义或扩展的错误处理程序(记录器)是个好主意。就像您制作公共 exe 时所做的那样,其他人可以向您发送调试文本。在这种情况下,您将其发回给自己。;)
下一步:堆栈溢出和其他页面提供哪些选项?
堆栈溢出的相关主题
- 如何调试 winforms 设计器- 与 MS 文档相同,但接受的答案很有趣:
- 如何在设计时进行调试?-
XDesProc.exe
VS12++ 我们已经知道的相同程序,也许只是我,但我不喜欢它。 - Visual Studio 2015 调试自定义控件- 这个只是让我感到困惑,似乎是一种不同于我喜欢的编程类型。
- 如何排查和调试 Visual Studio 设计模式错误?- 只是方案 f 的另一个副本
- 如何在设计模式下调试 WPF 用户控件?- 只是方案 f 的另一个副本
- 带有嵌套控件的设计模式
- 这个很有趣。但是他们在某种程度上对 iDesignerHost 有问题,因此在组件类中设置了只读。在开始阅读之前,我已经解决了这两个问题。稍后看我的回答。而这个。
- 调试 Visual Studio 设计器错误的好方法- 这个很好。
其他网站上的相关主题
我目前的目标
- 远离 MS 文档“解决方案”
- 为设计人员建立一个简单的调试输出系统
解决方案
到目前为止,我所做的是一个简单的消息系统,到目前为止似乎还有效。我不想在设计器生成的窗口中输出调试内容。我知道,我可以创建一个表单窗口。或者更好的是,我可以创建一个其他应用程序来与设计师交流。目前我对一个非常简单的解决方案感到满意。只是为了得到任何输出。
我做了一些有趣的经历,例如在设计时获取解决方案/项目文件夹。
这有点小题大做,但我对此很满意。所以我有一个荒谬的想法,将文本简单地在内部输出到记事本窗口中。我没有“使用”名称空间来使其更加灵活和清晰地表达。还有一个 dispose 实现。我不确定这是否有意义。但是我的想法是如果在设计时解构类,则避免使用持久线程。或者只是为案件做好准备。这是代码:
public class SendToNotepad : System.IDisposable
{
private bool _disposed;
public SendToNotepad() { }
public static void Text(string text, bool d) { Send.SendText(text, d); }
~SendToNotepad() { Dispose(false); }
public void Dispose()
{
Dispose(true);
System.GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) { return; }
if (disposing)
{
// Dispose managed state
}
Send.Dispose();
_disposed = true;
}
private static class Send
{
private const int STARTF_USESHOWWINDOW = 1;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOWMINNOACTIVE = 7;
private const int CREATE_NEW_CONSOLE = 0x000010;
private const int EM_SETSEL = 0x00B1;
private const int EM_REPLACESEL = 0x00C2;
private const int WM_SETTEXT = 0x000C;
private const int WM_GETTEXTLENGTH = 0x000E;
private const int WM_COMMAND = 0x0111;
private const int WM_APPCOMMAND = 0x0319;
private const int WM_QUIT = 0x0012;
private const int APPCOMMAND_SAVE = 0x201000;
private const int SleepTime = 10;
private static UnsafeNativeMethods.STARTUPINFO _si;
private static UnsafeNativeMethods.PROCESS_INFORMATION _pi;
private static System.Diagnostics.Process _p;
private static System.Threading.Timer _timer;
private static bool _isWaiting;
private static string _waitingCatcher;
private static string _debugFileName;
private static readonly string Namespace = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace;
private static readonly string[] TitleLoopAniChars = new[] { "|", "/", "––", "\\" };
private static readonly int TitleLoopAniCharsLength = TitleLoopAniChars.Length - 1;
public static void Dispose()
{
if (_p != null)
{
try
{
UnsafeNativeMethods.CloseHandle(_pi.hProcess);
UnsafeNativeMethods.CloseHandle(_pi.hThread);
}
catch { }
_p.Dispose();
_p = null;
}
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
}
public static void SendText(string text, bool d)
{
if (!d) { return; }
text = System.DateTime.Now.TimeOfDay + ": " + text + System.Environment.NewLine;
if (_isWaiting)
{
_waitingCatcher += text;
return;
}
_isWaiting = true;
int maxWait = 200; // Max timeout over all (* SleepTime)
if (_p == null)
{
_debugFileName = GetDebugFileName();
if (System.String.IsNullOrEmpty(_debugFileName))
{
_waitingCatcher += text;
_isWaiting = false;
return;
}
if (!System.IO.File.Exists(_debugFileName))
{
try { System.IO.File.Create(_debugFileName).Dispose(); }
catch { }
}
if (!System.IO.File.Exists(_debugFileName))
{
_waitingCatcher += text;
_isWaiting = false;
return;
}
_si = new UnsafeNativeMethods.STARTUPINFO
{
dwFlags = STARTF_USESHOWWINDOW,
wShowWindow = SW_SHOWMINNOACTIVE,
cb = System.Runtime.InteropServices.Marshal.SizeOf(_si)
};
bool success = UnsafeNativeMethods.CreateProcess(null, "notepad /W \"" + _debugFileName + "\"", System.IntPtr.Zero, System.IntPtr.Zero, true, 0, System.IntPtr.Zero, null, ref _si, out _pi);
while (maxWait-- > 0 && success)
{
System.Threading.Thread.Sleep(SleepTime);
try { _p = System.Diagnostics.Process.GetProcessById(_pi.dwProcessId); } // grab Process to handle WaitForExit()
catch { }
if (_p != null) { break; }
}
if (_p == null)
{
_waitingCatcher += text;
_isWaiting = false;
return;
}
while (maxWait-- > 0 && (!_p.Responding || !_p.WaitForInputIdle())) { System.Threading.Thread.Sleep(SleepTime); }
_timer = new System.Threading.Timer(NotifyOnProcessExits);
_timer.Change(0, 0);
}
else
{
while (maxWait-- > 0 && (!_p.Responding || !_p.WaitForInputIdle())) { System.Threading.Thread.Sleep(SleepTime); }
}
System.IntPtr fwx = UnsafeNativeMethods.FindWindowEx(_p.MainWindowHandle, System.IntPtr.Zero, "Edit", null);
UnsafeNativeMethods.SendMessage(fwx, EM_SETSEL, 0, -1);
UnsafeNativeMethods.SendMessage(fwx, EM_SETSEL, -1, -1);
UnsafeNativeMethods.SendMessageW(fwx, EM_REPLACESEL, 1, text);
if (!System.String.IsNullOrEmpty(_waitingCatcher))
{
UnsafeNativeMethods.SendMessage(fwx, EM_SETSEL, 0, -1);
UnsafeNativeMethods.SendMessage(fwx, EM_SETSEL, -1, -1);
UnsafeNativeMethods.SendMessageW(fwx, EM_REPLACESEL, 1, _waitingCatcher);
_waitingCatcher = "";
}
UnsafeNativeMethods.SendMessage(_p.MainWindowHandle, WM_COMMAND, 0x0003, 0x0); // first menu, item 3 (save)
_isWaiting = false;
}
private static void NotifyOnProcessExits(object timer)
{
if (_p == null) { return; }
// _p.WaitForExit();
// This is just for fun as response feedback
int i = 0;
while (!_p.HasExited)
{
System.Threading.Thread.Sleep(500);
UnsafeNativeMethods.SetWindowText(_p.MainWindowHandle, Namespace + " –> " + _debugFileName + " " + TitleLoopAniChars[i]);
i = i == TitleLoopAniCharsLength ? 0 : i + 1;
}
Dispose();
}
private static string GetDebugFileName() // Hack to get solution path while design time
{
string s;
try
{
var trace = new System.Diagnostics.StackTrace(true);
var frame = trace.GetFrame(0);
s = System.IO.Path.GetDirectoryName(frame.GetFileName());
}
catch { return null; }
return s == null ? null : s + "Debug.txt";
}
[System.Security.SuppressUnmanagedCodeSecurity]
private static class UnsafeNativeMethods
{
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct STARTUPINFO
{
public System.Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public System.Int32 dwX;
public System.Int32 dwY;
public System.Int32 dwXSize;
public System.Int32 dwYSize;
public System.Int32 dwXCountChars;
public System.Int32 dwYCountChars;
public System.Int32 dwFillAttribute;
public System.Int32 dwFlags;
public System.Int16 wShowWindow;
public System.Int16 cbReserved2;
public System.IntPtr lpReserved2;
public System.IntPtr hStdInput;
public System.IntPtr hStdOutput;
public System.IntPtr hStdError;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public System.IntPtr hProcess;
public System.IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[System.Runtime.InteropServices.DllImport("kernel32.dll")]
public static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
System.IntPtr lpProcessAttributes,
System.IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
System.IntPtr lpEnvironment,
string lpCurrentDirectory,
[System.Runtime.InteropServices.In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool CloseHandle(System.IntPtr hObject);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(System.IntPtr hWnd, int uMsg, int wParam, int lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern System.IntPtr SendMessage(System.IntPtr hWnd, int uMsg, System.IntPtr wParam, System.IntPtr lParam);
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
public static extern int SendMessageW(System.IntPtr hWnd, int uMsg, int wParam, string lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern System.IntPtr DefWindowProc(System.IntPtr hWnd, int msg, System.IntPtr wParam, System.IntPtr lParam);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern System.IntPtr FindWindowEx(System.IntPtr hwndParent, System.IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SetWindowText(System.IntPtr hWnd, string text);
}
}
}
在组件类中:
private static readonly bool DesignTime = LicenseManager.UsageMode == LicenseUsageMode.Designtime;
SendToNotepad.Text("Debug text", DesignTime);
这有一个很好的(不例外)副作用,如果 VS 崩溃,调试内容会被保留。
现在我有了一个更荒谬的想法,那就是连接到 vs 进程并将文本发回。我想我正在把自己挖得越来越深。;)
推荐阅读
- python-3.x - 使用 tkinter 按钮在散点图中填充坐标
- c# - 重新运行更新迁移时实体框架核心出现错误
- flutter - 如何在 Flutter 中临时保存文件?
- git - 创建 git 分支时出错 - 偏移量和长度超出了数组的范围
- sql - 为什么在 WHERE 子句中使用 CONVERT(DATETIME, [date], [format]) 需要这么长时间?
- vue.js - 相关项目的链接断开
- python - 使用 EvolutionaryFS 时,只有整数、切片 (`:`)、省略号 (`...`)、numpy.newaxis (`None`) 和整数或布尔数组是有效的索引
- compression - 如何使用预测概率模型压缩序列?
- ruby-on-rails - 在 Heroku 上部署失败 - Ruby(在 Windows 上)
- python - 如何在pygame中添加具有原始图像形状的白色表面?