MS Windows Clipboard Woes

Mindwatering Incorporated

Author: Tripp W Black

Created: 07/20 at 02:49 PM

 

Category:
Notes Developer Tips
LotusScript, Bugs/Workarounds

Issue 1:
With the 64-bit Notes 12.0.x client, after upgrading to MS Windows 11, the LotusScript UI front-end copy to clipboard are no longer working and hanging notes (spinning forever cursor wheel). If you create a VM with only two processor cores, the issue generally is not seen with the first copy-and-paste. If you increase the amount of text from several lines to 20 to 30 lines and perform the Lotusscript UI call, the hang potential increases. If you have a VM or workstation with lots of processors (e.g. 8 or more), the hang issue occurs 100% of the time at the second use.

The issue cannot be reproduced on MS Windows 10. It is a MS Windows 11 specific bug.

Workaround:
HCL had a sample 64-bit workaround of using the MS native DLLs directly for 64-bit. Based on their sample, we created this GetCopyWinClipboard library code included at the bottom of this page. We did NOT verify the 32-bit functionality because we no longer have 32bit MS Windows clients which to test.


Issue 2:
Clipboard does not work after days running and sleep/hibernation/screensaver.
(Not in an issue in MS Win NT, Issue persists in Windows XP until FP 3, and Windows 7 through 11)

- Workaround 1:
The very old Shift+Insert copy and paste still work in XP and in early MS Win 7.


- Workaround 1:
Disable all Screensavers and Sleep/Hibernation. Maybe not great for the energy bill, but at least the operating system clipboard works.


- Workaround 2:
In Windows 11, disable the "Connected Devices Platform Service)
Computer Management --> Services --> Connected Devices Platform Service --> Start-up Type change from Automatic (Delayed) to Disabled.

(source: answers.microsoft.com forum 4d89fe8-0336-48ba-9296-0adcecddbd98. Various sites have lifted the content, which HCL found.)

Note:
This service is used with "smart devices" to sync data to those devices, and the MS cloud.



Copy to Clipboard Code Snippet

(Options)
Use "GetCopyWinClipboard"


Click
...
Dim strToClip as String ' working string of collected string content joined together with Chr$(10)
...

strToClip = Join(fullnmLst(), Chr$(10) & "")


Select Case s.Platform

Case "Windows/64"
' ms win11 is crashing w/high CPU copy-to-clipboard, switching to 64-bit ptr C-API method
strFromClip = GetClipboard()
Msgbox "Overwriting existing contents: " & strFromClip
Call SetClipboard(strToClip)
Msgbox "Copied to 64-bit MS Windows: " & strToClip

Case Else
' use stock UI objects
Print "Composing document vi UI objects . . ."
' create/open Temporary form
Set uiDoc = w.ComposeDocument("","","ToClp")
Sleep 1
If (uiDoc Is Nothing) Then
' skip, failure
Msgbox "Failure creating doc to paste value",, "Aborted"
Exit Sub
End If
Print "Copying to field for copy-and-paste..."
' copy the value to the field
Call uidoc.FieldAppendText("FldToClpBd",strToClip)
Sleep 1
Print "Selecting text for clipboard..."
'copy the value to the clipboard
Call uidoc.GotoField( "FldToClpBd" )
Call uidoc.SelectAll
Print "Copying to clipboard..."
Call uidoc.Copy
Print "Closing temporary form..."
Sleep 1
' clear form
Call uiDoc.Close(True)
Set uidoc = Nothing
' done UI
End Select
...


GetCopyWinClipboard LS Library:

%REM
Library GetCopyWinClipboard
Created Jun 3, 2024 by MW Admin/Mindwatering
Description: Used w/32-bit or w/64-bit copy to clipboard
%END REM
Option Public
Option Declare


'dataformat ID for ANSI text with ending null (\0). CR(13)/ LF(10) are for end of line.
Const CF_UNICODETEXT = 13
Const OS_TRANSLATE_UNICODE_TO_LMBCS = 23
Private Const CF_TEXT = 1
Private Const GMEM_MOVABLE = &H2&
Private Const GMEM_DDESHARE = &H2000&

'** 32-bit API calls
Declare Function OpenClipboard Lib "user32.dll" (ByVal hwnd As Long) As Long
Declare Function CloseClipboard Lib "user32.dll" () As Long
Declare Function GetClipboardData Lib "user32.dll" (ByVal wFormat As Long) As Long
Declare Function GlobalLock Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function GlobalUnlock Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function OSTranslateFromPtr Lib "nnotes.dll" Alias "OSTranslate" (ByVal mode As Integer,ByVal strIn As Long,ByVal lenIn As Integer,ByVal strOut As LMBCS String, ByVal lenOut As Integer ) As Integer

Declare Private Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Declare Private Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
Declare Private Function EmptyClipboard Lib "user32" () As Long
Declare Private Sub MoveMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal strDest As Any, _
ByVal lpSource As Any, ByVal Length As Any)
Declare Private Function SetClipboardData Lib "user32" (ByVal wFormat As Long, ByVal hData As Long) As Long

'** 64-bit API calls
Declare Function OpenClipboard_64 Lib "user32.dll" Alias "OpenClipboard" (ByVal hwnd As Double) As Long '** hwnd is a window handle
Declare Function GetClipboardData_64 Lib "user32.dll" Alias "GetClipboardData" (ByVal wFormat As Long) As Double '** returns a memory handle
Declare Function CloseClipboard_64 Lib "user32.dll" Alias "CloseClipboard" () As Long
Declare Function GlobalLock_64 Lib "kernel32.dll" Alias "GlobalLock" (ByVal hMem As Double) As Double '** hMem is a memory handle, returns a pointer
Declare Function GlobalUnlock_64 Lib "kernel32.dll" Alias "GlobalUnlock" (ByVal hMem As Double) As Long '** hMem is a memory handle, returns a BOOL
Declare Function GlobalSize_64 Lib "kernel32.dll" Alias "GlobalSize" (ByVal hMem As Double) As Long '** hMem is a memory handle, returns a size
Declare Function OSTranslateFromPtr_64 Lib "nnotes.dll" Alias "OSTranslate" ( ByVal mode As Integer, ByVal strIn As Double, _ '** strIn is a string pointer
ByVal lenIn As Integer, _
ByVal strOut As LMBCS String, _
ByVal lenOut As Integer ) As Integer

