Solved

Catching Events from control

Posted on 1998-11-26
5
286 Views
Last Modified: 2013-12-26
I have created an exe server using ATL in Visual C++(5.0) & I have methods & events in my exe component. I create an object of the class from visual basic. I have declared my variable with "WithEvents" keyword. Then I created an object & call its method. In the method i fire an event which i am able to catch. But..

In the method if i fire a thread & in from thread i fire the same event, visual basic is not able to catch it.
0
Comment
Question by:nrusinh
  • 3
  • 2
5 Comments
 
LVL 13

Accepted Solution

by:
Mirkwood earned 100 total points
ID: 1488535
There is an article in the MSDN about using events in ATL and VB. You have to do something special since VB only supports dispatch events.

Here it is:

Events: From an ATL Server to a Visual Basic Sink
Robert Coleridge
MSDN Content Development Group

March 26, 1997

Click to copy the files for the ATLEVNTS sample discussed in this technical article

Introduction
This article demonstrates how to create an Active Template Library (ATL) Component Object Model (COM) server that triggers events in a Microsoft® Visual Basic® sink object. Although this may sound like a straightforward COM coding task, there are several idiosyncrasies that must be taken into account. These idiosyncrasies will be demonstrated by writing a simple ATL COM server that reads a Uniform Resource Locator (URL) that has been requested by a Visual Basic client. Once the URL is read, the server raises an event that is picked up by the Visual Basic client.

Most of us who have used Visual Basic are familiar with the terms VBX and OCX. These are generally third-party controls that, when added to a project, give it increased functionality. The ATL COM server that this article will help you to build functions just like a VBX or OCX control.

In order to compile and run the ATLEVNTS sample, you must be running Microsoft Visual C++® version 5.0 and Visual Basic version 5.0. These tools are available either separately or as part of the Microsoft Visual Studio™ 97 package.

Creating the Project and Objects
Creating a project and an object with the new Visual C++ 5.0 ATL COM Wizard is a very straightforward process, as described in the following steps.

To create a new project, select New from the File menu.


Select ATL COM Wizard, fill in the Project name and, if necessary, the Location and Platforms text boxes, and click OK.


Select the Server Type and click Finish.


From the Workspace window, right-click on the Classes entry you just created, and then select New ATL Object. Select Simple Object and click Next.


After filling in the class and object information, click the Attributes tab and select the Support Connection Points and Support ISupportErrorInfo options.


Add methods or properties to the created object.


Voila! You have just created a complete ATL COM Server.
That is all it takes to create a generic ATL COM Server. For further details, see the Visual C++ 5.0 documentation. Now let us examine how to add events to make the object work with a Visual Basic sink.

Creating the Event Interfaces
In order to generate the event interface, we must first generate a dispinterface for the event object. This is because Visual Basic does not generate vtables for its event sinks. The events must be fired through an IDispatch object. Therefore, source and interface attributes are not allowed because ATL assumes that a vtable is supported.

Let's generate an interface for our event methods. First, generate a GUID for the interface with the GuidGen.exe utility. Then, add the event methods to the .idl file, as follows:

   [
      object,
      dual,
      uuid(723B5B11-9EEF-11d0-BD88-00A0C90F282F),
      helpstring("IUrlReaderEvent Interface"),
      version(1.0)
   ]
   interface IUrlReaderEvents : IDispatch {

      HRESULT ReadUrlFinished(long lFlags);

      HRESULT ReadUrlError(long lErrorCode);

   };

Note that since events are in essence subroutines, they must be defined as HRESULT methods that do not return any value.

Once this is done, we need to generate the dispinterface for the .idl file as well. Use the GuidGen.exe utility to generate another GUID, and then place the dispinterface definition inside the library definition, as follows:

[
   uuid(511A66F3-9EEE-11D0-BD88-00A0C90F282F),
   version(1.0),
   helpstring("ReadUrl 1.0 Type Library")
]
library READURLLib
{
   importlib("stdole32.tlb");
   importlib("stdole2.tlb");

   [

      uuid(723B5B10-9EEF-11d0-BD88-00A0C90F282F),

      nonextensible,

      helpstring("DIUrlReaderEvents Interface")

   ]

   dispinterface DIUrlReaderEvents

   {

      properties:

      methods:

         [id(1)] void ReadUrlFinished(long lFlags);

         [id(2)] void ReadUrlError(long lErrorCode);

   };


   . . .
};

Note that the event methods use a void return. This is due to the fact, as stated above, that the events are treated as subroutines and do not return values. The difference in syntax is due to the different requirements of an interface object and a dispinterface object.

The final step in creating the event interfaces is to modify the coclass definition so that the interfaces are exposed and COM knows which interface is the source of events. The sample .idl file looks like the following:

