I am using WndProc
and WM_DRAWCLIPBOARD
to monitor the Windows clipboard and save text and images copied. It's working well except for one scenario. I am using NotifyIcon
and have the application docked to the notification area by default (ShowInTaskbar
= False, WindowState
= Minimized). The copy logic works well except after I open my form. When I right click the icon for the app and click Open, WM_DRAWCLIPBOARD
isn't called anymore when I copy a piece of text or image, although WndProc
is called constantly.
Does NotifyIcon
use a different thread than the main UI, if so it could be that the NotifyIcon
thread isn't listening to the UI thread. Note that I'm not actually Form.Show()
ing, rather I
m just maximizing the form and showing the taskbar icon. The opposite is done when the form is "closed". Here is my code I am using. It's just one class/form, so here is all of it.
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();
}
}
}
Would appreciate another set of eyes. Thanks.