Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

VB - Shell Quotes Madness

Status
Not open for further replies.

snikde

Programmer
Mar 13, 2002
35
US
I have tried to create a shell command to run a command line from an external application. The quoting of the command and the paths to the files it operates on as well as the redirect for the log file is proving.... well exasperating to say the least. Here is the code the VB has in it:
Shell Environ$("COMSPEC") & " /C " & Chr$(34) & "c:\Program Files\EDS\Unigraphics NX 2.0\UGII\ug_convert_part.exe" _
& Chr$(34) & " -in " & InpuDir & "\" & InputFil & " -o " & OutpuDir & " > " & Chr(34) & Chr(34) & "c:\temp\ugconvertlog.txt" & Chr(34)

This yields to the immediate screen the following:

"c:\Program Files\EDS\Unigraphics NX 2.0\UGII\ug_convert_part.exe" -in "j:\Gedkins\Tangent Edges.prt" -o C:\Temp > "c:\temp\ugconvertlog.txt"

This fails to work via VB however if I cut in paste this directly to a cmd window it runs just fine. One last issue I need to enclose the C:\temp path in quotes in case the output directory (path following the -o) has a space in its name like the in put path. The log file directory is hard coded so it won't need to change, I delete the log at the end of the program.

Any ideas why this works on copy and paste and not via the shell cmd in VB.

Guy
 
Guy,

It could be more or less complicated but try;

Shell Environ$("COMSPEC") & " /C " & Chr$(34) & "c:\Program Files\EDS\Unigraphics NX 2.0\UGII\ug_convert_part.exe" _
& Chr$(34) & " -in "& Chr(34) & InpuDir & "\" & InputFil & Chr(34)& " -o " & Chr(34) & OutpuDir & Chr(34) & " > " & Chr(34) & "c:\temp\ugconvertlog.txt" & Chr(34)

Remember that you should always have an even number of Chr$(34) s in such fomulae, think of them as brackets.

Out of interest have you tried just placing a chr$(34) at the very begining and at the very end; ie none within. That would be too easy though..

You can probably avoid all this madness if you used a routine to convert all the longpathnames into shortpathnames. There should be info on such stuff in the forum if not I could post an example.

regards Hugh,
 
Guy,

ShortPath from LongPath here is the API method;

Public Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long

Function ShortPath$(PathName$)

'PathName may be a file or a Folder and must exist

Dim Temp$, Buffer$, length&

Temp$ = PathName$ & Chr$(0)
Buffer$ = Space$(255)
length = GetShortPathName(Temp$, Buffer$, 255)
ShortPath$ = Left$(Buffer$, length)

End Function
'Can be done with the FileSystemObject too!

Take care applying this to the log file because it may NOT exist before the shell.


regards Hugh
 
Man this is frustrating! Still no luck. Here are a couple pieces of info. The VB program I have attempts to read the log file after the Shell is run. The log file is not created so that fails, that tells me the shell command is not understanding the redirect at the very least. However it more likely it fails eralier; the file that the script is trying to convert never gets created in the output directory for whatever reason.
Not sure I understand how to convert long filenames to short ones? It might be worth a try.

Guy
 
Just a little FYI here...

Did you know "" is the same as Chr(34)
Such as...
Text1 = "this is a ""quoted"" string"

The output will be:
this is a "quoted" string

As opposed to:
Text1 = "this is a " & Chr(34) & "quoted" & Chr(34) & " string"

For example:
Shell Environ$("COMSPEC") & " /C " & """c:\Program Files\EDS\Unigraphics NX 2.0\UGII\ug_convert_part.exe"" -in """ & InpuDir & "\" & InputFil & """ -o """ & OutpuDir & """ > """c:\temp\ugconvertlog.txt"""

