扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
这几天一直在研究这个东东,总算小有所成,中间走了不少弯路,摔了不少跟头,也学到了不少知识。
之所以把这点东西写出来,是为了让和我曾经一样迷茫的兄弟们找到一点方向,不要像我一样浪费那么多时间在一些无谓的事情上。
实现软件国际化目前有两种方式:
第一种是像FLASHGET那样将菜单文本等按资源ID写入一个TXT文件,需要时提取文本,刷新窗体,这种方式的弊端就是语言选择菜单内的语言种类是固定的,想要添加/删除一种语言要修改主程序,而且不方便管理,甚至会造成资源无法完全本地的问题。
第二种是用资源DLL实现软件的国际化,是MS推荐的方式,也是比较好管理的方式,这种方式可以动态的添加和删除附属语言DLL而不用更改主程序的代码,效率高,管理方便。
下面我就说说我在windows moblie下是如何实现第2种方法的。
1。建立一个win32应用程序MUL_LAN 作为主程序,并且添加rc档作为默认的菜单
//
// Menu
//
IDR_MENU MENU
BEGIN
POPUP "menu"
BEGIN
MENUITEM "文件", ID_MENU_FILE
MENUITEM "语言", IDM_LANG//选择语言的菜单按键
MENUITEM "关于", IDM_ABOUT
END
END
// Dialog
//
IDD_ABOUTBOX DIALOG 0, 0, 156, 129
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
EXSTYLE 0x80000000L
CAPTION "关于 MUL_LAN"
FONT 9, "MS Shell Dlg"
BEGIN
ICON IDI_MUL_LAN,IDC_STATIC_1,12,12,20,20,SS_REALSIZEIMAGE
LTEXT "MUL_LAN 1.0 版",IDC_STATIC_2,12,36,70,8,SS_NOPREFIX
LTEXT "版权所有 (C) 2006",IDC_STATIC_3,12,48,66,8
END
IDD_ABOUTBOX_WIDE DIALOG 0, 0, 210, 129
STYLE DS_SETFONT | WS_POPUP | WS_CAPTION
EXSTYLE 0x80000000L
CAPTION "关于 MUL_LAN"
FONT 9, "MS Shell Dlg"
BEGIN
ICON IDI_MUL_LAN,IDC_STATIC_1,12,12,21,20,SS_REALSIZEIMAGE
LTEXT "MUL_LAN 1.0 版",IDC_STATIC_2,48,12,66,8,SS_NOPREFIX
LTEXT "版权所有 (C) 2006",IDC_STATIC_3,48,24,66,8
END
IDD_COMBOBOX DIALOG 0, 0, 186, 95
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog"
FONT 8, "MS Shell Dlg"
BEGIN
DEFPUSHBUTTON "确定",IDOK,129,7,50,14
PUSHBUTTON "取消",IDCANCEL,129,24,50,14
LTEXT "请选择语言",IDC_STATIC,7,7,41,8
COMBOBOX IDC_COMBO1,7,22,111,131,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL |
END
//选择语言的DialogBox,上面有一个combobox用来动态列出可以使用的附属语言。
2。建立对应的附属语言DLL项目,添加资源档,其他可以不动,打开属性面板在链接器-〉高级-〉将“无入口点”属性改为“/NOENTRY”,以表明此项目的输出是纯资源DLL。
3。在主程序的MUL_LAN.cpp当中的主窗体消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)当中添加对“IDM_LANG”的处理
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
……
case IDM_LANG:
DialogBox(g_hInst,(LPCTSTR)IDD_COMBOBOX,hWnd,(DLGPROC)Language);
break;
……
}
3。加载被选择语言对应DLL的函数
LRESULT CALLBACK Language(HWND hDlg,UINT message,WPARAM wParam,LPARAM)
{
int index;
LRESULT NewUILanguage;
HWND hwndCombo = GetDlgItem(hDlg,IDC_COMBO1);
HWND hMainWindow = GetParent(hDlg);
HMENU hNewMenu;
HMENU g_hMenu;
DWORD error;
switch (message)
{
case WM_INITDIALOG:
PopulateLanguages(hwndCombo);//寻找DLL目录并填充语言选择下拉cai
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
index = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
if( index != CB_ERR ) {
NewUILanguage = SendMessage(hwndCombo, CB_GETITEMDATA, index, 0);
if( NewUILanguage != CB_ERR && NewUILanguage != UILanguage ) {
UILanguage = (LANGID)NewUILanguage;
hSatDLL=LoadSatelliteDLL(UILanguage);
if( hSatDLL == NULL ) {
hSatDLL = g_hInst;
}
SetLastError(0);
hNewMenu=LoadMenu(hSatDLL,MAKEINTRESOURCE(IDR_MENU));
error=GetLastError();
SHMENUBARINFO mbi;//创建新的菜单栏
memset(&mbi, 0, sizeof(SHMENUBARINFO));
mbi.cbSize = sizeof(SHMENUBARINFO);
mbi.hwndParent = hMainWindow;
mbi.nToolBarId =IDR_MENU ;
mbi.hInstRes =hSatDLL;
if (!SHCreateMenuBar(&mbi))
{
error=GetLastError();
g_hWndMenuBar = NULL;
}
else
{
g_hWndMenuBar = mbi.hwndMB;
}
}
}
case IDCANCEL:
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
// 填充选择对话框中的语言列表
// 根据用十进制 LCID 命名的子目录
int PopulateLanguages(HWND hwndCombo) {
TCHAR CurrentDirectory[MAX_PATH];
TCHAR CurrentFile[MAX_PATH];
int AvailableLangID;
TCHAR AvailableLangName[MAX_LANGNAME];
wchar_t *prt;
int PathLength;
HANDLE hDir;
int i = 0;
int j;
// 检索当前目录名
GetModuleFileName(NULL,CurrentFile,sizeof(CurrentFile)/sizeof(TCHAR));//当前文件
prt=wcsrchr(CurrentFile,TEXT('\\'));
PathLength=prt-CurrentFile;
wcsncpy_s(CurrentDirectory,sizeof(CurrentDirectory)/sizeof(TCHAR),CurrentFile,PathLength);
wcscat_s(CurrentDirectory,sizeof(CurrentDirectory)/sizeof(TCHAR),_T("\\*.*"));
// 循环访问所有目录并填充语言选择的
// 下拉列表
hDir = FindFirstFile(CurrentDirectory, &FindFileData);
if( hDir == INVALID_HANDLE_VALUE ) {
FindClose(hDir);
SendMessage(hwndCombo,CB_ADDSTRING,0,(LPARAM)CurrentDirectory);
return 0;
}
SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);
do {
if( FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
AvailableLangID = _ttoi(FindFileData.cFileName);
if( AvailableLangID ) {
if( GetLocaleInfo(AvailableLangID,LOCALE_SNATIVELANGNAME,AvailableLangName,MAX_LANGNAME)) {
CurrentIndex = SendMessage(hwndCombo, CB_INSERTSTRING , 0, (LPARAM)AvailableLangName);
SendMessage(hwndCombo, CB_SETITEMDATA, (WPARAM)CurrentIndex, (LPARAM)AvailableLangID);
i++;
}
}
}
}while ( FindNextFile(hDir, &FindFileData) );
FindClose(hDir);
// 为了设置当前选择,我们必须循环访问组合框
// 因为添加操作按字母顺序排序组合框
for(j=0 ; j<=i ; j++) {
if( UILanguage == SendMessage(hwndCombo, CB_GETITEMDATA, j, 0) )
SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM)j, 0);
}
return i;
}
//加载选定语言DLL
HMODULE LoadSatelliteDLL(LANGID DesiredLanguage) {
TCHAR CurrentFile[MAX_PATH];
TCHAR CurrentDirectory[MAX_PATH];
TCHAR SatellitePath[MAX_PATH];
TCHAR buffer[100];
HMODULE hDLL;
int PathLength;
wchar_t* prt;
DWORD error;
// WIN32_FIND_DATA FindFileData;
//wchar_t* DLLname;
GetModuleFileName(NULL,CurrentFile,sizeof(CurrentFile)/sizeof(TCHAR));//当前文件
prt=wcsrchr(CurrentFile,TEXT('\\'));
PathLength=prt-CurrentFile+1;
wcsncpy_s(CurrentDirectory,sizeof(CurrentDirectory)/sizeof(TCHAR),CurrentFile,PathLength);
// // 先尝试加载具有完全指定语言的库
wcscpy_s(SatellitePath,sizeof(SatellitePath)/sizeof(TCHAR),CurrentDirectory);
_itot_s(DesiredLanguage,buffer, sizeof(buffer)/sizeof(TCHAR), 10);
wcscat_s(SatellitePath,sizeof(SatellitePath)/sizeof(TCHAR),buffer);
wcscat_s(SatellitePath,sizeof(SatellitePath)/sizeof(TCHAR),_T("\\LANG.dll"));
hDLL=LoadLibrary( SatellitePath);
if( hDLL )
return hDLL;
else { // 尝试主语言 ID
_tcscpy_s(SatellitePath, sizeof(SatellitePath)/sizeof(TCHAR), CurrentDirectory);
DesiredLanguage = PRIMARYLANGID(DesiredLanguage);
_itot_s(DesiredLanguage,buffer,sizeof(buffer)/sizeof(TCHAR),10);
wcscat_s(SatellitePath,sizeof(SatellitePath)/sizeof(TCHAR),buffer);
wcscat_s(SatellitePath,sizeof(SatellitePath)/sizeof(TCHAR),_T("\\1233.dll"));
hDLL = LoadLibraryEx(SatellitePath,NULL,DONT_RESOLVE_DLL_REFERENCES);
if( hDLL )
return hDLL;
else
return NULL;
}
}
就是这么简单,这个程序参考了MSDN的例程,但是关键部分都是自己重写的。
其中遇到的各种问题我也总结一下:
1.WINCE当中没有currentdirectory这个概念,如果想要加载DLL必须要使用绝对路径,问题是怎么找到DLL的绝对路径呢?方法是调用GetModuleFileName()函数,原形可以查MSND得到,该函数的第一个参数如果为NULL,则返回最后一个发出WM_CREAT 消息的文件的绝对路径,根据这个路径就可以找到DLL的路径。
2. WINCE当中使用的是UNICODE,对应的操作其实不难,只要记住:将char声明改称TCHAR,将CHAR* 改成wchar_t*,字符串引用都用TEXT宏包起来,搞定!
对应的还有几个字符串操作的函数,UNICODE的版本都是WCS开头的,比如wcscat_s(是wcscat的安全版本)是unicode版的字符串追加宏,强烈建议使用加_s而不是没有的的版本。原因不多说了,安全嘛呵呵,尤其是在拼路径的时候。
3.这个问题是浪费我时间的第2大罪魁祸首,他就是GetLastError();
此函数相当无聊,在我调用LoadLibrary的时候他不停的抱错,比如说代码6号,invalid module handle,再比如说126号 module handle could not found, 无知的我努力的寻找错误的根源,最终发现,众里寻她千百度。。。
强烈建议各位朋友在LoadLibrary 有返回值得时候别去管GetlastError给出的错误,完全是SHIT~
顺便提一下,我的LoadLibrary返回值unused是负数,如果你也返回了类似的数不要慌,转换成16进制看看~
4.第一大罪魁 模拟器 他加载了主程序的模块,但是不加载DLL模块。所以生成新菜单总是失败,害得我几乎把程序重写一遍都找不到错误。奉劝和我一样的新手们,一定要设备调试。。。。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者