Tips and Triks Visual Basics (VB)

  • These tips & Tricks are donated by Microsoft.nl They provides examples of how you can solve a particulair problem with Visual Basic. They are free to use and free to re-publish.
Tip 63: Preventing List Box from Redrawing (Refreshing)
Tip 64: Changing the Case of Text Entered in a Text Box Control
Tip 65: Separating a Path into Individual Fields
Tip 66: Disabling Task-Switching with the SetSysModalWindow Function
Tip 67: Returning Focus to a Specific Control After Executing WinHelp
Tip 68: Removing Duplicate Items from List Box Controls
Tip 69: Forcing a Picture Control to Use Only a Specific Font
Tip 70: Creating Temporary Files
Tip 71: Dragging Controls at Run Time
Tip 72: Positioning the Cursor over a Control That Receives Focus
Tip 73: Creating Nested Directories
Tip 74: Scrolling Through Two List Box Controls Simultaneously
Tip 75: Invoking Menu Items in Other Applications with SendMessage
Tip 76: Detecting Right Mouse Button Clicks on List Box Controls
Tip 77: Determining the Amount of RAM Installed in a Computer
Tip 78: Retrieving the Names of All Printers
Tip 79: Closing All MDI Child Windows at One Time
Tip 80: Drawing Borders Around Controls
Tip 81: Repairing and Compressing a Microsoft Access Database from
Tip 82: Retrieving Multiple Filenames from the Common Dialog Control
Tip 83: Listing Fields and Associated Properties for an Attached
Tip 84: Creating a Scrolling "Credits" Control
Tip 85: Hiding MDI Child Forms at Run Time
Tip 86: Allowing a Visual Basic Application to Accept Drag-and-Drop
Tip 87: Sending Output to the Printer in Any Order
Tip 88: Shrinking Icons Down to Size
Tip 89: Invoking the Microsoft Word Page Setup Dialog Box
Tip 90: Adding ToolTips to Visual Basic applications
Tip 91: Determining If a Form Is Loaded
Tip 92: Using the Shell Statement to Execute MS-DOS Programs
Tip 93: Retrieving the Names of Installed Printers
Tip 94: Using Drag-and-Drop on Multiple items in a List Box Control
Tip 95: Deleting All Records from Every Table in a Microsoft Access Database
Tip 96: Centering a Form over Another Form
Tip 97: Creating a Task List
Tip 98: Saving a Window's Client Area in Bitmap Format
Tip 99: Adding Three-Dimensional Effects to Visual Basic Forms
Tip 100: Printing a Form Multiple Times on One Page
Tip 101: Using the Built-In Windows Icons
Tip 102: Modifying a Child Window's Caption
Tip 103: Preventing the CTRL+TAB and CTRL+F6 Key Combinations from Activating
Tip 104: Creating a Form with a Thin Title Bar
Tip 105: Removing a Form's Title Bar
Tip 106: Centering Text Vertically in a Text Box Control
Tip 107: Detecting Double-Click Events in Combo Box Controls
Tip 108: Flashing Controls to Get the User's Attention
Tip 109: Modifying an MDI Form's Border Style
Tip 110: Sending a Click Event to a Command Button Control
Tip 111: Using Different Fonts in List Boxes That Have Tab Stops
Tip 112: Preventing ListIndex Property from Triggering a Click Event
Tip 113: Creating Transparent Forms
Tip 114: Preventing a Portion of a Text Box from Scrolling
Tip 115: Performing Smart Searches in Combo Box Controls
Tip 116: Sending Data to the Printer in Landscape or Portrait Mode
Tip 117: Changing a Menu's Shortcut Key at Run Time
Tip 118: Converting a Word for Windows Document to RTF Format
Tip 127: Stamping Files with the Current Date and Time
Tip 128: Calculating the Beginning and Ending Date of a Month
Tip 130: Using the Undo Feature with a Text Box Control
Tip 131: Determining Whether a DLL File Is 16-Bit or 32-Bit
Tip 132: Preventing Duplicate Items from Being Added to List Box Controls
Tip 133: Using the SendMessage Function to Scroll Contents of Text Box =
Tip 134: Creating Temporary Files
Tip 135: Preventing a User from Editing the Contents of a Text Box =
Tip 136: Using Keyboard and Mouse Shortcuts
Tip 137: Removing Items from a Drive List Box Control
Tip 138: Enabling and Disabling Scroll Bars in a List Box
Tip 139: Retrieving a Disk's Volume Label
Tip 140: Identifying CD-ROM Drives
Tip 141: Searching a List Box Control for a Partial Match
Tip 142: Determining Whether the Operating System Supports Long File
Tip 143: Determining Whether the Windows 95 Taskbar Is Visible or=20
Tip 144: Using Accelerator Keys with the TabStrip Control
Tip 145: Inserting Tab Characters in the Rich-Text Box Control
Tip 146: Retrieving the Printer Name from the Windows 95 Registry
Tip 147: Retrieving Multiple File Names from the Common Dialog Control
Tip 148: Using OLE Automation to Check Spelling
Tip 149: Enumerating Disk Drives in Visual Basic 4.0
Tip 150: Determining How Many Items Can Be Displayed in the List Box
Tip 151: Finding Whole Words in a Text Box Control
Tip 152: Determining the Status of Virtual Keys on the Keyboard
Tip 155: Scrolling Text Horizontally in a Picture Box Control
Tip 156: Adding New Icons to the Windows 95 Taskbar
Tip 153: Searching PATH for Specific Files
Tip 154: Terminating Windows 95 in Visual Basic
Tip 159: Setting the Minimum Size of a Window
Tip 160: Retrieving the Versions of MS-DOS and Windows
Tip 161: Forcing the Common Dialog Control to Save Files to a Specific Disk Drive
Tip 162: Determining Whether a File Exists
Tip 163: Emulating a Double-Click Event in the Directory List Box Control
Tip 164: Copying Files from One Directory to Another
Tip 165: Locating CD-ROM Drives Installed on a Computer System
Tip 166: Playing .WAV Files in Visual Basic
Tip 167: Controlling the State of Virtual Keys on the Keyboard
Tip 168: Using the ShellExecute Function to Print Files
Tip 169: Setting the Position and Size of the Windows Help File
Tip 170: Positioning the Mouse Pointer over a Specific Control
Tip 171: Determining RGB Color Values
Tip 172: Extracting the Directory Name and the Filename from the Path
Tip 173: Launching Applications in Visual Basic
Tip 174: Using the GetKeyState Function to Determine the State of
Tip 175: Determining the Current Screen Resolution
Tip 176: Sending Files to the Recycle Bin in Visual Basic 4.0
Tip 177: Adding Drag-and-Drop Functionality to Your Application
Tip 178: Enabling or Disabling Fast Task Switching in Windows
Tip 179: Retrieving the Computer Name
Tip 180: Performing Searches in a Combo Box Control with SendMessage
Tip 182: Temporarily Enabling or Disabling Tabs on the TabStrip Control
Tip 183: Changing the Color of the Grid Control's Cells
Tip 184: Creating a List of Directories and Files in a List Box Control
Tip 185: Determining the Number of Printable Lines Per Page on the Printer
Tip 186: Formatting Text in a Rich Text Box Control in Visual Basic 4.0
Tip 187: Determining Whether an Application Was Closed from the Control Menu
Tip 188: Adding New Commands to the Control Menu
Tip 189: Determining Which Screen and Printer Fonts Are Available
Tip 190: Adding Hot Key Access to Your Visual Basic Application
Tip 191: Shelling to Other Applications
Tip 192: Selecting a New Desktop Wallpaper
Tip 193: Launching Windows 95 Control Panel Applets in Visual Basic
Tip 195: Modifying a File's Date and Time Stamp in Visual Basic 4.0
Tip 196: Using the Common Dialog Control to Invoke Context-Sensitive Help
Tip 197: Preventing a Right-Click from Displaying a Context Menu
Tip 198: Retrieving File Information in Visual Basic 4.0
Tip 199: Saving the Contents of the List Box Control to Disk in Visual
Tip 200: Determining the Amount of Free Disk Space
Tip 201: Retrieving the Task List in Windows 95
Tip 202: Forcing a Combo Box Control to Drop Down with the ENTER Key
Tip 203: Searching a Disk for Files Based on Filename and Path
Tip 204: Changing the Screen Resolution at Run Time in Visual Basic 4.0
Tip 205: Drawing a Gradient Background Pattern on Forms in Visual Basic 4.0
Tip 206: Populating the Outline Control with a List of Directories and
Tip 208: Changing the Displayed Icon in the About Dialog Box in
Tip 209: Changing the Color of a Label Control When the Mouse Pointer Is

Tip 110: Sending a Click Event to a Command Button Control

Abstract
In a Microsoft(r) Visual Basic(r) application, you can simulate a Click =
event to
a Command Button control. This article explains how to send a BN_CLICKED
notification message to a control.

Executing a BN_CLICKED Message
A user who wants to carry out a command in your Microsoft(r) Visual =
Basic(r)
application usually clicks a Command Button control. The code in the =
Command
Button's Click event is then executed.

