• Welcome to PlanetSquires Forums.
 

Strings in FreeBASIC

Started by Paul Squires, August 25, 2015, 09:11:16 AM

Previous topic - Next topic

José Roca

Almost. As BSTR, not as CBSTR.


#IfDef UNICODE
   Function AfxGetWindowText (ByVal HWnd As HWnd) As BSTR
#Else
   Function AfxGetWindowText (ByVal HWnd As HWnd) As String
#EndIf
[code]

Paul Squires

Got it. Thanks. :)

#IfDef UNICODE
   #Define AfxGetWindowText AfxGetWindowTextW
#Else
   #Define AfxGetWindowText AfxGetWindowTextA
#EndIf
                                             
Declare Function AfxGetWindowTextW(ByVal HWnd As HWnd) As BSTR
Declare Function AfxGetWindowTextA(ByVal HWnd As HWnd) As String
Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

An example:


' ========================================================================================
' Gets the text of a window.
' Note: GetWindowText cannot retrieve the text of a control in another application.
' ========================================================================================
FUNCTION AfxGetWindowTextW (BYVAL hwnd AS HWND) AS BSTR
   DIM nLen AS LONG
   DIM bstrHandle AS BSTR
   DIM wbuffer AS WSTRING * MAX_PATH
   nLen = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
   nLen = SendMessageW(hwnd, WM_GETTEXT, nLen + 1, CAST(LPARAM, @wbuffer))
   IF nLen THEN bstrHandle = SysAllocString(@wBuffer)
   FUNCTION = bstrHandle
END FUNCTION
' ========================================================================================


Usage:


DIM bs AS CBStr
bs = AfxGetWindowTextW(hwnd)
print *bs.Handle    ' Unicode
Print bs.ToAnsi ' Ansi


Will have to change DIM wbuffer AS WSTRING * MAX_PATH to a WSTRING pointer and allocate the buffer dynamically according to te length returned by WM_GETTEXTLENGTH.

José Roca

Since the returned data type is a BSTR, FB will call this version of the overloaded = operator


' ========================================================================================
OPERATOR CBStr.Let (BYREF bstrHandle AS BSTR)
   IF bstrHandle = NULL THEN EXIT OPERATOR
   IF m_bstr THEN SysFreeString(m_bstr)
   m_bstr = bstrHandle
END OPERATOR
' ========================================================================================


That attaches the BSTR handle to the class.

José Roca

New version:


' ========================================================================================
' Gets the text of a window.
' Note: GetWindowText cannot retrieve the text of a control in another application.
' ========================================================================================
FUNCTION AfxGetWindowTextW (BYVAL hwnd AS HWND) AS BSTR
   DIM nLen AS LONG, bstrHandle AS BSTR, pwbuffer AS WSTRING PTR
   FUNCTION = NULL
   nLen = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0)
   IF nLen = 0 THEN EXIT FUNCTION
   pwBuffer = CAllocate(nLen + 1)
   IF pwBuffer = NULL THEN EXIT FUNCTION
   nLen = SendMessageW(hwnd, WM_GETTEXT, nLen + 1, CAST(LPARAM, pwbuffer))
   IF nLen THEN bstrHandle = SysAllocString(pwBuffer)
   DeAllocate pwBuffer
   FUNCTION = bstrHandle
END FUNCTION
' ========================================================================================


José Roca

#20
New operator (*). Now we case use print **bs (notice the double indirection) instead of print *bs.Handle.


' ========================================================================================
OPERATOR * (BYREF pBStr AS CBStr) AS BSTR
   OPERATOR = pBStr.Handle
END OPERATOR
' ========================================================================================


José Roca

#21
New operators (&) and (+). Concatenates BSTRings with other BSTRings or FB Strings, WStrings or literals. Returns  a new BSTR with the contents.


