在设计期间在VS(10)中调试C#.NET自定义组件/控件

在设计期间在VS(10)中调试C#.NET自定义组件/控件,c#,visual-studio,winforms,visual-studio-2010,debugging,C#,Visual Studio,Winforms,Visual Studio 2010,Debugging,我在so中看到了很多问题和主题,人们尝试使用messagebox.Show调试组件(包括我自己)。这绝对是一个糟糕的方法。原因是,像Debug.Write或Console.Write这样的功能在设计时不起作用。因此,您可以做下一个最好的事情来输出字符串 另一点是,我以前从未真正研究过组件。当我编写程序时,我通常直接以代码/类的形式编写,并在运行时进行测试 如果你对我写的东西感兴趣(或困惑,或两者兼而有之),请随时纠正我的错误和/或添加你的建议 我的问题应该是:“在设计期间调试组件的最佳方法是什么

我在so中看到了很多问题和主题,人们尝试使用
messagebox.Show
调试组件(包括我自己)。这绝对是一个糟糕的方法。原因是,像
Debug.Write
Console.Write
这样的功能在设计时不起作用。因此,您可以做下一个最好的事情来输出字符串

另一点是,我以前从未真正研究过组件。当我编写程序时,我通常直接以代码/类的形式编写,并在运行时进行测试

如果你对我写的东西感兴趣(或困惑,或两者兼而有之),请随时纠正我的错误和/或添加你的建议

我的问题应该是:“在设计期间调试组件的最佳方法是什么?”。但是堆栈溢出不喜欢这样的问题

或者只针对以下问题:“设计期间调试/控制台输出的问题”


我首先尝试的是:《设计时调试组件的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过程并将文本发回。我想我正在把自己挖得越来越深

提示:当调试设计器组件、控件、某些VSIX_3;时,不要附加到进程。。。(可能为时已晚)考虑改变项目的开始行动。请参见>属性>调试>启动外部程序并指定devenv.exe(Visual Studio)的路径。在命令行参数中,输入包含组件的新最小测试床解决方案项目的路径。理想情况下,它应该是一个不同的解决方案。现在,当您调试>开始调试时,Visual Studio将启动并加载您的testbedHmmm…假设您的是调试版本,
Debug.Write
xxx应该可以工作。输出可能正在写入第二个Visual Studio输出窗口。否则,请使用Windows本机功能并使用MS,如果您所说的“MS文档”是指此过程有效。我遇到的一个问题是将“DebuggingExample”项目包含在与“DebugControlLibrary”项目相同的解决方案中;我宁愿把它们分开。写入控制台不会写入VS输出窗口。但是,与您的断言相反,Debgug.writexxx方法将生成输出。如果您没有在输出窗口中看到这些调用的结果,请确保未启用“将所有输出窗口文本重定向到即时窗口”选项。请考虑不使用控制台方法进行调试输出,调试/跟踪方法是生成这些信息的方式。<代码>调试。XYZ < /C> >应该工作,是的。如前所述,我已经阅读了许多链接。我注意到许多其他人也同意MS Docs的调试方法一点也不好。即使过程基本上是有效的。如果Debug.Writexxx不起作用,为什么不使用您最喜欢的日志库并使用类似的工具写入文件呢。后者将实时显示文件的内容和任何更改,并根据需要滚动内容。你可以考虑把它写成一个适当的,以便它与普通的调试。写方法。@ MICKYD,因为我投身到一些事先没有足够的思考。然后我完成了。你说的是我在学习过程中计划做的事情。考虑到你和其他人的意见,我一定会这么做。TraceListener类看起来非常有前途。也许我甚至可以带着它潜入组件。让我们看看结果如何。(我是给你投赞成票的人之一)。关于“我正在弄清楚VS到底做什么”,您可能想查看VisualStudio扩展性SDK