科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道windows mobile 下用资源DLL实现多语言支持的方法

windows mobile 下用资源DLL实现多语言支持的方法

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

用资源DLL实现软件的国际化,是MS推荐的方式,也是比较好管理的方式,这种方式可以动态的添加和删除附属语言DLL而不用更改主程序的代码,效率高,管理方便。

作者:HelloHalo 来源:CSDN 2007年11月26日

关键字: 语言 Dll

  • 评论
  • 分享微博
  • 分享邮件

这几天一直在研究这个东东,总算小有所成,中间走了不少弯路,摔了不少跟头,也学到了不少知识。

之所以把这点东西写出来,是为了让和我曾经一样迷茫的兄弟们找到一点方向,不要像我一样浪费那么多时间在一些无谓的事情上。

        实现软件国际化目前有两种方式:

       第一种是像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领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章