' ========================================================================================
' Concatenates two BSTRs and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM b2 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = b1 & b2
' Print **b
' ========================================================================================
OPERATOR & (BYREF pBStr1 AS CBStr, BYREF pBSTR2 AS CBStr) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = SysStringLen(*pBStr1.Handle)
   n2 = SysStringLen(*pBStr2.Handle)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT OPERATOR
   IF n1 THEN memcpy(b, pBStr1.Handle, n1 * SIZEOF(WSTRING))
   IF n2 THEN memcpy(b+n1, pBStr2.Handle, n2 * SIZEOF(WSTRING))
   OPERATOR = b
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM wsz AS WSTRING = " - concatenated string"
' DIM b AS CBStr
' b = b1 & wsz
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR & (BYREF pBStr AS CBStr, BYREF wszStr AS WSTRING) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = SysStringLen(*pBStr.Handle)
   n2 = .LEN(wszStr)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT OPERATOR
   IF n1 THEN memcpy(b, pBStr.Handle, n1 * SIZEOF(WSTRING))
   IF n2 THEN memcpy(b+n1, @wszStr, n2 * SIZEOF(WSTRING))
   OPERATOR = b
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM wsz AS WSTRING = "Test string 1"
' DIM b1 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = wsz & b1
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR & (BYREF wszStr AS WSTRING, BYREF pBStr AS CBStr) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = .LEN(wszStr)
   n2 = SysStringLen(*pBStr.Handle)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT OPERATOR
   IF n1 THEN memcpy(b, @wszStr, n1 * SIZEOF(WSTRING))
   IF n1 THEN memcpy(b+n1, pBStr.Handle, n2 * SIZEOF(WSTRING))
   OPERATOR = b
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates two BSTRs and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM b2 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = b1 + b2
' Print **b
' ========================================================================================
OPERATOR + (BYREF pBStr1 AS CBStr, BYREF pBSTR2 AS CBStr) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = SysStringLen(*pBStr1.Handle)
   n2 = SysStringLen(*pBStr2.Handle)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT OPERATOR
   IF n1 THEN memcpy(b, pBStr1.Handle, n1 * SIZEOF(WSTRING))
   IF n2 THEN memcpy(b+n1, pBStr2.Handle, n2 * SIZEOF(WSTRING))
   OPERATOR = b
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM wsz AS WSTRING = " - concatenated string"
' DIM b AS CBStr
' b = b1 + wsz
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR + (BYREF pBStr AS CBStr, BYREF wszStr AS WSTRING) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = SysStringLen(*pBStr.Handle)
   n2 = .LEN(wszStr)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT OPERATOR
   IF n1 THEN memcpy(b, pBStr.Handle, n1 * SIZEOF(WSTRING))
   IF n2 THEN memcpy(b+n1, @wszStr, n2 * SIZEOF(WSTRING))
   OPERATOR = b
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM wsz AS WSTRING = "Test string 1"
' DIM b1 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = wsz + b1
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR + (BYREF wszStr AS WSTRING, BYREF pBStr AS CBStr) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = .LEN(wszStr)
   n2 = SysStringLen(*pBStr.Handle)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT OPERATOR
   IF n1 THEN memcpy(b, @wszStr, n1 * SIZEOF(WSTRING))
   IF n1 THEN memcpy(b+n1, pBStr.Handle, n2 * SIZEOF(WSTRING))
   OPERATOR = b
END OPERATOR
' ========================================================================================


Note: If you don't assign the returned BSTR to a variable of type CBStr, but to a variable of type BSTR, then you will have to free the returned BSTR with SysFreeString.

José Roca

#22
Changed the function Compare, that was using WSTRINGs instead of BSTRs, and added CompareI, CompareN and CompareNI.


' ========================================================================================
' Compares two BSTRs. The comparison is case sensitive.
' Returns zero if the strings are identical. Returns a positive value if the string pointed
' to by lpStr1 is alphabetically greater than that pointed to by lpStr2. Returns a negative
' value if the string pointed to by lpStr1 is alphabetically less than that pointed to by lpStr2.
' ========================================================================================
FUNCTION CBStr.Compare (BYREF pBStr1 AS CBStr, BYREF pBStr2 AS CBStr) AS LONG
   FUNCTION = StrCmpW(*pBStr1.Handle, *pBStr2.Handle)
END FUNCTION
' ========================================================================================

' ========================================================================================
' Compares two BSTRs. The comparison is not case sensitive.
' Returns zero if the strings are identical. Returns a positive value if the string pointed
' to by lpStr1 is alphabetically greater than that pointed to by lpStr2. Returns a negative
' value if the string pointed to by lpStr1 is alphabetically less than that pointed to by lpStr2.
' ========================================================================================
FUNCTION CBStr.CompareI (BYREF pBStr1 AS CBStr, BYREF pBStr2 AS CBStr) AS LONG
   FUNCTION = StrCmpIW(*pBStr1.Handle, *pBStr2.Handle)
