Using the Clarion Debugger to examine memory and follow memory offsets

Over in this thread, (Using WTSQuerySessionInformation to get client computer name when running in a remote session) a few of us are discussing the use of WTSQuerySessionInformation. This API returns a address that is the offset to the return information. In this case, the return information was a null terminated string (CSTRING) and it was uncertain if the returned buffer length included the NULL character or not.

I fired up the debugger to examine the data and thought it might be interesting for others to see how I did it.
Turns out the buffer length does include the NULL character.

First, compile your application in debug mode.
I started by putting a breakpoint right after my API call.
When the debugger stops on your line of code, go to the Stack Trace window and right click on the variable containing your memory offset and pick Examine Memory. In this case BufferPtr.
image

This opens a window containing the memory of your variable.

When a memory location contains the offset of another memory location, you can use the debugger to jump to that offset by right clicking on the first byte of the memory and choosing Locate Offset.

image

This opens a little window with the memory offset from the original locations contents. Notice that it automatically handles for you that the original memory location had the offset in little endian form and converts it for you. In the first screenshot the byte values are 98 B4 D5 00, but the locate offset window changes that to 00 D5 B4 98 for you.

Press OK to go to the new offset and you see the new memory location that contains the data. In this case, it is the results of our API call which is my computer name DIMHOLT.
image

Voila, we used the debugger to dive into the memory offsets returned by the API call.
Our original question, was did the returned Buffer Length include the NULL terminating character.
We can see here that the buffer length returned is 8, which is 1 more than the 7 character computer name. My original code made the incorrect assumption that the buffer length did not contain the NULL character, so my code was allocating 9 characters for the return value. You can see below that was not necessary and there are two NULL characters at the end of the CSTRING.

5 Likes

Yes, that is how I do it as well. :sunglasses:

It works as long as you don’t get directed to memory that the debugger is not allowed to see. Which is rare, but can happen.

As long as the destination of the name is > 8 then it can be as long as you want. Doesn’t matter if it’s 8,9 or 900 bytes.

Hey Paul, I’m not sure what you’re saying?

Whether your call to New() allocates 8 or 9 bytes doesn’t matter, as long as it’s more than the data you want to send.
And if you grew up calling OutputDebugString when New/Dispose didn’t exist then just allocate a local string on the stack.

Capture

Rick that is an excellent post, I always want to learn more about the debugger. Many of its features are hidden.

But I was thinking more like a message or debug out :upside_down_face:

    BufferRef &= new(cstring(BufferLength+1))
    memcpy(address(BufferRef), BufferPtr, BufferLength)
    MESSAGE('WTS val [' & BufferLength & ']=' &VAL( BufferRef[ BufferLength ] ) )
1 Like