Day 025: _CLIPBOARD$
#1
_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/showthre...t=sam-clip

Pete
Reply


Messages In This Thread
Day 025: _CLIPBOARD$ - by Pete - 12-06-2022, 06:34 AM
RE: Day 025: _CLIPBOARD$ - by Pete - 12-11-2022, 05:38 PM
RE: Day 025: _CLIPBOARD$ - by mnrvovrfc - 12-11-2022, 06:45 PM
RE: Day 025: _CLIPBOARD$ - by Pete - 12-11-2022, 07:00 PM



Users browsing this thread: 3 Guest(s)