-2

I am trying to dump a PDF to text using a command line utility (It works with tests from dos command line) from my Delphi code.

Here is my code

if fileexists(ExtractFilePath(Application.ExeName) + 'pdftotext.exe') then
begin
  ShellExecute(H,'open', 'pdftotext.exe', PWideChar(fFileName), nil, SW_SHOWNORMAL);
  if fileExists(changeFileExt(fFileName, '.txt')) then
    Lines.LoadFromFile(changeFileExt(fFileName, '.txt'))
  else
    ShowMessage('File Not found');
end;

When placing breakpoints in code and stepping through, it makes it to the

if fileExists(changeFileExt(fFileName, '.txt')) then  

line but returns false, so the Shellexecute was called but no file was ever dumped

What have I done wrong?

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
IElite
  • 1,818
  • 9
  • 39
  • 64
  • sorry, that last end wasn't needed. I was cutting and pasting code from elsewhere. The fFileName is a string variable defined elswhere in procedure – IElite Jan 07 '11 at 22:25
  • 2
    For your own sake, you should also format your code properly (using consistent indenting, for one thing). And, for the sake of the end-user, 'File Not found' is a terrible error message (and so is 'File not found.'). – Andreas Rejbrand Jan 07 '11 at 22:30
  • THanks, but this is experimental coding right now, and far from something deliverable (testing if you will). The showMessage is what i was using to test whther or not the file was found – IElite Jan 08 '11 at 02:40
  • possible duplicate of [How can I wait for a command-line program to finish?](http://stackoverflow.com/questions/4295285/how-can-i-wait-for-a-command-line-program-to-finish) – Jeroen Wiert Pluimers Jan 08 '11 at 07:39
  • @Shane: If Rob's hypothesis is correct, you could also use the Windows Explorer to test if the file is really created or not. – Andreas Rejbrand Jan 08 '11 at 14:58
  • @Jeroen - its not a duplicate. ANd that is very easy to see. I shouldn't have to explain why. Thats for the negative! You didn't answer on that question and you didn't answer on this one. Is that your goal, to go around and give negatives where you see fit? – IElite Jan 10 '11 at 15:28
  • @Shane: I voted for the close, not for the negative. Actually, I hardly cast downvotes, so thanks for accusing me of such. Since you really want to wait for the `pdftotext.exe` to exit, and `ShellExecute` cannot wait for it, you should actually look into that other question. Currently your code will not wait, and this will result in errors on slow machines. This is another error, apart from the error in your question you fixed in your answer (and I overlooked). Sorry my cyborg mind overlooked that one, and good you fixed it in a maintainable way. – Jeroen Wiert Pluimers Jan 10 '11 at 16:52

2 Answers2

7

ShellExecute doesn't wait for the invoked program to finish running. You're probably checking for the file too soon. The file simply hasn't been created yet.

Run the program and wait for it to terminate before you check for the output file. ShellExecute doesn't return enough information for you to do that, so you should try CreateProcess instead. There are several examples of how to do that. Try this:

How can I wait for a command-line program to finish?

Community
  • 1
  • 1
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • +1. This seems to be it. (But the OP could have realised this problem himself very easily by using the Windows Explorer...) – Andreas Rejbrand Jan 08 '11 at 15:13
  • 1
    Or use ShellExecuteEX (http://msdn.microsoft.com/en-us/library/bb762154%28v=vs.85%29.aspx) which provides the process id of the new process so you can use WaitForSingleObject() – Gerry Coll Jan 09 '11 at 03:39
  • @ Andreas - are you always that critical? Do you really believe every one is as smart as you, and that they should be able to figure these things out? If so, there would be no reason for this site, now would there. Get a life! – IElite Jan 10 '11 at 15:29
1

It turns out, that adding the fill path to the execuatble made it work just fine

uses
  Forms, ShellAPI, SysConst, SysUtils;

procedure Pdf2Text(const fFileName: string; const Lines: TStrings);
var
  H: HWND;
  PdfToTextPathName: string;
  ReturnValue: Integer;
  TxtFileName: string;
begin
  H := 0;
  PdfToTextPathName := ExtractFilePath(Application.ExeName) + 'pdftotext.exe'; // full path
  if FileExists(PdfToTextPathName) then
  begin
    ReturnValue := ShellExecute(0,'open', PWideChar(PdfToTextPathName), PWideChar(fFileName), nil, SW_SHOWNORMAL);
    if ReturnValue <= 32 then
      RaiseLastOsError();
    // note: the code below this line will crash when pdftotext.exe does not finish soon enough; you should actually wait for pdftotext.exe completion
    TxtFileName := ChangeFileExt(fFileName, '.txt');
    if FileExists(TxtFileName) then
      Lines.LoadFromFile(TxtFileName)
    else
      raise EFileNotFoundException.CreateRes(@SFileNotFound);
  end;
end;

Edit: Some code cleanup helps big time to catch errors early on, especially when testing a proof of concept.

Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
IElite
  • 1,818
  • 9
  • 39
  • 64
  • 3
    The lesson to be learned here is this: **Never ignore the return value of an API function.** If you had checked the result before, you would have seen it return `Error_File_Not_Found`, which would have been your cue to figure out why `ShellExecute` couldn't find the file you asked it to execute. – Rob Kennedy Jan 10 '11 at 18:18
  • 1
    Another lesson: **Don't assume the current working directory is what you think it is.** Shane, you assumed that the OS would know where *pdftotext.exe* was. Without a fully qualified path, the OS will use the `PATH` environment variable to look for a program, and that includes the current program's working directory. Often, that will be the directory where the current EXE lives, but that can change. If you know where the desired program is supposed to be, then ask for that location specifically, as your answer does. – Rob Kennedy Jan 10 '11 at 18:29
  • 1
    Actually, i didn't post the entire line, it was the handle portion of the call. I replaced it with zero and it worked ShellExecute(0,'open', PWideChar(ExtractFilePath(Application.ExeName) + 'pdftotext.exe'), PWideChar(fFileName), nil, SW_SHOWNORMAL); – IElite Jan 10 '11 at 18:44