There may be times, however, when you will want to initiate a Click =
event from
within your Visual Basic program. You can use the Microsoft Windows(r) =
application
programming interface (API) PostMessage function to send a BN_CLICKED =
notification
message to the parent of the Command Button control. This will call the =
button's
Click event.
As you can see from the example program below, the GetDlgCtrlID function
retrieves the Command Button's handle. Next, a call is made to the =
GetParent
function, which retrieves the handle of the window that the Command =
Button resides
on. (In other words, we must retrieve the parent window's handle.)

The last step is to execute a PostMessage function. PostMessage sends a =
BN_CLICKED
notification message to the parent window, which then processes the =
Click event
for the Command Button.
When you run the example program below, the second Command Button's =
Click event
is executed. However, the second Command Button does not receive the =
focusonly its
code is executed.

Example Program
This program shows how to send a Command Button click to the Windows =
operating
system.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following Constant and Declare statements to the General =
Declarations
section of Form1 (note that each Declare statement must be typed as =
a single
line of text):

Const BN_CLICKED =3D 0
Const WM_COMMAND =3D &H111
Private Declare Function GetDlgCtrlID Lib "User" (ByVal hWnd As Integer) =
As
Integer
Private Declare Function GetParent Lib "User" (ByVal hWnd As Integer) As =
Integer
Private Declare Function PostMessage Lib "User" (ByVal hWnd As Integer, =
ByVal
wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Long) As =
Integer

3. Add a Command Button control to Form1. Command1 is created by =
default. Set
its Caption property to "Send".
4. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
ClickButton Command2.hWnd
End Sub

5. Add a second Command Button control to Form1. Command2 is created by =
default.
Set its Caption property to "Receive".
6. Add the following code to the Click event for Command2:

Private Sub Command2_Click()
MsgBox "Command2 was CLICKED!"
End Sub

7. Create a new function called ClickButton. Add the following code to =
this
function:

Sub ClickButton(ByVal hWnd As Integer)
Dim Button As Integer
Dim ParentHwnd As Integer
Dim X As Integer
=20
Button =3D GetDlgCtrlID(hWnd)
ParentHwnd =3D GetParent(hWnd)

X =3D PostMessage(ParentHwnd, WM_COMMAND, Button, BN_CLICKED * =
&H10000 + hWnd)
End Sub

Run the example program by pressing F5. Click the Send Command Button. =
The Click
event for the second Command Button control is immediately executed (the =
message
box is displayed).

Additional References
"BN_CLICKED." (Development Library, Product Documentation, SDKs,
Windows 3.1 SDK, Programmer's Reference Volume 3: Messages, =
Structures)
"GetDlgCtrl." (Development Library, Product Documentation, SDKs,
Windows 3.1 SDK, Programmer's Reference Volume 2: Functions)
PostMessage." (Development Library, Product Documentation, SDKs,
Windows 3.1 SDK, Programmer's Reference Volume 2: Functions)

Tip 109: Modifying an MDI Form's Border Style

Abstract
This article explains how you can create a multiple-document interface =
(MDI) form
that has a fixed border in a Microsoft(r) Visual Basic(r) application.

Retrieving and Setting a Form's BorderStyle
Every form you create when designing a Microsoft(r) Visual Basic(r) =
application
can have one of four border styles. Just set the form's BorderStyle =
property to
one of the following styles:
0 - None
1 - Fixed Single
2 - Sizeable
3 - Fixed Double

An MDI child form, however, does not have a BorderStyle property. But by =
using the
Microsoft Windows(r) GetWindowLong and SetWindowLong application =
programming
interface (API) functions , you can change an MDI form's border style to =
a fixed
border style.
The GetWindowLong function retrieves information about the specified =
window's
style attributes and the SetWindowLong function modifies the specified =
window's
style attributes.

GetWindowLong requires only two arguments. The first argument is the =
target
window's handle. The second argument specifies the type of information =
you want
to retrieve, which is the style settings for the window.
After retrieving the window's current style settings, use the bitwise =
And Not
function to remove the WS_THICKFRAME attribute from the style settings =
value.
Next, issue a call to the SetWindowLong function to set the new style =
settings
for the specified window. This creates an MDI form that has a fixed =
border style.


Example Program
This program shows how to create an MDI form that has a fixed border. =
Run the
example program by pressing F5. The MDI form will be displayed with a =
fixed
border.

1. Create a new project in Visual Basic. Form1 is created by default.
2. From the Insert menu, select MDI Form to create an MDI form. =
MDIForm1 is
created by default.
3. Set Form1's MDIChild property to True. Modify the size of this form =
so that
it is smaller than the MDIForm1 form.
4. Add the following Constant and Declare statements to the General =
Declarations
section of Form1 (note that each Declare statement must be typed as =
a single
line of text):

Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As =
Integer, ByVal
nIndex As Integer) As Long
Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As =
Integer, ByVal
nIndex As Integer, ByVal dwNewLong As Long) As Long
Const GWL_STYLE =3D (-16)

Const WS_THICKFRAME =3D &H40000

5. Add the following code to the Load event for MDIForm1 (note that the =
NewStyle
line must be typed as a single line of code):

Private Sub MDIForm_Load()
Dim CurStyle As Long
Dim NewStyle As Long
CurStyle =3D GetWindowLong(MDIForm1.hWnd, GWL_STYLE)
NewStyle =3D SetWindowLong(MDIForm1.hWnd, GWL_STYLE, CurStyle And
Not (WS_THICKFRAME))
End Sub

Additional References

