Thank you for commenting.

2015-03-02 16:17 GMT+08:00 Nikolay Sivov <bunglehead@gmail.com>:
On 02.03.2015 11:12, Shuai Meng wrote:
+    HRESULT hres;
+    BSTR bstr;
+    WCHAR ret;
+
+    TRACE("(%s)\n", debugstr_variant(arg));
+
+    switch(V_VT(arg)) {
+    case VT_NULL:
+        return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE);
+    case VT_EMPTY:
+        return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL);
+    case VT_BSTR:
+        bstr = V_BSTR(arg);
+    default:
+        hres = to_string(arg, &bstr);
+        if(FAILED(hres))
+            return hres;
+    }
+
+    ret = bstr[0];
+    if(ret == 0)
+        return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL);
+    return return_short(res, ret);

Looks like you need to handle VT_NULL explicitly and after that only distinguish between VT_BSTR or the rest.
Yes,you are right. 
Also you're leaking memory and missing a 'break'.