Discussion:
Automation Client to Server connection
(too old to reply)
Paul E. Schoen
2006-12-23 06:38:10 UTC
Permalink
I posted something on this 6/30/06, but now I am revisiting it and I hope
someone can help.

I am making a client program which connects to an Automation Server of a
running application. I can make the connection, but it fails if I
disconnect and reconnect. Actually I have found that my application creates
another instance of the running application (as a process reported by
Windows Task Manager). I must close and reopen the running application
before my client can reconnect. The sample programs I have available are in
VB (in Excel) and C.

The applicable Delphi code is as follows:

=======================================================================
var
powerLogicApp: IPowerLogicApp;
powerLogicDoc: IPowerLogicDoc; // was Variant;

procedure Connect;
begin
if( powerLogicApp = nil ) then begin
powerLogicApp := CoApplication_.Create;
powerLogicDoc := powerLogicApp.ActiveDocument; end;
end;

procedure Disconnect;
begin
// powerLogicApp.Quit; // This closes the application
powerLogicApp := nil;
powerLogicDoc := nil;
end;
======================================================================

The visual basic code in Excel is as follows:

======================================================================
Dim WithEvents powerLogicApp As PowerLogic.Application
Dim WithEvents powerLogicDoc As PowerLogic.Document

Private Sub Connect()
On Error GoTo OnErrorGetObject ' Enable error-handling routine.
Set powerLogicApp = GetObject(, "PowerLogic.Application") ' Connect to
a running instance of PowerLogic server
Set powerLogicDoc = powerLogicApp.ActiveDocument
Exit Sub ' Exit to avoid handler.

Private Sub Disconnect()
Set powerLogicDoc = Nothing
Set powerLogicApp = Nothing ' Disconnect from PowerLogic server
End Sub

======================================================================

The VB routine seems to be able to connect to the running application even
when my Delphi routine no longer can. It does not help to exit and restart
the Delphi program. The Delphi help is confusing with Dispatch and Dual
interfaces, and using OleVariants.

The C examples use Windows function calls to access the registry and obtain
a CLSID:

======================================================================
// Get the CLSID from the PowerLogic name string ID (system registry
query)
hResult = ::CLSIDFromProgID(OLESTR("PowerLogic.Application"), &clsID);
// Try to get an active instance of PowerLogic. The ::GetActiveObject()
function
// will query the Running Object Table and search for a registered
PowerLogic
// instance currently running on the system. If the return value of
::GetActiveObject()
// is not S_OK, it means that the server is not running on the system at
this time.
hResult = ::GetActiveObject(clsID, NULL, &pUnknown);
// Get IDispatch interface of the PowerLogic application object.
LPDISPATCH pDispatch = NULL;
hResult = pUnknown->QueryInterface(IID_IDispatch, (void **) &pDispatch);
// Allocate the interface
m_pPowerLogicApplication = (IPowerLogicApp *) new IPowerLogicApp;

The code for disconnect:

// Disconnect sink
SetSink(FALSE);

// Delete the connection with PowerLogic by deleting it's reference.
delete m_pPowerLogicApplication;
m_pPowerLogicApplication = NULL;

======================================================================

I'm not very clear how the automation interface works, although it seems
fairly straightforward once I am connected. I am using this to implement a
periodic save function for the application, as its own backup routine is
not always reliable. I got this to work by using the system timer to call
the SAVE function in the application. I also had the application pop up a
message box to indicate the save, which I could do only by a call to
RunMacro, where the macro runs a VB script. Klunky but it works. Maybe it
would be better to use a system modal DialogBox within Delphi. If I can get
the connect/disconnect reliable, then I can add the bells and whistles.

Thanks, and Happy Holidays!

