views:

159

answers:

1

I'm writing an application in C that can be extended at runtime by means of modules / shared objects / DLLs. Those modules may use the API of the existing program but may also provide new functions for use in later loaded modules, so there is the possibility for modules to have dependencies on each other.

My current approach under Linux is to have every module define a depends() function that returns a list of other module names it depends on. That way, I can compile and link every module for itself, load a module with dlopen() and RTLD_LAZY, resolve its dependencies first and then fully load it with RTLD_GLOBAL. This works just fine and does exactly what I want. It also allows me to replace a module with a different version without recompiling all other modules depending on it.

The actual problem arises when porting this to Windows. First, I haven't found any way to link a DLL without already providing it with the export symbol tables of all its dependencies. Is there one I've overlooked?

Second, LoadLibraryEx from the Windows API doesn't seem to be able to perform any lazy loading because instead of letting me handle dependencies, it goes ahead and loads all referenced DLLs itself before it even returns. Since I'd like to perform version checking as well before actually loading modules in the future, this it not at all what I want. Is there any way to circumvent this behaviour?

The third odd thing is that I cannot replace a DLL without recompiling all other modules depending on it. It actually does work sometimes, but usually wild things start to happen or the program segfaults.

Is it even possible to write a modular application like that on Windows? Any suggestions or different approaches are highly appreciated!

Update: Just to provide some clarification on how my modules use each others functions on Linux (which I would like to have on Windows as well): Every module just returns the name of another module it would like to call functions from in the described depends() function and includes its header, then calls the used functions directly in the code without any wrapping. This works because Linux does not require you to have all symbols resolved at link time for shared objects.

+3  A: 

You can export all functions manually (using __declspec(dllexport)) and load them using GetProcAddress. In this case you need to know the signature of each function, and you're limited to C functions only, but this is going to work. If you compile both modules, your C functions can also return C++ classes, more on this later. Using GetProcAddress & LoadLibrary makes modules totally independent. Basically, you do the linking manually, but as I understand, this is what you do on Linux, right?

LoadLibary only loads the libraries a library is dependent on, so make sure they don't load depend on each other. Either they are really independent, or they aren't, if done properly, changing one library will not force a recompile of the other (as you don't link them together).

A good idea is to use something like COM, so make each of your libraries return an interface, instead of individual functions. That way, you can simply load a whole DLL and also link them easily together (passing a DLL -> passing an object). Look up XPCOM and COM, it's actually very easy to do.

Anteru
Alright, your suggestion about `GetProcAddress` already helps a bit. I just tried it out and calling a function from a DLL that way works without having linked against it. But this would mean that I couldn't just let a module include the headers of the one it depends on like in Linux, right? I would have to somehow collect all needed function pointers with `GetProcAddress` first before calling anything from the parent module.Thanks also for the tip with COM, I'll have a look at that.
smf68
Yes, but as I said, simply group all functions for one module into a class, and call that. That's much easier and it also reduces the number of exported symbols. It's not really COM, as you don't have inheritance involved, it's just based on a similar idea. Your clients use the interface, not knowing the actual implementation behind it.
Anteru
Interesting topic. So there is no way to do it without that the developer of a module must have knowledge about WinAPI and/or COM? :-(
Gregor
Of course there is, you can do something like COM, without using COM actually. As I said, in the simplest case, you have something likestruct Interface { your methods here ... };, and your module consists of extern "C" Interface* GetInterface(); -- so you get the address of `GetInterface` only, and work on that module pointer.
Anteru
Ah ok, I misunderstood this one with the "Interface".
Gregor
Since it doesn't seem like there is another way to do it, I'm marking this as answered.
smf68