2

I am working on an application written in Lisp. I am using Franz OLE to start the process. It open excel fine, but when I close excel interactively, the process still there. I am not sure how to kill the process when I close the excel window interactively. Any suggestion?

Thanks

Minh
  • 29
  • 1
  • 4
  • I have never written a real Lisp application but an automated Excel instance that does not close sounds familiar. In .NET there is often an automatically created object that's not releasing its COM reference. Maybe some thoughts from this question will help you: http://stackoverflow.com/questions/1041266/c-sharp-and-excel-automation-ending-the-running-instance? – Paul B. Jun 26 '12 at 10:56

1 Answers1

1

When you start Excel through automation, it's kept alive for as long as it is referenced 1. Closing it interactively doesn't necessarily exit the application, in this case because your ole:remote-autotool for Excel hasn't been released.

One way to let Excel exit is to get rid of the autotool wrappers, e.g. if you have one stored in a variable or in an object's slot, set its value to nil:

(let ((excel-app (ole:ask-for-autotool "Excel.Application"
                                       ole:CLSCTX_LOCAL_SERVER)))
  ;; ...
  ;; Break the reference to all autotool wrappers you no longer need
  (setf excel-app nil))

In Allegro CL's OLE Interface, the creation of an IUnknown client wrapper registers a finalization that releases the underlying interface pointer when it's garbage collected.

An autotool is yet another wrapper, which containts an IDispatch client wrapper (it inherits from IUnknown, as all COM interfaces), easing its use in the same way as scripting languages with what is known as late binding in COM automation. When you stop referring to the autotool, the underlying IDispatch/IUnknown client wrapper may be garbage collected, and thus released.

However, Excel might not quit promptly this way, unless you're expecting or forcing a garbage collection to happen 2. If you want Excel to exit immediately without interaction, you should Quit Excel with DisplayAlerts set to false and then ole:release your autotool objects (note: I haven't tested this thoroughly):

(let ((excel-app (ole:ask-for-autotool "Excel.Application"
                                       ole:CLSCTX_LOCAL_SERVER)))
  ;; ...
  ;; Disable prompts asking to save unsaved workbooks
  ;; They will not be saved
  (setf (ole:auto-getf excel-app "DisplayAlerts") nil)
  (ole:auto-method excel-app "Quit")
  ;; Manually release all autotool wrappers you no longer need
  (ole:release excel-app))

The scheduled finalization for the autotool won't fail after we release it manually. It checks if the underlying interface pointer wrapper has been released, probably by checking if its class is now ole::released-IUnknown 3, as it is documented to happen in the description of ole:release (ole:IUnknown-Client).

There's also a brute-force way using ole:release-process-client-interfaces, but you should make sure you won't access any OLE objects created so far in the current lisp process (thread):

(mp:process-run-function
 "Excel worker"
 #'(lambda ()
     (ole:start-ole)
     (unwind-protect
         (let ((excel-app (ole:ask-for-autotool "Excel.Application"
                                                 ole:CLSCTX_LOCAL_SERVER)))
           ;; ...
           ;; Disable prompts asking to save unsaved workbooks
           ;; They will not be saved
           (setf (ole:auto-getf excel-app "DisplayAlerts") nil)
           (ole:auto-method excel-app "Quit")
           ;; Make sure this is the last thing you do, the wrappers
           ;; created in the current process will become invalid
           (ole:release-process-client-interfaces))
       ;; Given this call, the brute force doesn't make much sense anyway
       (ole:stop-ole))))

1. As long as a lock to any registered factory is held or a reference to any object created by excel.exe is kept, it should keep running. This is normal for out-of-process servers.

2. If your autotool instance has been tenured, its generation needs to be garbage collected, often achievable with a global GC, to be finalized and thus released.

3. In Common Lisp, you can change the class of an object.

acelent
  • 7,965
  • 21
  • 39