You are on page 1of 36

Sage 50 Accounting 2016

Software Development Kit


COM API User’s Guide

Event Handling
Table of Contents

About this User’s Guide ................................................................................................................................ 3

Events ............................................................................................................................................................ 3

Application Events .................................................................................................................................... 4

Purchase Events ........................................................................................................................................ 6

Payment Events ........................................................................................................................................ 7

Event Synchronization .................................................................................................................................. 8

Visual Basic 6.0 .................................................................................................................................... 10

Visual Basic .NET ................................................................................................................................. 12

Visual C++ ............................................................................................................................................ 14

Page 2
About this User’s Guide
This guide has been compiled for the use of the Sage 50 COM Interface by Third Party Sage 50 SDK
Partners. It has been designed to provide useful information regarding the objects, methods, and
enumerations of the Sage 50 SDK. Although the sample code in this user’s guide will execute without
error, it was not intended to be commercial quality. The code samples are coded in a style that is easily
understood by even the most novice programmer. Many of the code references in this document make
use of Microsoft Visual Studio. The Visual Studio Express IDE can be downloaded at
http://www.microsoft.com/express/Windows to evaluate the sample code provided.

Events
In order to facilitate tighter, better integration between Sage 50 Accounting and third party applications,
the product supports a series of events that are generated by user actions. These events provide third
party developer with the means to develop event handlers capable of extending the functionality of the
product.

For example, a third party developer may wish to create an add-in that waits for checks to be written.
They could write an object that registers with Sage 50 Accounting for Payment events, extracts the
vendor information when a payment is generated, and then e-mails a notice indicating that a payment
has been made. All of this can be developed to work as a seamless extension of the payment screen in
Sage 50 Accounting.

To handle an event, a third party application must create a COM object that supports a Dispatch or Dual
interface with the exact same interface layout as that of the source synch. The interface cannot be a
custom (or pure VTBL) interface. The dispatch IDs of the interfaces must match up precisely. Then it
must register itself as an event synch handler with the event sink object. The developer can then write a
handler object that implements this interface.

The object can be implemented in any language that supports COM (Visual Basic, Delphi, Visual C++ with
MFC or ATL, etc). Event Synchronization section under application objects demonstrates the use of
event handlers in Visual Basic 6.0 and Visual C++ with ATL. Either of these should provide an effective
roadmap for adding event handling to a Sage 50 Partner application.

Page 3
Application Events
The Application Events interface allows the user to catch several important events. Some of these
events are used internally for components that are shipped with Sage 50 Accounting. Most developers
will find little if any benefit to using those. Some events are very useful and, in fact, third party
applications are required to handle the events.

Event Signature DISPID Description


OnShutdown() 1 This event is signaled when Sage 50 is about to shutdown.
Third Party applications are required to release all
outstanding references to Sage 50 Accounting COM objects
when this event is signaled.
OnChangeSystemDate(DATE 2 This event is signaled when the user changes the system
dtSystemDate) date.
OnCompanyClose() 4 This event is signaled when Sage 50 is about to close the
current company.

The actual layout of the application events interface (_IapplicationEvents) is shown in the following
table.

Page 4
//////////////////////////////////////////////////////////////////////////////
// Application Event Sink Connection Point Interface
//////////////////////////////////////////////////////////////////////////////
[
uuid(953EEE3C-9A80-4b63-9409-A9D93C654657),
helpstring("_IApplicationEvents Interface")
]

dispinterface _IApplicationEvents
{
properties:
methods:
[id(1), helpstring("method OnShutdown")]
HRESULT OnShutdown();

[id(2), helpstring("method OnChangeSystemDate")]


HRESULT OnChangeSystemDate(DATE dtSystemDate);

[id(3), helpstring("method OnModalRequest")]


HRESULT OnModalRequest(long lFlags);

[id(4), helpstring("method OnCompanyClose")]


HRESULT OnCompanyClose();

[id(5), helpstring("method OnImportPost")]


HRESULT OnImportPost(long lPostOrder,BSTR* pResponse);

[id(6), helpstring("method OnImportPostEx")]


HRESULT OnImportPostEx(long, lResultCode, VARIANT* pvarKey, BSTR* pResponse);

[id(7), helpstring("method OnNavigate")]


HRESULT OnNavigate(short nCommand);
};

Page 5
Purchase Events
The purchase events interface allows the user to handle instances where purchases are created,
updated or deleted through the user interface in Sage 50 Accounting. When one of these events is
signaled, a purchase event properties object is passed to the handler allowing it to obtain additional
information about the event.

Event Signature DISPID Description


OnCreate(LPDISPATCH pEventProperties) 1 When a purchase is created, this event is
signaled.
OnUpdate(LPDISPATCH pEventProperties) 2 When a purchase is updated, this event is
signaled.
OnDelete(LPDISPATCH pEventProperties) 3 When a purchase is deleted, this event is
signaled

The actual layout of the Purchase Events interface (-IpurchaseEventSinkEvents) is show below.

//////////////////////////////////////////////////////////////////////////////
// Purchase Event Sink Connection Point Interface
//////////////////////////////////////////////////////////////////////////////
[
uuid(9A95C9A0-B6CD-4F6A-8ABE-3ED1A93F4F13),
helpstring("_IPurchaseEventSinkEvents Interface")
]
dispinterface _IPurchaseEventSinkEvents
{
properties:
methods:
[id(1), helpstring("method OnCreate")]
HRESULT OnCreate([in] LPDISPATCH pEventProperties);

[id(2), helpstring("method OnUpdate")]


HRESULT OnUpdate([in] LPDISPATCH pEventProperties);

[id(3), helpstring("method OnDelete")]


HRESULT OnDelete([in] LPDISPATCH pEventProperties);
};

Page 6
Payment Events
The payment events interface allows developers to handle instances where payments are created,
updated, deleted or voided through the user interface in Sage 50 Accounting. When one of these events
is signaled, a payment event properties object is passed to the handler allowing it to obtain additional
information about the event.

Event Signature DISPID Description