"GetSystemMenu." (Development Library, Product Documentation, SDKs,
Windows 3.1 SDK, Programmer's Reference, Volume 2: Functions)
Knowledge Base Q118376. "How to Lock a Form So It Cannot Be Moved."
Knowledge Base Q110393. "How to Remove Menu Items from a Form's =
Control-Menu Box."
Knowledge Base Q77930. "Modifying the System Menu of an MDI Child =
Window."
Knowledge Base Q71669. "Preventing an MDI Child Window from Changing =
Size."

Tip 108: Flashing Controls to Get the User's Attention

Abstract
When developing an application in Microsoft(r) Visual Basic(r), you can =
use the
BackColor property to change the background color of a control. This =
article
explains how you can temporarily flash a control's BackColor property to =
draw the
user's attention to a specific control.

Changing a Control's BackColor Property
When designing a Microsoft(r) Visual Basic(r) application , you place =
controls
such as List Boxes and Text Boxes on a form. At run time, you can move =
the focus
to one of these objects by using Visual Basic's SetFocus method. Users =
can then
see that that particular control needs to be addressed in some way. For =
example,
if a Text Box receives the focus, users know they must type some text =
into that
control.

However, users may not actually notice that the focus has been set to a =
specific
control because the "rubberband" (highlighting) around the inside of the =
control
is not that obvious. To alert the user, you could change the background =
color of
the control from white to, say, red, to draw the user's attention to =
that control.
When the control loses the focus, you could reset the control's =
background color
to white. This procedure, however, means that the control would be a =
different
color as long as that control retained the focus. In some situations, =
this would
not be appropriate.

A far better solution would be to change the control's background color =
for just
a few seconds. The example program below "flashes" a control by quickly =
changing
the control's background color three times in succession. The Timer =
function is
used to cause a short time delay in the program. Each time a 2-second =
interval
elapses, the control's color is changed from white to red, then back to =
white.
The For-Next loop dictates how many times the control is flashed. In =
this case,
a value of 3 was used to flash the color three times. This creates a =
very visual
clue to draw the user's attention to that specific control.

Example Program
This program shows how to highlight the control that has the focus. Run =
the
example program by pressing F5 . Then click the Flash Command Button. =
Note that
the background color of the List Box control is changed to red and =
flashed three
times.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following code to the Form_Load event for Form1:

Private Sub Form_Load()
List1.AddItem "Item #1"
List1.AddItem "Item #2"
List1.AddItem "Item #3"
End Sub

3. Add a Command Button control to Form1. Command1 is created by =
default. Set
its Caption property to "Flash".
4. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
FlashControl List1
End Sub

5. Add a List Box control to Form1. List1 is created by default.
6. Create a new function called FlashControl. Add the following code to =
this
function:

Sub FlashControl(C As Control)
Dim OldColor As Double
Dim Delay As Double
Dim X As Integer
=20
OldColor =3D C.BackColor
For X =3D 1 To 3
C.BackColor =3D QBColor(12)
Delay =3D Timer
While Timer - Delay < 0.2
DoEvents
Wend
C.BackColor =3D OldColor
Delay =3D Timer
While Timer - Delay < 0.2
DoEvents
Wend
Next X
C.SetFocus
End Sub

Tip 107: Detecting Double-Click Events in Combo Box Controls

Abstract
When developing an application in Microsoft(r) Visual Basic(r), you may =
want to
let the user double-click the edit portion of a Combo Box control. This =
article
explains how to process this double-click event by using the Message =
Blaster
custom control.

Using Message Blaster to Detect Double-Clicks on Combo Boxes
In a Microsoft(r) Visual Basic(r) application, you can use a Combo Box =
control to
allow your user to easily select an item. Unfortunately, the Combo Box =
control
only responds to single-click events, not to double-click events in the =
box's
edit portion.

You can, however, use a subclassing control such as Message Blaster to =
detect
when a user has double-clicked the combo box. Before you can do this, =
you need
to use two Microsoft Windows(r) application programming interface (API)
functions GetWindow and GetClassName.
The GetWindow function retrieves the handle of a window that has a =
specific
relationship to the source window. In other words, we need to determine =
the
Combo Box's handle. The Combo Box window is actually a sibling window of =
our
Visual Basic application's main form. Next, we need to call the =
GetClassName
function to make sure that the edit portion of the Combo Box is the =
window we are
dealing with.

If you are using a combo box with its Style property set to 0 - Drop =
Down Combo,
detecting a double-click message is relatively straightforward. Just =
retrieve the
handle of the Combo Box's edit window, and then tell Message Blaster to =
intercept
the Windows WM_LBUTTONDBLCLK message.
On the other hand, if you are using a Combo Box with its Style property =
set to
1 - Simple Combo, the procedure is a little different. Because the edit =
portion
of the Combo Box is already displayed, you need to call GetWindow to =
retrieve
the handle of the edit window.

After you have retrieved the edit window's handle, you must call the =
GetClassName
function. This function is called so that we can be certain we are =
processing a
double-click message for only the edit portion of the Combo Box.

Example Program
This program shows how your Visual Basic application can respond to a =
double-click event when such an event is detected in the edit portion of =
a Combo Box control.
1. Create a new project in Visual Basic. Form1 is created by default.
2. From the Insert menu, select Custom Control. Add the Message Blaster =
custom
control to this project.
3. Add a Message Blaster control to Form1. MsgBlast1 is created by =
default.
4. Add the following code to the Message event for MsgBlaster1 (note =
that the
first two lines of code must be typed as a single line of text):

Private Sub MsgBlaster1_Message(MsgVal As Integer, wParam As Integer,
lParam As Long, ReturnVal As Long)
MsgBox "Combo1 box double-clicked"
End Sub

5. Add a Combo Box control to Form1. Combo1 is created by default. Set =
its Style
property to 1-Simple Combo.
6. Add the following Constant and Declare statements to the General =
Declarations
section of Form1 (note that each Declare statement must be typed as =
a single
line of text):

Private Declare Function GetWindow Lib "User" (ByVal hWnd As Integer,
ByVal wCmd As Integer) As Integer
Private Declare Function GetClassName Lib "User" (ByVal hWnd As Integer,

ByVal lpClassName As String, ByVal nMaxCount As Integer) As Integer
Const WM_LBUTTONDBLCLK =3D &H203
Const GW_CHILD =3D 5
Const GW_HWNDNEXT =3D 2

7. Add the following code to the Form_Load event for Form1:

Private Sub Form_Load()
Dim hWndList As Integer
Dim hWndEdit As Integer
Dim Buf As String * 10
Dim X As Integer
=20
Combo1.AddItem "Item #1"
Combo1.AddItem "Item #2"
Combo1.AddItem "Item #3"
=20
hWndList =3D GetWindow(Combo1.hWnd, GW_CHILD)
Select Case Combo1.Style
Case 0
MsgBlaster1.hWndTarget =3D hWndList
MsgBlaster1.MsgList(0) =3D WM_LBUTTONDBLCLK
Case 1
hWndEdit =3D GetWindow(hWndList, GW_HWNDNEXT)
Buf =3D ""
X =3D GetClassName(hWndEdit, Buf, Len(Buf))
=20
If StrComp(Trim(Buf), "Edit" & Chr$(0)) =3D 0 Then

MsgBlaster1.hWndTarget =3D hWndEdit
MsgBlaster1.MsgList(0) =3D WM_LBUTTONDBLCLK
End If
End Select
End Sub

Run the example program by pressing F5. When you double-click the edit =
portion of
the Combo Box control, a message box will confirm this action. You can =
also change
the Style property of the Combo Box to 0 - Drop Down Combo to get the =
same effect.

Additional References

"GetClassName." (Development Library, Product Documentation, SDKs,
Windows 3.1 SDK, Programmer's Reference Volume 2: Functions)
"GetWindow." (Development Library, Product Documentation, SDKs,
Windows 3.1 SDK, Programmer's Reference Volume 2: Functions)
Knowledge Base Q110104. "Using MSGBLAST.VBX Control to Process Windows =
Messages
from VB."
"Processing Messages in Visual Basic." (Development Library, Technical =
Articles,
Visual Basic Articles)

Tip 106: Centering Text Vertically in a Text Box Control

Abstract
The Microsoft® Visual Basic® Text Box control lets your user enter text that can
later be used by your application. This article explains how you can center the
text that the user types vertically within the Text Box control.

Vertically Centering Text in Visual Basic
In your application, you may need to display the text typed by the user, centered
vertically within the Text Box control. The only way to accomplish this task is
to place the Text Box control within a larger Picture Box control. The Text Box
control allows you to type text that can later be used by your Microsoft® Visual
Basic® application. As the user types the text, the text wraps to the next line
(if the MultiLine property is set to True).

The example program below centers the text in the text box by first setting the
size of the Text Box control to the same as the size of the Picture Box control.
Whenever a Change event is detected by the Text Box control, the text is redrawn
in the control so that it appears vertically centered.

Example Program
This program shows how to center text vertically within a Text Box control.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.
3. Add a Text Box control to Form1 over top of the Picture Box control. Text1 is
created by default. Set its MultiLine property to True.
4. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statement must be typed as a single
line of text):

Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer,
ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
Const WM_USER = &H400
Const EM_GETLINECOUNT = WM_USER + 10
Dim NumLines As Integer

5. Add the following code to the Form_Load event for Form1:

Private Sub Form_Load()
Dim HT As Integer
Text1.Left = 0
Text1.Width = Picture1.Width
Text1.Height = Picture1.TextHeight("A")
Text1.Top = (Picture1.Height = Text1.Height) / 2
Text1.Visible = True
NumLines = 1
End Sub

6. Add the following code to the Change event for Text1:

Private Sub Text1_Change()
Dim Ret As Long
Dim HT As Integer
Ret = SendMessage(Text1.hWnd, EM_GETLINECOUNT, 0, ByVal 0&)
If Ret <> NumLines Then
HT = Picture1.TextHeight("A")
Text1.Height = HT * Ret
Text1.Top = (Picture1.Height - Text1.Height) / 2
NumLines = Ret
SendKeys "{PGUP}", True
Text1.SelStart = Len(Text1)
End If
End Sub

Tip 105: Removing a Form's Title Bar

Abstract
This article explains how you can remove the title bar from a window or form from
within a Microsoft® Visual Basic® application.

Modifying the Style Attributes of a Window
You can use two Microsoft® Windows® application programming interface (API)
functions GetWindowLong and SetWindowLong to modify the appearance of a window
when your Visual Basic® application is running.

These functions allow you to programmatically change one or more style bits
associated with a specific window. For example, you can remove a window's
title bar by changing the following style bits at run time:

WS_SYSMENU The window has a control menu on the left side of its title bar
WS_MINIMIZEBOX The window has a minimize box on the right side of its title bar
WS_MAXMIZEBOX The window has a maximize box on the right side of its title bar
WS_DLGFRAME The window has a double border, but does not have a title bar

First, we must first call GetWindowLong. This function reports the window style
associated with a window, among other pieces of information.
To use GetWindowLong, you must include its Declare statement in your program as
follows (note that the Declare statement must be typed as a single line of text):

Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer,
ByVal nIndex As Integer) As Long

The GetWindowLong function requires two arguments. The first argument is the
window's handle; the second argument specifies the type of information you
want to retrieve. In this case, we want to retrieve the window's style settings.
The current window style is returned as a long value after GetWindowLong is
called.

After we have retrieved the current window style for the window, we need to save
the original style value that was just retrieved so that we can later restore the
window's title bar, if desired. This is done by testing for the individual title
bar attributes and saving each in turn to a new OriginalStyle variable. Next, we
need to remove the attributes associated with the window's title bar. These
attributes are the Minimize and Maximize buttons, the control menu, and the
dialog box frame. We can remove them from the original window style value that
was just retrieved by using the bitwise AND NOT function. Finally, we can call
SetWindowLong to send this information to Windows, which causes the title bar
to be removed from the window.

Example Program

This program shows how you can remove and later restore a window's title bar.
1. Create a new project in Visual Basic. Form1 is created by default.
2. Add a Command Button control to Form1. Command1 is created by default. Set
its Caption property to "Remove Title Bar".
3. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
RemoveTitleBar Form2
Form2.Show
End Sub

4. Add a second Command Button control to Form1. Command2 is created by default.
Set its Caption property to "Restore Title Bar".
5. Add the following code to the Click event for Command2:

Private Sub Command2_Click()
RestoreTitleBar Form2
Form2.Show
End Sub

6. From the Insert menu, select Form. Form2 is created by default. Adjust the
size of this form so that it is approximately half the size of Form1. Set
its AutoRedraw property to True and its Caption property to an empty (NULL)
string.
7. Add a Command Button control to Form2. Command1 is created by default. Set
its Caption property to "OK".
8. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
Form1.Show
Unload Form2
End Sub

9. From the Insert menu, select Module. Module1.Bas is created by default.
10. Add the following Constant and Declare statements to Module1.Bas (note that
each Declare statement must be typed as a single line of text):

Option Explicit
Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer,
ByVal nIndex As Integer) As Long
Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer,
ByVal nIndex As Integer, ByVal dwNewLong As Long) As Long
Const GWL_STYLE = (-16)
Const WS_DLGFRAME = &H400000
Const WS_SYSMENU = &H80000
Const WS_MINIMIZEBOX = &H20000
Const WS_MAXIMIZEBOX = &H10000

11. Create a new function called RemoveTitleBar to Module1.Bas. Add the following
code to this function:

Sub RemoveTitleBar(frm As Form)
Static OriginalStyle As Long
Dim CurrentStyle As Long
Dim X As Long
OriginalStyle = 0
CurrentStyle = GetWindowLong(frm.hWnd, GWL_STYLE)

OriginalStyle = OriginalStyle Or (CurrentStyle And WS_DLGFRAME)
OriginalStyle = OriginalStyle Or (CurrentStyle And WS_SYSMENU)
OriginalStyle = OriginalStyle Or (CurrentStyle And WS_MINIMIZEBOX)
OriginalStyle = OriginalStyle Or (CurrentStyle And WS_MAXIMIZEBOX)

CurrentStyle = CurrentStyle And Not WS_DLGFRAME
CurrentStyle = CurrentStyle And Not WS_SYSMENU
CurrentStyle = CurrentStyle And Not WS_MINIMIZEBOX
CurrentStyle = CurrentStyle And Not WS_MAXIMIZEBOX

X = SetWindowLong(frm.hWnd, GWL_STYLE, CurrentStyle)
frm.Refresh
End Sub

12. Create a new function called RestoreTitleBar to Module1.Bas. Add the
following code to this function:

Sub RestoreTitleBar(frm As Form)
Static OriginalStyle As Long
Dim CurrentStyle As Long
Dim X As Long

CurrentStyle = GetWindowLong(frm.hWnd, GWL_STYLE)
CurrentStyle = CurrentStyle Or OriginalStyle
X = SetWindowLong(frm.hWnd, GWL_STYLE, CurrentStyle)
frm.Refresh
End Sub

Run this example program by pressing F5. Click the "Remove Title Bar" command
button. Form2 is displayed. Notice that the title bar has been removed from
the form. Click the OK command button, then click the Restore Title Bar command
button. Form2 is displayed again, this time with its title bar intact.