Paul
MikeB
2006-12-23 12:51:48 UTC
Permalink
GetActiveOleObject
Post by Paul E. Schoen
I posted something on this 6/30/06, but now I am revisiting it and I hope
someone can help.
I am making a client program which connects to an Automation Server of a
running application. I can make the connection, but it fails if I disconnect
and reconnect. Actually I have found that my application creates another
instance of the running application (as a process reported by Windows Task
Manager). I must close and reopen the running application before my client
can reconnect. The sample programs I have available are in VB (in Excel) and
C.
=======================================================================
var
powerLogicApp: IPowerLogicApp;
powerLogicDoc: IPowerLogicDoc; // was Variant;
procedure Connect;
begin
if( powerLogicApp = nil ) then begin
powerLogicApp := CoApplication_.Create;
powerLogicDoc := powerLogicApp.ActiveDocument; end;
end;
procedure Disconnect;
begin
// powerLogicApp.Quit; // This closes the application
powerLogicApp := nil;
powerLogicDoc := nil;
end;
======================================================================
======================================================================
Dim WithEvents powerLogicApp As PowerLogic.Application
Dim WithEvents powerLogicDoc As PowerLogic.Document
Private Sub Connect()
On Error GoTo OnErrorGetObject ' Enable error-handling routine.
Set powerLogicApp = GetObject(, "PowerLogic.Application") ' Connect to a
running instance of PowerLogic server
Set powerLogicDoc = powerLogicApp.ActiveDocument
Exit Sub ' Exit to avoid handler.
Private Sub Disconnect()
Set powerLogicDoc = Nothing
Set powerLogicApp = Nothing ' Disconnect from PowerLogic server
End Sub
======================================================================
The VB routine seems to be able to connect to the running application even
when my Delphi routine no longer can. It does not help to exit and restart
the Delphi program. The Delphi help is confusing with Dispatch and Dual
interfaces, and using OleVariants.
The C examples use Windows function calls to access the registry and obtain a
======================================================================
// Get the CLSID from the PowerLogic name string ID (system registry query)
hResult = ::CLSIDFromProgID(OLESTR("PowerLogic.Application"), &clsID);
// Try to get an active instance of PowerLogic. The ::GetActiveObject()
function
// will query the Running Object Table and search for a registered PowerLogic
// instance currently running on the system. If the return value of
::GetActiveObject()
// is not S_OK, it means that the server is not running on the system at this
time.
hResult = ::GetActiveObject(clsID, NULL, &pUnknown);
// Get IDispatch interface of the PowerLogic application object.
LPDISPATCH pDispatch = NULL;
hResult = pUnknown->QueryInterface(IID_IDispatch, (void **) &pDispatch);
// Allocate the interface
m_pPowerLogicApplication = (IPowerLogicApp *) new IPowerLogicApp;
// Disconnect sink
SetSink(FALSE);
// Delete the connection with PowerLogic by deleting it's reference.
delete m_pPowerLogicApplication;
m_pPowerLogicApplication = NULL;
======================================================================
I'm not very clear how the automation interface works, although it seems
fairly straightforward once I am connected. I am using this to implement a
periodic save function for the application, as its own backup routine is not
always reliable. I got this to work by using the system timer to call the
SAVE function in the application. I also had the application pop up a message
box to indicate the save, which I could do only by a call to RunMacro, where
the macro runs a VB script. Klunky but it works. Maybe it would be better to
use a system modal DialogBox within Delphi. If I can get the
connect/disconnect reliable, then I can add the bells and whistles.
Thanks, and Happy Holidays!
Paul
Paul E. Schoen
2006-12-24 02:48:34 UTC
Permalink
Post by MikeB
GetActiveOleObject
PowerLogicApp: IPowerLogicApp;

PowerLogicApp := GetActiveOleObject( 'Powerlogic.Application' );

I tried this, but it returns a type IDispatch rather than IPowerLogicApp.
If I do a cast, it will compile but I get runtime errors. If I set
PowerLogicApp to OLEVariant or IDispatch, it will not compile. I am using
'Powerlogic.Application' for the class string, and this seems to be
correct.

I could not find a PowerLogicApp.Destroy, and when I used
PowerLogicApp._Release I got an access violation.

Thanks,

