101 Tech Tips For VB Developers 004
101 Tech Tips For VB Developers 004
For VB Developers
Dim i As Integer
For i = 1 To num_lines msg = msg & Chr(13) & Chr(10) Next NL = msg End Function
VB4 16/32
Level: Beginning
FEBRUARY 1997
1 01 TECH TIPS
For VB Developers
If FalseClick Then Exit Sub FalseClick = True CommonDialog1.DialogTitle = "Open File" CommonDialog1.filename = "*.*" CommonDialog1.DefaultExt = ".*" CommonDialog1.CancelError = True CommonDialog1.Filter = "All Files (*.*)|*.*" On Error Resume Next CommonDialog1.Action = 1 If Err = CDERR_CANCEL Then FalseClick = False Exit Sub End If On Error GoTo 0 DoEvents 'allow for recursion FalseClick = False End Sub
VB4 16/32
Level: Intermediate
The DoEvents function is required in any procedure that is so short that the last statement executes before the recursion begins. I hope this workaround can help others fix the same problem. Michael P. Rose, Kingwood, Texas
VB3
Level: Intermediate
Run the program, click on the command button, and when the common dialog appears, move it so that the name of any file appears over the button. Double-click on the file name. The common dialog will disappear and a new one will appear, resulting from the SSCommand1_Click event firing again. The solution is to declare a static flag variable, FalseClick, within the buttons Click event. Then change the code in the SSCommand1_Click procedure to read:
Private Sub SSCommand1_Click() Const CDERR_CANCEL = &H7FF3 Static FalseClick As Boolean
1 01 TECH TIPS
For VB Developers
& strretsep & DD & strretsep & "YYYY" case "Y" : datemask = "####" _ & Strretsep & "##" & strretsep & "##" dateformat = "YYYY" & _ strretsep & "MM" & strretsep & "DD" End select End Sub
In the Form_Load event, call the DateCheck procedure to get the current date format, and assign format and mask properties of the Masked Edit control:
Sub Form_Load() DateCheck ' Call the datecheck procedure Mskdd.format = dateformat Mskdd.mask = datemask End Sub
Dim File As String File = Dir1.Path If Right$(File, 1) = "\" Then File = File & "himem.sys" Else File = File & "\" & "himem.sys" End If ' Chuong Van Huynhs tip If Dir$(File) <> "" Then MsgBox "himem.sys exists !" End If End Sub
Without testing to see if the path already ends with a backslash, as it would when its the root directory, an error occurs because of the string c:\\himem.sys. Michel Rohan, Menthonnex Sous Clermont, France
VB4 32
Level: Intermediate
TRIMMED TO FIT
This piece of code trims long file names to fit into forms captions, text boxes, and other limited spaces. The code allows you to specify the number of characters a file name must be before it performs the trimming. For example, if the label can hold 50 characters, then you would type LongDirFix(nFile,50). Its as simple as that. Heres the code:
Function LongDirFix(Incomming As _ String, Max As Integer) As String Dim i As Integer, LblLen As Integer, StringLen As Integer Dim TempString As String TempString = Incomming LblLen = Max If Len(TempString) <= LblLen Then LongDirFix = TempString Exit Function End If LblLen = LblLen - 6 For i = Len(TempString) - LblLen To Len(TempString) If Mid$(TempString, i, 1) = "\" Then Exit For Next LongDirFix = Left$(TempString, 3) + _ "..." + Right$(TempString, Len(TempString) - (i - 1)) End Function
This trick may prove useful with any application you pass file names to. It would be smart to try passing strange file names/paths to make sure. Andrea Nagar, Torino, Italy
FEBRUARY 1997
1 01 TECH TIPS
For VB Developers
VB4 16/32
Level: Beginning
VB4 16/32
Level: Intermediate
Note that for this example, the Windows directory name is hard coded. For a production app, call the GetWindowsDirectory API and use that return value instead. Fred Lyhne, Salt Spring Island, British Columbia, Canada
VB4 16/32
Level: Intermediate
Its no longer necessary to refer to DBGrid1.Columns(0).Value, for example, but you can use ColOrder_ID.Value instead. ColOrder_ID is, of course, the same as DBGrid1.Columns(0). If any property value of DBGrid1.Columns(0) changes, the same property value of ColOrder_ID changes accordingly, and vice versa. Later on, when you insert a database field into the grid, you change only the index numbers of the columns in the code of the Form_Load event. George van der Beek, Nieuw Lekkerland, The Netherlands
VB4 32
Level: Intermediate
1 01 TECH TIPS
For VB Developers
label with the same text thats going to be in the header. Then use Label1.Width in the Add argument for the ColumnHeader:
Private Sub Command1_Click( ) Dim ColumnText as String, clmx as ColumnHeader ColumnText = _ "A very long header for " & "the ListView control" Label1.Caption = ColumnText set clmx = ListView1.ColumnHeaders.Add_ (, , ColumnText, Label1.Width) ListView1.View = lvwReport End Sub
VB3, VB4 16
Level: Advanced
VB3
Level: Beginning
Use a label control covering the visible area of the form to allow switching on and off:
If form1.Height = 0 Then form1.Move form1.Left, form1.Top, _ form1!Label1.Width, form1!Label1.Height Else form1.Move form1.Left, form1.Top, 0, 0 End If
The next routine writes a two-dimensional integer array (integer_array) to a binary file (binary_file) with the API function hwrite. The array can later be read back from the file with a nearly identical routine calling the hread API function. It is straightforward to modify this procedure for other sizes and types of arrays (except for arrays of user-defined types and variable-length strings), but it cannot be made generic over any number of array dimensions because of the way these API functions are called:
Sub WriteIntegerArrayToFile_ (ByVal binary_file As String, _ integer_array() As Integer) Const INTEGER_BYTE_SIZE = 2 Dim binary_file_handle As Integer, _ dos_file_handle As Integer Dim bytes_written As Long, bytes_to_write As Long 'get the size of the array in bytes: bytes_to_write = _ (UBound(integer_array, 1) - _ LBound(integer_array, 1) + 1) _ (UBound(integer_array, 2) - _ LBound(integer_array, 2) + 1) _ INTEGER_BYTE_SIZE 'open the file in binary mode: binary_file_handle = FreeFile Open binary_file For Output As binary_file_handle dos_file_handle = _ FileAttr(binary_file_handle, 2) 'make the API call: If dos_file_handle <> 0 Then bytes_written = _ hwrite(dos_file_handle, integer_array ( _ LBound(integer_array, 1), _ LBound(integer_array, 2)), _ bytes_to_write) End If Close binary_file_handle End Sub
The form remains loaded but invisible and is immediately available when required. The label control should consume no extra resources. This method provides a quick popup window within MDI. Ross Gourlay, Edinburgh, Scotland
FEBRUARY 1997
1 01 TECH TIPS
For VB Developers
VB4 32
Level: Intermediate
VB3
Level: Beginning
Public MyDB As Database Public MyRS As Recordset Dim TestVariant As Variant Set MyDB = OpenDatabase("testjet3db") Set MyRS = MyDB.OpenRecordset("NameTable") 'Sample Calls TestVariant = ScreenForNull(MyRS![FirstName]) TestVariant = ScreenForNull(MyRS![FirstName], " ") Public Function ScreenForNull(aField As Variant, Optional _ ByVal DefaultReturn As Variant) As Variant If IsNull(aField) Then If Not _ IsMissing(DefaultReturn) Then ScreenForNull = DefaultReturn Else ScreenForNull = "" End If Else ScreenForNull = aField End If End Function
When the user tries to resize the form, the sizing rectangle will be visible, but the form will snap back to its former size. Ken Zinn, Miamisburg, Ohio
1 01 TECH TIPS
For VB Developers
Left(Command$, InStr(Command$, " ") - 1) End If 'start the program x = Shell(Left(Command$, InStr(Command$, " ") - 1), 3) End Exit Sub errorhandler: If Err = 53 Then 'file not found Resume Next 'copy the file anyway Else 'trap other errors MsgBox "Error # " & Err & Chr(10) & Error _ & Chr(10) & "program will be terminated" End End If Exit Sub End Sub
VB3
Level: Intermediate
AUGMENTING ERROR$
In many places, I include Error$ as picked up in an error trap in a descriptive message. I use a function to expand its meaning. I call the function TSS for Time Stamped String to keep it short. It works something like this:
MsgBox TSS(Error$) & " in Mytest" Public Function TSS (ByVal aString As String) If Len(aString) Then TSS = aString & " at " & CStr(Now) Else TSS = "Error$ is Blank at " & CStr(Now) End If End Function
CALLING ON WORD
Microsoft Word exposes the WordBasic object. Through this object, you can execute WordBasic statements. The WordBasic statements and functions can be used as methods of the WordBasic object. Most WordBasic method names match the menu selection available in Word, and the parameters match the dialog items:
'Declare form level Dim wd As Object Sub CreateWordObject( ) Set wd = CreateObject ("Word.Basic") wd.FileNewDefault wd.FontSize 20 wd.Insert "Hello, World" wd.FileSaveAs "Hello.Doc" wd.FileClose
If you are familiar with Microsoft Word, you know that each method name corresponds to the menu name concatenated by submenu option names, and the functionality is the same as in Word. You can also use the Word Macro recorder to create code and then copy that code to your Visual Basic application. Shikha Arora, Detroit, Michigan
For more information, check out the Microsoft Knowledge Base article Q130650. Clinton D. Hess, Ocoee, Florida
FEBRUARY 1997
1 01 TECH TIPS
For VB Developers
VB4 16/32
Level: Beginning
HardLockTable = False MsgBox Error$ & " error " & _ "in HardLockTable trying " & _ "to " & whichAction & " " & _ aTable Resume HardLockTableErrorExit End Function
VB4 16/32
Level: Intermediate
HARD-LOCK A TABLE
In many applications, I want to make absolutely sure that the data in a Jet table doesnt get modified under any circumstances. I hard-lock the table in addition to using any system-level security to protect it. Also, this hard lock stays with the MDB if it is issued with an application. Place the expression True=False into the ValidationRule property of the table to lock it. The Jet evaluates this expression to False and blocks updates to the table:
'Declarations Public MyDB As Database Dim Dummy As Integer 'Sample calls Dummy = HardLockTable("UnLock", "TestTable") Dummy = HardLockTable("Lock", "TestTable") Function HardLockTable_ (ByVal whichAction As String, _ ByVal aTable As String) As Integer On Error GoTo HardLockTableError 'Default return HardLockTable = True Select Case whichAction Case "Lock" MyDB.TableDefs(aTable).ValidationRule = "True=False" MyDB.TableDefs(aTable).ValidationText = _ "This table locked via " & _ "ValidationRule on " & Now Case "UnLock" MyDB.TableDefs(aTable).ValidationRule = "" MyDB.TableDefs(aTable).ValidationText = "" Case "TestThenUnLock" If MyDB.TableDefs(aTable)._ ValidationRule = "True=False" Then MyDB.TableDefs(aTable).ValidationRule = "" MyDB.TableDefs(aTable).ValidationText = "" End If End Select HardLockTableErrorExit: 'subFlushDBEngine 'optional, see next suggestion Exit Function HardLockTableError:
This next code sets up a tree with three branches. The first branch has three leaves, the second branch has two leaves, and the third branch has five leaves:
ReDim Tree(1 To 3) 'three branches ReDim Tree(1).Branch(1 To 3) 'three leaves on the first branch ReDim Tree(2).Branch(1 To 2) 'two leaves on the second branch ReDim Tree(3).Branch(1 To 5) 'five leaves on the third branch
You can add other fields to allow parts of the tree other than the leaves to store data. Dave Doknjas, Surrey, British Columbia, Canada
1 01 TECH TIPS
For VB Developers
Printer.Print Clipboard.GetText () 'Form Feed in case printer needs it 'to print the last page. Printer.NewPage 'End the print job. Printer.EndDoc 'Unmark the Sub or Function. SendKeys "{HOME}", True End Sub
This will tab to all the controls, so if you dont want to tab to a command button, set its tab stop to False. Mark Patenaude, Santee, California
Move the form to a spot you want it to appear in when running. Compile it to an EXE file. Run it along with Visual Basic. When you want to print a sub or function, make sure the cursor is in the Sub or Functions code box, and click on the Print Sub button. Printer.EndDoc called immediately after Printer.NewPage ensures that no blank page is printed. Kenneth L. Creel, Lynden, Washington
VB3
Level: Intermediate
If you have multiple text controls needing this input, put the Sub in a code module, and call it from the KeyPress event of the Text controls whenever you need it. Similarly, 32 = 00100000, so to convert from upper to lower case, use:
If KeyAscii > 64 and KeyAscii < 91 Then KeyAscii = _ (KeyAscii Or 32)
FEBRUARY 1997
1 01 TECH TIPS
For VB Developers
TABLE CHECKER
I use a housekeeping routine to check the state of tables during system initialization. The routine tests whether the table is in a collection, whether the table has data, and whether the table is local or attached. The routine helps to establish the state of the application at startup. For example, a table name that appears in the Tables collection can be either attached or local. If it is attached and the second MDB where it resides is not present, this state is only discovered when the application first attempts to access it. Usually, an error condition occurs in the middle of application logic. I prefer to test for this condition at the beginning as opposed to being surprised later on. I perform a trial read on the attached table:
Public MyDB As Database Dim Dummy As Integer Set MyDB = OpenDatabase("testjet3db") 'Sample call Dummy = CheckTable("NameTable", "readarecord", "local") 'More sophisticated call to trial read an attached table If Not CheckTable("AnotherTable", _ "readarecord", "attached") Then 'weve got trouble in River City End If Function CheckTable(ByVal whichTable As String, _ ByVal whichTest As String, _ ByVal whichAttach As String) As Integer Dim ErrorStage As String Dim aTable As Recordset ' This is a comprehensive table checker ' which tests for: ' .is the table in the collection ' .is it attached or local as the case may be ' NOTE: have found subtle differences in ' how DAO Find FindNext ' commands behave wrt attached and ' local tables ' message boxes are useful in ' debugging, can be removed On Error GoTo CheckTableError 'Set Default return condition CheckTable = True ErrorStage = " Test for table in collection " If Not CheckIfTableInCollection(whichTable) Then CheckTable = False MsgBox ErrorStage & whichTable _ & " failed in CheckTable" Exit Function End If ErrorStage = "SetTable" Set aTable = MyDB.OpenRecordset(whichTable) ErrorStage = "Test read a record " If whichTest = "readarecord" Then aTable.MoveFirst End If ErrorStage = "Test attachment status " Select Case whichAttach Case "attached" If InStr(MyDB.TableDefs(whichTable).Connect, _ "DATABASE=") = 0 Then CheckTable = False
MsgBox ErrorStage & whichTable & _ " failed " & whichAttach & _ " in CheckTable" End If Case "local" If MyDB.TableDefs_ (whichTable).Connect <> "" Then CheckTable = False MsgBox ErrorStage & whichTable & _ " failed " & whichAttach & _ " in CheckTable" End If Case "dontcare", "" End Select CheckTableErrorExit: If ErrorStage <> "SetTable" Then aTable.Close End If Set aTable = Nothing Exit Function CheckTableError: CheckTable = False MsgBox Error$ & " ErrorTrap " & _ ErrorStage & " " & whichTable _ & " failed in CheckTable" Resume CheckTableErrorExit End Function Function CheckIfTableInCollection_ (ByVal TableName As String) _ As Integer ' This function checks that the tables ' collection has a table ' it looks for tables by name and ' returns true/false setting Dim i% CheckIfTableInCollection = False For i% = MyDB.TableDefs.Count - 1 _ To 0 Step -1 DoEvents If TableName = Trim$(MyDB._ TableDefs(i%).Name) Then CheckIfTableInCollection = True End If Next i% End Function
VB3, VB4 16
Level: Intermediate
10
1 01 TECH TIPS
For VB Developers
'item ListBox_getItemHeight = SendMessage ((1st.hWnd), _ LB_GETITEMHEIGHT, 0, &) End Function 'Sets the height of the items of the 'listbox to a specified one. Sub ListBox_setRowHeight (1st As Control, 1Height As Long) Dim ignore as integer ignore = SendMessage ((1st.hWnd), LB_SETITEMHEIGHT, 0, _ ByVal 1Height) 'Refresh the listbox ignore = SendMessage ((1st .hWnd), WM_SETREDRAW, True, 0&) End Sub
VB4 32
Level: Intermediate
However, you can use VBs MOD operator to perform the test in roughly half the time:
If (Weekday (sDate) MOD 6 = 1) Then 'Its a weekend End If
VB4 16/32
Level: Intermediate
Next, insert a new form into the project. Add a text box, Text1, to this form and place this code in the Form_Load event procedure:
Call NumericEdit(Text1)
VB4 16/32
Rank: Beginning
FEBRUARY 1997
11
1 01 TECH TIPS
For VB Developers
VB3, VB4 16
Level: Intermediate
mand line with full path. Here you give the action name as Register and press Browse to select the regsvr32.exe file. Normally, Regsvr32.exe resides in the Windows directory. Now the entry looks like C:\Windows\Regsvr32. Press OK to save the action. Then, for Unregister, give the action name as Unregister and press Browse to select the regsvr32.exe file. Add the parameter /u for unregister. Now the entry looks like C:\Windows\Regsvr32 /u. Remember, when adding a parameter, the path name should follow the MS-DOS path convention. By pressing the right mouse button on any OCX file listed in the Explorer, you will see the Register and Unregister option. Select the appropriate action to perform. The same procedure can be done for any file type. Using this option of Explorer, you can edit any existing action on any file type shown in the list, as well as remove the actions or the whole entry for the selected file type. System Builders International, New Dehli, India
VB4 16/32
Level: Beginning
Every time you load a new picture, the scrollbars will adjust to its size. Write the code to load the pictures, and write other code to deal with exceptions to the rules. Joel Paula, Carcavelos, Portugal
1 01 TECH TIPS
For VB Developers
VB4 16/32
Level: Beginning
AVOID THE VALIDATION ROUTINE AUTOMATICALLY RESIZE WHEN THE USER PRESSES CANCEL CONTROLS
When a user presses the Cancel button, the control that previously had focus triggers its LostFocus event, which is where many programs do their validity checking. If the input wasnt filled, as is often the case when the user clicks on Cancel, the validation may display a message. The user wont expect this after clicking on Cancel, and shouldnt ever see it. To avoid this problem, add a form-level variable to indicate if Cancel has been clicked on. In the Cancels MouseDown event, set it to True. In the MouseUp event, set it back to Falsethis way the LostFocus event occurs between the two. One more catchif the user activates the Cancel button with a mouse-down click on the Cancel button, changes his or her mind, and moves the mouse off the Cancel button and then mouseups, you still have it set to Cancel. So set it to False in the MouseMove event of the parent of the Cancel button. To avoid doing validations, check against your Cancel Pressed variable. Jed Walker, Denver, Colorado Sometimes you need to change the size of controls by changing the size of a container. For example, try this:
Private Sub Form_Resize() Picture1.Align = vbAlignLeft Picture1.Align = vbAlignTop End Sub
Every sizing event that changes the forms size also changes the size of the picture box. Make sure that the control you want to resize has the align property. Uwe Pryka, Bochum, Germany
VB4 16/32
Level: Intermediate
VB4 16/32
Level: Intermediate
FEBRUARY 1997
13
1 01 TECH TIPS
For VB Developers
This code relies on a 16-bit-only API function, GetModuleUsage. Several 32-bit workarounds have been published, but here is the simplest and most reliable solution Ive seen:
Declare Function OpenProcess Lib "Kernel32" _ (ByVal dwDesiredAccess As Long, _ ByVal bInheritHandle As Long, _ ByVal dwProcessId As Long) As Long Declare Function WaitForSingleObject Lib "Kernel32" _ (ByVal hHandle As Long, _ ByVal dwMilliseconds As Long) As Long Const SYNCHRONIZE = &H100000 Const INFINITE = &HFFFFFFFF Private Function SyncShell(ByVal pathname As String, _ windowstyle As Integer) As Boolean Dim ProcessID As Long Dim ProcessHandle As Long ' In VB4, an error occurs if Shell ' fails to start the program On Error GoTo SyncShell_Error ' Shell the program, get its handle, ' and wait for it to terminate ProcessID = Shell(pathname, windowstyle) ProcessHandle = OpenProcess(SYNCHRONIZE, True, ProcessID) WaitForSingleObject ProcessHandle, INFINITE SyncShell = True Exit Function SyncShell_Error: On Error GoTo 0 SyncShell = False Exit Function End Function
This code retrieves an IP address from an Access table, where the host ID in the table matches the host ID in the parameter. This code is stored in the Access database and has already been parsed and optimized. This is the key to gaining the performance benefits of stored queries. The Jet engine then uses this QueryDef when it is called from VB. Assume that there is a form with a data control on it. Here is the corresponding VB code to send the parameter:
Dim db As Database Dim rs As Recordset Dim qd As QueryDef Set db = dthosts.Database 'dthosts is a data control on a form Set qd = db.QueryDefs("aq_host") 'aq_host is the MS Access query name qd.Parameters("ID") = "MY_Host_ID" 'host ID is the parameter for this query Set rs = qd.OpenRecordset() 'open the recordset Set dthosts.Recordset = rs 'return the recordset to the data control
You can now use this data control in conjunction with a listbox control (or whatever control you like) to display the data. You can also add multiple parameters by adding more qd.parameters to the middle section of this code. Insert, update, and delete queries (or action queries, as they are called in Access) can use parameters in the same fashion. Al Gehrig, Jr., Laguna Hills, California
VB4 32
Level: Intermediate
14
1 01 TECH TIPS
For VB Developers
This method works well with Windows 3.11. You can use this basic idea with any application you write, not just screen savers. Uwe Pryka, Bochum, Germany
For the icon to appear as a mouse pointer, both of these properties must be set. Prashanti Doma, Inkster, Michigan
VB4 16/32
Level: Intermediate
VB4 16/32
Level: Beginning
Code Profiler does not understand the context of the file reference. Before running the Profiler, change all the type relative references to full references:
Module=PASSMAN1; PASSMAN.BAS Module=CRW45; C:\DEV\CM4\CRW45.BAS
FEBRUARY 1997
15
1 01 TECH TIPS
For VB Developers
Disable List2 so that the user is forced to use only the scrollbar of List1, but still sees the information displayed in List2. Eric Bernatchez, Montreal, Quebec, Canada
SPEED TIPS
Reading data into and out of a VB program is straightforward. Most folks use the Input and Put statements in a ForNext loop. However, this straightforward technique takes time. Because I am impatient when it comes to machinesand that includes computersI use a time-tested technique to speed along the input and output of data files. The technique involves the use of strings. I use strings because Basic-type programs, including VB, make efficient use of strings. One distinct reason for using strings is that a specific size is not needed for the array definition. This provides some programming flexibility if you work with files of different lengths and formats. Also, and more to the point, data is input and output faster when the Input and Put statements move strings into and out of computer programs. Speed is the big advantage of using strings in conjunction
16
1 01 TECH TIPS
For VB Developers
with the Input and Put statements, in lieu of embedding the statements in a ForNext loop. The speed increase is obvious with a few simple illustrations. Start with this program listing:
Open Path$ For Binary As #1 Datarray$ = Input(31680, #1) Workarray$ = Datarray$ Close
with the appropriate name you are looking for. Calvin Smith, San Francisco, California
VB4 16/32
Level: Intermediate
This listing inputs a string named Datarray and copies that string to a work string named Workarray. I do this to maintain the integrity of the original data. As you can see, the code opens, inputs, copies, and closes a complete file in just four computer instructions. For comparison, look at a typical code listing to read this file. This is the simplified code listing:
Open Path$ For Binary As #1 For Position = 1 to 31680 Datarray$(Position) = Input(1,#1) Next Position Close
This example inputs data with a ForNext loop. Although the number of programming lines may be relatively small, the number of computer instructions is large. To input the same file with a ForNext loop, the For, Next, and Input statements each need to be executed 31,680 times. Thats a whole heap of instructions when compared to executing the Input statement just once. The speed implication is obvious. My programming philosophy to output data follows the same techniqueeliminate data loops where possible. This example shows how this is easily accomplished without a data loop:
Open Path$ For Binary As #1 Put #1,,Workarray$ Close
VB4 16/32
Level: Intermediate
Path$ is the output location and name of the file. Workarray is the string that holds the data to save. In summary, it is important to limit the length of ForNext and Do loops in any language. Long loops for I/O are real speed killers. Donald L. Parrish, Huntsville, Alabama
IS MAIL RUNNING?
Use this code to quickly determine whether Microsoft Mail is currently running:
Declare Function GetModuleHandle Lib _ "Kernel" (ByVal lpModuleName As String) As Integer Function IsMicrosoftMailRunning () On Error GoTo IsMicrosoftMailRunning_Err IsMicrosoftMailRunning = GetModuleHandle("MSMAIL") IsMicrosoftMailRunning_Err: If Err Then 'Do whatever you need to here End If End Function
You can also use this method to determine if just about any particular program is running. Simply replace the Mail references
Supplement to Visual Basic Programmers Journal FEBRUARY 1997 17
1 01 TECH TIPS
For VB Developers
(ByVal lpBuffer As String, _ ByVal nSize As Integer) As Integer Function WinDirs$ (strDirNameNeeded$) On Error GoTo WinDirs_Err Dim strBuffer$ Dim iSize% Dim iResult% iSize = 256 strBuffer$ = Space$(iSize) Select Case strDirNameNeeded$ Case "WindowsDirectory" iResult% = wu_GetWindows_ Directory(strBuffer$, iSize) Case "WindowsSystemDirectory" iResult% = wu_GetWindows_ SystemDirectory(strBuffer$, iSize) End Select WinDirs$ = Left$_ (strBuffer$, iResult%) WinDirs_Err: If Err Then ' Do whatever you need to here End If End Function
This is a simple Edit menu with cut, copy, and paste options. You can copy this section, for example, and paste it into another form you have open in Notepad. Words to the wise: back up the files first (always be safe), and dont randomly paste the section in the recipient form. Make sure it is between other objects defined there. For example, insert the menu where the asterisk is:
Top = Width -30 = 7215
18
1 01 TECH TIPS
For VB Developers
As a result, Ive developed new programming style. My read / write and special functions are all in one routine selected by case logic based on descriptive strings. The strings are used only within the programs and are self documenting to some extent. Data is passed through a Public WorkVariant array:
Public MyDB As Database Public WorkVariant(10) As Variant Dim Dummy As Integer Set MyDB = OpenDatabase("testjet3db") 'Sample calls Dummy = NameTableIO(writerecord) Dummy = NameTableIO("readrecord") Public Function NameTableIO_ (ByVal whichAction As String) As Integer 'Assumes navigation to the record has 'been made externally Dim MyTable As Recordset Dim ErrorStage As String On Error GoTo NameTableIOError NameTableIO = True ErrorStage = "OpenRecordSet" Set MyTable = MyDB.OpenRecordset("NameTable") ErrorStage = "AfterOpen" Select Case whichAction Case "readrecord" WorkVariant(1) = MyTable![FirstName] WorkVariant(2) = MyTable![Age] Case "writerecord" MyTable.Edit MyTable![FirstName] = WorkVariant(1) MyTable![Age] = WorkVariant(2) MyTable.Update Case "validateage" 'specialized routine 'etc End Select NameTableIOErrorExit: If ErrorStage <> "OpenRecordSet" Then MyTable.Close End If Set MyTable = Nothing Exit Function NameTableIOError: NameTableIO = False MsgBox Error$ &_ " Error trap in NameTableIO " & _ "at " & ErrorStage & " Action: " & whichAction Resume NameTableIOErrorExit End Function
Note that you must surround the search value with quotes if you are searching a text field; no quotes are necessary if the target field contains numeric data. Claudia Vega Cach, Tepic, Nayarit, Mexico
FEBRUARY 1997
19
1 01 TECH TIPS
For VB Developers
If Len(TempString) > 40 Then TempString = Mid$(TempString, 1, 40) End If Set MyTableDef = MyDB.CreateTableDef_ (TempString) Set MyField = MyTableDef._ CreateField("MyDate", dbDate) MyTableDef.Fields.Append MyField MyDB.TableDefs.Append MyTableDef Set MyField = Nothing Set MyTableDef = Nothing MDBrand = True Case "validate" For i% = MyDB.TableDefs.Count - 1 To 0 Step -1 If Mid$(LTrim$(MyDB.TableDefs(i%)._ Name), 1, Len(BrandString)) = _ BrandString Then If InStr(MyDB.TableDefs(i%)._ Name, aString) Then MDBrand = True End If End If Next i% End Select MDBrandErrorExit: Exit Function MDBrandError: MDBrand = False Resume MDBrandErrorExit End Function
Then, when you need to determine which value was selected, simply query the Tag property of the frame like this:
intGender = fraGender.Tag
20
1 01 TECH TIPS
For VB Developers
VB4 32
Level: Intermediate
also add a parser to parse out the function call to accept parameters:
Dim CommandArray_() 'This parses the function into its parameters ParseCommand Label1.Caption, CommandArray_() 'CommandArray_(0) is the function name 'CommandArray_(1...n) are the parameters Select Case CommandArray_(0) Case "Add2Array" Add2Array Internal_(), CommandArray_(1) Case "DeleteFromArray" DeleteArray Internal_(), _ CommandArray_(1), CommandArray_(2) ... End Select
Here is my solution:
Set Picture1.Picture = _ ImageList1.ListImages(1).Picture Picture1.Scalemode = 3 'pixels Picture1.PaintPicture ImageList1._ ListImages(2).Picture, 0, 0
When I need to add a row to my array from outside my procedure (from an MDI parent, lets say) I set it to this, and away it goes:
Form1.Label1.Caption = "Add2Array 1"
Because of the way VB indexes collections, it can remove items much more rapidly from the beginning of a collection than it can from the end. This code executes in only 1.5 seconds:
' Remove item 1, instead of item I ' (can loop in either direction) For I = 1 To colTest.Count colTest.Remove 1 Next
And, of course, if youre clearing an entire collection, its even faster to simply set it to Nothing:
' Runs in 0.35 seconds Set colTest = Nothing
VB makes too many redraws in a small period of time, so you can hardly read the text of the Label. The solution is to slightly increase the duration of each appearance of the Label so the human eye has enough time to see it. To obtain the same scrolling speed, you must increase the steps of the Label from 15 to 30, or even 60. Heres how to do it:
Public Sub Scrolling() Label1.Left = Me.Width Do Label1.Left = Label1.Left - 60 temp = Timer Do DoEvents Loop Until Timer - temp > 0.1 Loop Until Label1.Left <= -(Label1.Width + 15) End Sub
VB3
Level: Beginning
Doing so also makes the animation independent of the CPUs speed. Eric Bernatchez, Montreal, Quebec, Canada
FEBRUARY 1997
21
1 01 TECH TIPS
For VB Developers
In each of the forms to include this functionality, define these form-level variables to store the name of the form that this form was shown from:
Dim from_form_this As String
When you need to show a form from which you want to be able to return, include this code:
Sub btn_nextform_Click () from_form_tag = Me.Tag frm_nextform.Show End Sub
The code sets the Global variable to the name of the calling form. Then, in the new form you are showing, include this code:
Sub Form_Activate () If from_form_tag <> "" Then from_form_this = from_form_tag End If from_form_tag = "" End Sub
This sets the form-level variable to the name of the previous form, which it retains until the form is unloaded, allowing a chain of forms to go back to. Finally, in order to return back to the previous form that was shown, include this code:
Sub Dim Dim For btnback_Click () i As Integer from_form As Form i = 0 To forms.Count - 1 If forms(i).Tag = from_form_this Then Set from_form = forms(i) from_form.Show Exit For End If Next 'i End Sub
This segment of code goes through each loaded form, comparing the Tag value of that form to the value held in the formlevel variable. When it finds the form, it sets a form variable to the found form and then shows it. Campbell Maffett, Ascot Vale, Victoria, Australia
1 01 TECH TIPS
For VB Developers
sdirs(iLevel, iDirCount_ (iLevel)) = dir1.List(i) Next i Next j 'Load all the directory names in a list box List1 list1.Clear If iDirCount(iLevel) = 0 Then 'When no more sub-directories exist For i = 1 To iLevel For j = 1 To iDirCount(i) list1.AddItem sdirs(i, j) Next j Next i Exit Do End If Loop
VB4 32
Level: Beginning
FEBRUARY 1997
23
1 01 TECH TIPS
For VB Developers
AN INBETWEEN FUNCTION
This function returns True if the first number in the argument is in between the next two arguments. Use this function to detect collisions, and for many other useful numeric functions:
Function InBetween_ (ByVal Num As Variant, ByVal X As Variant, _ ByVal Y As Variant) As Boolean InBetween = False If IsNumeric(Num) And IsNumeric(X) _ And IsNumeric(Y) Then If X < Y Then If Num > X And Num < Y Then InBetween = True End If ElseIf Y < X Then If Num > Y And Num < X Then InBetween = True End If End If End If End Function
For example:
X = IsBetween(1, 5, 3) X = IsBetween(2, 12, 6) 'X returns True 'X returns False
STACK EM UP
A status panel on an MDI form is an ideal way to keep your user informed about what the system is currently doing. Often, you may have several nested processes that may write to that status panel as each one executes. Maintaining an accurate status through these nested processes can be messy, if not impossible. This code allows you to maintain a stack of statuses to allow for this type of nesting:
Global StatusPanelStack(1 To 10) As String Global StatusPanelSize As Integer Sub PushStatusPanel () StatusPanelSize = StatusPanelSize + 1 If StatusPanelSize > UBound(StatusPanelStack) Then Redim Preserve StatusPanelStack_ (1 To UBound(StatusPanelStack) + 10) As String End If StatusPanelStack(StatusPanelSize) _ = MDIMain.pnlMain.Caption End Sub Sub UpdateStatusPanel (StatusText As String) PushStatusPanel MDIMain.pnlMain.Caption = Trim$(StatusText) End Sub Sub PopStatusPanel () StatusPanelSize = StatusPanelSize - 1 If StatusPanelSize = 0 Then MDIMain.pnlMain.Caption = "Ready" Else MDIMain.pnlMain.Caption = _
24
1 01 TECH TIPS
For VB Developers
Server", lpszAttributes) 'if you already have data source with 'the same name, display confirmation 'message; otherwise, setup ODBC If iRet <> 1 Then iRet =SQLConfigDataSource(setup1.hWnd, 1, _ "SQL Server", lpszAttributes) If iRet <> 1 Then MsgBox _ "Error during setup of the " & _ "Data Source. Install data " & _ "source manually" End If
VB4 16/32
Level: Beginning
Remember that some of the attributes depend on the way your SQL Server is set up. For example, if you exclusively use Named Pipes, the address parameter will not be included. Eugene Dvorkin Weehawken, New Jersey
You can also use this tip to do many other things, such as making all command buttons disabled, making List Indexes of all combo boxes -1, and so on. You only need to modify the If condition and property of that control. Adesh Jain, Newington, Virginia
This routine swaps the values of A and B. There is no need to use a third variable:
Private Sub SwapThis (A As Integer, B As Integer) 'before the swap MsgBox "A = " & Str(A) & " B = " & Str(B) A = A + B B = A - B A = A - B 'After the swap MsgBox "A = " & Str(A) & " B = " Str(B) End Sub
Note that this code crashes with an overflow error if (A+B) > 32K. Edwin M. de Guzman, Houston, Texas
FEBRUARY 1997
25
1 01 TECH TIPS
For VB Developers
These easy steps will save you a lot of work. Gonzalo Medina Galup, Miami, Florida
REPLICATE MENUS
Press the Menu Design icon in VB. Make the menu follow these steps:
caption: name: index: Menu 1 mnuMain1 0
VB4 16/32
Level: Intermediate
Press the icon with the right arrow to create the menu popup. Add this code:
caption: name: idex: caption: name: index: caption: name: idex: Item 1 mnuItem1 1 Item 2 mnuItem1 2 Item 3 mnuItem1 3
Place a Sheridan 3D Panel on your form, change the name to pn3Status, clear the Caption property, and set the FloodType property to 1 (Left to Right). Then place this code on your form:
Private Sub SetStatus(value%) If value < 0 Or value > 100 Then Exit Sub pn3Status.FloodPercent = value pn3Status.FloodColor = _ RGB(-255 * (value >= 50), _ -255 * (value < 75), 0) End Sub
Repeat the second step, changing the Menu title from Menu 1 to Menu 2. When you see the menu in your form, save it. Call Notepad or Write to see how VB saves the form. Copy these lines:
Begin Menu mnuMain1 Caption = "&Menu 1" Index = 0 Begin Menu mnuItem1 Caption = "Item Index = 1 End Begin Menu mnuItem1 Caption = "Item Index = 2 End Begin Menu mnuItem1 Caption = "Item Index = 3 End End Begin Menu mnuMain2 Caption = "M&enu 2" Index = 0 Begin Menu mnuItem2 Caption = "Item Index = 1 End Begin Menu mnuItem2 Caption = "Item Index = 2 End Begin Menu mnuItem2 Caption = "Item Index = 3 End End
1"
This not only updates the percentage, but it changes the FloodColor accordingly. You can also put this into a Property procedure and make it public so that the status can be set from elsewhere in the project. To test this function, place this in a command button Click event:
For i = 1 To 100 SetStatus (i) Next
2"
3"
VB4 16/32
Level: Intermediate
1"
2"
3"
Open the other file and paste it. Save the new file with another name, such as mnuHead. Each time you need to make a menu, you simply go to mnuHead, copy these lines, open the form, and paste it. Remember to save the form in text mode. If I need five, I only copy and paste five. Remember to insert the End of Begin Menu mnuMain1. Rename each of the items before you save the form.
26
The last argument should be on the right side of the assignment. K. Ramesh, Kottur, Madras, India
1 01 TECH TIPS
For VB Developers
VBS
Level: Intermediate
Declare Function GetModuleHandle Lib "Kernel" _ (ByVal lpModuleName As String) As Integer Sub Form_Load () Dim Handle As Integer Dim Counter As Integer Handle = GetModuleHandle("CommDlg.dll") ' Get the handle of 'the DLL you want to monitor Counter = GetModuleUsage(Handle) ' Get the number of usage. Debug.Print Str(Counter) ' Print on the debug window End Sub
VBS
Level: Intermediate
VB3, VB4 16
Level: Intermediate
FEBRUARY 1997
27
1 01 TECH TIPS
For VB Developers
VBS
Level: Intermediate
Dim rs As Recordset Dim SQL As String SQL = "SELECT DISTINCT Field " & FROM Table;" Set rs = db.OpenRecordset(SQL, dbOpenSnapshot) If rs.RecordCount Then Do Until rs.EOF lstBox.AddItem rs!Field rs.MoveNext Loop End If
VB3, VB4 16
Level: Intermediate
As long as the form-level variable bProcessing is True, this loop generates and prints random numbers. The DoEvents at the bottom of the loop yields control to other processes, and to other code within the same app, allowing it to perform other activities simultaneously. Ion Saliu, Cashtown, Pennsylvania