Announcement

Collapse
No announcement yet.

Limitation with COMMAND$ and Windows "Open With..." command

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

  • Limitation with COMMAND$ and Windows "Open With..." command

    Aloha everyone. Thanks for all the help lately. I discovered a limitation, and I'm already afraid it's a Bill Gates oversight, not Bob Zale's. But I thought I'd ask.

    Suppose I create an application in PB/CC that processes BMP files. Normally, when you double-click a BMP file, it will open with some kind of picture viewer (you can tell Windows to 'associate' certain file types with certain applications). But you can also override the association by RIGHT-clicking the BMP file, and choosing "Open With..." (in Windows XP anyway). From there, you can select a compiled PB/CC program. The filename can be picked up with COMMAND$.

    It works fine. The issue? Selecting/highlighting more than one BMP file, and trying to use "Open With..." to open them all with the PB/CC program. In that case, only one instance of the PB/CC program will launch and COMMAND$ will only show one filename, not all of them.

    Ideally, I'd like to easily open multiple files with a single instance of a PB/CC program and have all filenames show up in COMMAND$. Because I'll be sharing the application with others, I can't rely on re-associating BMP files (other people will likely be reluctant to re-associate a common file to open with my application).

    Ideas?
    Christopher P. Becker
    signal engineer in the defense industry
    Abu Dhabi, United Arab Emirates

  • #2
    I'm not sure Windows will even pass more than one file name.

    I selected three BMP files, and "open with" Paint. It only opened the first one.

    Not that Paint is necessarily one of your "high-end" applications but it may be indicative of something in "open with"

    I thought of something else I could try - in the AM. I'm tired tonite and would just screw it up.

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

    Comment


    • #3
      I think this might be a Windows issue.

      Earlier, I used BMP as an example. When I select more than one MP3, as another example, I can send them all to a media player with a right-click. So, I know it's possible -- somehow.

      Also, I note that I can also select more than one file and send it to PKZIP to create a new zip archive that contains all the files. But now that I'm thinking this over, I bet PKZIP (and others) use some kind of "handler" in the right-click menu and the handler passes the command line to the EXE. Just a hunch.

      This probably isn't a PB issue, but I hoped someone else had solved it.
      Christopher P. Becker
      signal engineer in the defense industry
      Abu Dhabi, United Arab Emirates

      Comment


      • #4
        Hi Christopher,

        AFAIK the only way to get Windows to start only a single instance of your app and pass all the filenames to it is to use a ContextMenuHandler, which AFAIK can only be done with COM - meaning it's probably not easy from PB (Jose Roca notwithstanding).

        However, it's relatively simple to get Windows to start one instance of your app for each selected file, in which case you can then use interprocess communication to get each subsequent instance to pass the filename to the first instance and close - perhaps using WM_COPYDATA etc. Not exactly what you want, but much the same effect.

        If you'd like to try this, firstly, in the registry, go to HKEY_CLASSES_ROOT\*\shell - if it's not there, just create it. Now add another key beneath that and name it anything you like. Make its "(Default)" (REG_SZ) value whatever text you want to appear in the context menu (e.g. "Open in my PBCC Prog"). Then add a "command" key beneath that, and set its "(Default)" (REG_SZ) value to the program you want to run - e.g. C:\PBCC40\MyApp.exe "%1"

        Now, when you right-click on a file, you should see your text in the top level context menu (not in Open With...). It should have the same effect as your Open With item if you select a single file. But if you select multiple files, you should get multiple instances of your app, with each one getting its own filename passed to it.

        As for the WM_COPYDATA, I'm sure someone here can help with that too, but it won't be me - at least not tonight!

        Hope that's of some help anyway.

        Regards,

        Pete.

        Comment


        • #5
          Explorer will pass the address of the ASCIIZ command line of selected file names to an app/shortcut in Send To. (Check the GetCommandLine function.) Names containing spaces are enclosed in $DQs.

          The maximum length of the string can pose a restriction (two thousand something chars) because the path+filenames can add up fast with a long path. If the command line is too long, Windows steps in and displays an error (which is misleadingly worded, as I recall).

          Here's something yanked from a utility I wrote for my own use. (ErrorBox is missing here. You can guess at the code pretty easily.)

          Code:
          %ENTRYSIZE              = %MAX_PATH
          %MAX_ENTRIES            = (%ENTRYSIZE * 8)
          
          '====================
          FUNCTION GetNames(T$) AS LONG
          REGISTER i&, idx&
          LOCAL c AS BYTE, pCommandLine AS DWORD
          LOCAL t1$, names$(), pByte AS BYTE PTR
          redim names$(%MAX_ENTRIES)
          
          pCommandLine = GetCommandLine
          if pCommandLine = 0 then
             ErrorBox "Cannot retrieve command line arguments"
             function = 0 : exit function
          end if
          
          pByte = pCommandLine
          
          idx = -1
          do
             c = @pByte : incr pByte
          
             if c = 0 then 
                if len(t1) then 
                   incr idx : names$(idx) = t1
                end if
                exit loop
             end if
          
             if c = 34 then    '$DQ
                t1 = $DQ
                do
                   c = @pByte : incr pByte
                   t1 = t1 + chr$(c)
                loop until c = 34
                incr idx : names$(idx) = t1 : t1 = ""
             elseif c = 32 then
                if len(t1) then
                   incr idx : names$(idx) = t1 : t1 = ""
                end if
             else
                t1 = t1 + chr$(c)
             end if
             if idx >= %MAX_ENTRIES then
                ErrorBox "Maximum entries (" + ltrim$(str$(%MAX_ENTRIES)) + ") reached."
                exit loop
             end if
          loop
          
          for i = 1 to idx
             T$ = T$ + names$(i) + $CRLF
          next i
          END FUNCTION
          Last edited by Greg Turgeon; 30 Jul 2008, 02:40 AM.

          Comment


          • #6
            If I did this right, 'open with' does not pass more than one file, not even as a null-delimited array.

            To Test:
            Compile Program.
            From Explorer, select any number of files, right click, "open with"
            Browse for your EXE and select that.

            Code:
            ' GetManyFiles.bas
            ' Test if Windows will pass many files on CommandLine from Explorer
            ' PB/Win 8.03 
            ' Nope it don't.
            ' 7-30.08
            
            #COMPILE EXE
            #DIM ALL
            
            '#INCLUDE "Win32API.INC"
            ' all we are using from here is IsBadReadPtr function and %TRUE
            
            %TRUE = 1
            DECLARE FUNCTION IsBadReadPtr LIB "KERNEL32.DLL" ALIAS "IsBadReadPtr" (BYVAL lp AS DWORD, BYVAL ucb AS DWORD) AS LONG
            
            FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                              BYVAL hPrevInstance AS LONG, _
                              BYVAL lpCmdLine     AS ASCIIZ PTR, _
                              BYVAL iCmdShow      AS LONG) AS LONG
            
            
             LOCAL   dwAddr AS DWORD, cb AS LONG, iRet AS LONG
             LOCAL   s AS STRING, W AS STRING
             LOCAL   nValidChar  AS LONG
             LOCAL   pb AS BYTE PTR
             LOCAL   bLastWasNull AS LONG
             
             
                     dwAddr = lpCmdLine   ' assign to regular numeric var
                     
                     nValidchar   = 0&    ' not yet, anyway
                     cb = 1&  ' we test a byte at a time 
                    ' -------------------------------------------------------------
                    ' count characters until double-null or we don't own the memory
                    ' -------------------------------------------------------------
                     
                     DO
                         IF ISTRUE IsBadReadPtr (dwAddr, cb) THEN
                             EXIT DO
                         ELSE
                             pb  =  dwAddr
                             IF @pb = 0? THEN
                                 IF bLastWasNull THEN
                                      EXIT DO
                                 ELSE
                                     bLastWasNUll = %TRUE
                                 END IF
                             ELSE   ' not a nul 
                                INCR nValidChar
                             END IF
                             INCR dwAddr   ' next byte of command line 
                         END IF
                     LOOP
                     
                     IF nValidChar > 0 THEN
                         W = PEEK$(lpCmdLine, nValidChar)
                         REPLACE $NUL WITH "|" IN W
                     END IF
                     
                     
                     S = USING$("Valid command line chars #,  '&'", nValidChar, W)
                     
                     MSGBOX S
                     
            END FUNCTION
            Michael Mattias
            Tal Systems Inc. (retired)
            Racine WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              I don't know if this will help with what you want to do, but the "Send To" option from the context menu does pass all selected file names.

              If your requitement is for "Open With" then I have less help in that area than the other posters have offered.
              The boy just ain't right.

              Comment


              • #8
                don't know if this will help with what you want to do, but the "Send To" option from the context menu does pass all selected file names.
                How? As array of null-terminated strings?

                Is anything special required in the 'send to' setup, eg "%1" "%2" "%3"

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

                Comment


                • #9
                  Quite honestly, I have not yet tried this with PB. I know this works in PERL, I have a nice utility that I wrote to add a datestamp to filenames. It's just a loop processing the list of input parameters (which are full-path filenames.)

                  Look at how the other items in the "send to" section of your Documents and Settings are set up. Nothing very complicated.

                  Sorry I'm vague here, not at home at the moment, or I'd try some things myself to give a proper answer.
                  The boy just ain't right.

                  Comment


                  • #10
                    Peter,

                    I tried your suggestion for multiple instances of the program. Very cool. The interprocess communication will be a little more work, but it's clean and I like the Windows interface. In fact, the processing of the files is one-at-a-time anyway, so adding files to a queue from another instance of the program really won't be that difficult.

                    Thanks to everyone who offered advice. Peter's solution is what I was looking for.

                    Christopher
                    Christopher P. Becker
                    signal engineer in the defense industry
                    Abu Dhabi, United Arab Emirates

                    Comment


                    • #11
                      Open with is not Send to. I've offered rough code that works fine with Explorer-selected files (sequential or non-sequential) and Send To.

                      Comment


                      • #12
                        Just as a followup...

                        To use SendTo, you create a shortcut in the SendTo section of your Documents and Settings that points to the exe.

                        From the explorer context menu, your program will now appear in the Send To list. The file names selected will all be placed in the COMMND$, fully qualifed paths, space delimited except for files with an embedded space somwhere which will be quoted with double quote marks.

                        With the caveat that the maximum number of characters passed to COMMAND$ seems to be right around 2047. If more chars than that would be put into the list, Windows (XP and ME anyway) will pop up some phony error message about the files selcted being unavailable or some kind of bumph like that. I had exactly the same problem when I was doing my PERL utility for datestamping, so I'm calling this a windows issue.

                        However, Windows itself seems to be able to handle a lot of files at once in its SendTo programs. Is this possibly a limitation of dos/console apps?
                        Last edited by Joseph Cote; 31 Jul 2008, 07:18 AM. Reason: spelling
                        The boy just ain't right.

                        Comment


                        • #13
                          From the explorer context menu, your program will now appear in the Send To list. The file names selected will all be placed in the COMMND$, fully qualifed paths, space delimited except for files with an embedded space somwhere which will be quoted with double quote marks.
                          Now THAT is nicely phrased and easy to understand.

                          With the caveat that the maximum number of characters passed to COMMAND$ seems to be right around 2047
                          As said above 'open with' is not the same as 'send to', but when I was testing the above code with 'open with,' I ran the program testing ONLY the validity of the pointer without regard to content.

                          In that case I got about 32K characters (31800 to be exact) in the "owned" memory range.

                          No, I don't know what that means in the cosmic scope of things.

                          the maximum number of characters passed to COMMAND$ seems to be right around 2047....
                          I would think COMMAND$ would be returning all characters up to the first $NUL, and any limitation on length would be something in the operating system parameters. But COMMAND$() is proprietary and we really don't know how it works.


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

                          Comment


                          • #14
                            You know what might work in your application?

                            Make it a tray application.

                            The user can then - in Explorer - select multiple files, drag them to the tray icon and drop them... and the application can process the WM_DROPFILES message and get all the files in that single instance.

                            That 'might' work with select, copy, then paste on the tray icon, too. (You might have to add a 'paste' to your tray app's context menu).

                            Worth a thought, anyway....
                            Michael Mattias
                            Tal Systems Inc. (retired)
                            Racine WI USA
                            [email protected]
                            http://www.talsystems.com

                            Comment


                            • #15
                              I guess I'm late on every discussion here. Don't have time to snoop around in the forums too much although I always learn something when I do.

                              FWIW: I tried the following two snippets and found that the SendTo-interface would send a bunch of files through the command-line to the .EXEs - just as if they would have been specified as parametres at the command-line. Very easy. I also found that the limitation of 2047 chars are common for console and windows apps.

                              Here goes:
                              PBWin (tested in 9.0):
                              Code:
                              #COMPILE EXE
                              #DIM ALL
                              
                              FUNCTION WINMAIN (BYVAL hCurInstance  AS LONG, _
                                                BYVAL hPrevInstance AS LONG, _
                                                BYVAL pzCmdLine     AS ASCIIZ PTR, _
                                                BYVAL nCmdShow      AS LONG) EXPORT AS LONG
                              
                                LOCAL sInLine AS STRING
                                LOCAL sTxt AS STRING
                              
                                sInLine = @pzCmdLine
                              
                                sTxt = "Received from the SendTo interface:" + $CRLF
                                sTxt = sTxt + sInLine + $CRLF
                                sTxt = sTxt + "Number of chars received:" + STR$(LEN(sInLine)) + $CRLF
                                MSGBOX sTxt
                              
                              END FUNCTION
                              and PBCC (tested in 5.0)
                              Code:
                              #COMPILE EXE
                              #DIM ALL
                              
                              FUNCTION WINMAIN (BYVAL hCurInstance  AS LONG, _
                                                BYVAL hPrevInstance AS LONG, _
                                                BYVAL pzCmdLine     AS ASCIIZ PTR, _
                                                BYVAL nCmdShow      AS LONG) EXPORT AS LONG
                              
                                LOCAL sInLine AS STRING
                              
                                sInLine = @pzCmdLine
                                
                                PRINT "Received from the SendTo interface:"
                                PRINT sInLine
                                PRINT "Number of chars received:" + STR$(LEN(sInLine))
                                WAITKEY$
                                
                              END FUNCTION
                              I've gotten the habit of using the WINMAIN() style as opposed to PBMAIN() since the WINMAIN method gives me the proper character-casing directly, while PBMAIN will need COMMAND$ that always returns upper case chars.

                              Vidar.

                              Comment


                              • #16
                                >COMMAND$ that always returns upper case chars.

                                :confused2:


                                HELP FILE 8.03:
                                COMMAND$
                                ....
                                Code:
                                FASTSORT.EXE cust.dta cust.new

                                When FASTSORT begins execution, COMMAND$ will hold the string:

                                cust.dta cust.new ....
                                Example NOT Upper case. (not tested by me, either).

                                If statement above is true.....is it a doc error, or compiler error?

                                :confused2:
                                Michael Mattias
                                Tal Systems Inc. (retired)
                                Racine WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                • #17
                                  Hmmm .... looks like I stand corrected. I just tested it with PBWin9 and PBCC5, and both return the same case in COMMAND$ as entered on the command line.

                                  This goes back to my very first attempt to use PB (pre PBDLL6, if my memory serves me), where I struggled with just that. Since QB45 did the same (upper cased everything in COMMAND$), I didn't bother too much, and went for an alternative.
                                  WINMAIN() was the first alternative I tested that worked, and I have never looked back.
                                  Did it change somewhere with newer versions, maybe because compatibility with QB/the old DOS world stopped being a major issue - or was it an error on my hand? I don't know - and I do not have the early version at hand to check it out either. Not that it matters any more - it's ancient history anyway.

                                  Just for the record - and to put things straight -COMMAND$ _DOES NOT_ convert your command line to upper case!

                                  Vidar

                                  Comment


                                  • #18
                                    Did [upper casing of command line returned by COMMAND$ function] change somewhere with newer versions
                                    I doubt it; I'd remember.

                                    Well, what I'd probably actually remember would have been complaining about it if COMMAND$ had not been faithful..

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

                                    Comment

                                    Working...
                                    X