# Visual Basic > Visual Basic FAQs >  Classic VB - Does Visual Basic 6 support Unicode?

## Merri

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)
        For A = 0 To UBound(Text)
            tmpArr(A + A) = Text(A)
        Next A
    Else
        textStr = CStr(Text) & "|"
        textLen = LenB(textStr)
        tmpLen = textLen + textLen
        ReDim Preserve tmpArr(tmpLen + 1)
        ' get the new string to tmpArr
        tmpLen = MultiByteToWideChar(CLng(cPage), lFlags, ByVal StrPtr(textStr), -1, _
                                     ByVal VarPtr(tmpArr(0)), tmpLen)
        If tmpLen = 0 Then Exit Function
        tmpLen = tmpLen + tmpLen - 5
        'If tmpArr(tmpLen - 1) = 0 And tmpArr(tmpLen) = 0 Then tmpLen = tmpLen - 2
        If UBound(tmpArr) <> tmpLen Then ReDim Preserve tmpArr(tmpLen)
    End If
    ' return the result
    ANSItoUTF16 = tmpArr
End Function
Public Function UTF16toANSI(ByRef Text() As Byte, Optional ByVal cPage As KnownCodePage = CP_UNKNOWN, _
                            Optional lFlags As Long) As Byte()
    Static tmpArr() As Byte
    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 + 1) \ 2 - 1
        If (Not tmpArr) = True Then ReDim Preserve tmpArr(tmpLen)
        If UBound(tmpArr) <> tmpLen Then ReDim Preserve tmpArr(tmpLen)
        For A = 0 To tmpLen
            tmpArr(A) = Text(A + A)
        Next A
    Else
        textLen = (UBound(Text) + 1) \ 2
        ' at maximum ANSI can be four bytes per character in new Chinese encoding GB180302000
        tmpLen = textLen + textLen + textLen + textLen + 1
        ReDim Preserve tmpArr(tmpLen - 1)
        ' get the new string to tmpArr
        tmpLen = WideCharToMultiByte(CLng(cPage), lFlags, ByVal VarPtr(Text(0)), textLen, ByVal VarPtr(tmpArr(0)), _
                                     tmpLen, ByVal 0&, ByVal 0&)
        If tmpLen = 0 Then Exit Function
        ' a hopeless try to correct a weird error?
        ReDim Preserve tmpArr(tmpLen - 1)
    End If
    ' return the result
    UTF16toANSI = tmpArr
End Function

----------


## freshblueO2

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...

READ HERE -> INFO: Usage and Redistribution of FM20.DLL 
Download -> MS ActiveX Control Pad

----------


## LaVolpe

> ... 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 IfEnd Sub Private Sub UserControl_ReadProperties(PropBag As PropertyBag)    Dim bData() As Byte, tLen As Long    bData() = PropBag.ReadProperty("Text", vbNullString)    tLen = UBound(bData) + 1    If tLen = 0 Then       m_Text = vbNullString    Else       m_Text = String$(tLen \ 2, vbNullChar)       CopyMemory ByVal StrPtr(m_Text), bData(0), tLen    End IfEnd Sub

----------


## 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:


```
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.

----------


## Elroy

Here's some nice code for using the rtf control (don't forget to add to project) with Unicode.


```
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).

----------


## Elroy

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.

----------


## georgekar

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.

----------


## georgekar

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..

----------


## Elroy

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.

Just download http://motionlabresources.org/Unicod...0for%20VB6.zip

Your Welcome.

----------


## Elroy

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:


```
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
```

----------


## chrislong2

> 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:
> 
> 
> ```
> 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
> ...


Just a note that the code Elroy posted does seem to work and obviously is a real easy way to get Unicode to display.  :Smilie:   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!  :Smilie: 

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?

----------

