• DotNet4应用程序打包工具>升级版【二】安装工具分析


    在本系列的上一篇中,

    我们给出了这个工具的具体的思路。

    得到了很多朋友的反馈!

    综合朋友的意见,

    在没有改变工具原理的基础上

    我对这个程序做了升级

    如下图:

    image

    如你所见,

    现在这个打包工具可以打包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了!多轻便啊!)

  • 相关阅读:
    MYSQL 神奇的操作insert into test select * from test;
    mysql innodb与myisam存储文件的区别
    centos 普通用户 和 root 相互切换方法
    MySQL
    mysql查看数据库表数量
    PHP是单线程还是多线程?
    PHP如何解决网站大流量与高并发的问题(一)
    PHP如何解决网站大流量与高并发的问题(二)
    Work at home, Work as a distributed team | TVP思享
    区块链上的虚拟开放世界游戏是怎样的?| TVP思享
  • 原文地址:https://www.cnblogs.com/liulun/p/2292878.html
Copyright © 2020-2023  润新知