Additional References
"GetWindowLong." (Development Library, Product Documentation, SDKs, Windows 3.1
SDK, Programmer's Reference, Volume 2: Functions)
Knowledge Base Q77316."How to Determine Display State of a VB Form, Modal or
Modeless."
Knowledge Base Q83915. "SAMPLE: Adding and Removing Caption of a Window."
"SetWindowLong." (Development Library, Product Documentation, SDKs, Windows 3.1
SDK, Programmer's Reference Volume 2: Functions)

Tip 104: Creating a Form with a Thin Title Bar

Abstract
Many Microsoft® Windows®-based applications include a Toolbox control. A toolbox
is a group of icons that the user can click to perform various operations within
a running application. These toolbox windows usually display a very small, thin
title bar instead of the normal-sized title bar. This article explains how you
can create forms with thin title bars in your Visual Basic® applications.

Using SendMessage and GetCursorPos to Create Title Bars

You can design a form that contains a thin title bar. This is most often used in
Toolbox controls from which the user can click on an icon to perform a program
operation.
In the example program below, we use a Label control, sized to fit at the top of
our form. This control, which will become the thin title bar, responds to both
the Click and MouseDown events. These two events allow the user to click on the
Label control (that is, the thin title bar) and drag the entire form to a new
location on the screen. This gives our Microsoft® Visual Basic® application the
same functionality as a toolbox window.

To enable our user to drag the form to a new location on the screen, we need to
determine the cursor's current X and Y coordinates on the screen. You can use the
Microsoft® Windows® application programming interface (API) GetCursorPos function
to retrieve the cursor's current location.
To call the GetCursorPos function, you must first add its Declare statement to
the General Declarations section of your Visual Basic application. Following is
the declaration for the GetCursorPos function:

Private Declare Sub GetCursorPos Lib "User" (lpPoint As POINTAPI)

The GetCursorPos function requires only one argument a POINTAPI structure. This
structure will hold the cursor's current position, which is reported in screen
coordinate values.
The cursor's horizontal position is stored in the X variable; the cursor's
vertical position is stored in the Y variable within the POINTAPI structure:

Type POINTAPI
X As Integer
Y As Integer
End Type

After the cursor's position has been retrieved, we issue the LSet statement to
convert the X and Y values to values that can be understood by the SendMessage
function. In other words, LSet converts the X and Y integers to a single long
value.
Next, we issue two SendMessage commands to Windows. The first SendMessage
statement tells Windows that, because a MouseDown event has just occurred,
it needs an equivalent MouseUp event. The second SendMessage statement tells
Windows that the user has clicked the title bar. Windows then processes our
thin title bar's Click and MouseDown events as it would for a normal window.

Example Program
This program shows how to create a form with a small title bar.
1. Create a new project in Visual Basic. Form1 is created by default.
2. Set the following properties for Form1:

ClipControls 0 'False
ControlBox 0 'False
MaxButton 0 'False
MinButton 0 'False

3. Add the following Constant and Declare statements to the General Declarations
section of Form1 (note that the Declare statements must be typed as single
lines of text):

Private Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal
wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Any) As Long
Private Declare Sub GetCursorPos Lib "User" (lpPoint As POINTAPI)
Const WM_LBUTTONUP = &H202
Const WM_SYSCOMMAND = &H112
Const MOUSE_MOVE = &HF012

4. Add a Label control to Form1. Label1 is created by default. Set its Caption
property to "Thin Title Bar".
Note: For this example program, change the size of Form1 so that it
resembles the size and shape of a Toolbox window. Next, position
the Label control at the top of the form and adjust its size so
that it isthe same size as a thin title bar.
5. Add the following code to the Click event for Label1:

Private Sub Label1_Click()
Dim mpos As POINTAPI
Dim P As ConvertPOINTAPI
Dim Ret As Integer

Call GetCursorPos(mpos)
LSet P = mpos
Ret = SendMessage(Me.hWnd, WM_LBUTTONUP, 0, P.XY)
Ret = SendMessage(Me.hWnd, WM_SYSCOMMAND, MOUSE_MOVE, P.XY)
End Sub

6. Add the following code to the MouseDown event for Label1 (note that the first
two lines must be typed as a single line of code):

Private Sub Label1_MouseDown(Button As Integer, Shift As Integer,
X As Single, Y As Single)
Dim mpos As POINTAPI
Dim P As ConvertPOINTAPI
Dim Ret As Integer

Call GetCursorPos(mpos)
LSet P = mpos
Ret = SendMessage(Me.hWnd, WM_LBUTTONUP, 0, P.XY)
Ret = SendMessage(Me.hWnd, WM_SYSCOMMAND, MOUSE_MOVE, P.XY)
End Sub

7. From the Insert menu, select Module. Module1.Bas is created by default.
8. Add the following type declarations to Module1.Bas:

Type POINTAPI
X As Integer
Y As Integer
End Type

Type ConvertPOINTAPI
XY As Long
End Type

Run the example program by pressing F5. Form1 should be displayed on the screen
with the thin title bar appearing at the top of the form. You can drag the form
by clicking on the title bar, just as you would do with any other window that has
a title bar.

Additional References
"Dave's Top Ten List of Tricks, Hints, and Techniques for Programming in Windows."
(Development Library, Books and Periodicals, Microsoft Systems Journal,
October 1992, Volume 7, Number 6)
Knowledge Base Q83349. "How to Create a Form with No Title Bar in VB for Windows."
Knowledge Base Q71280. "How to Create a Flashing Title Bar on a Visual Basic Form."

Tip 103: Preventing the CTRL+TAB and CTRL+F6 Key Combinations from Activating

Abstract
A multiple-document interface (MDI) window can contain multiple child windows. A
user can switch between child windows by pressing the CTRL+TAB or CTRL+F6 key
combinations. This article explains how you can prevent the user from using these
keys to switch to another child window.

Using Message Blaster to Disable Keystrokes
Many Microsoft® Windows®-based applications use multiple-document interface (MDI)
windows to display several child windows to the user. For instance, Word for
Windows lets you work with several different documents at the same time. Each text
file is displayed in its own child window.

When users want to switch from one child window to another, they press either
CTRL+TAB or CTRL+F6. The next window in the list is then brought to the top of
the window list (that is, it becomes the currently active window).
In a Visual Basic® application, you can disable this window-switching by
intercepting the messages sent to Windows. The WM_SYSCOMMAND message triggers the
event that switches between child windows. The Message Blaster custom control can
be used to process this WM_SYSCOMMAND message in your Visual Basic program. You
can retrieve Message Blaster from the Microsoft Development Library. For
information on the Message Blaster custom control, see "Additional References" at
the end of this article.

The general idea, however, is to capture the WM_SYSCOMMAND that is sent to
Windows when the CTRL+F6 or CTRL+TAB combination is pressed. To do this, you must
register the Message Blaster control to the target controlin this case, the first
child window (Form1). To prevent a user from activating other child windows,
execute the following statement:

MsgBlaster1=MsgPassage(0)

After you have disabled a child window in this manner, the user will not be able
to minimize or maximize the target window. In addition, the resize and move
options are also disabled.

Example Program
This program shows how to disable the CTRL+F6 and CTRL+TAB key combinations so
that the user cannot move to the next MDI child window. Run the example program
by pressing F5. The program displays two child windows (Form1 and Form2) within
an MDI document window. Normally, you can press the CTRL+TAB or CTRL+F6 keys to
switch between the child windows. The Message Blaster control has been used to
disable these two key combinations if you try to use them in Form1. Click Form2
to bring that child window to the top. Unlike Form 1, the Form 2 child window
will process the CTRL+TAB and CTRL+F6 key combinations.

1. Create a new project in Visual Basic. Form1 is created by default.
2. From the Visual Basic Insert menu, select MDI Form. MDIForm1 is created by
default.
3. Set Form1's MDIChild property to True.
4. From the Visual Basic Insert menu, select Form. Form2 is created by default.
5. Set the Form 2 MDIChild property to True.
6. From the Visual Basic Tools menu, select Custom Controls. Add a Message
Blaster control to Form1. MsgBlaster1 is created by default.
7. Add the following code to the General Declarations section of Form1:

Option Explicit
Const WM_SYSCOMMAND = &H112

8. Add the following code to the Form_Load event for Form1:

Private Sub Form_Load()
MsgBlaster1.hWndTarget = Form1.hWnd
MsgBlaster1.MsgList(0) = WM_SYSCOMMAND
End Sub

9. Add the following code to the MsgBlaster1_Message event for Form1 (note that
the first two lines below must be typed as a single line of code):

Private Sub MsgBlaster1_Message(MsgVal As Integer, wParam As Integer,
lParam As Long, ReturnVal As Long)
MsgBlaster1.MsgPassage(0) = 0
End Sub

10. Add the following code to the Form_Load event for MDIForm1:

Private Sub MDIForm_Load()
Form1.Show
Form2.Show
End Sub

Additional References
"The Elements of MDI." (Development Library, Books and Periodicals,
"Programming Windows 3.1" by Charles Petzold, PART 5 Data Exchange and Links,
Chapter 18 The Multiple-Document Interface [MDI])
Knowledge Base Q110104. "Using MSGBLAST.VBX Control to Process Windows Messages."
"Message Blaster: Processing Messages in Visual Basic." (Development Library,
Technical Articles, Visual Basic Articles)
"Switching Between MDI Child Windows." (Development Library,
Product Documentation, SDKs, Windows Interface Guidelines for Software Design,
Chapter 9 Window Management, Multiple-Document Interface)

Tip 102: Modifying a Child Window's Caption

Abstract
This article explains how you can modify the caption displayed in a
multiple-document interface (MDI) child window.

Changing the Caption of Child Windows or Forms
The Microsoft® Word for Windows® application allows you to have several
documents loaded into memory at one time. These text files are displayed in
multiple-document interface (MDI) child windows.
An MDI child window automatically inherits the parent window's caption. This
caption is inserted before the child window's own caption. As an example, if
the parent window's caption is MDIForm1 and the child window's caption is Form1,
the child window's caption, at run time, will be set to MDIForm1 - [Form1].

In your Visual Basic® application, you can set the child form's caption to a
NULL string so that only the caption of the parent window is displayed. However,
the dash and bracket characters must also be deleted. Because the caption is
displayed in the non-client area of a window, you must use a subclassing control
to process the paint event yourself. The Message Blaster custom control can be
used to modify a child window's caption.
However, another solution can do the same thing without using a subclassing
control. Just use Visual Basic's string functions (Mid and InStr) to remove the
unwanted text from the child window's caption. This code, as shown below in the
example program, is placed in the child form's Resize event. Each time Windows
needs to repaint the window, the caption will be modified.

Example Program
This program shows how to modify a child window's caption text. Start this
program by pressing F5. The original caption is "MDIForm1 - [Form1]".
Start this program again after removing the Exit Sub statement from the Resize
event (at the beginning of the code listing). The caption for the MDIForm1
window now reads, "MDIForm1 - Form1". The dash and bracket characters have been
removed from the string. In addition, if you set Form1's caption to a NULL
string, MDIForm1 will simply display its own caption even when Form1 is
minimized.

1. Create a new project in Visual Basic. Form1 is created by default.
2. From the Visual Basic Insert menu, select MDI Form. MDIForm1 is created by
default.
3. Set Form1's MDIChild property to True. Modify the size of this form so that
it is smaller than the MDIForm1 form.
4. Add the following code to the Form_Load event for Form1:

Private Sub Form_Load()
Form1.Tag = Form1.Caption
MDIForm1.Tag = MDIForm1.Caption
Form1.WindowState = 2 'Maximize this form
End Sub

5. Add the following code to the Resize event for Form1:

Private Sub Form_Resize()
Dim Cap As String
Dim Postn As Integer

If (Form1.WindowState = 2) Then
Cap = MDIForm1.Caption

MDIForm1.Caption = ""
Postn = InStr(Cap, "[")
If (Postn) Then
Mid(Cap, Postn, 1) = " "
End If

Postn = InStr(Cap, "]")
If (Postn) Then
Mid(Cap, Postn, 1) = " "
End If

Postn = InStr(Cap, "-")
If (Postn) Then
Mid(Cap, Postn, 1) = " "
End If

Form1.Caption = ""
MDIForm1.Caption = Cap
End If

If (Form1.WindowState = 0) Then
Form1.Caption = Form1.Tag
MDIForm1.Caption = MDIForm1.Tag
End If
End Sub

Tip 101: Using the Built-In Windows Icons

Abstract
There are several icons built into the Windows® operating system that are used
by Windows when displaying message boxes. This article explains how to use the
built-in icons in your own Visual Basic® applications.

Displaying the Built-In Windows Icons
In a Visual Basic® application, you can use the icons built into the Windows®
operating system. These include the hand, exclamation point, question mark,
asterisk, and application icons. The CONSTANT.TXT file defines these as follows:

Const IDI_APPLICATION = 32512&
Const IDI_HAND = 32513&
Const IDI_QUESTION = 32514&
Const IDI_EXCLAMATION = 32515&
Const IDI_ASTERISK = 32516&

Before you can use one of these icons in your Visual Basic application, you must
load the icon using the Windows application programming interface (API) LoadIcon
function. This function loads a specified icon into the device context. In the
example program below, we want to display the icon in a Picture Box control.
Therefore, we must first retrieve a device context for the Picture Box control.

To retrieve a device context for a window, you use the Windows application
programming interface (API) GetWindowDC function, as follows:

Private Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer)
As Integer

