Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. DLL with a callback

DLL with a callback

Scheduled Pinned Locked Moved C / C++ / MFC
databasetestingbeta-testing
14 Posts 3 Posters 1 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • B Offline
    B Offline
    Bodo2407
    wrote on last edited by
    #1

    Hi! I try to implement a callback function within a dll, which triggers an assigned function in the main application. I want to trigger this "event" in the main app from within the dll. Here is my code from the dll:

    //a normal function I call from the main application:
    extern "C" __declspec(dllexport) void funcA(const unsigned long long a, const unsigned long long b);
    //following function should be the callback, triggering an assigned function in the main application
    extern "C"
    {
    typedef void(__stdcall* callback_t)(unsigned int halfbeast);

    \_\_declspec(dllexport) void public\_func\_taking\_callback(callback\_t evHnd)
    {
        evHnd(333);//value just for testing
    }
    

    }

    void funcA(
    const unsigned long long a,
    const unsigned long long b)
    {
    //some code here... do things with a and b
    if(a>10) //trigger the event in the main application
    callback_t(public_func_taking_callback);
    }

    Here is the code I use in the main application to load the library and attach to the functions:

    typedef void (__stdcall *eventCallback)(unsigned int miau);
    typedef void (__stdcall *setCallback)(eventCallback evHnd);
    void __stdcall CallB(unsigned int);

    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    signed int retval;
    unsigned int i;
    char txt[7];
    unsigned long long a,index;
    UnicodeString USt;
    HINSTANCE dllHandle=NULL;

    //Obtain a handle to the DLL
    dllHandle = LoadLibrary(L"test.dll");
    if (!dllHandle){
    ShowMessage("Could not load test.dll");
    Form1->Close();
    }
    else{ //Lib was loaded, try to get data
    void (__stdcall* funcA)(const unsigned long long a, const unsigned long long b);
    funcA=(void(__stdcall*)(const unsigned long long a, const unsigned long long b))GetProcAddress(dllHandle,"funcA");

    // DLL function pointer
    setCallback values=(setCallback)GetProcAddress(dllHandle, "public\_func\_taking\_callback");
    if(values != NULL){
        values(&CallB);
        }
    
    if(!funcA){
        Memo1->Text="No handle to function found.";
        }
    else{
        funcA(1,1);
        funcA(100,100);
        }
    FreeLibrary(dllHandle);
    }
    

    }
    //---------------------------------------------------------------------------
    void __stdcall CallB(unsigned int halfbeast){
    ShowMessage("triggered");
    }
    //-------------------------------------------------------------------------

    L 1 Reply Last reply
    0
    • B Bodo2407

      Hi! I try to implement a callback function within a dll, which triggers an assigned function in the main application. I want to trigger this "event" in the main app from within the dll. Here is my code from the dll:

      //a normal function I call from the main application:
      extern "C" __declspec(dllexport) void funcA(const unsigned long long a, const unsigned long long b);
      //following function should be the callback, triggering an assigned function in the main application
      extern "C"
      {
      typedef void(__stdcall* callback_t)(unsigned int halfbeast);

      \_\_declspec(dllexport) void public\_func\_taking\_callback(callback\_t evHnd)
      {
          evHnd(333);//value just for testing
      }
      

      }

      void funcA(
      const unsigned long long a,
      const unsigned long long b)
      {
      //some code here... do things with a and b
      if(a>10) //trigger the event in the main application
      callback_t(public_func_taking_callback);
      }

      Here is the code I use in the main application to load the library and attach to the functions:

      typedef void (__stdcall *eventCallback)(unsigned int miau);
      typedef void (__stdcall *setCallback)(eventCallback evHnd);
      void __stdcall CallB(unsigned int);

      //---------------------------------------------------------------------------
      void __fastcall TForm1::Button1Click(TObject *Sender)
      {
      signed int retval;
      unsigned int i;
      char txt[7];
      unsigned long long a,index;
      UnicodeString USt;
      HINSTANCE dllHandle=NULL;

      //Obtain a handle to the DLL
      dllHandle = LoadLibrary(L"test.dll");
      if (!dllHandle){
      ShowMessage("Could not load test.dll");
      Form1->Close();
      }
      else{ //Lib was loaded, try to get data
      void (__stdcall* funcA)(const unsigned long long a, const unsigned long long b);
      funcA=(void(__stdcall*)(const unsigned long long a, const unsigned long long b))GetProcAddress(dllHandle,"funcA");

      // DLL function pointer
      setCallback values=(setCallback)GetProcAddress(dllHandle, "public\_func\_taking\_callback");
      if(values != NULL){
          values(&CallB);
          }
      
      if(!funcA){
          Memo1->Text="No handle to function found.";
          }
      else{
          funcA(1,1);
          funcA(100,100);
          }
      FreeLibrary(dllHandle);
      }
      

      }
      //---------------------------------------------------------------------------
      void __stdcall CallB(unsigned int halfbeast){
      ShowMessage("triggered");
      }
      //-------------------------------------------------------------------------

      L Offline
      L Offline
      Lost User
      wrote on last edited by
      #2

      1. That is correct. At that point, values is set to public_func_taking_callback, which takes the address of a function and immediately calls it. 2. funcA calls callback_t, but callback_t has never been set to any value. To be honest, I find it difficult to understand your code, so I may have still missed something. However, you should be able to see the exact sequence of events by running the code in the debugger.

      B 1 Reply Last reply
      0
      • L Lost User

        1. That is correct. At that point, values is set to public_func_taking_callback, which takes the address of a function and immediately calls it. 2. funcA calls callback_t, but callback_t has never been set to any value. To be honest, I find it difficult to understand your code, so I may have still missed something. However, you should be able to see the exact sequence of events by running the code in the debugger.

        B Offline
        B Offline
        Bodo2407
        wrote on last edited by
        #3

        Hi Richard, thanks for the answers. Yes I can debug it, but it doesn't help me much, since I don't understand why it behaves that way. 1.) OK, got it. Unfortunately then I do not know, how I pass the address of the function of CallB to the dll. I want the dll to call CallB() in the moment callback_t is called. Most likely I confuse here something, but I find it very hard to understand the syntax. It seems even harder since it is wrong :D I wasn't able to find any C++ examples for that. 2.) I thought I set it to public_func_taking_callback, but obviously this is not the case. Maybe I should explain again, what I try to do: In my main application I want to declare a function. This function should be registered in the dll as a callback function. When during run time in the dll some criteria match ( if(a>10) ) the dll should call the function in the main application and execute it.

        L 1 Reply Last reply
        0
        • B Bodo2407

          Hi Richard, thanks for the answers. Yes I can debug it, but it doesn't help me much, since I don't understand why it behaves that way. 1.) OK, got it. Unfortunately then I do not know, how I pass the address of the function of CallB to the dll. I want the dll to call CallB() in the moment callback_t is called. Most likely I confuse here something, but I find it very hard to understand the syntax. It seems even harder since it is wrong :D I wasn't able to find any C++ examples for that. 2.) I thought I set it to public_func_taking_callback, but obviously this is not the case. Maybe I should explain again, what I try to do: In my main application I want to declare a function. This function should be registered in the dll as a callback function. When during run time in the dll some criteria match ( if(a>10) ) the dll should call the function in the main application and execute it.

          L Offline
          L Offline
          Lost User
          wrote on last edited by
          #4

          Bodo2407 wrote:

          I don't understand why it behaves that way.

          It behaves that way because that is the way you have written the code. 1. So you need to change the code in public_func_taking_callback as follows:

          \_\_declspec(dllexport) void public\_func\_taking\_callback(callback\_t evHnd)
          {
           //   evHnd(333);//value just for testing // this was causing your problem
              callback\_t = evHnd; // save the callback address for funcA.
          }
          

          And you need to change funcA to:

          if(a>10) //trigger the event in the main application
          callback_t(a); // the callback is set to invoke void __stdcall CallB(unsigned int halfbeast)

          Again, make these changes and then step through the code in the debugger to check that it is correct.

          B 1 Reply Last reply
          0
          • L Lost User

            Bodo2407 wrote:

            I don't understand why it behaves that way.

            It behaves that way because that is the way you have written the code. 1. So you need to change the code in public_func_taking_callback as follows:

            \_\_declspec(dllexport) void public\_func\_taking\_callback(callback\_t evHnd)
            {
             //   evHnd(333);//value just for testing // this was causing your problem
                callback\_t = evHnd; // save the callback address for funcA.
            }
            

            And you need to change funcA to:

            if(a>10) //trigger the event in the main application
            callback_t(a); // the callback is set to invoke void __stdcall CallB(unsigned int halfbeast)

            Again, make these changes and then step through the code in the debugger to check that it is correct.

            B Offline
            B Offline
            Bodo2407
            wrote on last edited by
            #5

            Hi Richard, thanks a lot, but unfortunately it cannot compile:

            __declspec(dllexport) void public_func_taking_callback(callback_t evHnd)
            {
            // evHnd(333);//value just for testing // this was causing your problem
            callback_t = evHnd; // save the callback address for funcA. //this line doesn't compile
            }

            Failure given: -Error C2513 'void (__stdcall *)(unsigned int)': no variable declared before '=' So the typedef earlier doesn't work or... ?

            CPalliniC 1 Reply Last reply
            0
            • B Bodo2407

              Hi Richard, thanks a lot, but unfortunately it cannot compile:

              __declspec(dllexport) void public_func_taking_callback(callback_t evHnd)
              {
              // evHnd(333);//value just for testing // this was causing your problem
              callback_t = evHnd; // save the callback address for funcA. //this line doesn't compile
              }

              Failure given: -Error C2513 'void (__stdcall *)(unsigned int)': no variable declared before '=' So the typedef earlier doesn't work or... ?

              CPalliniC Offline
              CPalliniC Offline
              CPallini
              wrote on last edited by
              #6

              callback_t is a type, not a function. In your DLL, you do need a variable of such a type, in order to store the user function address. Something similar to (not tested)

              typedef void (__stdcall* callback_t) (unsigned int halfbeast);
              static callback_t s_user_function = nullptr; // variable used to store the user function address

              extern "C" __declspec(dllexport) void funcA(const unsigned long long a, const unsigned long long b);
              //following function should be the callback, triggering an assigned function in the main application
              extern "C"
              {
              __declspec(dllexport) void public_func_taking_callback(callback_t evHnd)
              {
              s_user_function = evHnd; // store the passed user function address
              }
              }

              void funcA(
              const unsigned long long a,
              const unsigned long long b)
              {
              //some code here... do things with a and b
              if(a>10 && s_user_function != nullptr) //trigger the event in the main application
              s_user_function(333); // call the user function
              }

              "In testa che avete, Signor di Ceprano?" -- Rigoletto

              In testa che avete, signor di Ceprano?

              L 1 Reply Last reply
              0
              • CPalliniC CPallini

                callback_t is a type, not a function. In your DLL, you do need a variable of such a type, in order to store the user function address. Something similar to (not tested)

                typedef void (__stdcall* callback_t) (unsigned int halfbeast);
                static callback_t s_user_function = nullptr; // variable used to store the user function address

                extern "C" __declspec(dllexport) void funcA(const unsigned long long a, const unsigned long long b);
                //following function should be the callback, triggering an assigned function in the main application
                extern "C"
                {
                __declspec(dllexport) void public_func_taking_callback(callback_t evHnd)
                {
                s_user_function = evHnd; // store the passed user function address
                }
                }

                void funcA(
                const unsigned long long a,
                const unsigned long long b)
                {
                //some code here... do things with a and b
                if(a>10 && s_user_function != nullptr) //trigger the event in the main application
                s_user_function(333); // call the user function
                }

                "In testa che avete, Signor di Ceprano?" -- Rigoletto

                L Offline
                L Offline
                Lost User
                wrote on last edited by
                #7

                Thanks Carlo, I missed that.

                B CPalliniC 2 Replies Last reply
                0
                • L Lost User

                  Thanks Carlo, I missed that.

                  B Offline
                  B Offline
                  Bodo2407
                  wrote on last edited by
                  #8

                  Thanks a lot guys! It works now! I really appreciate your help. Now it also makes much more sense to me. :-D

                  1 Reply Last reply
                  0
                  • L Lost User

                    Thanks Carlo, I missed that.

                    CPalliniC Offline
                    CPalliniC Offline
                    CPallini
                    wrote on last edited by
                    #9

                    As you pointed out, the OP code looked a bit confused.

                    "In testa che avete, Signor di Ceprano?" -- Rigoletto

                    In testa che avete, signor di Ceprano?

                    L 1 Reply Last reply
                    0
                    • CPalliniC CPallini

                      As you pointed out, the OP code looked a bit confused.

                      "In testa che avete, Signor di Ceprano?" -- Rigoletto

                      L Offline
                      L Offline
                      Lost User
                      wrote on last edited by
                      #10

                      CPallini wrote:

                      a bit confused

                      I assumed that was just me. :^)

                      B 1 Reply Last reply
                      0
                      • L Lost User

                        CPallini wrote:

                        a bit confused

                        I assumed that was just me. :^)

                        B Offline
                        B Offline
                        Bodo2407
                        wrote on last edited by
                        #11

                        OK, now I experience another issue with it, I'm sorry. Hopefully you can help me here as well. I now want to call the callback function if an timer expires. Therefore I create an timer in a function I call from the main application. I store the handles to the timer and the timer queue globally in static variables.

                        static HANDLE gDoneEvent;
                        static HANDLE hTimer = NULL;
                        static HANDLE hTimerQueue = NULL;
                        int arg = 123; //will be passed to callback of timer

                        VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
                        {
                        if (lpParam == NULL)
                        {
                        //do some error handling her
                        return;
                        }
                        else
                        {
                        if(s_user_function!=nullptr)
                        s_user_function(12);
                        }
                        //SetEvent(gDoneEvent); //for periodic calls, don't set gDoneEvent
                        }

                        Now, in some function called from the main application, I allocate the timer and start it:

                        char timer_init(void)
                        {
                        // Use an event object to track the TimerRoutine execution
                        gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
                        if (NULL == gDoneEvent)
                        {
                        return 1;
                        }
                        // Create the timer queue.
                        hTimerQueue = CreateTimerQueue();
                        if (NULL == hTimerQueue)
                        {
                        return 2;
                        }
                        CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK)TimerRoutine, &arg, 3000, 10000, 0);

                        WaitForSingleObject(gDoneEvent, INFINITE); //if we don't wait it crashes although handles are stored globally?
                            
                        //the handles should be closed and deleted only after timer had occured. 
                        
                        ////CloseHandle(gDoneEvent);
                        ////// Delete all timers in the timer queue.
                        ////if (!DeleteTimerQueue(hTimerQueue))
                        ////    printf("DeleteTimerQueue failed (%d)\\n", GetLastError());
                        

                        }

                        As you can see, I don't close the handles and delete the timer queue on purpose. Everything works fine as long as the process waits. The callback in the main application is called. If I comment out the line with WaitForSingleObject I'll get an access exception, although I stored all the handles globally. Teh exception occurs with the timing the timer would have. So most likely it tries to call the callback of the timer function but something is missing. What is it I'm overlooking? I need the dll running, not waiting inside a fucntion for a timer...

                        L 1 Reply Last reply
                        0
                        • B Bodo2407

                          OK, now I experience another issue with it, I'm sorry. Hopefully you can help me here as well. I now want to call the callback function if an timer expires. Therefore I create an timer in a function I call from the main application. I store the handles to the timer and the timer queue globally in static variables.

                          static HANDLE gDoneEvent;
                          static HANDLE hTimer = NULL;
                          static HANDLE hTimerQueue = NULL;
                          int arg = 123; //will be passed to callback of timer

                          VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
                          {
                          if (lpParam == NULL)
                          {
                          //do some error handling her
                          return;
                          }
                          else
                          {
                          if(s_user_function!=nullptr)
                          s_user_function(12);
                          }
                          //SetEvent(gDoneEvent); //for periodic calls, don't set gDoneEvent
                          }

                          Now, in some function called from the main application, I allocate the timer and start it:

                          char timer_init(void)
                          {
                          // Use an event object to track the TimerRoutine execution
                          gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
                          if (NULL == gDoneEvent)
                          {
                          return 1;
                          }
                          // Create the timer queue.
                          hTimerQueue = CreateTimerQueue();
                          if (NULL == hTimerQueue)
                          {
                          return 2;
                          }
                          CreateTimerQueueTimer(&hTimer, hTimerQueue, (WAITORTIMERCALLBACK)TimerRoutine, &arg, 3000, 10000, 0);

                          WaitForSingleObject(gDoneEvent, INFINITE); //if we don't wait it crashes although handles are stored globally?
                              
                          //the handles should be closed and deleted only after timer had occured. 
                          
                          ////CloseHandle(gDoneEvent);
                          ////// Delete all timers in the timer queue.
                          ////if (!DeleteTimerQueue(hTimerQueue))
                          ////    printf("DeleteTimerQueue failed (%d)\\n", GetLastError());
                          

                          }

                          As you can see, I don't close the handles and delete the timer queue on purpose. Everything works fine as long as the process waits. The callback in the main application is called. If I comment out the line with WaitForSingleObject I'll get an access exception, although I stored all the handles globally. Teh exception occurs with the timing the timer would have. So most likely it tries to call the callback of the timer function but something is missing. What is it I'm overlooking? I need the dll running, not waiting inside a fucntion for a timer...

                          L Offline
                          L Offline
                          Lost User
                          wrote on last edited by
                          #12

                          Bodo2407 wrote:

                          I need the dll running, not waiting inside a fucntion for a timer..

                          I am not sure what you mean by that. When you create a timer, it is designed to call some function after a specified period of time, either once or repeatedly. The reason for having the call to WaitForSingleObject, is in case the main code needs the timer event to complete some action first. But it is not necessary if the remainder of the code does not rely on the actions of the timer event.

                          B 1 Reply Last reply
                          0
                          • L Lost User

                            Bodo2407 wrote:

                            I need the dll running, not waiting inside a fucntion for a timer..

                            I am not sure what you mean by that. When you create a timer, it is designed to call some function after a specified period of time, either once or repeatedly. The reason for having the call to WaitForSingleObject, is in case the main code needs the timer event to complete some action first. But it is not necessary if the remainder of the code does not rely on the actions of the timer event.

                            B Offline
                            B Offline
                            Bodo2407
                            wrote on last edited by
                            #13

                            Hi Richard, yes, I know that. I did put that in, because I got the exception and wanted to try the example code. This worked two times, so I thought it must be caused by leaving the function. Unfortunately thsi is not the case: After some debugging runs it seems that (in rare cases) it works a few times, but most likely it crashes immediately. So the observation I made that it is working if I call WaitForSingleObject isn't correct, it can crash randomly with or without. Unfortunately when it crashs the program pointer isn't on frame any more, so I don't see why it does that. One observation I made is that the handle of gDoneEvent is allocated in an entirely different address room (0x000003b0) than the other two handles hTimer and hTimerQueue(0x008b8928 and 0x008bsomething) which seems strange to me, since I thought allocated event should be also pretty close to the handles, but obviously it is not.

                            L 1 Reply Last reply
                            0
                            • B Bodo2407

                              Hi Richard, yes, I know that. I did put that in, because I got the exception and wanted to try the example code. This worked two times, so I thought it must be caused by leaving the function. Unfortunately thsi is not the case: After some debugging runs it seems that (in rare cases) it works a few times, but most likely it crashes immediately. So the observation I made that it is working if I call WaitForSingleObject isn't correct, it can crash randomly with or without. Unfortunately when it crashs the program pointer isn't on frame any more, so I don't see why it does that. One observation I made is that the handle of gDoneEvent is allocated in an entirely different address room (0x000003b0) than the other two handles hTimer and hTimerQueue(0x008b8928 and 0x008bsomething) which seems strange to me, since I thought allocated event should be also pretty close to the handles, but obviously it is not.

                              L Offline
                              L Offline
                              Lost User
                              wrote on last edited by
                              #14

                              Those numbers do not mean anything, except to the operating system.

                              1 Reply Last reply
                              0
                              Reply
                              • Reply as topic
                              Log in to reply
                              • Oldest to Newest
                              • Newest to Oldest
                              • Most Votes


                              • Login

                              • Don't have an account? Register

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • World
                              • Users
                              • Groups