Code Example B-5 The Example Administrator pre- and post-installation DLL.
/////////////////////////////////////////////////////////////////////////////
|
// insnda.c - Administration Installation Plug-In Template
|
//
|
|
// All Rights Reserved.
|
//
|
|
#include <windows.h>
|
#include <nssetup.h>
|
#include <ldap.h>
|
#include <ldapu.h>
|
#include <global.h>
|
#include <setupldap.h>
|
#include <stdio.h>
|
#include "insnda.h"
|
|
/////////////////////////////////////////////////////////////////////////////
|
//
|
// The procedure is called right after the dll is loaded into memory during
|
// installation and uninstallation. You should
|
// initialize your control data here with default values.
|
//
|
|
static void _InitializeControlData(void)
|
{
|
ZeroMemory(&cd, sizeof(CONTROLDATA));
|
cd.szConfigDSHost = "localhost";
|
cd.nConfigDSPort = 389;
|
cd.szLdapUser= setupStrdup("cn=Directory Manager");
|
cd.szLdapPWD= NULL;
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
/
|
// _DialogProc
|
//
|
// The procedure is for the property page that ask for configuration directory
|
// information, such as configuration host name and port number. This
|
// "refresh" procedure will get called during the property page
|
// initialization. You will need to create one of these for each property page
|
// used in the property sheet.
|
|
static void _RefreshConfigDSInfo(HWND hwndDlg)
|
{
|
BOOL bResult = FALSE;
|
if(!IsWindow(hwndDlg))
|
{
|
setupLogMessage("Error", "nda", "Calling RefreshConfigDSInfo with bad
|
handle");
|
return;
|
}
|
if(cd.szConfigDSHost)
|
{
|
bResult = SetDlgItemText(hwndDlg,
|
IDC_LDAP_NAME,
|
cd.szConfigDSHost);
|
if(FALSE == bResult)
|
{
|
setupLogMessage("Error", "nda", "unable to set ldap server name");
|
}
|
}
|
if(cd.nConfigDSPort)
|
{
|
bResult = SetDlgItemInt(hwndDlg,
|
IDC_LDAP_PORT,
|
cd.nConfigDSPort, TRUE);
|
if(FALSE == bResult)
|
{
|
setupLogMessage("Error", "nda", "unable to set ldap port");
|
}
|
}
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
// _DialogProc
|
//
|
// The procedure is for the property page that ask for configuration directory
|
// information, such as configuration host name and port number. This "save"
|
// procedure will get called whenever the user hits the "Next" button,
|
// saving all the datathat the user have entered. You will need
|
// to create one of these for each property page used in the property sheet.
|
//
|
|
static void _SaveConfigDSInfo(HWND hwndDlg)
|
{
|
if(IsWindow(hwndDlg))
|
{
|
BOOL bSuccess = FALSE;
|
int nPort = 0;
|
char szTemp[MAX_PATH];
|
char szLdapUrl[MAX_PATH];
|
GetDlgItemText(hwndDlg, IDC_LDAP_NAME, szTemp, MAX_PATH);
|
if(cd.szConfigDSHost)
|
setupFree(cd.szConfigDSHost);
|
cd.szConfigDSHost = setupStrdup(szTemp);
|
SetDlgItemText(hwndDlg,IDC_LDAP_NAME, cd.szConfigDSHost);
|
nPort = GetDlgItemInt(hwndDlg, IDC_LDAP_PORT, &bSuccess, FALSE);
|
if(bSuccess)
|
{
|
cd.nConfigDSPort = nPort;
|
}
|
SetDlgItemInt(hwndDlg, IDC_LDAP_PORT, cd.nConfigDSPort, TRUE);
|
wsprintf(szLdapUrl, "ldap://%s:%d/", cd.szConfigDSHost,
|
cd.nConfigDSPort);
|
cd.szLdapUrl = setupStrdup(szLdapUrl);
|
}
|
}
|
/////////////////////////////////////////////////////////////////////////////
|
//
|
// The dialog procedure for a single property page. You will need to create
|
// one of these for each property page used in the property sheet. This
|
// procedure processes dialog messages sent to your property page by Windows.
|
//
|
|
static BOOL CALLBACK
|
_DialogProcConfigDSInfo(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
{
|
BOOL bValueReturned = FALSE;
|
BOOL bResult = TRUE;
|
switch(uMsg)
|
{
|
case WM_INITDIALOG:
|
{
|
HGDIOBJ hGdiObj = GetStockObject(DEFAULT_GUI_FONT);
|
HWND heditwnd;
|
SendMessage(hwndDlg, WM_SETFONT, (WPARAM) ((struct HFONT__ *)hGdiObj)
|
, MAKELPARAM(TRUE, 0));
|
heditwnd = GetDlgItem(hwndDlg, IDC_LDAP_NAME);
|
SendMessage(heditwnd, WM_SETFONT, (WPARAM) ((struct HFONT__
|
*)hGdiObj) , MAKELPARAM(TRUE, 0));
|
heditwnd = GetDlgItem(hwndDlg, IDC_LDAP_PORT);
|
SendMessage(heditwnd, WM_SETFONT, (WPARAM) ((struct HFONT__
|
*)hGdiObj) , MAKELPARAM(TRUE, 0));
|
cd.hwndPage[PG_FIFTH] = hwndDlg;
|
_RefreshConfigDSInfo(hwndDlg);
|
}
|
break;
|
|
case WM_COMMAND:
|
// Windows sends WM_COMMAND messages whenever the user clicks on
|
// a control in your property page. If you need to perform some
|
// special action, such as validating data or responding to a
|
// button click, do it here.
|
|
break;
|
|
case WM_NOTIFY:
|
// Windows sends WM_NOTIFY messages to your property page whenever
|
// something interesting happens to the page. This could be page
|
// activation/deactivation, a button click, etc. The wParam parameter
|
// contains a pointer to the property page. The lParam parameter
|
// contains a pointer to an NMHDR structure. The code field of this
|
// structure contains the notification message code being sent. The
|
// property sheet API allows you to alter the behavior of these
|
// messages by returning a value for each message. To return a value,
|
// use the SetWindowLong Windows SDK function.
|
|
switch(((NMHDR*)lParam)->code)
|
{
|
|
case PSN_SETACTIVE:
|
{
|
CenterWindow(GetParent(hwndDlg));
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK |
|
PSWIZB_NEXT);
|
_RefreshConfigDSInfo(hwndDlg);
|
break;
|
|
}
|
case PSN_KILLACTIVE:
|
// This notification is sent upon deactivation of the property page.
|
// Here you can do whatever might be necessary for this action, such
|
// as saving the state of the controls. You should also reset the
|
// the state of the wizard buttons here, as both the Back and Next
|
// buttons should be active when you leave the AskOptions function.
|
//
|
// NOTE: If you do not want the page deactivated, return -1.
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK |
|
PSWIZB_NEXT);
|
break;
|
|
case PSN_WIZBACK:
|
_SaveConfigDSInfo(hwndDlg);
|
break;
|
|
case PSN_WIZNEXT:
|
{
|
_SaveConfigDSInfo(hwndDlg);
|
|
if(!IsValidLdapServer(cd.szConfigDSHost, cd.nConfigDSPort, NULL,
|
NULL, NULL))
|
{
|
char szErrMsg[MAX_PATH];
|
LoadString(mi.m_hModule, IDS_ERR_BAD_LDAPURL, szErrMsg,
|
sizeof(szErrMsg));
|
if(NsSetupMessageBox(GetParent(hwndDlg), szErrMsg, NULL,
|
MB_ICONWARNING | MB_OK) == IDOK)
|
{
|
SetFocus(GetDlgItem(hwndDlg, IDC_LDAPURL));
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
|
bValueReturned = TRUE;
|
break;
|
}
|
}
|
|
break;
|
}
|
case PSN_QUERYCANCEL:
|
// This notification is sent when the user clicks the Cancel button.
|
// It is also sent in response to the WM_CLOSE messages issued
|
// by PSN_WIZBACK and PSN_WIZNEXT. Make sure that we only process
|
// this message if the result is not back or next so that we don't
|
// nuke the return value assigned by PSN_WIZBACK or PSN_WIZNEXT.
|
//
|
// NOTE: To prevent the cancel from occuring, return -1.
|
|
if(mi.m_nResult != NS_WIZBACK && mi.m_nResult != NS_WIZNEXT)
|
{
|
if(QueryExit(hwndDlg))
|
{
|
mi.m_nResult = NS_WIZCANCEL;
|
}
|
else
|
{
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
|
bValueReturned = TRUE;
|
}
|
}
|
break;
|
}
|
break;
|
}
|
|
return bValueReturned;
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
//
|
// The procedure is for the property page that ask for configuration directory
|
// information, such as base dn (e.g cn=directory manager) and password. This
|
// "refresh" procedure will get called during the property page
|
// initialization. You will need to create one of these for each property page
|
// used in the property sheet.
|
|
static void _RefreshLdapUser(HWND hwndDlg)
|
{
|
BOOL bResult = FALSE;
|
if(!IsWindow(hwndDlg))
|
{
|
setupLogMessage("Error", "nda", "Calling RefreshLdapUser with bad
|
handle");
|
return;
|
}
|
|
if(cd.szLdapUser)
|
{
|
bResult = SetDlgItemText(hwndDlg, IDC_USERID, cd.szLdapUser);
|
|
if(FALSE == bResult)
|
{
|
setupLogMessage("Error", "nda", "unable to set ldap user");
|
}
|
}
|
|
if(cd.szLdapPWD)
|
{
|
bResult = SetDlgItemText(hwndDlg, IDC_PASSWORD, cd.szLdapPWD);
|
|
if(FALSE == bResult)
|
{
|
setupLogMessage("Error", "nda", "unable to set ldap password");
|
}
|
}
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
//
|
// The procedure is for the property page that ask for configuration directory
|
// information, such as base dn (e.g cn=directory manager) and password. This
|
// "save" procedure will get called whenever the user hits the "Next" button,
|
// saving all the data that the user have entered. You will need
|
// to create one of these for each property page used in the property sheet.
|
//
|
|
static void _SaveLdapUser(HWND hwndDlg)
|
{
|
BOOL bResult = FALSE;
|
char szTemp[MAX_PATH];
|
|
if(IsWindow(hwndDlg))
|
{
|
GetDlgItemText(hwndDlg, IDC_USERID, szTemp, MAX_PATH);
|
|
if(cd.szLdapUser)
|
setupFree(cd.szLdapUser);
|
|
cd.szLdapUser = setupStrdup(szTemp);
|
SetDlgItemText(hwndDlg, IDC_USERID, cd.szLdapUser);
|
|
GetDlgItemText(hwndDlg, IDC_PASSWORD, szTemp, MAX_PATH);
|
|
if(cd.szLdapPWD)
|
setupFree(cd.szLdapPWD);
|
|
cd.szLdapPWD = setupStrdup(szTemp);
|
SetDlgItemText(hwndDlg, IDC_PASSWORD, cd.szLdapPWD);
|
}
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
//
|
// The dialog procedure for a single property page. You will need to create
|
// one of these for each property page used in the property sheet. This
|
// procedure processes dialog messages sent to your property page by Windows.
|
//
|
|
static BOOL CALLBACK
|
_DialogProcLdapUser(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
{
|
BOOL bValueReturned = FALSE;
|
BOOL bResult = TRUE;
|
|
switch(uMsg)
|
{
|
case WM_INITDIALOG:
|
{
|
HGDIOBJ hGdiObj;
|
HWND heditwnd;
|
|
hGdiObj = GetStockObject(DEFAULT_GUI_FONT);
|
SendMessage(hwndDlg, WM_SETFONT, (WPARAM) ((struct HFONT__ *)hGdiObj)
|
, MAKELPARAM(TRUE, 0));
|
heditwnd = GetDlgItem(hwndDlg, IDC_USERID);
|
SendMessage(heditwnd, WM_SETFONT, (WPARAM) ((struct HFONT__
|
*)hGdiObj) , MAKELPARAM(TRUE, 0));
|
|
cd.hwndPage[PG_SIXTH] = hwndDlg;
|
_RefreshLdapUser(hwndDlg);
|
}
|
break;
|
|
|
case WM_COMMAND:
|
// Windows sends WM_COMMAND messages whenever the user clicks on
|
// a control in your property page. If you need to perform some
|
// special action, such as validating data or responding to a
|
// button click, do it here.
|
|
break;
|
|
case WM_NOTIFY:
|
// Windows sends WM_NOTIFY messages to your property page whenever
|
// something interesting happens to the page. This could be page
|
// activation/deactivation, a button click, etc. The wParam parameter
|
// contains a pointer to the property page. The lParam parameter
|
// contains a pointer to an NMHDR structure. The code field of this
|
// structure contains the notification message code being sent. The
|
// property sheet API allows you to alter the behavior of these
|
// messages by returning a value for each message. To return a value,
|
// use the SetWindowLong Windows SDK function.
|
|
switch(((NMHDR*)lParam)->code)
|
{
|
|
case PSN_SETACTIVE:
|
{
|
|
CenterWindow(GetParent(hwndDlg));
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK |
|
PSWIZB_NEXT);
|
_RefreshLdapUser(hwndDlg);
|
|
break;
|
}
|
case PSN_KILLACTIVE:
|
// This notification is sent upon deactivation of the property page.
|
// Here you can do whatever might be necessary for this action, such
|
// as saving the state of the controls. You should also reset the
|
// the state of the wizard buttons here, as both the Back and Next
|
// buttons should be active when you leave the AskOptions function.
|
//
|
// NOTE: If you do not want the page deactivated, return -1.
|
|
PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK |
|
PSWIZB_NEXT);
|
|
break;
|
|
case PSN_WIZBACK:
|
_SaveLdapUser(hwndDlg);
|
|
break;
|
|
case PSN_WIZNEXT:
|
{
|
Ldap *ldap;
|
LdapErrorCode err;
|
|
UINT uLdapPort = 0;
|
char *szLdapHost = NULL;
|
char *szLdapSuffix = NULL;
|
char *szLdapUser = NULL;
|
|
|
_SaveLdapUser(hwndDlg);
|
|
szLdapHost = setupStrdup(cd.szConfigDSHost);
|
uLdapPort = cd.nConfigDSPort;
|
szLdapUser = setupStrdup(cd.szLdapUser);
|
|
if(!IsValidLdapUser(szLdapHost, uLdapPort, NULL, &szLdapUser,
|
cd.szLdapPWD, TRUE))
|
{
|
char szErrMsg[MAX_PATH];
|
|
LoadString(mi.m_hModule, IDS_ERR_BAD_USER, szErrMsg,
|
sizeof(szErrMsg));
|
|
if(NsSetupMessageBox(GetParent(hwndDlg), szErrMsg, NULL,
|
MB_ICONWARNING | MB_OK) == IDOK)
|
{
|
bValueReturned = TRUE;
|
SetFocus(GetDlgItem(hwndDlg, IDC_USERID));
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
|
|
break;
|
}
|
}
|
|
if(szLdapHost)
|
setupFree(szLdapHost);
|
if(szLdapUser)
|
setupFree(szLdapUser);
|
|
break;
|
}
|
case PSN_QUERYCANCEL:
|
// This notification is sent when the user clicks the Cancel button.
|
// It is also sent in response to the WM_CLOSE messages issued
|
// by PSN_WIZBACK and PSN_WIZNEXT. Make sure that we only process
|
// this message if the result is not back or next so that we don't
|
// nuke the return value assigned by PSN_WIZBACK or PSN_WIZNEXT.
|
//
|
// NOTE: To prevent the cancel from occuring, return -1.
|
|
if(mi.m_nResult != NS_WIZBACK && mi.m_nResult != NS_WIZNEXT)
|
{
|
if(QueryExit(hwndDlg))
|
{
|
mi.m_nResult = NS_WIZCANCEL;
|
}
|
else
|
{
|
SetWindowLong(hwndDlg, DWL_MSGRESULT, -1);
|
bValueReturned = TRUE;
|
}
|
}
|
break;
|
}
|
break;
|
}
|
|
return bValueReturned;
|
}
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
//
|
// This procedure is called by the NDA_PostInstall API after the binaries
|
// are laid down as part of the post installation configuration.
|
// In this example, post-installation will import a schema file
|
// into the configuration directory server
|
//
|
|
void ImportFiles()
|
{
|
char *ldapURL;
|
char *dcroot;
|
char *userid;
|
char *userpwd;
|
char schemaFile[MAX_PATH];
|
int err;
|
|
ldapURL = localToUTF8(getFromInfo(NDA_LDAPURL));
|
userid = getFromInfo(NDA_LDAPUSER);
|
userpwd = getFromInfo(NDA_LDAPPWD);
|
|
wsprintf(schemaFile, "%s/ldif/schema.conf", serverRoot);
|
|
err = setupInsertPluginSchemaEntries(ldapURL, userid, userpwd, schemaFile);
|
|
if(err != OKAY) {
|
setupLog(NULL, "Fail to update schema entries");
|
}
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_PreInstall
|
//
|
// This function is called by the installation framework before asking the
|
// user any questions. Here you should determine if all of the requisites
|
// for installing this component are being met. If this operation succeeds
|
// return TRUE, otherwise display an error message and return FALSE to abort
|
// installation.
|
//
|
|
BOOL __declspec(dllexport)
|
NDA_PreInstall(void)
|
{
|
return TRUE;
|
}
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_AskOptions
|
//
|
// This function is called by the installation framework to query the user for
|
// information about your component. Here you should ask all of the questions
|
// required to install your component as a series of wizard property sheets.
|
//
|
|
INT __declspec(dllexport)
|
NDA_AskOptions(HWND hwndParent, INT nDirection)
|
{
|
|
PROPSHEETPAGE psp[NUM_PROP_PAGES];
|
int nPages = 1;
|
int nStartPage = 0;
|
|
mi.m_nResult = NS_WIZERROR;
|
nStartPage = (nDirection == NS_WIZNEXT) ? PG_FIRST : PG_SECOND;
|
|
|
AddWizardPage(mi.m_hModule, &psp[PG_FIRST], IDD_LDAP_INFO,
|
_DialogProcConfigDSInfo);
|
nPages++;
|
AddWizardPage(mi.m_hModule, &psp[PG_SECOND], IDD_LDAPUSER,
|
_DialogProcLdapUser);
|
nPages++;
|
|
if(WizardDialog(mi.m_hModule, hwndParent, psp, nPages, nStartPage) < 0)
|
{
|
mi.m_nResult = NS_WIZERROR;
|
}
|
|
return mi.m_nResult;
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_GetSummary
|
//
|
// This function is called by the installation framework after all questions,
|
// for all components, have been asked. Here you should provide a detailed
|
// summary explaining all of the choices selected by the user.
|
//
|
// IMPORTANT NOTE: Each line MUST end in a carriage return/line feed
|
// combination ("\r\n") as this string is placed in an edit control. Edit
|
// controls do not properly handle single "\n" end-of-line characters.
|
//
|
|
VOID __declspec(dllexport)
|
NDA_GetSummary(char * lpszSummary)
|
{
|
char* psz = lpszSummary;
|
if(cd.szLdapUrl)
|
{
|
psz += sprintf(psz, NDA_INFO_LDAPURL, cd.szLdapUrl);
|
}
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_ReadLocalCache
|
//
|
// This function is called by the installation framework during silent install
|
// to intialize your data from the local section of the cache created above.
|
// Here you should read any information stored in the installation cache's
|
// local section that you need. If this operation succeeds return TRUE,
|
// otherwise display an error message and return FALSE to indicate an error.
|
//
|
|
BOOL __declspec(dllexport)
|
NDA_ReadLocalCache(LPCSTR lpszCacheFileName, LPCSTR lpszSectionName)
|
{
|
if (_infoFile == NULL)
|
{
|
_infoFile = strdup(lpszCacheFileName);
|
}
|
|
cd.szLdapUrl = setupGetInfString(lpszSectionName, NDA_LDAPURL , NULL,
|
lpszCacheFileName);
|
cd.szLdapUser = setupGetInfString(lpszSectionName, NDA_LDAPUSER , NULL,
|
lpszCacheFileName);
|
cd.szLdapPWD = setupGetInfString(lpszSectionName, NDA_LDAPPWD , NULL,
|
lpszCacheFileName);
|
|
return TRUE;
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_PostInstall
|
//
|
// The framework calls this function to perform post-installation
|
// configuration. Here you should set values in any product configuration
|
// files, install services, add registry keys, start servers, and anything
|
// else that can only be done once the binaries are layed down on the disk.
|
// If the function succeeds return TRUE, otherwise return FALSE to indicate
|
// an error.
|
//
|
|
BOOL __declspec(dllexport)
|
NDA_PostInstall(VOID)
|
{
|
char url[1024];
|
|
ImportFiles();
|
|
wsprintf(url, "http://www.example.com");
|
StartNavigator((char *)url);
|
return TRUE;
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_PreUnInstall
|
//
|
// The framework calls this function to perform pre-uninstallation
|
// configuration. Here you should delete registry keys, stop services etc.
|
// If the function succeeds return TRUE, otherwise return FALSE to indicate
|
// an error.
|
//
|
|
BOOL __declspec(dllexport)
|
NDA_PreUnInstall(LPCSTR pszServerRoot)
|
{
|
return TRUE;
|
}
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_PostUnInstall
|
//
|
// The framework calls this function to perform post-uninstallation
|
// configuration. Here you should restart services etc.
|
// If the function succeeds return TRUE, otherwise return FALSE to indicate
|
// an error.
|
//
|
BOOL __declspec(dllexport)
|
NDA_PostUnInstall(LPCSTR pszServerRoot)
|
{
|
return TRUE;
|
}
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
// NDA_WriteLocalCache
|
//
|
// This function is called by the installation framework when the user clicks
|
// Next at the summary screen. Here you should write all information entered
|
// by the user into the installation cache for use during silent installation.
|
// Data written to this file is not interpreted by the framework, and may
|
// consist of any values that you will need to perform the installation (not
|
// just values entered by the user). If this operation succeeds return TRUE,
|
// otherwise display an error message and return FALSE to indicate an error.
|
//
|
|
BOOL __declspec(dllexport)
|
NDA_WriteLocalCache(LPCSTR lpszCacheFileName, LPCSTR lpszSectionName)
|
{
|
|
if (_infoFile == NULL)
|
{
|
_infoFile = strdup(lpszCacheFileName);
|
}
|
|
setupWriteInfString(lpszSectionName, NDA_LDAPURL, cd.szLdapUrl,
lpszCacheFileName);
|
setupWriteInfString(lpszSectionName, NDA_LDAPUSER, cd.szLdapUser,
lpszCacheFileName);
|
setupWriteInfString(lpszSectionName, NDA_LDAPPWD, cd.szLdapPWD,
lpszCacheFileName);
|
|
return TRUE;
|
}
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
// DllMain
|
//
|
// The Windows DLL main entry point. Called upon loading the DLL into memory.
|
// Perform all initialization in the DLL_PROCESS_ATTACH reason handler, and
|
// release any resources that you have allocated in the DLL_PROCESS_DETACH
|
// message handler. See the Windows SDK documentation for more information
|
// on this function.
|
//
|
|
BOOL WINAPI
|
DllMain(HANDLE hModule, ULONG ulReasonForCall, LPVOID lpReserved)
|
{
|
switch(ulReasonForCall)
|
{
|
case DLL_PROCESS_ATTACH:
|
ZeroMemory(&mi, sizeof(MODULEINFO));
|
mi.m_hModule = hModule;
|
_InitializeControlData();
|
break;
|
case DLL_PROCESS_DETACH:
|
break;
|
case DLL_THREAD_ATTACH:
|
break;
|
case DLL_THREAD_DETACH:
|
break;
|
}
|
return TRUE;
|
}
|
|