Now...
X = ""
Will return a null (empty) string...
X = """"
Will return a 1 double quote charater (")

you could also try adding a "cmd /c " before your Shell command string...

Visit My Site
PROGRAMMER: (n) Red-eyed, mumbling mammal capable of conversing with inanimate objects.
 
cube said:
you could also try adding a "cmd /c " before your Shell command string...
That's what:

Environ$("COMSPEC") & " /C " actually does!

________________________________________________________________
If you want to get the best response to a question, please check out FAQ222-2244 first

For tsunami relief donations

'If we're supposed to work in Hex, why have we only got A fingers?'

for steam enthusiasts
 
Guy,

Shells are done asynchronously (your code does not wait for them to complete) so it could be the log file is not being created in time for your code to read it. Does the file turn up under Windows Explorer?

To test that put a pause after the shell and before the file reading code eg;

Doevents
MsgBox "Check for log file in Explorer then click here to read it"

If that works a more intelligent pause can be arranged.

Cube,

Thanks. Of course a chr$(34) is a " is a double quote, but some end up in the final string and some don't. Using chr$(34) for the outer ones just makes the code a little easier to understand I think; its only preference.

regards Hugh,

 
>> That's what:
>> Environ$("COMSPEC") & " /C " actually does!


That's what I thought, but I've never used it like that...

Another way to wait for the log file is:
Code:
'Execute shell...
'...
[b]'Wait For Log File...
Do: DoEvents: Loop Until Len(Dir("LogFile.log"))[/b]
'Read Log File
'...

I believe there is a way to wait for the Shell to finish also...
Such as faq222-428

Then there is also the option to go with ShellExecute API...
See MSDN Info HERE
The declaration for it is:
Code:
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hWnd As Long, ByVal lpOperation As String, ByVal lpFile As String, ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Private Const SW_SHOWNORMAL = 1

In some cases I have had better luck with it over the standard Shell command...

Hugh,
What do you mean by...?
>> some end up in the final string and some don't.

I have never had a problem using "" in place of Chr(34)

If you want to keep the code REALLY clean, another thing I have done in the past was to create a small function to wrap text in quotes...
Code:
Function Q(Text As String) As String
  Q = Chr(34) & Text & Chr(34)
  [b][i]'Same As: Q = """" & Text & """"[/i][/b]
End Function

Then your code might look like:
Code:
Dim Program As String, Target As String, LogFile As String

Program = "c:\Program Files\EDS\Unigraphics NX 2.0\UGII\ug_convert_part.exe"
Target = "j:\Gedkins\Tangent Edges.prt"
LogFile = "c:\temp\ugconvertlog.txt"

Shell "cmd /c " & Q(Program) & " -in " & Q(Target) & " -o C:\Temp > " & Q(LogFile)

Do: DoEvents: Loop Until Len(Dir(LogFile))

Open LogFile For Input As #1
  Text1.Text = Input(LOF(1), #1) 'Dump file to text box
Close

Visit My Site
PROGRAMMER: (n) Red-eyed, mumbling mammal capable of conversing with inanimate objects.
 
Okay gentlemen,

Here is the skinny to date. All the help is great and in the end the thing that did it was putting quotes around the entire output string and now it works just great with one exception.

The logfile is created, but the VB code is getting to the logfile to early by the looks of it(i.e. before the shell closes the logfile). The logfile is created and there is input redirected to it as wanted(when I look later thru the explorer interface. VB does not choke so it does find the file however it must be empty (0 kb) when it gets to it because the log screen in my form where the log is read into is empty still. If I run just the log read code after the fiel is there I get the log texxt on the form. This must be the asynchronous problem as mentioned since I do have sleep state after the shell and before log open. Could I use the timer control to start the log read?

Guy
 
Comspec allows for different operating systems - as you recall in 95/98 the command processor was called command.com, in XP its called cmd.com

That info (and the full path to it) is stored in Comspec

________________________________________________________________
If you want to get the best response to a question, please check out FAQ222-2244 first

For tsunami relief donations

'If we're supposed to work in Hex, why have we only got A fingers?'

for steam enthusiasts
 
Guy,

Ref my previous
<<Out of interest have you tried just placing a chr$(34) at the very begining and at the very end; ie none within. That would be too easy though..>>
It was that easy eh!

Good so can I take it that it is working after using the message box as in my example or the Loop until len(Dir$(logfile)) in Cube's example?

Another way to do the pause is as follows WITH the option to timeout.

'ShellAndWait declarations
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Public Declare Function WaitForInputIdle Lib "user32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Public Const INFINITE = &HFFFF, SYNCHRONIZE = &H100000

Sub ShellAndWait(sAppPath$, Optional iWindowStyle As VbAppWinStyle = vbMinimizedFocus, Optional lmsTimeOut As Long = INFINITE)

Dim lPid As Long, hProc As Long, lTimeout As Long

lPid = Shell(sAppPath, iWindowStyle)
hProc = OpenProcess(SYNCHRONIZE, 0&, lPid)
If hProc <> 0& Then
WaitForInputIdle hProc, INFINITE
WaitForSingleObject hProc, lmsTimeOut
CloseHandle hProc
End If

End Sub

usage
ShellAndWait (ShellString$[,window style as in VB Shell][,Seconds to Wait])

Regards Hugh,



 
johnwm said:
Comspec allows for different operating systems - as you recall in 95/98 the command processor was called command.com, in XP its called cmd.com

That info (and the full path to it) is stored in Comspec

hmmm....

Yeah, I knew about the old command.com being used all the way from dos days through 98 (ME?) and I believe cmd was introduced in NT(?)

In fact, you can still use command in XP...
Here is the /? dump...
Code:
Starts a new instance of the MS-DOS command interpreter.

COMMAND [[drive:]path] [device] [/E:nnnnn] [/P] [/C string] [/MSG]

  [drive:]path    Specifies the directory containing COMMAND.COM file.
  device          Specifies the device to use for command input and output.
  /E:nnnnn        Sets the initial environment size to nnnnn bytes.
  /P              Makes the new command interpreter permanent (can't exit).
  /C string       Carries out the command specified by string, and then stops.
  /MSG            Specifies that all error messages be stored in memory. You
                  need to specify /P with this switch.

Command is for DOS (I want to say it is 16 bit shell), Where cmd is for windows (32 bit shell)
Note how much more functionality cmd has compared to command...
Code:
Starts a new instance of the Windows XP command interpreter

CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
    [[/S] [/C | /K] string]

/C      Carries out the command specified by string and then terminates
/K      Carries out the command specified by string but remains
/S      Modifies the treatment of string after /C or /K (see below)
/Q      Turns echo off
/D      Disable execution of AutoRun commands from registry (see below)
/A      Causes the output of internal commands to a pipe or file to be ANSI
/U      Causes the output of internal commands to a pipe or file to be
        Unicode
/T:fg   Sets the foreground/background colors (see COLOR /? for more info)
/E:ON   Enable command extensions (see below)
/E:OFF  Disable command extensions (see below)
/F:ON   Enable file and directory name completion characters (see below)
/F:OFF  Disable file and directory name completion characters (see below)
/V:ON   Enable delayed environment variable expansion using ! as the
        delimiter. For example, /V:ON would allow !var! to expand the
        variable var at execution time.  The var syntax expands variables
        at input time, which is quite a different thing when inside of a FOR
        loop.
/V:OFF  Disable delayed environment expansion.

Note that multiple commands separated by the command separator '&&'
are accepted for string if surrounded by quotes.  Also, for compatibility
reasons, /X is the same as /E:ON, /Y is the same as /E:OFF and /R is the
same as /C.  Any other switches are ignored.

If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:

    1.  If all of the following conditions are met, then quote characters
        on the command line are preserved:

        - no /S switch
        - exactly two quote characters
        - no special characters between the two quote characters,
          where special is one of: &<>()@^|
        - there are one or more whitespace characters between the
          the two quote characters
        - the string between the two quote characters is the name
          of an executable file.

    2.  Otherwise, old behavior is to see if the first character is
        a quote character and if so, strip the leading character and
        remove the last quote character on the command line, preserving
        any text after the last quote character.

If /D was NOT specified on the command line, then when CMD.EXE starts, it
looks for the following REG_SZ/REG_EXPAND_SZ registry variables, and if
either or both are present, they are executed first.

    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\AutoRun

        and/or

    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\AutoRun

Command Extensions are enabled by default.  You may also disable
extensions for a particular invocation by using the /E:OFF switch.  You
can enable or disable extensions for all invocations of CMD.EXE on a
machine and/or user logon session by setting either or both of the
following REG_DWORD values in the registry using REGEDT32.EXE:

    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\EnableExtensions

        and/or

    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\EnableExtensions

to either 0x1 or 0x0.  The user specific setting takes precedence over
the machine setting.  The command line switches take precedence over the
registry settings.

The command extensions involve changes and/or additions to the following
commands:

    DEL or ERASE
    COLOR
    CD or CHDIR
    MD or MKDIR
    PROMPT
    PUSHD
    POPD
    SET
    SETLOCAL
    ENDLOCAL
    IF
    FOR
    CALL
    SHIFT
    GOTO
    START (also includes changes to external command invocation)
    ASSOC
    FTYPE

To get specific details, type commandname /? to view the specifics.

Delayed environment variable expansion is NOT enabled by default.  You
can enable or disable delayed environment variable expansion for a
particular invocation of CMD.EXE with the /V:ON or /V:OFF switch.  You
can enable or disable completion for all invocations of CMD.EXE on a
machine and/or user logon session by setting either or both of the
following REG_DWORD values in the registry using REGEDT32.EXE:

    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\DelayedExpansion

        and/or

    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\DelayedExpansion

to either 0x1 or 0x0.  The user specific setting takes precedence over
the machine setting.  The command line switches take precedence over the
registry settings.

If delayed environment variable expansion is enabled, then the exclamation
character can be used to substitute the value of an environment variable
at execution time.

File and Directory name completion is NOT enabled by default.  You can
enable or disable file name completion for a particular invocation of
CMD.EXE with the /F:ON or /F:OFF switch.  You can enable or disable
completion for all invocations of CMD.EXE on a machine and/or user logon
session by setting either or both of the following REG_DWORD values in
the registry using REGEDT32.EXE:

    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\CompletionChar
    HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor\PathCompletionChar

        and/or

    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\CompletionChar
    HKEY_CURRENT_USER\Software\Microsoft\Command Processor\PathCompletionChar

with the hex value of a control character to use for a particular
function (e.g.  0x4 is Ctrl-D and 0x6 is Ctrl-F).  The user specific
settings take precedence over the machine settings.  The command line
switches take precedence over the registry settings.

If completion is enabled with the /F:ON switch, the two control
characters used are Ctrl-D for directory name completion and Ctrl-F for
file name completion.  To disable a particular completion character in
the registry, use the value for space (0x20) as it is not a valid
control character.

Completion is invoked when you type either of the two control
characters.  The completion function takes the path string to the left
of the cursor appends a wild card character to it if none is already
present and builds up a list of paths that match.  It then displays the
first matching path.  If no paths match, it just beeps and leaves the
display alone.  Thereafter, repeated pressing of the same control
character will cycle through the list of matching paths.  Pressing the
Shift key with the control character will move through the list
backwards.  If you edit the line in any way and press the control
character again, the saved list of matching paths is discarded and a new
one generated.  The same occurs if you switch between file and directory
name completion.  The only difference between the two control characters
is the file completion character matches both file and directory names,
while the directory completion character only matches directory names.
If file completion is used on any of the built in directory commands
(CD, MD or RD) then directory completion is assumed.

The completion code deals correctly with file names that contain spaces
or other special characters by placing quotes around the matching path.
Also, if you back up, then invoke completion from within a line, the
text to the right of the cursor at the point completion was invoked is
discarded.

The special characters that require quotes are:
     <space>
     &()[]{}^=;!'+,`~

I did not know that the default was stored in comspec though...

Guess you learn something everyday ;-)

Visit My Site
PROGRAMMER: (n) Red-eyed, mumbling mammal capable of conversing with inanimate objects.
 
Absolutely so - if you're sure you will never run on an old machine then cmd is to be preferred. However - the point of Comspec is seen if you run your super-duper new prog on an old machine, when a call to cmd will crash&burn

________________________________________________________________
If you want to get the best response to a question, please check out FAQ222-2244 first

For tsunami relief donations

'If we're supposed to work in Hex, why have we only got A fingers?'

for steam enthusiasts
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top