Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Sendmessage and GETTEXTEX in vb/vba 1

Status
Not open for further replies.

formerTexan

Programmer
Apr 10, 2004
504
0
0
US
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
 
You need to pass sBuff string buffer by value instead of by reference as you are currently doing.

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.
[tt]
lRet1 = SendMessageRef(m_hWnd, EM_GETTEXTEX, tGTT, ByVal sBuff)
[/tt]
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.
[tt]
lRet = SendMessageRef(m_hWnd, EM_GETTEXTLENGTHEX, tGTL, ByVal 0&)
[/tt]
Hope this helps.
 
Thank you Hypetia for the response. Making the suggested correction has resolved the crashing application. And I appreciate the explanation on “pointer to a pointer”. If I am following it correctly, then both of the following lParam variations should be functionally equivalent in this situation.

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!!!
 
Bill,

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.
[tt]
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
[/tt]
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.
[tt]
lRet = SendMessage(m_hWnd, EM_GETTEXTLENGTHEX, VarPtr(tGTL), ByVal 0&)
...
lRet1 = SendMessage(m_hWnd, EM_GETTEXTEX, VarPtr(tGTT), ByVal sBuff)
[/tt]
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.
 
Hypetia

Just a quick thanks for the excellent reply before I sit down to experiment and tinker.

Cheers, Bill
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top