Announcement

Collapse
No announcement yet.

Sharing variables between programs?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Sharing variables between programs?

    I have a PB/CC program that I end it's process when a file exists as shown below.

    DO
    ...
    IF Exist("file.txt") THEN EXIT LOOP

    LOOP

    I dislike having to create "file.txt" to accomplish the termination for various reasons: I must create/delete the file or I may want to run the program in an environment that has no writeable disk.

    Is there anyway to accomplish this with some kind variable that exists in memory? Am I correct in my thinking that most variables are confined to the executable in which they reside? One method that succeeded for me was to write a certain string to the Windows clipboard. The looping program reads the clipboard then exits if the string is found. This proves to me that what I want can be accomplished totally in memory without writing to a drive, but my clipboard is jeophardized for other applications. Can I do this with REGISTER variables (i.e. #REGISTER statement) or an ENVIRONMENT variable? I want to use the least amount of code as possible.

  • #2
    Win32: Memory Mapped Files/Control User Count April 26, 2001
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Above is but one method of performing Interprocess Communication..

      However, a better question might be ....

      >IF Exist("file.txt") THEN EXIT LOOP

      .... "What causes 'file.txt' to exist?"

      Window provides numerous synchronization objects which are designed to transfer the load of "waiting for something to happen" to the operating system and to tell your program it is time to "do something."

      MCM
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        My goal is to stop/kill the exe, MyProg.exe, which contains the DO LOOP. I simply create a zero-byte file.txt file using batch commands, then MyProg.exe terminates when file.txt is discovered. I used Mark Russinovich's process utility to kill MyProg.exe but this like blunt trauma to the head. Your suggestion of using mutex's was helpful but I keep thinking something simpler exists like a single-bit register tucked away somewhere that both programs can share. I'm not ruling out assembler code. Is there nothing like this?

        Comment


        • #5
          If they are both your programs (which we would call "cooperating programs") then the solution I would recommend would be a user-named Windows Event.

          Demo here:
          Terminate Worker Threads Using Windows Events (and Using Thread Local Storage) Demo Dec 23 2005

          That demo uses a named event; different functions obtain handles to that event as needed; as long as the "Cancel" event is non-signalled, the processing loop continues. When you signal the cancel event (from any program), the loop terminates.

          But different processes (programs) can obtain handles to a named event, too.

          Study up on Windows synchronization objects and you'll see what I am thinking.

          You could also create a (non-visible) Window in the program to be stopped, and send a message to it telling it to end.

          You have many options short of 'blunt force trauma' as long as you are talking about cooperating applications.

          MCM
          Last edited by Michael Mattias; 14 Aug 2008, 04:41 PM.
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Windows' events, memory mapped files and other 'system' objects (eg atoms, mutexes, semaphores, etc. ) really are 'shared variables' when you think about it.

            At least you can use 'em that way.

            If you want to stick with creating 'file.txt' in a batch file to signal your program that's it's time to quit, you can use a directory change notification object as demo'd here:

            Win32(SDK): Internet Cookie Monitor April 25, 2001

            If 'file.txt' is the new file in the monitored folder (it wasn't there last time the event triggered) it's showtime.
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Originally posted by Alan Clarke View Post
              ...I keep thinking something simpler exists like a single-bit register tucked away somewhere that both programs can share. I'm not ruling out assembler code. Is there nothing like this?
              There must be something on a port somewhere which you could use but that approach would be deprecated by Windows enthusiasts, and in any event (pun inteneded) you would have to write code to do it, so why not "just" write code which does what your utility program does. Which is to identify the process by name* and send it a polite but firm request to terminate. There are at least two code examples in the forums. Here's a recent thread - note especially posts #9 and #11*.
              Last edited by Chris Holbrook; 16 Aug 2008, 09:17 AM. Reason: left the link out. Sorry!

              Comment


              • #8
                One of the founding design principles of 32-bit Windows was the isolation of memory, to make it IMPOSSIBLE for one process do something silly which would affect other processes. Anyone who ever worked on Win/16 (= Windows 3.x) understands why that feature was important. (Heck, USERS know... remember how if one Win/3 program crashed, it generally took a couple of other programs with it?)

                However, the designers recognized the need for interprocess communication, and included a ton of ways to accomplish that ... but only when it was done intentionally.

                So I wouldn't even bother looking for a 'back door' because even if you find one, there's a good chance it would be closed in a 'security update' since such a back door is virtually the definition of a security flaw.

                Instead, learn and use the interprocess communications facilities provided by Windows/32.

                Or, contact my office for a quotation.
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  Private clipboard

                  Private clipboard (for running on same machine, not network)
                  Clipboard Interprocess Communications demo, an IPC Mechanism. This program will run 4 instances of itself to show some clipboard interprocess functions. Have fun... Pierre #COMPILE EXE '#Win 9.07# #DIM ALL #INCLUDE "Win32Api.inc" %Listbox = 101 %ButtonSend = 102 %ButtonClear = 103 %ButtonExit = 104 GLOBAL
                  The world is full of apathy, but who cares?

                  Comment


                  • #10
                    You've got to love this routine

                    Original code by: Pierre Bellisle

                    Orignal source automatically opens 4 programs at http://www.powerbasic.com/support/pb...ad.php?t=23335

                    Most of the code is for the LISTBOX and the compiled program should be run several times to see results.
                    Sprinkled in a few remarks where files could be sent.

                    Thanks again, Pierre!

                    Code:
                    #COMPILE EXE "Private.exe"
                    #DIM ALL
                    #INCLUDE "Win32Api.Inc"
                    %Listbox1    = 100
                    %SendButton  = 101
                    %ClearButton = 102
                    %ExitButton  = 103
                    GLOBAL AppTitle    AS STRING
                    GLOBAL hDlg        AS DWORD
                    GLOBAL ClipNoRead  AS LONG
                    GLOBAL gWorthlessCounter AS DWORD
                    '_________________________________________________________________________________________________
                    FUNCTION WriteToClipBoard (BYVAL Txt AS STRING, MyFormat AS LONG) AS LONG
                     LOCAL lpMem AS ASCIIZ PTR
                     LOCAL hMem  AS DWORD
                     ClipNoRead = 1 'No need to read what we send
                     hMem       = GlobalAlloc(%GHND, LEN(Txt) + 1)
                     lpMem      = GlobalLock(hMem)
                     @lpMem     = Txt
                     GlobalUnlock hMem
                     OpenClipboard 0
                     'EmptyClipboard
                     SetClipboardData MyFormat, hMem
                     CloseClipboard
                     FUNCTION = 1
                    END FUNCTION
                    '_________________________________________________________________________________________________
                    CALLBACK FUNCTION DlgProc
                     STATIC szMyFormat      AS ASCIIZ * 20
                     STATIC MyFormat        AS LONG
                     LOCAL  ClipDataPtr     AS ASCIIZ PTR
                     LOCAL  ClipData        AS STRING
                     STATIC hClipboardChain AS LONG
                     LOCAL  Retval          AS LONG
                     LOCAL OneLost          AS STRING
                     SELECT CASE CBMSG
                       CASE %WM_INITDIALOG
                         szMyFormat      = "MyOwnClipboardFormat"
                         hClipboardChain = SetClipboardViewer(CBHNDL) 'Add ourselve to clipboard chain
                         MyFormat        = RegisterClipboardFormat(szMyFormat)
                         IF MyFormat = 0 THEN MSGBOX "Error registering clipboard format"
                       CASE %WM_DESTROY
                         WriteToClipBoard AppTitle & " just past the way!!!" , MyFormat 'Tell everybody that we are ending
                         ChangeClipboardChain CBHNDL, hClipboardChain 'Remove ourselve from clipboard chain
                         PostQuitMessage 0
                       CASE %WM_DRAWCLIPBOARD   'Here we know the clipboard content is beeing updated
                         IF IsClipboardFormatAvailable(MyFormat) THEN
                           IF ClipNoRead THEN
                             ClipNoRead = 0
                           ELSE
                             OpenClipboard 0
                             ClipDataPtr = GetClipboardData(MyFormat)
                             ClipData = @ClipDataPtr
                             CloseClipboard
                             'MSGBOX "Bytes received" + STR$(LEN(ClipData))
                             CONTROL SEND hDlg, %Listbox1, %LB_INSERTSTRING, -1, STRPTR(ClipData)
                             CONTROL SEND hDlg, %Listbox1, %LB_GETCOUNT, 0, 0 TO Retval
                             CONTROL SEND hDlg, %Listbox1, %LB_SETCURSEL, Retval - 1, 0
                           END IF
                         END IF
                         SendMessage hClipboardChain, CBMSG, CBWPARAM, CBLPARAM 'Forward data to next clipboard chain
                      CASE %WM_CHANGECBCHAIN
                        OneLost = "Some previous clipboard app just ended"
                        CONTROL SEND hDlg, %Listbox1, %LB_INSERTSTRING, -1, STRPTR(OneLost)
                        CONTROL SEND hDlg, %Listbox1, %LB_GETCOUNT, 0, 0 TO Retval
                        CONTROL SEND hDlg, %Listbox1, %LB_SETCURSEL, Retval - 1, 0
                        IF CBWPARAM = hClipboardChain THEN
                          hClipboardChain = CBLPARAM
                        ELSE
                          IF hClipboardChain <> 0 THEN SendMessage hClipboardChain, CBMSG, CBWPARAM, CBLPARAM
                        END IF
                      CASE %WM_COMMAND
                          SELECT CASE LOWRD(CBWPARAM)
                     
                            CASE %SendButton
                              'Send data here
                              IF CBCTLMSG = %BN_CLICKED THEN
                                 LOCAL s AS STRING, h AS LONG  'I know, shouldn't go here
                                 INCR gWorthLessCounter
                                 s = "#" + FORMAT$(gWorthLessCounter) + " from " + AppTitle + " at " + TIME$
                     
                                 '---  send file ---
                                 'h = freefile
                                 'open s for binary As #h
                                 'Get$ #h, lof(h), s
                                 'Close #h
                     
                                 WriteToClipBoard s, MyFormat
                              END IF
                     
                            CASE %ClearButton
                              IF CBCTLMSG = %BN_CLICKED THEN LISTBOX RESET hDlg, %Listbox1
                     
                            CASE %ExitButton
                              IF CBCTLMSG = %BN_CLICKED THEN DIALOG END CBHNDL, 0
                          END SELECT
                     END SELECT
                    END FUNCTION
                    '_________________________________________________________________________________________________
                    FUNCTION PBMAIN
                     AppTitle = "user" + STR$(INT(TIMER))
                     DIALOG NEW 0, AppTitle, 10, 10, 160, 210, _
                      %WS_POPUP OR %DS_MODALFRAME OR %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, _
                      0 TO hDlg
                      CONTROL ADD LISTBOX, hdlg, %Listbox1, , 5, 5, 150, 190, _
                      %WS_CHILD OR %WS_VISIBLE  OR %WS_VSCROLL OR %WS_TABSTOP, 0
                      CONTROL ADD BUTTON, hDlg, %SendButton, "SEND", 20, 192, 35, 15, _
                        %WS_CHILD OR %WS_VISIBLE OR %BS_PUSHBUTTON OR %WS_TABSTOP
                      CONTROL ADD BUTTON, hdlg, %ClearButton, "CLEAR", 60, 192, 35, 15, _
                        %WS_CHILD OR %WS_VISIBLE OR %BS_PUSHBUTTON OR %WS_TABSTOP
                      CONTROL ADD BUTTON, hDlg, %ExitButton, "EXIT", 100, 192, 35, 15, _
                        %WS_CHILD OR %WS_VISIBLE OR %BS_PUSHBUTTON OR %WS_TABSTOP
                      DIALOG SHOW MODAL hDlg CALL DlgProc
                    END FUNCTION
                    Last edited by Mike Doty; 16 Aug 2008, 01:44 PM. Reason: Lost formatting
                    The world is full of apathy, but who cares?

                    Comment


                    • #11
                      Just noticed this was PB/CC so I'll post something for that in a few minutes.
                      The world is full of apathy, but who cares?

                      Comment


                      • #12
                        Uses CreateWindow with PB/CC

                        Code:
                        #COMPILE EXE
                        #DIM ALL  'Private Clipboard with PBCC
                        %CCWIN=1  'CreateWindow is needed
                        #INCLUDE "WIN32API.INC"
                        DECLARE FUNCTION WriteToClipBoard (BYVAL Txt AS STRING, MyFormat AS LONG) AS LONG
                        DECLARE SUB ShutDown(BYVAL dwEvent AS DWORD)
                        %ClipBoardSend            = 101
                        %ClipBoardInit            = 102
                        GLOBAL hwndTCP  AS LONG
                        GLOBAL hThread  AS LONG
                        GLOBAL hDlg               AS DWORD
                        GLOBAL ClipNoRead         AS LONG
                        GLOBAL gWorthlessCounter  AS DWORD
                        GLOBAL gMyFormat          AS LONG
                        GLOBAL gsSendThis         AS STRING
                        GLOBAL gsYourName         AS STRING
                        'Original code is by Pierre Bellisle, modified for use with PB/CC Mike Doty 8/19/08
                        FUNCTION PBMAIN
                          COLOR 7,1,1
                          CLS
                          ? "PB/CC Private Clipboard"
                          LINE INPUT "Your name? ";gsYourName
                          IF LEN(gsYourName) = 0 THEN ? "Bye":SLEEP 1000:EXIT FUNCTION
                         
                          CONSOLE NAME gsYourName
                          CLS
                          SetConsoleCtrlHandler CODEPTR(ShutDown), 1  'user might click X to terminate
                          THREAD CREATE WindowThread(%NULL) TO hThread
                          SLEEP 250
                         
                          SendMessage hwndTCP,%WM_COMMAND,%ClipBoardInit,0
                          gsSendThis =  gsYourName + " is now here"
                          SendMessage hwndTCP, %WM_COMMAND,%ClipboardSend,0
                          DO
                            LINE INPUT "<Me> " gsSendThis                       'data to send to everybody
                            LOCATE CURSORY-1,1: ? "<Me> " + gsSendThis
                            gsSendThis = "<" + gsYourName + "> " + gsSendThis
                            SendMessage hwndTCP,%WM_COMMAND,%ClipBoardSend,0
                         
                           'click X to end programp
                          LOOP
                         
                          '-----------------------------------------------------------------------
                        EXIT FUNCTION 'line below will never execute.  This line isn't needed
                          '-----------------------------------------------------------------------
                          SetConsoleCtrlHandler CODEPTR(ShutDown), 0 'uninstall user might click X
                        END FUNCTION
                        FUNCTION WriteToClipBoard (BYVAL Txt AS STRING, MyFormat AS LONG) AS LONG
                        LOCAL lpMem AS ASCIIZ PTR
                        LOCAL hMem  AS DWORD
                        ClipNoRead = 1 'No need to read what we send
                        hMem       = GlobalAlloc(%GHND, LEN(Txt) + 1)
                        lpMem      = GlobalLock(hMem)
                        @lpMem     = Txt
                        GlobalUnlock hMem
                        OpenClipboard 0
                        'EmptyClipboard
                        SetClipboardData MyFormat, hMem
                        CloseClipboard
                        FUNCTION = 1
                        END FUNCTION
                        '_________________________________________________________________________________________________
                        FUNCTION HelloWindows (BYVAL hWnd AS LONG, BYVAL wMsg AS LONG, _
                                          BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
                        STATIC szMyFormat      AS ASCIIZ * 21
                        STATIC MyFormat        AS LONG              'made a global
                        LOCAL  ClipDataPtr     AS ASCIIZ PTR
                        LOCAL  ClipData        AS STRING
                        STATIC hClipboardChain AS LONG
                        LOCAL  Retval          AS LONG
                        LOCAL OneLost          AS STRING
                        SELECT CASE wMsg
                          CASE %WM_COMMAND
                            SELECT CASE LOWRD(WParam)
                              CASE %ClipboardSend
                                WriteToClipBoard gsSendThis, gMyFormat
                              CASE  %ClipBoardInit
                                szMyFormat = "MyOwnClipboardFormat"
                                hClipboardChain = SetClipboardViewer(hWnd)    'PBCC Add ourselve to clipboard chain
                                gMyFormat        = RegisterClipboardFormat(szMyFormat)     'was MyFormat made a global
                                IF gMyFormat = 0 THEN ? "Clipboard not registered"
                            END SELECT
                          CASE %WM_DESTROY
                             WriteToClipBoard gsYourName + " left clipboard" , gMyFormat 'Tell everybody that we are ending
                             'ChangeClipboardChain CBHNDL, hClipboardChain 'Remove ourselve from clipboard chain
                             ChangeClipboardChain hWnd, hClipboardChain 'PB/CC Remove ourselve from clipboard chain
                             ChangeClipboardChain hWnd, hClipboardChain
                             PostQuitMessage 0
                          CASE %WM_DRAWCLIPBOARD   'Here we know the clipboard content is being updated
                             IF IsClipboardFormatAvailable(gMyFormat) THEN
                               IF ClipNoRead THEN
                                 ClipNoRead = 0
                               ELSE                                        'read the clipboard
                                 OpenClipboard 0
                                 ClipDataPtr = GetClipboardData(gMyFormat)
                                 ClipData = @ClipDataPtr
                                 CloseClipboard
                                 LOCATE ,1                                 'display inbound message
                                 ? ClipData                                 'display contents
                               END IF
                             END IF
                             'SendMessage hClipboardChain, CBMSG, CBWPARAM, CBLPARAM 'Forward data to next clipboard chain
                             SendMessage hClipboardChain, wMsg,WParam, LParam
                          CASE %WM_CHANGECBCHAIN
                              OneLost = "Somebody exited"
                              IF wParam   = hClipBoardChain THEN
                                hClipBoardChain = lParam
                              ELSE
                                IF hClipBoardChain <> 0 THEN SendMessage hClipBoardChain, wMsg, WParam, LParam
                              END IF
                          END SELECT
                         FUNCTION = DefWindowProc(hWnd, wMsg, wParam, lParam)   'don't forget to add this
                        END FUNCTION
                        FUNCTION MakeWindow () AS LONG
                        LOCAL wce         AS WndClassEx
                        LOCAL szClassName AS ASCIIZ * 64
                        LOCAL hWnd        AS LONG
                        LOCAL hInst       AS LONG
                        STATIC registered AS LONG
                        hInst = GetModuleHandle(BYVAL %NULL)
                        IF ISFALSE registered THEN
                          szClassName          = "PRIVATECLIPBOARDTEST"
                          wce.cbSize           = SIZEOF(wce)
                          wce.style            = %CS_HREDRAW OR %CS_VREDRAW
                          wce.lpfnWndProc      = CODEPTR(HelloWindows)
                          wce.cbClsExtra       = 0
                          wce.cbWndExtra       = 0
                          wce.hInstance        = hInst
                          wce.hIcon            = %NULL
                          wce.hCursor          = %NULL
                          wce.hbrBackground    = %NULL
                          wce.lpszMenuName     = %NULL
                          wce.lpszClassName    = VARPTR(szClassName)
                          wce.hIconSm          = %NULL
                          RegisterClassEx wce
                          registered = %TRUE
                        END IF
                        hWnd = CreateWindow(szClassName, "Cool Beans Private Clipboard",  _
                                            %WS_OVERLAPPEDWINDOW, 5, 5, 10, 10, %NULL,%NULL, hInst, BYVAL %NULL)
                            IF ISFALSE hWnd THEN
                                hWnd = GetLastError
                            ELSE
                                ShowWindow hWnd, %SW_HIDE
                                UpdateWindow hWnd
                            END IF
                            FUNCTION = hWnd
                        END FUNCTION
                        FUNCTION WindowThread (BYVAL nIgnored AS LONG) AS LONG
                        LOCAL Msg AS tagMsg
                        hwndTCP = MakeWindow
                        DO WHILE IsWindow(hwndTCP) AND GetMessage(Msg, %NULL, 0, 0)
                          TranslateMessage Msg
                          DispatchMessage Msg
                        LOOP
                        END FUNCTION
                         
                        SUB ShutDown(BYVAL dwEvent AS DWORD)
                          'in case user clicks X to terminate
                          'SELECT CASE dwEvent
                            'CASE %CTRL_C_EVENT
                            '  'Do whatever needed...
                            '  PRINT "Ctrl-C pressed"
                            'CASE %CTRL_BREAK_EVENT
                            '  'Do whatever needed...
                            '  PRINT "Ctrl-Break pressed"
                            'CASE %CTRL_CLOSE_EVENT
                            '  'Do whatever needed...
                            '  PRINT "[x] Close pressed"
                            'CASE %CTRL_LOGOFF_EVENT
                            '  PRINT "User logging off or restarting"
                            'CASE %CTRL_SHUTDOWN_EVENT
                            ' PRINT "System shutting down"
                          'END SELECT
                          SendMessage hwndTCP, %WM_CLOSE,0,0
                        END SUB
                        The world is full of apathy, but who cares?

                        Comment


                        • #13
                          Thanks so much for the varied responses. All were helpful in bringing me to the realizaton that a deep chasm exists between each application's memory space. I was hoping for a one-liner. But as you said, if it was that easy, every Tom, Dick & Harry would be corrupting someone else's address space.

                          Comment


                          • #14
                            One-liner, okay: lResult& = WriteToPrivateClipBoard("Anything")
                            Anthing can be a block of memory, file, etc ...
                            Getting closer?

                            Probably wont work unless the clipboard is left open.



                            Code:
                            'WriteToPrivateClipboard.bas
                            '
                            #INCLUDE "Win32Api.Inc"
                            '
                            FUNCTION WriteToPrivateClipBoard (BYVAL Txt AS STRING) AS LONG
                             
                             STATIC MyFormat&
                             IF MyFormat& = 0 THEN
                               MyFormat& = RegisterClipBoardFormat("MyOwnClipBoardformat") 'case-insensitive
                               'IF MyFormat& = 0 then ? "Error Registering":EXIT FUNCTION
                             END IF
                             LOCAL lpMem AS ASCIIZ PTR
                             LOCAL hMem  AS DWORD
                             LOCAL lResult AS LONG
                             hMem       = GlobalAlloc(%GHND, LEN(Txt) + 1)
                             lpMem      = GlobalLock(hMem)
                             @lpMem     = Txt
                             GlobalUnlock hMem
                             OpenClipboard 0
                             lresult = SetClipboardData(MyFormat, hMem)
                             CloseClipboard
                             FUNCTION = lResult
                            END FUNCTION
                            '
                            FUNCTION PBMAIN
                                lResult& = WriteToPrivateClipBoard("Anything")
                            END FUNCTION
                            Last edited by Mike Doty; 20 Aug 2008, 12:11 PM.
                            The world is full of apathy, but who cares?

                            Comment


                            • #15
                              >Anthing can be a block of memory, file, etc

                              No, it must be a string, as specified in function header.

                              >Probably wont work unless the clipboard is left open.

                              Probably will.
                              Michael Mattias
                              Tal Systems (retired)
                              Port Washington WI USA
                              [email protected]
                              http://www.talsystems.com

                              Comment


                              • #16
                                I have it work being closed.
                                The world is full of apathy, but who cares?

                                Comment


                                • #17
                                  It was here all the time:
                                  Note: The length of the format should be 1-byte longer (for the NUL.)




                                  Anyone know how to put binary data into a private clipboard?
                                  The world is full of apathy, but who cares?

                                  Comment


                                  • #18
                                    Eventually you can always UUE or MIME64 encode them (you pay it with some overhead, say about 30%).
                                    It depends on what you are after, off course.

                                    Bye!
                                    -- The universe tends toward maximum irony. Don't push it.

                                    File Extension Seeker - Metasearch engine for file extensions / file types
                                    Online TrID file identifier | TrIDLib - Identify thousands of file formats

                                    Comment


                                    • #19
                                      > Eventually you can always UUE or MIME64 encode them

                                      ???

                                      Why bother? It's a private clipboard format, a pedestrian block of memory.
                                      Michael Mattias
                                      Tal Systems (retired)
                                      Port Washington WI USA
                                      [email protected]
                                      http://www.talsystems.com

                                      Comment


                                      • #20
                                        "eventually" and "it depends" should give enough space options...
                                        -- The universe tends toward maximum irony. Don't push it.

                                        File Extension Seeker - Metasearch engine for file extensions / file types
                                        Online TrID file identifier | TrIDLib - Identify thousands of file formats

                                        Comment

                                        Working...
                                        X