線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:1956
推到 Plurk!
推到 Facebook!

Events and callback functions

 
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2003-04-10 09:29:28 IP:61.218.xxx.xxx 未訂閱

Events and callback functions

By David Bridges http://www.bridgespublishing.com/articles/issues/0110/Events_and_callback_functions.htm Any veteran Windows programmer should be familiar with callback functions. In this article, I will cover how to use callback functions and explain how VCL events are just a special kind of callback function. Once you understand how VCL events work, youll be able to add events to your own classes for added functionality, even if your classes arent derived from the VCL. Using callbacks The most common way to have access to a function in C is to either declare the function, or include a header file that declares the function. This tells the compiler that the function is defined somewhere, but doesnt say exactly where it is. The linker is responsible for finding the actual functions address and linking it into the final code. Generally, the code for the function will be in a .LIB file or in another source file in the project. Using function pointers, you can call any function that is in the processs memory space, without having to declare it or having the source code or .LIB file. All you need to know is the address of the function, and what type of parameters it expects. Unlike other types, function pointers do not require the usual syntax for pointers. To take the address of a function, you dont have to use the & operator. Instead, just leave off the parentheses and use only the function name. When calling the function via its address, it does not have to be dereferenced. The following code shows an example:

typedef void (*MyFunctionType)();    void MyFunction()
{
  MessageBox(
    NULL, "Hello", "Example", MB_OK);
}    void CallFunction()
{
  // Create a function pointer and 
  // assign the address of a function.
  MyFunctionType func = MyFunction;
  func();
}
Callback functions are generally functions that are written by a programmer with the sole purpose of being called from somewhere elsefor example, the operating system. The way this is done is to supply the functions address to the outside program, which then calls it at some later time. This is whats referred to as the "callback". One common use of callbacks in the Windows API is for enumeration functions. For example, suppose you want to load a combo box with the names of all of the fonts available on the machine. One possible solution would be to have a Windows API function that takes all of the available font names and loads them into a specified combo box. This would work fine, but what if we wanted to add the font names to some other kind of control, or put them into a user-defined memory structure? This is one situation where callback functions are particularly useful. The example below shows how to write a callback function that will perform a certain action for every font name that exists on the system.

extern "C" int CALLBACK AddFontToList(
  const LOGFONT* lf, const TEXTMETRIC* tm,
  unsigned long iType, long lData)
{
  TMyForm* form = 
    reinterpret_cast(lData);
  form->edtFonts->Lines->Add(
    lf->lfFaceName);
  return 1;
}    void __fastcall TMyForm::FormCreate(
  TObject *Sender)
{
  EnumFontFamilies(Canvas->Handle, 
    NULL, AddFontToList, (LPARAM)this);
}
Theres actually a much easier way to do thisyou could just use the following line:

edtFonts->Lines->Assign(Screen->Fonts);
The example is meant to show that by the use of a callback function, Windows can transfer a set of information to another program without any knowledge of what the program will do with that information. Caller ID Many callback functions require a parameter that represents an object or context. This is commonly known as the "user data" parameter. In the AddFontToList function, this is the lData parameter. This parameter allows us to pass an application-defined value to the enumeration function along with the address of the callback function. Every time Windows calls the AddFontToList function, it passes back the same value for lData that was passed to EnumFontFamilies. The value of this parameter is totally up to the programmer. In this case, it contains the address of the form object. The __closure keyword In this example, it would be much easier if we could just specify a member function of the class as the callback function ?that way, we wouldnt have to keep passing the this pointer around. Unfortunately, C does not allow callbacks into class member functions. Fortunately, Borland C Builder provides a way to do this using the __closure keyword extension. The example below shows how to use this keyword to enable callbacks into a class member function.

typedef void (__closure *AddStringEvent)
  (AnsiString sValue);    // This function supplies a list of 
