Equivalent to run-time dynamic linking in .NET

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I have an unmanaged Win32 app that looks up the name of a DLL (unknown at
compile time) from an external location, loads it with LoadLibrary, and then
uses GetProcAddress on three exported functions (whose names and signatures
are known at compile time). The app then calls these functions as needed
throughout its execution. Depending on circumstances, there may be zero, one,
or many separate DLLs loaded that all conform to this pattern (that is, they
all export the same three functions).

I need to convert this app to managed code. It needs to perform the same
behavior: look up a DLL assembly (unknown at compile time) from an external
location, load it somehow and call its "exported" functions (presumably in
..NET these would be public methods of a public class).

Before I dive into the wonderful mess that constitutes System::Reflection,
so that I can supposedly drag the types and methods straight out of the DLLs
themselves, I have 3 questions:

1. Is using System::Reflection the preferred way of doing "run-time dynamic
linking" in .NET?
2. If not, what is the preferred way of handling this type of situation?
3. Does anyone know of any design pattern or at least a well-documented
example for this type of work?

Sean
 
Sean M. DonCarlos said:
I have an unmanaged Win32 app that looks up the name of a DLL (unknown at
compile time) from an external location, loads it with LoadLibrary, and
then
uses GetProcAddress on three exported functions (whose names and
signatures
are known at compile time). The app then calls these functions as needed
throughout its execution. Depending on circumstances, there may be zero,
one,
or many separate DLLs loaded that all conform to this pattern (that is,
they
all export the same three functions).

I need to convert this app to managed code. It needs to perform the same
behavior: look up a DLL assembly (unknown at compile time) from an
external
location, load it somehow and call its "exported" functions (presumably in
.NET these would be public methods of a public class).

Before I dive into the wonderful mess that constitutes System::Reflection,
so that I can supposedly drag the types and methods straight out of the
DLLs
themselves, I have 3 questions:

1. Is using System::Reflection the preferred way of doing "run-time
dynamic
linking" in .NET?

I hate to use terms like "preferred way". It is the way I do it.
2. If not, what is the preferred way of handling this type of situation?

FWIW: it's my choice.
Does anyone know of any design pattern or at least a well-documented
example for this type of work?

In an application of mine I do pretty much what you suggest to support .Net
"plugins".

In my case there is a base class that has the required method(s). I require
that the plugins be derived fom my base class. I use
Reflection::Assembly::LoadFrom() so that I can load the assembly from where
ever the lookup takes me. The assembly's GetTypes() method returns an array
of types in the assembly. It use the Type class's BaseType member to insure
that I have the kind of class hat I need. I used the GetMethod() member of
the Type class to perform a similar function to GetProcAddress(). Finally
the Invoke member of the MethodInfo class gets me the dynamic invocation
that I need.

Regards,
Will
 
Sean said:
1. Is using System::Reflection the preferred way of doing "run-time dynamic
linking" in .NET?

Here's an example of loading a plugin from an application (using
C++/CLI, assuming you're using VC++ 2005):

#using "Interface.dll"
using namespace System;

Reflection::Assembly^ assembly =
Reflection::Assembly::LoadFrom("Plugin1.dll");
Type^ t = assembly->GetType("Plugin.Plugin1");
Plugin::Interface^ plugin =
safe_cast<Plugin::Interface^>(Activator::CreateInstance(t));
String^ result = plugin->GetName();


The interface is published from the interface.dll assembly, declared as

namespace Plugin
{
public interface class Interface
{
public:
virtual String^ GetName() abstract;
};
}


And the plugin is implemented like this in Plugin1.dll:

#using "Interface.dll"

namespace Plugin
{
public ref class Plugin1 : public Interface
{
public:
virtual String^ GetName() { return "Hello"; }
};
}


