关于mfc:MFC基于对话框使用dll进行多语言切换
title: MFC基于对话框应用dll进行多语言切换 categories:[MFC] tags:[音视频编程] date: 2021/12/15 作者:hackett 微信公众号:加班猿 一、前言Qt应用qm文件切换有两种加载形式,比拟容易一些 在资源文件中加载(这个比拟好):长处: 在程序公布时不必把最新的.qm文件拷贝到加载门路中,升高了批改翻译时界面没有翻译或翻译不精确的危险;不会把翻译的信息裸露给用户;毛病: 资源文件会编译时可执行程序,所以会导致可执行程序的体积变大(然而.qm文件个别很小,这个毛病能够忽略不计);当只批改翻译不批改源码时也必须从新编译可执行程序。在程序内部加载: 长处: 当只批改翻译不批改源码时不必从新编译可执行程序,只有替换批改后的.qm文件;不会影响可执行程序的大小;毛病: 程序公布时,要把最新.qm文件中复制到相应加载门路下(在理论开发中常常会忘了这个步骤),导致没有翻译或翻译不精确;把翻译信息裸露给用户,可能会被用户批改(不可抗因素,能够疏忽)。有个需要MFC界面须要做一个多语言切换,百度了一番,MFC实现多国语言有不少办法: VS提供对话框的“插入正本”办法;将要显示的界面文字做一个相似ini文件,加载界面的时候各控件用SetWindowText来显示不同的语言内容;制作Dll资源文件,不同语言调用不同的Dll;综合下来dll形式比拟适宜本人的需要,就应用这个来做 二、筹备工作visual studio中新建一个MFC我的项目 【文件】——【新建】——【我的项目】,抉择“MFC 应用程序”,项目名称:test,放在workspace文件夹里 抉择“基于对话框”,勾销“应用 Unicode 库” 【实现】实现对基于对话框MFC我的项目的创立。 界面轻易来两个按钮跟text、Edit Control 右键【增加资源】——【Menu】——【新建】 ,建一个menu来进行语言的抉择,增加语言切换选项 关上test.rc的属性,找到【杂项】的Menu中选中刚刚的IDR_MENU1,做好这些就能够筹备dll的制作了 三、制作dll创立英文版的dllvisual studio中 解决方案右键 【增加】——【新建我的项目】 抉择MFC DLL,输出我的项目名English,确定 MFC DLL向导不做批改,采纳默认的共享MFC DLL规定设置,【实现】。 删除DLL中不须要的文件 移除工程的“资源文件”下的English.rc,English.rc2文件,“头文件”下的Resource.h文件,并到对应目录将其删除 导入相干文件: 将workspace\test下的“test.rc”、“resource.h”复制到英文DLL我的项目的文件夹test\English下。 而后将test的workspace\test\res文件夹中的文件全副复制到英文DLL我的项目对应的文件夹下。 再将上述文件增加到 English 我的项目中。 右击English我的项目,【增加】——【现有项】,如图所示 批改English我的项目的相干资源属性为英语,并将对应的button和text翻译成英文 批改English我的项目属性。【配置属性】——【链接器】——【高级】,“无入口点”改为“是(/NOENTRY)”。 接下来就能够右键【生成】生成English.dll 按English我的项目的办法可创立中文、日语、其余语言版本。 文本框中的显示内容也能够采纳从配置文件读取,形式跟读取语言配置的一样操作 test.h// test.h : PROJECT_NAME 应用程序的主头文件//#pragma once#ifndef __AFXWIN_H__ #error "在蕴含此文件之前蕴含“stdafx.h”以生成 PCH 文件"#endif#include "resource.h" // 主符号// CtestApp:// 无关此类的实现,请参阅 test.cpp//class CtestApp : public CWinApp{public: CtestApp();// 重写public: virtual BOOL InitInstance(); // 新增加代码private: CString pExePath; char m_strLanguage[MAX_PATH]; HINSTANCE m_hLangDLL;// 实现 DECLARE_MESSAGE_MAP()};extern CtestApp theApp;test.cpp// test.cpp : 定义应用程序的类行为。//#include "stdafx.h"#include "test.h"#include "testDlg.h"#ifdef _DEBUG#define new DEBUG_NEW#endif// CtestAppBEGIN_MESSAGE_MAP(CtestApp, CWinApp) ON_COMMAND(ID_HELP, &CWinApp::OnHelp)END_MESSAGE_MAP()// CtestApp 结构CtestApp::CtestApp(){ // 反对重新启动管理器 m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART; // TODO: 在此处增加结构代码, // 将所有重要的初始化搁置在 InitInstance 中 // 新增代码 // 初始化变量 memset(m_strLanguage,0,sizeof(m_strLanguage)); m_hLangDLL=NULL;}// 惟一的一个 CtestApp 对象CtestApp theApp;BOOL CtestApp::InitInstance(){ // 如果一个运行在 Windows XP 上的利用程序清单指定要 // 应用 ComCtl32.dll 版本 6 或更高版本来启用可视化形式, //则须要 InitCommonControlsEx()。否则,将无奈创立窗口。 INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // 将它设置为包含所有要在应用程序中应用的 // 公共控件类。 InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); AfxEnableControlContainer(); // 创立 shell 管理器,以防对话框蕴含 // 任何 shell 树视图控件或 shell 列表视图控件。 CShellManager *pShellManager = new CShellManager; // 规范初始化 // 如果未应用这些性能并心愿减小 // 最终可执行文件的大小,则应移除下列 // 不须要的特定初始化例程 // 更改用于存储设置的注册表项 // TODO: 应适当批改该字符串, // 例如批改为公司或组织名 SetRegistryKey(_T("应用程序向导生成的本地应用程序")); // ***********新增代码。语言选择 ********** // 读取 INI 文件中的设置 pExePath = CtestDlg::GetExePath(); pExePath += _T("\\ip.ini"); ::GetPrivateProfileString(_T("Language"), _T("currentlanguage"), _T(""),m_strLanguage, MAX_PATH, pExePath); // 加载对应的 DLL 文件 switch (atoi(m_strLanguage)) { case LANG_CHINESE: m_hLangDLL=::LoadLibrary("Chinese.dll"); break; case LANG_ENGLISH: m_hLangDLL=::LoadLibrary("English.dll"); break; default: m_hLangDLL=::LoadLibrary("Chinese.dll"); break; } if (m_hLangDLL!=NULL) { AfxSetResourceHandle(m_hLangDLL); } else { AfxMessageBox("语言DLL文件加载失败!"); exit(1); // 非正常终止程序 } // *************************************** CtestDlg dlg; m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { // TODO: 在此搁置解决何时用 // “确定”来敞开对话框的代码 } else if (nResponse == IDCANCEL) { // TODO: 在此搁置解决何时用 // “勾销”来敞开对话框的代码 } else if (nResponse == -1) { TRACE(traceAppMsg, 0, "正告: 对话框创立失败,应用程序将意外终止,如果您在对话框上应用 MFC 控件,则无奈 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n"); } // 删除下面创立的 shell 管理器。 if (pShellManager != NULL) { delete pShellManager; } // 因为对话框已敞开,所以将返回 FALSE 以便退出应用程序, // 而不是启动应用程序的音讯泵。 return FALSE;}testDlg.cpp// testDlg.cpp : 实现文件//#include "stdafx.h"#include "test.h"#include "testDlg.h"#include "afxdialogex.h"#ifdef _DEBUG#define new DEBUG_NEW#endif// 用于应用程序“对于”菜单项的 CAboutDlg 对话框class CAboutDlg : public CDialogEx{public: CAboutDlg();// 对话框数据 enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 反对// 实现protected: DECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD){}void CAboutDlg::DoDataExchange(CDataExchange* pDX){ CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)END_MESSAGE_MAP() // CtestDlg 对话框CtestDlg::CtestDlg(CWnd* pParent /*=NULL*/) : CDialogEx(CtestDlg::IDD, pParent){ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); // 初始化变量 m_hLangDLL=NULL;}void CtestDlg::DoDataExchange(CDataExchange* pDX){ CDialogEx::DoDataExchange(pDX);}BEGIN_MESSAGE_MAP(CtestDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BUTTON1, &CtestDlg::OnBnClickedButton1) ON_COMMAND(ID_MENU_CHINESE, &CtestDlg::OnChinese) ON_COMMAND(ID_MENU_ENGLISH, &CtestDlg::OnEnglish)END_MESSAGE_MAP()// CtestDlg 音讯处理程序BOOL CtestDlg::OnInitDialog(){ CDialogEx::OnInitDialog(); // 将“对于...”菜单项增加到零碎菜单中。 // IDM_ABOUTBOX 必须在系统命令范畴内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将主动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此增加额定的初始化代码 m_menu.LoadMenu(IDR_MENU1); // 留神这里须要加载menu SetMenu(&m_menu); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE}void CtestDlg::OnSysCommand(UINT nID, LPARAM lParam){ if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); }}// 如果向对话框增加最小化按钮,则须要上面的代码// 来绘制该图标。对于应用文档/视图模型的 MFC 应用程序,// 这将由框架主动实现。void CtestDlg::OnPaint(){ if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设施上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); }}//当用户拖动最小化窗口时零碎调用此函数获得光标//显示。HCURSOR CtestDlg::OnQueryDragIcon(){ return static_cast<HCURSOR>(m_hIcon);}CString CtestDlg::GetExePath() { char szExePath[MAX_PATH] = {0}; GetModuleFileName(AfxGetApp()->m_hInstance, szExePath, 256); int i = (int)strlen(szExePath) - 1; for (; i >= 0 && szExePath[i] != '\\'; i--) ; szExePath[i + 1] = 0; return szExePath;}void CtestDlg::OnBnClickedButton1(){}BOOL CtestDlg::ResetDialog(){ //TODO: 解决可能已增加的附加资源 AfxOleTerm(FALSE); if(m_hLangDLL) { FreeLibrary(m_hLangDLL); } STARTUPINFO StartupInfo={0}; PROCESS_INFORMATION ProcessInfo; StartupInfo.cb=sizeof(STARTUPINFO); char Path[256]; GetModuleFileName(NULL,(LPSTR)(LPCTSTR)Path,250); CreateProcess(NULL,(LPSTR)(LPCTSTR)Path,NULL,NULL,FALSE,0,NULL,NULL,&StartupInfo,&ProcessInfo); return TRUE;}void CtestDlg::OnChinese(){ // TODO: 在此增加命令解决程序代码 pExePath = GetExePath(); pExePath += _T("\\ip.ini"); int iRet = MessageBox(_T("Make sure to switch to the Chinese version?"),_T("Tips"),MB_YESNO); if (iRet == IDYES) { WritePrivateProfileString(_T("Language"), _T("currentlanguage"), "0", pExePath); m_hLangDLL=::LoadLibrary("Chinese.dll"); if (m_hLangDLL!=NULL) { ResetDialog(); AfxSetResourceHandle(m_hLangDLL); exit(1); } else { AfxMessageBox("语言Chinese.dll文件加载失败!"); exit(1); // 非正常终止程序 } }}void CtestDlg::OnEnglish(){ // TODO: 在此增加命令解决程序代码 pExePath = GetExePath(); pExePath += _T("\\Config.ini"); int iRet = MessageBox(_T("确定切换到英文版本?"),_T("提醒"),MB_YESNO); if (iRet == IDYES) { WritePrivateProfileString(_T("Language"), _T("currentlanguage"), "1", pExePath); m_hLangDLL=::LoadLibrary("English.dll"); if (m_hLangDLL!=NULL) { ResetDialog(); AfxSetResourceHandle(m_hLangDLL); exit(1); } else { AfxMessageBox("语言English.dll文件加载失败!"); exit(1); //非正常终止程序 } }}testDlg.h// testDlg.h : 头文件//#pragma once// 增加宏定义#define LANG_CHINESE 0#define LANG_ENGLISH 1#define LANG_JAPNESE 2// CtestDlg 对话框class CtestDlg : public CDialogEx{// 结构public: CtestDlg(CWnd* pParent = NULL); // 规范构造函数// 对话框数据 enum { IDD = IDD_TEST_DIALOG }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 反对// 实现protected: HICON m_hIcon; // 生成的音讯映射函数 virtual BOOL OnInitDialog(); virtual BOOL ResetDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP()public: afx_msg void OnBnClickedButton1(); afx_msg void OnChinese(); afx_msg void OnEnglish(); static CString GetExePath(); CString pExePath; HINSTANCE m_hLangDLL; CMenu m_menu;};ip.ini[Language]currentlanguage=0四、遇到的坑1、用dll进行多语言切换的时候不要用 MFC Button Control类型的button,用一般的button(不然程序运行会一闪而过) ...