// strings by using a callback function
void ListEmployees(
  AddStringEvent AddString)
{
  AddString("Alex");
  AddString("Brian");
  AddString("David");
  // ...
}    void TMyForm::AddNextEmployeeName(
  AnsiString sName)
{
  edtEmployees->Lines->Add(sName);
}    void __fastcall TMyForm::FormCreate(
  TObject *Sender)
{
  ListEmployees(AddNextEmployeeName);
}
In standard C , the reason you cant use a class member function as a callback function is because in C (and C ), a pointer is a pointer, and they are all the same size (32 bits in the Win32 world). In order to use a class member function, you must have two pointers: one that has the address of the object, and the other which contains the address of its member function, or method. The __closure keyword passes the objects this pointer as a "hidden parameter". This address is actually stored along with the function address in the pointer. If you use the sizeof() operator on a closure function pointer, you will see that its size is 64 bits?2 bits for the objects address and 32 bits for the function address. Events So far, weve seen how to use callback functions to transfer sets of information, and how to use the __closure keyword in order to use class member functions as callbacks. Now well see how to create and handle our own events. In the VCL, events are exposed as properties. Events are no different from other properties, except that their type is a pointer to a function. Whenever the event occurs, the function whose address is contained in the property gets called. If the event is NULL, no function is called. The simplest type of event in C Builder is TNotifyEvent. Here is the definition of TNotifyEvent:

typedef void __fastcall (__closure *TNotifyEvent)(System::TObject* Sender);
For a function to be of the TNotifyEvent type, it must be a class member function that has the same return type and parameters, and must be declared as __fastcall. For example:

void __fastcall MyForm::edtNameChange(
  TObject* Sender)
{
  // ...
}
The IDE automatically writes functions such as this and assigns them to the events of controls through the Object Inspector. However, you can also assign these events programatically. Instead of double-clicking on the controls OnChange event, you could instead just write the member function above and assign it with this code:

void __fastcall TMyForm::FormCreate(
  TObject *Sender)
{
  edtName->OnChange = edtNameChange;
}
Conclusion Listing A shows the code for a component that uses events. The ValueChangeEvent type is a function that has two arguments, Sender and NewValue. Most VCL events have TObject* Sender as their first parameter, and this new event type follows that convention. The Sender parameter is very useful because it tells the callback function what object generated the event. The class MyComponent is a very simple oneit has one property and one event. Whenever the Value property is changed, the OnChange event is generated. This class is derived from TComponent, but it doesnt have to be. You can use properties and events in your own classes even if they arent derived from VCL classes. Listing A: A component with an event.

// Define the event type. 
typedef void (__closure *ValueChangeEvent)
  (TObject* Sender, AnsiString NewValue);    class MyComponent : public TComponent
{
  private:
    AnsiString       FValue;
    ValueChangeEvent FOnChange;      public:        __fastcall MyComponent(TComponent* owner)
      : TComponent(owner) 
    {
      // Assign a null handler by default
      OnChange = NULL;
    }        void SetValue(AnsiString sNewValue)
    {
      FValue = sNewValue;          // If a handler is assigned to 
      // this event, call it.
      if (OnChange) {
        OnChange(this, FValue);
      }
    }        // 'Value' property
    __property AnsiString Value = 
      {read=FValue, write=SetValue};        // 'OnChange' event
    __property ValueChangeEvent OnChange = 
      {read=FOnChange, write=FOnChange};
};    // This is the OnChange event handler
void TMyForm::ComponentValueChanged(
  TObject* Sender, AnsiString sNewValue)
{
  MessageBox(Handle, sNewValue.c_str(), 
    "Value Changed", MB_OK);
}    void __fastcall TMyForm::FormCreate(
  TObject *Sender)
{
  // Create the object
  MyComponent* c = new MyComponent(this);      // Assign the handler for the 'OnChange' event.
  c->OnChange = ComponentValueChanged;      // Setting the 'Value' property 
  // will generate the 'OnChange' event.
  c->Value = "New Value";      delete c;
}
Copyright ?2003, Bridges Publishing. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Bridges Publishing is prohibited. All other product names and logos are trademarks or registered trademarks of their respective owners.
聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]---
axsoft
版主


發表:681
回覆:1056
積分:969
註冊:2002-03-13

發送簡訊給我
#2 引用回覆 回覆 發表時間:2003-04-10 09:49:45 IP:61.218.xxx.xxx 未訂閱
How to Implement Callbacks in C and C http://www.function-pointer.org/CCPP/callback/callback.html#static Method Callbacks http://www.drbob42.com/cbuilder/lstfnd15.htm 聯盟----Visita網站http://www.vista.org.tw ---[ 發問前請先找找舊文章 ]---
系統時間:2024-04-29 20:47:41
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!