The key is to ensure that each plugin DLL uses the same Interface as the
application. In .NET, just naming two classes the same way alone doesn't
ensure that the two classes are the exact same type. This plugin sample
only works if Interface is the exact same class in both the application
and the plugin. The most common way to ensure this is by referencing the
same Interface (for example, with the #using directive), instead of
#including the declaration for Interface. If you were to use #include
instead of #using, the two Interface classes would be completely
independent, and the application would fail to load the plugin.

In this example I chose to introduce a separate interface.dll, which is
referenced by both the application and the plugin. I believe you could
publish the Interface class from the application itself and reference it
from the plugin, but I haven't tried that. I'm not sure if #using is
allowed for an ".exe" or for a ".dll" only.

Tom
 
William DePalo said:
I hate to use terms like "preferred way". It is the way I do it.

I don't much care for the term myself, but I've yet to come up with a better
term that isn't a whole sentence. I was just trying to make sure that before
I invested time and effort learning about reflection that reflection was at
least a valid way of handling the problem, if not the "best" way.
FWIW: it's my choice.


In an application of mine I do pretty much what you suggest to support .Net
"plugins".

In my case there is a base class that has the required method(s). I require
that the plugins be derived fom my base class. I use
Reflection::Assembly::LoadFrom() so that I can load the assembly from where
ever the lookup takes me. The assembly's GetTypes() method returns an array
of types in the assembly. It use the Type class's BaseType member to insure
that I have the kind of class hat I need. I used the GetMethod() member of
the Type class to perform a similar function to GetProcAddress(). Finally
the Invoke member of the MethodInfo class gets me the dynamic invocation
that I need.

If only this paragraph had been in the Visual Studio documentation, it would
have saved a lot of time! Thanks!

Sean
 
Sean M. DonCarlos said:
I don't much care for the term myself, but I've yet to come up with a
better
term that isn't a whole sentence. I was just trying to make sure that
before
I invested time and effort learning about reflection that reflection was
at
least a valid way of handling the problem, if not the "best" way.


If only this paragraph had been in the Visual Studio documentation, it
would
have saved a lot of time! Thanks!

A couple things:

1. Don't use Assembly.GetTypes. Use Assembly.GetExportedTypes instead.
That way you don't waste time iterating through all the internal types (that
you won't be able to access anyway) and instead look only at public types.

2. Instead of defining a base class, define an interface - it's lighter
weight, and generally you don't need to impose an implementation base class,
but just a calling contract (i.e. an interface).

3. Put your plugin interface(s) into a separate assembly and strong-name
that assembly. That way your code and the plug-ins are strongly de-coupled
from one another - either can be recompiled independently without causing
the other to become obsolete. If you need to change the interface, then you
change the version number of the interface assembly which results in a
change to the strong name so there's no undetected interface
incompatibilities.

FWIW, the technique you're using for native code also works for managed, at
least if the managed code is written in C++. You can still have a
__declspec(dllexport) function in managed code - the compiler will emit an
unmanaged "thunk" that transitions to managed code and calls the actual
managed function. That "thunk" can be called from any Win32 application -
even one that's not (yet) hosting the CLR.

-cd
 
Tamas Demjen said:
In this example I chose to introduce a separate interface.dll, which is
referenced by both the application and the plugin. I believe you could
publish the Interface class from the application itself and reference it
from the plugin, but I haven't tried that. I'm not sure if #using is
allowed for an ".exe" or for a ".dll" only.

I think I read somewhere that #using "Something.exe" is allowed only if
Something.exe was compiled with /clr:pure or /clr:safe.

Thanks for the example code. Between your suggestions and Will's, I now at
least know that what I was thinking of is possible.

Sean
 
Carl Daniel said:
A couple things:

1. Don't use Assembly.GetTypes. Use Assembly.GetExportedTypes instead.
That way you don't waste time iterating through all the internal types (that
you won't be able to access anyway) and instead look only at public types.

Sounds sensible enough.
2. Instead of defining a base class, define an interface - it's lighter
weight, and generally you don't need to impose an implementation base class,
but just a calling contract (i.e. an interface).

No, I don't need a base class for this. The existing unmanaged app only
cares about the function names and signatures. After that, the DLL providing
the functions can do whatever it wants within reason. An interface will be
fine.

But say I define an interface, and I already know the names and signatures
of the three methods I need implemented, and I already know or can look up
from an external resource the name of the class that will be implementing the
interface, must I get all the exported types? Can't I just use GetType with
the known class name, create an instance of that type, safe_cast to the
interface type, and then call the needed methods, as in Tom's sample?
(Obviously I would put some exception handling in there.)
3. Put your plugin interface(s) into a separate assembly and strong-name
that assembly. That way your code and the plug-ins are strongly de-coupled
from one another - either can be recompiled independently without causing
the other to become obsolete. If you need to change the interface, then you
change the version number of the interface assembly which results in a
change to the strong name so there's no undetected interface
incompatibilities.
Certainly.

FWIW, the technique you're using for native code also works for managed, at
least if the managed code is written in C++. You can still have a
__declspec(dllexport) function in managed code - the compiler will emit an
unmanaged "thunk" that transitions to managed code and calls the actual
managed function. That "thunk" can be called from any Win32 application -
even one that's not (yet) hosting the CLR.

I was unaware that any of the __declspec specifiers worked in managed code.
Shows what I get for spending two years stubbornly refusing to use the
Managed Extensions and then trying to dive right in to C++/CLI.

Thanks for your help!
Sean
 
Sean M. DonCarlos said:
I don't much care for the term myself, but I've yet to come up with a
better
term that isn't a whole sentence. I was just trying to make sure that
before
I invested time and effort learning about reflection that reflection was
at
least a valid way of handling the problem, if not the "best" way.

I wasn't a criticism. :-) I've been hanging around here long enough that I
can tell when what I'm about to post is off the beaten path enough to think
about it.

See my reply to Carl.
If only this paragraph had been in the Visual Studio documentation, it
would
have saved a lot of time! Thanks!

You are welcome.

IME, reflection in any language is not something that most developers ever
need to do so you don't find a lot of examples.

If my experience is any predictor, the next issue you may have to deal with
is whether you host the common language runtime explicitly or let it happen
automatically by virtue of building a mixed-mode application.

Regards,
Will
 
Carl Daniel said:
1. Don't use Assembly.GetTypes. Use Assembly.GetExportedTypes instead.
That way you don't waste time iterating through all the internal types
(that you won't be able to access anyway) and instead look only at public
types.

Thanks. I didn't know that method existed.
2. Instead of defining a base class, define an interface - it's lighter
weight, and generally you don't need to impose an implementation base
class, but just a calling contract (i.e. an interface).

That's true but In my case, not only does the server need to communicate
with its plugins but the plugins need to communicate with the server as
well. For me, it made sense to put that stuff in the base class so that the
plugins could get it "for free".
3. Put your plugin interface(s) into a separate assembly and strong-name
that assembly. That way your code and the plug-ins are strongly
de-coupled from one another - either can be recompiled independently
without causing the other to become obsolete. If you need to change the
interface, then you change the version number of the interface assembly
which results in a change to the strong name so there's no undetected
interface incompatibilities.

We are in complete agreement here. I load the CLR, create an app-domain and
then load the strong-named assembly that contains my base class into it. And
just because I'm a control freak I used the stuff I found here:

http://blogs.msdn.com/junfeng/articles/229648.aspx

to forcibly shove my assembly into the GAC.

Regards,
Will
 
Sean said:
I think I read somewhere that #using "Something.exe" is allowed only if
Something.exe was compiled with /clr:pure or /clr:safe.

Carl's reply confirmed that it's a good idea to separate the interface
DLL from the application and the plugin. This separation also makes it
possible to reuse my plugins with other applications. After all, I'd say
forget about #using "App.exe" -- my original design was fine.

Tom
 
Sean said:
Sounds sensible enough.


No, I don't need a base class for this. The existing unmanaged app
only cares about the function names and signatures. After that, the
DLL providing the functions can do whatever it wants within reason.
An interface will be fine.

But say I define an interface, and I already know the names and
signatures of the three methods I need implemented, and I already
know or can look up from an external resource the name of the class
that will be implementing the interface, must I get all the exported
types? Can't I just use GetType with the known class name, create an
instance of that type, safe_cast to the interface type, and then call
the needed methods, as in Tom's sample? (Obviously I would put some
exception handling in there.)

Yep. If you already know the full class name you can dispense with the trip
through Get{External}Types().

-cd
 
William said:
"Carl Daniel [VC++ MVP]"
1. Don't use Assembly.GetTypes. Use Assembly.GetExportedTypes
instead. That way you don't waste time iterating through all the
internal types (that you won't be able to access anyway) and instead
look only at public types.

Thanks. I didn't know that method existed.

I didn't either until ~1 month ago - I was doing exactly what you described.
I'm not sure if that function is new in 2.0, but I don't think so.
That's true but In my case, not only does the server need to
communicate with its plugins but the plugins need to communicate with
the server as well. For me, it made sense to put that stuff in the
base class so that the plugins could get it "for free".

Still, if your base class has an implementation error in it, you end up
recompiling both your server and all of the plugins (which may not be a
problem for your app, but it would be for some). I'd define two interfaces:
one that the plugin implements and one that the server implements. Pass the
server interface to an "Initialize" method on the plug-in interface. With
those in place, I'd publish the source code to the base class - it's just a
sample at that point.
We are in complete agreement here. I load the CLR, create an
app-domain and then load the strong-named assembly that contains my
base class into it. And just because I'm a control freak I used the
stuff I found here:
http://blogs.msdn.com/junfeng/articles/229648.aspx

to forcibly shove my assembly into the GAC.

Excellent!

-cd
 
Back
Top