Page 1 of 1

Monitoring the clipboard?

Posted: Tue Mar 21, 2006 12:35 pm
by PB
Is this the correct way to monitor the clipboard?

Code: Select all

Global nextviewer

Procedure Callback(WindowID,Message,wParam,lParam)
  Result=#PB_ProcessPureBasicEvents
  Select Message
    Case #WM_CHANGECBCHAIN
      If wParam=nextviewer
        nextviewer=lParam
      ElseIf nextviewer<>0
        SendMessage_(nextviewer,Message,wParam,lParam)
      EndIf
      Result=1
    Case #WM_DRAWCLIPBOARD
      Debug GetClipboardText() ; Show new contents.
      SendMessage_(nextviewer,Message,wParam,lParam)
      Result=1
  EndSelect
  ProcedureReturn Result
EndProcedure

If OpenWindow(0,300,350,200,100,"Clipboard Viewer",#PB_Window_SystemMenu)
  nextviewer=SetClipboardViewer_(WindowID(0)) ; So we are informed of clipboard changes.
  SetWindowCallback(@Callback()) : Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
  ChangeClipboardChain_(WindowID(0),nextviewer) : PostQuitMessage_(0)
EndIf
Reasons for asking:

(1) The MSDN SDK ( http://tinyurl.com/4grj8 ) talks of the callback using
#WM_CREATE to add the viewer, but do I need to do that if I'm using the
SetClipboardViewer API?

(2) Justin's example ( http://www.purebasic.fr/english/viewtopic.php?t=3784 )
uses a Shared variable type for "nextviewer" but I'm using a Global; so which
is correct? The SDK says to use a "static variable" so I'm guessing Shared?
Although it shouldn't really matter in this example, right?

(3) Justin's example, in the #WM_CHANGECBCHAIN event, sets the value of
"nextviewer" to "hwnd" but the SDK says to set it to "lParam" (like I've done).
I assume that's what Justin meant?

(4) Justin doesn't use PostQuitMessage_(0), so is it necessary?

(5) The SDK has "break;" and I assume "Result=1" is the equivalent here?

Now, assuming what I've done is correct: I've noticed that at work, where my
PC uses "Remote Desktop Connection" (mstsc.exe) on Windows 2000, that
sometimes the clipboard in the remote desktop is empty after I copy something
from the "host" desktop. So, I'm guessing I'm missing something up there in my
clipboard code. Or, maybe it's just a bad app breaking the clipboard chain?

My exact problem is mentioned here: http://tinyurl.com/nbn38
The poster is referred to this topic: http://tinyurl.com/n2j6z

In that topic, the response is that another app enters the clipboard chain but
then doesn't exit it cleanly, causing the problem. If I quit "Remote Desktop
Connection" at work and re-launch it, all works well again, which would thus
suggest it's a remote app breaking the chain, and not my code above?

I guess I just want some reassurance that my code above is fine. :)

Posted: Tue Mar 21, 2006 7:09 pm
by Xombie
1. I believe WM_CREATE is used to set clipboard capture for when the window itself is created. You can set it manually after all of your own window creation/gadget creation. No problems.

2. I used a global variable and it worked perfectly in my own code. The main point is to always update it when needed. In that case, a global works just fine.

3. Yeah. wParam stores the window being removed while lParam stores the next available window (or null if none). So you'd set the next one to lParam.

4. I certainly didn't use it in mine. I think MSDN is using it because the original window is actually being destroyed (closed).

5. Hmmm... I can't remember exactly what I did in mine... sorry. Code is at home and I'm at work ^_^

Wait... I just found it. So, first I create my window and controls. Then...

Code: Select all

   ClipNext = SetClipboardViewer_(WindowID(#WindowMain))
   ;
   SetWindowCallback(@HandleEvents(), #WindowMain)
Then the main callback...

Code: Select all

Procedure.l HandleEvents(HandleWindow, message, wParam, lParam) 
   ; Main form callback procedure. 
   Define.l lResult
   ;
   lResult = #PB_ProcessPureBasicEvents
   ;
   If PushedCopy : ProcedureReturn lResult : EndIf
   ;
   If message = #WM_CHANGECBCHAIN
      ;
      If WindowID(#WindowMain) = ClipNext
         ;
         ClipNext = WindowID(#WindowMain)
         ;
      Else
         ;
         If ClipNext <> #Null : SendMessage_(ClipNext, message, wParam, lParam) : EndIf 
         ;
      EndIf
      ;
      lResult = 1
      ;
   ElseIf message = #WM_DRAWCLIPBOARD
      ;
      SendMessage_(ClipNext, message, wParam, lParam)
      ;
      lResult = 1
      ;
   EndIf 
   ;
   ProcedureReturn lResult 
   ; 
EndProcedure
And then at the end, when my program is closing down I call...

Code: Select all

ChangeClipboardChain_(WindowID(#WindowMain), ClipNext)
This was all adapted from Justin's original code, I believe.

Does that help at all?

Re: Monitoring the clipboard?

Posted: Tue Mar 21, 2006 7:59 pm
by Trond
PB wrote:Is this the correct way to monitor the clipboard?
Dunno.

PB wrote:(1) The MSDN SDK ( http://tinyurl.com/4grj8 ) talks of the callback using
#WM_CREATE to add the viewer, but do I need to do that if I'm using the
SetClipboardViewer API?
No.
PB wrote:(2) Justin's example ( http://www.purebasic.fr/english/viewtopic.php?t=3784 )
uses a Shared variable type for "nextviewer" but I'm using a Global; so which
is correct? The SDK says to use a "static variable" so I'm guessing Shared?
Although it shouldn't really matter in this example, right?
It doesn't matter what kind of variable you use as long you make sure that it's value is always preserved correctly in between all places where it is accessed.
PB wrote:(3) Justin's example, in the #WM_CHANGECBCHAIN event, sets the value of
"nextviewer" to "hwnd" but the SDK says to set it to "lParam" (like I've done).
I assume that's what Justin meant?
nextviewer should be set to lParam. When a clipboard viewer window receives the WM_CHANGECBCHAIN message, it should call the SendMessage function to pass the message to the next window in the chain, unless the next window is the window being removed. In this case, the clipboard viewer should save the handle specified by lParam as the next window in the chain.
PB wrote:(4) Justin doesn't use PostQuitMessage_(0), so is it necessary?
It's not neccessary unless I missed something fundamental. Actually, I think it's plain wrong.
PB wrote:(5) The SDK has "break;" and I assume "Result=1" is the equivalent here?
I don't know what the SDK says, but if you see a break and replace it with Result=1, make sure that you do not unconditionally modify Result afterwards.