A common method to implement plug-ins is to have a main application EXE, a separate DLL that defines the plug-in interface (and possibly a host interface to allow the plug-in to invoke methods on the host environment), and both the EXE and plug-ins should reference the DLL, which references neither. This eliminates circular dependencies and provides an API for programmers to write plug-ins without a dependency on the actual EXE.
This is not necessary, though. It is acceptable for the main EXE to define the plug-in interface, and the host interface if there is one. The plug-in can reference the EXE to obtain the API (i.e. interfaces). This is the important part, which Gill Bates pointed out: The main EXE does not need to, and further more, should not, reference the plug-in DLL. Rather, the main EXE should use reflection to load the plug-in DLL and search for classes that implement the plug-in interface. You can think of this as a sort of late-bound dependency to circumvent circular dependency issues, although it introduces a great deal more flexibility than just that.
I mentioned a "host interface" twice, so I will explain exactly what I mean by that now. A plug-in system usually defines an interface for the plug-in. Something like
IPlugIn. You can also declare a host interface that the hosting application should provide to the plug-in, for example,
IPlugInHost.
IPlugIn should expose a property that accepts an
IPlugInHost and when the host application instantiates the plug-in class it should assign an instance of an
IPlugInHost to the said property. This provides the plug-in with a method to make callbacks to the host, eliminating the need for the use of events. But I highly recommend that you carefully consider whether or not you need either a host interface or events. Generally all the feedback you need to get from a plug-in can be obtained by return values from function calls into the plug-in.
Just some notes on other posts:
- internal in C# is the same as Friend in VB.
- As was said, VB6 projects could not reference EXEs, only DLLs, and the same applies to .Net 1.x, but in .Net 2.0 EXEs and DLLs can both reference EXEs and DLLs. This makes things easier in some situations. For example, if you provided a feature-rich file compression application and wanted its functions to be available through its normal GUI but also available in code for other programmers you no longer need to put the good stuff in a DLL and distribute two files. In .Net 2.0 you can distribute it all as a single file, declaring what you want to be accessible to other coders as public.