Search The ForumSearch   RegisterRegister  LoginLogin

MailBee Objects

 AfterLogic Forum : MailBee Objects
Subject Topic: sink for Post ReplyPost New Topic
Author
Message << Prev Topic | Next Topic >>
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 14 January 2007 at 6:42am | IP Logged Quote johan

Hi All,

I am using mailbee objects in powerbuilder and need to write a sink in c++ to get hold of the events triggered by mailbee objects as powerbuilder does not support com object event handling.
I tried to get the event interface for the SMTP object and code a sink for the SMTP events. The code compiles and seems to work fine until I call any function that triggers events like Send or SendEx...
I get "error callling external function .. R0035" (Powerbuilder error).
Without the sink everything works fine ,but then I do not have access to the events!

I am lost.
Any ideas ?

Regards
johan

Back to Top View johan's Profile Search for other posts by johan
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 14 January 2007 at 4:52pm | IP Logged Quote Alex

Actually, COM event sinks is very complicated thing and it's hard to tell what exactly causes the problem. To make the life easier, I developed a small wrapper which creates ActiveX control from a MailBee object. The original version of this wrapper supported IMAP4 object only, now I added POP3 and SMTP. You can get it from http://www.afterlogic.com/updates/mailbee_ctls.zip
You'll also need the most recent version of MailBee.dll

Re-register MailBee.dll and register MailBeeCtls.dll with regsrv32. Now you should be able to add it as a control to your application form and subscribe to the events using UI of your development environment (PowerBuilder allows subscribing to events of visual ActiveX controls). Every ActiveX control in MailBeeCtls library has writable property (SMTP, POP3, or IMAP4) which provides access to the corresponding object. You can attach your existing SMTP object like below (in VBScript syntax):
Code:

Set MySMTPControl.SMTP = MySMTPObject

Of course, these MailBee "visual" controls are not visible at runtime. They are design-time components.

Note to other readers. The same approach can be used to listen to MailBee events in other languages which can consume events of ActiveX controls only (but cannot consume generic COM events).

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 15 January 2007 at 6:11am | IP Logged Quote johan

Alex,

Thanks for the reply.
This will make my life a lot easier.
I appreciate the response.

Regards

johan
Back to Top View johan's Profile Search for other posts by johan
 
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 15 January 2007 at 2:24pm | IP Logged Quote johan

Hi Alex,

Tried to insert it as activex control in powerbuilder, but got error:
"OLE Object is missing critical interface 'IPersistStorage'.

I managed to get my sink working for the SMTP object. Events get triggered in my custom non-visual object in powerbuilder now through this sink. The only problem is that events with pointer parameters like bool* does not seem to send the modified pointer value back to the control even if you change it in the event. It just uses the default values. Are you able to send data back to control via the sink outgoing interface of the control ?
Any ideas ?

Regards

johan
Back to Top View johan's Profile Search for other posts by johan
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 15 January 2007 at 4:09pm | IP Logged Quote Alex

Quote:
"OLE Object is missing critical interface 'IPersistStorage'

Ah, seems powerbuilder requires ActiveX control be Full, not Lite control. I now added IPersistStorage and IDataStorage to the supported interfaces. The link to the update is the same.

Quote:
Are you able to send data back to control via the sink outgoing interface of the control?

Actually, I got the same problem. I think this is limitation of script-oriented languages (I found the same problem with javascript). Seems passing parameters by reference (not by value) is not always supported by host environments. However, there is a couple of workarounds available:
1) Why do you need to set Proceed to false? To abort things if desired? If yes, you can call POP3.Abort instead.
2) You can change event object model in your wrapper to somewhat like MailBee.NET event object model. I assume you have no problems when changing values of event parameters in C++ code (i.e. the value does not get updated when you attempt to change it in your wrapper ActiveX event handler in powerbuilder but you can change event parameter values of MailBee.SMTP object in C++ code of your wrapper). For instance, create OnSendProgressEventArgs class containing get/put Proceed property and pass a reference to the instance of this class as an event parameter. Powerbuilder event handler will simply set eventParam.Proceed=False, but the object reference eventParam will remain the same and lack of passing parameters by reference will not be a concern. When your C++ code then gets results from the host, it updates Proceed value of MailBee.SMTP event with eventParam.Proceed value.

