-3

The contents for the *.bat file:

cd "C:\Program Files\WinRAR"
WinRAR a -r -ep1 "D:\Temp\250815\GiftCard-250815-1to10.zip" "D:\Temp\250815\word"

Basically, this file is telling WinRAR to compress the folder "word" into "GiftCard-250815-1to10.zip".

The delphi line of code:

StartProcess('cmd.exe', '/C ' + 'D:\Temp\GenCards.bat', true, true);

I also tried:

ShellExecute(Handle, 'runas', PChar('C:\Program Files\WinRAR.exe a -r -ep1 "D:\Temp\250815\GiftCard-250815-1to10.zip" "D:\Temp\250815\word"'), nil, nil, SW_SHOWNORMAL);

I also tried:

ShellExecute(Handle, 'runas', 'cmd.exe', PChar('C:\Program Files\WinRAR.exe a -r -ep1 "D:\Temp\250815\GiftCard-250815-1to10.zip" "D:\Temp\250815\word"', nil, SW_SHOWNORMAL);

The fact is, I'm really lost here since, if I go to the folder and double click the *.bat file, it WORKS PERFECTLY!

But, if I execute the bat from my code, it won't work.

* EDIT *

This is StartProcess function:

function TgenerateForm.StartProcess(ExeName: string; CmdLineArgs: string = ''; ShowWindow: boolean = True; WaitForFinish: boolean = False): integer;
var
  StartInfo: TStartupInfo;
  ProcInfo: TProcessInformation;
begin
  //Simple wrapper for the CreateProcess command
  //returns the process id of the started process.
  FillChar(StartInfo,SizeOf(TStartupInfo),#0);
  FillChar(ProcInfo,SizeOf(TProcessInformation),#0);
  StartInfo.cb := SizeOf(TStartupInfo);

  if not(ShowWindow) then begin
    StartInfo.dwFlags := STARTF_USESHOWWINDOW;
    StartInfo.wShowWindow := SW_HIDE;
  end;

  CreateProcess(nil,PChar(ExeName + ' ' + CmdLineArgs),nil,nil,False,
    CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS,nil,nil,StartInfo,
    ProcInfo);

  Result := ProcInfo.dwProcessId;

  if WaitForFinish then begin
    WaitForSingleObject(ProcInfo.hProcess,Infinite);
  end;

  //close process & thread handles
  CloseHandle(ProcInfo.hProcess);
  CloseHandle(ProcInfo.hThread);
end;

Please, do not be unpolite and copy & paste a url. I searched all the stackoverflow and tried all "how-tos ... bat files".

Wago
  • 21
  • 1
  • 3
  • 1
    http://stackoverflow.com/questions/1454501/how-do-i-run-a-command-line-program-in-delphi – Jens Borrisholt Aug 25 '15 at 18:50
  • We've no idea what StartProcess is. The shell verb runas is not needed here. You seem to be doing this with a defect of understanding. Trial and error is not a good approach. Slow down. Learn the details. Do the debugging. – David Heffernan Aug 25 '15 at 18:57
  • What could be the difference between double clik the bat file and ShellExecute it? – Wago Aug 25 '15 at 19:07
  • 1
    The difference is that your code is forcing the `runas` verb, whereas double-clicking invokes the clicked file's default verb instead. If you want to invoke the default verb with `ShellExecute()`, specify a `nil` verb. – Remy Lebeau Aug 25 '15 at 19:08
  • 1
    Now, that said, you have shown several lines of code, but you have not explained what is going WRONG with them. Are they not launching WinRAR? Are there any error codes being reported? You need to provide more details about what you have done to diagnose your problem, or else this discussion is likely to get closed as off-topic. – Remy Lebeau Aug 25 '15 at 19:10
  • Sorry. The command line open and run smoothly. There must be a problem with WinRAR method. I guess I'll need to read more. I tried 'runas' because sometimes double-click won't work, then I have to right-click > run as administrator. Yep. This is a mess. – Wago Aug 25 '15 at 19:23
  • FWIW, the link posted by Jens looks quite promising. Did you try `ShellExecute(0, nil, 'cmd.exe', '/C D:\Temp\GenCards.bat', nil, SW_SHOW);`? Note that the current drive must probably be 'C:' for this to work, so I would put that as first line in the .bat file. – Rudy Velthuis Aug 26 '15 at 22:35

1 Answers1

2

Your implementation should work. It successfully runs a batch file in a test case I put together using your StartProcess() method as written. This suggests there is some other issue affecting the operation of the code.

A couple of pointers may help.

From the documentation for CreateProcess():

To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.

lpApplicationName is the initial parameter to CreateProcess() and is specified in your code as NIL. You are passing the application name in the command line params. This is normally legitimate and in my test of your code does still work. However, it is vulnerable to spaces in long filename paths to the application module. Passing the application module filename and path explicitly in the application module parameter avoids this.

This is easily achieved by changing your CreateProcess call to:

CreateProcess(PChar(EXEName), PChar(CmdLineArgs), ....);

Expanding on David's comment, the Unicode version of CreateProcess may modify the contents of the command line parameter, so if you are using a Unicode version of Delphi, or wish to write code that will safely migrate to a Unicode compiler as-is, then you should ensure that you pass a reference to a unique, writable string. To ensure that you get a unique copy, you should first store the args in a local variable:

var
  cmdline: String;

begin 
  ..
  cmdline := CmdLineArgs;
  UniqueString(cmdline);

  ..

  CreateProcess(PChar(EXEName), PChar(cmdline), ....);

  ..
end;

Also, I would suggest that a batch file a sufficiently specialised case with such specific requirements that a specific alternate implementation wrapping StartProcess may be advisable, called StartBatch(), for example:

procedure StartBatch(const aFilename: String, ... );
begin
  StartProcess('cmd.exe', '/C ' + aFilename, ... );
end;

On a general point, repeating default parameter values in the implementation of a method is potentially confusing. They are ignored by the compiler and any default parameter values in the declaration take precedence anyway. As a result, if the parameter default values declared in the declaration are changed in the future, the values stated in the implementation then become misleading.

Deltics
  • 22,162
  • 2
  • 42
  • 70
  • You have to make sure that the second arg is writeable. A call to UniqueString does the trick. – David Heffernan Aug 25 '15 at 20:09
  • Good point. Of course, it only applies to the Unicode version and UniqueString() alone may not be enough. A local variable is needed as well as a call to UniqueString since if the string already has a ref count of 1 then UniqueString is a no-op. A local variable also referencing the string ensures a ref count of at least 2, ensuring that UniqueString() creates a unique copy, as required. I've updated the answer with this additional information and suggestion. – Deltics Aug 25 '15 at 20:29
  • If the string has a ref count of 1 it is in editable memory. Constant strings have ref count 1. UniqueString is exactly what is needed. – David Heffernan Aug 25 '15 at 21:06
  • Um, constant strings have ref count -1. A string with ref count 1 may be in edit_able_ memory but it may not necessarily be supposed to be edit_ed_. So yes, UniqueString may be all that's needed to avoid an AV in this case, but not necessarily enough to avoid more subtle potential problems. Besides though not relevant in this particular case, UniqueString() cannot be used on **const** arguments. – Deltics Aug 25 '15 at 23:04
  • Typo on my part. Was meant to say -1. If the user passes a string that is not meant to be edited, then obviously it breaks. In the code as presented, calling UniqueString on the by value arg is enough. No need to make a copy. Copy only needed if arg passed as const. – David Heffernan Aug 26 '15 at 12:50
  • Jesus Christ guys. I have so much to learn from you. I'll give you the overall info about this program: It creates barcodes numbers, store them in a database, and edit a XML file, including the barcodes. This XML is from a 'DOCX' document. As you probably know, you can alter the extension of a 'docx' to 'zip' and check it's contents. So, after saving the XML to the folder 'word', the program runs the 'batch' to 'winrar' that folder and later, change it's name back to 'docx'. So, dying in the last step of this process would be a shame for me. I'll try your suggestions later tonight. THANK YOU!! – Wago Aug 26 '15 at 22:58
  • Well, I think I'll need to reinstall Windows. Now, the batch is only working with right click > run as administrator. – Wago Aug 27 '15 at 23:43
  • Reinstalling windows doesn't sound like the solution – David Heffernan Aug 30 '15 at 22:13