(Note that this statement must be typed as a single line of code.)
This function requires only one argumentan integer value containing the window's
handle. The device context's handle is returned as an integer value or, if the
function was not successful, a value of zero is returned.

When you have finished using the device context you must remember to release the
device context. This can be done by calling the ReleaseDC function, passing it
the handle of the device context that you want to release.
After retrieving the device context, you can call the LoadIcon function to
display the specified icon in the device context. Because this is a built-in
Windows icon, we set the first argument to a value of zero. The second argument
to the LoadIcon function is a constant value telling the function which icon you
want to load.

Example Program
This program shows how to use the built-in icons used by the Windows operating
system. This program displays the exclamation icon in the Picture Box control.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following Constant and Declare statements to the General
Declarations section of Form1 (note that each Declare statement must be
typed as a single line of code):

Private Declare Function DrawIcon Lib "User" (ByVal hDC As Integer, ByVal X
As Integer, ByVal y As Integer, ByVal hIcon As Integer) As Integer
Private Declare Function LoadIcon Lib "User" (ByVal hInstance As Integer, ByVal
lpIconName As Any) As Integer
Private Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer) As
Integer
Private Declare Function ReleaseDC Lib "User" (ByVal hWnd As Integer, ByVal hDC
As Integer) As Integer
Const IDI_EXCLAMATION = 32515&

3. Add a Picture Box control to Form1. Picture1 is created by default. Set its
AutoRedraw property to True.
4. Add a Command Button control to Form1. Command1 is created by default.
5. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
Dim hDCCur As Long
Dim hIcon As Integer
Dim X As Integer

hDCCur = GetWindowDC(Picture1.hWnd)
hIcon = LoadIcon(0, IDI_EXCLAMATION)
X = DrawIcon(hDCCur, 0, 0, hIcon)
Call ReleaseDC(Picture1.hWnd, hDCCur)
End Sub

Additional References
Knowledge Base Q88944. How to Extract a Windows Program IconRunning or Not.
Specifying a Class Icon and Using Built-In Icons (Product Documentation, SDKs,
Windows 3.1 SDK, Guide to Programming)

Tip 100: Printing a Form Multiple Times on One Page

Abstract
This article explains how you can print a Visual Basic® form several
times on a single piece of paper.

Printing Forms on the Printer Device
You can use the Windows® application programming interface (API)
StretchBlt function to copy a form to another form multiple times. For
instance, the example program below uses the StretchBlt function to
copy a form to a new form. The original form is copied four times. The
destination form then contains a copy of the original form in its
upper left, lower left, upper right, and lower right corners.

The technique presented in this article is usefull for duplicating a
form several times. For example, if you designed a form to keep track
of telephone messages, you could print four messages per printed page,
instead of using one piece of paper per telephone message.
The StretchBlt function can be used to copy an image from one device
context to another. To use this function in your Visual Basic®
application, include the following Declare statement in the General
Declarations section of your form (note that it must be typed as a
single line of code):

Private Declare Function StretchBlt Lib "GDI" (ByVal hDC%, ByVal X%, ByVal y%,
ByVal nWidth%, ByVal nHeight%, ByVal hSrcDC%, ByVal XSrc%, ByVal YSrc%, ByVal
nSrcWidth%, ByVal nSrcHeight%, ByVal dwRop&) As Integer

The StretchBlt function requires the following arguments:

hDC An integer value containing the destination device context.
X,Y Integer values defining the rectangle's upper left corner
for the destination device context.
nWidth The width of the image.
nHeight The height of the image.
hSrcDC An integer value containing the source device context.
Xsrc, YSrc Integer values defining the rectangle's upper left corner
for the source device context.
nSrcWidth The width of the image.
nSrcHeight The height of the image.
dwRop The raster operation that is to be used.

In the example program below, the StretchBlt function is called four
times to copy the original Form1 to the destination Form2. Each time
the copy operation is performed, the destination is offset to the next
quarter section of Form2.

Example Program
Before running this program, make sure your printer is online and
ready to accept data. Press the F5 function key to run the example
program, which will print Form2 on the paper four times.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add a Command Button control to Form1. Command1 is created by
default.
3. Add the following code to the Click event for Command1 (note that
the X = lines must each be typed as a single line of code):

Private Sub Command1_Click()
Dim W As Integer
Dim H As Integer
Dim X As Integer

W = Form2.ScaleWidth / 2
H = Form2.ScaleHeight / 2
X = SetStretchBltMode(Form2.hDC, 3)

X = StretchBlt(Form2.hDC, 0, 0, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
Form1.ScaleHeight, SRCCOPY)
X = StretchBlt(Form2.hDC, W, 0, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
Form1.ScaleHeight, SRCCOPY)
X = StretchBlt(Form2.hDC, W, H, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
Form1.ScaleHeight, SRCCOPY)
X = StretchBlt(Form2.hDC, 0, H, W, H, Form1.hDC, 0, 0, Form1.ScaleWidth,
Form1.ScaleHeight, SRCCOPY)

Form2.Refresh
Form2.PrintForm
End Sub

4. From Visual Basic's menu, select Insert. Select Form to create a new form.
Form2 is created by default. Add a Text Box control to Form2 or otherwise
put some type of material on the form so you can see it printed on the paper.
5. Add the following code to the General Declarations section of Form1 (note
that each Declare statement should be typed as a single line of code):

Private Declare Function SetStretchBltMode Lib "GDI" (ByVal hDC%, ByVal
nStretchMode%) As Integer
Private Declare Function StretchBlt Lib "GDI" (ByVal hDC%, ByVal X%, ByVal y%,
ByVal nWidth%, ByVal nHeight%, ByVal hSrcDC%, ByVal XSrc%, ByVal YSrc%, ByVal
nSrcWidth%, ByVal nSrcHeight%, ByVal dwRop&) As Integer
Const SRCCOPY = &HCC0020

Additional References
"Creating Bitmaps of Any Size with StretchBlt." (Books and Periodicals, Inside
Visual Basic Articles [Cobb])
Knowledge Base Q84066. "How to Print Entire VB Form and Control the Printed Size."
Knowledge Base Q77060. "How to Print a VB Picture Control Using Windows API
Functions."

Tip 99: Adding Three-Dimensional Effects to Visual Basic Forms

Abstract
You can use functions in the CTL3D.DLL dynamic-link library to add a
three-dimensional (3-D) look to any form. This article explains how to
use this .DLL to create a 3-D form in a Visual Basic® application.

Creating 3-D Forms
The CTL3D.DLL dynamic-link library contains Windows® application
programming interface (API) functions you can use to create
three-dimensional (3-D) message boxes and common dialog boxes in your
Visual Basic® applications. You can also use these functions to create
a 3-D form. The form must have a fixed-double-style border. In
addition, the form's MinButton and MaxButton properties must be set to
False.

As the example program below shows, the CTL3D.DLL functions can enable
your Visual Basic application to display 3-D forms, message boxes, and
common dialog boxes.

