在本系列的上一篇中,
我们给出了这个工具的具体的思路。
得到了很多朋友的反馈!
综合朋友的意见,
在没有改变工具原理的基础上
我对这个程序做了升级
如下图:
如你所见,
现在这个打包工具可以打包dotNet2.0 3.5 4
乃至所有在注册表中添加过注册表项的应用程序
下面我们就开始分析安装工具(也就是上面你看到的那个图片)
--------------------------
入口函数:
代码语言:javascript复制int WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR lpCmdLine,int nCmdShow)
{
DialogBox(hInstance,MAKEINTRESOURCE(MainWinDL),0,DlgProc);
return 0;
}
好吧,入口函数很简单,只是创建了一个窗体,并注册了窗口过程函数
-----------------------------
窗口过程
代码语言:javascript复制//窗口过程
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的单选按钮
代码语言:javascript复制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;
}
---------------------------------------------------------
四个单选按钮的单击事件
设置了文本框的内容,
并记录了当前选中的是哪个单选按钮
代码语言:javascript复制void CheckRadio(HWND hwnd,int rdioID,LPCSTR val)
{
if(rdioID == LastCheckRdioId)
{
return;
}
::SetDlgItemText(hwnd,IDC_EDIT2,val);
LastCheckRdioId = rdioID;
}
------------------------------------------------------------
选择文件
把选中的文件路径赋值给相应的文本框
代码语言:javascript复制//得到文件
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) *.exe ";
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);
}
}
--------------------------------------------------------------------
从资源中读取宿主程序,并按指定的文件名,释放到当前目录下
代码语言:javascript复制//释放资源
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就不多解释了~~
代码语言:javascript复制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
代码语言:javascript复制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
代码语言:javascript复制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;
}
-------------------------------------------------------------
其他的一些代码如下
代码语言:javascript复制#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了!多轻便啊!)