Author Topic: String to BSTR and back  (Read 3858 times)

0 Members and 1 Guest are viewing this topic.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
String to BSTR and back
« on: October 20, 2021, 11:34:41 am »
has anyone coded routines to convert a string to BSTR and vice-versa? https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: String to BSTR and back
« Reply #1 on: October 20, 2021, 11:59:12 am »
There, I just did!

Code: QB64: [Select]
  1. a$ = "I am a happy BSTR"
  2. string_2_bstr$ = "BSTR MyBstr = SysAllocString(L" + CHR$(34) + a$ + CHR$(34) + ");"
  3. bstr_2_string$ = MID$(string_2_bstr$, INSTR(string_2_bstr$, CHR$(34)), LEN(string_2_bstr$) - INSTR(string_2_bstr$, CHR$(34)) - 1)
  4.  
  5. PRINT "string_2_bstr$ = "; string_2_bstr$
  6. PRINT "bstr_2_string$ = "; bstr_2_string$
  7.  

Well it must be a whole heck of a lot more involved than that, but this is the very first article I've seen about B-Strings. Granted, we do see a lot of G-Strings out here in California, but that's a whole different kind of article, as in article of clothing, or lack thereof. @Cobalt

Anyway, other than instr() template formatting, and probably some data tables for identifying usage, what else
is involved in this conversion process you are seeking?

Pete

Edit: I like where @bplus is heading with forming a TYPE declaration.
« Last Edit: October 20, 2021, 12:24:20 pm by Pete »
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String to BSTR and back
« Reply #2 on: October 20, 2021, 12:09:58 pm »
Quote
A BSTR is a composite data type that consists of a length prefix, a data string, and a terminator. The following table describes these components.

REMARKS
Item   Description
Length prefix   A four-byte integer that contains the number of bytes in the following data string. It appears immediately before the first character of the data string. This value does not include the terminator.
Data string   A string of Unicode characters. May contain multiple embedded null characters.
Terminator   A NULL (0x0000) WCHAR.

Is a 4 byte integer a QB64 Long? ie

Type Bstr
length as long
s as string
t as string * 1 'terminator
End Type