OnCreate(LPDISPATCH pEventProperties) 1 This event is signaled when a purchase is
created.
OnUpdate(LPDISPATCH pEventProperties) 2 This event is signaled when a purchase is
updated.
OnDelete(LPDISPATCH pEventProperties) 3 This event is signaled when a purchase is
deleted.
OnVoid(LPDISPATCH pEventProperties) 4 This event is signaled when a purchase is
voided.

The actual layout of the Payment Events interface (_IpaymentEventSinkEvents) is shown below.

//////////////////////////////////////////////////////////////////////////////
// Payment Event Sink Connection Point Interface
//////////////////////////////////////////////////////////////////////////////
[
uuid(CD108F08-69AF-431A-812D-6C0DF2674176),
helpstring("_IPaymentEventSinkEvents Interface")
]
dispinterface _IPaymentEventSinkEvents
{
properties:
methods:
[id(1), helpstring("method OnCreate")]
HRESULT OnCreate([in] LPDISPATCH pEventProperties);

[id(2), helpstring("method OnUpdate")]


HRESULT OnUpdate([in] LPDISPATCH pEventProperties);

[id(3), helpstring("method OnDelete")]


HRESULT OnDelete([in] LPDISPATCH pEventProperties);

[id(4), helpstring("method OnVoid")]


HRESULT OnVoid([in] LPDISPATCH pEventProperties);
};

Page 7
Event Synchronization
As developers will see, it is very simple to create Third Party applications for Sage 50 Accounting using
Visual Basic. In the example shown in this section, a developer has created a COM object in Visual Basic
to handle Purchase and Payment events. This COM object uses the IPeachtreeAddIn COM Component
Category, which allows it to be launched when Sage 50 first starts up.

Since Visual Basic does not support any extension of the self-registration facilities (as Visual C++ does),
developers who implement components here will need to modify their install programs to add a special
registry key. First, they would obtain the CLSID of the COM object that implements the IPeachtreeAddIn
interface. Then, they would add a key similar to the one show below (replacing the <OBCLID> text with
the CLSID of their COM object). This would ensure that the new COM object would be in the
enumerated list of objects that implement the IPeachtreeAddIn interface (which are basically the Sage
50 Add-In COM Components).

// Version 10 Add In
[HKEY_CLASSES_ROOT\CLSID\ <OBCLID>\Implemented Categories\{59CEE40B-63B9-454b-8915-6074563632DA}]

// Version 11 Add In
[HKEY_CLASSES_ROOT\CLSID\ <OBCLID>\Implemented Categories{78F9241F-78EC-4EA9-BF6A-0C72079D1109}]

// Version 12 Add In
[HKEY_CLASSES_ROOT\CLSID\ <OBCLID>\Implemented Categories\DA8A560A-2A5C-4c80-944E-2D56AA270E31]

// Version 13 Add In
[HKEY_CLASSES_ROOT\CLSID\ <OBCLID>\Implemented Categories\{DA8A560A-2A5C-4C80-944E-2D56AA270E31}]

// Same Through Sage 50 Accounting 2016

The following code illustrates how to use the Third Party SDK that ships with Sage 50 Accounting to
capture Purchase and Payment events.

Since this object implements the IPeachtreeAddIn interface, the code contains get methods for the
Author, Name and Description properties. In addition, it contains implementations of the Start and Stop
methods as defined by the IPeachtreeAddIn interface. The Start and Stop methods serve as the
Initialization/cleanup methods for the component. A Login object is passed in the Start method that
should be used to obtain an Application object. The Application object can be cached until the Stop
method is called.

In order to create handler functions for events, a developer should create a public/global variable and
use the WithEvents keyword in the declaration. By doing this, whenever a value is assigned to the
variable, Visual Basic will automatically execute the Advise/Un-advise code behind the scenes to tie the
Connection Point interface to the event synchronization.

Page 8
Once these variables are declared, developers will see the objects in the code browser drop downs and
can select any of the events to write code for which they want to write handler code. For instance, after
declaring the following line:

Public WithEvents PurchaseSink as PeachtreeAccounting.PurchaseEventSink

The developer can add the following handler to catch “Create Purchase” events:

