需求说明:
假设有这样一个应用程序(发布器):在运行时会释放出另一个应用程序(客户端),生成的客户端会记住我们的在发布器里的配置信息
发布器如下:
客户端如下:
我可能需要把释放出的这个应用程序分发给多个国家的多个客户,但他们却过连接同一个服务器(我服务器的IP)
所以,我希望通过这个程序去根据我的填入的配置信息,释放出相应的客户端,这个客户端本身(EXE文件)就记录了配置信息
程序是一个类似于发布工具的简单程序,基于MFC的对话框应用程序。需要解决如下几个问题:
1 . 如何去修改一个EXE文件
2 . 如何实现多语言版本
问题1:
如何修改EXE?我的做法是在 客户端里面加入一个资源文件,是一个txt文件,内如如下:
======== Start User Setting Area ======== Language = .........1.........2.........3 Target Address = .........1.........2.........3.........4.........5.........6.........7.........8 Port = ..... User Name = .........1.........2.........3.........4.........5.........6.........7.........8 Password = .........1.........2.........3.........4.........5.........6.........7.........8 Force GDI Plus = ... Not Cache Image = ... Pre-Load All Images = ... Network Checking Interval = .........1...... Network Receive Time-Out = .........1...... Network Send Time-Out = .........1...... ======== End User Setting Area ========
在编译生成客户端的时候,这些信息会写入EXE文件的,如下所示:
这样,我们可以通过找字符串 ======== 来定位到资源文件,并修改资源文件内容,亲测,最后生成的EXE可以正常运行
问题2:
如何实现多国语言版,以下两种方案我都实现了,初步测试没得什么大问题
1 )重启应用程序。当用户点击了 radiobutton时,把用户的选择信息,还有那些textbox内的信息都写入到一个临时文件,并重启,重启时如果发现临时文件存在就读入信息然后删除这个临时文件。
2 )重启对话框。
第一个方案被老板否定了,因为只要涉及到写文件就有可能失败,什么权限不够,如何设置临时文件的路劲等等问题~~
整体上看,资源组织关系如下:
客户端把一个空的UserSetting文件作为资源,这个资源将会以二进制的形式存在于 client.exe 文件中,发布器将整个 client.exe 作为资源,在代码里通过查找资源,把真个client.exe读入内存,在这段内存中,通过 内存比较 我们协定的 起始标志 =======,把内存这段 setting area 区域换成我们配置的即可,然后把改好之后的内存释放到一个exe文件就是生成的客户端,客户端通过查看资源文件,来设置自身的启动信息。
发布器流程如下:
通过创建多个资源(同名),不同的国家罢了,如下:
重启对话框代码如下:
BOOL CrwExeFileApp::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); AfxEnableControlContainer(); // Create the shell manager, in case the dialog contains // any shell tree view or shell list view controls. CShellManager *pShellManager = new CShellManager; // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T("Local AppWizard-Generated Applications")); isRestart=false; CrwExeFileDlg *pDlg; //m_pMainWnd = pDlg; while(1) { if(isRestart) { SetThreadUILanguage(saves.lcidNew);//选择程序语言 } pDlg=new CrwExeFileDlg(); INT_PTR nResponse = pDlg->DoModal(); if (nResponse == IDOK) { // TODO: Place code here to handle when the dialog is // dismissed with OK } else if (nResponse == IDCANCEL) { // TODO: Place code here to handle when the dialog is // dismissed with Cancel if(isRestart) { // } else { delete pDlg; if (pShellManager != NULL) delete pShellManager; return FALSE; } } // Since the dialog has been closed, return FALSE so that we exit the // application, rather than start the application's message pump. delete pDlg; } // Delete the shell manager created above. if (pShellManager != NULL) { delete pShellManager; } }
void CrwExeFileDlg::OnBnClickedRadio1() { // TODO: Add your control notification handler code here saves.lcidNew = MAKELANGID(MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), SORT_DEFAULT); SetInputToSaves(&saves); isRestart=true; EndDialog(1); }
参考了:http://www.codeproject.com/Articles/27205/XBalloonMsg-a-non-MFC-balloon-shaped-message-box
效果如下:
具体实现,提供下载,采用vs2010编译。
点击下载整个工程