Categories

CODE
Visual C++ Programming
C++ (Non Visual C++ Issues)
Modern Windows Apps (Metro)
C++ and WinAPI
Managed C++ and C++/CLI
Visual C++ Bugs & Fixes
Graphics Programming
Multithreading
Network Programming
Driver Development
Visual Basic 6.0 Programming
Visual Basic .NET
Crystal Reports
C-Sharp Programming
Managed C++
Visual Basic .NET
ASP.NET
.NET Framework
ADO.NET
Windows Presentation Foundation (WPF) & XAML forum
Silverlight
.NET Installation and Configuration Issues
Java Programming
AJAX
Scripting - Client Side
Database
XML
Wireless/Mobile Development
Assembly
Scripting - Server Side (PHP, Perl, etc.)
Python
General Developer Topics
Project Planning, Design, and Management
Testers and Testing
Algorithms & Data Structures
General Discussion / Chit Chat
Announcements, Press Releases, & News
Feedback
Articles Suggestions
Testing Area
Programming Projects
C# Game(s) Project
Game Engine Project
C++ Coding Project
Project: Code War
Slow Chat: Talk with Microsoft Developer Teams
Slow Chat: Developing Multithreaded Applications
Slow Chat: C++0x
Slow Chat: Visual C++: Yesterday, Today, and Tomorrow
Directory Services
General Windows and DNA Programming
Windows OS Issues
Open Positions (Jobs)
Looking for Work
Visual Basic .NET FAQs
Visual Basic FAQs
CodeGuru Individual FAQs
CodeGuru Individual Visual Basic FAQs

Resources

Java Database
Linux
Coding
Mobile
Hardware
Software Development
Software Development
iOS,OS X
iOS,OS X
ORACLE
IBM DEVELOPER
IBM DEVELOPER
MSDN
MSDN


Tags

C++ and WinAPI

Discuss Windows API related issues using C++ (and Visual C++). This is a non-MFC forum.

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.  				  			

  				  					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.  				  			

  				  					Alternatively, you could try to hook WM_PASTE message and hope that most application use it and not some WM_USER + ???.  				  			

  				  					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 ?  				  			

  				  					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.  				  			

  				  					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 ?  				  			

  				  					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.  				  			

  				  					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.  				  			

  				  					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 ?  				  			

  				  					  	  		  			  			  				  					 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.  				  			

  				  					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 ?  				  			

  				  					  	  		  			  			  				  					 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.  				  			

  				  					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/  				  			

  				  					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().  				  			

  				  					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.


Related Links

How can I handle ON_UPDATE_COMMAND_UI in a non-mfc proj?
Protecting (Already compiled) executables and .dll's and such.
Dib
How to find the owner of a file
All Running Processes (newbie question)
ticker like rich edit box
Disabling the toolbar button of MSWORD
Taskmanager Like Program
Dll explicit linkage problem
How to put text in the window?
Message box w/variable problem
DeviceIOControl Camera..
Unicode Porting Issues !!
Searching a Process
[RESOLVED] Ws_drawitem
Reading from Registry and change strings