Willy-Kimura / SharpClipboard

A library for anonymously monitoring clipboard entries.
188 stars 36 forks source link

Seems not working in console app with .net core 3.1 #10

Closed jiayinzhang-mint closed 4 years ago

jiayinzhang-mint commented 4 years ago

Issue: the event listener start successfully, but ClipboardChanged function is never triggered.

Target framework: .NET Core 3.1 Output type: Console Application

Code: `namespace TestClipboardWatcher { class Watcher { [STAThread] static void Main(string[] args) { using SharpClipboard clipboard = new SharpClipboard(); clipboard.ClipboardChanged += ClipboardChanged;

        Console.WriteLine("Start watching");
        Console.WriteLine("Press 'q' to quit");

        while (Console.Read() != 'q') ;
    }

    private static void ClipboardChanged(Object sender, ClipboardChangedEventArgs e)
    {
        Console.WriteLine("Clipboard changed");
    }
}

}`

Willy-Kimura commented 4 years ago

It looks like .Net Core console applications may require a reference to System.Windows.Forms in order to obtain a Window handle to the System Clipboard.

I've tested the library with .Net Core 3.1 Windows Forms app and it's working perfectly. I'll probably need to update the repo with a few projects targeting .Net Core later.

jiayinzhang-mint commented 4 years ago

Thanks for reply! It looks like some thread-related issues. Like native windows Clipboard API, which works on main thread but fails if running in a new thread / task.

Willy-Kimura commented 4 years ago

That's another very likely cause. Let's keep testing...

KoalaBear84 commented 3 years ago

I can't get this to work.

@mintxtinm Do you have some guidance or a minimal project where it works?

jiayinzhang-mint commented 3 years ago

@KoalaBear84 This piece of code seems to work fine with console app, through far less elegant than this project.

` class ClipboardMonitor { public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data); public static event OnClipboardChangeEventHandler OnClipboardChange;

    public static void Start()
    {
        ClipboardWatcher.Start();
        ClipboardWatcher.OnClipboardChange += (ClipboardFormat format, object data) =>
        {
            if (OnClipboardChange != null)
                OnClipboardChange(format, data);
        };
    }

    public static void Stop()
    {
        OnClipboardChange = null;
        ClipboardWatcher.Stop();
    }

    class ClipboardWatcher : Form
    {
        // static instance of this form
        private static ClipboardWatcher mInstance;

        // needed to dispose this form
        static IntPtr nextClipboardViewer;

        public delegate void OnClipboardChangeEventHandler(ClipboardFormat format, object data);
        public static event OnClipboardChangeEventHandler OnClipboardChange;

        // start listening
        public static void Start()
        {
            // we can only have one instance if this class
            if (mInstance != null)
                return;

            Thread t = new Thread(new ParameterizedThreadStart(x =>
            {
                Application.Run(new ClipboardWatcher());
            }));
            t.SetApartmentState(ApartmentState.STA); // give the [STAThread] attribute
            t.Start();
        }

        // stop listening (dispose form)
        public static void Stop()
        {
            mInstance.Invoke(new MethodInvoker(() =>
            {
                ChangeClipboardChain(mInstance.Handle, nextClipboardViewer);
            }));
            mInstance.Invoke(new MethodInvoker(mInstance.Close));

            mInstance.Dispose();

            mInstance = null;
        }

        // on load: (hide this window)
        protected override void SetVisibleCore(bool value)
        {
            CreateHandle();

            mInstance = this;

            nextClipboardViewer = SetClipboardViewer(mInstance.Handle);

            base.SetVisibleCore(false);
        }

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SetClipboardViewer(IntPtr 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);

        // defined in winuser.h
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x030D;

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    ClipChanged();
                    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;
            }
        }

        static readonly string[] formats = Enum.GetNames(typeof(ClipboardFormat));

        private void ClipChanged()
        {
            IDataObject iData = Clipboard.GetDataObject();

            ClipboardFormat? format = null;

            foreach (var f in formats)
            {
                if (iData.GetDataPresent(f))
                {
                    format = (ClipboardFormat)Enum.Parse(typeof(ClipboardFormat), f);
                    break;
                }
            }

            object data = iData.GetData(format.ToString());

            if (data == null || format == null)
                return;

            if (OnClipboardChange != null)
                OnClipboardChange((ClipboardFormat)format, data);
        }

    }
}

public enum ClipboardFormat : byte
{
    Text,
    UnicodeText,
    Dib,
    Bitmap,
    EnhancedMetafile,
    MetafilePict,
    SymbolicLink,
    Dif,
    Tiff,
    OemText,
    Palette,
    PenData,
    Riff,
    WaveAudio,
    FileDrop,
    Locale,
    Html,
    Rtf,
    CommaSeparatedValue,
    StringFormat,
    Serializable,
}

`

KoalaBear84 commented 3 years ago

Thanks, I'm going to take a look at it ASAP. 👍