Sendmessage and GETTEXTEX in vb/vba
Sendmessage and GETTEXTEX in vb/vba
(OP)
Could someone have a look at the following code and suggest where I am going wrong in adapting to vb/vba.
As is, the code crashes the application on:
SendMessageRef(m_hWnd, EM_GETTEXTEX, tGTT, sBuff)
C Datatypes and all the fun stuff like memory management are a new experience to me and a prickly introduction.
Thanks, Bill
CODE:
Private Declare Function apiSendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal msg As Long, ByRef tGTT As Any, ByRef lp As Any) As Long
Dim tGTL As GETTEXTLENGTHEX
Dim tGTT As GETTEXTEX
Dim lRet As Long '
Dim lRet1 As Long
Dim sBuff As String
Dim sRTText As String
Call apiZeroMemory(tGTL, Len(tGTL))
tGTL.flags = GTL_NUMCHARS
tGTL.codepage = CP_ACP
'return number of TChars in RtichText control
lRet = SendMessageRef(m_hWnd, EM_GETTEXTLENGTHEX, tGTL, 0&)
Call apiZeroMemory(tGTT, Len(tGTT))
tGTT.cb = lRet + 1
tGTT.flags = GT_DEFAULT
tGTT.codepage = CP_ACP
tGTT.lpDefaultChar = Chr(164) '0&
tGTT.lpUsedDefChar = 0&
sBuff = String$(lRet + 1, 0)
lRet1 = SendMessageRef(m_hWnd, EM_GETTEXTEX, tGTT, sBuff) 'CRASH!!
If (lRet1 > 0) Then
sRTText = Left$(sBuff, lRet1)
End If
As is, the code crashes the application on:
SendMessageRef(m_hWnd, EM_GETTEXTEX, tGTT, sBuff)
C Datatypes and all the fun stuff like memory management are a new experience to me and a prickly introduction.
Thanks, Bill
CODE:
Private Declare Function apiSendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal msg As Long, ByRef tGTT As Any, ByRef lp As Any) As Long
Dim tGTL As GETTEXTLENGTHEX
Dim tGTT As GETTEXTEX
Dim lRet As Long '
Dim lRet1 As Long
Dim sBuff As String
Dim sRTText As String
Call apiZeroMemory(tGTL, Len(tGTL))
tGTL.flags = GTL_NUMCHARS
tGTL.codepage = CP_ACP
'return number of TChars in RtichText control
lRet = SendMessageRef(m_hWnd, EM_GETTEXTLENGTHEX, tGTL, 0&)
Call apiZeroMemory(tGTT, Len(tGTT))
tGTT.cb = lRet + 1
tGTT.flags = GT_DEFAULT
tGTT.codepage = CP_ACP
tGTT.lpDefaultChar = Chr(164) '0&
tGTT.lpUsedDefChar = 0&
sBuff = String$(lRet + 1, 0)
lRet1 = SendMessageRef(m_hWnd, EM_GETTEXTEX, tGTT, sBuff) 'CRASH!!
If (lRet1 > 0) Then
sRTText = Left$(sBuff, lRet1)
End If
RE: Sendmessage and GETTEXTEX in vb/vba
VB string variable is a pointer to the memory location holding the string data. When you pass a string by reference, the API function actually receives a pointer to pointer and things go wrong. Modify the SendMessage call as follows.
lRet1 = SendMessageRef(m_hWnd, EM_GETTEXTEX, tGTT, ByVal sBuff)
Note that same thing applies to the first SendMessage call. You are passing 0 to lParam argument by reference. Although it is working in this case, as this value is not used, the correct way is to pass this 0 by value, as required.
lRet = SendMessageRef(m_hWnd, EM_GETTEXTLENGTHEX, tGTL, ByVal 0&)
Hope this helps.
RE: Sendmessage and GETTEXTEX in vb/vba
1. Declare ByRef and pass ByVal
Private Declare Function apiSendMessageGTT Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal msg As Long, wp As GETTEXTEX, ByRef lp As Long) As Long
lRet1 = apiSendMessageGTT(m_hWnd, EM_GETTEXTEX, tGTT, ByVal sBuff)
2. Declare ByVal and pass ByRef
Private Declare Function apiSendMessageGTT Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal msg As Long, wp As GETTEXTEX, ByVal lp As Long) As Long
lRet1 = apiSendMessageGTT(m_hWnd, EM_GETTEXTEX, tGTT, sBuff)
That brings a new difficulty – “type mismatch” when calling the SendMessage function. My arguments seem appropriately typed and I assume that the return value will come back as a long (“The return value is the number of TCHARs copied into the output buffer”). Everything compiles. In the meantime I've come across EM_GETTEXTRANGE and beat a tactical retreat. But I'd like to try both approaches and compare performance, so if you are still outthere and have any further suggestions, I'd like to hear them.
Cheers, Bill
Current CODE:
Dim tGTL As GETTEXTLENGTHEX
Dim tGTT As GETTEXTEX
Dim lRet As Long
Dim lRet1 As Long
Dim sBuff As String
Call apiZeroMemory(tGTL, Len(tGTL))
tGTL.flags = GTL_NUMCHARS
tGTL.codepage = CP_ACP
lRet = apiSendMessage(m_hWnd, EM_GETTEXTLENGTHEX, tGTL, ByVal 0&)
sBuff = Space$(lRet + 1)
tGTT.cb = Len(sBuff)
tGTT.flags = GT_DEFAULT
tGTT.codepage = CP_ACP
tGTT.lpDefaultChar = 0&
tGTT.lpUsedDefChar = 0&
lRet1 = apiSendMessageGTT(m_hWnd, EM_GETTEXTEX, tGTT, ByVal sBuff) ‘TYPE MISMATCH!!!
RE: Sendmessage and GETTEXTEX in vb/vba
I did not get online after my last above post. So thats the cause of the delay.
1. When you put the ByVal keyword in an API function call, it instructs the compiler to pass this argument by value, even if it is declared by reference, in the Declare statement.
The converse of this is not true. You cannot use ByRef keyword in an API function call to pass a ByVal argument by reference. Use of ByRef keyword gives a compile error.
2. You get the Type mismatch error not because of the return value of the function, but because you are passing a String (sBuff) when the function is expecting a Long (ByVal lp As Long).
I have seen many programmers using various forms of an API function using multiple Declare statements when they want to pass different kinds or arguments.
I also see this in your code, having various versions of SendMessage function: apiSendMessage, SendMessageRef, apiSendMessageGTT.
Sometimes I see: SendMessageRef, SendMessageVal, SendMessageStr, SendMessageLong etc.
I strongly oppose this practice. It creates obfuscation and repetition of same function with different names.
The VB's Declare statement is powerful enough that even a single function signature, if written properly, can handle all variations in its argument types.
Below is the declare statement I always use in my programs.
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Look at the declaration of last two parameters. When both ByVal and ByRef are missing, the argument is considered ByRef. Note the type of lParam is declared as Any. "Any" is a special keyword which disables type checking in a declare statement, thus eliminating the need to have so many variations of the same function. This allows you to pass a String, a Long, a UDT or an array using the same function declaration. Furthermore, the lParam argument is declared ByRef. This allows you to modify its status on the fly using the ByVal keyword wherever needed.
One question arises, how to pass tGTL user-defined type in place of wParam argument which is declared as Long? If you pass plain tGTL in place of wParam, it will give a "Type mismatch" compile error.
The answer is VarPtr() function. As the name implies, it returns the pointer (address) of the variable passed to it. This returned pointer (which is a Long value) can be passed as wParam without any problem.
So, both of your above function calls can be made using the same function declaration mentioned above.
lRet = SendMessage(m_hWnd, EM_GETTEXTLENGTHEX, VarPtr(tGTL), ByVal 0&)
...
lRet1 = SendMessage(m_hWnd, EM_GETTEXTEX, VarPtr(tGTT), ByVal sBuff)
Note that you can also declare wParam as Any which will give you more liberty allowing you to pass tGTL and tGTT directly, instead of using their pointers.
RE: Sendmessage and GETTEXTEX in vb/vba
Just a quick thanks for the excellent reply before I sit down to experiment and tinker.
Cheers, Bill