How to Start and stop a VBA application from within a Windows Service (Delphi XE5)

Hi

I have a service application within which I wish to start / stop any executable (in this instance, written in VBA) from the service. I can launch the program using the code below:
function TSvceMgrMethods.StartApplication(Visibility : integer; Wait: Boolean; var AProcessId: integer) : boolean;
var
  ProcessID : DWord;


  Function WinExecute32(FileName : String; Visibility : integer; Wait: Boolean;
  Var ProcessId: DWORD): DWORD;
  var
    WorkDir: String;
    StartupInfo: TStartupInfo;
    ProcessInfo: TProcessInformation;
  begin
    UniqueString(FileName); // important for CreateProcessW()
    WorkDir := GetCurrentDir;
    ZeroMemory(@StartupInfo, Sizeof(StartupInfo));
    ZeroMemory(@ProcessInfo, Sizeof(ProcessInfo));
    StartupInfo.cb := Sizeof(StartupInfo);
    StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
    StartupInfo.wShowWindow := Visibility;
    if not CreateProcess(
      nil,
      PChar(FileName), { pointer to command line string }
      nil, { pointer to process security attributes}
      nil, { pointer to thread security attributes }
      false, { handle inheritance flag }
      CREATE_NEW_CONSOLE or { creation flags }
      NORMAL_PRIORITY_CLASS,
      nil, { pointer to new environment block }
      nil, { pointer to current directory name }
      StartupInfo, { pointer to STARTUPINFO }
      ProcessInfo) then
    begin
      raise Exception.Create('WinExecute32: Error Creating Process:'+ GetLastError().ToString());
    end;
    ProcessId := ProcessInfo.dwProcessId;

    if Wait then
    begin
      WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
      GetExitCodeProcess(ProcessInfo.hProcess, Result);
      Result := 0;
    end else begin
      Result := 0;
    end;
    CloseHandle(ProcessInfo.hThread);
    CloseHandle(ProcessInfo.hProcess);
  end;



begin

  result := WinExecute32('M:\XE5\projects\win32\debug\MKTplaceAPIcontrolP.exe',Visibility, Wait, ProcessId) = 0;
  AProcessID := ProcessID;

end;

But when I try to stop the application using this code, I get nowhere:

function TSvceMgrMethods.StopApplication(ProcessID: integer): boolean;
var
  ProcessList : TDGProcessList;
  AIndex : integer;
  PID : DWord;
  hProcess: THandle;

  function TerminateProcessByID(dwProcessID: Cardinal): Boolean;
  begin
    Result := False;
    hProcess := OpenProcess(PROCESS_TERMINATE, False, dwProcessID);
    if hProcess <> 0 then begin
      Result := WinAPI.Windows.TerminateProcess(hProcess,0);
      CloseHandle(hProcess);
    end;
  end;


begin
  PID := ProcessID;
  result := TerminateProcessByID(PID);
end;

Open in new window

The code works fine if run from a Windows Application, but when launched (via a DataSnap call) the application does not close.

I have seen something related to privileges, but cannot fathom out how to elevate such privileges.

I would be most grateful for any assistance?

Thanks
TimFHayesAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

aikimarkCommented:
a service application ... to start / stop any executable (in this instance, written in VBA)
VBA code runs inside another application, such as Excel, Word, Access, Visio.  Please explain.
TimFHayesAuthor Commented:
Apologies - I am new to Expert's exchange.

The VBA is running in Access.
Sinisa VukSoftware architectCommented:
It is different when service start some app. Go here (another EE question) - where I wrote example how to destroy such process using wmi.

When you create/run process - you should get process id and keep it for destroying.
Acronis True Image 2019 just released!

Create a reliable backup. Make sure you always have dependable copies of your data so you can restore your entire system or individual files.

TimFHayesAuthor Commented:
Thank you.

I have resolved the issue :

1) Needed to run the service in a user account with admin privileges.
2) Ran MSAccess.exe with the test.accde in the command line using CreateProcessWithLogonW.
3) Obtained the exe handle (which I could have saved but wanted to be safe in case the service had inadvertently closed) by iterating thru snapshot  made by CreateToolhelp32Snapshot using Process32First and Process32Next and comparing process entries with EXE name until matched.
4) finally able to terminate the process  using the handle of the EXE process with the TerminateProcess command.

Cheers

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
sarabandeCommented:
terminate the process  using the handle of the EXE process with the TerminateProcess command.
TerminateProcess is not a friendly way to close a process. if the program is busy while being killed, your action may cause some severe damage to the system, for example a corrupt database or a file and directory which couldn't be moved or removed since there are some open file handles.

you may try some alternatives before you call TerminateProcess. for example you could send WM_ENDSESSION to the main window of the process. this message normally was sent to windows programs if the system performs a shutdown. normaly, an application getting this message would close and save the current screen and terminate. see https://msdn.microsoft.com/en-us/library/aa376889(v=vs.85).aspx for more information. if that doesn't work (or leads to unwished messages to the user), you may send WM_CLOSE message to the current active window of the process and its parent windows. by sending WM_CLOSE you also could close a message box like 'do you really want to close?' and have an orderly termination. the TerminateProcess i only would do as a last resort after a reasonable timeout.

Sara
TimFHayesAuthor Commented:
Generally a roundabout way to work with MS Access from Delphi. Difficulty overcome was understanding that my service needed administrator privileges before it had the proper visibility.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.