Author Topic: Cleaning up after hook into Win32 Clipboard chain  (Read 3602 times)

0 Members and 1 Guest are viewing this topic.

kaefer

  • Guest
Cleaning up after hook into Win32 Clipboard chain
« on: October 25, 2010, 06:33:40 AM »
Hi friends,

enclosed is a simple class which provides the message handling for an UserControl that hooks into the Clipboard chain.

Is it in any way critical that when the UserControl gets destroyed, a call to ChangeClipboardChain is made to inform the chain that our chlipboard viewer is about to be removed?

If for example the UserControl resides inside an AutoCAD Palette Set, this could be effected by a handler for PaletteSetDestroy, which then calls Unhook() below.

Code: [Select]
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
   
namespace MyClipBoard
{
    class WinInvoke
    {
   
    [DllImport("user32.dll")]
    public static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

    [DllImport("user32.dll")]
    public static extern IntPtr GetClipboardViewer();

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

    }
    public class CbPalette : UserControl
    {
        //  Constants for Windows API calls
        public const int WM_DRAWCLIPBOARD = 0x308;
        public const int WM_CHANGECBCHAIN = 0x30D;

        // Handle for next clipboard viewer
        public IntPtr _nxtCbVwrHWnd;

        public CbPalette()
        {
            _nxtCbVwrHWnd = WinInvoke.SetClipboardViewer(this.Handle);
        }

        public void Unhook()
        {
            WinInvoke.ChangeClipboardChain(this.Handle, _nxtCbVwrHWnd);
        }

        protected override void WndProc(ref Message m)
        {
            switch(m.Msg)
            {
                // The clipboard has changed
                case WM_DRAWCLIPBOARD:
                    WinInvoke.SendMessage(this._nxtCbVwrHWnd, m.Msg, m.WParam, m.LParam);
                    break;
                // Another clipboard viewer has removed itself
                case WM_CHANGECBCHAIN:
                    if (m.WParam == _nxtCbVwrHWnd)
                        _nxtCbVwrHWnd = m.LParam;
                    else
                        WinInvoke.SendMessage(_nxtCbVwrHWnd, m.Msg, m.WParam, m.LParam);
                    break;
            }
            base.WndProc(ref m);
        }
    }
}

(No, I don't tell anyone where this idea came from... since there was no unhooking.)
Cheers, Thorsten

pkohut

  • Bull Frog
  • Posts: 483
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #1 on: October 25, 2010, 08:16:56 AM »
It's correct. CbPalette is destroyed before UserControl.
New tread (not retired) - public repo at https://github.com/pkohut

jgr

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #2 on: October 25, 2010, 09:27:07 AM »
WM_CREATE, WM_DESTROY

Code: [Select]
Protected Overrides Sub WndProc(ByRef m As Message)

    Select Case m.Msg

        Case NativeConstants.WM_CREATE

            ' HookClipBoard

        Case NativeConstants.WM_DESTROY

            'UnHookClipBoard

            MyBase.WndProc (m)

        Case NativeConstants.WM_DRAWCLIPBOARD

            '.....

            NativeMethods.SendMessageW(_hwndNextViewer, CUInt(m.Msg), m.WParam, m.LParam)

            m.Result = IntPtr.Zero

        Case NativeConstants.WM_CHANGECBCHAIN

            If m.WParam.Equals(_hwndNextViewer) Then
                _hwndNextViewer = m.LParam

            ElseIf Not (_hwndNextViewer.Equals(IntPtr.Zero)) Then
                NativeMethods.SendMessageW(_hwndNextViewer, CUInt(m.Msg), m.WParam, m.LParam)
            End If

            m.Result = IntPtr.Zero

        Case Else

            MyBase.WndProc (m)
    End Select

End Sub

kaefer

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #3 on: October 25, 2010, 11:35:41 AM »
WM_CREATE, WM_DESTROY

Code: [Select]
Case NativeConstants.WM_DESTROY

            'UnHookClipBoard

            MyBase.WndProc (m)
Hi jgr,

yes, of course, there's already a handler in place! Thanks a lot.

New and improved version:
Code: [Select]
    public class CbPalette : UserControl
    {
        [DllImport("user32.dll")]
        static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

        [DllImport("user32.dll")]
        static extern IntPtr GetClipboardViewer();

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll", SetLastError = true)]
        static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        //  Constants for Windows API calls
        const int WM_DESTROY = 0x0002;
        const int WM_DRAWCLIPBOARD = 0x308;
        const int WM_CHANGECBCHAIN = 0x30D;

        // Handle for next clipboard viewer
        public IntPtr _nxtCbVwrHWnd;
        public Func<bool> _f;

        public CbPalette()
        {
            _nxtCbVwrHWnd = SetClipboardViewer(this.Handle);
        }

        // Override WndProc to get messages
        protected override void WndProc(ref Message m)
        {
            switch(m.Msg)
            {
                case WM_DESTROY:
                    ChangeClipboardChain(this.Handle, _nxtCbVwrHWnd);
                    break;
                // The clipboard has changed
                case WM_DRAWCLIPBOARD:
                    if (_f())
                        SendMessage(this._nxtCbVwrHWnd, m.Msg, m.WParam, m.LParam);
                    break;
                // Another clipboard viewer has removed itself
                case WM_CHANGECBCHAIN:
                    if (m.WParam == _nxtCbVwrHWnd)
                        _nxtCbVwrHWnd = m.LParam;
                    else
                        SendMessage(_nxtCbVwrHWnd, m.Msg, m.WParam, m.LParam);
                    break;
            }
            base.WndProc(ref m);
        }
    }
}
Now what would happen if I omit the WM_DESTROY case? Flood, quicksands, earthquake or worse?
Thorsten

