WM_DRAWCLIPBOARD在打开表单后停止触发

时间:2014-09-12 19:40:17

标签: c# clipboard wndproc notifyicon

我正在使用WndProcWM_DRAWCLIPBOARD来监控Windows剪贴板并保存复制的文本和图像。除了一种情况外,它运作良好。我正在使用NotifyIcon并且默认情况下应用程序停靠在通知区域(ShowInTaskbar = False,WindowState =最小化)。除了打开表单后,复制逻辑运行良好。当我右键单击应用程序的图标并单击“打开”时,虽然WM_DRAWCLIPBOARD被不断调用,但在复制一段文本或图像时,WndProc不再被调用。

NotifyIcon是否使用与主UI不同的线程,如果是这样,NotifyIcon线程可能不会监听UI线程。请注意,我实际上并不是Form.Show(),而是我 我只是最大化表单并显示任务栏图标。当表格“关闭”时,相反的情况就完成了。这是我正在使用的代码。它只是一个类/形式,所以这就是全部。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Copy_Storer
{
[DefaultEvent("ClipboardChanged")]
public partial class frmMain : Form
{
    IntPtr _NextClipboardViewer;
    bool _AppStarting = true;

    public frmMain()
    {
        InitializeComponent();
        _NextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
    }

    private void frmMain_Load(object sender, EventArgs e)
    {
        nfyApp.ShowBalloonTip(10000);
        cmsCSRightClick.ItemClicked += new ToolStripItemClickedEventHandler(cmsCSRightClick_ItemClicked);
        _AppStarting = false;
    }

    [DllImport("User32.dll")]
    public 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);

    void DisplayClipboardData()
    {
        object dataConverted = null;
        string destinationFileName = string.Empty;
        string destinationFilePath = string.Empty;
        string dataType = string.Empty;

        // Problem: After opening the form, the clipboard monitoring function stops working.

        try
        {
            IDataObject iData = new DataObject();
            iData = Clipboard.GetDataObject();

            if (iData.GetDataPresent(DataFormats.Text) || iData.GetDataPresent(DataFormats.UnicodeText) ||
                iData.GetDataPresent(DataFormats.Rtf))
            {
                // First check if the file already exists.
                dataConverted = iData.GetData(DataFormats.Text); //http://social.msdn.microsoft.com/Forums/vstudio/en-US/9a09cb14-5eb3-4b74-9cf1-ac9e0ae641fc/convert-string-to-unicode?forum=csharpgeneral
                destinationFileName = string.Format("{0} {1}.txt", "CopiedText", DateTime.Today.ToString("MM-dd-yy"));
                destinationFilePath = string.Format("{0}\\{1}", Path.GetTempPath(), destinationFileName);
                dataType = DataFormats.Text.ToString();


                if (File.Exists(destinationFilePath))
                {
                    // Append to the file
                    File.AppendAllText(destinationFilePath, string.Format("\r\n{0}", dataConverted));
                }
                else
                {
                    // Create the file
                    File.WriteAllText(destinationFilePath, dataConverted.ToString());
                }
            }
            else if (iData.GetDataPresent(DataFormats.Bitmap))
            {
                // Works for most (or all?) image types.
                dataConverted = iData.GetData(DataFormats.Bitmap);
                destinationFileName = string.Format("{0} {1} {2}.bmp", "CopiedImage", new Random().Next(1, 10000).ToString(), DateTime.Today.ToString("MM-dd-yy"));
                destinationFilePath = string.Format("{0}\\{1}", Path.GetTempPath(), destinationFileName);
                dataType = DataFormats.Bitmap.ToString();

                while (File.Exists(destinationFilePath))
                {
                    // Write a new path.
                    destinationFileName = string.Format("{0} {1} {2}.bmp", "CopiedImage", new Random().Next(1, 100000).ToString(), DateTime.Today.ToString("MM-dd-yy"));
                    destinationFilePath = string.Format("{0}\\{1}", Path.GetTempPath(), destinationFileName);
                }

                Bitmap copiedImage = (Bitmap)dataConverted;
                copiedImage.Save(destinationFilePath);
            }

            // Add file to grid.
            FileInfo f = new FileInfo(destinationFilePath);
            string[] newRow = new string[] {destinationFileName, destinationFilePath, dataType, f.Length.ToString()};
            dgvFilesCopied.Rows.Add(newRow);
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
        }
    }

    protected override void Dispose(bool disposing)
    {
        ChangeClipboardChain(this.Handle, _NextClipboardViewer);
        if (disposing)
        {
            if (components != null)
            {
                components.Dispose();
            }
        }
        base.Dispose(disposing);
    }

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        // When user opens main form, WndProc is still reacting

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

        if (m.Msg == /*WM_SIZE*/ 0x0005) 
        {
            if (this.WindowState == FormWindowState.Minimized) randomValue = false;
        }

        switch (m.Msg)
        {
            case WM_DRAWCLIPBOARD:
                if (!_AppStarting)
                {
                    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;
        }
    }

    private void cmsCSRightClick_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
    {
        switch (e.ClickedItem.Text)
        {
            case "Open":
                // We want to show the form.
                ShowHideMainForm(true);
                break;
        }
    }
    private void nfyApp_MouseDoubleClick(object sender, MouseEventArgs e)
    {
        // Show files saved
        ShowHideMainForm(true);
        if (dgvFilesCopied.Rows.Count < 1)
        {
            lblHeader.Text = "No files have been copied recently.";
        }
    }

    private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
    {
        // We only want to hide the form when using the X button of the form.
        ShowHideMainForm(false);

        e.Cancel = true;
        // The form/app can be closed when the icon is right clicked -> Close.
    }

    private void ShowHideMainForm(bool show)
    {
        if (show)
        {
            this.ShowInTaskbar = true;
            this.WindowState = FormWindowState.Normal;
        }
        else
        {
            this.ShowInTaskbar = false;
            this.WindowState = FormWindowState.Minimized;
        }
    }

    private void exitToolStripMenuItem_Click(object sender, EventArgs e)
    {
        this.Close();
    }
}
}

会欣赏另一双眼睛。感谢。

1 个答案:

答案 0 :(得分:2)

    _NextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);

您将此代码放在错误的位置。 Handle属性不保证是稳定属性,当您修改表单的某些属性时,它可以更改。与ShowInTaskbar属性一样,它是一个传递给CreateWindowEx()的样式标志。更改它需要重新创建窗口。这改变了Handle,现在观众链断了。

你需要这样做:

    protected override void OnHandleCreated(EventArgs e) {
        base.OnHandleCreated(e);
        _NextClipboardViewer = SetClipboardViewer(this.Handle);
    }

    protected override void OnHandleDestroyed(EventArgs e) {
        ChangeClipboardChain(this.Handle, _NextClipboardViewer);
        base.OnHandleDestroyed(e);
    }

现在,您还可以在OnHandleCreated()上设置断点,并查看代码中的哪个语句导致此情况发生。第二次突破是最初的麻烦制造者。不再。请注意,您对SetClipboardViewer()的声明是错误的。