Paul
Post by MikeB
Post by Paul E. Schoen
I posted something on this 6/30/06, but now I am revisiting it and I hope
someone can help.
I am making a client program which connects to an Automation Server of a
running application. I can make the connection, but it fails if I
disconnect and reconnect. Actually I have found that my application
creates another instance of the running application (as a process
reported by Windows Task Manager). I must close and reopen the running
application before my client can reconnect. The sample programs I have
available are in VB (in Excel) and C.
=======================================================================
var
powerLogicApp: IPowerLogicApp;
powerLogicDoc: IPowerLogicDoc; // was Variant;
procedure Connect;
begin
if( powerLogicApp = nil ) then begin
powerLogicApp := CoApplication_.Create;
powerLogicDoc := powerLogicApp.ActiveDocument; end;
end;
procedure Disconnect;
begin
// powerLogicApp.Quit; // This closes the application
powerLogicApp := nil;
powerLogicDoc := nil;
end;
======================================================================
======================================================================
Dim WithEvents powerLogicApp As PowerLogic.Application
Dim WithEvents powerLogicDoc As PowerLogic.Document
Private Sub Connect()
On Error GoTo OnErrorGetObject ' Enable error-handling routine.
Set powerLogicApp = GetObject(, "PowerLogic.Application") ' Connect
to a running instance of PowerLogic server
Set powerLogicDoc = powerLogicApp.ActiveDocument
Exit Sub ' Exit to avoid handler.
Private Sub Disconnect()
Set powerLogicDoc = Nothing
Set powerLogicApp = Nothing ' Disconnect from PowerLogic server
End Sub
======================================================================
The VB routine seems to be able to connect to the running application
even when my Delphi routine no longer can. It does not help to exit and
restart the Delphi program. The Delphi help is confusing with Dispatch
and Dual interfaces, and using OleVariants.
The C examples use Windows function calls to access the registry and
======================================================================
// Get the CLSID from the PowerLogic name string ID (system registry query)
hResult = ::CLSIDFromProgID(OLESTR("PowerLogic.Application"), &clsID);
// Try to get an active instance of PowerLogic. The ::GetActiveObject()
function
// will query the Running Object Table and search for a registered PowerLogic
// instance currently running on the system. If the return value of
::GetActiveObject()
// is not S_OK, it means that the server is not running on the system at
this time.
hResult = ::GetActiveObject(clsID, NULL, &pUnknown);
// Get IDispatch interface of the PowerLogic application object.
LPDISPATCH pDispatch = NULL;
hResult = pUnknown->QueryInterface(IID_IDispatch, (void **)
&pDispatch);
// Allocate the interface
m_pPowerLogicApplication = (IPowerLogicApp *) new IPowerLogicApp;
// Disconnect sink
SetSink(FALSE);
// Delete the connection with PowerLogic by deleting it's reference.
delete m_pPowerLogicApplication;
m_pPowerLogicApplication = NULL;
======================================================================
I'm not very clear how the automation interface works, although it seems
fairly straightforward once I am connected. I am using this to implement
a periodic save function for the application, as its own backup routine
is not always reliable. I got this to work by using the system timer to
call the SAVE function in the application. I also had the application
pop up a message box to indicate the save, which I could do only by a
call to RunMacro, where the macro runs a VB script. Klunky but it works.
Maybe it would be better to use a system modal DialogBox within Delphi.
If I can get the connect/disconnect reliable, then I can add the bells
and whistles.
Thanks, and Happy Holidays!
Paul
Paul E. Schoen
2006-12-24 04:31:00 UTC
Permalink
I found a way to do this successfully (so far):

var
powerLogicApp: Variant;
powerLogicDoc: Variant;

procedure Connect;
begin
if( VarType(powerLogicApp) = VarEmpty ) then begin
Try
powerLogicApp := GetActiveOleObject('PowerLogic.Application');
except
Application.MessageBox('Cannot connect', 'Ole server', MB_OK );
exit;
end;
powerLogicDoc := PowerLogicApp.ActiveDocument;
end;

