Thread: How to share a callback function between two instances of a DLL

  1. #1
    Registered User
    Join Date
    Feb 2008
    Posts
    147

    How to share a callback function between two instances of a DLL

    To share data between different dll instances, you must compile with

    Code:
    #pragma data_seg(".shared_data")
    int example=0:
    ...
    #pragma data_seg()
    but in this link Microsoft says you can not share pointers or pointer to callback functions.

    I have a DLL that acts as a kind of driver reading serial port activity. One function is called from exe program (I have no access to exe file code) to register callback notification function. Then DLL launch a thread and when a condition is done far notification function is called. But as the thread is loaded in other address space, the shared pointer is not valid. I think I have only two alternatives:

    1) a way of sharing pointers with its segment base address, but I think this is not allowed. do you know if possible?

    2) implement the solution without threads, maybe hooking an event, but I don't know how to hook serial port activity. do you?

    any solution to 1 or 2, or any other workaround?

    thx all.

  2. #2
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    I don't understand why you want to put the callback function pointers into shared memory - that doesn't really make sense. Each process that loads your DLL will register their own callbacks that are specific to that process.

    It sounds like what you want is to have a single thread no matter how many processes load and register callbacks with your DLL. Then the thread needs to trigger all callbacks to be called, with some data, in all processes. Does that sound right?

    gg

  3. #3
    Registered User
    Join Date
    Feb 2008
    Posts
    147
    I need samething to callback functions. Only one exe will execute the dll. I explain the process:

    1- The exe load the dll.

    2- the exe call Registercallback functions with pointer to function of the exe that will be triggered when certain events occur.

    3- when all function for events has been registered (or when the dll load), the dll launch a thread in order to manage the events. When events happens, callback function will be called

    4- as a new thread has been launched inside the dll, a new copy of the dll is loaded in memory, with his own address space. As pointer registered in step 2 are refered to instance one of the dll (the instance that the exe own), they can not be used in the thread, as the thread has a different offset for address space , and pointers are always based on the offset of the process they belong to. This is refered in the MS article I mentioned as you can not share pointers between instances of dll.

    So I dont know how can I make a driver to process events that callback without making a new instance of the dll (thread launch)?

    any suggestion?

  4. #4
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> as a new thread has been launched inside the dll, a new copy of the dll is loaded in memory
    No, there's not. There's only one *instance* of a DLL and its data per process - no matter how many threads. If you want to invoke the callback from within the thread, that's fine. The MS article is referring to multiple processes loading your DLL.

    >> Only one exe will execute the dll.
    Then the MS article doesn't apply to you.

    gg

  5. #5
    Registered User
    Join Date
    Feb 2008
    Posts
    147
    Hi. Thanks for the answer. Then I have a strange bug I don't see. I post you an example:

    Code:
    #include <stdio.h>
    #include <windows.h>
    #include <process.h>
    #include "debug.h"
    
    #define OK		1
    #define ERR		0
    
    int (__stdcall *f_status) (char *) = NULL;
    
    void thread_func(void *dummy) {
    	debug_msg("Thread running");
    	f_status("Thread running");
    }
    
    
    int __declspec(dllexport) __stdcall _RegisterStatusFunc (int (__stdcall *statusfunc) (char*)) {
    
    	long thread1 = 1;
    
    	debug_msg("Registering 'RegisterStatusFunc'");
    	f_status=statusfunc;
    	f_status("First call to f_status successfully");
    
    	thread1 = beginthread(thread_func,0, NULL);
    
    	if (thread1 ==  -1) {
    		debug_msg("Error creating main thread");
    		return ERR;
    	}
    	debug_msg_si("Thread ID",thread1);
    	return OK;
    }
    
    BOOL WINAPI __declspec(dllexport) DllMain (HINSTANCE hInst, DWORD Reason, LPVOID Reserved)
    {
      if(Reason==DLL_PROCESS_ATTACH) {
    		init_debug();
    		debug_msg("PROCESS_ATTACH");
    		f_status=NULL;
    		return   TRUE;
      	}
    
      if(Reason==DLL_PROCESS_DETACH) {
    		debug_msg("PROCESS_DETACH");
    	  	return   TRUE;
      	}
    
      if (Reason==DLL_THREAD_ATTACH) {
    		debug_msg("THREAD_ATTACH");
    	 	return TRUE;
    	}
      if (Reason==DLL_THREAD_DETACH) {
    		debug_msg("THREAD_DETACH");
    	 	return TRUE;
    	}
      return   FALSE;
    }
    if I run this dll with the line:

    Code:
    thread1 = beginthread(thread_func,0, NULL);
    it crash.
    If I run this dll, but replace this line with:

    Code:
    thread_func(NULL);
    without using a thread, it does not crash.

    In first case, I see the thread running, as I have a log in my debug file. It crash when call to f_status.
    Second case, not using threads, it does not crash and the status message is send to the calling program.

    Maybe is a calling convention problem, or a call with different code of compilers, ...... I dont know.

    I have tried the above code with differents compilers (VC, BCC55, lcc32, ...) and I have this problem with all.

    Any idea? It looks simple, but I dont see the point.

    thx in advance.
    Last edited by Kempelen; 03-03-2008 at 05:11 AM.

  6. #6
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    There's nothing terribly wrong with what you posted ("WINAPI" should be directly before "DllMain").

    Have you tried using a very simply callback that does nothing but "printf" the parameter and return? There may be issues with your how your client is using the DLL. Post a simple version of the client code that reproduces the issue with DLL code you already posted.

    gg

  7. #7
    Registered User
    Join Date
    Feb 2008
    Posts
    147
    Hi,

    I wish, but I have no code of caller programs. I only has one and it works perfect. Also I wrote a simple exe that make a callback printf. It runs ok.

    Anyway it causes me surprise. I can not understand how a dll would work with certain programs ok if no thread is launched, and run bad if it is launched. So dont think it wouldbe a coding fault. What in the code would be diffirent that crash with threads? I dont understand.

    These are things that I think about what is happend:

    - Exe and dll have different calling conventions (__sdtcall, __cdecl, ....) that I am not using them in the correct way. In fact, if I replace __stdcall with __cdecl and it works in the dll that not use threads.

    - Maybe a problem with the use of the stack internally.

    - the callback mechanism is not thread safe.

    - I dont know much of object oriented programming, but the callers exe look made in Visual Basic, Delphi and others. If a thread is launches, the object could changes its address. isn't it? I don't know how internally the object are managered.

    - it is a compiler issue. I am going to try to make the dll sample I posted in other compiler like VB.

    Don't know.... these are only speculations. any help?

  8. #8
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    How is it that you don't have the code for the client? Are you trying to replace an existing DLL?

    Sounds like the client just isn't expecting the callback to come in on a separate thread.

    >>Also I wrote a simple exe that make a callback printf. It runs ok.
    More evidence that the problem is not yours, but the clients.

    gg

  9. #9
    Registered User
    Join Date
    Feb 2008
    Posts
    147
    I have been working on this issue, and I am afraid that maybe I can not implement an easy solution.

    Yes!, I am trying to replace a DLL of which I have a rudimentary documentation of the API and working proccess. I want to use same exes with my RS232 device as it was connected to the original one.

    In one of my tests, I launched the thread but without calling callback function f_status. As expected, call to f_status from main thread don't crash, even the second thread is running. Only when I made a call from second thread is when it crash!. This has a conclusi&#243;n: called routine depends on the "local environment" of the main thread, maybe TLS slots.

    * Is it there any way to know the index of the current thread TLS slot?: I think I cant. I have try to allocate index to TlsAlloc, but new ones are done. I don't know of any way of knowning an index previously allocated. Only if passed as argument from the main thread, isn't it?

    * Other solution I am thinking is making a thread with CreateRemoteThread. Do you think it will work?

    * i am ussing beginthread to launch my event inspector. do you think this is "safe". would I use other functions like CreateThread or beginthreadex? I read comments here and there about the safe side of thouse thread functions, but dont know exactly what I have to do.

    I am suspicius about original DLL becuase I think it must launch threads in order to work property, but dont know how it has solved my problem.

    best regards,
    FS

  10. #10
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    >> I have try to allocate index to TlsAlloc...
    I don't think TLS is going to help at all in this situation.

    >> ... CreateRemoteThread.
    This is for creating a thread in another process. Not needed.

    >> i am using beginthread ...
    That's fine.

    >> I am trying to replace a DLL of which I have a rudimentary documentation
    If you can share with us what DLL you're replacing and any documentation, we may be able to give you additional advice on how to implement a replacement.

    gg

  11. #11
    Registered User
    Join Date
    Feb 2008
    Posts
    147
    At the end, I have discovered what the problem is. It is not a problem of the interfaze implementation. The problem happends only when I callback Visual Basic programs. It is in this situation that the program crashs when calling back functions from another DLL thread.

    In this link, about multithreads in VB, you can read:
    Multithreading DLL

    A multithreading DLL does not actually create its own threads. It is simply a DLL that creates objects that run in the same thread that requests the objects. For example: a multithreaded ActiveX control (which is a DLL) creates controls that run in the same thread as the form that contains the control. This can improve efficiency on a multithreaded client such as an Internet browser.
    So, I am creating threads which are not dependable of the main one, as the C DLL is programmed in low-level and not in VB. ..... it looks like the C DLL must support the Apartament-model of Visual Basic.

    So, the solution looks like I have to create an object with a thread inside and make dependable on the main thread of the program in Visual Basic. How this can be done?

    Maybe I must use C++? or maybe the best solution is to make the DLL in VB? or register the DLL?

    P.S. : In this link it is possible to read people having the same problem.
    Last edited by Kempelen; 03-07-2008 at 03:48 AM.

  12. #12
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Some solutions:
    One solution is to scrap VB entirely. It's very inflexible with these things.
    Don't do a callback to any VB function on a different thread.
    Upgrade to VB.NET.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  13. #13
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    http://support.microsoft.com/kb/198607/en-us

    You can not make a simple callback into VB from a thread that you created. Period.

    >> it looks like the C DLL must support the Apartament-model of Visual Basic.
    A DLL or thread does not "support" the "apartment threading model". This term applies to COM objects/servers. The simple DLL code that you've posted so far, _RegisterStatusFunc(), has nothing to do with COM. It's just a simple DLL. And since you say you are replacing a DLL I can only assume that it is also a simple DLL that has nothing to do with COM.
    Quote Originally Posted by Codeplug View Post
    >> I am trying to replace a DLL of which I have a rudimentary documentation
    If you can share with us what DLL you're replacing and any documentation, we may be able to give you additional advice on how to implement a replacement.
    gg

  14. #14
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Kempelen View Post
    but in this link Microsoft says you can not share pointers or pointer to callback functions.
    Of course you can't. Those pointers are pointing to locations in another process's address space and are meaningless.

    If you really want multiple processes to be able to interpret each others pointers, you should be using threads.

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    C# may be what you need as a bridge between VB and C. Mixing VB and C is not something I would even attempt. I'm not even sure C# can call out to VB but since it is .NET you may be able to do it. If your VB is non .NET I'm not sure it will work.

    Another alternative is to create an ATL COM object that would wrap the calls from VB and to VB. Since COM is not language dependent you would be able to use it as a bridge to the VB code and back to C++.

    VB call ---> COM wrapper ---> C function
    C call ---> COM wrapper ---> VB method/function

    You could also use a Singleton without lazy instantiation that would ensure that the DLL could access the object it needed to use.

    MySingleton::getInstance()->DoSomething();

    Since getInstance() would merely return the existing instance you should be ok. You would then need a specific createInstance() that would be responsible for instantiation of the Singleton. It's a bit of a derivative of the common Singleton pattern but in some cases it is needed.

    Without knowing more about what it is you are trying to do (in more exact terms) anything I offer up is merely a guess. I'm not sure why this is on the C board since it could be done in C++ unless you are restricted to just using C.
    Last edited by VirtualAce; 03-07-2008 at 08:39 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 4
    Last Post: 05-13-2011, 08:28 AM
  2. Compiling sample DarkGDK Program
    By Phyxashun in forum Game Programming
    Replies: 6
    Last Post: 01-27-2009, 03:07 AM
  3. DLL function by ordinal
    By George2 in forum Tech Board
    Replies: 2
    Last Post: 02-07-2008, 01:08 AM
  4. <Gulp>
    By kryptkat in forum Windows Programming
    Replies: 7
    Last Post: 01-14-2006, 01:03 PM
  5. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM