Program Jason Clark
This article assumes that you are familiar with. NET and C #
Download the code for this article: Plug-ins.exe (135KB)
Overview
Most user applications benefit from the ability to be extended by other developers. Extending an existing application that a user is already familiar with and training for is often simpler and more effective than developing from scratch. Therefore, scalability makes your application more appealing. You can make your application extensible by supporting features such as plug-ins and macros. This can be easily achieved with the. NET Framework, even if the core application is not a. NET Framework application. In this article, the author describes the extensible capabilities of the. NET Framework, including late binding and reflection, and how they are used, as well as plug-in security considerations.
Imagine what a perfect text editor looks like. It does not start for more than two seconds, supports contextual coloring and automatic indentation for popular programming languages, supports multiple document interfaces (MDI), and cool and popular tabbed document arrangements. The problem with conceiving this perfect text editor is that perfection exists only in the eyes of the beholder. These features are just my definition of a perfect text editor, and others will certainly have different standards. Perhaps the most important function that a perfect text editor can have is to support rich scalability, so that developers can extend the application with the functionality they need.
Extensible text editors may support the creation of custom toolbars, menus, macros, and even custom document types. It should allow me to write Plug-ins that can be linked to the editing process to add AutoComplete, spell checking, and other wonderful features. Finally, the perfect text editor should allow me to write my own plugin in any language (my personal preference is C #).
Admittedly, I want every application that I use to expand in this way. It would be nice if you could customize your favorite apps by writing a small amount of code in some places. Even if I can't, I know other people can do it, and I'm going to download it to leverage their extensions from the Internet. That's why I started this campaign to get all the developers to write scalable applications.
Ideal for scalable applications
Many applications can be modified using pluggable code. In fact, the entire Microsoft Office application suite can be customized so widely that people can use Office as a platform to write a complete custom application. However, even with such complete customization capabilities, I wrote my first plug-in for Microsoft Word, an application I used almost every day.
The reason is simple. All of the features of Microsoft Office do not fully conform to my standards, including:
• Simple nature. I want to manipulate my pluggable applications with very simple software tools that are already familiar.
Permissions I want my plug-in to have access to some of the objects and functional subsets built into the application. This permission should be natural, as part of the programming language I chose.
• Programming languages. Sometimes I want to use a specially chosen programming language.
Ability In addition to accessing the application's Document Object Model (DOM), I want a rich API.
Security I need to be able to download plug-ins that other people write and can download over the Internet. I want to execute components that are potentially dangerous or error-ridden without considering the security of the system.
The list is short but almost demanding. In fact, before the Microsoft. NET Framework was released, these standards were too stringent for ordinary applications to do. But now, I can show you how to use the. NET Framework to add all of these extensibility features to your managed and unmanaged applications.
. NET Framework Extensibility Features
Scalability builds on late binding, which refers to the ability to discover and execute code at run time, rather than compile (more typically). There have been a number of technical innovations in recent years that have contributed significantly to late binding, including DLLs and COM APIs. The. NET Framework raises the simplicity of late binding to a whole new level. To deepen our understanding, let's look at a very simple code example.
Figure 1 shows the simplicity of using reflection to perform late binding in managed objects. If you build the code in Figure 1 and run it within LateBinder.exe, you can pass the name of any assembly, such as the Assembly built from code in Figure 2, to it as a command-line argument. LateBinder.exe reflects the assembly and creates instances of the classes derived from Form in the assembly and makes them its own MDI child classes. Reflection in the. NET Framework simplifies late binding greatly.
Reflection is one of the basic tools of the. NET Framework, and it facilitates the development of scalable applications. It's one of the four features I've mentioned here to make an application scalable.
After a common type system uses the. NET Framework for some time, you may begin to think of the common type System (CTS) as a matter of course. However, it is one of the reasons why scalability in the platform has become so simple. The CTS defines some of the object-oriented features that all managed languages must follow, such as derived rules, namespaces, object types, interfaces, and primitive types. These basic rules for the CTS are set for code that is run by the common language runtime (CLR).
Reflection reflection is the ability to discover information at run time, such as the type or type-defined method of an assembly implementation. Reflection is possible because all managed code is self-describing by the data structures embedded in the assembly, called metadata.
The fusion. NET Framework uses fusion to load assemblies into a managed process (AppDomain). Fusion helps to implement advanced features such as strong naming and simplifying search rules for DLLs.
Code access security Code Access security (CAS) is a function of the. NET Framework to simplify the execution of partially trusted code. In short, you can use the features of the Microsoft. NET Framework to limit what the late-bound code can access, so you don't have to worry about plug-ins breaking the user's system.
This is the four features that the. NET Framework makes scalability a reality. However, because these features are so cool, an article describes a feature that is unlikely to be very thorough about scalability. Therefore, the best approach is to introduce this topic from a task.
Getting Started with scalability
Regardless of the purpose of your application, as long as it is extensible, you must perform three basic tasks: discovering, loading, and activating extensions. Discovery is the process of finding plug-ins and other code that your application binds to at run time. Loading is the process of putting code (packaged as an assembly) into a process or AppDomain to activate and use the type defined by an assembly. Activation is the process of creating instances of late-bound objects and invoking their methods.
Each of these three phases contains a number of. NET Technology and application design considerations. Although technically achievable, the. NET Framework does not define a special way to achieve scalability, so you can have many choices.
Loading: Binding to code at run time
Logically, an extensible application discovers the code before it is loaded. However, reflection must load code to discover what is relevant to it, so the discovery process may actually be after the code is loaded. Let's take a look at what this means.
Reflection can be used to implement late binding. Most reflection classes can be found in the System.Reflection namespace. The three most important classes are System.AppDomain, System.Type and System.Reflection.Assembly. Later I will introduce System.Type. To understand the AppDomain and Assembly classes, let's take a quick look at the hosting process.
The CLR runs managed code in the Win32 process, with finer granularity than found in unmanaged applications. For example, a managed process can contain multiple AppDomain, which you might consider a child process or lightweight application container.
Assemblies are managed versions of DLLs and EXE that contain reusable object types (such as class library types) and application code. In addition, any extension or plug-in of the application should also exist in the Assembly (very similar to the DLL). The assembly is also loaded into one or more AppDomain within the managed process.
Each managed process has at least one default AppDomain, and includes some shared resources, such as the managed heap (Heep), the managed thread pool, and the execution engine itself. In addition to these logical components, managed processes can create any number of AppDomain. See Figure 3, which shows a managed thread that contains two AppDomain. In later on the topic of plug-in discovery, AppDomain is extremely important.
Figure 3 The managed process
Now we're back to AppDomain and Assembly types. You can use the Assembly.Load static method to load an assembly into the current AppDomain. The Assembly.Load method returns the reference to the Assembly object. This method binds code to your application at run time by loading the assembly that hosts the code.
Assembly.Load loads an assembly from the Appbasedir directory by name (without extension) (AppDomain is the secret load of the deployed assembly from this directory). By default, Assembly.Load looks for regular executable programs from the directory where the EXE is loaded. When an earlier-bound assembly is loaded, Assembly.Load follows the same assembly binding rules as the CLR uses.
You can adjust AppDomain appbasedir by obtaining a reference to the AppDomain object and invoking the AppDomain's Appendprivatepath and Clearprivatepath instance methods. If you are using Assembly.Load to load plug-in assemblies, you may need to manipulate AppDomain's appbasedir. This is because it is useful to maintain the plugins in the subdirectory of the main application directory. I will explain the specific reasons very soon.
The Assembly class also implements a method called LoadFrom, which differs from Load in that it has the full name of the assembly file (including the path and extension). LoadFrom simply loads the files you point to, rather than looking for the assembly according to the binding and discovery rules. This is useful when loading assemblies that are used as plug-ins. Note, however, that LoadFrom has a detrimental effect on performance compared to Load, and that it lacks version binding intelligence, which makes it impossible for almost any scenario except for plug-in scenarios.
Once you get a reference to the Assembly object, you can discover the types that are contained in the assembly and create instances and call methods for those types. I'll describe these operations in the next two sections.
Discovery: Discovering code at run time
When you compile an application, you must explicitly or implicitly tell the compiler the code that the application binds to and uses at run time. The code appears in the form of widely reusable class library types (such as Object, FileStream, and ArrayList) and the types of applications that are packaged in the helper assembly. The compiler stores references to implementing these types of assemblies in the assembly manifest, and the CLR references the manifest at run time to load the necessary assemblies. This is the typical managed code binding process.
As mentioned earlier, late binding also uses assembly-form code, however, the compiler does not directly involve the binding process. Instead, the code must discover which assemblies it needs to load at run time. The discovery process can be implemented in a variety of ways. In fact, the. NET Framework does not specify a discovery method, but it does provide you with important tools to implement your own technology.
Both of these discovery mechanisms are worth mentioning. The first and simplest (from a software standpoint) is to maintain an XML file that records the code that the application should bind to at run time. The second is a more advanced method based on reflection, which can make applications more usable. Let's look at the XML method first.
An XML method can load an assembly by simply parsing the XML file with code and using the information in it. The following example shows a possible XML format:
This XML contains the minimum information needed to implement the load process using reflection, the name of the assembly you want to activate, and one or more types of names in the assembly. The code simply finds the name of the assembly in the XML and then loads the assembly into AppDomain using the code shown in Figure 1.
XML methods are easy to implement, but the resulting application availability is not high. For example, I'm not particularly fond of text editors: you must edit a. config file to extend the application. However, for server-side applications, this approach may be more appropriate than using reflection.
The reflection based discovery method can be automatically operated using a known location relative to the application directory. For example, the sample application I wrote for this article searches for a possible extensibility assembly in a subdirectory named "Plugins" (to download the complete source code, go to the link at the top of this article). This particular approach is useful because the user only needs to copy the assembly file to the file system, and the application will bind the new code when it starts.
There are two reasons why the implementation of this method is more difficult than the XML method. First, you must establish the criteria that should be followed for loading assemblies. Second, the Assembly must be reflected to see if it meets the criteria and whether it should be loaded into the AppDomain.
There are three useful criteria that you can use to determine a type for your code in an assembly that is a plug-in. When the reflection method is used for discovery, one of these criteria should be used.
Interface Standard code can use reflection to search the entire assembly to discover all types that implement a known interface type.
The base class standard code again uses reflection to search the entire assembly for all types that derive from a known base class.
Custom attribute criteria Finally, you can use a custom property to mark a type as the application's plug-in.
I'll show you how to use reflection to discover whether a type in a late-bound assembly implements an interface, derives from a base class, or is attributed. But first let's look at a design compromise that uses these binding standards.
Interfaces and base class standards are more useful than custom property methods (or any other method based on late binding of reflection) for two reasons. First, the plug-in developer is likely to be familiar with interfaces or base classes, relative to custom attributes. More importantly, you can use interfaces and base classes to call late-bound code in an early-bound way. For example, when you use an interface as a standard that is bound to an object type, you can use an instance of an object through a reference to that interface type. This avoids the need to use reflection to invoke methods and to access other members of the instance, thereby improving the performance and code capabilities of the extensible application.
To find relevant information about a type at run time, you can use the reflection type System.Type. Each instance of a type derived from types refers to a type in the system. The type instance can be used to discover some facts about the type at run time, such as its base class or the interface it implements.
To get a set of types implemented by an assembly, simply call the GetTypes instance method of the Assembly object. The GetTypes method returns an array of references for an object derived from type, and each type defined in the assembly corresponds to a reference.
To determine whether a type implements a known interface or derives from a base class, you can use the IsAssignableFrom method of the Type object. The following code fragment shows how to test whether the type represented by the SomeType variable implements the IPlugin interface:
It is efficient to load an assembly to reflect the types in the application's logic for which types to insert. But what if none of the types match your criteria? Simply put, you cannot unload an assembly that already exists in AppDomain. This is the second reason why the discovery method based on reflection is a little harder to implement than the XML method.
For some client applications, it may be acceptable to load all assemblies that are reflected by the code (even assemblies that do not conform to the plug-in binding standard) into the process. However, for extensible server applications, there is not enough space to load any assemblies that cannot be unloaded subsequently. The solution to this problem is divided into two stages. First, the assembly can be unloaded as long as the entire AppDomain of the loading assembly is unloaded. Second, a temporary AppDomain can be created programmatically, with the sole purpose of loading the assembly for reflection in order to discover whether the type in the assembly needs to be used as a plug-in. Once the discovery phase is complete, you can uninstall the temporary AppDomain and all of its assemblies.
The solution sounds complicated, but in fact it's a very simple piece of code to implement. The PlugInManager class used in this article takes this approach, and it does not have more than 100 lines of code to implement. If you want to take this approach in your own extensibility application, you'll find that PlugInManager's source code is very useful (see downloading files).
Activation: Creating instances and calling methods
Once you have identified which types to insert in your application, you need to create an instance of the object. This can be done entirely by reflection, but you will find that reflection is slow, impractical, and difficult to maintain. Alternatively, you should choose to use reflection to create an instance of the plug-in object, cast the object to a known interface or base class, and then use those objects based on known types throughout its remaining life cycle. Keep in mind that interfaces and base class standards are ideal for discovering plug-in types in this way.
Because of the CTS, managed code is object-oriented and type-safe. One of the challenges of late-bound code is that it does not necessarily know the type of object at compile time. However, because of the CTS, you can view any object as an object base class, and then cast it as an application and a base class or interface that the code is known to insert. If the object cannot be cast, a InvalidCastException exception is thrown, and the application can catch the exception and handle it accordingly.
However, you must create an instance of the object that you want to bind to before you do any such action. Unlike early-bound objects, you cannot simply use the New keyword to create an instance, because the compiler needs the type name to use with new, and for late-bound types, this is clearly not known. The solution is to use static method Activator.CreateInstance. This method creates an instance of the object (assuming that the reference object's Type derives an instance) and an optional object reference array (used as a constructor parameter). The following code uses CreateInstance to create an object and return a known interface:
Once you have an object and cast it to a known type, you can call the object's methods by referencing the variable, just as you would any other object's method. At this point, objects in late-bound code are seamlessly integrated with other parts of the application.
Protect scalability
If your application will be widely distributed, and anyone can write plug-ins, you must consider security. Fortunately, the. NET Framework makes it easy to protect your code. Let's take a look at how this is done.
Imagine the extensible text editor again. Ideally, users should be able to download plug-ins from the Internet and safely insert them into the application, even if the plug-in is designed by untrusted third parties. And if it is not trusted, it should be performed in a partially trusted state where it does not have access to system resources such as the file system or registry.
The. NET Framework can execute partially trusted code through CAS. Because managed code is compiled in real time, the CLR has the ability to assert that partially trusted managed code does not perform operations that it lacks permissions. If a partially trusted code attempts to perform an operation that is not allowed, the CLR throws a SecurityException exception that the application can catch.
In some cases, the code defaults to partially trusted, such as controls that are distributed across the Internet or embedded within an HTML document. However, you can also take advantage of CAS so that users can safely use an extension assembly from a third party. In all cases, the CLR treats an assembly as a security unit, which means that an application can contain multiple assemblies, and the security permissions granted to each assembly may vary. This is ideal for plug-ins.
In addition, the. NET Framework provides many features, and there are many ways in which you can use these methods to create partially trusted plug-ins. The following steps take the simplest and safest approach. First, create two subdirectories for the application's plug-ins. One that holds fully trusted plug-ins, and another that stores partially trusted plug-ins. The local security policy is then programmatically adjusted to associate the code group with the subdirectories corresponding to the partially trusted plug-ins. Finally, you grant code Internet permissions in the code group, which means that it has a subset of permissions, and that even potentially malicious code is considered safe.
Once this code group is generated, the CLR automatically associates the lowered permissions with the assemblies that are loaded from the partially trusted subdirectory. In addition to adjusting the local security policy (which I'll tell you about right away), the security of the plug-in automatically works.
Adjusting security Policy
The. NET Framework security Policy engine is flexible and can be adjusted. In practice, you can manually adjust the security policy using the. NET Framework Configuration Control Panel applet that is installed with the framework (see Figure 4). Alternatively, you can adjust the policy programmatically. To write code that modifies the security policy, you must start with the SecurityManager type. This useful type can help you access the three policy levels of the. NET Framework Installation: Enterprise, Machine, and User. I recommend that you add a custom code group for your plug-in to the Machine policy level. To discover the machine PolicyLevel object, you can use the code shown in Figure 5.
Figure 4 Security Configuration
Code groups are arranged in a logical hierarchy. Once you get the PolicyLevel object, you can begin traversing the hierarchy of the code group from the code group returned by the Policylevel.rootcodegroup property. By default, the name of the root code group is All_Code, which represents all managed code. You should create a custom code group as a subgroup of the All_Code code group.
The code in Figure 6 creates a custom code group for any code that is loaded with a specific URL. The code group has Internet permissions and sets the Policystatementattribute.exclusive and policystatementattribute.levelfinal Bits to indicate that the code that matches this code group has only these permissions. The URL can be HTTP, HTTPS, or a FILE URL. To associate this new code group with a directory in the file system, you can use a file url:file://d:/programs/extensible-app/partially-trusted with the following structure.
The. NET Framework has a very flexible CAS feature, but it may take some time to get used to it. By reading this article and the sample application code I provided in the download file, you should be able to get the information you need to create the plug-in architecture. However, I strongly recommend that you use the. NET Framework Configuration Control Panel applet to try to make changes to the system policy in order to familiarize yourself with the concepts. If you change too much, you can restore policy defaults at any time.
Extensible Application Design
The ExtensibleApp.exe sample application that is included with this article is an example of a custom plug-in support. In fact, the application is nothing more than a shell that only displays an MDI window and allows users to install Plug-ins. If you are using the code in the example as a learning tool for writing your own extensible application, you should pay special attention to the code in PluginManager.cs. The module contains the PlugInManager reusable class, which handles all application-neutral plug-in logic for the sample application.
Figure 7 before the plug-in is installed
If you build and run the ExtensibleApp.exe sample, you will find that it allows you to select a DLL to install as a plug-in into your application. The example contains two plug-in objects: PluginProject1.dll and PluginProject2.dll. They use APIs exposed by the application itself to create toolbars and menus, and to add a document type to the application. Figure 7 shows the application before inserting the custom code, figure 8 shows the application after the insert.
Figure 8 Running the plug-in
The application uses the techniques and techniques discussed earlier in this article. In addition, it shows some design methods that should be considered when making an application extensible. Let's take a look at some of the caveats.
Version control
Versioning is an important aspect of the application lifecycle. It also has an important impact on scalable applications. An extensible application needs to define the interface, base type, or attribute type used to discover and use Plug-ins. If you include these types in a general application EXE or a DLL assembly, their versions are controlled by other parts of the application, which may result in a binding conflict for plug-in assemblies that do not have version control on the same schedule. But there is a way to solve this problem.
The workaround is that any type used to discover or bind to the plug-in should be defined in its own assembly, and only other types that are generated by the plug-in exist in the assembly. You should also avoid placing any code (at least not too many) in the assembly because you need to have as few versioning as possible on that assembly. You can also make changes and versioning to other parts of the application as needed. Applications and Plug-ins share assemblies that are rarely versioned.
This brings up a problem with the base type used by the plug-in. Unlike interfaces, base classes typically contain the same code. This is a tricky problem, and it's also one of the main reasons we prefer to call late-bound objects through an interface.
However, you should know that if you need versioning of the code in the base class and in the interface assembly, the. NET Framework provides the flexibility to accommodate the need to redirect bindings from older versions of the assembly to the new version. However, this requires a change to the binding policy, which must be entered into the application's app.config file or the entire system's global assembly Cache (GAC). It is best to avoid this possibility, so manage the plug-in interfaces and base classes so that you can control them as little as possible (if you've ever done so).
of robustness
Keep in mind that while you are writing extensible applications, your code is likely to have no access to strict quality control. Therefore, any code that invokes late-bound objects through an interface or base class reference should encounter these unpredictable problems. Even though developers do not intend to cause damage, some trusted plug-ins can throw security exceptions. Similarly, some trusted plug-ins and fully trusted plug-ins may have errors because the plugin author's knowledge of your application is not the same as yours.
If you are designing an extensible application that only supports plug-ins written by yourself or your team, you may not consider this issue. However, many extensible applications want to be able to recover as normally as possible when an exception occurs in the plug-in object.
A powerful thing about managed code is that when an object does fail, it fails in a well-defined way-that is, the object throws an exception. So if you consistently persist in registering unhandled exception handlers and handling exceptions that should be generated by invoking plug-in code, you can provide a consistent experience even if the plug-in fails.
If you use an interface to invoke a plug-in object, you can use an advanced technique that wraps all late-bound objects in a proxy object that implements the same interface. The proxy object for this general purpose will pass all calls to the underlying plug-in, but will also wrap the exception handler that is consistent with logging failures with the call, warning the user of an exception and other such occurrences. This is a good idea for the ultimate plug-in robustness, but for many applications, this level of stability may not be required.
Security Considerations
The. NET Framework can maintain the security of partially trusted plug-ins as long as you develop policies to limit the permissions of the assemblies that are loaded. However, while this has a significant effect on the protection system, it does not automatically protect the state of the application. Partially trusted objects share a managed heap with objects in your application, and you need to consider how to restrict their access to your internal application objects. Here are some points.
If it is not necessary, do not specify the object type in the application as a public object. If the type is an internal type (by default), you can restrict access to it by partially trusted code in your application. However, it is easy to inadvertently introduce a common type into an application. For example, when you add a type derived from form to a project, the Visual Studio Wizard generates a public class. This is not necessary for most applications, so you should remove the public keywords from these types and add them when you feel it is necessary.
Similarly, if it is not necessary, members of the public type should not be public or protected members. Even for applications that are not scalable, members should be private until you feel the need to improve their accessibility. Therefore, if the internal accessibility can meet the requirements, do not ascend. Protected members and public members are used only when you intend to expose members outside the assembly.
You should be concerned about whether the class library type has a bad or incomplete security policy. For example, the System.Windows.Forms.MainMenu class exposes a method called GetForm, which returns the form on which the menu is located. This is usually the main form of the application. Even if you do not intend to pass a reference to an application's main form to a partially trusted plug-in, you may inadvertently let the plug-in directly access the object in your application that derives from menu, allowing the plug-in to access the application main form. CLR Class Library developers have considered similar security issues. For example, form.parent requires that its caller have a security permission before returning a reference to the parent form. For example, partially trusted code that runs with Internet permissions cannot access the property by default.
As you can see, some of the trusted plug-ins may not be able to access the general file system or the registry, but you still have to be wary of malicious plug-ins performing certain things, such as shutting down your application. For client applications, this type of problem usually does not matter. However, it is a significant problem for server applications.
In the last section, I'll briefly discuss some of the advanced possibilities for extensible applications. The scope of these topics is too broad to be detailed and most applications may not be needed, but it is helpful to understand these possibilities.
Uninstalled plug-in Assemblies
In the previous chapters of this article, you may recall the issue of uninstalling unwanted assemblies. The solution to this problem is to test whether the assemblies are useful in a temporary AppDomain so that they are unloaded when needed. Then, if you find that there are assemblies that you really need to use, you can load them into your main AppDomain. But what if you don't want all of your plug-ins to exist indefinitely?
For client applications, the following practices are acceptable: Load plug-in assemblies, use their types until they no longer need to be used, and then do not have to take care of them for the rest of the lifecycle of the application. However, the requirements on the server side are much stricter. Server-side applications must run indefinitely and cannot deplete important resources such as process address space, so you need to load and unload assemblies that contain plug-in types.
To do this, you need to look for an assembly in a temporary application domain and then use the plug-in type specifically in another AppDomain. This adds some complexity to the design of the application, but it also separates the Plug-ins from the core application logic.
It is possible to instantiate and use objects entirely from within a separate AppDomain, and the feature of the. NET Frame Work that implements it is called Remoting. It is possible to instantiate and use objects entirely in a separate AppDomain, while the functionality implemented in the. NET Framework is called Remoting (Remoting) 。 Remoting is used to access objects across processes and across networks, but it can also be used to access objects in different AppDomain, even if they are in the same Windows process. I can't fully describe remoting here, but you can find a detailed description in the previously published MSDN Magazine, or you can find some simple remoting code in my sample PlugInManager type.
Managed Plug-ins in an unmanaged application
With so many powerful scalability features available to you, it may be very disappointing if your application uses legacy applications written in unmanaged languages such as Visual Basic 6.0 or C + +. And now you don't have to be disappointed. The CLR itself is a COM object that can be hosted in any WIN32 process written in a language that can write COM clients.
This means that you can write plug-in managed code in C # or Visual Basic. NET), and then loads the runtime and the glue code through unmanaged code, and then the code loads plug-ins that you can interact with your unmanaged application in the way you like. At the same time, COM Interop allows you to seamlessly pass COM interfaces between managed and unmanaged code.
In fact, there are three ways to host managed code in an unmanaged assembly and interact with it. You can use COM Interop. The CLR allows you to create COM servers in C #, Visual Basic. NET, and other managed languages. You can bind and use these managed objects in any unmanaged application. You can also use C + + (mc++) with Managed Extensions, which can naturally mix managed and unmanaged code into a single process. If your application is written in C + +, you will find that managed C + + contains rich extensibility features. In addition, you can host the CLR directly. The CLR itself is a COM object that can be hosted. The. NET Framework SDK comes with a C + + header file named MSCorEE.h that contains the required definitions to use the runtime as a COM object.
Again, once managed code is bound to an unmanaged application, managed code can use the techniques described in this article to implement the extensibility of the application.
Summary
The. NET Framework provides some very flexible features for code reflection, late binding, and code security. These features can be mixed and used in a variety of ways to implement extensible applications. If a reliable application is extensible, it may have longer lifetimes and may be further developed, prompting more plug-ins to be created and accepted more widely. So please study these scalability features carefully. I think you'll like the result.
Related articles please refer to:
. NET framework:building, packaging, deploying, and administering applications and Types
. NET framework:building, packaging, deploying, and administering applications and Types-part 2
For background information, see:
. NET Framework Security by Brian A. Lamacchia, Sebastian Lange, Matthew Lyons, Rudi Martin, and Kevin T. Price (Addison-we Sley, 2002)
Jason Clark provides training and consulting for Microsoft and Wintellect (http://www.wintellect.com) and is a former deve Loper on the Windows NT and Windows Server team. Jason Clark provides training and advice to Microsoft and Wintellect (http://www.wintellect.com), a former developer of the Windows NT and Windows Server team. He co-authored programming Server-side applications for Microsoft Press, Microsoft Press, 2000. You can contact Jason through JClark@Wintellect.com.
The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion;
products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the
content of the page makes you feel confusing, please write us an email, we will handle the problem
within 5 days after receiving your email.
If you find any instances of plagiarism from the community, please send an email to:
info-contact@alibabacloud.com
and provide relevant evidence. A staff member will contact you within 5 working days.