Private Sub PurchSink_OnCreate(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo pEventProperties, “created”
End Sub

In the Start method, the Purchase and Payment sinks are retrieved using the Application object and the
EventSinkManager Object. These synchs are assigned to our synch variables, which were declared,
establishing the connection between Sage 50 and the Third Party Application.

Each of the event handler functions receives an event properties object. This object contains a variety
of read-only properties that give additional information about the event. This information can get used
with other parts of the SDK (such as the Import and Export objects) to perform additional tasks.

When the Stop method is called by Sage 50, all references to Sage 50 Accounting objects which have
been cached should be released.

Page 9
Visual Basic 6.0
'//****************************************************************************
'// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
'// This material is proprietary to Sage Software, Inc. and may not be
'// used, distributed or reproduced in any form or by an means without
'// written expression from Sage Software, Inc. Any use, distribution
'// or reproduction in any form or by any means without written permission
'// from Sage Software, Inc. is a violation of federal copyright laws
'// and international treaties.
'//***************************************************************************

Implements IPeachtreeAddIn
Public ESM As EventSinkManager
Public WithEvents App As PeachtreeAccounting.Application
Public WithEvents PaySink As PeachtreeAccounting.PaymentEventSink
Public WithEvents PurchSink As PeachtreeAccounting.PurchaseEventSink

Private Property Get IPeachtreeAddIn_Author() As String


IPeachtreeAddIn_Author = "Sage Software"
End Property

Private Property Get IPeachtreeAddIn_Description() As String


IPeachtreeAddIn_Description = "This is a sample Add In implemented in Visual Basic."
End Property

Private Property Get IPeachtreeAddIn_Name() As String


IPeachtreeAddIn_Name = "VBSampleAddIn"
End Property

Private Sub IPeachtreeAddIn_Start(ByVal pUnk As Object)


Dim Login As PeachtreeAccounting.Login

MsgBox("VBSampleAddIn:Start()", vbOKOnly, "VBSampleAddIn")


Login = pUnk
Set App = Login.GetApplication("<Your Sage 50 Partner Name>”,
"<Your Sage 50 Partner Key>”)
ESM = App.GetEventSinkManager
PaySink = ESM.GetPaymentEventSink
PurchSink = ESM.GetPurchaseEventSink
End Sub

Private Sub IPeachtreeAddIn_Stop(ByVal pUnk As Object)


pUnk = Nothing
PaySink = Nothing
PurchSink = Nothing
App = Nothing
ESM = Nothing
MsgBox("VBSampleAddIn:Stop()", vbOKOnly, "VBSampleAddIn")
End Sub

Private Sub PaySink_OnCreate(ByVal pEventProperties As Object)


DisplayPaymentEventInfo(pEventProperties, "created")
End Sub

Private Sub PaySink_OnDelete(ByVal pEventProperties As Object)


DisplayPaymentEventInfo(pEventProperties, "deleted")
End Sub

Page 10
Private Sub PaySink_OnUpdate(ByVal pEventProperties As Object)
DisplayPaymentEventInfo(pEventProperties, "updated")
End Sub

Private Sub PaySink_OnVoid(ByVal pEventProperties As Object)


DisplayPaymentEventInfo(pEventProperties, "voided")
End Sub

Private Sub PurchSink_OnCreate(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo(pEventProperties, "created")
End Sub

Private Sub PurchSink_OnDelete(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo(pEventProperties, "deleted")
End Sub

Private Sub PurchSink_OnUpdate(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo(pEventProperties, "updated")
End Sub

Private Sub DisplayPurchaseEventInfo(ByVal pEventProperties As Object,


ObjectAction As String)

Dim Message As String


Dim Props As IPurchaseEventProperties

Props = pEventProperties

Message = "A purchase for Vendor ID: " + Props.VendorID + " on " +
CStr(Props.TransactionDate) + " with reference number '" +
Props.ReferenceNumber(+"' was " + ObjectAction + ".")

MsgBox(Message, vbOKOnly, "VBSampleAddIn")


End Sub

Private Sub DisplayPaymentEventInfo(ByVal pEventProperties As Object,


ObjectAction As String)

Dim Message As String


Dim Props As IPaymentEventProperties
Dim PayeeType As String

Props = pEventProperties

If (Props.PayeeType = peachwPayeeTypeVendor) Then


PayeeType = "Vendor"
Message = "A payment for " + PayeeType + " " + Props.PayeeID + " on " +
CStr(Props.TransactionDate) + " with reference number '" +
Props.ReferenceNumber(+"' was " + ObjectAction + ".")

MsgBox(Message, vbOKOnly, "VBSampleAddIn")


End If
End Sub

Page 11
Visual Basic .NET
'//****************************************************************************
'// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
'// This material is proprietary to Sage Software, Inc. and may not be
'// used, distributed or reproduced in any form or by an means without
'// written expression from Sage Software, Inc. Any use, distribution
'// or reproduction in any form or by any means without written permission
'// from Sage Software, Inc. is a violation of federal copyright laws
'// and international treaties.
'//****************************************************************************

Implements PeachtreeAccounting.IPeachtreeAddIn
Public ESM As PeachtreeAccounting.EventSinkManager
Public WithEvents App As PeachtreeAccounting.Application
Public WithEvents PaySink As PeachtreeAccounting.PaymentEventSink
Public WithEvents PurchSink As PeachtreeAccounting.PurchaseEventSink

Private ReadOnly Property IPeachtreeAddIn_Author() As String Implements


PeachtreeAccounting.IPeachtreeAddIn.Author
Get
IPeachtreeAddIn_Author = "Sage Software"
End Get
End Property

Private ReadOnly Property IPeachtreeAddIn_Description() As String Implements


PeachtreeAccounting.IPeachtreeAddIn.Description
Get
IPeachtreeAddIn_Description = "This is a sample Add In implemented in Visual Basic."
End Get

End Property

Private ReadOnly Property IPeachtreeAddIn_Name() As String Implements


PeachtreeAccounting.IPeachtreeAddIn.Name
Get
IPeachtreeAddIn_Name = "VBSampleAddIn"
End Get
End Property

Public Sub IPeachtreeAddIn_Start(ByVal pUnk As Object) Implements IPeachtreeAddIn.Start


Dim Login As PeachtreeAccounting.Login

MsgBox("VBSampleAddIn:Start()", vbOKOnly, "VBSampleAddIn")


Login = pUnk
App = Login.GetApplication("<Company Name>", "<Password>")
ESM = App.GetEventSinkManager
PaySink = ESM.GetPaymentEventSink
PurchSink = ESM.GetPurchaseEventSink
End Sub

Public Sub IPeachtreeAddIn_Stop(ByVal pUnk As Object) Implements IPeachtreeAddIn.Stop


pUnk = Nothing
PaySink = Nothing
PurchSink = Nothing
App = Nothing
ESM = Nothing
MsgBox("VBSampleAddIn:Stop()", vbOKOnly, "VBSampleAddIn")
End Sub

Private Sub PaySink_OnCreate(ByVal pEventProperties As Object)

Page 12
DisplayPaymentEventInfo(pEventProperties, "created")
End Sub

Private Sub PaySink_OnDelete(ByVal pEventProperties As Object)


DisplayPaymentEventInfo(pEventProperties, "deleted")
End Sub

Private Sub PaySink_OnUpdate(ByVal pEventProperties As Object)


DisplayPaymentEventInfo(pEventProperties, "updated")
End Sub

Private Sub PaySink_OnVoid(ByVal pEventProperties As Object)


DisplayPaymentEventInfo(pEventProperties, "voided")
End Sub

Private Sub PurchSink_OnCreate(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo(pEventProperties, "created")
End Sub

Private Sub PurchSink_OnDelete(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo(pEventProperties, "deleted")
End Sub

Private Sub PurchSink_OnUpdate(ByVal pEventProperties As Object)


DisplayPurchaseEventInfo(pEventProperties, "updated")
End Sub

Private Sub DisplayPurchaseEventInfo(ByVal pEventProperties As Object, _


ByVal ObjectAction As String)

Dim Message As String


Dim Props As IPurchaseEventProperties

Props = pEventProperties

Message = "A purchase for Vendor ID: " + Props.VendorID + " on " + _
CStr(Props.TransactionDate) + " with reference number '" + _
Props.ReferenceNumber + "' was " + ObjectAction + "."

MsgBox(Message, vbOKOnly, "VBSampleAddIn")


End Sub

Private Sub DisplayPaymentEventInfo(ByVal pEventProperties As Object, _


ByVal ObjectAction As String)

Dim Message As String


Dim Props As IPaymentEventProperties
Dim PayeeType As String

Props = pEventProperties

If (Props.PayeeType = PeachwPayeeType.peachwPayeeTypeVendor) Then


PayeeType = "Vendor"
Message = "A payment for " + PayeeType + " " + Props.PayeeID + " on " + _
CStr(Props.TransactionDate) + " with reference number '" + _
Props.ReferenceNumber + "' was " + ObjectAction + "."

MsgBox(Message, vbOKOnly, "VBSampleAddIn")


End If
End Sub

Page 13
Visual C++
Using Visual C++ is a little more complicated when developing a Third Party application with the Sage 50
Accounting. While Visual Basic handles many things for the developer, Visual C++ gives the developer a
greater degree of control (and with it, responsibility).

For instance, with Visual C++ a developer can extend the self-registration functionality of a COM
component and register itself as implementing the PeachtreeAddIn COM Component Category. This is
very beneficial and removes a dependency on the installer to set up the object for use correctly.

However, the code required to tie the connection point and an event synch take several more lines of
code and requires the developer to cache a “cookie” between calls to Advise and Un-advise.

The following modules show how to implement the same functionality as shown in Appendix A using
Visual C++.

Page 14
SampleAddIn.CPP
This module is very similar to the boilerplate code created by the ATL/COM Wizard. There are three
main changes. The first is the definition of the variable CATID_PeachtreeAddIn. This variable represents
the GUID that is used to identify PeachtreeAddIn COM components. This number will be used with all
Third Party applications that wish to be launched automatically by Sage 50.

The second change a developer will make is to the DllRegisterServer() function. After successfully calling
the _Module.register() function, this method will use the value in CATID_PeachtreeAddIn to register this
object as an implementer of that COM Component Category.

Finally, in the DllUnregisterServer() function, the entry listing this object as implementer of the Sage 50
COM Component Category is removed.

//****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//****************************************************************************

/****************************************************************************/
/* File name: SampleAddIn.cpp */
/* Contains the code the implementation for the COM object DLL. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/

// Note: Proxy/Stub Information


// To build a separate proxy/stub DLL,
// run nmake -f SampleAddInps.mk in the project directory.

#include "stdafx.h"
#include "resource.h"
#include <initguid.h>
#include "SampleAddIn.h"

#include "SampleAddIn_i.c"
#include "PAWSampleAddIn.h"

CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_PAWSampleAddIn, CPAWSampleAddIn)
END_OBJECT_MAP()

#ifdef __cplusplus
extern "C"{
#endif

Page 15
// Registry format {78F9241F-78EC-4ea9-BF6A-0C72079D1109}
GUID CATID_PeachtreeAddIn = { 0x78f9241f, 0x78ec, 0x4ea9,
{ 0xbf, 0x6a, 0xc, 0x72, 0x7, 0x9d, 0x11, 0x9 } };

#ifdef __cplusplus
}
#endif

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance, &LIBID_SAMPLEADDINLib);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE; // ok
}

STDAPI DllCanUnloadNow(void)
{
return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)


{
return _Module.GetClassObject(rclsid, riid, ppv);
}

STDAPI DllRegisterServer(void)
{
HRESULT hr = _Module.RegisterServer(TRUE);

CComPtr<ICatRegister> spCatReg;
SUCCEEDED(spCatReg.CoCreateInstance(CLSID_StdComponentCategoriesMgr));

if ( spCatReg )
{
CATEGORYINFO ci;
ci.catid = CATID_PeachtreeAddIn;
ci.lcid = 0x409;
wcscpy(ci.szDescription,OLESTR("Sage 50 Accounting COM Add In"));

spCatReg->RegisterCategories(1,&ci);

spCatReg->RegisterClassImplCategories(CLSID_PAWSampleAddIn,1,
&CATID_PeachtreeAddIn );
}

// registers object, typelib and all interfaces in typelib


return hr;
}

STDAPI DllUnregisterServer(void)
{
CComPtr<ICatRegister> spCatReg;

Page 16
SUCCEEDED(spCatReg.CoCreateInstance(CLSID_StdComponentCategoriesMgr));

if ( spCatReg )
{
spCatReg->UnRegisterCategories(1,&CATID_PeachtreeAddIn);
spCatReg->UnRegisterClassImplCategories(CLSID_PAWSampleAddIn,1, &CATID_PeachtreeAddIn );
}

return _Module.UnregisterServer(TRUE);
}

Page 17
PAWSampleAddIn.CPP
This module contains the code that implements the IPeachtreeAddIn interface. There are three get
methods for the properties Name, Author and Description. In addition, there are two methods for Start
and Stop. Start and Stop serve as the initialization/cleanup methods of the component.

When Start is called here, the Login object that is passed in as a parameter is used to obtain an
Application object. If this fails, Start should return a failure condition (i.e. E_FAIL, E_ACCESSDENIED,
etc). If the Application object is obtained, then the component will try to get the EventSinkManager
object and then both the PurchaseEventSink and PaymentEventSink objects.

Then the Start method will try to create Purchase and Payment event handlers. These objects
implement the connection point interfaces for Purchase and Payment events. When these objects are
created, calls will be made to Advise to tie the event handlers to the synch objects. Advise returns a
DWORD value in a cookie. This value must be cached for a future call to Un-advise. Once these objects
have been tied together, the Start method returns successfully.

When the Stop method is called, if the cookies were cached, calls are made to the Un-advise. This
breaks the link between the event handler and the event synch. The event handler objects can then be
destroyed.

//*****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//*****************************************************************************

/****************************************************************************/
/* File name: PAWSampleAddIn.cpp */
/* Contains the code the implementation of a sample Sage 50 Add In */
/* written in C++/ATL. This class implements the IPeachtreeAddIn */
/* interface. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/

#include "stdafx.h"
#include "SampleAddIn.h"
#include "PAWSampleAddIn.h"
#include "PurchaseEventHandler.h"
#include "PaymentEventHandler.h"

//////////////////////////////////////////////////////////////////////////////
// InterfaceSupportsErrorInfo
//
// This method is used to determine if a specific interface supports
// the error info object.

Page 18
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPAWSampleAddIn::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&__uuidof(IPeachtreeAddIn)
};
for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

//////////////////////////////////////////////////////////////////////////////
// Start
// This is the implementation of the Start method as defined by IPeachtreeAddIn.
// Sage 50 Accounting calls this method on all COM objects that have registered
// under the ImplementationCategory for the CATID listed above. This method is
// called as Sage 50 starts up. Every add in should use this as their
// initialization function. The dispatch interface that is passed in is the
// Login object for Sage 50. This function should use the Login object to pass
// in the 3rd party authentication credentials and retereive an Application object.
//
// In the sample below, the Application object is retreived. Using the application
// object, the Event Sink Manager is retreived. Using the Event Sink Manager, the
// Purchase and Payment Event Sinks are retreived. Then, Purchase and Payment
// Event handlers (which implement the Connection Point interfaces) are created and
// registered with their respective sinks through AtlAdvise.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPAWSampleAddIn::Start(LPDISPATCH pUnk)
{
CComPtr<IDispatch> spIDispApplication;

// Create a login object


CComPtr<ILogin> spLogin;
spLogin.CoCreateInstance(__uuidof(Login));

// Call the GetApplication method to authorize


// credentials, and get back the IDispatch interface for
// the application object.
HRESULT hr = spLogin->GetApplication(CComBSTR("<Your Sage 50 Partner Name>”),
CComBSTR("<Your Sage 50 Partner Key>”),
&spIDispApplication );

if ( FAILED( hr ) )
{
return hr;
}

CComQIPtr<IApplication, &__uuidof(IApplication)> spIApplication;

// Get the IApplication interface (easier to call methods)


if ( spIDispApplication.p )
{
spIApplication = spIDispApplication;
}

CComPtr<IDispatch> spIDispEventSinkManager;

Page 19
// Using the IApplication interface, retreive the IDispatch interface
// for the Event Sink Manager.
if ( spIApplication.p )
{
spIApplication->GetEventSinkManager(&spIDispEventSinkManager);
}

CComQIPtr<IEventSinkManager, &__uuidof(IEventSinkManager)> spIEventSinkManager;

// Get the IEventSinkManager interface (easier to call methods)


if ( spIDispEventSinkManager )
{
spIEventSinkManager = spIDispEventSinkManager;
}

CComPtr<IDispatch> spIDispPurchaseEventSink;
CComPtr<IDispatch> spIDispPaymentEventSink;

// Retreive the IDispatch interface for the Purchase and Payment Event Sink objects
if ( spIDispEventSinkManager )
{
spIEventSinkManager->GetPurchaseEventSink(&spIDispPurchaseEventSink);
spIEventSinkManager->GetPaymentEventSink(&spIDispPaymentEventSink);
}

// Get the IUnknown interface for the Purchase Event Sink


// (for use in calling AtlAdvise)
if ( spIDispPurchaseEventSink )
{
spIDispPurchaseEventSink->QueryInterface(IID_IUnknown,
(LPVOID*)&m_pIUnkPurchaseSink);
}

// If it has not already been created


if ( m_pPurchaseHandler == NULL )
{
// Create the object that sinks our events
CComObject<CPurchaseEventHandler>::CreateInstance(&m_pPurchaseHandler);
m_pPurchaseHandler->AddRef();
}

if ( m_pPurchaseHandler )
{
// Get the IUnknown interface for the object (for use in calling AtlAdvise)
CComPtr<IUnknown> spIUnkPurchaseHandler;
m_pPurchaseHandler->QueryInterface(IID_IUnknown,
(LPVOID*) &spIUnkPurchaseHandler);

// Add this object to sink map, and store the cookie to be


// used for the AtlUnadvise call.
AtlAdvise(m_pIUnkPurchaseSink, spIUnkPurchaseHandler,
__uuidof(_IPurchaseEventSinkEvents), &m_dwCookiePurchase );
}

// Get the IUnknown interface for the Purchase Event Sink


// (for use in calling AtlAdvise)
if ( spIDispPaymentEventSink )
{
spIDispPaymentEventSink->QueryInterface(IID_IUnknown,
(LPVOID*)&m_pIUnkPaymentSink);

Page 20
}

// If it has not already been created


if ( m_pPaymentHandler == NULL )
{
// Create the object that sinks our events
CComObject<CPaymentEventHandler>::CreateInstance(&m_pPaymentHandler);
m_pPaymentHandler->AddRef();
}

if ( m_pPaymentHandler )
{
// Get the IUnknown interface for the object (for use in calling AtlAdvise)
CComPtr<IUnknown> spIUnkPaymentHandler;
m_pPaymentHandler->QueryInterface(IID_IUnknown, (LPVOID*) &spIUnkPaymentHandler);

// Add this object to sink map, and store the cookie


// to be used for the AtlUnadvise call.
AtlAdvise(m_pIUnkPaymentSink, spIUnkPaymentHandler,
__uuidof(_IPaymentEventSinkEvents), &m_dwCookiePayment );
}

::MessageBox(NULL,_T("CPAWSampleAddIn::Start() was called."),


_T("Sample Add In"),MB_OK);

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// Stop
// This is the implementation of the Stop() method as defined by IPeachtreeAddIn.
// Sage 50 Accounting calls this method on all COM objects that have were created
// by its Add In Manager. These objects were created when Sage 50 started and the
// Start() methods were called on them.
//
// In the sample below, the event handlers are unregistered from the sinks by passing
// the cached cookies to AtlUnadvise.
//////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CPAWSampleAddIn::Stop(LPDISPATCH pUnk)


{
// If the cookie was set during the Start call...
if ( m_dwCookiePurchase )
{
// Remove the Purchase Event Handler from Purchase Event Sink map.
AtlUnadvise( m_pIUnkPurchaseSink, __uuidof(_IPurchaseEventSinkEvents),
m_dwCookiePurchase );
m_pIUnkPurchaseSink->Release();
}

// If the handler object was created, destroy it.


if ( m_pPurchaseHandler )
{
delete m_pPurchaseHandler;
}

// If the cookie was set during the Start call...


if ( m_dwCookiePayment )
{
// Remove the Payment Event Handler from Purchase Event Sink map.
AtlUnadvise( m_pIUnkPaymentSink, __uuidof(_IPaymentEventSinkEvents),
m_dwCookiePayment );

Page 21
m_pIUnkPaymentSink->Release();
}

// If the handler object was created, destroy it.


if ( m_pPaymentHandler )
{
delete m_pPaymentHandler;
}

::MessageBox(NULL,_T("CPAWSampleAddIn::Stop() was called."),


_T("Sample Add In"),MB_OK);

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// get_Name
// Accessor method for the Name property of the IPeachtreeAddIn interface.
// This is the friendly name of the Add In Component which will be displayed
// in a list of Add Ins to the user.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPAWSampleAddIn::get_Name(BSTR* pbstrName)
{
CComBSTR bstrName("SampleAddIn");

*pbstrName = bstrName.Copy();

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// get_Description
// Accessor method for the Description property of the IPeachtreeAddIn interface.
// This is the desciption of the Add In Component which will be displayed
// in a list of Add Ins to the user.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPAWSampleAddIn::get_Description(BSTR* pbstrDescription)
{
CComBSTR bstrDescription("This is a sample Add In implemented in C++.");

*pbstrDescription = bstrDescription.Copy();

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// get_Author
// Accessor method for the Author property of the IPeachtreeAddIn interface.
// This is the name or company name of the people who produced the
// the Add In Component. It which will be displayed in a list of Add Ins
// to the user.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPAWSampleAddIn::get_Author(BSTR* pbstrAuthor)
{
CComBSTR bstrAuthor("Sage Software");

*pbstrAuthor = bstrAuthor.Copy();

return S_OK;
}

Page 22
PAWSampleAddIn.h
//****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//****************************************************************************

/****************************************************************************/
/* File name: PAWSampleAddIn.h */
/* Contains the class definition of a sample Sage 50 Add In */
/* written in C++/ATL. This class implements the IPeachtreeAddIn */
/* interface. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/

#ifndef __PAWSAMPLEADDIN_H_
#define __PAWSAMPLEADDIN_H_
#include "resource.h" // main symbols

#import "..\..\..\Main Program\Peachw\Peachwserver.tlb" no_namespace raw_interfaces_only

class CPurchaseEventHandler;
class CPaymentEventHandler;

/////////////////////////////////////////////////////////////////////////////
// CPAWSampleAddIn
class ATL_NO_VTABLE CPAWSampleAddIn :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CPAWSampleAddIn, &CLSID_PAWSampleAddIn>,
public ISupportErrorInfo,
public IDispatchImpl<IPeachtreeAddIn, &__uuidof(IPeachtreeAddIn),
&LIBID_SAMPLEADDINLib>
{
protected:
CComObject<CPurchaseEventHandler>* m_pPurchaseHandler;
DWORD m_dwCookiePurchase;
IUnknown* m_pIUnkPurchaseSink;

CComObject<CPaymentEventHandler>* m_pPaymentHandler;
DWORD m_dwCookiePayment;
IUnknown* m_pIUnkPaymentSink;

public:
CPAWSampleAddIn()
{
m_pPurchaseHandler = NULL;
m_dwCookiePurchase = 0;

Page 23
m_pPaymentHandler = NULL;
m_dwCookiePayment = 0;
}

DECLARE_REGISTRY_RESOURCEID(IDR_PAWSAMPLEADDIN)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CPAWSampleAddIn)
COM_INTERFACE_ENTRY(IPeachtreeAddIn)
COM_INTERFACE_ENTRY(IDispatch)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IPAWSampleAddIn
STDMETHOD(get_Name)(BSTR* pbstrName);
STDMETHOD(get_Description)(BSTR* pbstrDescription);
STDMETHOD(get_Author)(BSTR* pbstrAuthor);
STDMETHOD(Start)(LPDISPATCH pLoginObject);
STDMETHOD(Stop)(LPDISPATCH pLoginObject);
public:
};

#endif //__PAWSAMPLEADDIN_H_

Page 24
PurchaseEventHandler.CPP
The Purchase Event Handler implements the _IPurchaseEventSinkEvents interface. Each of the event
methods receives a dispatch interface to a Purchase Event Properties object. This object can be queried
for additional event information, such as the Vendor ID, Reference Number and Transaction Date.

//****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//****************************************************************************

/****************************************************************************/
/* File name: PurchaseEventHandler.cpp */
/* Contains the code implementation of an event handler object that */
/* will be attached to the Purchase Event Sink. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/
#include "stdafx.h"
#include "SampleAddIn.h"
#include "PurchaseEventHandler.h"

//////////////////////////////////////////////////////////////////////////////
// InterfaceSupportsErrorInfo
//
// This method is used to determine if a specific interface supports
// the error info object.
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPurchaseEventHandler::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&__uuidof(_IPurchaseEventSinkEvents)
};
for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

//////////////////////////////////////////////////////////////////////////////
// OnCreate
// Event handler for the creation of Purchases.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPurchaseEventHandler::OnCreate(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("created") );

Page 25
return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// OnDelete
// Event handler for the deletion of Purchases.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPurchaseEventHandler::OnDelete(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("deleted") );

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// OnUpdate
// Event handler for the updating of Purchases.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPurchaseEventHandler::OnUpdate(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("updated") );

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// DisplayEventInfo
// This is a helper method which takes the dispatch interface for the
// Purchase Event properties object, and pulls the Vendor ID, Reference Number,
// and Transaction date and puts them in to a message string which is shown
// to the user.
//////////////////////////////////////////////////////////////////////////////

void CPurchaseEventHandler::DisplayEventInfo( LPDISPATCH pEventProperties,


LPCTSTR strAction )
{
IPurchaseEventProperties* pIEventProperties = NULL;

pEventProperties->QueryInterface(__uuidof(IPurchaseEventProperties),
(LPVOID*)&pIEventProperties);

if ( pIEventProperties )
{
BSTR bstrVendorID;
BSTR bstrReferenceNumber;
BSTR bstrTransactionDate;
DATE dtTransactionDate;

pIEventProperties->get_VendorID( &bstrVendorID );
pIEventProperties->get_ReferenceNumber( &bstrReferenceNumber );
pIEventProperties->get_TransactionDate( &dtTransactionDate );
VarBstrFromDate( dtTransactionDate, 1024, VAR_DATEVALUEONLY,
&bstrTransactionDate );

USES_CONVERSION;

LPCTSTR szVendorID = OLE2T( bstrVendorID );


LPCTSTR szReferenceNumber = OLE2T( bstrReferenceNumber );
LPCTSTR szTransactionDate = OLE2T( bstrTransactionDate );

Page 26
TCHAR buffer[1024];

_stprintf( buffer,
_T("A purchase for Vendor ID: %s on %s with reference number '%s' was %s."),
szVendorID, szTransactionDate, szReferenceNumber, strAction );

::MessageBox(NULL,buffer,_T("Sample Add In"),MB_OK);

pIEventProperties->Release();
}
}

Page 27
PurchaseEventHandler.h
//****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//****************************************************************************

/****************************************************************************/
/* File name: PurchaseEventHandler.h */
/* Contains the class declaration of an event handler object that */
/* will be attached to the Purchase Event Sink. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/

#ifndef __PURCHASEEVENTHANDLER_H_
#define __PURCHASEEVENTHANDLER_H_

#include "resource.h" // main symbols

#import "..\..\..\Main Program\Peachw\Peachwserver.tlb" no_namespace raw_interfaces_only

/////////////////////////////////////////////////////////////////////////////
// CPurchaseEventHandler
class ATL_NO_VTABLE CPurchaseEventHandler :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CPurchaseEventHandler, &CLSID_PurchaseEventHandler>,
public ISupportErrorInfo,
public IDispatchImpl<IPurchaseEventHandler, &__uuidof(IPurchaseEventHandler),
&LIBID_SAMPLEADDINLib>
{
public:
CPurchaseEventHandler() {};

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CPurchaseEventHandler)
COM_INTERFACE_ENTRY(IPurchaseEventHandler)
COM_INTERFACE_ENTRY_IID(__uuidof(_IPurchaseEventSinkEvents),IPurchaseEventHandler)
COM_INTERFACE_ENTRY2(IDispatch, _IPurchaseEventSinkEvents)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IPurchaseEventHandler
public:
STDMETHOD(OnCreate)(LPDISPATCH pEventProperties);
STDMETHOD(OnDelete)(LPDISPATCH pEventProperties);
STDMETHOD(OnUpdate)(LPDISPATCH pEventProperties);

Page 28
private:
void DisplayEventInfo( LPDISPATCH pEventProperties, LPCTSTR strAction );
};

#endif //__PURCHASEEVENTHANDLER_H_

Page 29
PaymentEventHandler.CPP
The PaymentEventHandler implements the _IPaymentEventSinkEvents interface. Each of the event
methods receives a dispatch interface to a Payment Event Properties object. This object can be queried
for additional event information such as the Payee ID, Reference Number and the Transaction Date.

//****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//****************************************************************************

/****************************************************************************/
/* File name: PaymentEventHandler.cpp */
/* Contains the code implementation of an event handler object that */
/* will be attached to the Payment Event Sink. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/
#include "stdafx.h"
#include "SampleAddIn.h"
#include "PaymentEventHandler.h"

//////////////////////////////////////////////////////////////////////////////
// InterfaceSupportsErrorInfo
//
// This method is used to determine if a specific interface supports
// the error info object.
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPaymentEventHandler::InterfaceSupportsErrorInfo(REFIID riid)
{
static const IID* arr[] =
{
&IID_IPaymentEventHandler
};
for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
if (InlineIsEqualGUID(*arr[i],riid))
return S_OK;
}
return S_FALSE;
}

//////////////////////////////////////////////////////////////////////////////
// OnCreate
// Event handler for the creation of Payments.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPaymentEventHandler::OnCreate(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("created") );

Page 30
return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// OnDelete
// Event handler for the deletion of Payments.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPaymentEventHandler::OnDelete(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("deleted") );

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// OnUpdate
// Event handler for the updating of Payments.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPaymentEventHandler::OnUpdate(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("updated") );

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// OnVoid
// Event handler for the voiding of Payments.
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPaymentEventHandler::OnVoid(LPDISPATCH pEventProperties)
{
DisplayEventInfo( pEventProperties, _T("voided") );

return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// DisplayEventInfo
// This is a helper method which takes the dispatch interface for the
// Payment Event properties object, and pulls the Vendor ID, Reference Number,
// and Transaction date and puts them in to a message string which is shown
// to the user.
//////////////////////////////////////////////////////////////////////////////
void CPaymentEventHandler::DisplayEventInfo( LPDISPATCH pEventProperties,
LPCTSTR strAction )
{
IPaymentEventProperties* pIEventProperties = NULL;

pEventProperties->QueryInterface(__uuidof(IPaymentEventProperties),
(LPVOID*) &pIEventProperties);

if ( pIEventProperties )
{
BSTR bstrPayeeID;
BSTR bstrReferenceNumber;
BSTR bstrTransactionDate;
DATE dtTransactionDate;
PeachwPayeeType ePayeeType;

Page 31
pIEventProperties->get_PayeeID( &bstrPayeeID );
pIEventProperties->get_ReferenceNumber( &bstrReferenceNumber );
pIEventProperties->get_TransactionDate( &dtTransactionDate );
VarBstrFromDate( dtTransactionDate, 1024, VAR_DATEVALUEONLY,
&bstrTransactionDate );

pIEventProperties->get_PayeeType( &ePayeeType );

USES_CONVERSION;

LPCTSTR szPayeeID = OLE2T( bstrPayeeID );


LPCTSTR szReferenceNumber = OLE2T( bstrReferenceNumber );
LPCTSTR szTransactionDate = OLE2T( bstrTransactionDate );
LPCTSTR szPayeeType;

if ( ePayeeType == peachwPayeeTypeVendor )
{
szPayeeType = _T("Vendor");

TCHAR buffer[1024];

_stprintf( buffer,
_T("A payment for %s ID: %s on %s with reference number '%s' was %s."),
szPayeeType, szPayeeID, szTransactionDate, szReferenceNumber, strAction );

::MessageBox(NULL,buffer,_T("Sample Add In"),MB_OK);


}

pIEventProperties->Release();
}
}

Page 32
PaymentEventHandler.h
//****************************************************************************
// (c) Copyright 2008 Sage Software, Inc. All rights reserved.
// This material is proprietary to Sage Software, Inc. and may not be
// used, distributed or reproduced in any form or by an means without
// written expression from Sage Software, Inc. Any use, distribution
// or reproduction in any form or by any means without written permission
// from Sage Software, Inc. is a violation of federal copyright laws
// and international treaties.
//****************************************************************************

/****************************************************************************/
/* File name: PurchaseEventHandler.h */
/* Contains the class declaration of an event handler object that */
/* will be attached to the Purchase Event Sink. */
/****************************************************************************/

/****************************************************************************/
/* included files */
/****************************************************************************/

#ifndef __PURCHASEEVENTHANDLER_H_
#define __PURCHASEEVENTHANDLER_H_

#include "resource.h" // main symbols

#import "..\..\..\Main Program\Peachw\Peachwserver.tlb" no_namespace raw_interfaces_only

/////////////////////////////////////////////////////////////////////////////
// CPurchaseEventHandler
class ATL_NO_VTABLE CPurchaseEventHandler :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CPurchaseEventHandler, &CLSID_PurchaseEventHandler>,
public ISupportErrorInfo,
public IDispatchImpl<IPurchaseEventHandler, &__uuidof(IPurchaseEventHandler),
&LIBID_SAMPLEADDINLib>
{
public:
CPurchaseEventHandler() {};

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CPurchaseEventHandler)
COM_INTERFACE_ENTRY(IPurchaseEventHandler)
COM_INTERFACE_ENTRY_IID(__uuidof(_IPurchaseEventSinkEvents),IPurchaseEventHandler)
COM_INTERFACE_ENTRY2(IDispatch, _IPurchaseEventSinkEvents)
COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

// ISupportsErrorInfo
STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IPurchaseEventHandler
public:
STDMETHOD(OnCreate)(LPDISPATCH pEventProperties);

Page 33
STDMETHOD(OnDelete)(LPDISPATCH pEventProperties);
STDMETHOD(OnUpdate)(LPDISPATCH pEventProperties);

private:
void DisplayEventInfo( LPDISPATCH pEventProperties, LPCTSTR strAction );
};

#endif //__PURCHASEEVENTHANDLER_H_

Page 34
SampleAddIn.IDL
This is the IDL for the Sample Add In written in C++. The Payment and Purchase event handler interfaces
should be the same as the connection point interfaces _IPaymentEventSinkEvents and
_IPurchaseEventSinkEvents. The dispatch Ids of the methods must match up exactly (because the synch
code is dispatching using the DISPID and not GetIDsOfNames()).

// SampleAddIn.IDL : IDL source for SampleAddIn.dll


//

// This file will be processed by the MIDL tool to


// produce the type library (SampleAddIn.tlb) and marshalling code.

import "oaidl.idl";
import "ocidl.idl";

[
object,
uuid(41FB3D7F-5256-4632-BFBA-D1041CABA260),
dual,
helpstring("IPaymentEventHandler Interface"),
pointer_default(unique)
]
interface IPaymentEventHandler : IDispatch
{
[id(1), helpstring("method OnCreate")]
HRESULT OnCreate([in] LPDISPATCH pEventProperties);
[id(2), helpstring("method OnUpdate")]
HRESULT OnUpdate([in] LPDISPATCH pEventProperties);
[id(3), helpstring("method OnDelete")]
HRESULT OnDelete([in] LPDISPATCH pEventProperties);
[id(4), helpstring("method OnVoid")]
HRESULT OnVoid([in] LPDISPATCH pEventProperties);
};

[
object,
uuid(D08ECC09-C360-43E7-92D3-C04A38BF39E8),
dual,
helpstring("IPurchaseEventHandler Interface"),
pointer_default(unique)
]
interface IPurchaseEventHandler : IDispatch
{
[id(1), helpstring("method OnCreate")]
HRESULT OnCreate([in] LPDISPATCH pEventProperties);
[id(2), helpstring("method OnUpdate")]
HRESULT OnUpdate([in] LPDISPATCH pEventProperties);
[id(3), helpstring("method OnDelete")]
HRESULT OnDelete([in] LPDISPATCH pEventProperties);
};

cpp_quote("EXTERN_C GUID CATID_PeachtreeAddIn;")

[
uuid(CD54B641-F846-473D-BD6F-F9D3223EE3FB),
version(1.0),

Page 35
helpstring("SampleAddIn 1.0 Type Library")
]
library SAMPLEADDINLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
importlib("..\..\..\Main Program\Peachw\Peachwserver.tlb");

[
uuid(9C68DC52-DA1E-45A1-966E-A1D9928F50B2),
helpstring("PAWSampleAddIn Class")
]
coclass PAWSampleAddIn
{
[default] interface IPeachtreeAddIn;
};

[
uuid(202B2F44-9187-4DEC-ABD9-B84A8A6A8E83),
helpstring("PaymentEventHandler Class"),
noncreatable
]
coclass PaymentEventHandler
{
[default] interface IPaymentEventHandler;
interface _IPaymentEventSinkEvents;
};

[
uuid(0997185F-6DFC-45E7-BCC2-DA1E04534EE8),
helpstring("PurchaseEventHandler Class"),
noncreatable
]
coclass PurchaseEventHandler
{
[default] interface IPurchaseEventHandler;
interface _IPurchaseEventSinkEvents;
};
};

Page 36

You might also like