TVM_GETITEM does not return unicode label

G

Guest

Hi all

My TreeView has unicode and english labels. The treeview shows OK on the
screen.
When I am trying to get an item's label using TVM_GETITEM API message, the
buffer returned by SendMessage always contains single-byte coded labels
(ASCII) even though I use SendMessageW entry point. In other words, buffer is
unicode string each character of which contains two ASCII letters of
corresponding label.

English labels are returned fine (as ASCII), but for unicode label, buffer
contains only question marks (0x3F) - one byte per letter.
(Don't ask me why I am using Win32 API to read treeview labels - this is
another story)

Here is my code. Is there any way to get unicode label from treeview node?
Thank you
(OS = Windows 2000 Pro with all service packs and updates)


[DllImport("user32", EntryPoint = "SendMessageW")]
private static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, int
lParam);

[DllImport("user32", EntryPoint = "SendMessageW")]
private static extern int SendMessageTVI(IntPtr hWnd, int wMsg, int wParam,
ref TVITEM tvi);

[ DllImport("kernel32.dll")]
public static extern IntPtr LocalAlloc(uint flags, uint cb);

[ DllImport("kernel32.dll")]
public static extern IntPtr LocalFree(IntPtr p);


private struct TVITEM
{
public int mask;
public int hItem;
public int state;
public int stateMask;
public int pszText; //if string, must be preallocated
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public int lParam;
public int iIntegral;
}

private const int TV_FIRST = 0x1100;
private const int TVM_GETITEM = (TV_FIRST + 12);
private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
private const int TVGN_ROOT = 0x0;
private const int TVGN_NEXT = 0x1;
private const int TVGN_CHILD = 0x4;
private const int TVIF_TEXT = 0x1;
private const int MY_MAXLVITEMTEXT = 260;
....
private void Form1_Load(object sender, System.EventArgs e)
{
TreeNode node = new TreeNode("English"); //root = english label
string t = "" + '\u0434' + '\u0432' + '\u043e' + '\u0440'; //child =
unicode label
node.Nodes.Add(t);
this.treeView1.Nodes.Add(node);
}

private void buttonGetTreeItems_Click(object sender, System.EventArgs e)
{
int hRoot = SendMessage(this.treeView1.Handle, TVM_GETNEXTITEM, TVGN_ROOT,
0);
string strText = GetTreeItem_Local(hRoot);

int hChild = SendMessage(this.treeView1.Handle, TVM_GETNEXTITEM,
TVGN_CHILD, hRoot);
strText = GetTreeItem_Local(hChild);
}

private string GetTreeItem_Local(int hItem)
{
int ret;
TVITEM tvi = new TVITEM();
IntPtr pszText = LocalAlloc(0x40, MY_MAXLVITEMTEXT);

tvi.mask = TVIF_TEXT;
tvi.hItem = hItem;
tvi.cchTextMax = MY_MAXLVITEMTEXT;
tvi.pszText = (int)pszText;

ret = SendMessageTVI(this.treeView1.Handle, TVM_GETITEM, 0, ref tvi);
string buffer = Marshal.PtrToStringUni((IntPtr)tvi.pszText,
MY_MAXLVITEMTEXT);

char[] arr = buffer.ToCharArray(); //<== use this array to look at the
bytes
//in debug mode

LocalFree(pszText);
return buffer;
}
 
M

Mattias Sjögren

Alex,
Here is my code. Is there any way to get unicode label from treeview node? [...]
private const int TVM_GETITEM = (TV_FIRST + 12);

Just like there are ANSI and Unicode versions of the SendMessage API,
there are two different versions of the TVM_GETITEM message. From
Commctrl.h:

#define TVM_GETITEMA (TV_FIRST + 12)
#define TVM_GETITEMW (TV_FIRST + 62)

#ifdef UNICODE
#define TVM_GETITEM TVM_GETITEMW
#else
#define TVM_GETITEM TVM_GETITEMA
#endif

You have to use TVM_GETITEMW to get back unicode strings.



Mattias
 
B

Ben Voigt

Alex K. said:
Hi all

My TreeView has unicode and english labels. The treeview shows OK on the
screen.
When I am trying to get an item's label using TVM_GETITEM API message, the
buffer returned by SendMessage always contains single-byte coded labels
(ASCII) even though I use SendMessageW entry point. In other words, buffer
is
unicode string each character of which contains two ASCII letters of
corresponding label.

A window's unicodeness is set when the window class is registered, and all
messages sent to it are converted so the window procedure sees only one
style of string.

The relevant method is
System.Windows.Forms.NativeWindow+WindowClass.RegisterClass It looks like
it picks unicode or ANSI based on
System.Runtime.InteropServices.Marshal.SystemDefaultCharSize.

Since your TreeView is showing unicode labels, the NativeWindow probably is
unicode. Something in the way your p/invoke of SendMessageW is causing the
conversion.

Your API declarations are a mess.

You shouldn't use int to hold a pointer, because it's only a valid
assumption on 32-bit processors.

Why p/invoke LocalAlloc when there is Marshal.AllocHGlobal?

You haven't set CharSet on your DllImport attributes.

You've a problem because you allocate MY_MAXLVITEMTEXT bytes, but try to
retrieve MY_MAXLVITEMTEXT characters. Each unicode character takes two
bytes.

But none of these really explain why you would get ANSI characters.

Ahhh, here is your problem. Looking in CommCtrl.h:

#define TVM_GETITEMA (TV_FIRST + 12)

#define TVM_GETITEMW (TV_FIRST + 62)

#ifdef UNICODE

#define TVM_GETITEM TVM_GETITEMW

#else

#define TVM_GETITEM TVM_GETITEMA

#endif
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top