Note: as mentioned above, I assume everything is fine on C++ side of things. However, it might not be so if you're using VC++ 6.0. By default, their ClassWizard "Implement Connection Point" feature incorrectly implements BOOL* (VT_BYREF is missing). For instance, it generates something like this:
Code:

HRESULT Fire_OnReceiveData(LONG BytesReceived, VARIANT_BOOL * Proceed)
{
     CComVariant varResult;
     T* pT = static_cast<T*>(this);
     int nConnectionIndex;
     CComVariant* pvars = new CComVariant[2];
     int nConnections = m_vec.GetSize();
     
     for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
     {
           pT->Lock();
           CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);
           pT->Unlock();
           IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
           if (pDispatch != NULL)
           {
                VariantClear(&varResult);
                pvars [1] = BytesReceived;
                pvars [0] = Proceed;
                DISPPARAMS  disp = { pvars, NULL, 2, 0 };
                pDispatch->Invoke(0x1,  IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
           }
     }
     delete[] pvars;
     return varResult.scode;
}


"pvars[0] = Proceed;" makes it ByVal (which is default). To make it ByRef, replace it with 2 lines:
Code:

pvars[0].vt = VT_BYREF|VT_BOOL;
pvars[0].pboolVal = Proceed;

Backup this change since you have to repeat it every time you ask wizard to implement connection point.

I hope this helps.

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 16 January 2007 at 2:41am | IP Logged Quote johan

Hi Alex,

Thanks for the reply.
I have tried to set the bproceed=false value directly on the c++ side ,but with the same (wrong) result.
I am using the PBNI (powerbuilder native interface) and VC2003 to write the sink.
I am not using a wizard.

My code looks like this:

MyEventSink.h
=============


class CEventSink : public CCmdTarget
{     
        DECLARE_DYNCREATE(CEventSink)

     // constructor
     CEventSink();    ;       

public:
     
     virtual void OnFinalRelease();

     // destructor
     virtual ~CEventSink();

     // public function
     void PassUserData ( LPARAM userData ) ;

protected:

     //
     DECLARE_MESSAGE_MAP()

     afx_msg HRESULT OnSendStart(bool* bProceed);     
     DECLARE_DISPATCH_MAP()
     DECLARE_INTERFACE_MAP()

     // User data structure
     struct UserData
     {
           IPB_Session*     session;
           pbobject     object;
     };

     // ref to user data structure
     UserData*   &nb sp; m_userdata ;

};



MyEventSink.cpp
===============


IMPLEMENT_DYNCREATE(CEventSink, CCmdTarget)

// constructor for class CEventSink
CEventSink::CEventSink()
{
     EnableAutomation();
}

// destructor for class CEventSink
CEventSink::~CEventSink()
{
}

// OLE function call
void CEventSink::OnFinalRelease()
{
     CCmdTarget::OnFinalRelease();
}

// function to set data
void CEventSink::PassUserData ( LPARAM userData )
{
     m_userdata = (UserData*)userData;
}

// Begins the definition of your message map.
BEGIN_MESSAGE_MAP(CEventSink, CCmdTarget)
END_MESSAGE_MAP()

// Declares the definition of your dispatch map.
BEGIN_DISPATCH_MAP(CEventSink, CCmdTarget)
     
     DISP_FUNCTION(CEventSink, "OnSendStart", OnSendStart, VT_I4, VTS_PBOOL)        &n bsp; 
END_DISPATCH_MAP()

// set GUID of interface

static const GUID IID_ICEventSink = {0x4CEFF81B,0x2B0F,0x4E6A,{0xB7,0x53,0x48,0xE2,0x34,0x82,0xF 0,0xFE}};

// Begins the definition of the interfaced map when used in the implementation file.
BEGIN_INTERFACE_MAP(CEventSink, CCmdTarget)
     INTERFACE_PART(CEventSink, IID_ICEventSink, Dispatch)
