許多 C++ 應用程式希望支援使用者以 C#、VB.NET 等 .NET 語言撰寫外掛。傳統 C++ 透過虛擬函數指標模擬 COM 介面,但 .NET 提供更簡潔的機制:組件動態載入 + 反射,無需註冊、GUID、參考計數等繁瑣 COM 細節。
.NET 中對應 LoadLibrary 的是 System.Reflection.Assembly.Load / LoadFrom。載入組件後,可用反射取得型別、方法並動態呼叫:
Assembly asm = Assembly.Load("MyLib");
Type t = asm.GetType("MyLib.MyClass");
MethodInfo m = t.GetMethod("SayHello");
m.Invoke(null, new object[] { "Test" });
Load 依賴 Fusion 綁定規則(GAC、應用程式目錄等);LoadFrom 可指定路徑,適合外掛場景。
ITextPlugin.dll),僅含介面定義,無實作。public class MyPlugin : ITextPlugin)。CPluginMgrpublic class CPluginMgr
{
private List<object> m_objects = new List<object>();
private string m_pluginDir;
public CPluginMgr(string pluginDir = "Plugins") { m_pluginDir = pluginDir; }
public int LoadAll(Type iface)
{
int count = 0;
foreach (string dll in Directory.GetFiles(m_pluginDir, "*.dll"))
{
Assembly asm = Assembly.LoadFrom(dll);
foreach (Type t in asm.GetTypes())
{
if (iface.IsAssignableFrom(t))
{
object obj = Activator.CreateInstance(t);
m_objects.Add(obj);
count++;
}
}
}
return count;
}
public IEnumerable<T> GetPlugins<T>() where T : class
{
foreach (object o in m_objects)
if (o is T plugin) yield return plugin;
}
}
主程式啟動時:
m_plugins.LoadAll(typeof(ITextPlugin));
使用時:
foreach (ITextPlugin p in m_plugins.GetPlugins<ITextPlugin>())
p.Transform(text);
若外掛型別眾多,逐一檢查 IsAssignableFrom 會有效能負擔。建議採用 組件層級屬性 標記外掛型別,讓管理器快速定位,參考 Joel Pobar 在 MSDN 2005 年 7 月號文章。
若需運行時卸載/重載外掛,或限制外掛權限,必須將外掛載入獨立 AppDomain。跨域呼叫需型別繼承 MarshalByRefObject,透過 Remoting 封送。桌面應用若無熱插拔與沙箱需求,直接載入預設域即可。
ITextPlugin:MenuName、MenuPrompt、Transform(string)。PluginCaps(大寫)、PluginLower(小寫)、PluginScramble(打亂)。IDC_PLUGIN_BASE 起算,用 ON_COMMAND_RANGE 分派。
.NET 的語言中立特性使外掛架構極易實作:定義介面組件、動態載入組件、反射實例化。無需 COM 複雜度,即可讓使用者以任何 .NET 語言擴充應用程式功能。