GUI from a DLL


#1

Hi,

I am writing an application using the JUCE framework, and it is a most pleasant experience. I am now facing a bit of a challenge:

I need to retrieve a pointer to a Component from a DLL, and display this Component in my main application.

This is mostly fine. I can easily compile the DLL, and it is loaded with the platform specific loader function, and the getComponent() entrypoint is found and called, and I end up with a valid component pointer. The DLL Component is created using the jucer application and contains graphics and sub-components.

At first, attempting to addAndMakeVisible the component from the DLL would crash my app; this was solved by making the initJuceGui() call in the DLL onload() function. For those that are interested, in linux you can do that like this:

#ifdef __cplusplus
extern "C" {
#endif

void __attribute__ ((constructor)) onLoad(void);
void __attribute__ ((destructor)) onUnload(void);

#ifdef __cplusplus
}
#endif

// Called when the library is loaded and before dlopen() returns
void onLoad(void)
{
	initialiseJuce_GUI();
}

// Called when the library is unloaded and before dlclose()
// returns
void onUnload(void)
{
    shutdownJuce_GUI();
}

Now, this means that the component in the DLL is using a different static MessageManager, etc, etc. Which brings me to the problem in hand: the component displays properly, but is not interactive - clicking on controls does not do anything. I figure this is because my main application is not feeding it messages (why would it?).

The component from the DLL is deliberately ‘hosted’ in a component in my application, so I was aiming to pass all messages in the parent (application) component to the child (dll) component. But I dont know how to do this, or whether it would even work.

Also, since the handleMessage() member of Component is overloaded from MessageListener which is inherited as protected, I can’t overload it, so I dont know the way into the message system even if this is a good idea.

Can anyone help?


#2

Yes… that’s a tricky one. The only real way around it would be to build juce as a shared library and link to it from both your app and the other libraries that use it, so that there’s only one instance of it. That’s something that’s possible in the windows build, but not something I’ve ever thought about on linux…


#3

…but I must add that I can’t really think of a case where this would be a practical solution…

It’d only work if both the app and all the other modules are built with exactly the same version of the juce library, so I don’t think it’d be very robust as a way of allowing 3rd party extensions. So, since you’d probably be building all the modules yourself, and you can’t mix-and-match between different versions of them, you might as well just link it all statically.

What’s your actual reason for deciding to use DLLs?


#4

I am building it all myself, and I dont mind enforcing the (admitedly not very robust) practice that the plugins must use the same exact library.
I dont really want to go the shared library route - what I’m really writing is a plugin host type affair, the DLL returns an instance of an audio plugin (JUCE native class, not VST or anything fancy pants) and I query this to get a JUCE Component that can then be neatly integrated with my interface.

Visually I want to, within the JUCE framework, obtain a component from a DLL and integrate it seamlessly (i.e. doesnt pop up in a new window) into my interface.

… the best example I can think of is Propellorheads Reason style, rather than Cubase VST style.

I am open of course to any wisdom on how to do this really in any way at all.

Back on my message madness though (its the only way I have come up with so far),

Can you suggest any way of ‘breaking into’ the message system?

or, could I perhaps ‘reflect’ the component from the DLL (by querying the component class to determine the subcomponents and graphics it contains) in order to programatically create at runtime a Component class in my own application space (the trick then being mapping callbacks from the reflected components to the ones in the DLL, could be horrible)…

It seems to me that the best plan would be to loop over all the subcomponents in the DLL’s main component, and somehow register each one with the application’s MessageManager…

Is this a go-er do you think?


#5

No, I don’t think that’d be a goer. You might manage to get something working, but although there aren’t a lot of static objects in the library, you’re bound to come across other strange problems caused by some other obscure statics being in two different places.

If you’re building everything yourself, I’d always opt for the “one big exe” approach. Easier to build, easier to install, faster, smaller code size, and users can’t mess it up by moving your dlls around. Even if you were selling the plugins individually, I’d probably still choose to put them all into the exe and selectively disable them. There’s just so much less to go wrong when it’s done that way.


#6

I dig what your saying man.

I know I’m testing your patience here but I’d really like to get the DLL approach working. This is because some of the audio processors rely on old and obscure static libraries, and I dont want to pollute my main app with these - nor do I want to have to conform all the audio plugins to use the same static library if they use different versions of the same one, which I would have to do if they were statically linked.

If you can think of a way that isnt going to land me in horrible trouble of getting the Component GUI from DLL I would be really happy - let me know :-). I dont mind if its a little unorthodox.

What I will do until I think of something better is take your advice about the components, but not about the DLL’s. Its not beautiful but I can cope with putting the GUI interfaces of the plugins into the main exe, but the processing engines in the DLLs. I think.

(BTW I hope it goes without saying that when I finish this project I will be purchasing a licence for your fine libraries.)


#7

I reckon the way I’d do that would be to wrap those obscure libraries up in a DLL and put all your own code in an exe.

Anyway, no, sorry, there isn’t a simple answer to what you’re trying to do, it’s just not something that C++ is good at. You might be able to bodge it to get it working, but there’s always a danger that there’ll be a static in there that’ll cause obscure and subtle bugs.

