Q. Pick Pointers Apart
When an API hands me a pointer, how can I determine what it actually points to? I think the SDK documentation might be incorrect in places, but VB offers me no way to view a given chunk of memory to find out what's really there.
ABOUT THIS COLUMN
Ask the VB Pro provides you with free advice on programming obstacles, techniques, and ideas. Read more answers from our
crack VB pros. You can submit your questions, tips, or ideas on the site, or access a comprehensive database of previously answered questions.
A. Last month, I described exactly the same situation with the DsGetDcName API function (see the Ask the VB Pro column, "Add a Window to the Taskbar," VBPJ January 2001). The last parameter to this function is documented as returning "a pointer to a variable that receives a pointer to a structure…," but a little digging reveals the function actually returns a pointer to the structure itself. If you simply trust the SDK docs and try to double-dereference the returned value, you end up frustrated with the garbage results. I sure was.
Using the pointer returned from DsGetDcName as an example, the question becomes: What do you actually need to extract from memory? The DO-MAIN_CONTROLLER_INFO structure itself is composed mostly of pointers to strings, which translate to VB most cleanly as Long elements:
'typedef struct _DOMAIN_CONTROLLER_INFO
' LPTSTR DomainControllerName;
' LPTSTR DomainControllerAddress;
' ULONG DomainControllerAddressType;
' GUID DomainGuid;
' LPTSTR DomainName;
' LPTSTR DnsForestName;
' ULONG Flags;
' LPTSTR DcSiteName;
' LPTSTR ClientSiteName;
'} DOMAIN_CONTROLLER_INFO, *PDOMAIN_CONTROLLER_INFO;
Private Type DOMAIN_CONTROLLER_INFO
DomainControllerName As Long
DomainControllerAddress As Long
DomainControllerAddressType As Long
DomainGuid As GUID
DomainName As Long
DnsForestName As Long
Flags As Long
DcSiteName As Long
ClientSiteName As Long
Knowing what to look for is always the best way to narrow a search. (Duh.) VB distinctly lacks any mechanism to view raw memory contents. To solve the problem, you need a routine such as the one I wrote, which copies a requested number of bytes from any location in memory and formats them for display in a familiar Hex/ASCII representation (see
In the DsGetDcName example, I passed the lpDomain-Control-lerInfo pointer to HexDump. It became clear immediately that the SDK docs were incorrect. The first 48 bytes at the location lpDomain-ControllerInfo pointed to corresponded perfectly with the layout and content I would expect to find for a DOMAIN_CONTROL-LER_INFO structure. Furthermore, the pointer elements within this structure were pointing directly to the expected values, which I found immediately following the structure itself.
In one sample run, lpDomainControllerInfo returned &h1AC998, and HexDump was used to view the first 256 bytes at that location (see
Figure 1). The first four bytes, representing the Domain-ControllerName element, are "C8 C9 1A 00". When you rearrange these bytes to account for Intel architecture byte ordering and make an address out of them, the address is &h1AC9C8. Sure enough, scanning ahead in the hex dump to that address, you find the Unicode string "\\NT50-SVR.lj.home", which is the primary domain controller's name. The next four bytes point to the location immediately following this string. The four bytes after those"02 00 00 00"are not a pointer, but rather an unsigned Long value for the DomainControllerAddressType. The pattern continues with the GUID directly within the structure, then four more valid pointers, and finally a Null pointer.
Figure 1 | In this example, the lpDomainControllerInfo parameter of DsGetDcName returns a value of &h1AC998, which is passed to the HexDump routine in Listing 1 to produce this output.
Having a window into raw memory can be absolutely invaluable for deciphering the results of undocumented function calls, or worse, misdocumented ones. This technique is also useful for better understanding how both Windows and VB organize the memory your application uses. The issue of DWORD alignment is illustrated here beautifully, because the first two strings each have an odd number of characters, so the following string doesn't start until the next four-byte boundary, leaving two meaningless bytes between.
The standard caution applies for cases like this: Always save your work first to prevent potential losses due to access violations.
Q. Keep Responding to MouseDown
I'd like to have a single routine called repeatedly for as long as the user keeps the mouse button depressed over some command buttons. What's the best way to accomplish this?
A. You usually handle this sort of task with a timer. Enable the timer when the user first presses the command button. On each timer tick, call the code that handles the chore associated with that command button. Disable the timer when the user releases the mouse button.
As is so often the case, only a few small details require your attention to make this as smooth as possible. If you're providing this functionality for a number of command buttons on your form, I suggest grouping them into a control array. Create a Command-Button object at the module level so it's visible throughout the form. Before enabling the timer, set a reference to the pressed command button into this object on the MouseDown event (see Listing 2).
You'll also want to isolate the code to be called into a procedure of its own. This step allows you to write the code more generically so you can handle controls potentially from separate arrays, or even from different forms, depending on your needs. In this case, the example routine accepts the Index property of the command button as a parameter. You can use the Index within a Select Case or If block to ensure the proper code executes for the selected button. Make sure to also call the ButtonAction routine from MouseDown if you don't want to wait for one full timer interval before this code executes.
Experiment with different interval settings for the timer, from 10ms or 50ms for extremely fast repeat action, up to 250ms or even 500ms for slower action.
Karl E. Peterson is a GIS analyst with a regional transportation-planning agency and serves as a member of the Visual Basic Programmer's Journal Technical Review and Editorial Advisory Boards. Online, he's a Microsoft MVP and a section leader on several VBPJ forums. Find more of Karl's VB samples at