Declare Private Function GlobalAlloc_64 Lib "kernel32" Alias "GlobalAlloc" (ByVal wFlags As Long, ByVal dwBytes As Long) As Double ' returns a memory handle
Declare Private Function GlobalFree_64 Lib "kernel32" Alias "GlobalFree" (ByVal hMem As Double) As Double 'hmem is a memory handle, returns a pointer
Declare Private Function EmptyClipboard_64 Lib "user32" Alias "EmptyClipboard" () As Long
Declare Private Sub MoveMemory_64 Lib "kernel32" Alias "RtlMoveMemory" (ByVal strDest As Double, _
ByVal lpSource As String, ByVal Length As Long)'lpSource is a pointers
Declare Private Function SetClipboardData_64 Lib "user32" Alias "SetClipboardData" (ByVal wFormat As Long, ByVal hData As Double) As Double 'hData is a handle and it returns a handle

Sub Initialize
' WARNING - Make sure you set the pointer in order for MoveMemory to not crash Notes
End Sub



%REM
Function GetClipboard
Description: Retrieves the current clipboard contents for use by the calling code
%END REM
Function GetClipboard() As String
Dim session As New NotesSession
If (session.Platform = "Windows/64") Then
GetClipboard = GetClipboard64()
Exit Function
End If
Dim glbHandle As Long
Dim cbPointer As Long
Dim cbPointerLen As Long
Dim cbString As String
If OpenClipboard(0) Then
glbHandle = GetClipboardData(CF_UNICODETEXT)
cbPointer = GlobalLock(glbHandle)
cbPointerLen = GlobalSize(glbHandle)
cbString = Space(cbPointerLen)
Call OSTranslateFromPtr( OS_TRANSLATE_UNICODE_TO_LMBCS, _
cbPointer, cbPointerLen, cbString, cbPointerLen )
cbString = StrLeft(cbString, Chr(0))
Call GlobalUnlock(glbHandle)
Call CloseClipboard()
End If
GetClipboard = cbString
End Function


Function GetClipboard64() As String
Dim session As New NotesSession
session.UseDoubleAsPointer = True
Dim glbHandle_64 As Double
Dim cbPointer_64 As Double
Dim cbPointerLen As Long
Dim cbString As String
If OpenClipboard_64(0) Then
glbHandle_64 = GetClipboardData_64(CF_UNICODETEXT)
cbPointer_64 = GlobalLock_64(glbHandle_64)
cbPointerLen = GlobalSize_64(glbHandle_64)
cbString = Space(cbPointerLen)
Call OSTranslateFromPtr_64( OS_TRANSLATE_UNICODE_TO_LMBCS, _
cbPointer_64, cbPointerLen, cbString, cbPointerLen )
cbString = StrLeft(cbString, Chr(0))
Call GlobalUnlock_64(glbHandle_64)
Call CloseClipboard_64()
End If
GetClipboard64 = cbString
session.UseDoubleAsPointer = False
End Function
%REM
Sub SetClibboard
Description: Set the clipboard contents for transfer to another program
%END REM
Sub SetClipboard(cbstr As String)
Dim lSize As Long
Dim hMem As Long
Dim pMemory As Long
Dim temp As Variant
Dim session As New NotesSession
If (session.Platform = "Windows/64") Then
Call SetClipboard64(cbstr)
Exit Sub
End If

lSize = Len(cbstr)+1
hMem = GlobalAlloc(GMEM_MOVABLE Or GMEM_DDESHARE, lSize)
If hMem = 0 Or IsNull(hMem) Then Exit Sub
pMemory = GlobalLock(hMem)
If pMemory = 0 Or IsNull(pMemory) Then
GlobalFree(hMem)
Exit Sub
End If
Call MoveMemory(pMemory, cbstr, lSize)
Call GlobalUnlock(hMem)
If (OpenClipboard(0&) <> 0) Then
If (EmptyClipboard() <> 0) Then
temp = SetClipboardData(CF_TEXT, hMem)
End If
temp = CloseClipboard()
End If
GlobalFree(hMem)
End Sub
Sub SetClipboard64(cbstr As String)
Dim session As New NotesSession
session.UseDoubleAsPointer = True

Dim lSize As Long
Dim hMem As Double
Dim pMemory As Double
Dim temp As Double

lSize = Len(cbstr)+1
hMem = GlobalAlloc_64(GMEM_MOVABLE Or GMEM_DDESHARE, lSize)
If hMem = 0 Or IsNull(hMem) Then Exit Sub
pMemory = GlobalLock_64(hMem)
If pMemory = 0 Or IsNull(pMemory) Then
GlobalFree_64(hMem)
Exit Sub
End If
Call MoveMemory_64(pMemory, cbstr, lSize)
Call GlobalUnlock_64(hMem)
If (OpenClipboard_64(0&) <> 0) Then
If (EmptyClipboard_64() <> 0) Then
temp = SetClipboardData_64(CF_TEXT, hMem)
End If
temp = CloseClipboard_64()
End If
GlobalFree_64(hMem)
session.UseDoubleAsPointer = False
End Sub






previous page