Search code examples
c#winapiclipboardwndproc

Clipboard.GetText overrides clipboard?


Here is what im trying to do: There is some game which writes some info about item under mouse cursor into clipboard when i press Ctrl-C. Im trying to grab that info and select some stuff i need from it. Im doing it like this:

    //at form load
    RegisterHotKey(this.Handle, 0, 0x002, (int)Keys.C);

    protected override void WndProc(ref Message m)
            {
                if (m.Msg == 0x0312)
                {
                    int id = m.WParam.ToInt32();
                    if (id == 0)
                    {
                        System.Threading.Thread.Sleep(155); //ive thought if i add some delay it would help but it doesnt...
                        string textFromClipboard = Clipboard.GetText();
                        if (textFromClipboard.Contains("Itemlevel: "))
                        {
                          // do some stuff with data IF it exists in clipboard, doesnt important what i do - i never get here
                        }
                    }
                }
                base.WndProc(ref m);
            }

So basically, when i press Ctrl-C in-game without this program on - all works fine, info copied in clipboard. When i turn program on - clipboard stays same as it was before i press Ctrl-C in-game. How do i prevent this? How do i get text from clipboard correctly? Maybe the way i get this text is wrong? Or maybe that registered hotkey interferes with game hotkey so it doesn't work anymore?

update: Ive figured out some simple solution, but very rough and barbaric. But it works fine.

       public static void KeyDown(System.Windows.Forms.Keys key)
            {
               keybd_event((byte)key, 0x45, 0x0001 | 0, 0);
            }

            public static void KeyUp(System.Windows.Forms.Keys key)
            {
                keybd_event((byte)key, 0x45, 0x0001 | 0x0002, 0);
            }
     protected override void WndProc(ref Message m)
            {
                if (m.Msg == 0x0312)
                {
                    int id = m.WParam.ToInt32();
                    if (id == 0)
                    {                   
                        ToggleHotkeys(false);
                        KeyDown(Keys.Control);
                        KeyDown(Keys.C);
                        KeyUp(Keys.C);
                        KeyUp(Keys.Control);
                        System.Threading.Thread.Sleep(155); 
//if i comment this sleep - code executes too fast, making first Ctrl-C press 
//capture nothing, second press outputs results for first item
//third press - for second item, ...
                        string textFromClipboard = Clipboard.GetText();
                        if (textFromClipboard.Contains("Itemlevel: "))
                        {
                           //do stuff with data
                        }
                        ToggleHotkeys(true);
                    }
                }
                base.WndProc(ref m);
            }

Maybe there is more clever way to solve this problem?


Solution

  • I would use a ClipBoard monitor so you can get notified whenever the ClipBoard changes:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
    
            private ClipBoardMonitor cbm = null;
    
            public Form1()
            {
                InitializeComponent();
                cbm = new ClipBoardMonitor();
                cbm.NewText += cbm_NewText;
            }
    
            private void cbm_NewText(string txt)
            {
                Console.WriteLine(txt);
            }
    
        }
    
        public class ClipBoardMonitor : NativeWindow 
        {
    
            private const int WM_DESTROY = 0x2;
            private const int WM_DRAWCLIPBOARD = 0x308;
            private const int WM_CHANGECBCHAIN = 0x30d;
    
            [DllImport("user32.dll")]
            static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
            [DllImport("user32.dll")]
            static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    
            public event NewTextEventHandler NewText;
            public delegate void NewTextEventHandler(string txt);
    
            private IntPtr NextClipBoardViewerHandle;
    
            public ClipBoardMonitor() 
            {
                this.CreateHandle(new CreateParams());
                NextClipBoardViewerHandle = SetClipboardViewer(this.Handle);
            }
    
            protected override void WndProc(ref Message m)
            {
                switch (m.Msg)
                {
                    case WM_DRAWCLIPBOARD:
                        if (Clipboard.ContainsText())
                        {
                            if (NewText != null)
                            {
                                NewText(Clipboard.GetText());
                            }
                        }
                        SendMessage(NextClipBoardViewerHandle, m.Msg, m.WParam, m.LParam);
    
                        break;
    
                    case WM_CHANGECBCHAIN:
                        if (m.WParam.Equals(NextClipBoardViewerHandle))
                        {
                            NextClipBoardViewerHandle = m.LParam;
                        }
                        else if (!NextClipBoardViewerHandle.Equals(IntPtr.Zero))
                        {
                            SendMessage(NextClipBoardViewerHandle, m.Msg, m.WParam, m.LParam);
                        }
                        break;
    
                    case WM_DESTROY:
                        ChangeClipboardChain(this.Handle, NextClipBoardViewerHandle);
                        break;
    
                }
    
                base.WndProc(ref m);
            }
    
        }
    
    }