but this did set me off wondering how easy it’d be to move all the library statics/singletons into a single class, and have a single object that contains the entire working state of the library. Then a module would only need a single static pointer to this, which could be replaced. Probably not actually possible or efficient to do it that way, but it’s an interesting thought.


#8

Under Window, you can declare all static object to be in a static section. I think it has something to do with #pragma data(shared) or something like that, IIRC.
In Linux you can ensure your static are common in the dynamic loaded library by forcing them in a shared memory area (this is tricky, but it’s possible with linker directives).
Currently, in Juce, static object are not MACROized so you’ll have to grep for “static [^(]*;” to locate all the instance and change them yourself.

As far as I know, everybody else write a kind of API to enforce / simplify the work and the mess of different versions. You can do so in C++ by writing a wrapper around the component functions & a factory.


#9

Guys,

Im kinda overwhelmed with the support here!

This level of service is just staggering.

@jules, cheers for putting my mind to rest - at least I know i’m not just missing something obvious. It looks to me like the propellorheads guys made the same decision, perhaps for the same ‘Reason’ (geddit).

@X-Ryl669 im only bothered with linux on this project really - I will see if I can take the approach you suggest. Do you have a link on how to force static externs in a .so in linux? its not an oft talked about area.

Jules, whats your steer - shall I try out the above - do you think I’d ever get it to work?


#10

Sure, you could probably get it to work, but it might burn a lot of your time, both directly and indirectly because it’ll make development much more of a PITA. I wouldn’t recommend bothering unless it’s essential to your business case.


#11

I agree with Jules. You can’t just rely on just using this (see attribute(shared)) on all static objects and then everything would work magically.
If you don’t intend to publicize your plugin API, it’s probably a lot of work for few reproduceable results.

If you intend to publicize your plugin API, then I would probably move all the static object to getter functions like replacing:

static MyObj obj;
// By
static MyObj & getObj() { static MyObj obj; return obj; }

A macro here would have done wonders, so this code

DECLARE_STATIC(MyObj, obj);
// Could expand to (on static lib)
static MyObj & getObj() { static MyObj obj; return obj; }
// Or this (in DLL)
extern MyObj & getObj();

Again, that’s not enough, as for this to work, the EXACT type of the MyObj must match between both the plugin and the executable. So either you’re using kind of version-immutable objects, or you’re defining an API (like this:

class MyAbstractObj
{
    public:
     // Copy here all your object method as pure virtual method
     virtual void getSomething() = 0;
};
// And instead of using the static objects, you'll have to call the implementation from your plugin
// In executable
extern MyAbstractObj & getObj() = DLLOAD(...)
// In plugin
MyAbstractObj & getObj() { static MyImplObj impl; return impl; }

G++ warranty the C++ ABI from v4.0, so at least, the virtual table won’t change in any case, if you don’t change the method signature.
Like I said in my previous post, you probably want to “abstractize” the Component class, and implement a wrapper in your code.


#12

Ok gents,

Taken on board.

A double edged sword inherrant in the way JUCE is written is simplicity and a C++ OO paradigm event model on one side, but a degree of inflexability with regards to event handling/messaging on the other.
I am glad the balance is the way it is though. The only design pattern I really like that tackles all this is the C# event delegate model, which is a poor fit for the C++ language.

In case its interesting (I think its fairly obvious but just in case anyone else comes up against this) my solution is:

My audio processors will reside in DLL’s, and their parameters accessible using a hash list type class providing thread safe setting, getting and automation, similar to the way the JUCETICE JOST project does things.

By enumerating the generic parameterbag class one could make a generic interface (LADSPA style), but for prettyness I shall have a custom GUI component for each processor, and these are implemented in the main application.

A single factory class in the main application is used to create an instance of an audio processor. When this occurs, the processor name is queried, and used to look up the corresponding GUI component, which is instantiated as well. The factory class then sets the new GUI component pointer to the audio processor so both things are in one place.

Then I’m back in a good place, with an audio processor class, that has a pointer to a GUI interface component in it.

A function in the factory class will be used to destroy the audio processor, and this will also free the component.

we’re back on track :slight_smile:


#13

I still don’t understand what you expect to actually gain by having these DLLs? It’ll make your development process much harder - will your customers benefit from it in some way?


#14

This is a homework project - and as such I need to keep up my interest which means sometimes I need to see results, as a kinda reward for getting the more boring stuff done.

By using DLL’s here (which work and aren’t proving any hassle at the moment at all) I can use old synths I have hastily hacked from old projects. I wouldnt want to put that mess into my nice new application.

This gives me a good dev route because I can improve on the code in the DLL (remove rotton old dependancies, remove layers and layers of wrappers, and clean up). Also, I can (depending on success) pay someone to write a conforming plugin or port of one of their existing plugins.

Its purely for me, and keeping my main development environment with the minimum dependancies as the project is to run on an embedded system. customers will see no difference.


#15

Well, if it was me, I’d stick the old code in namespaces rather than DLLs - much easier!


#16

especially since were both in london perhaps I could ask you to have a look at it when its a bit more finished :slight_smile:


#17

Sure, always interested in what people are using my stuff for!