CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED but still in Brainstorming)
Hi everybody. I previously had some issues with WM_SETTEXT that are now fixed, thanks to some great help around here : ). I still have one more problem related to this though.... The problem is the CopyMemory function.
I don't know how exactly can I retrieve from memory the text that I receive with the WM_SETTEXT message. I send this message from a VC++ Application. If I send a short message (under 10 characters or so) it works. If I send a really long message (more than 50 characters), the below code crashes.
I think that "Len(lParam)" is not correct. Logically, that's a LONG so Len will always return 4 I guess. So how can I know how much memory to copy? It's a kind of recursive problem: I can't know how much memory to copy unless if I copy it and I can't copy it because I don't know how much to copy : |.
Anybody got an idea about this?
VB Code:
Public Function TheSubclassedWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim sString As String
If uMsg = WM_SETTEXT Then
CopyMemory sString, lParam, Len(lParam)
MsgBox sString, vbInformation + vbSystemModal, "Trying to get the value"
Exit Function
End If
TheSubclassedWndProc= CallWindowProc(loOriginalWindowProcedureAddress, hWnd, uMsg, wParam, lParam)
End Function
Thank you for reading anyway! : D.
Re: CopyMemory issue (WM_SETTEXT problem RELOADED)
You can turn the pointer into a string by copying to the address of the string.
VB Code:
Public Function PointerToString(ByVal lpString As Long) As String
Dim sTemp As String
'copy the pointer into the temporary string
CopyMemory ByVal VarPtr(sTemp), lpString, 4&
'since sTemp points to the original string it is an easy task to copy it
PointerToString = sTemp
'But we need to manually destroy the sTemp string to avoid a nasty GPF
CopyMemory ByVal VarPtr(sTemp), 0&, 4&
End Function
Simply pass lParam to this function to get the string.
Re: CopyMemory issue (WM_SETTEXT problem RELOADED)
All I get now is a row of question marks. I get a message box with "????????????????????????????????????". Maybe it's not formatted correctly with that function? As you can see, I just used the function you gave me there in the message box : ).
VB Code:
Public Function TheSubclassedWndProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Dim sString As String
If uMsg = WM_SETTEXT Then
MsgBox PointerToString(lParam), vbInformation + vbSystemModal, "Test"
Exit Function
End If
TheSubclassedWndProc= CallWindowProc(loOriginalWindowProcedureAddress, hWnd, uMsg, wParam, lParam)
End Function
This is the Visual C++ sender:
Code:
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(LPCTSTR) "Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. ");
Re: CopyMemory issue (WM_SETTEXT problem RELOADED)
Oh yeah. That's because in an NT based system (NT/200x/XP) the LPCTSTR is a wide unicode string so if you use it on a non Win9x/ME system you need to modify the function a little.
VB Code:
Public Function PointerToString(ByVal lpString As Long) As String
Dim sTemp As String
'copy the pointer into the temporary string
CopyMemory ByVal VarPtr(sTemp), lpString, 4&
'since sTemp points to the original string it is an easy task to copy it
PointerToString = StrConv(sTemp, vbFromUnicode)
'But we need to manually destroy the sTemp string to avoid a nasty GPF
CopyMemory ByVal VarPtr(sTemp), 0&, 4&
End Function
This might be a bit confusing since VB uses BSTR which is in Unicode format already.
Re: CopyMemory issue (WM_SETTEXT problem RELOADED)
Ain't workin'. Same "???" problem. Here are some things I tried:
Code:
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(BSTR) "Just a long string.");
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(LPCTSTR) "Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. ");
As you can see I also tried casting to BSTR. I knew that VB works with BSTRs but that casting didn't help. However, when I pass short values (like the first line of the above code) to VB, I also get one or two funny symbols in that message box. You know, weird symbols. Probably unicodes.
VB Code:
If uMsg = WM_SETTEXT Then
CopyMemory sString, lParam, Len(lParam)
MsgBox sString, vbInformation + vbSystemModal, "Working?"
MsgBox PointerToString2(lParam), vbInformation + vbSystemModal, "Working?"
Exit Function
End If
PointerToString2 = the new function you gave me.
When I comment the line with the message box with the new function, it works for short strings. For the first line of the first code I included here, it works. When I use longer strings, it crashes. If I comment my old copymemory and use only your function, I get question marks everywhere. No matter if I use short or long strings, BSTRs or LPCSTRs. I tried everything possible. Hm. Strange.
Here's the complete set of calls that I tried. Maybe it helps.
Code:
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(LPCSTR) "Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. ");
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(LPCSTR) "Just a long string.");
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(BSTR) "Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. ");
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(BSTR) "Just a long string.");
Re: CopyMemory issue (WM_SETTEXT problem RELOADED)
Maybe you could do this:
Code:
Private Declare Function lstrlenW Lib "kernel32" _
(ByVal lpString As Long) _
As Long
Public Function TheSubclassedWndProc(ByVal hWnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) _
As Long
Dim sString As String
Dim lStringLen As Long
If (uMsg = WM_SETTEXT) Then
lStringLen = lstrlenW(lParam)
sString = Space$(lStringLen)
CopyMemory sString, lParam, lStringLen
MsgBox sString, vbInformation + vbSystemModal, "Trying to get the value"
Exit Function
End If
TheSubclassedWndProc= CallWindowProc(loOriginalWindowProcedureAddress, hWnd, uMsg, wParam, lParam)
End Function
I know lstrlenW works with pointers because I only really pass pointers. I used to try to pass strings but I find pointers are much easier (especially when working with C++).
GRRRRRRRRRRRRRRRRRRRRR Visual Basic
Nope. Something still stinks here. I used your idea penagate but now when the CopyMemory tries to execute, my IDE crashes. I tried sending the message from Visual C++ like this:
Code:
SendMessage(hwndASDClockWindow, WM_SETTEXT, 0, (LPARAM)(LPCSTR) "Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string. Just a long string.\0");
And I also tried sending using casting to BSTR but I get the same result. What I can tell you is that lStringLen after I do lStringLen = lstrlenW(lParam) is equal to 98. Don't ask me why lStringLen is 98, since I measured the VC++ string that I'm sending and that is 181 characters long (179 if you don't consider the \0 at the end as two characters). I don't see the connection between 98 and 181 : ). It's not even double. So I don't know if this is a Unicode issue. It might be that the pointer isn't working properly? I don't know : |.
I also tried without the "\0". The MSDN documentation says that:
Quote:
"Security Alert Using this function incorrectly can compromise the security of your application. lstrlen assumes that lpString is a NULL-terminated string. If it is not, this could lead to a buffer overrun or a denial of service attack against your application."
I also tried hardcoding "181" instead of lStringLen. Ain't working.
Will I ever get rid of these WM_SETTEXT issues? Pf. Now that VB catches it, it's unable to get the data. How annoying : (.
Thanks for helping anyway : ). It's a lot nicer to crack a problem together with somebody else, no matter how far he/she is.
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED)
Welcome to the weird and (sometimes) wonderful world of VB and APIs :)
Sorry, I forgot you were sending the string from C++, so I used lstrlenW. The 'W' signifes a 'wide' string (Unicode, so dividing the ASCII length by 2) whereas the non-wide equivalent is the ASCII version which is as you found out the right one to use when the string is a C++ ASCII string.
Also, I stuffed up the string copying code. This version should work (a bit better at least :p):
Code:
Public Function TheSubclassedWndProc(ByVal hWnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) _
As Long
Dim strString As String
Dim lngStringLen As Long
If (uMsg = WM_SETTEXT) Then
If (lParam) Then
lngStringLen = lstrlen(lParam)
If (lngStringLen) Then
sString = Space$(lngStringLen)
CopyMemory ByVal strString, ByVal lParam, lngStringLen
End If
End If
MsgBox sString, vbInformation + vbSystemModal, "Trying to get the value"
Exit Function
End If
TheSubclassedWndProc = CallWindowProc(loOriginalWindowProcedureAddress, hWnd, uMsg, wParam, lParam)
End Function
I forgot the ByVal's before. HTH.
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED)
Hm. I don't think that byVal is important. Doesn't VB add that by its own? Anyway, I also used lstrlen and as I said, I got the same crash, except that it did return the proper amount of characters (179). I will try your new idea though... I'll tell you if it worked it a few minutes : ).
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED)
Quote:
Originally Posted by Axonn
Hm. I don't think that byVal is important. Doesn't VB add that by its own?
Nope, by default VB uses ByRef. As opposed to C++ which by default uses ByVal, unless you use a * character.
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED)
Hahahahaha. It worked!!!
WHY DID IT WORK??????? Only because of the ByVal! WEIRD DUDE! I've been using API in VB for some years, but this is way out there. Strange. I only added the ByVals and it worked straight out of the box. I didn't do any other modification. But I knew that VB passes using ByVal anyway. If it would have been byRef maybe I would have understood... but like this, I'm totally puzzled.
And why does it work with Space(lngStringLen)? Last night I was trying all sorts of things and I modified your Space$ with Space. What's the difference between Space$ and Space? I looked it up in MSDN but it doesn't say. I find only help related to Space, not to Space$.
Anyway Penagate dude, thanks a lot : D.
EDIT: Hm. Interesting about ByRef. I knew the opposite : ).
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED)
Quote:
Originally Posted by Axonn
Hahahahaha. It worked!!!
WHY DID IT WORK??????? Only because of the ByVal! WEIRD DUDE! I've been using API in VB for some years, but this is way out there.
ByVal is the most common way to pass parameters to API functions, you only use ByRef say with CopyMemory when you pass a pointer to an object. At all other times using CopyMemory you would use ByVal.
Although, myself, I'm not entirely sure why ByVal is used with the string destination parameter, surely it would be ByRef or ByVal StrPtr? but eh... :p
Quote:
But I knew that VB passes using ByVal anyway.
See my last post.
Quote:
And why does it work with Space(lngStringLen)? Last night I was trying all sorts of things and I modified your Space$ with Space. What's the difference between Space$ and Space? I looked it up in MSDN but it doesn't say. I find only help related to Space, not to Space$.
The $ character just tells VB to use the string-typed Space function, instead of the variant (default) function. It's more efficient and works with a lot of those string functions.
e.g. Left$ returns string, Left returns variant.
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED)
Quote:
Originally Posted by Me
ByVal is the most common way to pass parameters to API functions, you only use ByRef say with CopyMemory when you pass a pointer to an object. At all other times using CopyMemory you would use ByVal.
Maybe I should clarify this.
Use ByVal when you pass a memory address.
Use ByRef when you want to entice VB to pass a pointer.
For example, these two code snippets achieve the same result, but one using ByRef and the other using ByVal:
Code:
Dim lngDest As Long
Dim lngSource As Long
'---------------------------------
CopyMemory lngDest, lngSource, 4
'---------------------------------
Dim lngDestPtr As Long
Dim lngSrcPtr As Long
lngDestPtr = VarPtr(lngDest)
lngSrcPtr = VarPtr(lngSource)
CopyMemory ByVal lngDestPtr, ByVal lngSrcPtr, 4
Hope this clears it up a bit better :)
Quote:
Originally Posted by Axonn
Anyway, even with WM_COPYDATA, I'd probably have to use some string to pass the data and then parse it. Or was it working with custom made structures.... hm : ).
With WM_COPYDATA you pass data using a COPYDATASTRUCT type.
Quote:
Originally Posted by Axonn
Just in case you are wondering, I want to use WM_SETTEXT to pass data from VC++ to VB
I pass data between C++ and VB regularly. I use a C++ DLL (cos that's the only thing I know how to make in C++ :p) and store functions that can't be done in VB, in there. I find callbacks a fairly good way of passing data, even if they are a bit obscure they do work.
Example:
- VB app calls function in C++ DLL, passing address of callback function.
- C++ function calls callback function (maybe several times), passing data in the stack each time.
Or an even more spaghetti-like example:
- VB app calls function in VB DLL, passing address of callback function.
- VB DLL function calls callback function using function in C++ DLL, cos it can't be done in VB :sick:
- C++ DLL passes data back to VB app.
- Since callback function has to be in a module, an interface class is used to pass the data back to a form (if that's where it is needed).
Simple if you know what's going on, but not exactly straightforward :D
Pointers, Callbacks and sending data
About ByVal and ByRef: I'm not a beginner, I knew about those things (value and reference, pointer, etc). But I thought that VB passes ByVal by default, not ByRef.
And about the C++ DLL, we're very much alike here : ). This is just about all I know to do in VC++ too. I use a DLL to hook processes, which I can't do in VB. I did try the callbacks stuff, but I don't like it! You know why? It doesn't work right. THIS IS EXACTLY WHY I want to use WM_SETTEXT and stuff like that. I've been there: I made a VB application that calls a C++ DLL passing it the address of the callback. But the annoying thing is that I can't call the VB from C++ whenever I want to! I don't know why. I can call the VB function only when VB has just called a VC++ DLL function. Practically, I can't call the VB from a WM_TIMER callback procedure from within VC++. So I can call VB only when the two are somehow linked together. This problem was annoying. WM_SETTEXT allows me to send stuff whenever I want to.
Oh yeah, now I remember that I read last night about COPYDATASTRUCT. Hm. For me, it's worse than WM_SETTEXT. It does the same thing except it's more complicated. It is however safer I guess. And WM_SETTEXT is normally used for other stuff : ). For me it's not, 'cause I subclassed my VB application so I don't care what it does originally : ).
Re: Pointers, Callbacks and sending data
Quote:
Originally Posted by Axonn
About ByVal and ByRef: I'm not a beginner, I knew about those things (value and reference, pointer, etc).
OK, well hopefully my post might clear some things up for beginners then... if they read it :)
Quote:
I've been there: I made a VB application that calls a C++ DLL passing it the address of the callback. But the annoying thing is that I can't call the VB from C++ whenever I want to! I don't know why. I can call the VB function only when VB has just called a VC++ DLL function.
That's strange. Can you post the C++ code you were using to do that? I can take a look at it... I was trying to do something like that just now, but I kinda suck with C++ and for some strange reason the function doesn't want to export. :mad:
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED)
*laugh* I'm really "smart" today. I spent 2 minutes wondering why a message box ain't appearing. Hehe. I forgot to build the VB exe and copy it in the Debug folder of my VC++ DLL project : >.
And then again, I really am smart today. It's wierd. 'cause this time it worked *laugh*. Hm. Perhaps because I'm using a byte variable? Some while ago I was using a string and that didn't work, I'm sure of it. I'm gonna remake my code and try to pass a string. Who knows, maybe today I'm more in shape.
Here's the code, but unfortunately, it works : D. It's not the first time in my life I'm pissed off that something WORKS. Sorry for the delay, I had to change my code in order not to post useless stuff. I made a few new functions to remove my usual tasks in it and show you only the relevant code. Hm. I'm gonna try makin' it with string after this.
1. Visual Basic Declarations & Click event of the "Test" button.
VB Code:
Private Declare Function ASDSetExternalAddress Lib ".\Debug\ASDClock.dll" (ByVal NewVal As Long) As Long
Private Sub cmdTest_Click()
ASDSetExternalAddress AddressOf Testing
End Sub
2. Visual Basic Module
VB Code:
Public Sub Testing(ByVal byTest As Byte)
MsgBox byTest, , "Testing callbacks"
End Sub
3. Visual C++ Code
Code:
long extProc;
VOID CALLBACK TestTmrProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
typedef void (__stdcall *OutsideFunction)(BYTE byAByteVariable);
OutsideFunction TestFunctionCall;
TestFunctionCall = (OutsideFunction)extProc;
TestFunctionCall(22);
}
void WINAPI ASDSetExternalAddress(long ExtAddress)
{
extProc = ExtAddress;
ctRefreshTimerID = SetTimer(NULL, NULL, 2000, (TIMERPROC) TestTmrProc);
}
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED)
Hmm... I don't specify "CALLBACK".
here's one of my functions. I call it from a VB DLL to call the callback proc in the VB app. :cool:
Code:
long __stdcall CallProgressCallback( void* lpfnProcAddress,
long lpszStatus,
long dwProgress )
{
typedef long (__stdcall* ProcAddress)(long arg1, long arg2);
ProcAddress _lpfnProcAddress = (ProcAddress) lpfnProcAddress;
return _lpfnProcAddress(lpszStatus, dwProgress);
}
Also, I don't think you can pass strings very well, I just use pointers instead.
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED)
Nice, glad it works :) Sorry about the delay in replying.
I figured out my exportation issues, turned out I was building it to the wrong DLL... oops :blush:
Anyway I tried your timer function thing and it worked for me too. I did make some slight modifications though... not sure if they are any better than the code you had. I'm still learning C++ :p
Code:
typedef long (__stdcall* ProcAddress)(BYTE byAByteVariable);
ProcAddress extProc;
long ctRefreshTimerID;
VOID CALLBACK TestTmrProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
extProc(22);
}
void WINAPI ASDSetExternalAddress(long ExtAddress)
{
extProc = (ProcAddress) ExtAddress;
ctRefreshTimerID = SetTimer(NULL, NULL, 2000, (TIMERPROC) TestTmrProc);
}
Any good?
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED)
Sorry to jump into the discussion so late. I know it's resolved, but I have a few comments that might shed some light in the future.
1. The reason you have to use ByVal when passing a string pointer from VB to a C++ dll is that VB strings are not stored the same way as C++ strings. ByVal actually forces VB to pass the pointer to the sting and in addition adds a chr(0) to the end of the string so that it looks exactly like a C style string.
2. One way to copy the string sent to VB from a C-dll or API is to copy each character one at a time until the terminating chr(0) is reached. The lstrlen API function calculates the length of a string by looking for this termination character.
3. Callbacks from C to VB can be tricky especially from a seperate thread. This is because when VB creates threads, each thread acts as if it were an application of its own. Each thread gets its own copy of global variables, the Err object and the App object. The information for the location of these objects is stored in each thread's thread local storage (TLS). See this link for more info
http://www.vbforums.com/showthread.php?t=327589
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED)
Quote:
Originally Posted by moeur
1. The reason you have to use ByVal when passing a string pointer from VB to a C++ dll is that VB strings are not stored the same way as C++ strings. ByVal actually forces VB to pass the pointer to the sting and in addition adds a chr(0) to the end of the string so that it looks exactly like a C style string.
That sounds right, I did actually know that once. :p
Quote:
3. Callbacks from C to VB can be tricky especially from a seperate thread. This is because when VB creates threads, each thread acts as if it were an application of its own. Each thread gets its own copy of global variables, the Err object and the App object. The information for the location of these objects is stored in each thread's thread local storage (TLS). See this link for more info
http://www.vbforums.com/showthread.php?t=327589
But that I didn't know. Interesting stuff.
Re: CopyMemory issue (WM_SETTEXT problem - ADVANCED) (SOLVED but still in Brainstormi
Quote:
Originally Posted by Axonn
As long as it does the job and it's safe, USE IT : D.
Take a peek at the "Sending strings between VB apps" link in my sig. That's an example of a nice hacky method of achieving results :p It's also easier to receive them that way instead of using subclassing, but if you're subclassing anyway there's not all that much point.