?
Solved

Code Execution Only In Finished Exe

Posted on 2004-11-03
13
Medium Priority
?
401 Views
Last Modified: 2010-04-05
I have some code in my app which switches to the screen resolution the app was designed to work in (800x600). This works fine.

My native screen rez is 1280x1024 and every time I run my app while in the Delphi IDE, on exit, all the Delphi windows are bunched up in the top left corner.

Enabling it for the final compile and disabling it while working on it in the IDE is getting to be a bit of a pain - especially when I forget to enable it for the final compile and only discover it after I've re-built the installer and installed it on my test machine! :)

Is there any way to have the screen rez code executed ONLY when the finished exe is run - but not when working in the IDE.

I have a feeling that it's using the compiler directives, ($IFDEF?), but I can't find any info on exactly how it's done.

I'm sure this is one of those very easy questions to answer... if you know how it's done! :)

TDK_Man
0
Comment
Question by:tdk_man
13 Comments
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12483105
Actually, you could use FindWindow to check if the Delphi IDE is running or not but that doesn't make it really clear if you're running from within the IDE or not. But Delphi does not have an easy way to check if it's run from the IDE or in some other way.
Technically, it should be possible to check the process stack and check who created your process. If Delphi is the parent process of your executable then you know you're running in the IDE, otherwise you're running stand-alone, even through Delphi might be running at the same time.
0
 
LVL 2

Accepted Solution

by:
Molando earned 500 total points
ID: 12483624
Check to see  if DebugHook<>0 then the  program is running in the ide. if it is not zero, then it is running outside the  IDE

so:

var
  dm : TDEVMODE;

  if DebugHook = 0 then begin
      ZeroMemory(@dm, sizeof(TDEVMODE));
      dm.dmSize := sizeof(TDEVMODE);
      dm.dmPelsWidth  := 1024;
      dm.dmPelsHeight := 768;
      dm.dmFields := DM_PELSWIDTH or DM_PELSHEIGHT;
      ChangeDisplaySettings(dm, 0);
      end;

Molando
    end;

0
 
LVL 2

Expert Comment

by:cqhall
ID: 12484090
DebugHook is an excellent find.  

However, be aware that if integrated debugging is disabled (Tools|Debugger Options..., Integrated debugging checkbox), the result will be 0 (standalone) even if the application is run from the IDE (F9, Run|Run).  In fact, the application is essentially outside the IDE, because you can close Delphi without it complaining and Run|Program Reset is diabled.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 1

Author Comment

by:tdk_man
ID: 12488149
Mmm yes...

I forgot that when you run your program, you aren't really in the IDE - just executing the exe which has been created. Good point!

I'm thinking of the old interpreter days when you compiled your exe afterwards.

Molando:

That looks like it should do the trick - thanks. I'll try it out and report back to award the points if it all goes to plan. :)

TDK_Man
0
 
LVL 31

Expert Comment

by:moorhouselondon
ID: 12490961
You could use paramstr to pass a command line string to the program when it is run.  In the Run menu is a Command Line Parameters box where you can type in whatever parameters you want.  These parameters are persistent between runs of the compiler.

The snag with this is that the user of the application could guess that you were using this technique and go into the shortcut icon and amend it to run with the same parameter, but it is unlikely for the average user to hit on this.

In your program, before creating forms, etc.

if paramstr(1)='DebugMode' then
  screenres this
else
  screenres that;


0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12493972
I have thought about using DebugHook too but I realised that it would still be possible to run the application from the IDE without the debugger being attached. One other problem could arise if your process is run from another debugger, e.g. Borland's stand-alone debugger. But that's fortunately not the case here.

I did find one interesting alternative, though... I've been experimenting with WMI lately and when I create a list of processes (select * from Win32_Process) iced that my process has a ParentProcessId property. With this ParentProcessId you could search for the parent process and if this parent happens to be Delphi, you're running it from Delphi. Otherwise, it's run in some different way...

Let's show something WMI-based that WORKS! :-)

uses
  Windows, SysUtils, Classes, ActiveX, ComObj, WbemScripting_TLB, ShellAPI;

function IsRunFromDelphi: Boolean;
const
  sQuery = 'SELECT * FROM Win32_Process WHERE (ProcessId = "%s")';
