MFC uses dll for multi language switching based on dialog box

title: MFC uses dll for multi language switching based on dialog box

categories:[MFC]

tags: [audio and video programming]

date: 2021/12/15

Author: hackett

WeChat official account: overtime ape

1, Foreword

Qt uses qm file switching. There are two loading methods, which are easier

Load in the resource file (this is better):
advantage:

  • When the program is released, you don't have to put the latest qm files are copied to the loading path, which reduces the risk of no translation or inaccurate translation on the interface when modifying translation;
  • The translated information will not be exposed to users;

Disadvantages:

  • The resource file will compile the executable program, so it will lead to the larger volume of the executable program (but the. qm file is generally small, and this disadvantage can be ignored);
  • When only the translation is modified and the source code is not modified, the executable must also be recompiled.

Load outside the program:

advantage:

  • When you only modify the translation without modifying the source code, you don't need to recompile the executable, just replace the modified one qm documents;
  • It will not affect the size of the executable program;

Disadvantages:

  • When the program is released, it is necessary to update it qm file is copied to the corresponding loading path (this step is often forgotten in actual development), resulting in no translation or inaccurate translation;
  • If the translation information is exposed to the user, it may be modified by the user (irresistible factors, which can be ignored).

There is a demand that the MFC interface needs to be switched between multiple languages. Baidu has made a lot of efforts. There are many ways to realize multiple languages in MFC:

  • VS provides the "insert copy" method of the dialog box;
  • Make the interface text to be displayed as an ini file. When loading the interface, each control uses SetWindowText to display different language contents;
  • Make Dll resource files and call different DLLs in different languages;

Overall, the dll method is more suitable for your own needs, so use this to do it

2, Preparatory work

Create a new MFC project in visual studio

[file] - [new] - [PROJECT], select "MFC application", project name: test, and put it in the workspace folder

Select dialog based and cancel use Unicode library

[finish] complete the creation of MFC project based on dialog box.

There are two buttons in the interface, text and Edit Control

Right click Add Resource - menu - new to create a menu for language selection and add language switching options

Open test RC attribute, find the Menu in misc, and select IDR_MENU1 just now. After doing these, you can prepare for the production of dll

3, Make dll

Create English dll

In visual studio

Solution: right click [add] - [new project], select MFC DLL, enter the project name in English, and click OK

The MFC DLL wizard does not modify, but adopts the default shared MFC DLL rule settings and click finish.

Delete unwanted files from the DLL

Remove English under "resource file" of the project rc,English. RC2 file, resource under "header file" H file and delete it in the corresponding directory

Import related files:

Copy "test.rc" and "resource.h" under workspace\test to the folder test\English of the English DLL project.

Then copy all the files in the workspace\test\res folder of test to the folder corresponding to the English DLL project.

Then add the above files to the English project.

Right click the English item, and click Add - existing item, as shown in the figure

Modify the relevant resource attributes of the English project to English, and translate the corresponding button and text into English

Modify English project properties. [configuration attribute] - [linker] - [advanced], "no entry point" is changed to "Yes (/ NOENTRY)".

Next, you can right-click generate to generate English.dll

Chinese, Japanese and other language versions can be created according to the method of English project.

The display content in the text box can also be read from the configuration file in the same way as the language configuration

test.h

// test.h : PROJECT_NAME application's main header file
//
#pragma once

#ifndef __AFXWIN_H__
    #error "include" stdafx.h "before including this file to generate PCH file"
#endif
#include "resource.h" / / main symbol
// CtestApp:
// For the implementation of this class, see test cpp
//
class CtestApp : public CWinApp
{
public:
    CtestApp();
// rewrite
public:
    virtual BOOL InitInstance();
    // Add new code
private:
    CString pExePath;
    char m_strLanguage[MAX_PATH];
    HINSTANCE m_hLangDLL;
// realization
    DECLARE_MESSAGE_MAP()
};
extern CtestApp theApp;

test.cpp

// test.cpp: defines the class behavior of the application.
//

#include "stdafx.h"
#include "test.h"
#include "testDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CtestApp

BEGIN_MESSAGE_MAP(CtestApp, CWinApp)
    ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
END_MESSAGE_MAP()


// CtestApp construction

CtestApp::CtestApp()
{
    // Support Restart Manager
    m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;

    // TODO: add construction code here,
    // Place all important initializations in InitInstance

    // New code
    // initialize variable
    memset(m_strLanguage,0,sizeof(m_strLanguage));
    m_hLangDLL=NULL;
}


// The only CtestApp object

CtestApp theApp;