procedure Disconnect;
begin
powerLogicApp := Unassigned;
powerLogicDoc := Unassigned;
end;

Thanks for the suggestions. Originally this would get a runtime error
because I was checking for:

if( PowerLogicApp) = Unassigned )

Even though the variant was indeed unassigned this operation raises an
error. I don't know the reasoning for this but the important thing is that
it works. Just took a bit of digging and trial and error to get it.

Happy Holidays!

Paul
a***@aol.com
2006-12-24 06:56:25 UTC
Permalink
You may find some useful tips on Deborah Pate's site at ...

http://www.djpate.freeserve.co.uk/Automation.htm

Alan Lloyd
MikeB
2006-12-24 16:10:57 UTC
Permalink
Yep.. In my lookup to find the Delphi Equivalent of VB GetObject, I ended up at
Debra's site...
Post by a***@aol.com
You may find some useful tips on Deborah Pate's site at ...
http://www.djpate.freeserve.co.uk/Automation.htm
Alan Lloyd
Maarten Wiltink
2006-12-24 17:49:03 UTC
Permalink
"Paul E. Schoen" <***@smart.net> wrote in message news:458e0327$0$4985$***@news.coretel.net...
[...]
Post by Paul E. Schoen
if( PowerLogicApp) = Unassigned )
Even though the variant was indeed unassigned this operation raises
an error.
It's a Computer Science (mark the capitals) thing. They have a fetish
for propagating invalid values. Every expression in which 'null' or
'invalid' or 'bottom' or 'unassigned' occurs anywhere will never
evaluate to anything else.

Read the help on it. It suggests a function that you can call on a
variant, and it will check for you if it is unassigned, returning a
Boolean.

Groetjes,
Maarten Wiltink
Paul E. Schoen
2006-12-24 18:55:00 UTC
Permalink
Post by Maarten Wiltink
[...]
Post by Paul E. Schoen
if( PowerLogicApp) = Unassigned )
Even though the variant was indeed unassigned this operation raises
an error.
It's a Computer Science (mark the capitals) thing. They have a fetish
for propagating invalid values. Every expression in which 'null' or
'invalid' or 'bottom' or 'unassigned' occurs anywhere will never
evaluate to anything else.
Read the help on it. It suggests a function that you can call on a
variant, and it will check for you if it is unassigned, returning a
Boolean.
Groetjes,
Maarten Wiltink
Yes, I think that is the VarType() function, which I used successfully to
detect VarEmpty. I had a similar problem once with a VB program, where I
think the variable was not explicitly a variant (although I think VB uses
that as a default since you do not need to declare variables before use).

I will need to read up on Automation. It is very useful. Fortunately the
apps to which I am interfacing have their functions listed and explained in
a fairly good help file. I suppose this is available for other apps, but is
there a way to determine what functions are available from the executable?

Also, for these applications, I would really like to be able to run a VB
script that is called from within. I had to use a "RunMacro" function which
runs the script, which merely pops up a MsgBox saying "Saving". This is
useful because it suspends further execution of the AutoSave program until
the user of the server application clicks OK. This could probably also be
done by a system modal dialog from the Delphi program, but I would rather
it be modal to the server app.

Thanks for the help.

Froehe Weinachten!

Paul
Maarten Wiltink
2006-12-25 14:27:28 UTC
Permalink
Post by Paul E. Schoen
Post by Paul E. Schoen
if( PowerLogicApp) = Unassigned )
[...]
Post by Paul E. Schoen
Read the help on [Unassigned]. It suggests a function that you can
call on a variant, and it will check for you if it is unassigned,
returning a Boolean.
Yes, I think that is the VarType() function, which I used successfully
to detect VarEmpty.
I was thinking more of the VarIsEmpty function. But it may not have been
clear from the text that I was referring to the help page on Unassigned.


[...]
Post by Paul E. Schoen
Froehe Weinachten!
Zalig en geloekkig Kerstfeest.

Groetjes,
Maarten Wiltink

Loading...