END FUNCTION
' ========================================================================================

' ========================================================================================
' Compares a specified number of characters from the beginning of two BSTRs.
' nChars is the number of characters from the beginning of each string to be compared.
' The comparison is case sensitive.
' Returns zero if the strings are identical. Returns a positive value if the string pointed
' to by lpStr1 is alphabetically greater than that pointed to by lpStr2. Returns a negative
' value if the string pointed to by lpStr1 is alphabetically less than that pointed to by lpStr2.
' ========================================================================================
FUNCTION CBStr.CompareN (BYREF pBStr1 AS CBStr, BYREF pBStr2 AS CBStr, BYVAL nChars AS LONG) AS LONG
   FUNCTION = StrCmpNW(*pBStr1.Handle, *pBStr2.Handle, nChars)
END FUNCTION
' ========================================================================================

' ========================================================================================
' Compares a specified number of characters from the beginning of two BSTRs.
' nChars is the number of characters from the beginning of each string to be compared.
' The comparison is case sensitive.
' Returns zero if the strings are identical. Returns a positive value if the string pointed
' to by lpStr1 is alphabetically greater than that pointed to by lpStr2. Returns a negative
' value if the string pointed to by lpStr1 is alphabetically less than that pointed to by lpStr2.
' ========================================================================================
FUNCTION CBStr.CompareNI (BYREF pBStr1 AS CBStr, BYREF pBStr2 AS CBStr, BYVAL nChars AS LONG) AS LONG
   FUNCTION = StrCmpNIW(*pBStr1.Handle, *pBStr2.Handle, nChars)
END FUNCTION
' ========================================================================================


José Roca

#23
Added another Let (=) operator with a String parameter. The compiler was finding ambiguous the use of

DIM b AS CBStr
b = "Test string"


' ========================================================================================
OPERATOR CBStr.Let (BYREF szStr AS STRING)
   IF m_bstr THEN SysFreeString(m_bstr)
   m_bstr = SysAllocString(WSTR(szStr))
END OPERATOR
' ========================================================================================



José Roca

Please note that the FreeBasic intrinsic string functions can be used with CBStr strings, e.g.


DIM b AS CBStr = "Test string"
DIM wsz AS WSTRING * 250
wsz = MID(**b, 6)
print wsz



If the target is an asi string, the compiler will automatically convert it to ansi:


DIM b AS CBStr = "Test string"
DIM s AS STRING
s = MID(**b, 6)
print s



Same for UCase, LCase, Trim, etc.

This is becoming a tutorial, isn't it? :)

Paul Squires

Quote from: Jose Roca on August 25, 2015, 07:50:01 PM
This is becoming a tutorial, isn't it? :)


Lol :)    Yes, an awesome tutorial !!!

This string class will be indespensible moving forward with FB.

Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

#26
Until yesterday I didn't know how to write overloaded operators. Nothing like begin to write code and to figure solutions for the problems. This and a future class for variants will make COM programming much easier. I'm not obsessed with COM. It just happens that most of the current M$ technologies are COM based. And Unicode is essential.

Now, doing some cleaning to reduce bloat.

Removed some unneeded procedures.

Attach
No longer needed because now we can use the = operator.

ToAnsi
No longer needed because now we cab use STR(**bstring)

Equal
No longer needed because now we can use the = operator.

Compare, CompareI, CompareN, CompareNI
Not really needed because we can call the API functions easily, e.g.
DIM b1 AS CBStr = "Test string"
DIM b2 AS CBStr = "Test string 2"
print StrCmpW(**b1, **b2)

Added the Concat auxiliary function, now used by all the & and + operators.


' ========================================================================================
' Concatenates two WSTRINGs and returns a new BSTR.
' ========================================================================================
FUNCTION CBStr.Concat (BYREF wszStr1 AS CONST WSTRING, BYREF wszStr2 AS CONST WSTRING) AS BSTR
   DIM n1 AS INTEGER, n2 AS INTEGER, b AS BSTR
   n1 = .LEN(wszStr1)
   n2 = .LEN(wszStr2)
   b = SysAllocStringLen(NULL, n1+n2)
   IF b = NULL THEN EXIT FUNCTION
   IF n1 THEN memcpy(b, @wszStr1, n1 * SIZEOF(WSTRING))
   IF n2 THEN memcpy(b+n1, @wszStr2, n2 * SIZEOF(WSTRING))
   FUNCTION = b
