Q. Need DLL Initialize and Terminate Events
I've written an ActiveX DLL for which I have rather rigorous startup and shutdown requirements. On startup, I need to set up an API timer callback and instantiate a few shared objects. On shutdown, I must disable the timer callback to avoid GPFs. Are there any reliable ways to create Lib_Initialize and Lib_Terminate events?
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.
I've had requirements similar to yours and wished ActiveX DLLs offered both these events automatically. VB provides a native approach to detect library startup, but no corresponding method exists for shutdown.
Add a Sub Main to one of your BAS modules and set this as the startup procedure for the project. This routine is the first called after the DLL is loaded, and you can use it to initialize data, objects, or system callbacks that the library requires. In many circumstances, this routine would be sufficient. However, it supplies only half the answer in cases such as yours that require strict cleanup.
Here's a kludge that detects library termination extremely well. In your Sub Main, declare a static instance of a class. Because it's static, this class won't terminate until the library itself is unloading:
Public Sub Main()
Static Lib As CLibEvent
Set Lib = New CLibEvent
With this scheme, you can use the CLibEvent class to signal startup and shutdown for the entire library. Wrap up all your library housekeeping by performing all initialization chores during the Class_Initialize event, and all shutdown chores during the Class_Terminate event. (Thanks to Craig Clearman for posting this solution in the DevX discussion newsgroupsnews://news.devx.com.)
Q. React to Highlight Changes in Combo Box
I'm stumped. I have a combo box and a textbox on a form. I can write corresponding data into the textbox easily, when the user clicks on an item in the combo box. However, I can't find a method or message that lets me write the corresponding data into the textbox as the user moves the mouse along the list and each item in the combo box is highlighted prior to actual selection.
That's kind of a tough one, because no events or messages relate directly to the mouse traversing a list. In cases such as this, firing up Spy++which ships with Visual Studioprovides the necessary clues.
As you watch the messages that fly by as a user ponders a combo box choice, one message stands out: The control receives a WM_CTLCOLORLISTBOX message with each change in a highlighted item. The straightforward answer to your question is to subclass the combo box, watch for WM_CTLCOLORLISTBOX, and update your textbox each time this message is received.
However, the straightforward answer isn't always the most practical. Although subclassing certainly solves this problem, it seems like overkill unless you're already using that technique for other aspects of your application.
You can easily implement another extremely low-tech solution. Place a timer on your form and set its Interval so that it fires two to four times a second. Leave the timer disabled by default, but enable it when the combo box's DropDown event fires. On each timer tick, update your textbox using the combo box's current ListIndex. Disable the timer again and update your textbox one last time, when the combo box's Click event fires.
Here's an example:
Private Sub Combo1_Click()
Timer1.Enabled = False
Text1.Text = _
Private Sub Combo1_DropDown()
Timer1.Enabled = True
Private Sub Timer1_Timer()
Text1.Text = _
This solution meets your requirements perfectly and should be a cinch to implement. Sometimes, low-tech is best.
Q. Determine Host Filename and Path
I'm developing a UserControl that needs to know the App.Path of the parent application. Is there a property I can check?
Nothing built-in. But that just makes for an interesting challenge, doesn't it? One common answer I've seen to this problem is to require your client to provide this information through a property setting. I strive to avoid this solution in all circumstances, because each additional requirement you place on users increases the chances of dissatisfaction with your component.
In this case, one simple API call returns the information you need. When you pass Null as the module handle to GetModuleFileName, this function returns the path and name for the file that created the calling process. When you call GetModuleFileName from a UserControl, the function returns a string pointing at your host's EXE file.
As with nearly all APIs that return string data, you're required to preallocate a buffer that's filled upon return. Size a string buffer to MAX_PATH length before calling GetModuleFileName. On return, truncate the buffer at the position immediately before the first Null character, for the fully qualified filespec to your host (see Listing 1).
Q. Control Mouse in Demo
I've been asked to provide a demo mode for an application, and I'm stuck on one point. It's easy enough to move the cursor using the SetCursorPosition API, but how can I send a Click event to any given control?
Another API solves this problem quite nicely. The mouse_event API provides the means to move the mouse, press its buttons, and turn its wheel, all from within your code, to or at any position on the screen.
This API can be a bit confusing at first, because it offers two modesrelative and absoluteboth of which have idiosyncrasies. The relative mode moves the cursor a given distance from its current location. However, this distance isn't specified directly; it's derived based on what you request, and it's subject to unpredictable modification due to user settings for mouse speed and acceleration. The absolute mode moves the cursor to an absolute position within a scaled mapping of the screen. For the most control with the least confusion, I recommend sticking with mouse_event's absolute mode.
In absolute mode, the screen is mapped to 64K units along both the X and Y axes, so (0,0) represents the upper-left corner and (65535,65535) represents the lower-right corner. Use the GetSystemMetrics API to determine actual screen dimensions. With these values known, you can calculate easily the mapping for any given position on screen:
' Map into same coordinate space used by mouse_event.
X = (X / GetSystemMetrics(SM_CXSCREEN)) * &HFFFF&
Y = (Y / GetSystemMetrics(SM_CYSCREEN)) * &HFFFF&
As with nearly all API calls, mouse_event works strictly with pixel-based units. To move to a specific control, you need to know only its hWnd. Passing this handle to GetWindowRect allows calculation of the center point. I recommend using mouse_event's absolute mode for movement to a known location such as this. Then use the relative mouse_event mode to click on that spot.
A simulated click requires two calls to mouse_eventone for button down and another for button up. To simulate the same visual cues a user receives when clicking on a window, follow the button down event with a DoEvents call, which allows a screen update, then a brief sleep period of a quarter-second or so to give the user a chance to see the effect before issuing the button up event (see Listing 2).
For the most interesting effects, you might want to experiment with calculating a path to your target and moving the cursor a little bit at a time on Timer events. You can
download the sample project. The sample project causes the cursor to circle-in on a target before clicking on it, illustrating the sort of fun you can have with these effects.
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 www.mvps.org/vb.