RSS
热门关键字:  计算机有关资料  33252  mkv  word  MKV_NT
当前位置 : 主页>vc编程>其它编程知识>列表

使用MSLU(Microsoft Layer for Unicode)

来源:COM集中营 作者:lostall 时间:2006-12-01 点击:

一、什么是MSLU

    Microsoft Layer for Unicode on Windows 95/98/ME Systems(简称MSLU)是微软新提供的一个开发包。原本,Windows 95/98/ME不支持Unicode,Unicode程序在这些系统下运行都会出错。但是MSLU使得即使在这些系统下,Unicode程序仍然可以正常运行。对Windows程序来说这是非常重要的,因为大部分商用应用程序都不希望只能在NT/2000以上的系统上运行。当然,ANSI程序可以在所有Windows平台上跑,但是在Windows NT/2000/XP下,所有的ANSI函数都是先转换为Unicode函数运行的,这意味着效率的下降。所以让Unicode程序能在Win95/98/ME下跑,比让ANSI程序在WinNT/2000/XP上跑好,毕竟未来的Windows都将是基于Unicode的。这就是MSLU的目的。

    MSLU并不复杂,MSDN里也有关于它的文档,篇幅不大,但也足以说明它的原理和使用方法。但在实际使用MSLU里,大多数初学者都会倍感困扰。困难来自于MSLU本身存在的Bug,以及MFC库并没有支持MSLU。要注意到,MSLU出现在VC6甚至VC7之后,MS并没有在当前的VC版本中支持MSLU。但相信在下一个版本的VC中,这个问题将不复存在。

    我很早以前,在2001年11月份的时侯就已经使用了MSLU,当时很顺利,觉得它很好用。但直到最近才发现原来还有这么多的困难。一个重要原因就是以前我用的只是Win32 SDK程序,而现在是MFC程序。 MSLU应用在MFC程序时问题很多。为了解决这些问题,我花了很多时间在网上查找资料和实验,现在终于小有所成。天外有天人外有人,每个人的前进都是站在别人的肩膀上,现在我也让出我的肩膀:)

二、MSLU的原理

    MSLU的原理很简单,它把Unicode参数先转换成非Unicode字符,调用非Unicode版本的函数,运行完后再把运行结果转换回Unicode字符。因为实际调用的是非Unicode版本的函数,所以在Win95/98/ME这些操作系统上也一样可以正确运行。

    不用担心使用了MSLU的程序在WinNT/2000/XP中的执行效率,MSLU不是为这些系统设计的,它根本不能在这上面使用,所以效率丝毫不受影响。

    Unicows.dll中所有的API函数,都可以重载,包括Unicows.dll的装载过程。比如:

// overriding of the loading of unicows.dll
static HMODULE __stdcall MyLoadUnicowsProc( void )
{
    return (LoadLibraryA("unicows.dll")) ;
}
extern "C" FARPROC _PfnLoadUnicows = (FARPROC) &MyLoadUnicowsProc ;


// overriding SetWindowTextW
static BOOL __stdcall MySetWindowTextW( HWND hWnd, LPCWSTR lpString )
{
    int nBytes = WideCharToMultiByte( CP_ACP, 0, lpString, -1, 
NULL, 0, NULL, NULL ) ; char *lpStringA = (char*)_alloca( nBytes ) ;// alloc memory on statck if ( 0 == WideCharToMultiByte( CP_ACP, 0, lpString, -1, lpStringA,
nBytes, NULL, NULL ) ) return FALSE ; return SetWindowTextA( hWnd, lpStringA ) ; } extern "C" FARPROC Unicows_SetWindowTextW = (FARPROC) &MySetWindowTextW ;
    MSDN中的这部分文档有两个遗漏,首先必须是__stdcall方式,其次必须用extern "C"

三、Win32程序

    在Win32中应用MSLU是最容易的了,对VC6和VC7来说是一样的,按照MSDN里的指示做就可以了,具体步骤如下:
    (1)新建一个Win32程序,不使用MFC库。
    (2)设置为Unicode程序。在C++/Preprocessor definitions/中去除_MBCS,加上_UNICODE,UNICODE;在Link/Output/Entry-point symbol中设为wWinMainCRTStartup。
    (3)加上MSLU支持。在Link/ Object/Library Modules中加上下面几行:
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib /nod:shell32.lib 
/nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib
/nod:winspool.lib /nod:vfw32.lib /nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib
/nod:sensapi.lib unicows.lib kernel32.lib advapi32.lib user32.lib gdi32.lib shell32.lib
comdlg32.lib version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib oleacc.lib
oledlg.lib
之所以要这样设置是希望能保证正确的链接顺序,要保证unicows.lib在任何其他lib之前被链接。
   OK!就这么多,Win32程序就这么简单。

四、准备知识

   在开始复杂的MFC之旅前,先要了解一些基本知识:
(1)几个预处理定义:
_WINDLL : 表明这是个DLL
_USRDLL : 表明这是个Regular DLL
_AFXDLL : 表明是动态链接MFC库