var
  ExecutablePath: string;
  Item: SWbemObject;
  Locator: ISWbemLocator;
  NumProp: LongWord;
  ObjectSet: ISWbemObjectSet;
  OleProperty: OleVariant;
  ParentProcessId: string;
  Query: WideString;
  Services: ISWbemServices;
begin
  Result := False;
  try
    Locator := CoSWbemLocator.Create;
    Services := Locator.ConnectServer( '', 'root\cimv2', '', '', '', '', 0, nil );
    Query := Format( sQuery, [ IntToStr( GetCurrentProcessId ) ] );
    ObjectSet := Services.ExecQuery( Query, 'WQL', wbemFlagBidirectional, nil );
    if Succeeded( ( ObjectSet._NewEnum as IEnumVariant ).Next( 1, OleProperty, NumProp ) ) and ( NumProp > 0 ) and Succeeded( IDispatch( OleProperty ).QueryInterface( SWBemObject, Item ) ) then begin
      ParentProcessId := VarToStr( Item.Properties_.Item( 'ParentProcessId', 0 ) );
      Query := Format( sQuery, [ ParentProcessId ] );
      ObjectSet := Services.ExecQuery( Query, 'WQL', wbemFlagBidirectional, nil );
      if Succeeded( ( ObjectSet._NewEnum as IEnumVariant ).Next( 1, OleProperty, NumProp ) ) and ( NumProp > 0 ) and Succeeded( IDispatch( OleProperty ).QueryInterface( SWBemObject, Item ) ) then begin
        ExecutablePath := VarToStr( Item.Properties_.Item( 'ExecutablePath', 0 ) );
        Result := ( pos( 'delphi32.exe', LowerCase( ExecutablePath ) ) > 0 )
      end;
    end;
  except Result := False;
  end;
end;

Well, the uses clause might contain a bit too many units. Above code runs good in Delphi 5 and I've used the following code to test it:

    if IsRunFromDelphi then begin
      MessageBox( GetDesktopWindow, 'Delphi', 'Running from...', MB_OK );
    end
    else begin
      MessageBox( GetDesktopWindow, 'stand-alone', 'Running...', MB_OK );
    end;

When running from Delphi, it displayed Delphi. When run from the Norton commander, it returns Stand-alone. You could actually use above code to go all the way back to the top parent process. The drawback is of course that you need to use WMI in your application, which is only installed on newer operating systems and systems that have Internet Explorer 5 or higher installed.

I have a ready-to use WMI unit at http://www.workshop-alex.org/sources/WbemScripting_TLB/WbemScripting_TLB.html and http://www.workshop-alex.org/sources/WbemScripting_TLB/WbemScripting_TLB.pas that can be used in Delphi 7. It won't add much overhead to your application, though. It's just a bit complicated when you start playing with it...
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12494054
You don't need the ShellAPI unit. And some other units might be dropped too. I think you do need the Windows, SysUtils and ActiveX units, though. Above code snippet originates from a simple WMI test applicatiuon that I'm playing with, and I added above code about half an hour ago. :-) (And checked that it works!)
0
 
LVL 1

Author Comment

by:tdk_man
ID: 12498503
You certainly know your onions Alex!

I'm only a vey minor league programmer and to be honest, you lost me after        

 Locator :=    

:)

I didn't want anyone to go to absolutely *any* lengths to find a solution - I was just after a quick kludge to have a bit of code executed only when the IDE isn't running, (I hoped anyway).

This I'm afraid is from my old GFA days where you could put a single conditional statement  allowing code to be temporarily disabled in non-compiled code (or vice-versa).

When the solution gets that technical, you have to wonder whether it's worth all the effort you have obviously put into it just to solve a rather trivial problem - ie me being rather lazy! :)

"There's no easy way to do it" would have shut me up.

TDK_Man
0
 
LVL 6

Expert Comment

by:gwalkeriq
ID: 12499376
Try the DebugHook <> 0 test then and if you're happy with the result close the question. I use this all the time and it serves my purposes well to fill in magic passwords or popup debug messages, etc. -- it does not get simpler than this.
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12502737
@qwalkeriq, sure, use of the debughook is useful and has been suggested before. But it's not perfect, since the application could be run from the IDE without the debugger being attached in which case the screen will get messed up again.

