Re: Hooking automation object Events




"Rob Kennedy" <me3@xxxxxxxxxxx> wrote in message
news:50lfndF1gq0pfU1@xxxxxxxxxxxxxxxxxxxxx
Paul E. Schoen wrote:
I have had a lot of success with my automation client program to do
periodic autosaves, but now I have added a feature that requires
interception and handling of Events, namely the Document.SelectionChange
Event. The Help shows that the prototype is: Document_SelectionChange().

[snip VB example]
The C++ version implements a "Sink". I could supply the code but it
probably doesn't translate well to Delphi.

C++ code frequently translates _very_ well into Delphi. You can be sure
that the VB code implements a sink, too. You just don't get to see it.

I tried using various ways to hook my event handler to the
SelectionChange event. I always got runtime errors that the method is
not supported by the object.

var
App1: Variant;
Doc1: Variant;

Can't you get any more specific type than Variant? You imported the type
library, right? When you did that, Delphi should have declared a bunch of
interfaces for you. Use those interfaces. Variant and OleVariant are for
when you have absolutely no idea what you're really working with. Using
more specific type names lets you (and the compiler) know what you're
trying to do.

Procedure TForm1.OnSelectionChange;
begin
Application.MessageBox('Selection Changed', 'PADS OLE', MB_OKCANCEL);
end;

App1 := GetActiveOleObject(Type1);
Doc1 := App1.ActiveDocument;

Doc1.Document_SelectionChange := OnSelectionChange;
Doc1.SelectionChange := OnSelectionChange;

I assume that's where you got the "method is not supported by the object"
message.

I tried using the (Delphi4) Automation Object Wizard, but I was unsure
of the Class Name, Instancing, and Threading Model.

I think that wizard is for when you have a class and you want to turn it
into a COM object. That's the opposite direction from what you're trying
to do.

I thought so. It didn't look right.


The PowerPCB_TLB has the following:

const
LIBID_PowerPCB: TGUID = '{0821EF00-B880-11CF-8268-0020AF937987}';
DIID__PowerPCBAppEvents: TGUID =
'{0821EF22-B880-11CF-8268-0020AF937987}';
DIID__PowerPCBDocEvents: TGUID =
'{0821EF24-B880-11CF-8268-0020AF937987}';
IID_IPowerPCBDoc: TGUID = '{0821EF04-B880-11CF-8268-0020AF937987}';
IID_IPowerPCBApp: TGUID = '{0821EF02-B880-11CF-8268-0020AF937987}';

Elsewhere in the file you should declarations for IPowerPCBDoc and
IPowerPCBApp interfaces. Does one of them have a property named
SelectionChange?

--
Rob

The next few lines of my post show the procedure SelectionChange:

type
_PowerPCBDocEvents = dispinterface;

_PowerPCBDocEvents = dispinterface
['{0821EF24-B880-11CF-8268-0020AF937987}']
procedure Save; dispid 11700;
procedure SelectionChange; dispid 11701;

I did not have good luck previously with using more specific type names.

private
App: IPowerPCBApp;

App := GetActiveOleObject('PowerPCB.Application');

Error: Incompatible Types. IPowerPCBApp and IDispatch

Then I tried:


private
App: IDispatch;

The APP assignment was OK, but

Doc := App.ActiveDocument;

Gave an error: Undeclared Identifier ActiveDocument

Following is the C++ Sink module:

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

// PowerPCBSink

// IMPLEMENT_DYNCREATE(PowerPCBSink, CCmdTarget)

PowerPCBSink::PowerPCBSink()
{
EnableAutomation();
}

PowerPCBSink::~PowerPCBSink()
{
}


void PowerPCBSink::OnFinalRelease()
{
// When the last reference for an automation object is released
// OnFinalRelease is called. The base class will automatically
// deletes the object. Add additional cleanup required for your
// object before calling the base class.

CCmdTarget::OnFinalRelease();
}