And do you even have to put in anything for terminator ie automatic zero?

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String to BSTR and back
« Reply #3 on: October 20, 2021, 12:13:23 pm »
Maybe Long should be _Unsigned Long.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: String to BSTR and back
« Reply #4 on: October 20, 2021, 12:29:09 pm »
OK so Unicode chars are 4 bytes. There's a monkey wrench for me anyway.

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: String to BSTR and back
« Reply #5 on: October 20, 2021, 12:40:43 pm »
it's utf16 I think, 2 ubytes
any way, you nedd BSTR string conversion to use oleauto.dll VarDecFromStr and VarBstrFromDec functions
the reason is so that you can use the decimal functions, here's an example but limited to double conversion
Code: QB64: [Select]
  1. Option Explicit
  2.  
  3. Type DECIMAL
  4.     wReserved As Unsigned Integer
  5.     scale As Unsigned Byte
  6.     sign As Unsigned Byte
  7.     Hi32 As Unsigned Long
  8.     Lo32 As Unsigned Long
  9.     Mid32 As Unsigned Long
  10.  
  11.     Function VarDecFromI4& (ByVal i As Long, result As DECIMAL)
  12.     Function VarDecFromI8& (ByVal i&&, result As DECIMAL)
  13.     Function VarDecFromR4& (ByVal x As Single, result As DECIMAL)
  14.     Function VarDecFromR8& (ByVal x As Double, result As DECIMAL)
  15.     Function VarR4FromDec& (x As DECIMAL, result As Single)
  16.     Function VarR8FromDec& (x As DECIMAL, result As Double)
  17.     Function VarDecAbs& (x As DECIMAL, result As DECIMAL)
  18.     Function VarDecFix& (x As DECIMAL, result As DECIMAL)
  19.     Function VarDecInt& (x As DECIMAL, result As DECIMAL)
  20.     Function VarDecRound& (x As DECIMAL, Byval cDecimals As Long, result As DECIMAL)
  21.     Function VarDecNeg& (x As DECIMAL, result As DECIMAL)
  22.     Function VarDecCmp& (x As DECIMAL, y As DECIMAL)
  23.     Function VarDecCmpR8& (x As DECIMAL, Byval y As Double) '0 x<y, 1 x=y, 2 x>y, 3 Either expression is null
  24.     Function VarDecAdd& (x As DECIMAL, y As DECIMAL, result As DECIMAL)
  25.     Function VarDecSub& (x As DECIMAL, y As DECIMAL, result As DECIMAL)
  26.     Function VarDecMul& (x As DECIMAL, y As DECIMAL, result As DECIMAL)
  27.     Function VarDecDiv& (x As DECIMAL, y As DECIMAL, result As DECIMAL)
  28.  
  29. Dim As DECIMAL x, y, result
  30. Dim As Long flag
  31. Dim As Double p1, z
  32. p1 = 3.1415926535897932
  33.  
  34. flag = VarDecFromR8(p1, x)
  35. flag = VarDecAdd(x, x, result)
  36. flag = VarR8FromDec(result, z)
  37. Print "add decimal "; p1; " then print as double "; z
  38. flag = VarDecRound&(result, 2, result)
  39. flag = VarR8FromDec(result, z)
  40. Print "round to 2 decimals "; z
  41.  
  42. flag = VarDecFromI4(7, x)
  43. flag = VarDecFromI4(9, y)
  44. flag = VarDecDiv(x, y, result)
  45. flag = VarR8FromDec(result, z)
  46. Print "divide decima 7 by decimal 9 then print as double "; z
  47. flag = VarDecFromI8(777777777777777777&&, x)
  48. x.scale = 18
  49. flag = VarR8FromDec(x, z)
  50. Print "777777777777777777&& to decimal then scale to 18 decimals "; z
  51. flag = VarDecSub(result, x, result)
  52. flag = VarR8FromDec(result, z)
  53. Print "7/9 - .777777777777777777 = "; z
  54.  
output
Code: [Select]
add decimal  3.141592653589793  then print as double  6.28318530717958
round to 2 decimals  6.28
divide decima 7 by decimal 9 then print as double  .777777777777778
777777777777777777&& to decimal then scale to 18 decimals  .7777777777777778
7/9 - .777777777777777777 =  7.777777778000001D-19

Offline Pete

  • Forum Resident
  • Posts: 2361
  • Cuz I sez so, varmint!
    • View Profile
Re: String to BSTR and back
« Reply #6 on: October 20, 2021, 01:07:01 pm »
@SpriggsySpriggs Maybe Indy has a API snippet to do the conversion part, via Windows using calls like SysAllocString(), SysAllocStringByteLen, etc.

I have no clue what else may be involved at this point, but some interesting reading...

"Even if one could use a BSTR as a simple way to marshal a byte array (since the BSTR is length-prefixed, so it can store embedded NULs), and so potentially a BSTR could be used also to store non-UTF-16 text, the usual "natural" behavior for a BSTR is to contain a Unicode UTF-16 wchar_t-string.

So, the first problem is to clarify what kind of encoding the std::string uses (for example: Unicode UTF-8? Or some other code page?). Then you have to convert that string to Unicode UTF-16, and create a BSTR containing that UTF-16 string.

To convert from UTF-8 (or some other code page) to UTF-16, you can use the MultiByteToWideChar() function. If the source std::string contains a UTF-8 string, you can use the CP_UTF8 code page value with the aforementioned API.

Once you have the UTF-16 converted string, you can create a BSTR using it, and pass that as the output BSTR* parameter.

The main Win32 API to create a BSTR is SysAllocString(). There are also some variants in which you can specify the string length.