BOOL CtestApp::InitInstance()
{
    // If an application manifest running on Windows XP specifies to
    // Use comctl32 DLL version 6 or later to enable visualization,
    //InitCommonControlsEx() is required. Otherwise, the window cannot be created.
    INITCOMMONCONTROLSEX InitCtrls;
    InitCtrls.dwSize = sizeof(InitCtrls);
    // Set it to include all that you want to use in your application
    // Public control class.
    InitCtrls.dwICC = ICC_WIN95_CLASSES;
    InitCommonControlsEx(&InitCtrls);

    CWinApp::InitInstance();


    AfxEnableControlContainer();

    // Create a shell manager in case the dialog box contains
    // Any shell tree view control or shell list view control.
    CShellManager *pShellManager = new CShellManager;

    // Standard initialization
    // If you do not use these functions and want to reduce
    // The size of the final executable, the following should be removed
    // Specific initialization routines not required
    // Change the registry key used to store settings
    // TODO: the string should be modified appropriately,
    // For example, change to company or organization name
    SetRegistryKey(_T("Local application generated by application wizard"));
    // ***********New code. Language selection**********
    // Read settings from INI file
   
    pExePath = CtestDlg::GetExePath();
    pExePath += _T("\\ip.ini");
    ::GetPrivateProfileString(_T("Language"), _T("currentlanguage"), _T(""),m_strLanguage, MAX_PATH, pExePath);
    // Load the corresponding DLL file
    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("language DLL File loading failed!");
        exit(1);        // Abnormal termination procedure
    }
    // ***************************************
    CtestDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    if (nResponse == IDOK)
    {
        // TODO: when to place processing here
        //  Click OK to close the code of the dialog box
    }
    else if (nResponse == IDCANCEL)
    {
        // TODO: when to place processing here
        //  "Cancel" to close the code of the dialog box
    }
    else if (nResponse == -1)
    {
        TRACE(traceAppMsg, 0, "warning: Dialog creation failed,The application will terminate unexpectedly if you use it on the dialog box MFC Control, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS. \n");
    }

    // Delete the shell manager created above.
    if (pShellManager != NULL)
    {
        delete pShellManager;
    }

    // Since the dialog box is closed, FALSE will be returned to exit the application,
    //  Instead of starting the application's message pump.
    return FALSE;
}

testDlg.cpp

// testDlg.cpp: implementation file
//
#include "stdafx.h"
#include "test.h"
#include "testDlg.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CAboutDlg dialog box for application about menu item
class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// Dialog data
    enum { IDD = IDD_ABOUTBOX };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// realization
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 dialog box
CtestDlg::CtestDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(CtestDlg::IDD, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    // initialize variable
    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 message handler

BOOL CtestDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // "About..." Add menu items to the system menu.

    // IDM_ABOUTBOX must be within the scope of the system command.
    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);
        }
    }

    // Sets the icon for this dialog box. When the main window of the application is not a dialog box, the frame will automatically
    //  Do this
    SetIcon(m_hIcon, TRUE);            // Set large icon
    SetIcon(m_hIcon, FALSE);        // Set small icon

    // TODO: add additional initialization code here

    m_menu.LoadMenu(IDR_MENU1); // Note that you need to load the menu here
    SetMenu(&m_menu);


    return TRUE;  // Returns TRUE unless the focus is set to the control
}

void CtestDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialogEx::OnSysCommand(nID, lParam);
    }
}
// If you add the minimize button to the dialog box, you need the following code
//  To draw the icon. For MFC applications that use the document / view model,
//  This will be done automatically by the framework.

void CtestDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // Device context for drawing

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Centers the icon in the workspace rectangle
        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;

        // draw icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
}

//When the user drags the minimized window, the system calls this function to obtain the cursor
//Display.
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: processing additional resources that may have been added
    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: add command handler code here
    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("language Chinese.dll File loading failed!");
            exit(1);        // Abnormal termination procedure
        }
    }

}
void CtestDlg::OnEnglish()
{
    // TODO: add command handler code here
    pExePath = GetExePath();
    pExePath += _T("\\Config.ini");
    int iRet = MessageBox(_T("OK to switch to English version?"),_T("Tips"),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("language English.dll File loading failed!");
            exit(1);        //Abnormal termination procedure
        }
    }
}

testDlg.h

// testDlg.h: header file
//
#pragma once

// Add macro definition
#define LANG_CHINESE    0
#define LANG_ENGLISH    1
#define LANG_JAPNESE    2

// CtestDlg dialog box
class CtestDlg : public CDialogEx
{
// structure
public:
    CtestDlg(CWnd* pParent = NULL);    // Standard Constructors 

// Dialog data
    enum { IDD = IDD_TEST_DIALOG };

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
// realization
protected:
    HICON m_hIcon;

    // Generated message mapping function
    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

4, Pit encountered

1. When using dll for multi language switching, do not use MFC Button Control type buttons, but ordinary buttons (otherwise the program will flash)

Finally, a rendering:

If you think the article is good, you can give a "three links", and the article is synchronized to the personal WeChat official account.

I'm hackett. I'll see you next time

Keywords: MFC microsoft

Added by mazman on Fri, 17 Dec 2021 18:17:27 +0200