Question

I have a program that needs data from a SQL-Server, it can't work without (well, it can but has totally no use).

There are 2 autocreate forms, DMOD and Main, in that order.

This is the code in the OnCreate of DMOD:

  if not fileexists(UdlFile) then
  begin
    ITRCreateFile(UdlFile);
    ShellExecute(Application.Handle,'open',UdlFile,nil,nil,SW_SHOW);
     try
       cnConnect.Close;
       if gServerPort <> '' then
          cnConnect.connectionString:= 'Provider=SQLOLEDB.1;Password=*;Persist Security Info=True;User ID=itreflex;Initial Catalog=ExquisStudio;Data Source=' + gServerName + '\' + gServerPort + ';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=' + gServerName + ';Use Encryption for Data=False;Tag with column collation when possible=False'
       else
          cnConnect.connectionString:= 'Provider=SQLOLEDB.1;Password=*;Persist Security Info=True;User ID=itreflex;Initial Catalog=ExquisStudio;Data Source=' + gServerName + ';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=' + gServerName + ';Use Encryption for Data=False;Tag with column collation when possible=False';
       cnConnect.Open();
   except
      Showmessage('Problems with dataconnection - error SQL data');
      screen.Cursor := crDefault;
      Application.terminate;
   end;
  end
  else
  begin
     try
       cnConnect.Close;
       if gServerPort <> '' then
          cnConnect.connectionString:= 'Provider=SQLOLEDB.1;Password=*;Persist Security Info=True;User ID=itreflex;Initial Catalog=ExquisStudio;Data Source=' + gServerName + '\' + gServerPort + ';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=' + gServerName + ';Use Encryption for Data=False;Tag with column collation when possible=False'
       else
          cnConnect.connectionString:= 'Provider=SQLOLEDB.1;Password=*;Persist Security Info=True;User ID=itreflex;Initial Catalog=ExquisStudio;Data Source=' + gServerName + ';Use Procedure for Prepare=1;Auto Translate=True;Packet Size=4096;Workstation ID=' + gServerName + ';Use Encryption for Data=False;Tag with column collation when possible=False';
        cnConnect.Open();
     except
       Showmessage('Problems with dataconnection - error SQL data');
       screen.Cursor := crDefault;
       Application.terminate;
     end;
  end;

The problem is, the program closes, there is nothing left that is visible, but the process keeps running, mostly at 95% or higher CPU-usage. That's not really good....

I did some digging and found Thread not terminated while application is terminated under Delphi

I added ExitProcess(0); after both Application.terminate; and now the process closes as should be. I used 0 because the parameter is required, I have no idea what it really should be.

My question is: Is it OK to do it this way? It does what I want but I have the feeling I'm overlooking something.

Was it helpful?

Solution 2

OK, I think I found it.

This is what I did:

  1. I removed Main out of the list of autocreate forms, so that only DMOD remains there. I noticed the program really hang here if there was no connection.

  2. I removed Application.terminate; in the Exception-parts.

  3. This is the code that is below the code I posted before. This is also the code that was still getting executed after Application.terminate; was called in the Exception-part. I added an extra check here and call Main also here if everything is good to go.

    if cnConnect.Connected then
    begin
      ADOPermissions.Open;
      ADOGroupMembers.Open;
      ADOGroupAccess.Open;
      ADOGroups.Open;
      ADOUserAccess.Open;
      ADOUsers.Open;
      Application.CreateForm(TfrmMain, frmMain);
    end
    else
      Application.terminate;
    
  4. Now the process disappears as it should be.

I feel a bit stupid that I have not discovered this myself by using a breakpoint like I did now, but I thought Application.terminate; would stop and kill the process directly.

OTHER TIPS

ExitProcess will end your process. But at the expense of doing any proper tidy up. What you really ought to be doing is working out why the program is not shutting down. What is running that is preventing shut down? It is not possible to discern that information from the code in the question. You'll need to dig a bit deeper.

If you cannot work it our by static analysis then you can use the debugger to help:

  1. Start your program running under the debugger.
  2. Take the action which calls Application.Terminate.
  3. Pause execution with Run | Program Pause.
  4. Look at the state of the threads using View | Debug Windows | Threads.
  5. Double click on a thread to select it and look at its call stack.

These call stacks should hopefully be enough to work out which thread is busy and so stopping your program from terminating. The first place to look is the main thread. What is it waiting on? Answer that question and you should be able to solve the problem.

Your initial problem is that you are trying an orderly close of the program before the main form is created. This is generally a risky process as the processing of the WM_QUIT message (which is what terminate sends) may be adversely affected by all sorts of stuff in your program that happens between your call of terminate and entry into the applications main message loop.

I generally use one of two constructions to manage this

First option is for the main form to ask the datamodule its state during the main form's initialisation and have the main form decide on exiting.

Second option is to have the main form create the datamodule rather than having it auto-create first. This is essentially similar but does mean that terminate in the datamodule is much more likely to work.

Either option puts the termination code in the main form which puts it after the Delphi run time has understood the identity of the primary form that needs to be sent a close message to initiate shutdown.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top