Or, as a more convenient alternative, you can use the ATL's CComBSTR class to wrap a BSTR in safe RAII boundaries, and use its Detach() method to pass the BSTR as an output BSTR* parameter."

More interesting reading: https://ericlippert.com/2003/09/12/erics-complete-guide-to-bstr-semantics/

Pete
Want to learn how to write code on cave walls? https://www.tapatalk.com/groups/qbasic/qbasic-f1/

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: String to BSTR and back
« Reply #7 on: October 20, 2021, 10:49:25 pm »
What's up? Why are we needing BSTR? I have functions for ASCII to Unicode / Unicode to ASCII strings (wrappers for MultiByteToWideChar and WideCharToMultiByte). Give me some background info for what you're doing and, when I have some time, I'll try to assist.

The answer you seek lies below.
« Last Edit: October 20, 2021, 11:05:34 pm by SpriggsySpriggs »
Shuwatch!

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: String to BSTR and back
« Reply #8 on: October 20, 2021, 10:59:48 pm »
I mean, if this is all you are needing, then it's really as simple as:

Code: QB64 $NOPREFIX: [Select]
  1.  
  2.     Function SysAllocString%& (OLECHAR As String)
  3.     Sub SysFreeString (ByVal bstrString As Offset)
  4.  
  5. Dim As Offset bstr: bstr = SysAllocString(ANSIToUnicode("This is a test"))
  6.  
  7. Print bstr
  8.  
  9. Print wCharPtrToString(bstr)
  10.  
  11. SysFreeString bstr
  12.  
  13. Function wCharPtrToString$ (wchar As _Offset)
  14.         Function wcslen%& (ByVal str As _Offset)
  15.     End Declare
  16.  
  17.     Dim As _Offset wlen: wlen = wcslen(wchar) * 2 'The length does not account for the 2-byte nature of Unicode so we multiply by 2
  18.     Dim As _MEM pChar: pChar = _Mem(wchar, wlen) 'Declaring a new _MEM block and setting it to grab the number of bytes referenced by wlen at pointer wchar
  19.     Dim As String char: char = Space$(wlen) 'Declaring a new string large enough to hold the unicode string
  20.     _MemGet pChar, pChar.OFFSET, char 'Storing the data in the string
  21.     _MemFree pChar 'Freeing the _MEM block
  22.     wCharPtrToString = UnicodeToANSI(char) 'Returning the converted Unicode string
  23.  
  24.     Function WideCharToMultiByte& (ByVal CodePage As _Unsigned Long, Byval dwFlags As Long, Byval lpWideCharStr As _Offset, Byval cchWideChar As Integer, Byval lpMultiByteStr As _Offset, Byval cbMultiByte As Integer, Byval lpDefaultChar As _Offset, Byval lpUsedDefaultChar As _Offset)
  25.     Function MultiByteToWideChar& (ByVal CodePage As _Unsigned Long, Byval dwFlags As Long, Byval lpMultiByteStr As _Offset, Byval cbMultiByte As Integer, Byval lpWideCharStr As _Offset, Byval cchWideChar As Integer)
  26.  
  27. Function UnicodeToANSI$ (buffer As String)
  28.     Dim As String ansibuffer: ansibuffer = Space$(Len(buffer))
  29.     Dim As Long a: a = WideCharToMultiByte(437, 0, _Offset(buffer), Len(buffer), _Offset(ansibuffer), Len(ansibuffer), 0, 0)
  30.     UnicodeToANSI = Mid$(ansibuffer, 1, InStr(ansibuffer, Chr$(0)) - 1)
  31.  
  32. Sub UnicodeToANSI (buffer As String, __dest As String)
  33.     Dim As String ansibuffer: ansibuffer = Space$(Len(buffer))
  34.     Dim As Long a: a = WideCharToMultiByte(437, 0, _Offset(buffer), Len(buffer), _Offset(ansibuffer), Len(ansibuffer), 0, 0)
  35.     __dest = Mid$(ansibuffer, 1, InStr(ansibuffer, Chr$(0)) - 1)
  36.  
  37. Function ANSIToUnicode$ (buffer As String)
  38.     Dim As String unicodebuffer: unicodebuffer = Space$(Len(buffer) * 2)
  39.     Dim As Long a: a = MultiByteToWideChar(65001, 0, _Offset(buffer), Len(buffer), _Offset(unicodebuffer), Len(unicodebuffer))
  40.     ANSIToUnicode = unicodebuffer
  41.  
  42. Sub ANSIToUnicode (buffer As String, __dest As String)
  43.     Dim As String unicodebuffer: unicodebuffer = Space$(Len(buffer) * 2)
  44.     Dim As Long a: a = MultiByteToWideChar(65001, 0, _Offset(buffer), Len(buffer), _Offset(unicodebuffer), Len(unicodebuffer))
  45.     __dest = unicodebuffer

