C++ Q&A... 如何获取某个动态链接库的版本信息 原著:Paul DiLascia 翻译:NorthTibet 下载源代码:Apr98CQAcode.exe (22KB) 原文出处:C++ Q&A April 1998
我如何获得安装在我的系统上的某个特定的 DLL 的版本信息?我尝试着确定系统安装了哪个版本的 comctl32.dll。我见过有些代码调用 GetProcAddress 来获取各种函数,如 InitCommonControlsEx,以确定基于不同版本的函数调用。对于我来说,这是一个坎儿,到底用什么方法获得版本号? 有两种方法:容易的和难的。容易的方法是调用一个专门用于此目的的函数 DllGetVersion。问题是虽然 comctl32.dll 支持该函数,但并不是所有的 DLLs 都具备它。如果不具备 DllGetVersion,那么就得用难的方法——使用 FileVersion API,这可能是你要遭遇到的最为暧昧的 API 之一。我写了一个类 CModuleVersion 来封装两种方法,同时还写了一个Demo程序 VersionDlg 来示范 CModuleVersion 的使用方法。程序画面如 Figure 1 所示。你可以在编辑框中敲入任何系统模块的名字,VersionDlg 将用 DllGetVersion (如果具备这个函数的话)和 FileVersion API 两种方法显示版本信息。源代码参见 Figure 2。 Figure 1 运行中的 VersionDlg 程序 让我们先看容易的方法。DllGetVersion 用 DLL 版本信息填写一个 DLLVERSIONINFO 结构。该结构定义在 Win32 SDK 的 showapi.h 头文件中。许多人可能都没有安装 Platform SDK,那么就得自己定义这个结构了(译者注:实际上,早期的 Developer Studio 不包含这个头文件。后来的 Visual Studio 6.0 安装已经包含该头文件,路经参见:Driver:/Program Files/Microsoft Visual Studio/VC98/Include),就像我在 VersionDlg 所做的那样。 typedef struct _DllVersionInfo { DWORD cbSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber; DWORD dwPlatformID; } DLLVERSIONINFO; 这个结构中的字段基本不用怎么说明就知道是什么意思:dwPlatformID 为 DLLVER_PLATFORM_WINDOWS (value = 1)指 Windows 9x,而 DLLVER_PLATFORM_NT (value = 2)用于 Windows NT。一旦定义了 DLLVERSIONINFO 结构,就可以调用 DllGetVersion 了,该函数的署名如下: HRESULT DllGetVersion(DLLVERSIONINFO*); 因为并不是任何给定的 Dll 都输出 DllGetVersion 函数,你得按照标准套路来调用它,即调用 GetProcAddress 并判断返回值是否为 NULL。我编写的类 CModuleVersion 中含有一个 DllGetVersion 函数,它把所有细节都进行了封装(参见 Figure 2 中的 ModulVer.cpp。)CModuleVersion 类的使用方法如下: DLLVERSIONINFO dvi; if (CModuleVersion::DllGetVersion("comctl32.dll", dvi)) { // now info is in dvi } DllGetVersion 是一个比较新的函数(译者注:在1998年是这样。)对于 comctl32 很好使,因为它实现并输出 DllGetVersion——但是对于那些不输出 DllGetVersion 的 DLLs 来说怎么办呢?例如:shell32.dll 就没有实现 DllGetVersion,如 Figure 3 所示。这时你就得用可怕以及奇怪的 GetFileVersionInfo 和 VerQueryValue 函数,它们在 winver.h 中定义。 Figure 3 No DllGetVersion Info 大多数可执行程序和 DLLs 都具备 VS_VERSION_INFO 资源,在模块的 RC 文件中定义。Figure 4 是 VersionDlg 的 RC 文件中的版本信息。你可以用文本编辑器或者 Visual Studio 直接编辑资源文件中的这段信息。你可以指定文件版本,产品版本等等,以及任何你想要编辑的字段,如:CompanyName、InternalName。文件版本信息与 Exe 或 DLL 文件在资源管理器“属性”页“版本”标签中显示的信息相同(参见 Figure 5)。 Figure 5 Version Tab 等一会儿你就会发现,这些版本 APIs 十分暧昧,很容易把人搞晕菜,但 CModuleVersion 使一切都变得简单明了。这个类派生于 VS_FIXEDFILEINFO(参见 Figure 6),此结构包含“固定的”版本信息,其中有主版本号和次版本号,还有一些 DLLVERSIONINFO 里的东西。使用 CModuleVersion 时,只要像下面这样写即可: CModuleVersion ver; if (ver.GetFileVersionInfo(_T("comctl32.dll")) { WORD major = HIWORD(ver.dwFileVersionMS); WORD minor = LOWORD(ver.dwFileVersionMS); ... } 为了存取 CompanyName 这样的可变信息以及内涵的模块创建信息,你可以用另外一个函数 CModuleVersion:: GetValue,例如,下面代码段执行之后,sCompanyName 的值将类似“XYZ”或“Acme Corporation”这样的公司名称: CString sCompanyName = ver.GetValue(_T("CompanyName")); CModuleVersion 隐藏了获取信息所要做的所有邋遢细节——相信我,都是些邋遢细节!如果你只是想使用 CModuleVersion,那么看到这里就可以打住了;如果你想要了解 CModuleVersion 的工作原理,那就继续往下看。 假设 CModuleVersion::GetFileVersionInfo 能加载模块并获取 HINSTANCE,它调用 ::GetFileVersionInfoSize 来获取版本信息的大小,然后分配一个缓冲并调用 GetFileVersionInfo 来填充该缓冲。原始缓冲(CModuleVersion::m_pVersionInfo)是一个数据块,它包含固定的信息和可变信息。VerQueryValue 将一个指针指向你感兴趣的特定信息的起始位置。例如,为了得到固定的信息(VS_FIXEDFILEINFO),你得这样写: LPVOID lpvi; UINT iLen; VerQueryValue(buf, _T("//"), &lpvi, &iLen); 此处 buf 是从 GetFileVersionInfo 返回的完整信息。字符串“/”(在 C 中用“//”),你如果把它看作是一个目录,那它就是根信息(有一点像注册表)。VerQueryValue 将 lpvi 置到 VS_FIXEDFILEINFO 的起始处,iLen 为其长度。 以上是获取固定信息的方法,可变信息获取更奇怪,因为你必须首先知道语言 ID 和代码页是什么。在 Winidows 里,代码页指定了一个字符集,它是字符文字与表示它们的 1 或 2 字节值之间映射。标准的 ANSI 代码页是 1252;Unicode 是 1200。Figure 7 是语言ID和代码页的清单。Figure 4 中文件信息里的 Translation 键指定模块的语言ID和代码页。在 CModuleVersion 中,我使用自己的 Translation 结构来获取这个信息。 // in CModuleVersion struct TRANSLATION { WORD langID // language ID WORD charset; // code page } m_translation; 为了获取语言信息,CModuleVersion 用 VerQueryValue 函数以 /VarFileInfo/Translation 作为键。 if (VerQueryValue(m_pVersionInfo,"//VarFileInfo//Translation", &lpvi, &iLen) && iLen >= 4) { m_translation = *(TRANSLATION*)lpvi; } 一旦你知道了语言ID和代码页,你就可以得到 CompanyName 和 InternalName 这样的可变信息。实现方法是构造一个如下形式的查询: /StringFileInfo// 这里 是十六进制 ASCI 形式的语言ID(中文是 0804;US English 是 0409), 是代码页,格式为(1252 即 ANSI 的代码页是04e4), 是你想要的键,如:CompanyName。为了构造这个查询,你得用 sprintf 或者 CString::Format 来构造字符串: //StringFileInfo//040904e4//CompanyName 然后将这个字符串传给 VerQueryValue。如果你对这些繁琐的细节感到晕菜,不用担心——很幸运,CModuleVersion::GetValue 对所有邋遢细节都进行了封装,所以你只要像下面这样写即可: CString s = ver.GetValue(_T("CompanyName")); 实现了 CModuleVersion,VersionDlg 就简单多了。 它实际上就是一个对话框,这个对话框带有一个编辑框,用于输入模块名称,每当用户在编辑框中敲入模块名称时,MFC 便调用 ON_EN_CHANGE 消息处理例程 CVersionDialog::OnChangedModule。OnChangedModule 例程通过 CModuleVersion 对象及其 GetFileVersionInfo 和 GetDllVersion 函数来获得版本信息,然后将信息显示在对话框的两个静态文本控件中。这个过程很简单。 最后还有个技巧我得提一下。GetFileVersionInfo,VerQueryValue 以及其它有关文件版本函数在一个叫做 version.lib 的库中,你必须将它链接到你程序中。从而避免链接时出现烦人的“undefined symbol”(未定义符号)错误,ModuleVer.h 使用了一个鲜为人知但特别有用的 #pragma comment 语法,即使你忘记在 Project|Settings 的 Link 属性页中添加 Input ==〉Libraries 也没关系,#pragma comment 会告诉链接器与 version.lib 链接。 // 告诉链接器与 version.lib 进行链接 #pragma comment(linker, "/defaultlib:version.lib") 现在,有人可能会问,为什么这些东西如此重要?以及谁会需要这些东西呢?一般来说,如果你编写的是显示文件属性之类的工具程序,那你只是需要获取诸如 CompanyName 和 LegalCopyright 之类的变量。但你也许发现用 CModuleVersion 从自己的应用程序中吸取文件信息很有用,例如,为了在“关于”对话框和启动屏幕中显示版本信息。如果你使用 CModuleVersion,你只需修改资源文件中相应位置的版本信息即可,“关于”对话框和启动屏幕会自动显示当前最新版本信息。 版本信息另一个重要的用途是确定某个DLL是针对哪种语言编写的,这样你代码能与之对应。随着当今基于 Windows 的编程技术迅猛发展,DLLs 的新版本也随之日新月异,你很快就会发现下面这样的代码越来越多: if (version <= 470) // do one thing else if (version==471) // do something else else if (version==472) // do a third thing else // scream 这是一件很郁闷的事情,我敢说这也是微软的大佬们引入 DllGetVersion 来快速获取版本号的一个原因,从而避免了面对让人恐惧的 GetFileVersionInfo 函数,只用它来获取语言 IDs 和代码页(仅在需要获取诸如 CompanyName 这样的信息时使用)。 comctl32.dll 的与众不同也没有什么意外的,这个模块版本问题已经程序员最大的祸害之一,我可怜的邮箱曾被读者关于 comctl32.dll 这个模块的问题撑爆,很多问题都是客户下载了微软最新版本的 comctl32.dll 到机器上之后,应用程序就无法运行了。我会在以后的文章中解释 comctl32.dll 的版本问题,以及新的 toolbar 特性,如何解决 MFC 中 CToolBar 的 bug。现在,由于篇幅所限,我只能点到为止,目前 comctl32.dll 最新的版本为 6.00(随 IE 一起发布)。 最后,感谢上帝,微软已经出台关于可以随你的应用程序一起分发 comctl32.dll!但不是单独分发 comctl32.dll,而是可以随你程序的更新包及其它文件一起分发。详情参见:http://msdn.microsoft.com/developer/downloads/files/40comupd.htm,请在你的新版本出炉之前仔细阅读。
编程愉快!
您的提问和评论可发送到 Paul 的信箱:cppqa@microsoft.com. |
本文出自 Microsoft System Journal (MSJ) 的 April 1998 期刊,可通过当地报摊获得,或者最好是 订阅 Figure 2 VersionDlg VersionDlg.cpp // // VersionDlg 1998 Microsoft Systems Journal. // If this program works, it was written by Paul DiLascia. // If not, I don&#39;t know who wrote it. // // VersionDlg illustrates how to use CModuleVersion and DllGetVersion to // read the version info for a DLL/EXE. The only interesting function // for the purpose of CModuleVersion is OnChangedModule, which is called // when the user enters a new module name into the edit control. // #include "stdafx.h" #include "resource.h" #include "ModulVer.h" #include "StatLink.h" #include "TraceWin.h" //#include // if you have the Nov 1997 SDK installed #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif / // CVersionDialog dialog // class CVersionDialog : public CDialog { public: CVersionDialog(CWnd* pParent = NULL); CString m_sModuleName; // module name typed by user protected: CStaticLink m_wndLink1; // web link CStaticLink m_wndLink2; // web link CStaticLink m_wndLink3; // web link virtual void DoDataExchange(CDataExchange* pDX); virtual BOOL OnInitDialog(); virtual void OnOK(); afx_msg void OnChangedModule(); DECLARE_MESSAGE_MAP() }; // CMyApp // class CMyApp : public CWinApp { public: CMyApp() { } virtual BOOL InitInstance(); } theApp; / // Initialize: just run the dialog and quit. // BOOL CMyApp::InitInstance() { CVersionDialog dlg; // create dialog.. dlg.DoModal(); // ..run it return FALSE; // ..and quit } // // CVersionDialog // BEGIN_MESSAGE_MAP(CVersionDialog, CDialog) ON_EN_CHANGE(IDC_EDIT_MODULE, OnChangedModule) END_MESSAGE_MAP() CVersionDialog::CVersionDialog(CWnd* pParent) : CDialog(IDD_VERSION, pParent) { } / // Initialize dialog: subclass static hyperlinks // BOOL CVersionDialog::OnInitDialog() { m_wndLink1.SubclassDlgItem(IDC_STATICPD, this, _T("http://pobox.com/~dilascia")); m_wndLink2.SubclassDlgItem(IDC_STATICMSJ, this, _T("http://www.microsoft.com/msj")); m_wndLink3.SubclassDlgItem(IDC_ICONMSJ, this, _T("http://www.microsoft.com/msj")); return CDialog::OnInitDialog(); } / // When user pressed Enter, don&#39;t exit // void CVersionDialog::OnOK() { return; // (don&#39;t exit) } // // Standard MFC DDX data exchange for edit control // void CVersionDialog::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_MODULE, m_sModuleName); } // // User changed the module name: vet version info if I can. // void CVersionDialog::OnChangedModule() { UpdateData(TRUE); // get dialog data (module name) CString s; CModuleVersion ver; // 1st get version using File version API // if (ver.GetFileVersionInfo(m_sModuleName)) { // display file version from VS_FIXEDFILEINFO struct s.Format("Version: %d.%d.%d.%d/n", HIWORD(ver.dwFileVersionMS), LOWORD(ver.dwFileVersionMS), HIWORD(ver.dwFileVersionLS), LOWORD(ver.dwFileVersionLS)); // display a bunch of string values static LPCTSTR Keys[] = { _T("CompanyName"), _T("FileDescription"), _T("FileVersion"), _T("InternalName"), _T("LegalCopyright"), _T("OriginalFilename"), _T("ProductName"), _T("ProductVersion"), NULL }; for (int i=0; Keys[i]; i++) { CString temp; temp.Format("%s:/t%s/n", Keys[i], ver.GetValue(Keys[i])); s += temp; } } // set static text GetDlgItem(IDC_STATICINFO)->SetWindowText(s); // 2nd get version using DllGetVersion API // s.Empty(); DLLVERSIONINFO dvi; if (ver.DllGetVersion(m_sModuleName, dvi)) { s.Format(_T("DLL Version = %d.%02d/nBuild# = %d/n"), dvi.dwMajorVersion, dvi.dwMinorVersion, dvi.dwBuildNumber); s +=_T("Platform is "); if (dvi.dwPlatformID == DLLVER_PLATFORM_WINDOWS) s +=_T("Windows"); else if (dvi.dwPlatformID == DLLVER_PLATFORM_NT) s +=_T("Windows NT"); else s += _T("unrecognized"); } else { s += _T("This file does not implement DllGetVersion."); } // set static text GetDlgItem(IDC_STATICINFO2)->SetWindowText(s); } ModulVer.h // 1998 Microsoft Systems Journal // // If this code works, it was written by Paul DiLascia. // If not, I don&#39;t know who wrote it. // #ifndef __MODULEVER_H #define __MODULEVER_H // tell linker to link with version.lib for VerQueryValue, etc. #pragma comment(linker, "/defaultlib:version.lib") #ifndef DLLVERSIONINFO // following is from shlwapi.h, in November 1997 release of the Windows SDK typedef struct _DllVersionInfo { DWORD cbSize; DWORD dwMajorVersion; // Major version DWORD dwMinorVersion; // Minor version DWORD dwBuildNumber; // Build number DWORD dwPlatformID; // DLLVER_PLATFORM_* } DLLVERSIONINFO; // Platform IDs for DLLVERSIONINFO #define DLLVER_PLATFORM_WINDOWS 0x00000001 // Windows 95 #define DLLVER_PLATFORM_NT 0x00000002 // Windows NT #endif // DLLVERSIONINFO // // CModuleVersion version info about a module. // To use: // // CModuleVersion ver // if (ver.GetFileVersionInfo("_T("mymodule))) { // // info is in ver, you can call GetValue to get variable info like // CString s = ver.GetValue(_T("CompanyName")); // } // // You can also call the static fn DllGetVersion to get DLLVERSIONINFO. // class CModuleVersion : public VS_FIXEDFILEINFO { protected: BYTE* m_pVersionInfo; // all version info struct TRANSLATION { WORD langID; // language ID WORD charset; // character set (code page) } m_translation; public: CModuleVersion(); virtual ~CModuleVersion(); BOOL GetFileVersionInfo(LPCTSTR modulename); CString GetValue(LPCTSTR lpKeyName); static BOOL DllGetVersion(LPCTSTR modulename, DLLVERSIONINFO& dvi); }; #endif
ModulVer.cpp // 1998 Microsoft Systems Journal // If this code works, it was written by Paul DiLascia. // If not, I don&#39;t know who wrote it. // // CModuleVersion provides an easy way to get version info // for a module.(DLL or EXE). // #include "StdAfx.h" #include "ModulVer.h" CModuleVersion::CModuleVersion() { m_pVersiOnInfo= NULL; // raw version info data } // // Destroy: delete version info // CModuleVersion::~CModuleVersion() { delete [] m_pVersionInfo; } // // Get file version info for a given module // Allocates storage for all info, fills "this" with // VS_FIXEDFILEINFO, and sets codepage. // BOOL CModuleVersion::GetFileVersionInfo(LPCTSTR modulename) { m_translation.charset = 1252; // default = ANSI code page memset((VS_FIXEDFILEINFO*)this, 0, sizeof(VS_FIXEDFILEINFO)); // get module handle TCHAR filename[_MAX_PATH]; HMODULE hModule = ::GetModuleHandle(modulename); if (hModule==NULL && modulename!=NULL) return FALSE; // get module file name DWORD len = GetModuleFileName(hModule, filename, sizeof(filename)/sizeof(filename[0])); if (len <= 0) return FALSE; // read file version info DWORD dwDummyHandle; // will always be set to zero len = GetFileVersionInfoSize(filename, &dwDummyHandle); if (len <= 0) return FALSE; m_pVersiOnInfo= new BYTE[len]; // allocate version info if (!::GetFileVersionInfo(filename, 0, len, m_pVersionInfo)) return FALSE; LPVOID lpvi; UINT iLen; if (!VerQueryValue(m_pVersionInfo, _T("//"), &lpvi, &iLen)) return FALSE; // copy fixed info to myself, which am derived from VS_FIXEDFILEINFO *(VS_FIXEDFILEINFO*)this = *(VS_FIXEDFILEINFO*)lpvi; // Get translation info if (VerQueryValue(m_pVersionInfo, "//VarFileInfo//Translation", &lpvi, &iLen) && iLen >= 4) { m_translation = *(TRANSLATION*)lpvi; TRACE("code page = %d/n", m_translation.charset); } return dwSignature == VS_FFI_SIGNATURE; } // // Get string file info. // Key name is something like "CompanyName". // returns the value as a CString. // CString CModuleVersion::GetValue(LPCTSTR lpKeyName) { CString sVal; if (m_pVersionInfo) { // To get a string value must pass query in the form // // "/StringFileInfo//keyname" // // where is the languageID concatenated with the // code page, in hex. Wow. // CString query; query.Format(_T("//StringFileInfo//%04x%04x//%s"), m_translation.langID, m_translation.charset, lpKeyName); LPCTSTR pVal; UINT iLenVal; if (VerQueryValue(m_pVersionInfo, (LPTSTR)(LPCTSTR)query, (LPVOID*)&pVal, &iLenVal)) { sVal = pVal; } } return sVal; } // typedef for DllGetVersion proc typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *); / // Get DLL Version by calling DLL&#39;s DllGetVersion proc // BOOL CModuleVersion::DllGetVersion(LPCTSTR modulename, DLLVERSIONINFO& dvi) { HINSTANCE hinst = LoadLibrary(modulename); if (!hinst) return FALSE; // Must use GetProcAddress because the DLL might not implement // DllGetVersion. Depending upon the DLL, the lack of implementation of the // function may be a version marker in itself. // DLLGETVERSIONPROC pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinst, _T("DllGetVersion")); if (!pDllGetVersion) return FALSE; memset(&dvi, 0, sizeof(dvi)); // clear dvi.cbSize = sizeof(dvi); // set size for Windows return SUCCEEDED((*pDllGetVersion)(&dvi)); } Figure 4Ver.rc VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,1 PRODUCTVERSION 1,0,0,1 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904B0" BEGIN VALUE "CompanyName", "Written by Paul DiLascia/0" VALUE "FileDescription", "VersionDlg Application by Paul DiLascia/0" VALUE "FileVersion", "1, 0, 0, 1/0" VALUE "InternalName", "VersionDlg/0" VALUE "LegalCopyright", "1998 Paul DiLascia/0" VALUE "LegalTrademarks", "/0" VALUE "OriginalFilename", "VersionDlg.EXE/0" VALUE "ProductName", "VersionDlg/0" VALUE "ProductVersion", "1, 0, 0, 1/0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END Figure 6VS_FIXEDFILEINFO typedef struct tagVS_FIXEDFILEINFO { DWORD dwSignature; /* e.g. 0xfeef04bd */ DWORD dwStrucVersion; /* e.g. 0x00000042 = "0.42" */ DWORD dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */ DWORD dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */ DWORD dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */ DWORD dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */ DWORD dwFileFlagsMask; /* = 0x3F for version "0.42" */ DWORD dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */ DWORD dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */ DWORD dwFileType; /* e.g. VFT_DRIVER */ DWORD dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */ DWORD dwFileDateMS; /* e.g. 0 */ DWORD dwFileDateLS; /* e.g. 0 */ } VS_FIXEDFILEINFO; Figure 7 Language IDs and Code Pages Language ID | Language | 0x0401 | Arabic | 0x0402 | Bulgarian | 0x0403 | Catalan | 0x0404 | Traditional Chinese | 0x0405 | Czech | 0x0406 | Danish | 0x0407 | German | 0x0408 | Greek | 0x0409 | U.S. English | 0x040A | Castilian Spanish | 0x040B | Finnish | 0x040C | French | 0x040D | Hebrew | 0x040E | Hungarian | 0x040F | Icelandic | 0x0410 | Italian | 0x0411 | Japanese | 0x0412 | Korean | 0x0413 | Dutch | 0x0414 | Norwegian - Bokml | 0x0810 | Swiss Italian | 0x0813 | Belgian Dutch | 0x0814 | Norwegian - Nynorsk | 0x0415 | Polish | 0x0416 | Brazilian Portuguese | 0x0417 | Rhaeto-Romanic | 0x0418 | Romanian | 0x0419 | Russian | 0x041A | Croato-Serbian (Latin) | 0x041B | Slovak | 0x041C | Albanian | 0x041D | Swedish | 0x041E | Thai | 0x041F | Turkish | 0x0420 | Urdu | 0x0421 | Bahasa | 0x0804 | Simplified Chinese | 0x0807 | Swiss German | 0x0809 | U.K. English | 0x080A | Mexican Spanish | 0x080C | Belgian French | 0x0C0C | Canadian French | 0x100C | Swiss French | 0x0816 | Portuguese | 0x081A | Serbo-Croatian (Cyrillic) | Code Page | Character Set | 932 | Windows, Japan (Shift - JIS X-0208) | 949 | Windows, Korea (Shift - KSC 5601) | 950 | Windows, Taiwan (GB5) | 1200 | Unicode | 1250 | Windows, Latin-2 (Eastern European) | 1251 | Windows, Cyrillic | 1252 | Windows, Multilingual | 1253 | Windows, Greek | 1254 | Windows, Turkish | 1255 | Windows, Hebrew | 1256 | Windows, Arabic |
|