Day 025: _CLIPBOARD$ - Pete - 12-06-2022
_CLIPBOARD$ captures the string contents of the operating system clipboard contents.
The neat thing about this platform cross-compatible feature is the ability to use it outside the immediate program. I'll explain...
_CLIPBOARD$ can be used to capture any copied text from any running application. Once captured, the string can be used inside your app, or transferred to another QB64 app. _CLIPBOARD$ is therefore one of a few ways we can communicate with other QB64 programs running simultaneously. Now as exciting as that may be, the use of _CLIPBOARD, for inter-program communications, is somewhat frowned upon by Microsoft. The preferred M$ method is to establish a TCP/IP communications, which will be discussed a bit later along with piping to the clipboard using Windows SHELL command.
Right now, let's take a look at a copying, parse and print text example.
For this demo, start the app and then come back to this page, do Ctrl + A to copy all the tet, and Ctrl + C to copy it to the clipboard. Upon copying, the app will parse and display the clipboard text capture.
Code: (Select All) $CONSOLE:ONLY
_CLIPBOARD$ = ""
COLOR 15, 1
CLS
PRINT " Copy the Keyword of the Day page..."
DO: _LIMIT 1: LOOP UNTIL LEN(_CLIPBOARD$)
CLS
a$ = _CLIPBOARD$ + CHR$(13)
DO
x$ = MID$(a$, 1, INSTR(a$, CHR$(13)))
a$ = LTRIM$(MID$(a$, LEN(x$) + 2))
x$ = _TRIM$(MID$(x$, 1, INSTR(x$, CHR$(13)) - 1))
IF LEN(x$) THEN
IF MID$(x$, 1, 11) = "IP Address:" OR INSTR(x$, "AM") AND LEFT$(x$, 1) = "(" OR INSTR(x$, "PM") AND LEFT$(x$, 1) = "(" THEN pon = 0: spacer = 0
IF pon THEN
w = _WIDTH - 2
IF MID$(a$, 1, 2) = CHR$(13) + CHR$(10) AND last = 0 THEN spacer = 1
IF w > 0 THEN
DO
t$ = MID$(x$, 1, w)
chop = 1
IF MID$(x$, w + 1, 1) <> " " THEN ' Now we have to chop it.
IF INSTR(x$, " ") > 1 AND INSTR(t$, " ") <> 0 AND LEN(x$) > w THEN
t$ = MID$(t$, 1, _INSTRREV(t$, " ") - 1)
chop = 2
END IF
ELSE
chop = 2
END IF
IF w = 1 THEN chop = 1
x$ = MID$(x$, LEN(t$) + chop)
'-----------------------------------------------------------------------
IF LEN(t$) THEN LOCATE , 2: PRINT LTRIM$(t$): IF spacer = 0 THEN last = 0
'-----------------------------------------------------------------------
LOOP UNTIL LEN(t$) AND LEN(LTRIM$(x$)) = 0
IF spacer = 1 THEN PRINT: spacer = 0
END IF
END IF
IF INSTR(x$, ",") <> 0 AND INSTR(x$, "-") <> 0 THEN
IF INSTR(x$, "AM") OR INSTR(x$, "PM") AND LEFT$(x$, 1) <> "(" THEN
pon = 1
END IF
END IF
ELSE
END IF
LOOP UNTIL a$ = ""
PRINT: PRINT "Click the 'X' in the title bar to close this window."
DO: _LIMIT 1: SLEEP: LOOP
Okay, now how about we have a look at using _CLIPBOARD$ to make a small chat app...
For this demo you will need to save the second app as: "myclip.exe" and run the first app to access it.
Code: (Select All) _SCREENMOVE 0, 0 ' Set up this host window to the left of your desktop.
WIDTH 60, 25
IF NOT _FILEEXISTS("myclip.exe") THEN PRINT "Cannot find file: "; "myclip.exe. Ending...": END
a$ = "Opening as host." '
PRINT a$: PRINT
SHELL _HIDE "start myclip.exe" ' Open the client window.
_SCREENCLICK 30 * 8, 10
PRINT "Connection established.": PRINT
DO
_CLIPBOARD$ = ""
' Okay, time to input something on the host that will be communicated to the client.
INPUT "Input a message: "; msg$: PRINT
_CLIPBOARD$ = msg$
_KEYCLEAR
_DELAY 2
_CLIPBOARD$ = ""
DO: _LIMIT 5: LOOP UNTIL LEN(_CLIPBOARD$)
_SCREENCLICK 30 * 8, 10
PRINT "Reply received: "; _CLIPBOARD$: PRINT
LOOP
Save this as "myclip.exe" but don't run it. Run the first app (it doesn't matter if it's named) to start the chat sequence.
Code: (Select All) _SCREENMOVE 60 * 8 + 10, 0 ' Set up this client window to the right of host.
WIDTH 60, 25
_CLIPBOARD$ = ""
a$ = "Opening as host." '
PRINT a$: PRINT
PRINT "Connection established.": PRINT
DO
DO: _LIMIT 5: LOOP UNTIL LEN(_CLIPBOARD$)
_SCREENCLICK 90 * 8, 10: _DELAY .25
PRINT "Message received: "; _CLIPBOARD$: PRINT
' Okay, time to input something on the client that will be communicated to the host.
INPUT "Input a message: "; msg$: PRINT
_CLIPBOARD$ = msg$
_KEYCLEAR
_DELAY 2
_CLIPBOARD$ = ""
LOOP
So M$ recommends using TCP/IP to accomplish what we just demoed with _CLIPBOARD. This example covers how to do that, but it's much more involved. Also, because it uses TCP/IP, you will have to clear it with Windows Defender.
This is a Windows only demo. It uses min/restore to regain focus to each window, instead of _SCREENCLICK like the _CLIPBOARD demo above. Save, but don't run the second app as "messenger_client.exe" then run the first one. Clear for use with Windows Defender when the alert pops up on your screen.
Code: (Select All) DECLARE DYNAMIC LIBRARY "user32"
FUNCTION FindWindowA%& (BYVAL ClassName AS _OFFSET, WindowName$) 'handle by title
FUNCTION ShowWindow& (BYVAL hwnd AS _OFFSET, BYVAL nCmdShow AS LONG) 'maximize process
FUNCTION SetForegroundWindow%& (BYVAL hwnd AS _OFFSET) 'set foreground window process(focus)
FUNCTION GetForegroundWindow%& 'Find currently focused process handle
END DECLARE
_SCREENMOVE 0, 0
title$ = "Messenger_Host"
_TITLE (title$)
_DELAY .1
_SCREENMOVE 0, 0 ' Set up this host window to the left of your desktop.
WIDTH 60, 25
IF NOT _FILEEXISTS("messenger_client.exe") THEN PRINT "Cannot find file: messenger_client.exe. Ending...": END
DIM host_msg AS STRING, client_msg AS STRING
DO
IF initiate = 0 THEN ' This only needs to be performed once, to open the client window.
DO UNTIL x ' Stay in loop until window determines if it is the host or client window.
x = _OPENCLIENT("TCP/IP:1234:localhost") ' Used to establish a TCP/IP routine.
IF x = 0 THEN
x = _OPENHOST("TCP/IP:1234") ' Note the host and clinet must have the same 1234 I.D. number.
a$ = "Opening as host." ' x channel is now open and this window becomes the host.
ELSE
a$ = "Opening as client." ' Should not go here for this demo.
END IF
PRINT a$
LOOP
SHELL _HIDE _DONTWAIT "messenger_client.exe" ' Open the client window.
initiate = -1 ' Switches this block statement off for all subsequent loops.
END IF
IF z = 0 THEN ' Initiates an open channel number when zero.
DO
z = _OPENCONNECTION(x) ' Checks if host is available to transfer data.
LOOP UNTIL z
PRINT "Connection established."
_DELAY 1
LOCATE 2: PRINT SPACE$(_WIDTH * 2) ' Remove these lines.
LOCATE 3, 1
GOSUB focus ' Sends focus back to host window.
END IF
' Okay, time to input something on the host that will be communicated to the client.
LINE INPUT "Message to client: "; host_msg: PRINT
PUT #z, , host_msg ' Input is now entered into TCP/IP routine.
DO
GET #z, , client_msg
LOOP UNTIL LEN(client_msg) ' Exits loop when a return msg is received.
PRINT "Message from client: "; client_msg: PRINT
host_msg = "": PUT #z, , host_msg$ ' Now put our client value back into the routine. Failure to do so would result in the client not waiting in the GET #x DO/LOOP.
_KEYCLEAR ' Prevents typing before ready.
GOSUB focus
LOOP
focus:
DO UNTIL hwnd%&
_LIMIT 10
hwnd%& = FindWindowA(0, title$)
LOOP
FGwin%& = GetForegroundWindow%& 'get current process in focus.
_DELAY .1
IF FGwin%& <> hwnd%& THEN
y& = ShowWindow&(hwnd%&, 0)
y& = ShowWindow&(hwnd%&, 2)
y& = ShowWindow&(hwnd%&, 9)
DO
_LIMIT 10
FGwin%& = GetForegroundWindow%&
LOOP UNTIL FGwin%& = hwnd%&
END IF
RETURN
This client app must be saved as: messenger_client.exe Run the first app after this app is saved.
Code: (Select All) DECLARE DYNAMIC LIBRARY "user32"
FUNCTION FindWindowA%& (BYVAL ClassName AS _OFFSET, WindowName$) 'handle by title
FUNCTION ShowWindow& (BYVAL hwnd AS _OFFSET, BYVAL nCmdShow AS LONG) 'maximize process
FUNCTION SetForegroundWindow%& (BYVAL hwnd AS _OFFSET) 'set foreground window process(focus)
FUNCTION GetForegroundWindow%& 'Find currently focused process handle
END DECLARE
title$ = "Messenger_Client"
_TITLE (title$)
_DELAY .1
DIM host_msg AS STRING, client_msg AS STRING
_SCREENMOVE 600, 0 ' Set up this client window next to your host window.
WIDTH 50, 25
x = _OPENCLIENT("TCP/IP:1234:localhost") ' Used to establish a TCP/IP routine.
PRINT "Opened as client.": PRINT
DO UNTIL x = 0 ' Prevents running if this app is opened without using host.
DO
_LIMIT 30
GET #x, , host_msg ' Waits until it receives message sent from the host.
LOOP UNTIL LEN(host_msg)
PRINT "Message from host: "; host_msg
PRINT
_KEYCLEAR ' Prevents typing before ready.
GOSUB focus
LINE INPUT "Message to host: "; client_msg: PRINT
PUT #x, , client_msg
LOOP
END
focus:
DO UNTIL hwnd%&
_LIMIT 10
hwnd%& = FindWindowA(0, title$)
LOOP
FGwin%& = GetForegroundWindow%& 'get current process in focus.
_DELAY .1
IF FGwin%& <> hwnd%& THEN
y& = ShowWindow&(hwnd%&, 0)
y& = ShowWindow&(hwnd%&, 2)
y& = ShowWindow&(hwnd%&, 9)
DO
_LIMIT 10
FGwin%& = GetForegroundWindow%&
LOOP UNTIL FGwin%& = hwnd%&
END IF
RETURN
PIPING:
In Windows, _CLIPBOARD can be used with SHELL to extract the directory contents. This method avoids then need to make and read temp file, which would be: SHELL _HIDE "dir /b *.bas>temp.tmp"
Windows Piping Example:
Code: (Select All) $CONSOLE:ONLY
SHELL _HIDE "dir /b *.bas | clip"
PRINT _CLIPBOARD$
For Windows users, _CLIPBOARD can also be used with Win32API SENDKEYS, which allows you to use your program to copy text from other apps, instead of manually doing a copy to the clipboard. See my Sam-Clip thread for more info: https://staging.qb64phoenix.com/showthread.php?tid=1042&highlight=sam-clip
Pete
RE: Day 025: _CLIPBOARD$ - Pete - 12-11-2022
Find your IP Address for Windows systems. (See the caution at the top of the code). English and German, apparently supported.
Code: (Select All) '' CAUTION - THE SHELL STATEMENT WILL OVERWRITE ANY PREEXISTING FILE YOU HAVE IN YOUR LOCAL DIRECTORY NAMED "tmp.tmp".
' Change tmp.tmp to something else if that's a problem.
SHELL _HIDE "ipconfig>tmp.tmp"
OPEN "tmp.tmp" FOR BINARY AS #1
a$ = SPACE$(LOF(1))
GET #1, , a$
CLOSE #1
DO
j = INSTR(LCASE$(a$), search$)
IPAddy$ = MID$(a$, j)
IPAddy$ = MID$(IPAddy$, 1, INSTR(IPAddy$, CHR$(13)) - 1)
IPAddy$ = _TRIM$(MID$(IPAddy$, INSTR(IPAddy$, ":") + 1))
IF j AND LEN(search$) > 0 THEN
PRINT "Your IP Address is: "; IPAddy$
EXIT DO
ELSE
SELECT CASE cnt
CASE 0
search$ = "ipv4 address"
CASE 1
search$ = "ip address"
CASE 2
search$ = "ipv4 "
CASE 3
search$ = "ipv4-address"
CASE 4
search$ = "ip-address"
CASE 5
search$ = "ipv4-"
CASE ELSE
PRINT "Sorry, can't find IP addy. Opening Notepad so you can find it..."
SHELL _DONTWAIT "Notepad tmp.tmp"
EXIT DO
END SELECT
END IF
cnt = cnt + 1
LOOP
Pete
RE: Day 025: _CLIPBOARD$ - mnrvovrfc - 12-11-2022
Must have the web browser opened before running any program created with QB64(PE) which uses the clipboard. Otherwise that clipboard will be wiped out of all text. Also in some conditions the browser stubbornly refuses to allow text pasted, not even from its own address bar or from a text field like the one I used to type this message.
I was able to paste into said text field, but it sensibly rejects an attempt to paste a password. However it's irritating when somebody chose an username that could be misspelled easily, has it written down correctly somewhere and cannot paste that.
It successfully frustrated a quick utility I called "Spanish write", which didn't require changing keyboard setups, it was only "o/" for ó, "n-" or "n/" for ñ, "u-" for ü and more.
There is also "_CLIPBOARDIMAGE" which doesn't work away from Windows.
RE: Day 025: _CLIPBOARD$ - Pete - 12-11-2022
Eeeww I hatez those sitez that don't letz ya paste in your pass-ee-word.
Windows only.
Code: (Select All) _KEYCLEAR
a$ = _CLIPBOARD$
' Click in the password field within 5 seconds...
SLEEP 5
_SCREENPRINT a$ ' Prints instead of pastes.
Pete
|