Search code examples
c#windowslistenerclipboardmonitor

Why is my clipboard listener being called twice/application opened twice?


I Wrote a short program in C# that monitors the clipboard. When a certain string enters the clipboard, a program must be opened with Process.Start (depending on the string). Everything works fine, but sometimes the application is being opened twice. I Don't know why this is happening.

namespace ClipboardMonitor {
    public class Form1 : System.Windows.Forms.Form {
        [DllImport("User32.dll")]
        protected static extern int SetClipboardViewer(int hWndNewViewer);
        [DllImport("User32.dll", CharSet=CharSet.Auto)]
        public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
        [DllImport("user32.dll", CharSet=CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();
        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        const int SW_HIDE = 0;
        const int SW_SHOW = 5;
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

        IntPtr nextClipboardViewer;
        private System.ComponentModel.Container components = null;

        public Form1() {
            InitializeComponent();
            nextClipboardViewer = (IntPtr)SetClipboardViewer((int) this.Handle);
            var handle = GetConsoleWindow();
            ShowWindow(handle, SW_HIDE);
        }
        protected override void Dispose( bool disposing ) {
            ChangeClipboardChain(this.Handle, nextClipboardViewer);
            if( disposing ) {
                if (components != null) {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }
        private void InitializeComponent() {
            this.WindowState = FormWindowState.Minimized;
            this.ShowInTaskbar = false;
            this.Name = "Form1";
        }

        [STAThread]
        static void Main()  {
            Application.Run(new Form1());
        }

        protected override void WndProc(ref System.Windows.Forms.Message m) {
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;

            switch(m.Msg) {
                case WM_DRAWCLIPBOARD:
                    DisplayClipboardData();
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;
                case WM_CHANGECBCHAIN:
                    if (m.WParam == nextClipboardViewer)
                        nextClipboardViewer = m.LParam;
                    else
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;
                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        void DisplayClipboardData() {
            Thread.Sleep(500);
            try {
                IDataObject iData = new DataObject();  
                iData = Clipboard.GetDataObject();
                 if (iData.GetDataPresent(DataFormats.Text)) {
                    string path = iData.GetData(DataFormats.Text).ToString();
                    string[] words = path.Split('_');
                    if (words[0] == "startLO") {
                            ProcessStartInfo info = new ProcessStartInfo(words[1]);
                            Process p = Process.Start(info);
                    }
                } else {
                    // We doen niets.
                }
            }
            catch(Exception e) {
                MessageBox.Show(e.ToString());
            }
        }
    }

Solution

  • One explanation would be that there are multiple clipboard events happening in rapid succession. This is fairly common. You can defend against this with a "settle time" delay. i.e. instead of reacting right away, set a timer or create a thread to handle it "in a little while". As more events come in, keep deferring the settletime. when the settletime finally expires, the timer or thread is allowed to actually run your program.