Example Program
1. Create a new project in Visual Basic. Form1 is created by default.
2. Add a Command Button control to Form1. Command1 is created by
default.
3. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
Form2.Show
End Sub

4. From the Insert menu, select Form to create a second form. Form2
is created by default. Set the form's BorderStyle property to
3-Fixed Double, the MinButton property to False, and the MaxButton
property to False.
5. Add the following code to the Form_Load event for Form2:

Private Sub Form_Load()
Call Ctl3DForm(Me)
End Sub

6. From the Insert menu, select Module to create a new module.
Module1.Bas is created by default.
7. Add the following code to Module1.Bas (note that the Private
lines must each be typed as a single line of code):

Const SWW_HPARENT = -8
Const GWW_HINSTANCE = -6
Const GWW_HPARENT = -8
Const BUTTON_FACE = &H8000000F
Const FIXED_DOUBLE = 3
Const DS_MODALFRAME = &H80&
Const GWL_STYLE = (-16)
Private Declare Function Ctl3DSubClassDlgEx Lib "CTL3D.DLL" (ByVal hWnd As
Integer, ByVal Flags As Long) As Integer
Private Declare Function GetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
nIndex As Integer) As Long
Private Declare Function SetWindowLong Lib "User" (ByVal hWnd As Integer, ByVal
nIndex As Integer, ByVal dwNewLong As Long) As Long
Sub Ctl3DForm(frm As Form)
Dim hWnd As Integer
Dim Result As Integer
Dim lStyle As Long
Dim Flag1 As Long

hWnd = frm.hWnd
Flag1 = 0

If frm.BorderStyle = FIXED_DOUBLE Then
frm.BackColor = BUTTON_FACE
lStyle = GetWindowLong(hWnd, GWL_STYLE)
lStyle = lStyle Or DS_MODALFRAME
lStyle = SetWindowLong(hWnd, GWL_STYLE, lStyle)
Result = Ctl3DSubClassDlgEx(hWnd, Flag1)
End If
End Sub

Run the example program by pressing the F5 function key. Click the
Command Button to display Form2. Form2 is modified so that it has a
three-dimensional look.

Additional References
Knowledge Base Q113898. "How to Use CTL3D.DLL in Your Visual Basic
Program."
"Seventeen Techniques for Preparing Your 16-Bit Applications for
Chicago." (Books and Periodicals, Microsoft Systems Journal,
1994 Volume 9, February 1994 Number 2)
"Windows Questions and Answers." (Books and Periodicals, Microsoft
Systems Journal, 1994 Volume 9, August 1994, Number 8)

Tip 98: Saving a Window's Client Area in Bitmap Format

Abstract
You may need to create a bitmap (.BMP) image file that contains a
window's client area. This article explains how to save a window's
client area to disk in a bitmap image file format.

Saving Bitmap Images to Disk
You can save the contents of a window to a disk file in bitmap format
by retrieving the target window's rectangle area and then using
several Windows® application programming interface (API) functions to
save that image to a device context such as a Picture Box control.

The GetWindowRect function can be used to retrieve the bounding
rectangle of a window or form. This rectangle includes the window's
borders, title bars, and other attributes associated with a window.
The Declare statement for the GetWindowRect function is as follows:

Private Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect
As RECT)

The GetWindowRect function takes two arguments: an integer value
containing the window's handle and a pointer to a RECT rectangle
structure. The RECT structure will contain the dimensions of the
window's rectangle area after the function is called.

The BitBlt function uses the rectangle that contains the image you
want to save to disk to copy the image from one device context to
another. In this case, BitBlt is used to copy the window's client
area to the Picture Box control. Then the Save As dialog box is used
to save the contents of the Picture Box to disk as a .BMP file.

Example Program
This program shows how to save the client area of a form or window
and save it as a .BMP file.

1. Create a new project in Visual Basic®. Form1 is created by default.
2. Add the following Constant and Declare statements to the General
Declarations section of Form1 (note that each Declare statement
must be typed as a single line of code):

Private Declare Function GetActiveWindow Lib "User" () As Integer
Private Declare Function GetWindowDC Lib "User" (ByVal hWnd As Integer)
As Integer
Private Declare Sub GetWindowRect Lib "User" (ByVal hWnd As Integer, lpRect
As RECT)
Private Declare Function ReleaseDC Lib "User" (ByVal hWnd As Integer, ByVal hDC
As Integer) As Integer
Private Declare Function BitBlt% Lib "GDI" (ByVal hDestDC%, ByVal X%, ByVal Y%,
ByVal nWidth%, ByVal nHeight%, ByVal hSrcDC%, ByVal XSrc%, ByVal YSrc%, ByVal
dwRop&)
Const SRCCOPY = &HCC0020

3. From the Insert menu, select Form to create a second form. Form2
is created by default. Set its Picture property to
"C:\WINDOWS\ARCHES.BMP".
4. From the Insert menu, select Module to create a BASIC module.
Module1.Bas is created by default.
5. Add the following Type structure to Module1.Bas:

Type RECT
Left As Integer
Top As Integer
Right As Integer
Bottom As Integer
End Type

6. Add a Timer control to Form1. Timer1 is created by default.
7. Add a Picture Box control to Form1. Picture1 is created by
default. Set its AutoRedraw property to True.
8. Add a Command Button control to Form1. Command1 is created by
default.
9. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
SaveToPicture
End Sub

10. Create a new function called SaveToPicture. Add the following code
to this function:

Sub SaveToPicture()
Dim hDCCur As Long
Dim hWndCur As Long
Dim HWndOld As Long
Dim Tim As Double
Dim ThisRect As RECT
Dim DX As Long
Dim DY As Long

HWndOld = GetActiveWindow()
Form2.Show
hWndCur = Form2.hWnd
Tim = Timer + 0.5
Do
DoEvents
Loop Until Timer >= Tim

hDCCur = GetWindowDC(hWndCur)
Call GetWindowRect(hWndCur, ThisRect)
DX = ThisRect.Right - ThisRect.Left + 2: DY = ThisRect.Bottom - ThisRect.Top + 2

With Picture1
.Width = Screen.TwipsPerPixelX * DX
.Height = Screen.TwipsPerPixelY * DY
Call BitBlt(.hDC, 0, 0, DX, DY, hDCCur, 0, 0, SRCCOPY)
.Picture = .Image
End With

Call ReleaseDC(hWndCur, hDCCur)
Form2.Hide

CommonDialog1.DefaultExt = "BMP"
CommonDialog1.DialogTitle = "Save Window As"
CommonDialog1.FileName = "*.BMP"
CommonDialog1.Action = 2

If CommonDialog1.FileName <> Empty Then
SavePicture Picture1.Picture, CommonDialog1.FileName
End If
End Sub

11. Add a Common Dialog control to Form1. CommonDialog1 is created by
default.

Run this program by pressing the F5 function key. Click once on the
command button. The ARCHES.BMP picture is displayed on Form2. Next,
the Save File As dialog box pops up on the screen. Type a filename
for the .BMP file and Visual Basic will save Form2's window (which
contains the ARCHES.BMP picture) to a new bitmap file.

Additional References
"Bitmaps." (Product Documentation, DDKs, Windows 3.1 DDK, Device
Driver Adaptation Guide)
"GetActiveWindow"; "GetWindowDC"; and "ReleaseDC." (Product
Documentation, SDKs, Windows 3.1 SDK, Programmer's Reference
Volume 2: Functions)
Knowledge Base Q71104. "How to Use Windows BitBlt Function in Visual
Basic Application."

Tip 97: Creating a Task List

Abstract
This article explains how you can determine what modules are currently
running under Microsoft® Windows® and create a task list based on that
information.

Determine What Modules Are Currently Running
The TOOLHELP.DLL dynamic-link library (.DLL) file contains two
Windows® application programming interface (API) functions that can be
used to create a list of modules currently loaded under Windows. These
are the ModuleFirst and ModuleNext functions. To declare these
functions within your program, include the following Declare
statements in the Global Module or General Declarations section of a
Visual Basic® form:

Private Declare Function ModuleFirst Lib "toolhelp.dll" (mdlentry As
ModuleEntry) As Integer
Private Declare Function ModuleNext Lib "toolhelp.dll" (mdlentry As ModuleEntry)
As Integer

Note that each Declare statement must be typed as a single line of text.
The ModuleFirst and ModuleNext functions are the key to traversing
the chain of loaded modules in Windows. The ModuleFirst function fills
the specified structure with information describing the first module
in the list of currently loaded modules. The ModuleNext function is
then called to find the next module in the list.

The MODULEENTRY structure required by these two functions must be
defined as follows:

dwSize The size of the structure in bytes.
szModule The module's name (a null-terminated string).
hModule The module's handle.
wcUsage Used by GetModuleUsage function.
szExePath The module.
wNext The window.

Before you can use these two functions, however, you must initialize
the dwSize field of the MODULEENTRY structure. This value should be
specified as the number of bytes needed to store the information
returned by ModuleFirst and ModuleNext.
These functions return a value indicating the status of the function.
The function was successful if the returned value is nonzero; the
function was not successful (or no more modules were found in memory)
if the value returned is zero.

Example Program
This program shows how to retrieve the module name and path for every
running task under Windows. The name of each module is displayed in
the first List Box control, while the full path of the module is
displayed in the second List Box control.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following code to the General Declarations section of
Form1 (note that each Declare statement should be typed as a
single line of text):

Private Declare Function ModuleFirst Lib "toolhelp.dll" (mdlentry As
ModuleEntry) As Integer
Private Declare Function ModuleNext Lib "toolhelp.dll" (mdlentry As ModuleEntry)
As Integer

3. Add a Command Button control to Form1. Add the following code to
the Click event for Command1:

Private Sub Command1_Click()
Dim Tmp As ModuleEntry
Dim Retn As Integer

