在软件中控制 Windows 防火墙
本文简述如何在程序中控制 Windows FireWall 并提供封装好的代码
在安装程序中,往往需要安装一个本地的网络服务,但是在开了 Windows 防火墙的机器上,安装了服务后即使启动了该服务,别的机器仍然无法连接,这是因为服务是在 system 账号中启动的,不会提示用户是否让该服务通过 Windows 防火墙,因此服务启动了,但是网络端口却被 Windows 防火墙屏蔽了,因此安装程序应该能够自己检测到 Windows 防火墙是否开启,如果开启应该自动将安装的服务增加到 Windows 防火墙的例外程序列表中,控制 Windows 防火墙其实很简单使用微软提供的 COM 接口就可以了,下面就是封装的类:
WindowsFirewall.h
//==================================================================================================
// MSI 安装函数封装类( Microsoft Installer Wrapper Template Class )
//
// 作者:王志科 版权没有,随意转载(请保留该文件头部信息)
//==================================================================================================
#pragma once
#include "msi.h"
#include "MsiDefs.h"
#include "MsiQuery.h"
#pragma comment(lib,"msi")
#ifndef CHK_EXP_RET
#define CHK_EXP_RET(exp,ret) if(exp){ return ret; }
#define CHK_EXP_RUN(exp,run) if(exp){ run; }
#endif
namespace MSI
{
//==============================================================================================
//
template<bool tManage = true> class HandlerT
{
public:
HandlerT(MSIHANDLE hMsi = NULL) : m_hMsi(hMsi) { }
~HandlerT() { if(tManage){ Close(); } }
public:
void Close(){ if(m_hMsi){ MsiCloseHandle(m_hMsi); m_hMsi = NULL; } }
public:
void Attach(MSIHANDLE hMsi){ ATLASSERT(m_hMsi != NULL); m_hMsi = hMsi; }
MSIHANDLE Detach(){ MSIHANDLE hMsi = m_hMsi; m_hMsi = NULL; return hMsi; }
public:
void operator =(MSIHANDLE hMsi){ Close(); m_hMsi = hMsi; }
MSIHANDLE* operator &() { Close(); return &m_hMsi; }
operator MSIHANDLE() { return m_hMsi; }
protected:
MSIHANDLE m_hMsi;
};
//==============================================================================================
//
template<bool tManage = true> class DatabaseT : public HandlerT<tManage>
{
public:
DatabaseT(MSIHANDLE hDatabase = NULL)
{
m_hMsi = hDatabase;
}
DatabaseT(LPCTSTR lpszDatabase, LPCTSTR szPersist)
{
Open(lpszDatabase, szPersist);
}
public:
UINT Open(LPCTSTR lpszDatabase, LPCTSTR szPersist)
{
return MsiOpenDatabase(lpszDatabase, szPersist, &m_hMsi);
}
VOID Close(BOOL bCommit = FALSE)
{
if(bCommit) Commit();
HandlerT::Close();
}
UINT Commit()
{
return MsiDatabaseCommit(m_hMsi);
}
UINT Import(LPCTSTR szFolderPath,LPCTSTR szFileName)
{
return MsiDatabaseImport(m_hMsi,szFolderPath,szFileName);
}
UINT Export(LPCTSTR szTableName,LPCTSTR szFolderPath,LPCTSTR szFileName)
{
return MsiDatabaseExport(m_hMsi,szTableName,szFolderPath,szFileName);
}
UINT Merge(MSIHANDLE hDatabase, LPCTSTR szTableName)
{
return MsiDatabaseMerge(m_hMsi,hDatabase,szTableName);
}
UINT GenerateTransform(MSIHANDLE hDatabase, LPCTSTR szTransformFile)
{
return MsiDatabaseGenerateTransform(m_hMsi,hDatabase,szTransformFile,0,0);
}
UINT ApplyTransform(LPCTSTR szTransformFile, INT iErrorConditions)
{
return MsiDatabaseApplyTransform(m_hMsi,szTransformFile,iErrorConditions);
}
MSIDBSTATE GetState()
{
return MsiGetDatabaseState(m_hMsi);
}
MSICONDITION TableExists(LPCTSTR szTableName)
{
return MsiDatabaseIsTablePersistent(m_hMsi,szTableName);
}
MSICONDITION IsTablePersistent(LPCTSTR szTableName)
{
return MsiDatabaseIsTablePersistent(m_hMsi,szTableName);
}
// RecordT
UINT PrimaryKeys(LPCTSTR szTableName, MSIHANDLE& hRecord)
{
return MsiDatabaseGetPrimaryKeys(m_hMsi,szTableName,&hRecord);
}
// View
UINT OpenView(LPCTSTR szQuery, MSIHANDLE* pView)
{
return MsiDatabaseOpenView(m_hMsi,szQuery, pView);
}
// View
UINT OpenExecuteView(LPCTSTR szQuery, MSIHANDLE* pView)
{
UINT nCaller = OpenView(szQuery, pView);
CHK_EXP_RET(nCaller != ERROR_SUCCESS , nCaller);
return MsiViewExecute(*pView, NULL);
}
};
//==============================================================================================
//
template<bool tManage = true> class RecordT : public HandlerT<tManage>
{
public:
RecordT(UINT cParams = 3)
{
m_hMsi = MsiCreateRecord(cParams);
}
public:
UINT GetFieldCount()
{
return MsiRecordGetFieldCount(m_hMsi);
}
BOOL IsNull(UINT iField)
{
return MsiRecordIsNull(m_hMsi,iField);
}
UINT SetNumber(UINT iField, INT iValue)
{
return MsiRecordSetInteger(m_hMsi,iField,iValue);
}
INT GetNumber(UINT iField)
{
return MsiRecordGetInteger(m_hMsi,iField);
}
BOOL GetString(UINT iField, CString& strValue)
{
DWORD dwValue = 4096;
LPTSTR szValue = strValue.GetBuffer(dwValue);
UINT nCaller = MsiRecordGetString(m_hMsi,iField,szValue,&dwValue);
if(nCaller == ERROR_MORE_DATA)
{
dwValue += 2;
strValue.ReleaseBuffer();
szValue = strValue.GetBuffer(dwValue);
nCaller = MsiRecordGetString(m_hMsi,iField,szValue,&dwValue);
}
strValue.ReleaseBuffer();
return nCaller == ERROR_SUCCESS;
}
UINT GetString(UINT iField, LPTSTR szValue, LPDWORD dwValue)
{
return MsiRecordGetString(m_hMsi,iField,szValue,dwValue);
}
UINT SetString(UINT iField, LPCTSTR szValue)
{
return MsiRecordSetString(m_hMsi,iField,szValue);
}
UINT GetStream(UINT iField, LPSTR szBuffer,LPDWORD dwBuffer)
{
return MsiRecordReadStream(m_hMsi,iField,szBuffer,dwBuffer);
}
UINT SetStream(UINT iField, LPCTSTR szFilePath)
{
return MsiRecordSetStream(m_hMsi,iField,szFilePath);
}
};
//==============================================================================================
//
template<bool tManage = true> class ViewT : public HandlerT<tManage>
{
public:
ViewT(MSIHANDLE hView = NULL)
{
m_hMsi = hView;
}
ViewT(MSIHANDLE hDatabase, LPCTSTR szQuery)
{
Open(hDatabase, szQuery);
}
public:
UINT Open(MSIHANDLE hDatabase, LPCTSTR szQuery)
{
return MsiDatabaseOpenView(hDatabase,szQuery,&m_hMsi);
}
BOOL Execute(MSIHANDLE hRecord = NULL)
{
return MsiViewExecute(m_hMsi, hRecord);
}
UINT Fetch(MSIHANDLE& hRecord)
{
return MsiViewFetch(m_hMsi, &hRecord);
}
UINT Modify(MSIMODIFY eModify, MSIHANDLE hRecord)
{
return MsiViewModify(m_hMsi,eModify,hRecord);
}
UINT GetColumnInfo(MSICOLINFO eColumnInfo,MSIHANDLE& hRecord)
{
return MsiViewGetColumnInfo(m_hMsi,eColumnInfo,&hRecord);
}
// 执行了 Execute 之后,那么在下一次执行 Execute 之前必须调用该函数
UINT Release()
{
return MsiViewClose(m_hMsi);
}
};
template<bool tManage = true> class SummaryInformationT : public HandlerT<tManage>
{
public:
SummaryInformationT(MSIHANDLE hDatabase, LPCTSTR szDatabase = NULL, UINT uiUpdateCount = 20)
{
Open(hDatabase,szDatabase, uiUpdateCount);
}
public:
UINT GetPropertyCount(PUINT puiPropertyCount)
{
return MsiSummaryInfoGetPropertyCount(m_hMsi, puiPropertyCount);
}
UINT GetProperty(UINT uProperty,PUINT puiDataType,PINT piValue,PFILETIME pFileTime,LPTSTR szValue,LPDWORD dwValue)
{
return MsiSummaryInfoGetProperty(m_hMsi,uProperty,puiDataType,piValue,pFileTime,szValue,dwValue);
}
UINT SetProperty(UINT uProperty,UINT uDataType,INT iValue,PFILETIME pFileTime,LPTSTR szValue)
{
return MsiSummaryInfoSetProperty(m_hMsi,uProperty,uDataType,iValue,pFileTime,szValue);
}
UINT Open(MSIHANDLE hDatabase, LPCTSTR szDatabase = NULL, UINT uiUpdateCount = 20)
{
return MsiGetSummaryInformation(hDatabase,szDatabase, uiUpdateCount, &m_hMsi);
}
UINT Commit()
{
return MsiSummaryInfoPersist(m_hMsi);
}
public:
UINT GetPropertyInteger(UINT uProperty,PINT piValue)
{
UINT uiDataType = VT_I4;
return GetProperty(uProperty,&uiDataType,piValue,NULL,NULL,0);
}
UINT GetPropertyFiletime(UINT uProperty,PFILETIME pFileTime)
{
UINT uiDataType = VT_FILETIME;
return GetProperty(uProperty,&uiDataType,NULL,pFileTime,NULL,0);
}
UINT GetPropertyString(UINT uProperty,LPTSTR szValue,LPDWORD dwValue)
{
UINT uiDataType = VT_LPSTR;
return GetProperty(uProperty,&uiDataType,NULL,NULL,szValue,dwValue);
}
UINT SetPropertyInteger(UINT uProperty,INT iValue)
{
return SetProperty(uProperty,VT_I4,iValue,NULL,NULL);
}
UINT SetPropertyString(UINT uProperty,LPWSTR szValue)
{
return SetProperty(uProperty, VT_LPSTR, 0, NULL, szValue);
}
UINT SetPropertyFiletime(UINT uProperty,PFILETIME pFileTime)
{
return SetProperty(uProperty,VT_FILETIME,0,pFileTime,NULL);
}
public:
VOID Close(BOOL bCommit = FALSE)
{
if(bCommit) Commit();
HandlerT::Close();
}
};
//==============================================================================================
// 安装控制函数
template<typename T = CWindow > struct InstallerT
{
public:
InstallerT()
{
MemberInitialize();
m_nProgressBar = 0;
m_nProcessInfo = 0;
}
~InstallerT()
{
CancelInstaller();
}
public:
VOID CancelInstaller()
{
m_bCancelInstall = FALSE;
}
VOID SetExternalUI(DWORD dwMsgFilter)
{
MsiSetExternalUI(InstallerCallback, dwMsgFilter, (LPVOID)this);
}
UINT SetInternalUI(INSTALLUILEVEL nInstallerLevel, HWND* pWindow = NULL)
{
return MsiSetInternalUI(nInstallerLevel, pWindow);
}
// 在命令行中可以设置动作和属性,例如下面将产品安装到 D:\LiveSystem
// TEXT("ACTION=INSTALL TARGETDIR=\"D:\\LiveSystem\"")
UINT StartInstaller(LPCTSTR lpszPackage, LPCTSTR szCommandLine = NULL)
{
return MsiInstallProduct(lpszPackage, szCommandLine);
}
UINT SetControlID(UINT nProgressBar, UINT nProcessInfo = 0)
{
m_nProgressBar = nProgressBar;
m_nProcessInfo = nProcessInfo;
return ERROR_SUCCESS;
}
UINT SetScriptStringID(UINT nScriptStrID = 0)
{
m_nScriptStrID = nScriptStrID;
return ERROR_SUCCESS;
}
public:
// 采用默认参数安装指定的包
UINT InstallPackage(LPCTSTR lpszPackage, LPCTSTR pszCommand = NULL)
{
// 准备参数变量
DWORD dwUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_SOURCERESONLY;
DWORD dwMessageFilter = INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO
| INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_OUTOFDISKSPACE
| INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_RESOLVESOURCE
| INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_ACTIONSTART
| INSTALLLOGMODE_SHOWDIALOG | INSTALLLOGMODE_TERMINATE
| INSTALLLOGMODE_FILESINUSE | INSTALLLOGMODE_PROGRESS
| INSTALLLOGMODE_EXTRADEBUG | INSTALLLOGMODE_WARNING
| INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR;
MemberInitialize();
SetExternalUI(dwMessageFilter);
SetInternalUI((INSTALLUILEVEL)dwUILevel);
return StartInstaller(lpszPackage, pszCommand);
}
public:
// 下面的函数是消息回调
enum InstallerProgress
{
PROGRESS_RESET = 0,
PROGRESS_ACTIONINFO = 1,
PROGRESS_REPORT = 2,
PROGRESS_ADDITION = 3,
};
enum InstallerCommonData
{
COMMON_DATA_LANGUAGE = 0,
COMMON_DATA_CAPTION = 1,
COMMON_DATA_CANCELSHOW = 2,
};
// premature termination, possibly fatal OOM
INT OnInstallerFatalExit(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
return pT->MessageBox(lpszMessage,m_strTitle, uFlags);
}
// formatted error message
INT OnInstallerError(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
return pT->MessageBox(lpszMessage, m_strTitle, uFlags);
}
// formatted warning message
INT OnInstallerWarning(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
return pT->MessageBox(lpszMessage, m_strTitle ,uFlags);
}
// user request message
INT OnInstallerUser(UINT uFlags, LPCTSTR lpszMessage)
{
// 需要分析 uFlags 来确定返回 IDOK 或者 IDYES 或者其他值
return (uFlags & 0x0F) == MB_YESNO ? IDYES : IDOK;
}
// informative message for log
INT OnInstallerInfo(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
// list of files in use that need to be replaced
INT OnInstallerFilesInuse(UINT uFlags, LPCTSTR lpszMessage)
{
// 显示文件被使用的对话框
return ERROR_SUCCESS;
}
// request to determine a valid source location
INT OnInstallerResolveSource(UINT uFlags, LPCTSTR lpszMessage)
{
return ERROR_SUCCESS;
}
// insufficient disk space message
INT OnInstallerOutOfDiskSpace(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
// start of action: action name & description
INT OnInstallerActionStart(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
// formatted data associated with individual action item
INT OnInstallerActionData(UINT uFlags, LPCTSTR lpszMessage)
{
// 如果没有初始化进度条,直接返回
CHK_EXP_RET( m_nProgressTotal == 0 , IDOK );
T* pT = static_cast<T*>(this);
pT->SetDlgItemText(m_nProcessInfo, lpszMessage);
if( m_bEnableActionData && m_nProgressBar )
{
HWND hProgressBar = pT->GetDlgItem(m_nProgressBar);
SendMessage(hProgressBar, PBM_STEPIT, 0, 0);
}
return IDOK;
}
// progress gauge info: units so far, total
INT OnInstallerProgress(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
HWND hProgressBar = pT->GetDlgItem(m_nProgressBar);
LONG nData[4] = { 0 , 0 , 0 , 0 };
if( ParseProgressString(lpszMessage, nData) )
{
if(nData[0] == PROGRESS_RESET)
{
m_nProgressTotal = nData[1];
m_bProgressForward = (nData[2] == 0);
m_nProgress = m_bProgressForward ? 0 : m_nProgressTotal ;
// 设置进度条的工作长度
SendMessage(hProgressBar, PBM_SETRANGE32, 0, m_nProgressTotal);
// 如果在处理脚本结束进度条,否则重置
WPARAM nCurrentPos = m_bProgressScript ? m_nProgressTotal : m_nProgress;
SendMessage(hProgressBar, PBM_SETPOS, nCurrentPos , 0);
m_nPosition = 0;
// 检测新的状态
m_bProgressScript = (nData[3] == 1);
if( m_bProgressScript && m_nProcessInfo && m_nScriptStrID )
{
TCHAR szScriptString[MAX_PATH] = { TEXT("") };
AtlLoadString(m_nScriptStrID, szScriptString, MAX_PATH);
pT->SetDlgItemText(m_nProcessInfo, szScriptString);
}
}
else if(nData[0] == PROGRESS_ACTIONINFO )
{
if( nData[2] && m_nProgressBar )
{
WPARAM nStep = m_bProgressForward ? nData[1] : -1 * nData[1] ;
SendMessage(hProgressBar, PBM_SETSTEP, nStep, 0);
}
m_bEnableActionData = nData[2] ? TRUE : FALSE ;
}
else if(nData[0] == PROGRESS_REPORT)
{
if( m_nProgressTotal != 0 )
{
m_nPosition += nData[1];
WPARAM nCurrentPos = m_bProgressForward ? m_nPosition : -1 * m_nPosition;
SendMessage(hProgressBar, PBM_SETPOS, nCurrentPos, 0);
}
}
else if(nData[0] == PROGRESS_ADDITION)
{
}
}
return m_bCancelInstall ? IDCANCEL : IDOK ;
}
// product info for dialog: language Id, dialog caption
INT OnInstallerCommonData(UINT uFlags, LPCTSTR lpszMessage)
{
ParseCommonDataString(lpszMessage);
return IDOK;
}
// sent prior to UI initialization, no string data
INT OnInstallerInitialize(UINT uFlags, LPCTSTR lpszMessage /* = NULL */)
{
return IDOK;
}
// sent after UI termination, no string data
INT OnInstallerTerminate(UINT uFlags, LPCTSTR lpszMessage /* = NULL */)
{
return IDOK;
}
// sent prior to display or authored dialog or wizard
INT OnInstallerShowDialog(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
public:
INT OnInstallerPrepare(UINT uiMessageType, LPCTSTR lpszMessage)
{
if( m_bFirstTimeCall )
{
MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
m_bFirstTimeCall = FALSE;
}
return ERROR_SUCCESS ;
}
public:
// 消息回调的处理函数
INT InstallerHandler(UINT uiMessageType, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
// 首先允许进行特殊的消息处理
INT nPrepare = pT->OnInstallerPrepare(uiMessageType, lpszMessage);
CHK_EXP_RET( FAILED( nPrepare ) , nPrepare );
// 根据消息类型进行分类处理
UINT uFlags = (uiMessageType & 0x00FFFFFF);
switch( (INSTALLMESSAGE)(0xFF000000 & uiMessageType) )
{
case INSTALLMESSAGE_FATALEXIT : return pT->OnInstallerFatalExit (uFlags, lpszMessage);
case INSTALLMESSAGE_ERROR : return pT->OnInstallerError (uFlags, lpszMessage);
case INSTALLMESSAGE_WARNING : return pT->OnInstallerWarning (uFlags, lpszMessage);
case INSTALLMESSAGE_USER : return pT->OnInstallerUser (uFlags, lpszMessage);
case INSTALLMESSAGE_INFO : return pT->OnInstallerInfo (uFlags, lpszMessage);
case INSTALLMESSAGE_FILESINUSE : return pT->OnInstallerFilesInuse (uFlags, lpszMessage);
case INSTALLMESSAGE_RESOLVESOURCE : return pT->OnInstallerResolveSource (uFlags, lpszMessage);
case INSTALLMESSAGE_OUTOFDISKSPACE : return pT->OnInstallerOutOfDiskSpace(uFlags, lpszMessage);
case INSTALLMESSAGE_ACTIONSTART : return pT->OnInstallerActionStart (uFlags, lpszMessage);
case INSTALLMESSAGE_ACTIONDATA : return pT->OnInstallerActionData (uFlags, lpszMessage);
case INSTALLMESSAGE_PROGRESS : return pT->OnInstallerProgress (uFlags, lpszMessage);
case INSTALLMESSAGE_COMMONDATA : return pT->OnInstallerCommonData (uFlags, lpszMessage);
case INSTALLMESSAGE_INITIALIZE : return pT->OnInstallerInitialize (uFlags, lpszMessage);
case INSTALLMESSAGE_TERMINATE : return pT->OnInstallerTerminate (uFlags, lpszMessage);
case INSTALLMESSAGE_SHOWDIALOG : return pT->OnInstallerShowDialog (uFlags, lpszMessage);
}
return ERROR_SUCCESS;
}
protected:
static INT CALLBACK InstallerCallback(LPVOID lpContext, UINT uiMessageType, LPCTSTR lpszMessage)
{
InstallerT< T >* lpInstaller = reinterpret_cast< InstallerT< T >* >(lpContext);
return static_cast<T*>(lpInstaller)->InstallerHandler(uiMessageType,lpszMessage);
}
protected:
void MemberInitialize()
{
m_bShowCancel = TRUE;
m_wLanguage = LANG_NEUTRAL;
m_wCodePage = GetACP();
m_bProgressForward = TRUE;
m_bProgressScript = FALSE;
m_nProgressTotal = 0;
m_nProgress = 0;
m_nPosition = 0;
m_bEnableActionData = FALSE;
m_bCancelInstall = FALSE;
m_bFirstTimeCall = TRUE;
}
protected:
BOOL ParseCommonDataString(LPCTSTR lpString)
{
CHK_EXP_RET(lpString == NULL , FALSE );
CHK_EXP_RET(lpString[0] == 0 , FALSE );
// 分析字符串类型
lpString = StrStr(lpString, TEXT("1:"));
CHK_EXP_RET(lpString == NULL , FALSE);
LONG nCommon = lpString[3] - TEXT('0');
LPCTSTR lpStr2 = StrStr(lpString, TEXT("2:"));
LPCTSTR lpStr3 = StrStr(lpString, TEXT("3:"));
CHK_EXP_RET(lpStr2 == NULL , FALSE);
if( nCommon == COMMON_DATA_CAPTION )
{
LPTSTR lpTail = (LPTSTR)lpStr3;
if(lpTail != NULL){ *lpTail = 0; }
m_strTitle = lpString + 2; //Skip "2:"
}
else if( nCommon == COMMON_DATA_LANGUAGE )
{
m_wLanguage = StrToLong(lpStr2 + 3);
CHK_EXP_RET(lpStr3 == NULL , FALSE);
m_wCodePage = StrToLong(lpStr3 + 3);
}
else if( nCommon == COMMON_DATA_CANCELSHOW )
{
LONG nShow = StrToLong(lpStr2 + 3);
m_bShowCancel = nShow ? TRUE : FALSE;
}
return TRUE;
}
BOOL ParseProgressString(LPCTSTR lpString, LONG nData[4])
{
ZeroMemory( nData, sizeof(LONG) * 4 );
CHK_EXP_RET(lpString == NULL , FALSE );
CHK_EXP_RET(lpString[0] == 0 , FALSE );
// 分析字符串类型
lpString = StrStr(lpString, TEXT("1:"));
CHK_EXP_RET(lpString == NULL , FALSE);
nData[0] = lpString[3] - TEXT('0');
// 第一个参数
lpString = StrStr(lpString, TEXT("2:"));
CHK_EXP_RET(lpString == NULL , TRUE);
nData[1] = StrToLong(lpString + 3);
// 第二个参数
lpString = StrStr(lpString, TEXT("3:"));
CHK_EXP_RET(lpString == NULL , TRUE);
nData[2] = StrToLong(lpString + 3);
// 第三个参数
lpString = StrStr(lpString, TEXT("4:"));
CHK_EXP_RET(lpString == NULL , TRUE);
nData[3] = StrToLong(lpString + 3);
return TRUE;
}
protected:
// 下面是 CommonData 分析出的数据
CString m_strTitle; // 一般是安装的产品标题
BOOL m_bShowCancel; // 是否要显示“取消”按钮
WORD m_wLanguage; // 当前的语言
WORD m_wCodePage; // 当前的代码页
// 下面是 ProgressInfo 分析出的数据
BOOL m_bProgressForward; // TRUE 表示当前处理进度条是增加的方式
BOOL m_bProgressScript; // TRUE 表示在处理脚本,可以显示“请稍后”
LONG m_nProgressTotal; // 进度条总数
LONG m_nProgress; // 进度数
LONG m_nPosition; // 当前位置
// 其他控制数据
BOOL m_bEnableActionData; // TRUE 表示 INSTALLOGMODE_ACTIONDATA 消息发送进度信息
BOOL m_bCancelInstall; // 如果要取消安装将该值设置为 TRUE
BOOL m_bFirstTimeCall; // 是否第一次调用外部界面接口
// 窗口显示信息控件的ID
UINT m_nProgressBar; // 进度条控件
UINT m_nProcessInfo; // 显示处理信息的控件
UINT m_nScriptStrID; // 处理脚本,请稍后
};
//==============================================================================================
// 全局的静态函数
inline UINT GetFileHash(LPCTSTR lpszFileName, DWORD dwOptions, MSIFILEHASHINFO& rMsiFileHashInfo)
{
rMsiFileHashInfo.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
return MsiGetFileHash(lpszFileName, dwOptions, &rMsiFileHashInfo);
}
inline UINT GetLastError(CString& strErrorDescription)
{
strErrorDescription = TEXT("");
MSIHANDLE hLastErrorRecord = MsiGetLastErrorRecord();
CHK_EXP_RET(hLastErrorRecord == NULL , NO_ERROR);
DWORD dwErrorLength = 4096;
HandlerT<> hRecord(hLastErrorRecord);
UINT nCaller = MsiFormatRecord(NULL,hRecord,NULL,&dwErrorLength);
if(nCaller == ERROR_MORE_DATA)
{
dwErrorLength ++;
LPTSTR lpszError = new TCHAR[dwErrorLength];
nCaller = MsiFormatRecord(NULL,hRecord,lpszError,&dwErrorLength);
if(nCaller == ERROR_SUCCESS) strErrorDescription = lpszError;
delete [] lpszError;
}
return nCaller;
}
inline UINT GetFileVersion(LPCTSTR szFilePath, CString& strVersion, CString& strLanguage)
{
DWORD dwVersion = 32, dwLang = 32;
LPTSTR lpszLang = strLanguage.GetBuffer(dwLang);
LPTSTR lpszVersion = strVersion.GetBuffer(dwVersion);
UINT nCaller = MsiGetFileVersion(szFilePath,lpszVersion, &dwVersion, lpszLang, &dwLang);
if(nCaller == ERROR_MORE_DATA)
{
// 增加内存空间
dwLang += 2; dwVersion += 2;
strVersion.ReleaseBuffer(); lpszVersion = strVersion.GetBuffer(dwVersion);
strLanguage.ReleaseBuffer(); lpszLang = strLanguage.GetBuffer(dwLang);
nCaller = MsiGetFileVersion(szFilePath,lpszVersion, &dwVersion, lpszLang, &dwLang);
}
else if(nCaller == ERROR_FILE_INVALID)
{
// 文件没有版本信息
nCaller = NO_ERROR;
}
strLanguage.ReleaseBuffer();
strVersion.ReleaseBuffer();
return nCaller;
}
}
//==================================================================================================
// MSI 安装函数封装类( Microsoft Installer Wrapper Template Class )
//
// 作者:王志科 版权没有,随意转载(请保留该文件头部信息)
//==================================================================================================
#pragma once
#include "msi.h"
#include "MsiDefs.h"
#include "MsiQuery.h"
#pragma comment(lib,"msi")
#ifndef CHK_EXP_RET
#define CHK_EXP_RET(exp,ret) if(exp){ return ret; }
#define CHK_EXP_RUN(exp,run) if(exp){ run; }
#endif
namespace MSI
{
//==============================================================================================
//
template<bool tManage = true> class HandlerT
{
public:
HandlerT(MSIHANDLE hMsi = NULL) : m_hMsi(hMsi) { }
~HandlerT() { if(tManage){ Close(); } }
public:
void Close(){ if(m_hMsi){ MsiCloseHandle(m_hMsi); m_hMsi = NULL; } }
public:
void Attach(MSIHANDLE hMsi){ ATLASSERT(m_hMsi != NULL); m_hMsi = hMsi; }
MSIHANDLE Detach(){ MSIHANDLE hMsi = m_hMsi; m_hMsi = NULL; return hMsi; }
public:
void operator =(MSIHANDLE hMsi){ Close(); m_hMsi = hMsi; }
MSIHANDLE* operator &() { Close(); return &m_hMsi; }
operator MSIHANDLE() { return m_hMsi; }
protected:
MSIHANDLE m_hMsi;
};
//==============================================================================================
//
template<bool tManage = true> class DatabaseT : public HandlerT<tManage>
{
public:
DatabaseT(MSIHANDLE hDatabase = NULL)
{
m_hMsi = hDatabase;
}
DatabaseT(LPCTSTR lpszDatabase, LPCTSTR szPersist)
{
Open(lpszDatabase, szPersist);
}
public:
UINT Open(LPCTSTR lpszDatabase, LPCTSTR szPersist)
{
return MsiOpenDatabase(lpszDatabase, szPersist, &m_hMsi);
}
VOID Close(BOOL bCommit = FALSE)
{
if(bCommit) Commit();
HandlerT::Close();
}
UINT Commit()
{
return MsiDatabaseCommit(m_hMsi);
}
UINT Import(LPCTSTR szFolderPath,LPCTSTR szFileName)
{
return MsiDatabaseImport(m_hMsi,szFolderPath,szFileName);
}
UINT Export(LPCTSTR szTableName,LPCTSTR szFolderPath,LPCTSTR szFileName)
{
return MsiDatabaseExport(m_hMsi,szTableName,szFolderPath,szFileName);
}
UINT Merge(MSIHANDLE hDatabase, LPCTSTR szTableName)
{
return MsiDatabaseMerge(m_hMsi,hDatabase,szTableName);
}
UINT GenerateTransform(MSIHANDLE hDatabase, LPCTSTR szTransformFile)
{
return MsiDatabaseGenerateTransform(m_hMsi,hDatabase,szTransformFile,0,0);
}
UINT ApplyTransform(LPCTSTR szTransformFile, INT iErrorConditions)
{
return MsiDatabaseApplyTransform(m_hMsi,szTransformFile,iErrorConditions);
}
MSIDBSTATE GetState()
{
return MsiGetDatabaseState(m_hMsi);
}
MSICONDITION TableExists(LPCTSTR szTableName)
{
return MsiDatabaseIsTablePersistent(m_hMsi,szTableName);
}
MSICONDITION IsTablePersistent(LPCTSTR szTableName)
{
return MsiDatabaseIsTablePersistent(m_hMsi,szTableName);
}
// RecordT
UINT PrimaryKeys(LPCTSTR szTableName, MSIHANDLE& hRecord)
{
return MsiDatabaseGetPrimaryKeys(m_hMsi,szTableName,&hRecord);
}
// View
UINT OpenView(LPCTSTR szQuery, MSIHANDLE* pView)
{
return MsiDatabaseOpenView(m_hMsi,szQuery, pView);
}
// View
UINT OpenExecuteView(LPCTSTR szQuery, MSIHANDLE* pView)
{
UINT nCaller = OpenView(szQuery, pView);
CHK_EXP_RET(nCaller != ERROR_SUCCESS , nCaller);
return MsiViewExecute(*pView, NULL);
}
};
//==============================================================================================
//
template<bool tManage = true> class RecordT : public HandlerT<tManage>
{
public:
RecordT(UINT cParams = 3)
{
m_hMsi = MsiCreateRecord(cParams);
}
public:
UINT GetFieldCount()
{
return MsiRecordGetFieldCount(m_hMsi);
}
BOOL IsNull(UINT iField)
{
return MsiRecordIsNull(m_hMsi,iField);
}
UINT SetNumber(UINT iField, INT iValue)
{
return MsiRecordSetInteger(m_hMsi,iField,iValue);
}
INT GetNumber(UINT iField)
{
return MsiRecordGetInteger(m_hMsi,iField);
}
BOOL GetString(UINT iField, CString& strValue)
{
DWORD dwValue = 4096;
LPTSTR szValue = strValue.GetBuffer(dwValue);
UINT nCaller = MsiRecordGetString(m_hMsi,iField,szValue,&dwValue);
if(nCaller == ERROR_MORE_DATA)
{
dwValue += 2;
strValue.ReleaseBuffer();
szValue = strValue.GetBuffer(dwValue);
nCaller = MsiRecordGetString(m_hMsi,iField,szValue,&dwValue);
}
strValue.ReleaseBuffer();
return nCaller == ERROR_SUCCESS;
}
UINT GetString(UINT iField, LPTSTR szValue, LPDWORD dwValue)
{
return MsiRecordGetString(m_hMsi,iField,szValue,dwValue);
}
UINT SetString(UINT iField, LPCTSTR szValue)
{
return MsiRecordSetString(m_hMsi,iField,szValue);
}
UINT GetStream(UINT iField, LPSTR szBuffer,LPDWORD dwBuffer)
{
return MsiRecordReadStream(m_hMsi,iField,szBuffer,dwBuffer);
}
UINT SetStream(UINT iField, LPCTSTR szFilePath)
{
return MsiRecordSetStream(m_hMsi,iField,szFilePath);
}
};
//==============================================================================================
//
template<bool tManage = true> class ViewT : public HandlerT<tManage>
{
public:
ViewT(MSIHANDLE hView = NULL)
{
m_hMsi = hView;
}
ViewT(MSIHANDLE hDatabase, LPCTSTR szQuery)
{
Open(hDatabase, szQuery);
}
public:
UINT Open(MSIHANDLE hDatabase, LPCTSTR szQuery)
{
return MsiDatabaseOpenView(hDatabase,szQuery,&m_hMsi);
}
BOOL Execute(MSIHANDLE hRecord = NULL)
{
return MsiViewExecute(m_hMsi, hRecord);
}
UINT Fetch(MSIHANDLE& hRecord)
{
return MsiViewFetch(m_hMsi, &hRecord);
}
UINT Modify(MSIMODIFY eModify, MSIHANDLE hRecord)
{
return MsiViewModify(m_hMsi,eModify,hRecord);
}
UINT GetColumnInfo(MSICOLINFO eColumnInfo,MSIHANDLE& hRecord)
{
return MsiViewGetColumnInfo(m_hMsi,eColumnInfo,&hRecord);
}
// 执行了 Execute 之后,那么在下一次执行 Execute 之前必须调用该函数
UINT Release()
{
return MsiViewClose(m_hMsi);
}
};
template<bool tManage = true> class SummaryInformationT : public HandlerT<tManage>
{
public:
SummaryInformationT(MSIHANDLE hDatabase, LPCTSTR szDatabase = NULL, UINT uiUpdateCount = 20)
{
Open(hDatabase,szDatabase, uiUpdateCount);
}
public:
UINT GetPropertyCount(PUINT puiPropertyCount)
{
return MsiSummaryInfoGetPropertyCount(m_hMsi, puiPropertyCount);
}
UINT GetProperty(UINT uProperty,PUINT puiDataType,PINT piValue,PFILETIME pFileTime,LPTSTR szValue,LPDWORD dwValue)
{
return MsiSummaryInfoGetProperty(m_hMsi,uProperty,puiDataType,piValue,pFileTime,szValue,dwValue);
}
UINT SetProperty(UINT uProperty,UINT uDataType,INT iValue,PFILETIME pFileTime,LPTSTR szValue)
{
return MsiSummaryInfoSetProperty(m_hMsi,uProperty,uDataType,iValue,pFileTime,szValue);
}
UINT Open(MSIHANDLE hDatabase, LPCTSTR szDatabase = NULL, UINT uiUpdateCount = 20)
{
return MsiGetSummaryInformation(hDatabase,szDatabase, uiUpdateCount, &m_hMsi);
}
UINT Commit()
{
return MsiSummaryInfoPersist(m_hMsi);
}
public:
UINT GetPropertyInteger(UINT uProperty,PINT piValue)
{
UINT uiDataType = VT_I4;
return GetProperty(uProperty,&uiDataType,piValue,NULL,NULL,0);
}
UINT GetPropertyFiletime(UINT uProperty,PFILETIME pFileTime)
{
UINT uiDataType = VT_FILETIME;
return GetProperty(uProperty,&uiDataType,NULL,pFileTime,NULL,0);
}
UINT GetPropertyString(UINT uProperty,LPTSTR szValue,LPDWORD dwValue)
{
UINT uiDataType = VT_LPSTR;
return GetProperty(uProperty,&uiDataType,NULL,NULL,szValue,dwValue);
}
UINT SetPropertyInteger(UINT uProperty,INT iValue)
{
return SetProperty(uProperty,VT_I4,iValue,NULL,NULL);
}
UINT SetPropertyString(UINT uProperty,LPWSTR szValue)
{
return SetProperty(uProperty, VT_LPSTR, 0, NULL, szValue);
}
UINT SetPropertyFiletime(UINT uProperty,PFILETIME pFileTime)
{
return SetProperty(uProperty,VT_FILETIME,0,pFileTime,NULL);
}
public:
VOID Close(BOOL bCommit = FALSE)
{
if(bCommit) Commit();
HandlerT::Close();
}
};
//==============================================================================================
// 安装控制函数
template<typename T = CWindow > struct InstallerT
{
public:
InstallerT()
{
MemberInitialize();
m_nProgressBar = 0;
m_nProcessInfo = 0;
}
~InstallerT()
{
CancelInstaller();
}
public:
VOID CancelInstaller()
{
m_bCancelInstall = FALSE;
}
VOID SetExternalUI(DWORD dwMsgFilter)
{
MsiSetExternalUI(InstallerCallback, dwMsgFilter, (LPVOID)this);
}
UINT SetInternalUI(INSTALLUILEVEL nInstallerLevel, HWND* pWindow = NULL)
{
return MsiSetInternalUI(nInstallerLevel, pWindow);
}
// 在命令行中可以设置动作和属性,例如下面将产品安装到 D:\LiveSystem
// TEXT("ACTION=INSTALL TARGETDIR=\"D:\\LiveSystem\"")
UINT StartInstaller(LPCTSTR lpszPackage, LPCTSTR szCommandLine = NULL)
{
return MsiInstallProduct(lpszPackage, szCommandLine);
}
UINT SetControlID(UINT nProgressBar, UINT nProcessInfo = 0)
{
m_nProgressBar = nProgressBar;
m_nProcessInfo = nProcessInfo;
return ERROR_SUCCESS;
}
UINT SetScriptStringID(UINT nScriptStrID = 0)
{
m_nScriptStrID = nScriptStrID;
return ERROR_SUCCESS;
}
public:
// 采用默认参数安装指定的包
UINT InstallPackage(LPCTSTR lpszPackage, LPCTSTR pszCommand = NULL)
{
// 准备参数变量
DWORD dwUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_SOURCERESONLY;
DWORD dwMessageFilter = INSTALLLOGMODE_USER | INSTALLLOGMODE_INFO
| INSTALLLOGMODE_ACTIONDATA | INSTALLLOGMODE_OUTOFDISKSPACE
| INSTALLLOGMODE_COMMONDATA | INSTALLLOGMODE_RESOLVESOURCE
| INSTALLLOGMODE_INITIALIZE | INSTALLLOGMODE_ACTIONSTART
| INSTALLLOGMODE_SHOWDIALOG | INSTALLLOGMODE_TERMINATE
| INSTALLLOGMODE_FILESINUSE | INSTALLLOGMODE_PROGRESS
| INSTALLLOGMODE_EXTRADEBUG | INSTALLLOGMODE_WARNING
| INSTALLLOGMODE_FATALEXIT | INSTALLLOGMODE_ERROR;
MemberInitialize();
SetExternalUI(dwMessageFilter);
SetInternalUI((INSTALLUILEVEL)dwUILevel);
return StartInstaller(lpszPackage, pszCommand);
}
public:
// 下面的函数是消息回调
enum InstallerProgress
{
PROGRESS_RESET = 0,
PROGRESS_ACTIONINFO = 1,
PROGRESS_REPORT = 2,
PROGRESS_ADDITION = 3,
};
enum InstallerCommonData
{
COMMON_DATA_LANGUAGE = 0,
COMMON_DATA_CAPTION = 1,
COMMON_DATA_CANCELSHOW = 2,
};
// premature termination, possibly fatal OOM
INT OnInstallerFatalExit(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
return pT->MessageBox(lpszMessage,m_strTitle, uFlags);
}
// formatted error message
INT OnInstallerError(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
return pT->MessageBox(lpszMessage, m_strTitle, uFlags);
}
// formatted warning message
INT OnInstallerWarning(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
return pT->MessageBox(lpszMessage, m_strTitle ,uFlags);
}
// user request message
INT OnInstallerUser(UINT uFlags, LPCTSTR lpszMessage)
{
// 需要分析 uFlags 来确定返回 IDOK 或者 IDYES 或者其他值
return (uFlags & 0x0F) == MB_YESNO ? IDYES : IDOK;
}
// informative message for log
INT OnInstallerInfo(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
// list of files in use that need to be replaced
INT OnInstallerFilesInuse(UINT uFlags, LPCTSTR lpszMessage)
{
// 显示文件被使用的对话框
return ERROR_SUCCESS;
}
// request to determine a valid source location
INT OnInstallerResolveSource(UINT uFlags, LPCTSTR lpszMessage)
{
return ERROR_SUCCESS;
}
// insufficient disk space message
INT OnInstallerOutOfDiskSpace(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
// start of action: action name & description
INT OnInstallerActionStart(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
// formatted data associated with individual action item
INT OnInstallerActionData(UINT uFlags, LPCTSTR lpszMessage)
{
// 如果没有初始化进度条,直接返回
CHK_EXP_RET( m_nProgressTotal == 0 , IDOK );
T* pT = static_cast<T*>(this);
pT->SetDlgItemText(m_nProcessInfo, lpszMessage);
if( m_bEnableActionData && m_nProgressBar )
{
HWND hProgressBar = pT->GetDlgItem(m_nProgressBar);
SendMessage(hProgressBar, PBM_STEPIT, 0, 0);
}
return IDOK;
}
// progress gauge info: units so far, total
INT OnInstallerProgress(UINT uFlags, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
HWND hProgressBar = pT->GetDlgItem(m_nProgressBar);
LONG nData[4] = { 0 , 0 , 0 , 0 };
if( ParseProgressString(lpszMessage, nData) )
{
if(nData[0] == PROGRESS_RESET)
{
m_nProgressTotal = nData[1];
m_bProgressForward = (nData[2] == 0);
m_nProgress = m_bProgressForward ? 0 : m_nProgressTotal ;
// 设置进度条的工作长度
SendMessage(hProgressBar, PBM_SETRANGE32, 0, m_nProgressTotal);
// 如果在处理脚本结束进度条,否则重置
WPARAM nCurrentPos = m_bProgressScript ? m_nProgressTotal : m_nProgress;
SendMessage(hProgressBar, PBM_SETPOS, nCurrentPos , 0);
m_nPosition = 0;
// 检测新的状态
m_bProgressScript = (nData[3] == 1);
if( m_bProgressScript && m_nProcessInfo && m_nScriptStrID )
{
TCHAR szScriptString[MAX_PATH] = { TEXT("") };
AtlLoadString(m_nScriptStrID, szScriptString, MAX_PATH);
pT->SetDlgItemText(m_nProcessInfo, szScriptString);
}
}
else if(nData[0] == PROGRESS_ACTIONINFO )
{
if( nData[2] && m_nProgressBar )
{
WPARAM nStep = m_bProgressForward ? nData[1] : -1 * nData[1] ;
SendMessage(hProgressBar, PBM_SETSTEP, nStep, 0);
}
m_bEnableActionData = nData[2] ? TRUE : FALSE ;
}
else if(nData[0] == PROGRESS_REPORT)
{
if( m_nProgressTotal != 0 )
{
m_nPosition += nData[1];
WPARAM nCurrentPos = m_bProgressForward ? m_nPosition : -1 * m_nPosition;
SendMessage(hProgressBar, PBM_SETPOS, nCurrentPos, 0);
}
}
else if(nData[0] == PROGRESS_ADDITION)
{
}
}
return m_bCancelInstall ? IDCANCEL : IDOK ;
}
// product info for dialog: language Id, dialog caption
INT OnInstallerCommonData(UINT uFlags, LPCTSTR lpszMessage)
{
ParseCommonDataString(lpszMessage);
return IDOK;
}
// sent prior to UI initialization, no string data
INT OnInstallerInitialize(UINT uFlags, LPCTSTR lpszMessage /* = NULL */)
{
return IDOK;
}
// sent after UI termination, no string data
INT OnInstallerTerminate(UINT uFlags, LPCTSTR lpszMessage /* = NULL */)
{
return IDOK;
}
// sent prior to display or authored dialog or wizard
INT OnInstallerShowDialog(UINT uFlags, LPCTSTR lpszMessage)
{
return IDOK;
}
public:
INT OnInstallerPrepare(UINT uiMessageType, LPCTSTR lpszMessage)
{
if( m_bFirstTimeCall )
{
MsiSetInternalUI(INSTALLUILEVEL_BASIC, NULL);
m_bFirstTimeCall = FALSE;
}
return ERROR_SUCCESS ;
}
public:
// 消息回调的处理函数
INT InstallerHandler(UINT uiMessageType, LPCTSTR lpszMessage)
{
T* pT = static_cast<T*>(this);
// 首先允许进行特殊的消息处理
INT nPrepare = pT->OnInstallerPrepare(uiMessageType, lpszMessage);
CHK_EXP_RET( FAILED( nPrepare ) , nPrepare );
// 根据消息类型进行分类处理
UINT uFlags = (uiMessageType & 0x00FFFFFF);
switch( (INSTALLMESSAGE)(0xFF000000 & uiMessageType) )
{
case INSTALLMESSAGE_FATALEXIT : return pT->OnInstallerFatalExit (uFlags, lpszMessage);
case INSTALLMESSAGE_ERROR : return pT->OnInstallerError (uFlags, lpszMessage);
case INSTALLMESSAGE_WARNING : return pT->OnInstallerWarning (uFlags, lpszMessage);
case INSTALLMESSAGE_USER : return pT->OnInstallerUser (uFlags, lpszMessage);
case INSTALLMESSAGE_INFO : return pT->OnInstallerInfo (uFlags, lpszMessage);
case INSTALLMESSAGE_FILESINUSE : return pT->OnInstallerFilesInuse (uFlags, lpszMessage);
case INSTALLMESSAGE_RESOLVESOURCE : return pT->OnInstallerResolveSource (uFlags, lpszMessage);
case INSTALLMESSAGE_OUTOFDISKSPACE : return pT->OnInstallerOutOfDiskSpace(uFlags, lpszMessage);
case INSTALLMESSAGE_ACTIONSTART : return pT->OnInstallerActionStart (uFlags, lpszMessage);
case INSTALLMESSAGE_ACTIONDATA : return pT->OnInstallerActionData (uFlags, lpszMessage);
case INSTALLMESSAGE_PROGRESS : return pT->OnInstallerProgress (uFlags, lpszMessage);
case INSTALLMESSAGE_COMMONDATA : return pT->OnInstallerCommonData (uFlags, lpszMessage);
case INSTALLMESSAGE_INITIALIZE : return pT->OnInstallerInitialize (uFlags, lpszMessage);
case INSTALLMESSAGE_TERMINATE : return pT->OnInstallerTerminate (uFlags, lpszMessage);
case INSTALLMESSAGE_SHOWDIALOG : return pT->OnInstallerShowDialog (uFlags, lpszMessage);
}
return ERROR_SUCCESS;
}
protected:
static INT CALLBACK InstallerCallback(LPVOID lpContext, UINT uiMessageType, LPCTSTR lpszMessage)
{
InstallerT< T >* lpInstaller = reinterpret_cast< InstallerT< T >* >(lpContext);
return static_cast<T*>(lpInstaller)->InstallerHandler(uiMessageType,lpszMessage);
}
protected:
void MemberInitialize()
{
m_bShowCancel = TRUE;
m_wLanguage = LANG_NEUTRAL;
m_wCodePage = GetACP();
m_bProgressForward = TRUE;
m_bProgressScript = FALSE;
m_nProgressTotal = 0;
m_nProgress = 0;
m_nPosition = 0;
m_bEnableActionData = FALSE;
m_bCancelInstall = FALSE;
m_bFirstTimeCall = TRUE;
}
protected:
BOOL ParseCommonDataString(LPCTSTR lpString)
{
CHK_EXP_RET(lpString == NULL , FALSE );
CHK_EXP_RET(lpString[0] == 0 , FALSE );
// 分析字符串类型
lpString = StrStr(lpString, TEXT("1:"));
CHK_EXP_RET(lpString == NULL , FALSE);
LONG nCommon = lpString[3] - TEXT('0');
LPCTSTR lpStr2 = StrStr(lpString, TEXT("2:"));
LPCTSTR lpStr3 = StrStr(lpString, TEXT("3:"));
CHK_EXP_RET(lpStr2 == NULL , FALSE);
if( nCommon == COMMON_DATA_CAPTION )
{
LPTSTR lpTail = (LPTSTR)lpStr3;
if(lpTail != NULL){ *lpTail = 0; }
m_strTitle = lpString + 2; //Skip "2:"
}
else if( nCommon == COMMON_DATA_LANGUAGE )
{
m_wLanguage = StrToLong(lpStr2 + 3);
CHK_EXP_RET(lpStr3 == NULL , FALSE);
m_wCodePage = StrToLong(lpStr3 + 3);
}
else if( nCommon == COMMON_DATA_CANCELSHOW )
{
LONG nShow = StrToLong(lpStr2 + 3);
m_bShowCancel = nShow ? TRUE : FALSE;
}
return TRUE;
}
BOOL ParseProgressString(LPCTSTR lpString, LONG nData[4])
{
ZeroMemory( nData, sizeof(LONG) * 4 );
CHK_EXP_RET(lpString == NULL , FALSE );
CHK_EXP_RET(lpString[0] == 0 , FALSE );
// 分析字符串类型
lpString = StrStr(lpString, TEXT("1:"));
CHK_EXP_RET(lpString == NULL , FALSE);
nData[0] = lpString[3] - TEXT('0');
// 第一个参数
lpString = StrStr(lpString, TEXT("2:"));
CHK_EXP_RET(lpString == NULL , TRUE);
nData[1] = StrToLong(lpString + 3);
// 第二个参数
lpString = StrStr(lpString, TEXT("3:"));
CHK_EXP_RET(lpString == NULL , TRUE);
nData[2] = StrToLong(lpString + 3);
// 第三个参数
lpString = StrStr(lpString, TEXT("4:"));
CHK_EXP_RET(lpString == NULL , TRUE);
nData[3] = StrToLong(lpString + 3);
return TRUE;
}
protected:
// 下面是 CommonData 分析出的数据
CString m_strTitle; // 一般是安装的产品标题
BOOL m_bShowCancel; // 是否要显示“取消”按钮
WORD m_wLanguage; // 当前的语言
WORD m_wCodePage; // 当前的代码页
// 下面是 ProgressInfo 分析出的数据
BOOL m_bProgressForward; // TRUE 表示当前处理进度条是增加的方式
BOOL m_bProgressScript; // TRUE 表示在处理脚本,可以显示“请稍后”
LONG m_nProgressTotal; // 进度条总数
LONG m_nProgress; // 进度数
LONG m_nPosition; // 当前位置
// 其他控制数据
BOOL m_bEnableActionData; // TRUE 表示 INSTALLOGMODE_ACTIONDATA 消息发送进度信息
BOOL m_bCancelInstall; // 如果要取消安装将该值设置为 TRUE
BOOL m_bFirstTimeCall; // 是否第一次调用外部界面接口
// 窗口显示信息控件的ID
UINT m_nProgressBar; // 进度条控件
UINT m_nProcessInfo; // 显示处理信息的控件
UINT m_nScriptStrID; // 处理脚本,请稍后
};
//==============================================================================================
// 全局的静态函数
inline UINT GetFileHash(LPCTSTR lpszFileName, DWORD dwOptions, MSIFILEHASHINFO& rMsiFileHashInfo)
{
rMsiFileHashInfo.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
return MsiGetFileHash(lpszFileName, dwOptions, &rMsiFileHashInfo);
}
inline UINT GetLastError(CString& strErrorDescription)
{
strErrorDescription = TEXT("");
MSIHANDLE hLastErrorRecord = MsiGetLastErrorRecord();
CHK_EXP_RET(hLastErrorRecord == NULL , NO_ERROR);
DWORD dwErrorLength = 4096;
HandlerT<> hRecord(hLastErrorRecord);
UINT nCaller = MsiFormatRecord(NULL,hRecord,NULL,&dwErrorLength);
if(nCaller == ERROR_MORE_DATA)
{
dwErrorLength ++;
LPTSTR lpszError = new TCHAR[dwErrorLength];
nCaller = MsiFormatRecord(NULL,hRecord,lpszError,&dwErrorLength);
if(nCaller == ERROR_SUCCESS) strErrorDescription = lpszError;
delete [] lpszError;
}
return nCaller;
}
inline UINT GetFileVersion(LPCTSTR szFilePath, CString& strVersion, CString& strLanguage)
{
DWORD dwVersion = 32, dwLang = 32;
LPTSTR lpszLang = strLanguage.GetBuffer(dwLang);
LPTSTR lpszVersion = strVersion.GetBuffer(dwVersion);
UINT nCaller = MsiGetFileVersion(szFilePath,lpszVersion, &dwVersion, lpszLang, &dwLang);
if(nCaller == ERROR_MORE_DATA)
{
// 增加内存空间
dwLang += 2; dwVersion += 2;
strVersion.ReleaseBuffer(); lpszVersion = strVersion.GetBuffer(dwVersion);
strLanguage.ReleaseBuffer(); lpszLang = strLanguage.GetBuffer(dwLang);
nCaller = MsiGetFileVersion(szFilePath,lpszVersion, &dwVersion, lpszLang, &dwLang);
}
else if(nCaller == ERROR_FILE_INVALID)
{
// 文件没有版本信息
nCaller = NO_ERROR;
}
strLanguage.ReleaseBuffer();
strVersion.ReleaseBuffer();
return nCaller;
}
}