CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 3 123 LastLast
Results 1 to 15 of 36
  1. #1
    Join Date
    May 2005
    Posts
    39

    Clipboard copy/paste detection

    Hello everybody,

    I am currently writing an application which can detect when the clipboard is used to copy and paste data to other applications. I've got the copy part of the program working.
    All I have to do for that is attach my program to the clipboard viewer chain like this:

    hNextView = SetClipboardViewer(hwnd);
    SendMessage(hNextView,WM_DRAWCLIPBOARD,0,0);

    then when I receive a WM_DRAWCLIPBOARD message, I can call GetClipboardOwner() to get a handle to the program which copied to the clipboard. Finally I remove the clipboard viewer from the chain before exiting the application.

    Is there any way that I can get a handle to the program that the clipboard pastes to?

    I would like to get a notification of paste messages and a handle to the application which is pasted to, in the same way that I can get a handle to the application which copies by calling GetClipboardOwner().

    I anybody has any ideas for how to do that, it would be greatly appreciated.

  2. #2
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    I don't see any other solution that hooking manually the user32.dll GetClipboardData or OpenClipboard function. I mean, modifying the .DLL code at the entry point of these functions.

    You can do that only if you need this program for personal use, but if you want to distribute your program i see no solution, and i fear that there is no one.

  3. #3
    Join Date
    Aug 2004
    Posts
    294

    Re: Clipboard copy/paste detection

    Alternatively, you could try to hook WM_PASTE message and hope that most application use it and not some WM_USER + ???.
    Boris Karadjov
    Brainbench MVP for Visual C++
    http://www.brainbench.com/

  4. #4
    Join Date
    May 2005
    Posts
    39

    Re: Clipboard copy/paste detection

    Thank you for your replies.
    If I wanted to monitor WM_PASTE with a global hook, which hook type (WH_CALLWNDPROC,WH_GETMESSAGE etc.) would be the right one to use ?

  5. #5
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    I suggest to use WH_CALLWNDPROC.
    It hooks the DispatchMessage method, and such hooks are called before the message are treated by the window procedures.

    The WH_GETMESSAGE hooks messages when GetMessage or PeekMessage retrieves the message.

    You can also use WH_CALLWNDPROCRET.
    Such hooks are called after the window procedure.

  6. #6
    Join Date
    May 2005
    Posts
    39

    Re: Clipboard copy/paste detection

    I've tried making a global dll hook, but for some reason most of the messages for other applications are ignored, and it doesn't detect the paste message.

    I set the hook like this:

    hHook = SetWindowsHookEx(WH_CALLWNDPROC,hkCallWndProc,hinstDLL,0);

    This is the code for the function exported from the dll:

    extern "C" __declspec(dllexport)LRESULT CALLBACK CallWndProc(int nCode,
    WPARAM wParam,LPARAM lParam){
    if(nCode < 0){//Do not process the message
    return CallNextHookEx(hHook,nCode,wParam,lParam);
    }
    else {
    LPMSG m=(LPMSG)lParam;
    if(m->message == WM_PASTE ||
    m->message == WM_RENDERFORMAT ||
    m->message == WM_RENDERALLFORMATS){
    cout<<"Paste detected!"<<endl;
    }
    else
    cout<<"Message: "<<m->message<<endl;
    }

    return CallNextHookEx(hHook,nCode,wParam,lParam);
    }
    I wanted to create a global hook. but this only seems to respond to messages passed to my own application. It will recognise if another application is opened,closed or minimized, but only prints messages for mouse movements or the menu for my own application, not for others where I would like to detect a paste.

    How can I get my program to recognise when a message is sent to or from a different application, like notepad, for example ?

  7. #7
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    Of course it does not work, because you use cout inside the DLL function.
    You must know that hooks run into the process owning the windows, and not in the process that called SetWindowsHookEx (except for windows owned by this process of course; it explains why it works for your windows).

    You must use a method to communicate with your process.
    For example, you can use a pipe to send data from the hook to your process.

    You can also send (with PostMessage)a user-defined message to an hidden window of your process (you can get a handle to this window with FindWindow in the hook function)
    I suggest that you give an improbable name to the window class of the hidden window: You can append a GUID generated with guidgen.exe to a human-readable string:
    "MainHookProcessWindowClass-D262F7E7-E558-478B-9E06-4404B258F231" for example.
    I suggest also to call FindWindow with a NULL title, to avoid that it sends WM_GETTEXT messages to all windows of the system, that may block indefinitely if some window are crashed.

    Note that you can only send 64 bits of data (LPARAM+WPARAM) with each message.
    If you want to send more data, you must use the WM_COPYDATA message.
    If you want to handle all messages, you must program your own message loop that don't dispatch your user-defined messages, to avoid infinite recursion, because of your message that is treated by your hook and send a message treated by your hook...
    Code:
    MSG msg;
    while(GetMessage(&msg,NULL,0,0))
    {
    if (msg.message=WM_USER_HOOKMESSAGERECIEVED)
    {
    // call a function that process the message, or directly process the message
    }
    else
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    }
    Since you send a message between process boundaries, you can generate your message by calling RegisterWindowMessage in your process and in the DllEntryPoint or DllMain of the DLL for the DLL_PROCESS_ATTACH event, with a unique string that you have generated with a human-readable-string+guid like that:
    "MessageHookReceptionMessage-B86F5F25-1F5E-45BD-9DC7-8360FB014148"

    Of course, it is not a necessity, and you can just use a statically defined message like (WM_USER+(a number you choosed)), because with the FindWindow method, you can be sure that the user-messages are sent to your application, and not any other.

    Of course, you can use other methods to share data, like using file mapping with serialized access (serialized with a named mutex), or named pipes.

    I i really helped, please, rate this post.
    Last edited by SuperKoko; June 9th, 2005 at 04:03 AM.

  8. #8
    Join Date
    May 2005
    Posts
    39

    Re: Clipboard copy/paste detection

    I've got the program working now, thanks. I can send information about the paste destination file from the dll to the main program by using SendMessage() and WM_COPYDATA.

  9. #9
    Join Date
    May 2005
    Posts
    39

    Re: Clipboard copy/paste detection

    I have now found a problem which may not have a solution - how to get the equivalent of the WM_PASTE message for programs which use a different message for paste operations.

    Catching the WM_PASTE message with a global dll hook works for some programs, but not others, since WM_PASTE is commonly used, but not a required standard. Notepad and Wordpad, for instance, use WM_PASTE but Excel does not. Is there any way to find the equivalent of WM_PASTE used in Excel etc., or are there any other messages which are passed in the system which can be used to detect a paste event ?

  10. #10
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    Quote Originally Posted by sbrown
    I have now found a problem which may not have a solution - how to get the equivalent of the WM_PASTE message for programs which use a different message for paste operations.

    Catching the WM_PASTE message with a global dll hook works for some programs, but not others, since WM_PASTE is commonly used, but not a required standard. Notepad and Wordpad, for instance, use WM_PASTE but Excel does not. Is there any way to find the equivalent of WM_PASTE used in Excel etc., or are there any other messages which are passed in the system which can be used to detect a paste event ?
    It is for that reason that i initialliy thougth that it was not diectly possible.
    But, i may have found a solution, but i am not sure that it works:
    Set your window as clipboard viewer with SetClipboardViewer.
    Treat the WM_DRAWCLIPBOARD message, doing that:
    get an IDataObject of the clipboard content, by calling OleGetClipboard.
    Create an own CDataObject containing a field set (in the constructor) to the IDataObject retrieved by OleGetClipboard.
    call OleSetClipboard with your CDataObject.

    The CDataObject member functions are just hooks that call the member functions of IDataObject, but you must add special handling for some of them:
    You should add a new format that you can name "AlreadyProcessedClipboardData-1B35A0CC-CD0A-4E57-B236-20F152C61661".
    Note that the GUID appended to the name, is just here to avoid conflicts with already existing clipboard formats.
    You must call RegisterClipboardFormat to register this clipboard format.
    To add this format, you should modify the behaviour of the IDataObject::GetData function and IDataObject::QueryGetData, and optionally IDataObject::GetDataHere and IDataObject::EnumFormatEtc.

    Now, if an application request the clipboard content, it will call the CDataObject::GetData member function. In that member you can add all processing you want.

    Note that in the WM_DRAWCLIPBOARD processing message you must call QueryGetData on the IDataObject, to see if the "AlreadyProcessedClipboardData-1B35A0CC-CD0A-4E57-B236-20F152C61661" clipboard format is supported by the object, and you also must call GetData after QueryGetData, because some IDataObjects always return S_OK with QueryGetData. And if both functions succeed, you must not set your CDataObject object, because you know that it is already a hooked IDataObject.

    What i hope is that OleSetClipboard works correctly when called in the WM_DRAWCLIPBOARD message.
    If it does not work because the clipboard is supposed to be already open when the WM_DRAWCLIPBOARD message is entered, try to close it (with CloseClipboard), do all the message must do, and reopen it (with OpenClipboard).

    One thing is really good if all of this works: You will be able to program all automatic clipboard formats conversions you want.
    Last edited by SuperKoko; June 15th, 2005 at 01:26 AM.

  11. #11
    Join Date
    May 2005
    Posts
    39

    Re: Clipboard copy/paste detection

    Thanks for the suggestions.
    Is the difference between Notepad, which uses WM_PASTE, and Excel, which doesn't, because Excel is using the OLE clipboard for pastes ?
    Would the CDataObject::GetData() function use a global dll hook with SetWindowsHookEx() and WH_CALLWNDPROC, like I used previously to detect WM_PASTE ?

  12. #12
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    Quote Originally Posted by sbrown
    Thanks for the suggestions.
    Is the difference between Notepad, which uses WM_PASTE, and Excel, which doesn't, because Excel is using the OLE clipboard for pastes ?
    No, the WM_PASTE message is artifical.
    This message is sent by an edit control to itself when the user click Copy in the context menu or press Ctrl+C downs.
    Some programs directly detect Ctrl+C events and don't send themselves WM_PASTE message which was only defined to eases subclassing (and also hooking) of some controls.
    It is really a good idea to use the WM_PASTE message in common controls, because new common controls in new versions of windows may add new methods to paste (middle-button click for example).

    In the general case some totally-custom controls like excel worksheet don't generate WM_PASTE messages but handle directly Ctrl+C keyboard events or menu commands.
    Would the CDataObject::GetData() function use a global dll hook with SetWindowsHookEx() and WH_CALLWNDPROC, like I used previously to detect WM_PASTE ?
    No, you don't need.
    It is an out-of-process ole object, what means that in the general case, applications can expose data objects (extending the notion of clipboard format) to other applications througth the clipboard by calling the OleSetClipboard function.
    And the miracle of OLE is that when another application accesses to some member functions of the data object, they accesses them through a COM proxy that redirects the calls in your process (in the thread that called OleSetClipboard to be more precise), with complex automatic DDE. And the program automatically waits for a response that is returned when your object functions returns in your process (the proxy handles the communication of the return value).
    So, with OLE you can imagine that other processes directly accesses the object in your process.
    But you must not omit calling OleInitialize (and OleUninitialize when you terminate), at the startup of your process if it is monothreaded, or in each thread which use OLE if you have more than one thread.

  13. #13
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    OLE is based on COM (Component Object Model).
    I suggest that you read these excellent articles about COM (principally inprocess servers).
    http://www.codeguru.com/Cpp/COM-Tech...int.php/c5529/
    http://www.codeguru.com/Cpp/COM-Tech...cle.php/c5533/

  14. #14
    Join Date
    May 2005
    Posts
    39

    Re: Clipboard copy/paste detection

    Thank you for your advice.
    IDataObject is an interface, not a class. Can I find a definition somewhere of a class which implements the IDataObject interface, or do I need to write my own and use that to initialise the IDataObject pointer ?
    I will need to initialise the IDataObject pointer before I pass it to OleGetClipboard().

  15. #15
    Join Date
    Feb 2005
    Location
    Normandy in France
    Posts
    4,590

    Re: Clipboard copy/paste detection

    You need to write your own class object.
    You need to create an instance of this class after having called OleGetClipboard, to pass the object returned to the constructor of your class, then you must call OleSetClipboard after having created your object with new.

Page 1 of 3 123 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured