Page 1 of 1

need help with bstr

Posted: Sat Jan 05, 2008 8:24 pm
by jack
just for fun I am trying to import the decimal functions available in the oleaut32.dll, but bstr has me baffled, I Googled for hours but still don't know anymore than I did before, here's what I have so far.
note you will need to make your own import lib as the lib that comes with PB is missing the decimal arithmetic functions.

Code: Select all

Structure decimal
  wReserved.w
  scale.b
  sign.b
  hi32.l
  lo64.q
EndStructure

Structure DATE
  date.d
EndStructure

Structure CY
  int64.q
EndStructure

Procedure StringToBStr (string$) ; By Zapman Inspired by Fr34k 
  Protected Unicode$ = Space(Len(String$)* 2 + 2) 
  Protected bstr_string.l 
  PokeS(@Unicode$, String$, -1, #PB_Unicode) 
  bstr_string = SysAllocString_(@Unicode$) 
  ProcedureReturn bstr_string 
EndProcedure

Procedure.s ReadBstr(*String.s) ; By Fr34k
  Result$ = "" 
  
  If *String 
    length.l = WideCharToMultiByte_(#CP_ACP, 0, *String, -1, 0, 0, 0, 0) 
    *Buffer.l = AllocateMemory(length) 
    
    If *Buffer 
      WideCharToMultiByte_(#CP_ACP, 0, *String, -1, *Buffer, length, 0, 0) 
      Result$ = PeekS(*Buffer) 
      FreeMemory(*Buffer) 
    EndIf 
  EndIf 
  
  ProcedureReturn Result$ 
EndProcedure

Import "oleaut32.lib"
  VarUI1FromDec.l  ( *pdecIn.decimal, *pbOut.b )                                 As "_VarUI1FromDec@8"
  VarI2FromDec.l   ( *pdecIn.decimal, *psOut.w )                                 As "_VarI2FromDec@8"
  VarI4FromDec.l   ( *pdecIn.decimal, *plOut.l )                                 As "_VarI4FromDec@8"
  VarR4FromDec.l   ( *pdecIn.decimal, *pfltOut.f )                               As "_VarR4FromDec@8"
  VarR8FromDec.l   ( *pdecIn.decimal, *pdblOut.d )                               As "_VarR8FromDec@8"
  VarDateFromDec.l ( *pdecIn.decimal, *pdateOut.DATE )                           As "_VarDateFromDec@8"
  VarCyFromDec.l   ( *pdecIn.decimal, *pcyOut.q )                                As "_VarCyFromDec@8"
  VarBstrFromDec.l ( *pdecIn.decimal, lcid.l, dwFlags.l, *pbstrOut.s)            As "_VarBstrFromDec@16"
  VarBoolFromDec.l ( *pdecIn.decimal, *pboolOut.l )                              As "_VarBoolFromDec@8"
  VarI1FromDec.l   ( *pdecIn.decimal, *pcOut.b )                                 As "_VarI1FromDec@8"
  VarUI2FromDec.l  ( *pdecIn.decimal, *puiOut.w )                                As "_VarUI2FromDec@8"
  VarUI4FromDec.l  ( *pdecIn.decimal, *pulOut.l )                                As "_VarUI4FromDec@8"
  VarDecFromUI1.l  ( bIn.b, *pdecOut.decimal )                                   As "_VarDecFromUI1@8"
  VarDecFromI2.l   ( uiIn.w, *pdecOut.decimal )                                  As "_VarDecFromI2@8"
  VarDecFromI4.l   ( lIn.l, *pdecOut.decimal )                                   As "_VarDecFromI4@8"
  VarDecFromR4.l   ( fltIn.f, *pdecOut.decimal )                                 As "_VarDecFromR4@8"
  VarDecFromR8.l   ( dblIn.d, *pdecOut.decimal )                                 As "_VarDecFromR8@12"
  VarDecFromDate.l ( dateIn.d, *pdecOut.decimal )                                As "_VarDecFromDate@12"
  VarDecFromCy.l   ( dcyIn.q, *pdecOut.decimal )                                 As "_VarDecFromCy@12"
  VarDecFromStr.l  ( *strIn.s, lcid.l, dwFlags.l, *pdecOut.decimal )             As "_VarDecFromStr@16"
  ;VarDecFromDisp.l ( *pdispIn.l, lcid.l, *pdecOut.decimal )                      As "_VarDecFromDisp@12"
  VarDecFromBool.l ( boolIn.l , *pdecOut.decimal )                               As "_VarDecFromBool@8"
  VarDecFromI1.l   ( cIn.b , *pdecOut.decimal )                                  As "_VarDecFromI1@8"
  VarDecFromUI2.l  ( uiIn.w , *pdecOut.decimal )                                 As "_VarDecFromUI2@8"
  VarDecFromUI4    ( ulIn.l , *pdecOut.decimal )                                 As "_VarDecFromUI4@8"
  VarDecAdd.l      ( *pdecLeft.decimal, *pdecRight.decimal, *pdecResult.decimal) As "_VarDecAdd@12"
  VarDecSub.l      ( *pdecLeft.decimal, *pdecRight.decimal, *pdecResult.decimal) As "_VarDecSub@12"
  VarDecMul.l      ( *pdecLeft.decimal, *pdecRight.decimal, *pdecResult.decimal) As "_VarDecMul@12"
  VarDecDiv.l      ( *pdecLeft.decimal, *pdecRight.decimal, *pdecResult.decimal) As "_VarDecDiv@12"
  VarDecAbs.l      ( *pdecIn.decimal, *pdecResult.decimal )                      As "_VarDecAbs@8"
  VarDecFix.l      ( *pdecIn.decimal, *pdecResult.decimal )                      As "_VarDecFix@8"
  VarDecInt.l      ( *pdecIn.decimal, *pdecResult.decimal )                      As "_VarDecInt@8"
  VarDecNeg.l      ( *pdecIn.decimal, *pdecResult.decimal )                      As "_VarDecNeg@8"
  VarDecRound.l    ( *pdecIn.decimal, cDecimals.l, *pdecResult.decimal )         As "_VarDecRound@12"
  VarDecCmp.l      ( *pdecLeft.decimal, *pdecRight.decimal )                     As "_VarDecCmp@8"
  VarDecCmpR8.l    ( *pdecIn.decimal, dblRight.d )                               As "_VarDecCmpR8@12"
EndImport

Define.decimal x,y,z
Define.w dw
cy.q
bstr.l=SysAllocStringLen_(bstr,255)
bstr2.l=StringToBStr ("3.1415926535897932384626433832795")
lcid.l
VarDecFromI4( 12345, x )
x\scale=3 ;x=12.345
VarDecAdd( x, x, y );y=24.690
VarI2FromDec( y, @dw )
VarDecFromStr( bstr2, lcid, 0, y)
VarBstrFromDec( y, lcid, 0, bstr )
Debug(dw)
Debug(ReadBstr(PeekL(bstr)))
SysFreeString_(bstr)
SysFreeString_(bstr2)

Posted: Sun Jan 06, 2008 6:29 pm
by Demivec
jack wrote:just for fun I am trying to import the decimal functions available in the oleaut32.dll, but bstr has me baffled, I Googled for hours but still don't know anymore than I did before, here's what I have so far.
I tested your code. It stops with an Invalid memory read on the line with "VarDecAdd( x, x, y );y=24.690" . That doesn't seem to have anything to do with bstr. What problems were you experiencing with bstr?

Where did you get the definition for the structure "decimal"?

Posted: Mon Jan 07, 2008 1:13 am
by jack
Demivec the code works for me but have not tested it extensibly and I am not sure the memory is released, about the decimal structure, I got it from José Roca at his forum http://www.jose.it-berater.org/smfforum ... pic=1523.0
about the program crashing on your computer, may I suggest that you build the import lib yourself, you probably already know how but for other people that don't.
polib oleaut32.dll /machine:ix86 /out:oleaut32.lib

Posted: Mon Jan 07, 2008 5:00 am
by Demivec
jack wrote:Demivec the code works for me but have not tested it extensibly and I am not sure the memory is released, about the decimal structure, I got it from José Roca at his forum http://www.jose.it-berater.org/smfforum ... pic=1523.0
about the program crashing on your computer, may I suggest that you build the import lib yourself, you probably already know how but for other people that don't.
polib oleaut32.dll /machine:ix86 /out:oleaut32.li
Actually, I wasn't familiar with importing a library before. With your directions I'm now up and running. In the process I discovered that I didn't have the file oleaut32.dll. The program runs fine now. I'm still puzzled about your topic though. What baffles you about bstr?

Posted: Mon Jan 07, 2008 6:43 am
by pdwyer
Have you tried just using the pseudotype rather than go through all that?
p-bstr: acts as a string type, but will always convert the string to bstr before calling the
function. It is very useful when accessing a shared library which needs bstr parameters,
like COM components.
Or perhaps ask Jose, I remember him from the powerbasic forums, very clever and always releasing modules that would extend the language into areas it couldn't go natively (like activeX controls etc). He's very helpful.

Posted: Mon Jan 07, 2008 11:30 am
by jack
Demivec I don't understand bstr in general, and why I have to use PeekL for the ReadBstr Procedure to work as in ReadBstr(PeekL(bstr))
hunting trough header files I found BSTR defined as pointer to OLECHAR and OLECHAR defined as WCHAR, but still confused.
pdwyer I will give pseudo types a try, that's probably the way to go.

Posted: Mon Jan 07, 2008 6:16 pm
by Demivec
Here's a brief explanation that I think should unbaffle the bstr.

C strings (which PureBasic uses), are arrays of characters terminated by a NULL character.

bstr strings (which are used by Visual Basic, for instance) differ in that the length of the string precede the characters in the string. So, a bstr string knows its own length. In addition, all bstr strings are Unicode (16 bits per character) and they may contain nulls as characters in the string (since they don't use them to flag the end of the string).

Code: Select all

So to review:
PureBasic string(Ascii):                "ASCII"+Chr(0)                    ;1 byte per character + null terminator
PureBasic string(Unicode):              "U N I C O D E " + Chr(0)+Chr(0)  ;2 bytes per character + 2 byte null terminator
bstr (known as Visual Basic string):    length.l + "U N I C O D E "       ;4 byte length + 2 bytes per character (Unicode)

So to convert from PureBasic$(Ascii) to bstr we have to:

Code: Select all

Procedure StringToBStr (string$) ; By Zapman Inspired by Fr34k
  Protected Unicode$ = Space(Len(string$)* 2 + 2) ;reserve twice as much bytes for Unicode  (note:the +2 is not needed here and can safely be removed, see explanation above)
  Protected bstr_string.l
  PokeS(@Unicode$, string$, -1, #PB_Unicode) ;convert string to unicode
  bstr_string = SysAllocString_(@Unicode$)  ;allocate space for the bstr's length+characters+null and copy string there  (returns pointer to characters)
  ProcedureReturn bstr_string 
EndProcedure
To convert from bstr to PureBasic$(Ascii) we have to:

Code: Select all

Procedure.s ReadBstr(*String.s) ; By Fr34k
  Result$ = ""
  
  If *String     ;*String points to the characters, the length is contained in the 4 bytes in front of this(it may be modified by the parameters in the next statement though)
    length.l = WideCharToMultiByte_(#CP_ACP, 0, *String, -1, 0, 0, 0, 0) ;get length of bstr in characters+null
    *Buffer.l = AllocateMemory(length)   ;reserve space for characters+null
    
    If *Buffer
      WideCharToMultiByte_(#CP_ACP, 0, *String, -1, *Buffer, length, 0, 0)
      Result$ = PeekS(*Buffer)
      FreeMemory(*Buffer)
    EndIf
  EndIf
  
  ProcedureReturn Result$
EndProcedure
So for ReadBstr(*String.s), it needs the pointer to the string which is at location PeekL(bstr) in your program. The pointer at location bstr was recorded there by the call VarBstrFromDec( y, lcid, $409, bstr ) .

Posted: Tue Jan 08, 2008 1:06 am
by jack
Thank you Demivec, good explanation. :D

Posted: Wed Jan 09, 2008 2:26 am
by Fred
btw, PB natively supports BSTR argument passing by using pseudo-types:

instead of

Code: Select all

VarDecFromStr.l  ( *strIn.s, lcid.l, dwFlags.l, *pdecOut.decimal )             As "_VarDecFromStr@16"
do that:

Code: Select all

VarDecFromStr.l  ( *strIn.p-bstr, lcid.l, dwFlags.l, *pdecOut.decimal )             As "_VarDecFromStr@16" 

Posted: Thu Jan 10, 2008 1:01 am
by jack
thank you Fred :)

Posted: Thu Jan 10, 2008 2:56 am
by pdwyer

Code: Select all

<Yoda Voice>
    HmmmMM, To Fred do you listen, YESSS!
</Yoda Voice>