Regular DLL : 普通的正常的DLL,以extern "C"的方式输出函数和类。
它本身可以使用MFC类,但绝不能输出MFC类。它的客户程序不一定是MFC程序 AFXDLL : 也可以把它看作“Extension DLL”,即扩展的DLL。
它输出MFC类,(比如派生的窗口类),所以其客户程序必须是MFC程序。 MFC DLL自己就是一种特殊的“Extension DLL”。

(2)有几个设置是和运行时库相关的:

/MD : 对应Mutlithreaded DLL。定义_MT,_DLL。链接MSVCRT.LIB
/MDd: 对应Debug Multithreaded DLL。定义_DEBUG,_MT,_DLL。链接MSVCRTD.LIB
/MT : 对应Mutlithreaded。定义_MT。链接LIBCMT.LIB
/MTd: 对应Debug Multithreaded。定义_DEBUG,_MT。链接LIBCMTD.LIB
/ML : 对应Single Threaded。链接LIBC.LIB
/MLd: 对应Debug Single Threaded。定义_DEBUG。链接LIBCD.LIB

(3)MFC 6.0的组成:

静态链接MFC
Debug : nafxcwd.lib
Release : nafxcw.lib
Unicode Debug : uafxcwd.lib
Unicode Release : uafxcw.lib

动态链接MFC
Debug : MFC42D.DLL (core), MFCO42D.DLL (OLE), MFCD42D.DLL (database), 
MFCN42D.DLL (network), MFCS42D.LIB (static) Release : MFC42.DLL (combined), MFCS42.LIB (static) Unicode Debug : MFC42UD.DLL (core), MFCO42UD.DLL (OLE),
MFCD42UD.DLL (database), MFCN42UD.DLL (network), MFCS42UD.LIB (static) Unicode Release : MFC42U.DLL (combined), MFCS42U.LIB (static)

(4)MFC 7.0的组成:

静态链接MFC
Debug : nafxcwd.lib
Release : nafxcw.lib
Unicode Debug : uafxcwd.lib
Unicode Release : uafxcw.lib

动态链接MFC
Debug : MFC70D.DLL, MFCS70D.LIB (static)
Release : MFC70.DLL, MFCS70.LIB (static)
Unicode Debug : MFC70UD.DLL, MFCS70UD.LIB (static)
Unicode Release : MFC70U.DLL, MFCS70U.LIB (static)

五、MFC程序,静态链接

    静态链接的MFC程序并不复杂,和Win32程序基本一样,只是要多加上MFC的lib和运行时库,如下:
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib 
/nod:shell32.lib /nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib /nod:winspool.lib /nod:vfw32.lib
/nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib unicows.lib kernel32.lib advapi32.lib user32.lib gdi32.lib
shell32.lib comdlg32.lib version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib oleacc.lib oledlg.lib uafxcw.lib libcmt.lib
    如果是Debug版,当然就是uafxcwd.liblibcmtd.lib了。
    要注意的是,这两个文件必须放在最后面。

六、MFC程序(VC6),动态链接

    (1)按照"Rebuild MFC 6.0 and CRT with MSLU"中所讲的方法重编译MFC和CRT DLLs,并将新生成的Lib和DLL文件拷到各自的目录下。
    (2)库链接设置如下:
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib 
/nod:shell32.lib /nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib /nod:winspool.lib /nod:vfw32.lib
/nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib unicows.lib kernel32.lib advapi32.lib user32.lib gdi32.lib shell32.lib
comdlg32.lib version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib oleacc.lib oledlg.lib mfc42u.lib msvcrt.lib
    如果是Debug版,则是mfc42ud.libmsvcrtd.lib

七、MFC程序(VC7),动态链接

    (1)按照"Rebuild MFC 7.0 and CRT with MSLU"中所讲的方法重编译MFC和CRT DLLs,并将新生成的Lib和DLL文件拷到各自的目录下。
    (2)库链接设置如下:
/nod:kernel32.lib /nod:advapi32.lib /nod:user32.lib /nod:gdi32.lib 
/nod:shell32.lib /nod:comdlg32.lib /nod:version.lib /nod:mpr.lib /nod:rasapi32.lib /nod:winmm.lib /nod:winspool.lib /nod:vfw32.lib
/nod:secur32.lib /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib unicows.lib kernel32.lib advapi32.lib user32.lib gdi32.lib
shell32.lib comdlg32.lib version.lib mpr.lib rasapi32.lib winmm.lib winspool.lib vfw32.lib oleacc.lib oledlg.lib mfc70u.lib msvcrt.lib
    如果是Debug版,则是mfc70ud.libmsvcrtd.lib

八、MSLU 3665中的一个重大的Bug

    (Bug本不应在这里列出,只怪这个Bug太过严重。)
    MSLU的当前最新版本1.0.3665中有个非常严重的Bug(VC7及老版本中没这问题),就是对话框是不可见的!一个基于对话框的MFC程序根本是看不见的,File/Open对话框也不可见。
    不过幸好也有大侠研究出了解决方案,方法如下:

    (1)在InitInstance()前面加上如下两句话:
extern "C" BOOL InitMultipleMonitorStubs( void ) ;
extern "C" BOOL (WINAPI* g_pfnGetMonitorInfo)( HMONITOR, LPMONITORINFO ) ;
    (2)在InitInstance()中加上如下语句:
if (InitMultipleMonitorStubs())
{
   if (HMODULE hUser32 = GetModuleHandle(_T("USER32")))
      *(FARPROC*)&g_pfnGetMonitorInfo = GetProcAddress( hUser32, 
"GetMonitorInfoA" ) ; }
    这种方法只适合于静态链接MFC的情况,如果是动态链接的话,还是直接改源码得了,把multimon.h中的第124行改为:
        (*(FARPROC*)&g_pfnGetMonitorInfo      = GetProcAddress(hUser32,
"GetMonitorInfoA")) )
然后再重新编译MFC就行了。当然也可以重新编译MFC静态库版本,这样即使是静态链接MFC,也不需要在程序中加上如上的额外代码了。
    至于这个Bug的产生源由,对比一下VC6中multimon.h的第124行和VC7中multimon.h的第194行,就能猜出个大概了。
    有消息称,新版本的MSLU即将推出,届时这个Bug必将不复存在。

九、MSLU以外...

    MSLU并非全部,在Win95/98/ME上已存在一些别的Unicode的支持:
    (1)有些函数的Unicode版本在这些系统下已经实现了,比如常用的MessageBox、TextOut等。MSLU没有重载这些函数,但我们自己可以重载它,就象重载一个MSLU实现的API一样。
    (2)以下技术也已经支持Unicode了:
  • Common Control (commonctrl32), 必须需要comctl32.dll 5.80 (IE5.0)以上
  • Input Method Editor (IME)
  • MultiLanguage Object (MLang)
  • Rich Edit, 需要Rich Edit 2.0以上
  • Uniscribe
    MSLU与它们不重叠。

    (a)关于Common Control
    我起始并不知道这些。我在Win98第一版下做了一个MDI的测试程序,一切运行正常,但是Tooltip有问题,Debug下有ASSERT错误,原因是Tooltip的几个消息处理失败。后来我才明白是因为我的comctl32.dll的版本太低,我换到Win98第二版下,comctl32.dll的版本是5.81,Tooltip就能正常显示了。    

    (b)关于RichEdit
    Richedit目前有3个版本,1.0(riched32.dll),2.0(riched20.dll),3.0(riched20.dll)。在VC6中缺省地使用的是RichEdit 1.0,如果想在VC6中使用Richedit 2.0以上,以支持Unicode,可以参考下面的代码:
// AfxInitRichEdit()里调用的是LoadLibraryA("RICHED32.DLL"),这不行,要改掉它:
_AFX_RICHEDIT_STATE* pState = _afxRichEditState;
if (pState->m_hInstRichEdit == NULL)
	pState->m_hInstRichEdit = LoadLibraryA("RICHED20.DLL");

HWND hWnd = CreateWindowEx( 0, RICHEDIT_CLASSW, NULL, // L"RichEdit20W"
	WS_CHILD|WS_VISIBLE|WS_BORDER|ES_SUNKEN|ES_MULTILINE,
	100, 100, 200, 200, m_hWnd, NULL, AfxGetInstanceHandle(), NULL ) ;

::SetWindowText( hWnd, _T("test") ) ;
    在richedit.h里有如下定义:
#define RICHEDIT_CLASSA		"RichEdit20A"
#define RICHEDIT_CLASSW		L"RichEdit20W"
#define RICHEDIT_CLASS10A	"RICHEDIT"

#if (_RICHEDIT_VER >= 0x0200)
#ifdef UNICODE
#define RICHEDIT_CLASS		RICHEDIT_CLASSW
#else
#define RICHEDIT_CLASS		RICHEDIT_CLASSA
#endif /* UNICODE */
#else
#define RICHEDIT_CLASS		RICHEDIT_CLASS10A
#endif /* _RICHEDIT_VER >= 0x0200 */
    所以如果觉得用RICHEDIT_CLASSW过于死板,想用RICHEDIT_CLASS的话,自己定义_RICHEDIT_VER吧:
#undef _RICHEDIT_VER
#define _RICHEDIT_VER	0x0300
注意必须加在#include <afxwin.h>后面。
    如果用CRichEditCtrl创建的话,CRichEditCtrl自己内部调用了LoadLibraryA("RICHED32.DLL"),所以用同样的办法把它改掉就行了。

    以上都说的是6.0,在7.0里没这些问题了。7.0里多了个AfxInitRichEdit2(),它装载的是RICHED20.DLL; CRichEditCtrl里缺省也是Load RICHED20.DLL。

参考资料

1、http://www.microsoft.com/globaldev/articles/mslu_announce.asp,简单的介绍。
2、http://www.trigeminal.com/usenet/,本文大部分内容来源于此。
3、MSLU的新闻组
 

下载

msluexample.cpp(Overriding an API)
6.0示例代码(80KB) 7.0示例代码(80KB)
MSLU 1.0.3665(257KB) MSLU 1.0.3590(224KB)
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册