END FUNCTION
' ========================================================================================

' ========================================================================================
' Concatenates two BSTRs and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM b2 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = b1 & b2
' Print **b
' ========================================================================================
OPERATOR & (BYREF pBStr1 AS CBStr, BYREF pBStr2 AS CBStr) AS BSTR
   OPERATOR = pBStr1.Concat(*pBStr1.Handle, *pBStr2.Handle)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM wsz AS WSTRING * 250 = " - concatenated string"
' DIM b AS CBStr
' b = b1 & wsz
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR & (BYREF pBStr AS CBStr, BYREF wszStr AS WSTRING) AS BSTR
   OPERATOR = pBStr.Concat(*pBStr.Handle, wszStr)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM wsz AS WSTRING * 250 = "Test string 1"
' DIM b1 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = wsz & b1
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR & (BYREF wszStr AS WSTRING, BYREF pBStr AS CBStr) AS BSTR
   OPERATOR = pBStr.Concat(wszStr, *pBStr.Handle)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates two BSTRs and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM b2 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = b1 + b2
' Print **b
' ========================================================================================
OPERATOR + (BYREF pBStr1 AS CBStr, BYREF pBStr2 AS CBStr) AS BSTR
   OPERATOR = pBStr1.Concat(*pBStr1.Handle, *pBStr2.Handle)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM b1 AS CBStr = "Test string 1"
' DIM wsz AS WSTRING * 250 = " - concatenated string"
' DIM b AS CBStr
' b = b1 + wsz
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR + (BYREF pBStr AS CBStr, BYREF wszStr AS WSTRING) AS BSTR
   OPERATOR = pBStr.Concat(*pBStr.Handle, wszStr)
END OPERATOR
' ========================================================================================

' ========================================================================================
' Concatenates a BSTR and a WSTRING and returns a new BSTR.
' Usage:
' DIM wsz AS WSTRING * 250 = "Test string 1"
' DIM b1 AS CBStr = " - concatenated string"
' DIM b AS CBStr
' b = wsz + b1
' Print **b
' Note: Instead of wsz, we can also pass an string literal or a FB string
' ========================================================================================
OPERATOR + (BYREF wszStr AS WSTRING, BYREF pBStr AS CBStr) AS BSTR
   OPERATOR = pBStr.Concat(wszStr, *pBStr.Handle)
END OPERATOR
' ========================================================================================


Paul Squires

I am loving the class so far. You have done so much with it in such a short time.

I especially like the double asterisk notation.  **bstring instead of *bstring.handle   That is really cool.

You can't do the following yet, right? It prints garbage for me.


Dim bs1 As CBStr = "Test string 1"
Dim bs2 As CBStr = "Test string 2"
Dim bs3 As CBStr

bs3 = **bs1 & **bs2 
Print **bs3       


This works though...

Dim st As WString * 100

Dim bs1 As CBStr = "Test string 1"
Dim bs2 As CBStr = "Test string 2"
Dim bs3 As CBStr

st = **bs1 & **bs2 
Print st       


Paul Squires
PlanetSquires Software
WinFBE Editor and Visual Designer

José Roca

> I especially like the double asterisk notation.  **bstring instead of *bstring.handle   That is really cool.

This has been the hardest one to figure.

> You can't do the following yet, right? It prints garbage for me.


Dim bs1 As CBStr = "Test string 1"
Dim bs2 As CBStr = "Test string 2"
Dim bs3 As CBStr

bs3 = **bs1 & **bs2 
Print **bs3


Since the target is a CBStr, you have to use


Dim bs1 As CBStr = "Test string 1"
Dim bs2 As CBStr = "Test string 2"
Dim bs3 As CBStr

bs3 = bs1 & bs2 
Print **bs3


> This works though...

Because the target is a WSTRING.

**bs returns a WSTRING pointer to the beginning of the string data, like STRPTR. We only need to use ** when we want to use a CBStr with a FB instrinsic function, that requires a WSTRING pointer, or a function that also requires an WSTRING pointer. For all the procedures and operators of the class, just use bs, without the **.

José Roca

It is not possible to overload the & operator having two WSTRING parameters because this is already defined by the compiler and gives a duplicate definition error.