Tmp.dwSize = Len(Tmp)
Retn = ModuleFirst(Tmp)
While Retn <> 0
If InStr(Tmp.szExepath, ".VBX") <> 0 Or InStr(Tmp.szExepath, ".DLL") <>
0 Or InStr(Tmp.szExepath, ">DRV") <> 0 Then
List1.AddItem Tmp.szModule
List2.AddItem Tmp.szExepath
End If
Tmp.szExepath = ""
Retn = ModuleNext(Tmp)
Wend
End Sub

4. Add a List Box control to Form1. List1 is created by default.
5. Add a second List Box control to Form1. List2 is created by
default.
6. From the Insert menu, select Module. Module1.Bas is created by
default.
7. Add the following user-defined Type to Module1.Bas:

Type ModuleEntry
dwSize As Long
szModule As String * 10
hModule As Integer
wcUsage As Integer
szExepath As String * 256
wNext As Integer
End Type

Additional References

Knowledge Base Q78001. "How to Get Windows Master (Task List) Using
Visual Basic."
Knowledge Base Q80124. "Retrieving the Names of Simultaneous Tasks
Under Windows."
"Windows Questions and Answers." (Books and Periodicals, Microsoft
Systems Journal, 1994 Volume 9, May 1994 Number 5)

Tip 96: Centering a Form over Another Form

Abstract
You can position a form so it appears centered within another form.
This article explains how to center a form within its parent form.
This same technique can be applied to centering controls such as
Picture Box controls over other controls.

Centering Forms in Visual Basic
When developing an application in Visual Basic®, you may need to
position a form so it is centered over another form. You can position
a form in this way by using Visual Basic's Left, Top, Height, and
Width properties.

The Left property defines the position of the form's left edge and the
Top property defines the position of the form's top edge. In the same
manner, the Width and Height properties define how wide and high the
form is. It is easy enough to center a form on a container by
calculating the width and height of the form and dividing that value
by two to center it within the control.
To center a form within a parent form, take the width of Form1 and
Form2, subtract these two values, and divide the result by two. Next,
add Form1's width to the result to determine the position of Form2's
left edge within Form1. This will center Form2 horizontally within
Form1.

In the same manner, you can center a form vertically within another
form by using the Top and Height properties of each form, dividing by
two, and setting Form2's Top property to the result.

Example Program
This program shows how to center a form over another form. After you
run this example program by pressing the F5 function key, click the
Command Button. The program displays Form2 centered over its "parent,"
Form1.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add a Command Button control to Form1. Command1 is created by
default.
3. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
Form2.Show
CentreFormWithParent Form2, Form1
End Sub

4. From the Insert menu, select Form to create a second form. Form2
is created by default. Change this form's size so it is smaller
than Form1.
5. Create a new function called CreateFormWithParent. Add the
following code to this function:

Sub CenterFormWithParent(aForm As Form, aParent As Form)
aForm.Left = aParent.Left + (aParent.Width - aForm.Width) / 2
aForm.Top = aParent.Top + (aParent.Height - aForm.Height) / 2
If (aForm.Left + aForm.Width) > Screen.Width Then
aForm.Left = Screen.Width - aForm.Width
Else
If aForm.Left < 0 Then aForm.Left = 0
End If
If (aForm.Top + aForm.Height) > Screen.Height Then
aForm.Top = Screen.Height - aForm.Height
Else
If aForm.Top < 0 Then aForm.Top = 0
End If
End Sub

Tip 95: Deleting All Records from Every Table in a Microsoft Access Database

Abstract
A Microsoft® Access® database may contain several tables, with each
table holding many records. This article explains how to delete all
records from all tables associated with a Microsoft Access database
application.

Removing Records from Tables
There may be occasions when you need to delete all records in a table
from a Microsoft® Access® database application. For instance, if you
have an inventory program that contains a table of stock and another
table of purchase orders, at the end of the year you would need to
remove these records in preparation for the next year's information.
To remove each record from both tables would be a tedious job.

You can create an Access Basic function that will remove every record
from the specified table. However, if your Microsoft Access database
contains many tables, you need to process each individual table
associated with that specific Microsoft Access database.

Example Program
This example Access program shows how to delete all records from every
table in a Microsoft Access database application. Note that this
function also processes those table names that include space
characters in them (Inventory Year1, for example).

1. Open the sample database ORDERS.MDB. (This database can usually be
found in the C:\ACCESS\SAMPAPPS directory.)
Note: The example program will permanently modify this Access
database. Therefore, you should copy ORDERS.MDB to another
directory and run this program on the temporary copy of ORDERS.MDB.
2. From the File menu, choose New, and select Module.
3. Enter the following code to create the DeleteAllRecords()
function:

Function DeleteAllRecords ()
Dim DB As Database
Dim X As Integer
Dim TDF As TableDef
DoCmd SetWarnings False
Set DB = CurrentDB()
For X = 0 To DB.TableDefs.Count - 1
Set TDF = DB.TableDefs(X)
If (TDF.Attributes And DB_SYSTEMOBJECT) = 0 Then
DB.Execute "Delete * From [" & DB.TableDefs(X).Name & "]"
End If
Next X
DoCmd SetWarnings True
End Function

4. From the View menu, choose Immediate Window.
5. In the Immediate Window, type the following line and press the ENTER key:

?DeleteAllRecords()

This statement will execute the DeleteAllRecords() function. After a
short time, all records will be deleted from each table included in
the ORDERS.MDB database.

Tip 94: Using Drag-and-Drop on Multiple items in a List Box Control

Abstract
The drag-and-drop functionality provided in many Windows®-based
applications allows you to copy an item from one program to another
or from one control to another control in the same application. This
article explains how to use this drag-and-drop technique in Visual
Basic® to copy multiple items selected in a List Box control to
another List Box control.

Dragging Multiple list Box Items
Many Windows-based applications include drag-and-drop functionality.
This means that you can select an item, such as an entry in a List
Box control, click on the item, and, while holding the mouse button
down, drag that item to another window or control and drop it on its
new location.

The example program below shows how you can add this drag-and-drop
feature to your Visual Basic® applications. This program allows you
to select multiple items in the source List Box control and drag the
whole group of selected items to a second List Box control all at one
time.

Example Program
This program shows how to drag several items selected in one List Box
control to another List Box control. Run the example program by
pressing the F5 function key. From the first List Box control, click
the mouse on several items to select (highlight) them. While clicking
each item, hold down the SHIFT key. When you want to drag the selected
items to the second List Box control, click once on the first List Box
control and hold the mouse button down while you drag the control to
the second List Box. Release the mouse button to drop the selected
items onto the second List Box control.

While using this program, you can select the items from the first
List Box either by holding the SHIFT key down while you click on each
entry, or by simply clicking the mouse on each individual entry. If
you hold the SHIFT key down when selecting entries, those entries will
remain selected (highlighted) in the first List Box control after the
items have been dropped onto the second List Box control. If the
SHIFT key is not used, one of the selected items will not retain its
selected status after the drag-and-drop operation has finished.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following code to the General Declarations section of
Form1:

Option Explicit
Dim IG As Integer
Dim LIG(20) As Integer
Dim LGlobal As Long
Const VK_SHIFT = &H10

3. Add the following code to the Form_Load event for Form1:

Private Sub Form_Load()
Dim X As Integer
IG = 0
For X = 0 To 9
List1.AddItem "Item #" + Str$(X)
Next X
List1.DragMode = 0
LGlobal = 99999
End Sub

4. Add a List Box control to Form1. List1 is created by default. Set
its MultiSelect property to 1-Simple.
5. Add the following code to the MouseDown event for List1 (note that
the Private line must be typed as a single line of code):

Private Sub List1_MouseDown(Button As Integer, Shift As Integer, X As Single,
Y As Single)
LGlobal = List1.ListIndex
For X = 1 To IG
List1.Selected(LIG(X)) = True
Next X
List1.Drag
End Sub

6. Add a second List Box control to Form1. List2 is created by
default. Set its MultiSelect property to 1-Simple.
7. Add the following code to the DragDrop event for List2:

Private Sub List2_DragDrop(Source As Control, X As Single, Y As Single)
For X = 0 To List1.ListCount - 1
If X = LGlobal Then
List2.AddItem List1.List(X)
Else
If List1.Selected(X) Then
List2.AddItem List1.List(X)
End If
End If
Next X
LGlobal = 99999
IG = 0
End Sub

Additional References
Knowledge Base Q80187. "How to Drop Item into Specified Location in
VB List Box."
Tip 50: Using Drag and Drop in List Box Controls

Tip 93: Retrieving the Names of Installed Printers

Abstract
The Windows® initialization file, WIN.INI, contains a list of all
printers attached to the computer system. This article contains an
example program that retrieves the name of each printer stored in the
WIN.INI initialization file.

Using GetProfileString and GetPrivateProfileString
The Devices section of the WIN.INI initialization file contains the
names of all printers attached to your computer system. You can
retrieve this list of printer names by using two Windows® application
programming interface (API) functions.

The Windows API GetProfileString and GetPrivateProfileString functions
can be used to retrieve the name of a printer as stored in the WIN.INI
file. For a complete discussion of these functions, see the articles
listed in the "Additional References" section of this article.

Example Program
This program retrieves the names of all installed printers from the
WIN.INI initialization file. The printer names are displayed in a
List Box control.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following Constant, Declare, and Type statements to the
General Declarations section of Form1 (note that each Declare
statement must be typed as a single line of text):

Option Explicit
Private Type WindowsDevice
WindowsDeviceUserName As String
WindowsDeviceShortName As String
WindowsDevicePortName As String
End Type
Private Declare Function GetProfileString Lib "Kernel" (ByVal lpAppName
As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal
lpReturnedString As String, ByVal nSize As Integer) As Integer
Private Declare Function GetPrivateProfileString Lib "Kernel" (ByVal lpAppName
As String, ByVal lpKeyName As String, ByVal lpDefault As String, ByVal
lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As
String) As Integer
Private Declare Function GetProfileKeys Lib "Kernel" Alias "GetProfileString"
(ByVal lpAppName As String, ByVal lpKeyName As Long, ByVal lpDefault As
String, ByVal lpReturnedString As String, ByVal nSize As Integer) As Integer
Private Declare Function GetPrivateProfileKeys Lib "Kernel" Alias
"GetPrivateProfileString" (ByVal lpAppName As String, ByVal lpKeyName As
Long, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal
nSize As Integer, ByVal lpFileName As String) As Integer

