Original: Paul Dilascia Translation: Northtibet Download Source Code: Catwork0510.exe (273KB) Original Source: Writing, Loading, And Accessing Plug-Ins In the MSDN magazine article in January 2005, you have an example program code is mixed. Written pattern. Is there possible to dynamically load the .NET class or DLL and call those functions? Suppose I have a native C app, I want to allow users to write plugins for the C program in .NET. Just like using LoadLibrary load DLLs in .NET.
Ravi Singh
I am writing a plug-in application with Visual C 6.0, which is a DLL, output, and receives a pure virtual interface pointer. After loading the DLL, the exe adjusts the C function output in the DLL, which returns a pure virtual interface pointer. Then EXE calls the method on the interface and sometimes passes back another interface pointer to the DLL process. Some people must write plugins with C #, Visual Basic .NET and other languages. I don't have anything based on .NET programming experience, I don't understand communication between hosting and non-hosting code, I found many information about this, but the more I got. How can I make the user write a .NET language-based plugin?
Daniel Godson
In MSDN Journal, 2003, there was an article about plug-ins, but I didn't mind reviewing this theme here, especially because the plugin itself is a plenty of plenty of parties (see: Plug-ins: let users add functionality to your .NET Applications with macros and plug-ins. After all, one of the main purposes of Microsoft .NET framework is to provide a language-independent system for writing reusable software components. From the first "Hello, World" program to the present, this has become a high-level criterion for software development. Reusability from copy / paste into subroutines, then to static link libraries, then to DLLS, and more professional VBX, OCX, and COM. Although the last three things belong to different topics (they are native DLLS), the NET framework marks a real start, because all code is compiled into a Microsoft Intermediate Language (MSIL). The interoperability is an indispensable component because all code is the same in the public language time level. This makes it easy for programs that write a plug-in architecture that supports language neutrality. So how do you use this advantage in your C program? Daniel's virtual function pointer system is a manual COM. It is the essence of COM objects: pure virtual function pointer. You can use COM for the plugin model, developers can write plugins for any .NET language, because this framework creates and uses COM objects. It is well known that COM coding is very complicated, as it needs to be considered, such as registration, reference count, type library, etc. - these things are enough to make you think COM is just "cumbersome object model" (trouble object model). If you are writing a new code and trying to simplify your daily work, use .NET to directly implement a plugin model, I am now discussing this topic. First, let me answer Ray's question, that is, there is something like LoadLibrary in .NET, the answer is: Yes, you can use the static method system :: askEMBLY:: LOAD to load any framework assembly (that is, a .NET class DLL). In addition, .NET supports reflex mechanism. Each assembly provides all the information you need, such as what kind of program set, what method, and what interface. There is no need to care about the things such as Guids, registration, reference counting. Before I show a more general plug-in system, I will start from a simple example. Figure 1 is a C # class that provides a static function Sayello. Note Unlike C / C , the function is not output in .NET; each function must belong to a class, although this class can be static, that is, it does not need to instantiate. In order to compile myLib.cs into a library, you can do this: CSC / Target: library mylib.cs compiler will generate a .NET assembly named mylib.dll. In order to transfer Sayhello from C by hosted expansion, you have to write this: #using
Using namespace mylib;
void main ()
{
Myclass :: SayHello ("Test1");} The compiler links to myLib.dll and calls the correct entry point. All this is simple, it belongs to the basis of .NET. Now suppose you don't want to link MYLIB when compiling, but want to perform dynamic links, just like the C / C uses LoadLibrary. After all, the plug-in is nothing more than the time to run, after you have generated and delivered the application. The things you do in Figure 2 are the same as the aforementioned code segment, but it is dynamically loaded with MYLIB. The key function is Assembly :: LOAD. Once you load this assembly, you can call assembly :: gettype to get TYPE information about class (note that you must provide full-defined namespaces and class names), and then call Type :: getMethod to get information about the method, Even call it, just like this: MethodInfo * m = ...; // Get IT
String * args [] = {"TEST2"};
M-> Invoke (NULL, ARGS); the first parameter is an object instance (null in this case, because Sayello is static), the second parameter is an Object (object) array, understand? Before you continue to discuss it, I must point out that there are several LOAD functions. It is very easy to make us intact. One problem with .NET is designed to solve is the so-called DLL Hell (DLL Hell) problem, when several applications share a common DLL and want to update the DLL often happens to this problem - it can make some Some applications crash. In .NET, different applications can load different versions of the same assembly / DLL. Unfortunately, DLL Hell now turns to Load Hell, because the rule of loading assembly is so complicated, I can write a column to describe it. The process of loading and putting the program set to your program is called fusion, even the framework has a special program, and FusLogyW.exe (Fusion Log Viewer) is doing this, you can use it to determine the load. Which version of the assembly is available. Just as I said, how to completely describe how the frame is loaded and bonded, and how it defines "Identity" to say clearly. But for plug-ins, just consider two functions: askMBLY:: LOAD and Assembly :: loadFrom. Assembly :: LOAD parameters can be a complete or part of the name (for example, "MYLIB" or "MyLib Version = XXX" Culture = XXX "). The test program in Figure 2 loads" MYLIB ", then display the complete assembly Name, as shown in Figure 3: Figure 3 Test Program Assembly :: LOAD Use the framework discovery rule to determine which file is actually loaded. It is given in the GAC (global assembly buffer: global assembly cache) The path and the directory where the application is located and the paths such as this kind of path are found. Another function assembly :: LoadFrom enables you to load an assembly from the external path. This is a little blurred if the same assembly (determined by the same rules) Loaded from different paths, the frame will be used. So LoadFrom does not always use the assembly specified by this path, although most of the time can be used correctly. Dizziness? There is another method is asmble: : LoadFile, it always loads the path of the request - but you can't use LoadFile, because it does not solve the dependency problem and cannot load the assembly to the correct environment (loadFrom). Don't know all the details I will simply discuss LoadFrom, with this, for a simple plugin model, it is a very good function. The basic idea of such a model is to define an interface and then let others write classes that implement this interface. Your app can call Assembly :: LoadFrom to load the plugin, and use the reflection to find the class that implements the interface you defined. But before you do it, there are two important questions to ask: Your application needs to be running Uninstall or reload the plug-in? Your program needs to consider secure access to files or other resources that you must use to plugins? If you have the answer to both questions, you will need Appdomain.
In the framework, there is no way to uninstall an assembly. The only way is to load the assembly to separate Appdomain and uninstall the entire appdomain. Each appdomain can also have its own safety license. AppDomains has an isolated processing unit, typically manipulated by a separate process, generally used in server programs, and the server is basically running around the clock (24x7) and requires dynamic loading and unloading components without restarting. AppDomain is also used to restrict the permission obtained by the plug-in so that an application can load non-trusting components without worrying about its malicious behavior. To enable this isolation, you need a remote mechanism to use multiple Appdomains; different AppDomains cannot call each other, they must be encapsulated across the Appdomain boundary. In particular, the class sharing instance must be derived from MarshalByrefObject. This is Appdomains I have to say now. Next I will describe a very simple plugin model that does not need Appdomains. Suppose you have generated an image editor, and you want other developers to write plugins to implement special effects such as exposure, blurring or to gain of partial pixels. In addition, if you have database ownership, you want to let other developers write special import / export filters to convert your data and their custom file formats. In this case, the application loads all plugins at startup, the plugin has been reserved, other, it has been to the user to exit the program. This model does not require a server program to have a reload function, and the plugin has the same security license with the application itself. So there is no need to use Appdomains; all plugins can be loaded into the main application domain. This is a typical mode of use of desktop applications. In order to truly implement this model, first define the interfaces that must be implemented each plugin. The interface is actually like a COM interface, which is an abstract base class that defines the properties and methods that the plugins must be implemented in this class. In the example of this article, I wrote an extensible text editor, named PGEDIT, with a plug-in interface ITextPlugin (see Figure 4). ITextPlugin has two properties, Menuname and MenuPrompt, and a method Transform, which takes a string parameter, processes the incoming string, and then returns a new string. I have implemented three specific plugins for PGEDIT: PlugIncaps, PluginLower and PluginscRamble, which features uppercase, lowercase, and chaos. As shown in Figure 5, the three plugins of PGEDIT are added to the EDIT menu. Figure 5 PGEDIT with three plug-ins I wrote a class called CPluginmgr, which is responsible for managing plugins (see Figure 6). PGEDIT When you call CPluginmgr :: loadAll loading all plugins: BOOL CMYAPP :: InitInstance () {
...
m_plugins.loadall (__ typeof (itextplugin);
} This m_plug-ins is an instance of CPLugINMGR. The parameter of the constructor is a sub-directory name (the default value is "plugins"); loadAll searches for the folder lookup assembly, and the classes included in the program have implemented the requested interface. When it finds such an assembly, CPluginmgr creates an instance of this class and adds it to a list (STL Vector). Here is a key code segment: for (/ * Each type in assembly * /) {iface-> isassileFrom (type)) {
{Object * Obj = activator :: createInstance (TYPE);
m_Objects.push_back (obj);
COUNT ;
}
} In other words, if the type (TYPE) can be assigned to ITextPlugin, CPLugINMGR creates an instance and adds it to an array. Because CPluginmgr is a type of this machine, it cannot save managed objects directly, so the array m_objects is actually an array of gcroot
For (PluginList :: item (); it! = pl.end (); it ) {
Object * Obj = * it;
ITextPlugin * plugin = Dynamic_cast
Plugin-> DOSMETHING ();
}
} (PluginList is a typedef for vector
Cedit & edit = geteditctrl ();
INT begin, end;
Edit.getsel (Begin, End);
PCMDUI-> Enable (Begin! = End);
} I have already demonstrated how Pgedit loading and access plug-ins, how do you implement plugins? It is very easy. First, the assembly of a defined interface - this article is TextPlugin.dll. The assembly does not implement any code or class, only the interface is defined. Remember, .net is the language neutral, so there is no source code, which is completely different from the C header file. Instead, you generate the assembly of the definition interface and distribute it to developers who write plugins. The plugin is linked to the assembly, so they derive from the interface you provide. For example, the following C # code: Using textPlugin;
Public class myplugin: ITextPlugin
{
... // Implement ITextPlugin
Figure 8 shows the PlugInCaps plugin written with C #. Just as you can see, it is very simple. For details, please refer to the source code of this article. I wish you a happy program! Your question and comments can be sent to the Paul's mailbox: CPPQA@microsoft.com. He is the author of "Writing Reusable Windows Code In C " book (Addison-Wesley, 1992). Amateur time he developed Pixelib, this is a MFC class library, from Paul's website http://www.dilascia.com you can get this class library. This article comes from the October 2005 Journal of MSDN Magazine, or through local newsstands, or best subscriptions