Yes and no. For yes, VB strings can hold 16-bit characters and is thus Unicode compatible. The problems come from several directions:
Strings passed to API are converted to ANSI and vice versa
Reading a file to a string is done by automatically converting ANSI to Unicode
The same applies when saving: string is converted to ANSI
Visual Basic controls are not Unicode aware
Luckily we have byte arrays to help us with the reading and writing business: they are easy to convert to strings and string contents can be copied to byte arrays easily with native VB code. Thus when working with Unicode, a byte array is your best bet.
Where are the controls?
The worst news is that VB6 controls use ANSI. The VB propertybags can't hold Unicode data and VB runtime also passes the data to the controls after ANSI conversion. Thus none of the default controls can be used for Unicode. The only free choice left is to code the controls by yourself or to seek for controls done by others on the internet. The easy solution costs money: people are selling Unicode aware controls and there is no free version available for many. Personally, I've made a UniLabel and a UniCommand and both are available for free (link).
Switching codepages
This code is still a work in progress: it is a simple module that allows to switch between codepages. This includes UTF-8 conversions, which might be handy. The functions take in and return a byte array. This far I haven't found an error with the current code. Use GetACP to find out the default codepage the system has in use (this codepage is used when a file is loaded by VB into a string).
VB Code:
Option Explicit
Public Enum KnownCodePage
CP_UNKNOWN = -1
CP_ACP = 0
CP_OEMCP = 1
CP_MACCP = 2
CP_THREAD_ACP = 3
CP_SYMBOL = 42
' ARABIC
CP_AWIN = 101 ' Bidi Windows codepage
CP_709 = 102 ' MS-DOS Arabic Support CP 709
CP_720 = 103 ' MS-DOS Arabic Support CP 720
CP_A708 = 104 ' ASMO 708
CP_A449 = 105 ' ASMO 449+
CP_TARB = 106 ' MS Transparent Arabic
CP_NAE = 107 ' Nafitha Enhanced Arabic Char Set
CP_V4 = 108 ' Nafitha v 4.0
CP_MA2 = 109 ' Mussaed Al Arabi (MA/2) CP 786
CP_I864 = 110 ' IBM Arabic Supplement CP 864
CP_A437 = 111 ' Ansi 437 codepage
CP_AMAC = 112 ' Macintosh Code Page
' HEBREW
CP_HWIN = 201 ' Bidi Windows codepage
CP_862I = 202 ' IBM Hebrew Supplement CP 862
CP_7BIT = 203 ' IBM Hebrew Supplement CP 862 Folded
CP_ISO = 204 ' ISO Hebrew 8859-8 Character Set
CP_H437 = 205 ' Ansi 437 codepage
CP_HMAC = 206 ' Macintosh Code Page
' CODE PAGES
CP_OEM_437 = 437
CP_ARABICDOS = 708
CP_DOS720 = 720
CP_DOS737 = 737
CP_DOS775 = 775
CP_IBM850 = 850
CP_IBM852 = 852
CP_DOS861 = 861
CP_DOS862 = 862
CP_IBM866 = 866
CP_DOS869 = 869
CP_THAI = 874
CP_EBCDIC = 875
CP_JAPAN = 932
CP_CHINA = 936
CP_KOREA = 949
CP_TAIWAN = 950
' UNICODE
CP_UNICODELITTLE = 1200
CP_UNICODEBIG = 1201
' CODE PAGES
CP_EASTEUROPE = 1250
CP_RUSSIAN = 1251
CP_WESTEUROPE = 1252
CP_GREEK = 1253
CP_TURKISH = 1254
CP_HEBREW = 1255
CP_ARABIC = 1256
CP_BALTIC = 1257
CP_VIETNAMESE = 1258
' KOREAN
CP_JOHAB = 1361
' MAC
CP_MAC_ROMAN = 10000
CP_MAC_JAPAN = 10001
CP_MAC_ARABIC = 10004
CP_MAC_GREEK = 10006
CP_MAC_CYRILLIC = 10007
CP_MAC_LATIN2 = 10029
CP_MAC_TURKISH = 10081
' CODE PAGES
CP_CHINESECNS = 20000
CP_CHINESEETEN = 20002
CP_IA5WEST = 20105
CP_IA5GERMAN = 20106
CP_IA5SWEDISH = 20107
CP_IA5NORWEGIAN = 20108
CP_ASCII = 20127
CP_RUSSIANKOI8R = 20866
CP_RUSSIANKOI8U = 21866
CP_ISOLATIN1 = 28591
CP_ISOEASTEUROPE = 28592
CP_ISOTURKISH = 28593
CP_ISOBALTIC = 28594
CP_ISORUSSIAN = 28595
CP_ISOARABIC = 28596
CP_ISOGREEK = 28597
CP_ISOHEBREW = 28598
CP_ISOTURKISH2 = 28599
CP_ISOLATIN9 = 28605
CP_HEBREWLOG = 38598
CP_USER = 50000
CP_AUTOALL = 50001
CP_JAPANNHK = 50220
CP_JAPANESC = 50221
CP_JAPANISO = 50222
CP_KOREAISO = 50225
CP_TAIWANISO = 50227
CP_CHINAISO = 50229
CP_AUTOJAPAN = 50932
CP_AUTOCHINA = 50936
CP_AUTOKOREA = 50949
CP_AUTOTAIWAN = 50950
CP_AUTORUSSIAN = 51251
CP_AUTOGREEK = 51253
CP_AUTOARABIC = 51256
CP_JAPANEUC = 51932
CP_CHINAEUC = 51936
CP_KOREAEUC = 51949
CP_TAIWANEUC = 51950
CP_CHINAHZ = 52936
CP_GB18030 = 54936
' UNICODE
CP_UTF7 = 65000
CP_UTF8 = 65001
End Enum
' Flags
Public Const MB_PRECOMPOSED = &H1
Public Const MB_COMPOSITE = &H2
Public Const MB_USEGLYPHCHARS = &H4
Public Const MB_ERR_INVALID_CHARS = &H8
Public Const WC_DEFAULTCHECK = &H100 ' check for default char
Public Const WC_COMPOSITECHECK = &H200 ' convert composite to precomposed
Public Const WC_DISCARDNS = &H10 ' discard non-spacing chars
Public Const WC_SEPCHARS = &H20 ' generate separate chars
Public Const WC_DEFAULTCHAR = &H40 ' replace with default char
Public Declare Function GetACP Lib "kernel32" () As Long
Private Declare Function MultiByteToWideChar Lib "kernel32" (ByVal CodePage As Long, _
ByVal dwFlags As Long, ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, _
ByVal lpWideCharStr As Long, ByVal cchWideChar As Long) As Long
Private Declare Function WideCharToMultiByte Lib "kernel32" (ByVal CodePage As Long, _
ByVal dwFlags As Long, ByVal lpWideCharStr As Long, ByVal cchWideChar As Long, _
ByVal lpMultiByteStr As Long, ByVal cchMultiByte As Long, ByVal lpDefaultChar As Long, _
lpUsedDefaultChar As Long) As Long
Public Function ANSItoUTF16(ByRef Text() As Byte, Optional ByVal cPage As KnownCodePage = CP_UNKNOWN, _
Optional lFlags As Long) As Byte()
Static tmpArr() As Byte, textStr As String
Dim tmpLen As Long, textLen As Long, A As Long
If (Not Text) = True Then Exit Function
' set code page to a valid one
If cPage = CP_UNKNOWN Then cPage = GetACP
If cPage = CP_ACP Or cPage = CP_WESTEUROPE Then
textLen = UBound(Text)
tmpLen = textLen + textLen + 1
If (Not tmpArr) = True Then ReDim Preserve tmpArr(tmpLen)
If UBound(tmpArr) <> tmpLen Then ReDim Preserve tmpArr(tmpLen)
Re: Classic VB - Does Visual Basic 6 support Unicode?
Let me provide an alternative. You can use Office 2000 activeX controls, otherwise known as the Forms 2.0 (FM20.dll) I use in all the senerios (at least I think I have) that you mentioned.
But you say "But I have to purchase Office 2000 in order to use it. My clients will have to purchase Office 2000 to use it." Nope, luckily you and they don't have to. Microsoft provided a legal free version of using the control, but you have to download and install a program to do so...
Re: Classic VB - Does Visual Basic 6 support Unicode?
Originally Posted by Merri
... The worst news is that VB6 controls use ANSI. The VB propertybags can't hold Unicode data
Though that is a true statement, there is a relatively easy workaround for storing unicode in a propertybag, i.e., usercontrols. VB can store 1 dimensional byte arrays in propertybags.
Let's say that a string variable m_Text contains unicode data. To store and read the data via a userconrol's propertybag, one can do this:
vb Code:
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Dim m_Text As String
Private Sub UserControl_WriteProperties(PropBag As PropertyBag)
Dim bData() As Byte, tLen As Long
If m_Text <> vbNullString Then
tLen = LenB(m_Text)
ReDim bData(0 To tLen - 1)
CopyMemory bData(0), ByVal StrPtr(m_Text), tLen
PropBag.WriteProperty "Text", bData()
End If
End Sub
Private Sub UserControl_ReadProperties(PropBag As PropertyBag)
Re: Classic VB - Does Visual Basic 6 support Unicode?
Codepages aren't the way to do it. It depends on what you want to do but VB6 will get UNICODE done fairly easily. For captions, here's some code:
Code:
Option Explicit
Private Const WM_GETTEXT As Long = &HD
Private Const WM_GETTEXTLENGTH As Long = &HE
Private Const WM_SETTEXT As Long = &HC
Private Declare Function DefWindowProcW Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SysAllocStringLen Lib "oleaut32" (ByVal OleStr As Long, ByVal bLen As Long) As Long
Private Declare Sub PutMem4 Lib "msvbvm60" (Destination As Any, Value As Any)
Public Property Let UniCaption(ctrl As Object, sUniCaption As String)
' USAGE: UniCaption(SomeControl) = s
'
' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
' Other controls are not known.
'
' As a tip, build your Unicode caption using ChrW.
' Also note the careful way we pass the string to the unicode API call to circumvent VB6's auto-ASCII-conversion.
DefWindowProcW ctrl.hWnd, WM_SETTEXT, 0, ByVal StrPtr(sUniCaption)
End Property
Public Property Get UniCaption(ctrl As Object) As String
' USAGE: s = UniCaption(SomeControl)
'
' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
' Other controls are not known.
Dim lLen As Long
Dim lPtr As Long
'
lLen = DefWindowProcW(ctrl.hWnd, WM_GETTEXTLENGTH, 0, ByVal 0) ' Get length of caption.
If lLen Then ' Must have length.
lPtr = SysAllocStringLen(0, lLen) ' Create a BSTR of that length.
PutMem4 ByVal VarPtr(UniCaption), ByVal lPtr ' Make the property return the BSTR.
DefWindowProcW ctrl.hWnd, WM_GETTEXT, lLen + 1, ByVal lPtr ' Call the default Unicode window procedure to fill the BSTR.
End If
End Property
The problem isn't in strings or in the captions. It's in the fact that Unicode wasn't implemented in the PropertyBag. Therefore, you just have to go around the PropertyBag to set control properties.
Also, the standard textbox doesn't support Unicode in the .Text property either, but the RTF control does. I'll make another post with some code for that.
Last edited by si_the_geek; Jun 22nd, 2014 at 09:17 AM.
Re: Classic VB - Does Visual Basic 6 support Unicode?
Here's some nice code for using the rtf control (don't forget to add to project) with Unicode.
Code:
Option Explicit
Private Const WM_USER As Long = &H400
Private Const EM_SETTEXTMODE As Long = WM_USER + 89
Private Const EM_SETTEXTEX As Long = WM_USER + 97
Private Const EM_GETTEXTEX As Long = WM_USER + 94
Private Const EM_GETTEXTLENGTHEX As Long = WM_USER + 95
Private Const TM_PLAINTEXT As Long = 1
Private Const CP_UNICODE = 1200&
Private Const GT_USECRLF = 1&
Private Const GTL_USECRLF = 1&
Private Const GTL_PRECISE = 2&
Private Const GTL_NUMCHARS = 8&
Private Enum RTBC_FLAGS ' CharFormat (SCF_) flags for EM_SETCHARFORMAT message.
RTBC_DEFAULT = 0
RTBC_SELECTION = 1
RTBC_WORD = 2 'Combine with RTBC_SELECTION!
RTBC_ALL = 4
End Enum
Public Enum RTBW_FLAGS ' Flags for the SETEXTEX data structure.
RTBW_DEFAULT = 0 ' Deletes undo stack, discards RTF formatting, replaces all text.
RTBW_KEEPUNDO = 1 ' Keeps undo stack.
RTBW_SELECTION = 2 ' Replaces selection and keeps RTF formatting.
End Enum
Private Type SETTEXTEX
flags As RTBW_FLAGS
codepage As Long
End Type
Private Type GETTEXTLENGTHEX
flags As Long
codepage As Long
End Type
Private Type GETTEXTEX
cb As Long
flags As Long
codepage As Long
lpDefaultChar As Long
lpUsedDefChar As Long
End Type
Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function SendMessageWLng Lib "user32" Alias "SendMessageW" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Public Sub SetupRichTextboxForUnicode(rtb As RichTextBox)
SendMessage rtb.hWnd, EM_SETTEXTMODE, TM_PLAINTEXT, 0 ' Set the control to use "plain text" mode so RTF isn't interpreted.
End Sub
Public Property Let RichTextboxUniText(rtb As RichTextBox, sUniText As String)
Dim stUnicode As SETTEXTEX
'
stUnicode.flags = RTBC_DEFAULT ' This could be otherwise.
stUnicode.codepage = CP_UNICODE
SendMessageWLng rtb.hWnd, EM_SETTEXTEX, VarPtr(stUnicode), StrPtr(sUniText)
End Property
Public Property Get RichTextboxUniText(rtb As RichTextBox) As String
Dim gtlUnicode As GETTEXTLENGTHEX
Dim gtUnicode As GETTEXTEX
Dim iChars As Long
'
gtlUnicode.flags = GTL_USECRLF Or GTL_PRECISE Or GTL_NUMCHARS
gtlUnicode.codepage = CP_UNICODE
iChars = SendMessageWLng(rtb.hWnd, EM_GETTEXTLENGTHEX, VarPtr(gtlUnicode), 0)
'
gtUnicode.cb = (iChars + 1) * 2
gtUnicode.flags = GT_USECRLF
gtUnicode.codepage = CP_UNICODE
RichTextboxUniText = String$(iChars, 0)
SendMessageWLng rtb.hWnd, EM_GETTEXTEX, VarPtr(gtUnicode), StrPtr(RichTextboxUniText)
End Property
Again, we've got to go around the PropertyBag to get it done.
There's also some nice code for using Unicode with the clipboard and also to write Unicode files that Windows Notepad can read (which also supports Unicode).
Last edited by si_the_geek; Jun 22nd, 2014 at 09:18 AM.
Reason: added Code tags
Re: Classic VB - Does Visual Basic 6 support Unicode?
I feel compelled to say it again: The VB6 controls are mostly ready to go for Unicode. It's that VB6 PropertyBag (the Properties window on the screen) that's stuck in ANSI/ASCII mode. The one exception to this is the .Text property of the textbox, but that's easily circumvented with the RTF control. Also, if you need to make API calls with Unicode, you just have to know to declare the string variable as LONG, and then use StrPtr(TheString) when you make the call.
Last edited by si_the_geek; Jun 20th, 2014 at 06:06 PM.
Reason: this is not the place for off-topic discussions
Re: Classic VB - Does Visual Basic 6 support Unicode?
I have made two controls, a listbox and a textbox for unicode support.
My approach is to use only user control and standard controls like a scroll bar. You can load file and save in UTF16 format.
About code page
Because vb standard forms are for ANSI, you need to set the codepage so the right conversion from utf16 can be done. For my controls there is no need to specify codepage because no conversion happen. If you have to display Chinese characters then you have to assign a font unicode with those characters, otherwise you see ?.
I use Greek Letters.
Print Asc("Á")
193
Print AscW("Á")
913
What this means? That in VB6 the real number for Alfa is 913 (Unicode UTF16), and when you ask the number using the codepage then you get 193 (ANSI). If you print CHRW$(20506) a chinnese char then youget ? because the print Function cannot find that UTF16 code in the ANSI of the system.
So where you can display that UTF16? Only where you can DRAWTEXT in a DC, through HDC founded from a hWnd. And for clipboard you can use api calls. For files is very easy...to save or load a unicode text, if you fill a byte array and put it in a binary file. If you place &HFEFF as the 2 bytes in position 1 and 2 in the file then you can open it from Word.
You can pass a string in iDE as a CHRW$(913), only. So is better to place it in a file.
In the exampes you can find how to use a picturebox as a label for unicode displaying, how to use the listbox as listbox and as combobox, and you can see the textbox how you can use it as multiline or not, with or without transparency, with colorized text.
Re: Classic VB - Does Visual Basic 6 support Unicode?
Here are some examples for using unicode in vb6.
Vb6 uses string as utf16 and in a picture box with a drawtext function we can display that code. In other cases vb6 check if each utf16 code in the string has a equivalent in ANSI user charset and if not fount the replace the char with a question mark. This is done with any print command. So print command is forbidden. For files there are byte arrays that can perform the job right. Its very simple to write and read these files. Also these files can opened from Word.
So the difficult part is about the controls for user input in unicode. These controls, that I provide are free and open source. The gEditbox is an advance gListbox. I made that for an ide to colorize code. So yer there isn't a wrapping function. Any paragraph (terminated with Cr or CrLf) is in one line. You can move lines with tab (or ctrl-tab if tab is using to change control), in the same way you can done in the VB IDE. There is no horizonal scroll bar, but there are events to utilize an external one. (I have an example for it). The purpose for this is to setup a split editor with two views of the same file to edit, and any change to one control copied to the other without notice any delay.
That's all..
Re: Classic VB - Does Visual Basic 6 support Unicode?
Here, full unicode support in both design mode and during runtime for VB6.
OptionButton
Checkbox
Label
COmmandButton
File I/O
Clipboard I/O
Other routines for putting Unicode into the caption of any VB control with a hWnd.
Re: Classic VB - Does Visual Basic 6 support Unicode?
People are all stressed about the PropertyBag not supporting Unicode, but that's not a problem. There is a problem with the actual properties window. That does NOT support Unicode, but if you're willing to build your own properties window (which can be done in VB6), you can circumvent that problem too. However, back to the PropertyBag. The problem is that .FRM files are written out in ANSI (which can't do Unicode), and the PropertyBag must be written out to these .FRM files. But there's also the .FRX files. Any binary stuff or non-ANSI stuff (including Unicode) is written into these .FRX files. Therefore, any Unicode fields we have in our custom controls must be coerced into these .FRX files and not the ANSI .FRM file. Here are two procedures for assisting with that. Just use them to go back and forth before writing your strings into the PropertyBag and everything will work. When you hit "save" that field of the PropertyBag will be written into the .FRX rather than the .FRM. Here you go:
Code:
Private Function UniToVar(sText As String) As Variant ' For keeping Unicode in the PropertyBag and the .FRX file.
Dim bb() As Byte
If Len(sText) = 0 Then
UniToVar = Null
Else
bb = sText
UniToVar = bb
End If
End Function
Private Function VarToUni(vVar As Variant) As String ' For keeping Unicode in the PropertyBag and the .FRX file.
Dim bb() As Byte
If IsNull(vVar) Then Exit Function ' Just return empty string.
bb = vVar
VarToUni = bb
End Function
Last edited by si_the_geek; Jul 15th, 2014 at 02:22 AM.
Reason: added Code tags
Re: Classic VB - Does Visual Basic 6 support Unicode?
Originally Posted by Elroy
Codepages aren't the way to do it. It depends on what you want to do but VB6 will get UNICODE done fairly easily. For captions, here's some code:
Code:
Option Explicit
Private Const WM_GETTEXT As Long = &HD
Private Const WM_GETTEXTLENGTH As Long = &HE
Private Const WM_SETTEXT As Long = &HC
Private Declare Function DefWindowProcW Lib "user32" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SysAllocStringLen Lib "oleaut32" (ByVal OleStr As Long, ByVal bLen As Long) As Long
Private Declare Sub PutMem4 Lib "msvbvm60" (Destination As Any, Value As Any)
Public Property Let UniCaption(ctrl As Object, sUniCaption As String)
' USAGE: UniCaption(SomeControl) = s
'
' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
' Other controls are not known.
'
' As a tip, build your Unicode caption using ChrW.
' Also note the careful way we pass the string to the unicode API call to circumvent VB6's auto-ASCII-conversion.
DefWindowProcW ctrl.hWnd, WM_SETTEXT, 0, ByVal StrPtr(sUniCaption)
End Property
Public Property Get UniCaption(ctrl As Object) As String
' USAGE: s = UniCaption(SomeControl)
'
' This is known to work on Form, MDIForm, Checkbox, CommandButton, Frame, & OptionButton.
' Other controls are not known.
Dim lLen As Long
Dim lPtr As Long
'
lLen = DefWindowProcW(ctrl.hWnd, WM_GETTEXTLENGTH, 0, ByVal 0) ' Get length of caption.
If lLen Then ' Must have length.
lPtr = SysAllocStringLen(0, lLen) ' Create a BSTR of that length.
PutMem4 ByVal VarPtr(UniCaption), ByVal lPtr ' Make the property return the BSTR.
DefWindowProcW ctrl.hWnd, WM_GETTEXT, lLen + 1, ByVal lPtr ' Call the default Unicode window procedure to fill the BSTR.
End If
End Property
Just a note that the code Elroy posted does seem to work and obviously is a real easy way to get Unicode to display. But the catch is that as soon as you theme your compiled EXE to Common Controls 6.0, which many people in the last 10-15 years have been doing for their apps, it no longer works. If anyone knows any way around this, please share. Thanks!
EDIT: I found reference on MSDN to a CCM_SETUNICODEFORMAT message you can send a control (i.e. a command button) to make it Unicode. I first verified with a CCM_GETUNICODEFORMAT message (sent via SendMessage API) that the format was indeed 0 (ANSI), and then I sent the CCM_SETUNICODEFORMAT message with a 1 (true) param and verified that it took with another CCM_GETUNICODEFORMAT message. The sending and receiving of the messages worked and the Unicode flag was set. But the button still would not show Unicode text like it did in the IDE (which is using Common Controls 5.0 rather than the 6.0 specified in the manifest in order to use themed controls). So I feel like I'm CLOSE, but am missing something. Anyone have any thoughts?
Last edited by chrislong2; Sep 7th, 2016 at 10:33 PM.