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 gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

What to use in place of 'ShellExecute' command

Status
Not open for further replies.

StevenK

Programmer
Jan 5, 2001
1,294
GB
I'm making use of 'ShellExecute' (opening of Crystal Reports) within my Delphi 5 application. On some machines this works with no problems.
On one (lower spec) machine I get the return value 'SE_ERR_OOM' ('There was not enough memory to complete this operation') when I try to run the command from the application.
Admittedly the application is quite large (database access work) HOWEVER I am able to open the report alongside the running application using 'Crystal Reports'.
This suggests to me that the machine itself can handle the memory required but from inside the application it cannot.
Can anyone suggest why this might be ?
Can anyone suggest how to resolve this, be it still using the 'ShellExecute' command or something similar ?
Thanks in advance
Steve
 
Have a look at CreateProcess() this gives you much more control than Shellexec.
I dont know if it will help with your problem, but it is recomended over WinExec() which has been suggested in a recent post.

Steve.
 
Will the 'CreateProcess' call work against a file rather than an application ?
I've tried some code I had available to me (from a Delphi Magazine Disk) but this only seemed to operate if I pointed to an application (.exe file) rather than something like a '.txt' or '.rpt' file which would prove to be ideal.
Can anyone help me with this, possibly providing a very simple (and idiot-proof) example.
Thanks in advance
Steve
 
hi Steven

What exactly are you trying to do, preview a CR ? Where does the .txt come in to it ?

If I understand correctly to preview a CR file, a utility is run to actually show the report. Alternatively can't you create a blank form and using Crystals API, embed the report onto the form ? Then, at runtime, create as many instances of the form as required with the appropriate report.

I haven't used Crystals API directly as I found it a pain. I used a 3rd party component (by SupraSoft - sounds like I'm plugging this), but it did make life a lot easier.

lou
 
weez : The '.txt' file reference was just a for instance.
I'm only only at opening up '.rpt' files.
I've previously made use of the 'CRViewer' component to review reports but through use of the 'ShellExecute' call I can fire up the report file using 'Crystal Reports' itself such that the user can work on the report design - rather than just view the report (and data) using the 'CRViewer' component.
The 'ShellExecute' call operates OK in a small app but when I introduce it into the existing (large) application I get the 'SE_ERR_OOM' return ('Out of memory').
I was hoping that there would be an alternate approach to this. As mentioned the file can be opened (using Crystal Reports) alongside the running (large) application but causes the error when attempting to open (in Crystal Reports) through use of the 'ShellExecute' command.
Any further advice would be appreciated.
Steve
 
StevenK,

I've had mixed luck when trying to use ShellExecute. In some ways, it's as finicky as DDE used to be, so I generally try to make sure that the user's machine is properly configured before calling shellExecute.

This shows one way to do this:

Code:
procedure TForm1.Button1Click(Sender: TObject);
var
   pcRetval : pChar;     // value returned by FindExecutable
   lwdError : longword;  // Same datatype as hInstance
   strError : string;    // use to build error message
begin

  pcRetval := strAlloc( MAX_PATH );  // Defined as 260 in SYSUTILS.PAS
  try

     lwdError := findExecutable( pChar( edtDocName.Text ),
                pChar( edtDefPath.Text ), pcRetVal );

     if lwdError > 32 then
        edtResult.Text := string( pcRetval )
     else begin
        case lwdError of

           0 : strError := 'The system is out of memory or resources';
          31 : strError := 'There is no association for the specified file type.';

          ERROR_FILE_NOT_FOUND :
             strError := 'The specified file was not found';
          ERROR_PATH_NOT_FOUND :
             strError := 'The specified path was not found.';
          ERROR_BAD_FORMAT     :
             strError := 'The .EXE file is invalid (non-Win32 ' +
                         '.EXE or error in .EXE image)';

          else
             strError := format( 'FindExecutable returned an ' +
                                 'unexpected error code (%d)', [ lwdError ] );
        end;
        application.MessageBox( pChar( strError ), 'Sorry', mb_ok );
     end; // else

  finally
     strDispose( pcRetval );
  end;

Now, this is a quick and dirty implementation, as you can tell. But, if findExecutable returns an error, then it's pretty likely you won't be able to use ShellExecute.

You could also take the result of findExecutable and then call the program directly using createProcess (example recently posted to another thread) by passing the name of your document as an appropriate command line parameter.

I should also point out the following:

1. findExecutable returns the short file name of the program.

2. findExecutable does not like wildcards, so you have to make certain you have a valid, existing document name to pass it. If this is a problem, you can always create a temporary file in %TEMP% and then call findExecutable. That works nicely.

Hope this helps...

-- Lance
 
Thanks for the pointers with this one people.
I'm now actually able to use 'ShellExecute' to open the reports as originally intended.
My problem (albeit unseen at first) was that the 'ShellExecute(Handle, ....)' was within the scope of a 'with Sender as TMenuItem do' code block.
I can only think that the 'Handle' attempted to be used was that of the 'TMenuItem' rather than a more global one.
Although this does beg the question as to why this would run with no issues on my Windows 2000 (high spec) machine and not the standard Windows NT one.
Thanks again for the pointers.
Steve
 
Steve,

Yes, it is very possible that your WITH construct was returning a different handle than the one you intended. I've documented a similar situation in where I'd was inadvertantly "releasing" the wrong object. As a result, I was seeing all sorts of strange behaviour.

Don't get me wrong, I *like* WITH and use it heavily, however, you need to be very careful that the assumptions that result are, in fact, the ones you intended.

When I see odd behavior inside a WITH block, I almost always try reworking the code so it doesn't use WITH, which frequently leads to the fix. Once you've gotten the problem taken care of, you can either leave it alone or add WITH back into the mix, depending on how badly you want to use it.

Perhaps changing the parameter to Application.Handle would help? Hard to say without a clearer idea of the code.

I do know that things are often (*cough*) handled differently in different versions of Windows. It's entirely possible that the value returned under Windows 2000 somehow matches the one that's expected whereas the value returned under your NT machine.

Case in point: There have been several documented differences between NT 3.5x and the more recent versions. It's entirely possible that it "works" under Windows 2000 through a happy accident.

In any event, you got it working. That's something. :)

Hope this helps...

-- LAnce
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top