@tdk_man, well... The solution does it's work so you could try to include it in your source. It's not really that difficult either, as long as you remember that WMI is just some kind of database with your whole Windows configuration inside. It contains the funniest stuff, all kinds of performance counters and anything else you might be interested in. It's just a bit difficult to get the right code to make it work. :-)
Basically, what I just do is connect to the WMI database and execute a query twice. First to find the current process using the GetCurrentProcessID API so I can determine the parent process. Then I use the same query to find the parent process so I can check it's name. If the name happens to contain the Delphi executable name then I know it's started from Delphi. This code could actually be used to make sure your application is executed from the right process. You could, for example, create an executable that could only be started from another of your applications. All you have to do is check for the name of that other process.

What I've done with WMI is probably also possible by using some other API techniques but those would be really complicated. I just execute two queries on the WMI database... :-)

And personally, as an experienced Delphi developer who "knows his onions" I don't think it's a complex solution. Then again, I'm already deep into this WMI stuff for the last 3, 4 years...
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12502783
Oh, what the heck... If you download that WbemScripting_TLB.pas file from my site and add the unit below too, then all you have to do is use the one and only function in this unit... I know the two additional units might make things seem more complicated, but they are ready-to-use solutions. ;-)

unit untCheckForDelphi;
interface
function IsRunFromDelphi: Boolean;
implementation
uses Windows, SysUtils, ActiveX, WbemScripting_TLB;
function IsRunFromDelphi: Boolean;
const
  sQuery = 'SELECT * FROM Win32_Process WHERE (ProcessId = "%s")';
var
  ExecutablePath: string;
  Item: SWbemObject;
  NumProp: LongWord;
  ObjectSet: ISWbemObjectSet;
  OleProperty: OleVariant;
  Query: WideString;
  Services: ISWbemServices;
begin
  Result := False;
  try
    Services := CoSWbemLocator.Create.ConnectServer( '', 'root\cimv2', '', '', '', '', 0, nil );
    Query := Format( sQuery, [ IntToStr( GetCurrentProcessId ) ] );
    ObjectSet := Services.ExecQuery( Query, 'WQL', wbemFlagBidirectional, nil );
    if Succeeded( ( ObjectSet._NewEnum as IEnumVariant ).Next( 1, OleProperty, NumProp ) ) and ( NumProp > 0 ) and Succeeded( IDispatch( OleProperty ).QueryInterface( SWBemObject, Item ) ) then begin
      Query := Format( sQuery, [ VarToStr( Item.Properties_.Item( 'ParentProcessId', 0 ) ) ] );
      ObjectSet := Services.ExecQuery( Query, 'WQL', wbemFlagBidirectional, nil );
      if Succeeded( ( ObjectSet._NewEnum as IEnumVariant ).Next( 1, OleProperty, NumProp ) ) and ( NumProp > 0 ) and Succeeded( IDispatch( OleProperty ).QueryInterface( SWBemObject, Item ) ) then begin
        ExecutablePath := VarToStr( Item.Properties_.Item( 'ExecutablePath', 0 ) );
        Result := ( pos( 'delphi32.exe', LowerCase( ExecutablePath ) ) > 0 )
      end;
    end;
  except Result := False;
  end;
end;
end.

Difficult? Using the function IsRunFromDelphi is all you need...
0
 
LVL 1

Author Comment

by:tdk_man
ID: 12503766
In my case, Molando's DebugHook suggestion worked perfectly - sorry for the delay in being able to test it BTW.

I accept that it may not work in 100% of situations, but in my particular case if it fails in the finished exe for example, the worst  that can happen is that the user will have to change screen rez before using it! That's not critical.

As such, I've gone for the DebugHook option because of it's simplicity.

Thanks for all the other suggestions though.

TDK_Man
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12503845
Oh, don't worry. I just showed an interesting alternative way. I just wished I had mentioned the DebugHook option too but didn't consider it reliable enough. :-)
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
This Micro Tutorial will teach you how to add a cinematic look to any film or video out there. There are very few simple steps that you will follow to do so. This will be demonstrated using Adobe Premiere Pro CS6.
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…
Suggested Courses
Course of the Month14 days, 9 hours left to enroll

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question