[
   uuid(511A66F3-9EEE-11D0-BD88-00A0C90F282F),
   version(1.0),
   helpstring("ReadUrl 1.0 Type Library")
]
library READURLLib
{
   . . .
   [
      uuid(511A6703-9EEE-11D0-BD88-00A0C90F282F),
      helpstring("UrlReader Class")
   ]
   coclass UrlReader
   {
      [default] interface IUrlReader;

      interface IUrlReaderEvents;

      [default, source] dispinterface DIUrlReaderEvents;

   };
};

The "interface IUrlReaderEvents;" line informs COM of the event interface and the "[default, source] dispinterface DIUrlReaderEvents;" line informs COM which interface is the source of events. This source must be the dispinterface in order to work with a Visual Basic sink.

The ATL Proxy Generator
The ATL Proxy Generator (ATLPG) is an add-in with Visual C++ 5.0 that generates the code necessary for the proxy stub. This code is also the code we will use to cause the firing of the Visual Basic event.

To have the ATLPG generate the event code, do the following:

On the Projects menu, point to Add To Project, and then click Components and Controls.


From the list of available components, double-click Developer Studio Components, select ATL Proxy Generator, and then click Insert.


The ATL Proxy generator requires the name of the type library containing the objects. You simply need to click on the button marked " . . . ", select our generated type library (you may have to browse for it), and click Open.


You will then see a list of defined interfaces in the type library. One of them will be the dual interface we defined in our .idl file. Select the DIUrlReaderEvents interface. Set the proxy type to Connection Points, and then click Insert. Click Save and close all remaining dialog boxes.
That is all it takes to have the ATLPG generate the event code. The ATLPG will examine the type library, find our events, and generate code titled Fire_xxxx, where xxxx is the event we defined in the .idl file. To fire a specified event, we simply invoke one of these Fire_xxxx functions.

The last step we need to take with the ATLPG-generated code is to add an #include line in the object implementation file. The above "walk-through" generated a file called cprdurl.h. Our object is the UrlReader, so we need to add the following line to the urlrdr.h file:

#include "cprdurl.h"

Altering the Templates and Macros
This is the next-to-last step in modifying the generated ATL code. What is necessary here is to modify the inheritance list for the object that fires events. The generated intermittence list for the CUrlReader class looks like the following:

class ATL_NO_VTABLE CUrlReader :
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CUrlReader, &CLSID_UrlReader>,
   public ISupportErrorInfo,
   public IConnectionPointContainerImpl<CUrlReader>,
   public IDispatchImpl<IUrlReader, &IID_IUrlReader, &LIBID_READURLLib>
{

Note that this list does not contain anything we don't need. The two CComxxxx templates are used to derive the base COM object and the base object for our CUrlReader class. The ISupportErrorInfo class and IConnectionPointContainerImpl template are simply a result of our selecting the Supports Connection Points and the Support ISupportErrorInfo options. The IDispatchImpl template is required because we selected a dual interface object. We need to add the template for the ATLPG-generated template, like so:

class ATL_NO_VTABLE CUrlReader :
   public CComObjectRootEx<CComSingleThreadModel>,
   public CComCoClass<CUrlReader, &CLSID_UrlReader>,
   public ISupportErrorInfo,
   public IConnectionPointContainerImpl<CUrlReader>,

   public CProxyDIUrlReaderEvents<CUrlReader>,

   public IDispatchImpl<IUrlReader, &IID_IUrlReader, &LIBID_READURLLib>
{

Once this template is added to the inheritance list, we need to add an entry to the connection point map. This is done by making the following change to the connection point map:

BEGIN_CONNECTION_POINT_MAP(CUrlReader)
   CONNECTION_POINT_ENTRY(DIID_DIUrlReaderEvents)
END_CONNECTION_POINT_MAP()

The final urlrdr.h file should look something like the following (our additions are in bold face):

// urlrdr.h : Declaration of the CUrlReader

 . .

#include "resource.h"       // main symbols

#include "cprdurl.h"

#include <ComDef.h>   // for _bstr_t class

/////////////////////////////////////////////////////////////////////////////
// CUrlReader
class ATL_NO_VTABLE CUrlReader :
   . . .
   public CProxyDIUrlReaderEvents<CUrlReader>,
   . . .
{

 . .

BEGIN_CONNECTION_POINT_MAP(CUrlReader)
   CONNECTION_POINT_ENTRY(DIID_DIUrlReaderEvents)
END_CONNECTION_POINT_MAP()

 . .

#endif //__URLREADER_H_

That's it! We now have a fully functional ATL COM Server that can fire events to a Visual Basic sink object. All that is left is to fire the event whenever it is logically necessary. In the ATLEVNTS sample, the firing code is launched on a separate thread to allow asynchronous processing. The thread creation code looks like the following:

// Create URL reading thread, passing in address of the thread code.
// The "this" pointer is passed in so that the thread code has
// access to the calling object.
m_hThread = ::CreateThread(
   NULL,
   0,

   (LPTHREAD_START_ROUTINE)&CUrlReader::ProcessUrl,

   this,

   0,
   &m_dwThreadId);

The following sample code fires the event:

DWORD WINAPI CUrlReader::ProcessUrl(void * pParam)
{
   // The "pParam" is a "this" pointer to the calling object,
   // so cast appropriately.
   CUrlReader * pCaller = (CUrlReader *)pParam;
.
   // Raise event notification
   pCaller->Fire_ReadUrlFinished(0);

   return 0;
}

Tying It All Together
The following list reiterates the 16 steps required to generate an ATL object that fires events to a Visual Basic sink object.

Create an ATL COM Wizard project.


Create an ATL object with support for connection points.


Add methods and properties to the object(s).


Run GuidGen.exe to get a GUID for the dispinterface event object.


Add the dispinterface definition inside the library definition.


Add void event methods to this interface.


Run GuidGen.exe to get a GUID for the interface event object.


Add the event interface.


Add HRESULT event methods to this interface.


Add dispinterface and interface to coclass.


Add forward references to the top of the .idl file.


Generate a typelib with the ATL Proxy Generator.


Add #include for the generated file to the object .h file.


Modify the class inheritance list.


Add connection point entries to the connection point map.


Add event firing to the appropriate code.
UrlReader: The Sample Code
The sample Visual Basic client is quite simple. All it does is provide two editboxes—one to enter the desired URL and the other for the name of the output file. Once the user enters the desired URL and the output file specification and clicks the Read URL button, the client will request the HTML page from the COM server and then immediately return to the client application. (Since this is a multithreaded application, the client can do further processing until the event notification is fired from the server.)

The COM server will establish a link to the web and attempt to read the specified URL. In order to allow the Visual Basic code to return immediately, the COM server creates a separate thread to do the Web work. If the Web work fails, then the COM server fires the UrlReader_ReadUrlError event. If the URL is read successfully, then the UrlReader_ReadUrlFinished event is fired.

For a technical discussion of the Visual Basic client and its event syntax, please refer to the Visual Basic documentation. See entries on WithEvents and classes.

Conclusion
As you can see from this article, adding the requisite components to an ATL object is not difficult at all. It is simply a series of sequential steps. With this knowledge in mind, you can now combine the power of ATL, the Visual C++ 5.0 wizardry, and what you've learned from this article to create a multitude of useful objects.


0
 

Author Comment

by:nrusinh
ID: 1488536
Mirkwood, Thank you very much your information is very useful for me. I have created the project according to the given information.
I have some small problems that are:
1>
The interface
       IUrlReaderEvents : IDispatch {
      HRESULT ReadUrlFinished(long lFlags);
      HRESULT ReadUrlError(long lErrorCode);
   };
is to be added by hand or by slecting "New ATL Object" ?

2>
If we add it by hand where to implement those two methods ?

3> Where to write "CreateThread" ?

4>How to prototype the thread function
 DWORD WINAPI CUrlReader::ProcessUrl(void * pParam)

& finally please tell me sample visual basic script for this same example.

Actuall I have tried the same steps before as you told. I am also able to fire events from method. But from metod if i fire the event I am not able to catch in visual basic script. Please tell me if there is something special to catch the event that is fired from thread. If possible please send me code to try the same example.

Giving you idea what I already in visual basic script.

1>I have declared variable using "withevents" keyword.
2> I have created object using "CreateObject".
3>Then if I call method of this object & fire event from the method, I am able to catch it. But If I fire thread from the method & from thread I fire event I am not able to catch it in visual basic. Also I was running visual basic script using F8 button.
0
 
LVL 13

Expert Comment

by:Mirkwood
ID: 1488537
0
 

Author Comment

by:nrusinh
ID: 1488538
I have unzip the file, but I could extract only the files for VC code but not for Visual Basic.
0
 

Author Comment

by:nrusinh
ID: 1488539
Mirkwood, thanks It worked fine. But I came to know that events can be fired from DLL server only. Is that true ?
0

Featured Post

Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

Join & Write a Comment

Introduction In a recent article (http://www.experts-exchange.com/A_7811-A-Better-Concatenate-Function.html) for the Excel community, I showed an improved version of the Excel Concatenate() function.  While writing that article I realized that no o…
When trying to find the cause of a problem in VBA or VB6 it's often valuable to know what procedures were executed prior to the error. You can use the Call Stack for that but it is often inadequate because it may show procedures you aren't intereste…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

19 Experts available now in Live!

Get 1:1 Help Now