Compact 2013 Ebook

17.8 Callbacks
Created by djones on 6/29/2013 8:31:59 AM

Managed Code Callbacks from Native Code

In this activity, the call to the native code to generate the local time string, will be actioned by a Callback. The action will be performed both synchronously and asynchronously.

(A) Synchronous Callback

In this segment of the activity, the managed code calls the native code which inturn calls back into the managed code using the method passed to it by the managed code. The instantiated StringBuilder object is passed to the native code, which is embellished with a call to GetTime and then passed back as a parameter to called managed code function. This is all synchronous as the calling managed thread actions all of the processing in sequence.

  1. Start with the C# PInvoke version of the applications, as in 17.5
  2. Add the following the the native code DLL header file (TimeDLL.h):
    C++ Callback Header File Code
    1. typedef int (CALLBACK *FunctionPtr)( TCHAR* str );
    2. extern "C" TimeDLL_API void CallbackGetTime( FunctionPtr pf, TCHAR *  value );

    Listing 17. The  Callback function pointer in eth native code
  3. Implement CallbackGetTime in the source file (TimeDLL.cpp):
    C++ CallbackTime Function
    1. extern "C" TimeDLL_API void CallbackGetTime( FunctionPtr pf, TCHAR *  value )
    2. {
    3.     RETAILMSG(1,( _T("\nReceived value: %s"), value));
    4.  
    5.     RETAILMSG(1,( _T( "\nPassing to callback..." )));
    6.  
    7.     GetTime( value);
    8.  
    9.     int res = (*pf)(value);
    10.  
    11.     if( res==3178 )
    12.         RETAILMSG(1,( _T( "Callback returned true.\n" )));
    13.     else
    14.         RETAILMSG(1,( _T( "Callback returned false. %d\n" ),res));
    15. }
    ck
    Listing 17. The CallbackGetTime( )  native code function.
  4. In the PInvoke class file, above the class definition insert (i.e. just below the using decelerations):
    C# Callback Delegate
    1. public delegate int FunctionPtr( StringBuilder value);

    Listing 17. The function delegate for the Callback
  5. Add the following to the PInvoke class:


    [DllImport("TimeDLL.dll")]
    public static extern void CallbackGetTime(FunctionPtr pf, StringBuilder value);

    Listing 17. The PInvoke signature for the native code function, CallbackGetTime( )

  6. Add the following function to the application:
    C# Callback Function
    1. public int UpdateTime(StringBuilder value)
    2. {
    3.     textBox1.Text = value.ToString();
    4.     return 3178;
    5. }

    Listing 17. The Callback function (that is called by the native code)

    Note: The "magic number" 3178 is used as a return value in UpdateTime( ).
    The native code checks that this value is returned from the callback.


  7. Modify the TimeButton event handler to:
    C# TimeButton Callback Version
    1. private void TimeButton_Click(object sender, EventArgs e)
    2. {
    3.     FunctionPtr cb = new FunctionPtr(this.UpdateTime);
    4.     StringBuilder str = new StringBuilder(100);
    5.     str.Insert(0, "The initial string");
    6.     PInvoke.CallbackGetTime(cb, str);
    7. }

    Listing 17. The Time Button event handler, when the Callback is used.
  8. Rebuild and deploy the DLL. Rebuild and test run the application. It should function as previous.

    18949897 PID:400002 TID:3ca0056 RELFSD: Opening file TimeDLL.dll from desktop
    18949906 PID:3ac005a TID:3ca0056 Received value: The initial string
    18949907 PID:3ac005a TID:3ca0056 Passing to callback...
    18949907 PID:3ac005a TID:3ca0056 Callback returned true.


    Listing 17. The Debug Output for the Native Code DLL, when the Callback is actioned.

Smile

Note:

       The correct string is received, it calls the callback and the correct value is returned from it.

Issue1: The StringBuilder object may be garbage collected before the callback is actioned. This is not an issue here as its call functions synchronously. More typically the call from the managed code to initiate the callback will return before the callback is actioned. The native code will do its requested processing in a separate thread (so the call from managed code can return) and then call the callback. The managed code function that is called back to therefore runs in a different thread to the managed code thread that initiated the call in the asynchronous case.

Issue2: 
When a UI control has a UI property such a Text is programmatically changed, that change must be actioned by a UI thread. As discussed immediately above, that is not a problem here as the callback is performed by the same UI thread that initiated the call.       

Asynchronous Callback

Callbacks are typically used to call Win32 functions where a Callback is required. In these cases the call is made and returns so as to not lockup the UI. Windows then performs the required processing and then calls the provided function in the calling application. Hence these Callbacks are completed asynchronously. 

In this segment of the activity, a thread is launched in the native code when it is called by the managed code, and the call returns to the managed code without the result. After a delay, the thread generates the DateTime string, then does the Callback asynchronously and exits. Normally a delay would not be inserted but its used in this case to demonstrate that it is the thread calling back that actions the update. To highlight this, the textbox is cleared before the initial call is made from the managed code.

