出自:http://www.codeproject.com/dll/plugin.asp
DLLs are the most effective way to implement plug-in functionality. Even Windows itself uses DLLs extensively for most of its tasks. DLLs can be delivered easily as a patch or as an upgrade version, without worrying about re-compiling the main application again and again. Once installed/copied they can be loaded at runtime by the main application to use the functionality provided using exported functions/classes within the DLL. Now many of you would question, that how would the application know what all functions are to be called? The answer to this would be that, all the DLLs must conform to some basic, generic, pre-defined interface. The call to the function (inside the DLL) can be issued only after knowing the function name. So the main app should, beforehand, know what all the functions that can be called. So this is the reason why a generic/pre-determined interface should be developed.
使用DLL来做插件是最有效的方法之一。主程序和插件之间必须通过事先“约定”的接口来沟通。
The validation of the DLL can be done in various ways. I used a method to extract the name of the DLL/plug-in by invoking an exported
如何验证DLL插件的“合法性”:可以尝试调用一个实现约定的函数(比如GetPlugInName()),如果执行成功且返回一个字符串,则该DLL就可视作合法的。
Now, with all this done. the user should be able to use the plug-in at runtime by clicking on the toolbar buttons just added. By default the buttons are disabled, since the compiler cannot find any "MESSAGE MAP" entries for it in the application. And also we cannot add the message map entry beforehand as the "Main" application doest not know even if the plug-ins existed or not beforehand, and also what resource IDs they use. After all, the main application has to be designed to be generic and flexible. So this must be done at runtime (enabling the buttons and handling events on them).
解决MESSAGE MAP的问题:由于主程序无法事先知道插件使用的资源ID,所以不能用常规的方法建立MESSAGE MAP。解决方法是重载CMainFrame::OnCmdMsg()方法:
DLL处理消息的能力目前存在的问题:
I would really like to handle DLL-specific events within the DLL itself, such as
DLLs are the most effective way to implement plug-in functionality. Even Windows itself uses DLLs extensively for most of its tasks. DLLs can be delivered easily as a patch or as an upgrade version, without worrying about re-compiling the main application again and again. Once installed/copied they can be loaded at runtime by the main application to use the functionality provided using exported functions/classes within the DLL. Now many of you would question, that how would the application know what all functions are to be called? The answer to this would be that, all the DLLs must conform to some basic, generic, pre-defined interface. The call to the function (inside the DLL) can be issued only after knowing the function name. So the main app should, beforehand, know what all the functions that can be called. So this is the reason why a generic/pre-determined interface should be developed.
使用DLL来做插件是最有效的方法之一。主程序和插件之间必须通过事先“约定”的接口来沟通。
So how should we go about making one??? The basic steps are as follows:
装载插件的大致流程:
- The main app is initialized (i.e. started)
- The DLLs/Plug-Ins are searched in the directory (may be pre-determined)
- Once "valid" DLLs are found, all or selected DLLs are loaded
- "Init" function is invoked on all loaded DLLs
- The instance and the DLL name/path are cached for future use (method invocation)
- User uses the functionality of the DLLs (using GUI interfaces provided by the DLLs)
- Once done, the main application invokes "Destroy" function of the DLLs one by one to free resources
- The main application exits
The validation of the DLL can be done in various ways. I used a method to extract the name of the DLL/plug-in by invoking an exported
GetPlugInName
function. If this function exists and return a name then I consider it as a valid Plug-In. Users can use different methods that suite their needs.
如何验证DLL插件的“合法性”:可以尝试调用一个实现约定的函数(比如GetPlugInName()),如果执行成功且返回一个字符串,则该DLL就可视作合法的。
Now, with all this done. the user should be able to use the plug-in at runtime by clicking on the toolbar buttons just added. By default the buttons are disabled, since the compiler cannot find any "MESSAGE MAP" entries for it in the application. And also we cannot add the message map entry beforehand as the "Main" application doest not know even if the plug-ins existed or not beforehand, and also what resource IDs they use. After all, the main application has to be designed to be generic and flexible. So this must be done at runtime (enabling the buttons and handling events on them).
解决MESSAGE MAP的问题:由于主程序无法事先知道插件使用的资源ID,所以不能用常规的方法建立MESSAGE MAP。解决方法是重载CMainFrame::OnCmdMsg()方法:
BOOL CMainFrame::OnCmdMsg (UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
// If pHandlerInfo is NULL, then handle the message
if (pHandlerInfo == NULL)
{
// If Plug-Ins have been loaded
if (m_bIsPlugInLoaded)
{
// Iterate through all loaded Plug-Ins
for (int nNumDlls = 0; nNumDlls < m_nNumberOfDLLs; nNumDlls++)
{
// If Plug-In instance is valid and not NULL
if (m_pLoadedDLLs[nNumDlls].m_pDLLhInstance != NULL)
{
// Get toolbar button ID from CDLLManager
UINT nTempID = m_pLoadedDLLs [nNumDlls].GetToolBarButtonID ();
// If toolbar button ID is equal to nID on OnCmdMsg ()
if (nTempID == nID)
{
// If command is and CN_UPDATE_COMMAND_UI
if (nCode == CN_UPDATE_COMMAND_UI)
{
// Update UI element state. Enable the button
((CCmdUI *) pExtra)->Enable (TRUE);
return TRUE;
}
else
// If it is CN_COMMAND. Generated by clicking the button
if (nCode == CN_COMMAND)
{
// Handle CN_COMMAND Message
// Call OnCallPlugInManager () to redirect the call
// to the Plug-Ins Event Handler method
OnCallPlugInManager (nID, nCode, pExtra, pHandlerInfo);
return TRUE;
}
}
}
}
}
}
// Else let windows handle the COMMAND
return CFrameWnd::OnCmdMsg (nID, nCode, pExtra, pHandlerInfo);
}
所有的CN_COMMAND类型的消息,最终都应该传到DLL的OnCmdMsg()方法中,以便DLL按照自己的方式处理这些消息。
DLL处理消息的能力目前存在的问题:
I would really like to handle DLL-specific events within the DLL itself, such as
WM_LBUTTONDOWN
,
ON_COMMAND
,
WM_MESSAGE
, etc. I tried it before, but the message handlers are never called, may be due to some silly mistake I had made.