BEGIN_MESSAGE_MAP(PowerPCBSink, CCmdTarget)
//{{AFX_MSG_MAP(PowerPCBSink)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_DISPATCH_MAP(PowerPCBSink, CCmdTarget)
//{{AFX_DISPATCH_MAP(PowerPCBSink)
DISP_FUNCTION_ID(PowerPCBSink, "OnOpenDocument",
IPOWERPCBAPP_OPENDOCUMENTEVENT, OnOpenDocument, VT_EMPTY, VTS_DISPATCH)
DISP_FUNCTION_ID(PowerPCBSink, "OnSelectionChange",
IPOWERPCBDOC_SELECTIONCHANGEEVENT, OnSelectionChange, VT_EMPTY, VTS_NONE)
DISP_FUNCTION_ID(PowerPCBSink, "OnQuit", IPOWERPCBAPP_QUITEVENT, OnQuit,
VT_EMPTY, VTS_NONE)
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()

// Note: we add support for IID_IPowerPCBSink to support typesafe binding
// from VBA. This IID must match the GUID that is attached to the
// dispinterface in the .ODL file.

// {667FDFC1-D446-11D0-BCBF-444553540000}
static const IID IID_IPowerPCBSink =
{ 0x667fdfc1, 0xd446, 0x11d0, { 0xbc, 0xbf, 0x44, 0x45, 0x53, 0x54, 0x0,
0x0 } };

BEGIN_INTERFACE_MAP(PowerPCBSink, CCmdTarget)
INTERFACE_PART(PowerPCBSink, DIID__PowerPCBAppEvents, Dispatch)
INTERFACE_PART(PowerPCBSink, DIID__PowerPCBDocEvents, Dispatch)
END_INTERFACE_MAP()

/////////////////////////////////////////////////////////////////////////////
// PowerPCBSink message handlers

void PowerPCBSink::OnOpenDocument(LPDISPATCH pDoc)
{
((CClientDlg *)AfxGetMainWnd())->SetDlgItemText(IDC_TXT_STATUS, "Received
OnOpenDocument from server.");
((CClientDlg *)AfxGetMainWnd())->Refresh();
}

void PowerPCBSink::OnSelectionChange()
{
((CClientDlg *)AfxGetMainWnd())->SetDlgItemText(IDC_TXT_STATUS, "Received
OnSelectionChange from server.");
((CClientDlg *)AfxGetMainWnd())->Refresh();
}

void PowerPCBSink::OnQuit()
{
((CClientDlg *)AfxGetMainWnd())->SetDlgItemText(IDC_TXT_STATUS, "Received
OnQuit from server.");
((CClientDlg *)AfxGetMainWnd())->Disconnect();
((CClientDlg *)AfxGetMainWnd())->Refresh();
}

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

If you can tell me how to convert that to Delphi I would be most grateful.
I would probably spend hours trying all sorts of wrong things. I am still
having a hard time wrapping my brain around OOP.

Thanks,

Paul





.



Relevant Pages

  • Re: inhibit compiler warning C4624 for a class hierarchy
    ... I'd really like to just pretend that the dynamic type is the base class. ... ** Carries a request or notification and any associated parameters. ... static void* operator new ... struct PNPEXPORT IConcurrentOperations::OpNotification abstract: public ...
    (microsoft.public.vc.language)
  • RE: Protected keyword
    ... Only the Employee class internally can see the base class RaiseEvent method. ... public void HoldBreath{ ... a child that inherits from it so as to be able to call the protected method ...
    (microsoft.public.dotnet.languages.csharp)
  • Threads
    ... base class and then at least 2 classes that extends Thread. ... void upDateScreen() ...
    (comp.lang.java.programmer)
  • Type of base pointer
    ... public override void Test() ... base class for B is A not A" ... I have a virtual function called Testin class A. Also i have class B ... As well base object doesn't have method OldTest(). ...
    (microsoft.public.dotnet.framework)