One issue that has been pointed to, that only UI threads can update a control, is addressed using a standard pattern involving Control.InvokeRequired.

          In the ManagedCode project:

  1. In the TimeButton event handler in  ManagedTime.cpp, insert the following at the start:
    C# Clear textBox1
    1. textBox1.Text = "";


    In the Native Code DLL project:
  2. Add the following at the bottom of the header file :
    C++StartThread Function
    1. int StartThread(FunctionPtr pf, TCHAR *  value);

  3. Add a new .cpp file and call it asynchronousThread.cpp
  4. Insert the following code into this new code file:
    C++ asynchronousThread.cpp
    1. #include
    2. #include
    3. #include
    4. #include "stdAfx.h"
    5. #include "TimeDLL.h"
    6.  
    7. FunctionPtr g_pf;
    8. TCHAR *  g_value;
    9.  
    10. DWORD WINAPI MyThread( LPVOID lpParam )
    11. {
    12.     RETAILMSG(1,( _T("Thread pausing")));
    13.     Sleep(1000);
    14.     RETAILMSG(1,( _T("Thread reactivated")));
    15.  
    16.     //Call same GetTime function as previous
    17.     GetTime( g_value);
    18.  
    19.     int res = (*g_pf)(g_value);
    20.  
    21.     if( res==3178 )
    22.         RETAILMSG(1,( _T( "Callback returned true.\n" )));
    23.     else
    24.         RETAILMSG(1,( _T( "Callback returned false. %d\n" ),res));
    25.     return 0;
    26. }
    27.  
    28. int StartThread(FunctionPtr pf, TCHAR *  value)
    29. {
    30.     //PMYDATA pData;
    31.     DWORD dwThreadId;
    32.     HANDLE hThread;
    33.     g_pf = pf;
    34.     g_value= value;
    35.  
    36.  
    37.     RETAILMSG(1,( _T("Starting thread")));
    38.  
    39.     hThread = CreateThread(
    40.         NULL,              // default security attributes
    41.         0,                 // use default stack size 
    42.         MyThread,          // thread function
    43.         NULL,             // argument to thread function
    44.         0,                 // use default creation flags
    45.         &dwThreadId);   // returns the thread identifier
    46.  
    47.  
    48.     // Check the return value for success.
    49.     if (hThread == NULL)
    50.     {
    51.         RETAILMSG(1,( _T("Thread not started")));
    52.         return -1;
    53.     }
    54.  
    55.     RETAILMSG(1,( _T("Thread started OK")));
    56.  
    57.     return 0;
    58. }

  5. Modify the CallbackGetTime( ) function in the DLL .cpp file so as to action the thread start rather than generate the DatteTime string:
    C++ Asynch. CallbackGetTime
    1. extern "C" TimeDLL_API void CallbackGetTime( FunctionPtr pf, TCHAR *  value )
    2. {
    3.     RETAILMSG(1,( _T("\nReceived value: %s"), value));
    4.  
    5.     RETAILMSG(1,( _T( "\nPassing to callback..." )));
    6.  
    7.     //Start the thread
    8.     int res= StartThread(pf, value);
    9.  
    10.     //GetTime( value);
    11.  
    12.      //int res = (*pf)(value);
    13.  
    14.     if( res==0 )
    15.         RETAILMSG(1,( _T( "Callback start thread returned true.\n" )));
    16.     else
    17.         RETAILMSG(1,( _T( "Callback start thread returned false. %d\n" ),res));
    18. }

  6. Rebuild the DLL and the Managed Code app.
  7. Test the Managed Code app.

When the Callback occurs an error is generated as shown:

image

This is because, as flagged, a UI thread must update a control. The native code thread actioning the Callback can't do that. There is a standard pattern for doing this as discussed in:
http://msdn.microsoft.com/en-us/library/ms171728.aspx.
Rather than directly update the Textbox Text property, a function is called. If the thread that calls that function isn't a UI thread then a Delegate is launched as a UI thread to perform that function call so that the update does occur.

  1. Add the following to the Managed Code form code:
    C# Asynchronous SetText
    1. //Ref: http://msdn.microsoft.com/en-us/library/ms171728.aspx
    2. // This delegate enables asynchronous calls for setting
    3. // the text property on a TextBox control.
    4. delegate void SetTextCallback(string text);
    5.  
    6. // This method demonstrates a pattern for making thread-safe
    7. // calls on a Windows Forms control. 
    8. //
    9. // If the calling thread is different from the thread that
    10. // created the TextBox control, this method creates a
    11. // SetTextCallback and calls itself asynchronously using the
    12. // Invoke method.
    13. //
    14. // If the calling thread is the same as the thread that created
    15. // the TextBox control, the Text property is set directly.
    16. public void SetText(string value)
    17. {
    18.     if (this.textBox1.InvokeRequired)
    19.     {
    20.         SetTextCallback d = new SetTextCallback(SetText);
    21.         this.Invoke(d, new object { value });
    22.     }
    23.     else
    24.     {
    25.         this.textBox1.Text = value;
    26.     }
    27. }

  2. Modify UpdateTime( ) so as to call SetText to do the update:
    C# Asynchronous UpdateTime( )
    1. public int UpdateTime(StringBuilder value)
    2. {
    3.     string strValue = value.ToString();
    4.     SetText(strValue);
    5.     return 3178;
    6. }

  3. Rebuild the managed Code app and test it. 

Winking smile    


NEXT:  17.9 Managed Code Access to Remote Data

print

Click here to provide feedback and input

  Comments

There is no comment.

Turkish porno izle video site in rokettubeporno izle