jgr

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #4 on: October 25, 2010, 03:32:29 PM »
What you need this?, Maybe you just need to use WM_COPY, WM_CUT or WM_PASTE.

sorry, I do not speak English.
« Last Edit: October 25, 2010, 03:40:17 PM by jgr »

kaefer

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #5 on: October 26, 2010, 03:06:02 AM »
What you need this?, Maybe you just need to use WM_COPY, WM_CUT or WM_PASTE.

WM_DRAWCLIPBOARD only. I left my work code accidently in, that is Func<bool>.

The calling site is actually from F#:
Code: [Select]
let initPS() =
    let ps = new Autodesk.AutoCAD.Windows.PaletteSet(...)
    ps.Add("Clipboard", new CbPalette(...)) |> ignore
    ps

let ps = lazy initPS()

[<CommandMethod "cbp">]
let cbpCmd() = ps.Value.Visible <- true

My question is simply: Is it good style to clean up or will the Win32 subsystem do this for me?

Thanks again, Thorsten

LE3

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #6 on: October 26, 2010, 09:49:56 PM »
The last time I did something about using the clipboard from arx, was the year 2006, but maybe:

I would end up placing:

Code: [Select]
ChangeClipboardChain(this.Handle, _nxtCbVwrHWnd);
In the clean up mechanism of dispose.

And to remove from this function switching portion:

Code: [Select]
protected override void WndProc(...)
The destroy message.

And to place inside of the switch:
Code: [Select]
switch(m.Msg)
{
...
default:
base.WndProc(ref m);
break;
}

kaefer

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #7 on: October 27, 2010, 12:14:14 PM »
The last time I did something about using the clipboard from arx, was the year 2006, but maybe:

I would end up placing:

Code: [Select]
ChangeClipboardChain(this.Handle, _nxtCbVwrHWnd);
In the clean up mechanism of dispose.

Will do. Thanks, Luis.

Quote
And to place inside of the switch:
Code: [Select]
switch(m.Msg)
{
...
default:
base.WndProc(ref m);
break;
}
Now that is because you surely were brought up by different flavors of C, where the absence of a default label could be held against you.

Cheers, Thorsten

Glenn R

  • Guest
Re: Cleaning up after hook into Win32 Clipboard chain
« Reply #8 on: October 27, 2010, 05:20:39 PM »
The golden rule, especially if you're using WIN32 unmanaged resources, is to dispose of it, but I'm sure you already knew that.

It's one, perhaps the only one, of the reasons to implement IDispose on a custom object/class that uses unmanaged resources.