END_INTERFACE_MAP()

HRESULT CEventSink::OnSendStart(bool* bProceed)
{
        // omitted pbni specific code

     *bProceed = false;

     return 0;
}


powerbuilder specific code omitted.
I am not a OLE guru, but it seems to me that the bool* datatype in the sink is not able to handle and set the memory to the passed in pointer correctly so that the control have access to the modified value.
Should I change bool* to VARIANT_BOOL *? I do not think powerbuilder will like this ?
Any ideas?

Regards
johan
Back to Top View johan's Profile Search for other posts by johan
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 16 January 2007 at 9:50am | IP Logged Quote Alex

Actually, we never used VC++ 2003 so I'm not familiar with the macros used in your code and cannot tell for sure what exactly is wrong.

Anyway, using VARIANT_BOOL is recommended since it's standard ActiveX version of Boolean type. bool, BOOL, and VARIANT_BOOL are all different types because they define True differently (non-zero in bool, 1 in BOOL, and -1 (0xFFFFFFFF) in VARIANT_BOOL).

However, since you're assigning to zero (which has the same meaning in all boolean types), I'm not sure migrating to VARIANT_BOOL will help with your particular problem.

On other hand, there is yet another difference between VARIANT_BOOL and bool. bool is 1-byte length, VARIANT_BOOL is 4-byte length. In theory, the following scenario is possible: "*bProceed = false" succeeds and the memory gets updated but only 1 byte of 4. The other 3 bytes remain 0xFF and the entire 4 byte value is still non-zero. Thus, it's still worth trying to use VARIANT_BOOL, and it will not do any harm since using VARIANT_BOOL is more correct anyway.

Regards,
Alex

Back to Top View Alex's Profile Search for other posts by Alex
 
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 17 January 2007 at 1:03am | IP Logged Quote johan

Hi Alex,

Your response send me in the correct direction.
Changing bool to VARIANT_BOOL solved the problem.
Everything seems to work fine now.
Appreciate your feedback and responses.

Regards
johan
Back to Top View johan's Profile Search for other posts by johan
 
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 22 January 2007 at 10:07am | IP Logged Quote johan

Hi Alex,

The above implementation works 100% when I did it for the SMTP event interface.
I want to do the same now for POP3. The problem is that

m_lpDispatch->QueryInterface(IID_IConnectionPointContaine r,...)

ONLY returns the SMTP event interface GUID.
Enumerating through the list only returns the one interface. Any ideas ?

Regards
johan

Back to Top View johan's Profile Search for other posts by johan
 
Alex
AfterLogic Support
AfterLogic Support
Avatar

Joined: 19 November 2003
Online Status: Offline
Posts: 2206
Posted: 22 January 2007 at 10:26am | IP Logged Quote Alex

I'm not sure I understood everything correctly. Do you mean m_lpDispatch is a reference to the instance of MailBee.POP3 interface but IConnectionPointContainer request returns event interface for SMTP rather than for POP3?

Maybe, lpDispatch simply refers to the wrong object (SMTP instead of POP3)?

Regards,
Alex
Back to Top View Alex's Profile Search for other posts by Alex
 
johan
Newbie
Newbie
Avatar

Joined: 14 January 2007
Location: South Africa
Online Status: Offline
Posts: 7
Posted: 22 January 2007 at 10:38am | IP Logged Quote johan

Hi Alex,

I am sorry. You are correct. m_lpDispatch is pointing to MailBee.SMTP while I am looking for MailBee.POP3 !
I am trying to incorporate all the outgoing event interfaces into one non-visual object, but this seems a bit tricky if not do-able at all...

regards
johan
Back to Top View johan's Profile Search for other posts by johan
 

If you wish to post a reply to this topic you must first login
If you are not already registered you must first register

  Post ReplyPost New Topic
Printable version Printable version

Forum Jump

Powered by Web Wiz Forums version 7.9
Copyright ©2001-2004 Web Wiz Guide