Recently, we are studying the source code of keepass. It is really amazing that foreigners are everywhere. I am waiting for the cainiao to take it orally. Needless to say, this source code is indeed worth learning. First, it is a plug-in-based program. That is, as long as you follow the defined specifications, you can write a plug-in for keepass as needed. Is that so dazzling. I remember that when I first came into contact with the vs Studio development environment, I installed the source code manager plug-in. Then we can directly use source code management in vs studio. This feature makes me feel amazing and later I learned that vs studio is based on plug-ins. O (keep _ running) O and the keepass interface is also very powerful.
The keepass plug-in mechanism is based on the COM specification and green. That is, the plug-in used can be used directly in the keepass program directory without having to register with the system. Then how is this plug-in mechanism implemented. If you do not understand the COM specification, refer to the documentation on msdn about the COM specification.
First, how does keepass implement the plug-in mechanism?
When keepass is started, find all the DLL files in the program directory and verify these DLL files. How can we verify them. Keepass is identified by the version information of the DLL file. That is to say, if the product of the dll version information is keepass pulgin, the DLL is considered as the keepass plug-in. Otherwise, it is not loaded. According to keepass specifications, all the DLL plug-ins of keepass must export three functions: kpcreateinstance (the function for creating the plug-in) and kpinitializelibrary (the function for initializing the plug-in) and kpreleaselibrary (release plug-in functions ).
Bool cpluginmanager: loadallplugins () <br/>{< br/> STD: vector <std_string> vcandidates = findplugincandidates (); <br/> ikpunknown * Papi = & ckpapiimpl:: instance (); </P> <p> for (size_t I = 0; I <vcandidates. size (); ++ I) <br/>{< br/> kp_plugin_instance kppi; <br/> cpluginmanager: clearstructure (& kppi ); </P> <p> std_string strpluginpath = vcandidates [I]; <br/> If (_ isvalidplugin (strpluginpath. c_str (), & kppi) = f ALSE) continue; </P> <p> kppi. hinstdll = loadlibrary (strpluginpath. c_str (); <br/> If (kppi. hinstdll = NULL) continue; </P> <p> // call optional library initialization function <br/> lpkplibfunc lpinit = (lpkplibfunc) getprocaddress (kppi. hinstdll, kp_ I _initializelib); <br/> If (lpinit! = NULL) <br/>{< br/> If (lpinit (Papi )! = S_ OK) {freeplugindllex (kppi. hinstdll); Continue ;}< br/>}</P> <p> lpkpcreateinstance lpcreate = (lpkpcreateinstance) getprocaddress (<br/> kppi. hinstdll, kp_ I _createinstance); <br/> If (lpcreate = NULL) {freeplugindllex (kppi. hinstdll); Continue ;}</P> <p> const hresult hR = lpcreate (iid_ikpplugin, (void **) & kppi. pinterface, papi); <br/> If (HR! = S_ OK) | (kppi. pinterface = NULL) <br/> {freeplugindllex (kppi. hinstdll); Continue ;}</P> <p> kppi. dwpluginid = m_dwfreepluginid; <br/> ++ m_dwfreefreeinid; </P> <p> kppi. strpath = strpluginpath; </P> <p> m_plugins.push_back (kppi); <br/>}</P> <p> return this-> _ assignplugincommands (); <br/>}
Keepass first obtains the kpinitializelibrary function through the API function getprocaddr to initialize the plug-in, and then obtains the kpcreateinstance function to create the plug-in. If no error occurs, then when the m_plugins. Plug-in the keepass plug-in list is added, keepass calls buildpluginmenu to create a user interface for the plug-in. That is, create a sub-menu of the plug-in menu tools. First, the plug-in must implement two specifications: getmenuitems is to obtain the menu Resources in the plug-in, and the return type is kp_menu_item pointer. This struct is defined as follows:
Typedef struct
{
DWORD dwflags; // menu ID
DWORD dwstate; // menu status
DWORD dwicon; // icon
Lptstr lpcommandstring; // menu text
DWORD dwcommandid; // The Menu ID. Note that this is used by keepass.
Dword_ptr dwreserved; // <reserved for future use, must be 0.
} Kp_menu_item;
Keepass creates a user interface (menu item) for the plug-in based on the kp_menu_item pointer and assigns a commandid for each menu item. Keepass does not know what user interfaces the plug-in has. How can it respond to the Command Message of the menu item. This is distinguished by commandid. First, map the message:
On_command_range (wm_plugins_first, wm_plugins_last, onpluginmessage)
That is to say, the Message ID between wm_plugins_first and wm_plugins_last is mapped to the onpluginmessage function, and then the commandid is used to identify the corresponding commandid of each message and find the corresponding plug-in, the onmessage interface is mapped to the onmessage of the plug-in for processing.
Bool cpluginmanager: callplugins (DWORD dwcode, lparam lparamw, lparam lparaml) <br/>{< br/> bool Bret = true; </P> <p> If (dwcode = kpm_direct_exec) // call single plugin <br/>{< br/> for (size_t I = 0; I <m_plugins.size (); ++ I) <br/>{< br/> kp_plugin_instance * P = & m_plugins [I]; <br/> If (P-> hinstdll = NULL) | (p-> pinterface = NULL) continue; </P> <p> const DWORD dwnumcommands = p-> pinterface-> getmenuitemcount (); <br/> kp_menu_item * pmenuitems = p-> pinterface-> getmenuitems (); </P> <p> If (dwnumcommands = 0) | (pmenuitems = NULL) continue; </P> <p> for (size_t J = 0; j <dwnumcommands; ++ J) <br/>{< br/> If (pmenuitems [J]. dwcommandid = (DWORD) lparamw) <br/> return p-> pinterface-> onmessage (kpm_direct_exec, lparamw, lparaml ); <br/>}< br/> else // call all plugins <br/>{< br/> for (size_t I = 0; I <m_plugins.size (); ++ I) <br/>{< br/> kp_plugin_instance * P = & m_plugins [I]; <br/> If (P-> hinstdll = NULL) | (p-> pinterface = NULL) continue; </P> <p> Bret & = p-> pinterface-> onmessage (dwcode, lparamw, lparaml ); <br/>}</P> <p> return Bret; <br/>}
Well, we can see that the key to implementing the plug-in mechanism is to define a set of interfaces and establish interaction between the plug-in and the main program through these interfaces.