• Thứ Hai, 06/06/2005 08:13 (GMT+7)

    Thư viện Unicode trên Win9x


    PCW VN 12/2001 có bài viết về phát triển ứng dụng Unicode trên Windows 9x với thư viện MSLU (Microsoft Layer for Unicode), tuy nhiên việc áp dụng không cho kết quả như ý với Visual C++ 6.0. Trong bài viết này tôi sẽ trình bày cách dịch một ứng dụng VC++ 6.0 với MSLU.

     

    Dịch Ứng Dụng Unicode Với MSLU Mà Không Dịch Lại Các Tập Tin Thư Viện Của Vc++6.0

    Theo như hướng dẫn trong MSDN thì việc dịch một ứng dụng với MSLU sẽ phải làm 3 bước sau:

          Bước 1.

    Chép tập tin unicows.lib vào thư mục C:\Program Files\Microsoft Visual Studio\VC98\Lib, chép tập tin unicows.dll vào thư mục của ứng dụng (vì unicows.dll không được nạp tự động nếu đặt vào thư mục $(WINDOWS) hoặc $(WINSYS).

          Bước 2.

    Trong môi trường làm việc của ứng dụng, vào Project.Settings.

    a) Chuyển tới tab Link và tại ô Object/library modules gõ dòng sau:

    /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.libs /nod:oleacc.lib /nod:oledlg.lib /nod:sensapi.lib

    Ghi chú: nod là viết tắt của nodefault

    b) Thêm vào unicows.lib.

    c) Và thêm dòng sau:

    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

    Cuối cùng, tại ô Object/library modules sẽ có nội dung sau:

    /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

          Bước 3.

    Dịch ứng dụng.

    Tuy nhiên ba bước trên vẫn chưa đảm bảo là ứng dụng sẽ hỗ trợ Unicode với MSLU.

    Bước 2a) chỉ là dẫn hướng cho ứng dụng liên kết (link) tới những tập tin thư viện mặc định của hệ thống là kernel32.lib... Nhưng thật ra một ứng dụng MFC không chỉ link tới những tập tin hệ thống này mà còn link tới những tập tin của chính MFC, ví dụ như  MSVCRTD.lib..., và những tập tin thư viện C-Runtime. Trong khi đó những tập tin như MSVCRTD.lib lại link tới các tập tin  như  kernel32.lib... Do đó việc khai báo unicows.lib ở bước 2b) không đảm bảo unicows.lib sẽ được link tới đầu tiên. Vì vậy ứng dụng mà ta xây dựng vẫn có thể gọi hàm ANSI mà không qua tầng chuyển đổi ANSI-UNICODE của MSLU.

    Do đó để đảm bảo ứng dụng sẽ gọi hàm Unicode, ta phải dịch lại những tập tin thư viện của MFC và thư viện C-Runtime.

     

    Dịch Ứng Dụng Unicode Với Mslu Và Dịch Lại Các Tập Tin  Thư Viện Của Vc++6.0

    Trước khi tiến hành ta phải thực hiện các bước chuẩn bị. Khi cài VC++6.0 nhớ cài Unicode MFC version và CRT source code (hai option này không được chọn ở chế độ mặc định, tốt nhất cài ở chế độ full).

    a) Dịch thư viện C-Runtime với MSLU

    • Để có thể trở lại trạng thái ban đầu, ta nên chép thư mục VC98\CRT\SRC sang một thư mục khác, ví dụ như C:\SRC.

    • Đổi tên các tập tin sau:

    ext_mkf thành Makefile

    ext_mkf.inc thành  Makefile.inc

    ext_mkf.sub thành  Makefile.sub

    _sample_.rc thành mslu_msvcrt.rc

    sample_i.rc thành mslu_msvcirt.rc

    sample_p.rc thành mslu_msvcp60.rc

    sample_i.def thành mslu_msvcirt.def

    sampld_i.def thành mslu_msvcirtd.def

    sample_p.def thành mslu_msvcp60.def

    sampld_p.def thành mslu_msvcp60d.def

    intel\_sample_.def thành intel\mslu _msvcrt.def

    intel\_sampld_.def thành intel\mslu _msvcrtd.def

    • Mở các tập tin *.def trên và ở đầu tập tin thay chuỗi sau từ LIBRARY thành tên tập tin tương ứng. Ví dụ ta mở tập tin MSLU_MSVCIRT.DEF và chuỗi cần thay là LIBRARY MSLU_MSVCIRT.

     • Mở tập tin makefile và thực hiện các thao tác sau:

    1) Thay khối đầu tập tin 

    RETAIL_DLL_NAME=_sample_

    RETAIL_LIB_NAME=_sample_

    RETAIL_DLLCPP_NAME=sample_p

    RETAIL_LIBCPP_NAME=sample_p

    RETAIL_DLLIOS_NAME=sample_i

    RETAIL_LIBIOS_NAME=sample_i

    DEBUG_DLL_NAME=_sampld_

    DEBUG_LIB_NAME=_sampld_

    DEBUG_DLLCPP_NAME=sampld_p

    DEBUG_LIBCPP_NAME=sampld_p

    DEBUG_DLLIOS_NAME=sampld_i

    DEBUG_LIBIOS_NAME=sampld_i

    thành :

    RETAIL_DLL_NAME=MSLU_MSVCRT

    RETAIL_LIB_NAME=MSLU_MSVCRT

    RETAIL_DLLCPP_NAME=MSLU_MSVCP60

    RETAIL_LIBCPP_NAME=MSLU_MSVCP60

    RETAIL_DLLIOS_NAME=MSLU_MSVCIRT

    RETAIL_LIBIOS_NAME=MSLU_MSVCIRT

    DEBUG_DLL_NAME=MSLU_MSVCRTD

    DEBUG_LIB_NAME=MSLU_MSVCRTD

    DEBUG_DLLCPP_NAME=MSLU_MSVCP60D

    DEBUG_LIBCPP_NAME=MSLU_MSVCP60D

    DEBUG_DLLIOS_NAME=MSLU_MSVCIRTD

    DEBUG_LIBIOS_NAME=MSLU_MSVCIRTD

     

    2) Khai báo đường dẫn đến thư mục của Visual Studio:

    V6TOOLS=C:\Program Files\Microsoft Visual Studio\VC98

    Tùy theo cài đặt Visual Studio mà đường dẫn trên có thể khác.

     

    3) Nên mở tập tin makefile trong VC++ hay bất kì chương trình nào khác có cho biết số dòng hiện tại của con trỏ để tiện thay đổi các dòng.

    • Thực hiện các thay đổi sau: 

    Dòng 331 thay thành:

    RC_INCS=”-I$(V6TOOLS)\include”

     

    Dòng 1728, 1770, 1810, 1853, 1898, 1941 thay thành:

     “$(V6TOOLS)\include\winver.h” \

     

    Dòng 381-383 thay thành:

    RELEASE_DLL_DBG_PDB= $(PDBDIR_CPU)\$(DEBUG_DLL_NAME).pdb

    RELEASE_DLLCPP_DBG_PDB  = $(PDBDIR_CPU)\$(DEBUG_DLLCPP_NAME).pdb

    RELEASE_DLLIOS_DBG_PDB  = $(PDBDIR_CPU)\$(DEBUG_DLLIOS_NAME).pdb

     

    Dòng  474 thay thành:

    $(CRT_RELDIR) $(RELDIR_CPU) :

     

    Dòng 987 thay thành:

    xdll : $(OBJROOT) $(OBJCPUDIR) $(OBJDIR_DLL_DBG) $(RELDIR_CPU) xothers \

     

    Sau dòng 1746, thêm:

    -pdb:$(RELEASE_DLL_PDB)

     

    Sau dòng 1789, thêm:

    -pdb:$(RELEASE_DLLCPP_PDB)

     

    Sau dòng 1829, thêm:

    -pdb:$(RELEASE_DLLIOS_PDB)

     

    Để link tới tập tin unicows.lib trước các tập tin  khác, dòng 1750, 1794, 1835, 1878, 1925, 1969 thay thành:

    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

     

    • Từ dấu nhắc DOS của thư mục C:\SRC, đánh vào các lệnh sau:

    set V6TOOLS=C:\Program Files\Microsoft Visual Studio\VC98

    “C:\Program Files\Microsoft Visual Studio\VC98\BIN\

    VCVARS32.bat”

    BLDWIN95.bat

     

    • Có thể bạn sẽ nhận thông báo lỗi do không đủ vùng nhớ để cấp cho biến môi trường. Ta sẽ

    vào property của cửa sổ DOS Prompt và khai báo báo kích thước cho biến môi trường là 4096.

    • Thời gian dịch có thể hơi lâu! 

    • Sau khi dịch xong ta sẽ được các tập tin  *.dll ,*.lib trong thư mục BUILD\INTEL.

    • Đổi tên các tập tin  thư viện sau về tên nguyên thủy của nó:

    mslu_msvcrt.lib thành msvcrt.lib

    mslu_msvcrtd.lib thành msvcrtd.lib

    mslu_msvcp60.lib thành msvcprt.lib

    mslu_msvcp60d.lib thành msvcprtd.lib mslu_msvcirt.lib thành msvcirt.lib mslu_msvcirtd.lib thành msvcirtd.lib

    • Chép 6 tập tin thư viện này vào thư mục VC98\Lib (ghi đè lên những tập tin  cũ).

     

    b) Dịch thư viện MFC với MSLU

    • Để có thể trở lại trạng thái ban đầu ta nên tạo backup cho thư mục VC98\MFC\LIB và VC98\MFC\SRC.

    • Có 4 tập tin  MFCDLL.MAK, MFCNET .MAK, MFCOLE.MAK, MFCDB.MAK trong thư mục VC98\MFC\SRC.  Nở các tập tin  này và trong mỗi tập tin  ta thay dòng link @<<  thành:

    /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

     

    • Trong tập tin DLLB.CPP thay dòng 38, 39, 46, 47 thành:

    #define MFC42_DLL   “MSLU_MFC42UD.DLL”

    #define MFCO42_DLL “MSLU_MFCO42UD.DLL”

    #define MFC42_DLL   “MSLU_MFC42U.DLL”

    #define MFCO42_DLL  “MSLU_MFCO42U.DLL”

    • Trong tập tin DLLNET.CPP thay dòng 37, 43 thành.

    #define MFC42_DLL “MSLU_MFC42UD.DLL”

    #define MFC42_DLL “MSLU_MFC42U.DLL”

    • Trong tập tin DLLOLE.CPP thay dòng 38, 44 thành:

    #define MFC42_DLL “MSLU_MFC42UD.DLL”

    #define MFC42_DLL “MSLU_MFC42U.DLL”

    • Trong tập tin DLLINIT.CPP thay dòng 371, 373, 391 thành:

    #define MSVCRT_DLL “MSLU_MSVCRTD.DLL”

    #define MSVCRT_DLL “MSLU_MSVCRT.DLL”

    #if 0 // Nội dung cũ của dòng này là #ifdef _UNICODE  

    • Trong tập tin viewedit.cpp thay dòng 30 thành:

    #if 1 //Nội dung cũ của dòng này là #ifndef _UNICODE 

    • Đổi tên các tập tin sau (các tập tin  này nằm trong thư mục VC98\MFC\SRC \INTEL):

    mfc42u.def thành mslu_mfc42u.def

    mfc42ud.def thành mslu_mfc42ud.def

    mfcn42ud.def thành mslu_mfcn42ud.def

    mfco42ud.def thành mslu_mfco42ud.def

    mfcd42ud.def thành mslu_mfcd42ud.def  

    • Mở các tập tin  *.def ở trên và thay chuỗi sau LIBRARY thành tên tập tin  tương ứng (như đã trình bày ở trên).

    • Từ DOS Prompt của thư mục C:\SRC, đánh vào các lệnh sau:

    set V6TOOLS=C:\Program Files\Microsoft Visual Studio\VC98

    “C:\Program Files\Microsoft Visual Studio\VC98\BIN\VCVARS32.bat”

    • Tạo tập tin  buildmfc.bat trong thư mục MFC\SRC với nội dung sau:

    nmake -f mfcdll.mak libname=MSLU_MFC42 DEBUG=0 UNICODE=1 /a

    copy /Y ..\lib\MSLU_MFC42U.LIB ..\lib\MFC42U.LIB

     

    nmake -f mfcdll.mak libname=MSLU_MFC42 DEBUG=1 UNICODE=1 /a   

    copy /Y ..\lib\MSLU_MFC42UD.LIB ..\lib\MFC42UD.LIB

     

    nmake -f mfcnet.mak libname=MSLU_MFCN42 DEBUG=1 UNICODE=1 /a

    copy /Y ..\lib\MSLU_MFCN42UD.LIB ..\lib\MFCN42UD.LIB

     

    nmake -f mfcole.mak libname=MSLU_MFCO42 DEBUG=1 UNICODE=1 /a

    copy /Y ..\lib\MSLU_MFCO42UD.LIB ..\lib\MFCO42UD.LIB

     

    nmake -f mfcdb.mak libname=MSLU_MFCD42 DEBUG=1 UNICODE=1 /a

    copy /Y ..\lib\MSLU_MFCD42UD.LIB ..\lib\MFCD42UD.LIB  

     

    Sau khi chạy tập tin  buildmfc.bat thì những tập tin  *.dll sẽ được tạo ra trong thư mục VC98\MFC\SRC, còn *.lib, *.def sẽ được tạo trong thư mục MFC\LIB.

    Vậy là ta đã dịch xong các tập tin  thư viện của MFC và CRT. Bây giờ để dịch một ứng dụng có hỗ trợ Unicode ta chỉ việc thực hiện tiếp các bước sau:

    - Chép unicows.lib vào VC98\Lib.

    - Chép unicows.dll vào thư mục ứng dụng.

    - Khai báo cho ứng dụng link tới tập tin  unicows.lib đầu tiên (đã trình bày ở trên).

    - Dịch ứng dụng.

     

    Dịch Ứng Dụng Unicode Trên Winnt Với Vc++6.0

    -Tạo một project mới

    -Vào menu Project (Settings, chọn tab C/C++, thêm option _UNICODE vào textbox Preprocessor.

    - Vào menu Project (Settings, chọn tab Link, chọn mục Output trong listbox Category, rồi nhập tên hàm “wWinMainCRTStartup” vào textbox “Entry-point symbol”.

    - Dịch ứng dụng.

    Nếu đem ứng dụng này chạy trên Win9X thì sẽ báo lỗi:

    Dòng thông báo lỗi này được hiện thực trong tập tin  dllinit.cpp (dòng 391).

    Lưu ý, nếu sử dụng Dependency Walker  để xem sự phụ thuộc  giữa các module của ứng dụng ta sẽ không thấy unicows.dll trong Module Dependency Tree View. Mô đun ứng dụng chỉ nạp unicows.dll thông qua hàm LoadLibrary() lúc runtime, vì vậy unicows.dll trở thành runtime dependency của ứng dụng mà trong trường hợp này thì Dependency Walker không quản lý.

     

    Kết luận:

    MSLU chỉ là tầng chuyển đổi UNICODE – ANSI và chỉ hỗ trợ Unicode ở cấp giao diện chứ hoàn toàn không hiện thực lại các hàm.

    MSLU giúp cho một ứng dụng Unicode có thể chạy trên cả hai hệ điều hành Win9X và WinNT, còn khả năng hỗ trợ Unicode của từng hệ điều hành đến đâu thì ứng dụng chỉ có thể được hỗ trợ  Unicode đến đó mà thôi.

     

    Nhận Xét

    Nếu muốn xây dựng một ứng dụng có hỗ trợ Unicode thì tốt nhất là nên xây dựng ứng dụng này trên các hệ điều hành được hỗ trợ Unicode ở cấp hệ thống như Windows XP, Windows 2000.

    MSLU có cơ chế cho phép override các hàm API, vì vậy theo lý thuyết, để xây dựng được ứng dụng có hỗ trợ Unicode một cách đầy đủ như trên WinNT ta có thể override các hàm API và hiện thực lại chúng để xử lý tốt Unicode.

    Thật ra thì MSLU vẫn chưa hoàn hảo. Vẫn có một số hàm không xử lý đúng trong một số trường hợp đặc biệt. Các phiên bản của MSLU đã sửa đổi dần những lỗi này, phiên bản mới nhất của MSLU (lúc thực hiện bài viết) là 1.0.3703.0 vẫn chưa sửa hết lỗi!


    Tài liệu Tham Khảo

    Tài liệu

    MSDN 10/2001

    -  http://microsoft.com

    -  http://www.trigeminal.com/usenet/usenet034.asp

    http://comcamp.myrice.com/techarticles/vc/0002.htm


    Tải về:

    unicows.lib tại http://devzone.icarusindie .com/PlatformSDK/installed/Lib/

    -  unicows.dll tại: http://download.microsoft .com/download/platformSDK/Redist/1.0/W9XME/EN-US/unicows.exe


    Phạm Đăng Khoa
    dkhoa81@yahoo.com

     

    ID: A0305_92