Const WINDOWS_SECTION_NAME = "windows"
Const DEVICES_SECTION_NAME = "devices"
Const DEVICE_KEY_NAME = "device"
Const NO_PRINTER = "(none)"

3. Add a Text Box control to Form1. Text1 is created by default.
4. Add a Command Button control to Form1. Command1 is created by default.
5. Add the following code to the Click event for Command1 (note that the
List1.AddItem statement must be typed as a single line of code):

Private Sub Command1_Click()
Dim OrgPrinter As WindowsDevice
Call GetDefaultPrinter(OrgPrinter)
Text1.Text = OrgPrinter.WindowsDeviceUserName
Dim NumPrinters As Integer
ReDim InstalledPrinters(0) As WindowsDevice
Call GetInstalledPrinters(InstalledPrinters())
For NumPrinters = 1 To UBound(InstalledPrinters)
List1.AddItem InstalledPrinters(NumPrinters).WindowsDeviceUserName + "
on " + InstalledPrinters(NumPrinters).WindowsDevicePortName
Next
List1.AddItem NO_PRINTER, 0
End Sub

6. Create a new function called GetDefaultPrinter. Add the following
code to this function:

Private Sub GetDefaultPrinter(recDefaultPrinter As WindowsDevice)
Dim StrPos As Integer
Dim DefaultPrinter As String
Dim RC As Integer
DefaultPrinter = GetString(WINDOWS_SECTION_NAME, DEVICE_KEY_NAME, "", "")
StrPos = InStr(DefaultPrinter, ",")
recDefaultPrinter.WindowsDeviceUserName = Left$(DefaultPrinter, StrPos - 1)
DefaultPrinter = Mid$(DefaultPrinter, StrPos + 1)
StrPos = InStr(DefaultPrinter, ",")
recDefaultPrinter.WindowsDeviceShortName = Left$(DefaultPrinter, StrPos - 1)
recDefaultPrinter.WindowsDevicePortName = Mid$(DefaultPrinter, StrPos + 1)
End Sub

7. Create a new function called GetInstalledPrinter. Add the
following code to this function (note that the InstalledPrinter
lines must be typed as a single line of code):

Private Sub GetInstalledPrinters(recInstalledPrinters() As WindowsDevice)
Dim StrPos As Integer
Dim PrtSub As Integer
Dim InstalledPrinter As String
ReDim PrinterNames(0) As String
Call GetKeyNames(DEVICES_SECTION_NAME, PrinterNames(), "")
ReDim recInstalledPrinters(UBound(PrinterNames))
For PrtSub = 1 To UBound(PrinterNames)
InstalledPrinter = GetString(DEVICES_SECTION_NAME, PrinterNames(PrtSub),
"", "")
StrPos = InStr(InstalledPrinter, ",")
recInstalledPrinters(PrtSub).WindowsDeviceUserName =
PrinterNames(PrtSub)
recInstalledPrinters(PrtSub).WindowsDeviceShortName =
Left$(InstalledPrinter, StrPos - 1)
InstalledPrinter = Mid$(InstalledPrinter, StrPos + 1)
StrPos = InStr(InstalledPrinter, ",")
If StrPos > 0 Then
recInstalledPrinters(PrtSub).WindowsDevicePortName =
Left$(InstalledPrinter, StrPos - 1)
Else
recInstalledPrinters(PrtSub).WindowsDevicePortName =
InstalledPrinter
End If
Next
End Sub

8. Create a new function called GetString. Add the following code to
this function (note that the Function and KeyValueLength lines
must each be typed as a single line of code):

Function GetString(SectionName As String, KeyName As String, DefaultValue
As String, ProfileName As String) As String
Dim KeyValueLength As Integer
Dim KeyValue As String
KeyValue = Space$(256)
If Trim$(ProfileName) = "" Then
KeyValueLength = GetProfileString(SectionName, KeyName, DefaultValue,
KeyValue, Len(KeyValue))
Else
KeyValueLength = GetPrivateProfileString(SectionName, KeyName,
DefaultValue, KeyValue, Len(KeyValue), ProfileName)
End If
GetString = Left$(KeyValue, KeyValueLength)
End Function

9. Create a new function called GetKeyName. Add the following code
to this function (note that the Sub and KeyNamesLength lines must
each be typed as a single line of code):

Sub GetKeyNames(SectionName As String, KeyNames() As String, ProfileName
As String)
Dim StrPos As Integer
Dim KeyCount As Integer
Dim Start As Integer
Dim KeyNamesLength As Integer
Dim KeyNameString As String
KeyNameString = Space$(1024)
If Trim$(ProfileName) = "" Then
KeyNamesLength = GetProfileKeys(SectionName, 0, "", KeyNameString,
Len(KeyNameString))
Else
KeyNamesLength = GetPrivateProfileKeys(SectionName, 0, "",
KeyNameString, Len(KeyNameString), ProfileName)
End If
KeyCount = 0
ReDim KeyNames(0)
If KeyNamesLength > 0 Then
KeyNameString = Left$(KeyNameString, KeyNamesLength)
If Right$(KeyNameString, 1) <> Chr$(0) Then
KeyNameString = KeyNameString + Chr$(0)
End If
KeyNamesLength = Len(KeyNameString)
Start = 1
Do
StrPos = InStr(Start, KeyNameString, Chr$(0))
If StrPos > 0 Then
KeyCount = KeyCount + 1
ReDim Preserve KeyNames(KeyCount)
KeyNames(KeyCount) = Mid$(KeyNameString, Start, StrPos - Start)
If StrPos < KeyNamesLength Then
Start = StrPos + 1
Else
Exit Do

End If
Else
Exit Do
End If
Loop
End If
End Sub

Run the example program by pressing the F5 function key. Click the
Command Button control. The name of the default printer is displayed
in the Text Box control, and a list of all installed printers is
displayed in the List Box control.

Additional References
Knowledge Base Q105839. "Changing WIN.INI Printer Settings from VB
using Windows API."
"Accessing Initialization Files." (Books and Periodicals, Inside
Visual Basic Articles [Cobb])
"Querying Windows for Printer Information." (Books and Periodicals,
Inside Visual Basic Articles [Cobb])
Tip 30: Retrieving the Default Printer's Name from WIN.INI

Tip 92: Using the Shell Statement to Execute MS-DOS Programs

Abstract
From within a Visual Basic® application, you can execute an MS-DOS®
program. This article explains how to use the Shell statement in
conjunction with the Windows® application programming interface (API)
GetNumTasks function to execute an MS-DOS program.

Running MS-DOS Programs
A very popular set of utilities called PKZIP and PKUNZIP can be found
on bulletin board systems throughout the world. These two utilities
are used to compress and decompress a group of related files. The
resulting ZIP file can then be distributed as one single entity.
Because these programs are used by people daily, you may need to allow
your user to execute these MS-DOS programs (or an entirely different
MS-DOS program) from within your Visual Basic® application program.

Visual Basic's Shell function can be used to execute another program.
To run a program, you would issue the statement:

X = Shell("program name",[windowstate]])

The program name must be a valid MS-DOS or Windows®-based-application
name and may optionally include any command-line parameters needed by
the program. The second parameter tells Visual Basic how to execute
the program. There are five possible values for this argument, as
follows:

1 Normal size, program retains focus
2 Minimized, program retains focus
3 Maximized, program retains focus
4 Normal size, Visual Basic application retains focus
5 Minimized, program does not retain focus

While the MS-DOS program is executing, you can use the SendKeys
statement to send specific keystrokes to the application. In this
manner, you can actually control what the MS-DOS program does or
provide some required input.
In addition, you can determine when the secondary program has
terminated by using the GetNumTasks function. To use the GetNumTasks
function in your Visual Basic application, you must include the
following Declare statement in the Global Module or General
Declarations section of your form:

Private Declare Function GetNumTasks Lib "Kernel" () As Integer

The GetNumTasks function does not require any arguments but simply
returns an integer value set to the number of tasks that are currently
running under Windows. Therefore, to determine when your MS-DOS
program has finished executing, call GetNumTasks first, saving the
value it returns in a variable. After the MS-DOS program has finished
executing, call the GetNumTasks function a second time to find out if
the number of tasks has decreased by a value of one. If the value has
decreased, you know that your MS-DOS program has finished executing
and you can return control to your Visual Basic application.

Example Program
This program shows how you can use the Shell function and the
GetNumTasks function to execute an MS-DOS program. This program
assumes that you have the PKUNZIP program stored in the UTILS
directory and that you have previously created a destination
directory called DESTDIR on your hard drive.

1. Create a new project in Visual Basic. Form1 is created by default.
2. Add the following statements to the General Declarations section
of Form1:

Dim ActiveApps As Integer
Private Declare Function GetNumTasks Lib "Kernel" () As Integer

3. Add a Command Button control to Form1. Command1 is created by
default.
4. Add the following code to the Click event for Command1:

Private Sub Command1_Click()
Dim AppDir As String
Dim Zip As String
Dim Y As Integer
Dim X As Integer

AppDir = "c:\destdir"
ActiveApps = GetNumTasks()

Zip = "c:\utils\pkunzip " & "c:\destdir\" & "test.zip" & " " & AppDir
X = Shell(Zip, 2)
SendKeys "%{enter}EXIT%{ }n"

Do While GetNumTasks() <> ActiveApps
Y = DoEvents()
Loop
MsgBox "Pkunzip is finished", 0, "Demo Program"
End Sub

Additional References
Knowledge Base Q96844. "How to Determine When a Shelled Process Has
Terminated."
"SendKeys Statement." (Product Documentation, Office Developer's
Kit 1.0, Visual Basic 3.0 Professional Edition, Language Reference)
"Shell Function." (Product Documentation, Office Developer's
Kit 1.0, Visual Basic 3.0 Professional Edition, Language Reference)