在本系列的上一篇中,
我们给出了这个工具的具体的思路。
得到了很多朋友的反馈!
综合朋友的意见,
在没有改变工具原理的基础上
我对这个程序做了升级
如下图:
如你所见,
现在这个打包工具可以打包dotNet2.0 3.5 4
乃至所有在注册表中添加过注册表项的应用程序
下面我们就开始分析安装工具(也就是上面你看到的那个图片)
--------------------------
入口函数:
int WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(MainWinDL),0,DlgProc); return 0; }
好吧,入口函数很简单,只是创建了一个窗体,并注册了窗口过程函数
-----------------------------
窗口过程
//窗口过程 BOOL CALLBACK DlgProc (HWND hDlg, UINT message,WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG : OnInitDlg(hDlg); return TRUE ; case WM_COMMAND : switch (LOWORD(wParam)) { case IDC_STATIC_Name: ShellExecute(hDlg,"open","http://www.cnblogs.com/liulun",NULL,NULL,SW_SHOWNORMAL); break; case IDC_BUTTON1: GetFile(hDlg,IDC_EDIT1); break; case IDC_BUTTON3: GetFile(hDlg,IDC_EDIT3); break; case IDC_RADIO1: CheckRadio(hDlg,IDC_RADIO1,"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727"); break; case IDC_RADIO2: CheckRadio(hDlg,IDC_RADIO2,"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5"); break; case IDC_RADIO3: CheckRadio(hDlg,IDC_RADIO3,"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4"); break; case IDC_RADIO_ELSE: CheckRadio(hDlg,IDC_RADIO_ELSE,""); break; case IDOK: ReleaseTar(hDlg); ReplaceICO(hDlg); BagTar(hDlg,IDC_EDIT3); BagTar(hDlg,IDC_EDIT1); BagStr(hDlg); Alert("打包成功"); break; case IDCANCEL: EndDialog (hDlg, 0) ; return TRUE ; } break; } return FALSE ; }
在这个过程函数里
接收到的每个消息都执行了一个或几个函数
那么,我们就一个函数一个函数的讲
-------------------------------------------------------
窗口初始化消息里
我们默认选中了dotNet4的单选按钮
void OnInitDlg(HWND hwnd) { HWND cld = ::GetDlgItem(hwnd,IDC_RADIO3); ::SendMessage(cld,BM_SETCHECK,1,0); ::SetDlgItemText(hwnd,IDC_EDIT2,"SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4"); LastCheckRdioId = IDC_RADIO3; }
---------------------------------------------------------
四个单选按钮的单击事件
设置了文本框的内容,
并记录了当前选中的是哪个单选按钮
void CheckRadio(HWND hwnd,int rdioID,LPCSTR val) { if(rdioID == LastCheckRdioId) { return; } ::SetDlgItemText(hwnd,IDC_EDIT2,val); LastCheckRdioId = rdioID; }
------------------------------------------------------------
选择文件
把选中的文件路径赋值给相应的文本框
//得到文件 void GetFile(HWND hwnd,int EDITId) { char szFile[MAX_PATH] = {0}; OPENFILENAME ofn; memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; ofn.lpstrFilter = "应用程序 (.exe)\0*.exe\0\0"; ofn.lpstrDefExt = "exe"; ofn.lpstrTitle = "选择exe文件"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; if(GetOpenFileName(&ofn)) { SetDlgItemText(hwnd,EDITId,szFile); } }
--------------------------------------------------------------------
从资源中读取宿主程序,并按指定的文件名,释放到当前目录下
//释放资源 int ReleaseTar(HWND hwnd) { ::GetDlgItemText(hwnd,IDC_EDIT1,szFilePath,MAX_PATH); ::strcat(szFilePath,".bag.exe"); HMODULE hInstance = ::GetModuleHandle(NULL); HRSRC hResID = ::FindResource(hInstance,(LPCSTR)IDR_BIN1,"bin"); HGLOBAL hRes = ::LoadResource(hInstance,hResID); LPVOID pRes = ::LockResource(hRes); DWORD dwResSize = ::SizeofResource(hInstance,hResID); if(!dwResSize) { return 0; } HANDLE hResFile = CreateFile(szFilePath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); DWORD dwWritten = 0; WriteFile(hResFile,pRes,dwResSize,&dwWritten,NULL); CloseHandle(hResFile); if(dwResSize == dwWritten); { return 1; } return 0; }
---------------------------------------------------------------------------
替换宿主程序的ICO图标资源
这里需要重点说明一下:
要想更新一个应用程序的资源
必须先知道这个资源的ID
GetIcoIndex函数的工作就是获取资源ID的
因为一般的应用程序图标资源都会有两个
所以获取了两个图表资源的ID
其他的WINAPI就不多解释了~~
int GetIcoIndex(HMODULE hExe,int index[]) { HRSRC hRes; int iLoop; int i = 0; for(iLoop = 1;;iLoop++) { hRes = FindResource(hExe, MAKEINTRESOURCE(iLoop), RT_ICON); if (NULL == hRes) { if(iLoop == 60) { break; } continue ; } else { index[i] = iLoop; i +=1; if(i == 2) { break; } } } return 1; } int ReplaceICO(HWND hwnd) { HMODULE hSrcExe,hDestExe; HANDLE hUpdateRes; HRSRC hRes; HRSRC hResLoad; char *lpResLock; int result; char szFile[MAX_PATH+1] = {0}; int hSrcIndex[2] = {0}; int hDestIndex[2] = {0}; ::GetDlgItemText(hwnd,IDC_EDIT1,szFile,MAX_PATH); hSrcExe = LoadLibrary(szFile); hDestExe = LoadLibrary(szFilePath); GetIcoIndex(hSrcExe,hSrcIndex); GetIcoIndex(hDestExe,hDestIndex); for(int i=0;i<2;i++) { hRes = FindResource(hSrcExe, MAKEINTRESOURCE(hSrcIndex[i]), RT_ICON); hResLoad=(HRSRC)LoadResource(hSrcExe,hRes); lpResLock=(char*)LockResource(hResLoad); FreeLibrary(hDestExe); hUpdateRes=BeginUpdateResource(szFilePath,FALSE); result=UpdateResource(hUpdateRes,RT_ICON,MAKEINTRESOURCE(hDestIndex[i]),MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),lpResLock,SizeofResource(hSrcExe,hRes)); EndUpdateResource(hUpdateRes, FALSE); } FreeLibrary(hSrcExe); return result; }
---------------------------------------
为宿主程序增加目标程序资源和dotNet安装包资源
增加的资源也是需要标明ID的
因为宿主程序会根据约定好的ID来得到这些资源
EditId参数就是这些资源的ID
int BagTar(HWND hwnd,int EditId) { HANDLE hFile; DWORD dwFileSize,dwBytesRead; LPBYTE lpBuffer; char szFile[MAX_PATH+1] = {0}; ::GetDlgItemText(hwnd,EditId,szFile,MAX_PATH); hFile = CreateFile(szFile,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); dwFileSize = GetFileSize(hFile, NULL); lpBuffer = new BYTE[dwFileSize]; ReadFile(hFile, lpBuffer, dwFileSize, &dwBytesRead, NULL); HANDLE hResource = BeginUpdateResource(szFilePath, FALSE); UpdateResource(hResource,RT_RCDATA,MAKEINTRESOURCE(EditId),MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPVOID)lpBuffer,dwFileSize); EndUpdateResource(hResource, FALSE); delete [] lpBuffer; CloseHandle(hFile); return 1; }
--------------------------------------------------------
把注册表项的路径也当作资源打包进宿主程序
我们约定这个资源的ID为1039
int BagStr(HWND hwnd) { char szFile[MAX_PATH+1] = {0}; ::GetDlgItemText(hwnd,IDC_EDIT2,szFile,MAX_PATH); HANDLE hUpdateRes=BeginUpdateResource(szFilePath,FALSE); int result=UpdateResource(hUpdateRes,RT_RCDATA,MAKEINTRESOURCE(1039),MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),(LPVOID)szFile,::strlen(szFile)); EndUpdateResource(hUpdateRes, FALSE); return 0; }
-------------------------------------------------------------
其他的一些代码如下
#include <Windows.h> #include <ShlObj.h> #include "resource.h" TCHAR szFilePath[MAX_PATH + 1]; int LastCheckRdioId; //提示 void Alert(LPCSTR msg) { MessageBox(NULL,msg,"系统提示",MB_OK); }
---------------------------------------------------------------
后记:
没有写容错的代码~
也没有遵循命名规范~
大家见谅~
请各位推荐我的文章
因为你们的支持才是我的动力->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->->
此工具编译后的可执行文件下载地址:BagDotNet.zip
(因为不在需要把dotNet4安装程序打包进来,所以只有几十K了!多轻便啊!)