A BSTR is really just a pointer to a wide-char string. It expects a wide-char string as an input so I use my ANSIToUnicode function to pass my string in. SysAllocString returns the pointer. If I want the string back as an ANSI type then I can just use my function wrapper called wCharPtrToString.
« Last Edit: October 21, 2021, 08:39:57 pm by SpriggsySpriggs »
Shuwatch!

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: String to BSTR and back
« Reply #9 on: October 21, 2021, 06:56:29 am »
thanks a million SpriggsySpriggs :-)
[edit]
it doesn't work if I do something like this, your code omitted for brevity
Code: QB64: [Select]
  1. Type DECIMAL
  2.     wReserved As Unsigned Integer
  3.     scale As Unsigned Byte
  4.     sign As Unsigned Byte
  5.     Hi32 As Unsigned Long
  6.     Lo32 As Unsigned Long
  7.     Mid32 As Unsigned Long
  8.  
  9.     Function WideCharToMultiByte& (ByVal CodePage As _Unsigned Long, Byval dwFlags As Long, Byval lpWideCharStr As _Offset, Byval cchWideChar As Integer, Byval lpMultiByteStr As _Offset, Byval cbMultiByte As Integer, Byval lpDefaultChar As _Offset, Byval lpUsedDefaultChar As _Offset)
  10.     Function MultiByteToWideChar& (ByVal CodePage As _Unsigned Long, Byval dwFlags As Long, Byval lpMultiByteStr As _Offset, Byval cbMultiByte As Integer, Byval lpWideCharStr As _Offset, Byval cchWideChar As Integer)
  11.  
  12.     Function VarDecFromStr& (st As Offset, Byval lcid As Unsigned Long, Byval flags As Unsigned Long, result As DECIMAL)
  13.     Function SysAllocString%& (OLECHAR As String)
  14.     Sub SysFreeString (ByVal bstrString As Offset)
  15.  
  16. Dim As DECIMAL x, y, result
  17. Dim As Unsigned Long lcid
  18. Dim As Offset b
  19. Dim As Long flag
  20.  
  21. s = "3.1415926535897932384626433832795" + Chr$(0)
  22. b = SysAllocString(ANSIToUnicode(s))
  23. flag = VarDecFromStr(b, lcid, 0, x)
  24.  
flag = -2147352571 or DISP_E_TYPEMISMATCH
« Last Edit: October 21, 2021, 08:17:29 am by jack »

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: String to BSTR and back
« Reply #10 on: October 21, 2021, 10:53:06 am »
Don't forget byval in your VarDecFromStr function
Shuwatch!

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: String to BSTR and back
« Reply #11 on: October 21, 2021, 12:35:43 pm »
yes that indeed works, thanks again :-)

Offline SpriggsySpriggs

  • Forum Resident
  • Posts: 1145
  • Larger than life
    • View Profile
    • GitHub
Re: String to BSTR and back
« Reply #12 on: October 21, 2021, 06:46:12 pm »
@jack Glad it works! Good luck with your project. Tag me if you need me again.
Shuwatch!