I have a requirement where I need to create a class library to notify the caller when the user does cntrl+c. I have created a windows form (disabling form UI, even the process is hidden from the taskbar) inside this class library!, so that will have a handler for a SetClipboardViewer
WinApi32 method. Below is the code snippet.
Form Initializer,
private void InitializeComponent()
{
this.SuspendLayout();
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.ClientSize = new System.Drawing.Size(326, 90);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "ClipboardHandle";
this.Text = "ClipboardHandler";
this.Opacity = 0D;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.WindowState = System.Windows.Forms.FormWindowState.Minimized;
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OnFormClosing);
this.Load += new System.EventHandler(this.OnLoad);
this.ResumeLayout(false);
}
And in my form class (i.e. ClipboardHandle) OnLoad
event, passing the form handler
to SetClipboardViewer
method as shown below,
IntPtr nextClipboardViewer;
private void OnLoad(object sender, EventArgs e)
{
nextClipboardViewer = SetClipboardViewer(this.Handle);
}
WinApi32
method
[DllImport("User32.dll")]
internal static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
Since this is a class library I can't use Application.Run
to load the form instead I am initiating the form class like below,
monitorTask = Task.Run(async delegate
{
log.Info("STA thread start");
var stathread = new Thread(() =>
{
try
{
handler = new ClipboardHandle(); // form class
handler.ClipboardEvent += (e) => { onEventOccured("clipboardCopy", e); };
handler.ShowDialog();
}
catch (Exception ex)
{
log.Error("Exception: ", ex);
}
});
stathread.SetApartmentState(ApartmentState.STA);
stathread.Start();
while (!ct.IsCancellationRequested)
{
await Task.Delay(100);
}
log.Info("unregister and close");
handler.UnRegister();
handler.Close();
stathread.Join();
ct.ThrowIfCancellationRequested();
}, ct);
I have tried using both handler.ShowDialog()
as well as handler.Show()
, the below error I get when I use handler.ShowDialog()
and the later one doesn't show any error but there won't be any event trigger at the WndProc
method on clipboard
changes.
Exception: :System.InvalidOperationException: Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application. at System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
First thing, I understand that using ShowDialog() inside a class library is not a way to go, but Show() method doesn't either fire up the clipboard change events and the thing I don't understand is, This application runs perfectly fine when the handler.ShowDialog()
method is used inside the class library and referred to in a console app (test application) in visual studio (in debug mode) but fails when hosted as a windows service.
Why the WndProc
doesn't get fired on clipboard changes when handler.Show()
is used but fires when handler.ShowDialog()
?
I have also tried running windows service in user context mode. Also referred below posts,
Any insights or help greatly appreciated. Thanks in advance.