Click here to download a free copy of the Visual Basic 5 Control Creation Edition (CCE). It does not let you make a compiled executable (.exe file), but you can build ActiveX controls (.ocx files) and run programs in the development environment. A great way to get started inexpensively!
Licensing Error Bugs
Visual Basic 5 can erroneously give these errors:
- License Information For This Component Not Found. You Do Not Have An Appropriate License To Use This Functionality In The Design Environment.
- Compile Error : Permission Denied
- Runtime Error '429' : ActiveX Component Can't Create Object
- License information for this component not found. You do not have an appropriate license to use this functionality in the design environment.
For more information and solutions to these problems, follow these Microsoft links.
PRB: "License Information for This Component Not Found" Error
Vbc.exe Fixes VB 5.0 Control Installation Problem
Disable All Controls On A Form
(By Theodore Fattaleh)
' Be careful using this option because if you
' have a code that says "Call Commad1_click"
' then it will not fire.
Dim i As Integer
For i = 0 To Form1.Controls.Count - 1
Form1.Controls(i).Enabled = False
Next i
Display a Picture in a Status Bar
Right click on the Status Bar control and select the Properties command. Click the Panels tab. Add a new panel. In the Picture area, click the Browse area and select the picture you want displayed in that panel.
Load a Blank Page in WebBrower Control
When you run a program that contains a WebBrower Control, by default it comes up invisibly. That can make the program look rather strange.
To make the WebBrowser appear and look empty but normal, execute this code when the form loads:
Private Sub Form_Load()
wbrMainBrowser.Navigate "about:blank"
End Sub
Creating an ActiveX Control
Creating an ActiveX control that does something useful is a bit too complicated to describe here, but the basics are simple. Start a new project. Select the ActiveX Control option from the new project dialog.
Give the control a meaningful name. If you call the control MyControl, then when the developer creates instances of the control, they will be named MyControl1, MyControl2, etc.
Give the control public properties and methods to let the developer control it. The ActiveX Control Interface Wizard can help with some of this.
Set the control's ToolboxBitmap property to the bitmap you want to display in the developer's toolbox. This bitmap should be 16 pixels wide and 15 tall. If it is not, Visual Basic will stretch it to fit. That usually produces an ugly result.
When you have given the control the properties and methods it needs, select the Project menu's Properties command at the bottom. Enter a meaningful project name and description on the General tab. On the Make tab, you can also enter the company name, copyright information, etc.
Now you are ready to build the OCX file. Select the File menu's Make Project_Name.ocx command. That's all there is to it.
To use the control, create a new Standard EXE project. Select the Project menu's Components command. In the dialog, find the new control. It will be listed using the description for the project that you entered in the project properties General tab.
When you click Ok, you should see the control's bitmap in the toolbar. You can now use the control just like any other.
There is a LOT more to say about ActiveX controls. For more information, look in my book Custom Controls Library. In addition to giving more information on control creation in general, this book shows how to build 101 useful custom controls. Complete source code is included on the CD-ROM. For more information including readers' comments and a table of contents, click here.
See Which Items Are Selected
When multiple selection is allowed in a ListBox, use a loop to see if the Selected property is true for each item.
Dim i As Integer
For i = 0 To List1.ListCount - 1
If List1.Selected(i) Then
MsgBox List1.List(i) & " is selected."
End If
Next I
Autoselect Text
To make a TextBox automatically select its text when focus enters it, give it a GotFocus event handler like this:
Private Sub Text1_GotFocus()
SendKeys "{home}+{end}"
End Sub
Or use SelStart and SelLength:
Private Sub Text1_GotFocus()
Text1.SelStart = 0
Text1.SelLength = Len(Text1.Text)
End Sub
Understand filters
The CommonDialog control's Filter property lists text strings alternating with filters separated by pipe | symbols. For example:
CommonDialog1.Filter = _
"Bitmap Files (*.bmp)|*.bmp|All Files (*.*)|*.*"
Here there are two filters. The first displays the string "Bitmap Files (*.bmp)" to the user. When the user selects the filter, the dialog applies the filter "*.bmp" The second filter displays "All Files (*.*)" and applies "*.*"
Scroll TextBoxes
You can make a TextBox scroll to the bottom with:
Text1.SelStart = Len(Text1.Text)
Learn Control Array Bounds
You can use a control array's UBound and LBound properties to determine how many controls are loaded. For example, the following code fills a string with the captions in the Label1 control array.
For i = Label1.LBound To Label1.UBound
txt = txt & Label1(i).Caption & ", "
Next i
Make Return Act Like Tab
Set the form's KeyPreview property to true. In the form's KeyPress event handler, check for the carriage return. If you get one, use SendKeys to generate a TAB instead. Set KeyAscii to 0 so the carriage return is discarded.
Private Sub Form_KeyPress(KeyAscii As Integer)
If KeyAscii = 13 Then
SendKeys "{TAB}"
KeyAscii = 0
End If
End Sub
Use Pictures on CommandButtons
To use a picture on a CommandButton, you must set its Picture property. You must also set its Style to 1 - Graphical. If you do not, the picture is ignored.
Combining Characters Using Xor
(By Ken Ives)
Have you ever wondered about the Xor command and how it works? Of course,
if you really want to get confused, read the VB help file. This will will
ruin your day. :-)
When you use the Xor command, you are making a bitwise comparison of the binary representation of two values. Xor stands for "exclusive or." A bit in A Xor B is true is the corresponding bit in A is set or the corresponding bit in B is set but not both.
Remember that a binary values consists of bits, each representing a power of 2. For example, the binary value 63 is represented by 00111111 = 32 + 16 + 8 + 4 + 2 + 1.
When working with text, one 8-bit byte represents one character with a value between 0 and 255. For example:
Character | Decimal | Binary | Value |
A | 65 | 01000001 | 64 + 1 = 65 |
? | 63 | 00111111 | 32 + 16 + 8 + 4 + 2 + 1 = 63 |
If you combine these binary values one bit at a time using Xor, you get 01111110 which represents 64 + 32 + 16 + 8 + 4 + 2 = 126 which is the ASCII code for the "~" character. Therefore "A" Xor "?" = "~"
Take Roots
To take a root, use: root_value = number ^ (1 / root)
For example, the cubed root of X is: root_value = X ^ (1 / 3)
Seed the Random Number Generator
You can use Rnd -1 followed by Randomize to seed Visual Basic's random number generator. Whenever you use the same seed, Rnd will produce the same sequence of random numbers.
Rnd -1
Randomize seed
Encrypt Code Easily
You can use random number generator seeds to quickly encrypt information. Seed the generator with a key value. Then for each character in the plaintext message, Xor the character with the output of Rnd to get the cypertext character.
To decrypt, seed the generator with the same key value as before. Then for each character in the encrypted message, Xor the character with the output of Rnd to get the plaintext character.
Display Program Version
(By Bill Mosca)
Here's how to get the program version to show in a Form's Caption. In
the Sub Form_Load() add this code:
Me.Caption = Me.Caption & _
App.Major & "." & _
App.Minor & "." & _
App.Revision
To set the version numbers, open the Project menu, select the Properties command, and click on the Make tab.
You can use a similar technique to display the version number in splash screens and About dialogs.
Put Notepad in the SendTo popup
(By Ted Fattaleh)
Its always pain to open Notepad and browse to open and edit a .vbp file.
Fix: Put a Notepad shortcut in the Windows\Send To folder. Any time you want to open a .vbp, .swt, .iwz, .ini, .sys etc with Notepad, right click file and select Send To\Notepad from Context menu.
Controlling Programs
Kenneth Ives has some advice on controlling an application:
- Always start your application from a BAS module named "Sub Main()".
- When shutting down, use a BAS subroutine (i.e. StopApplication()). In
this routine, add this code:
Dim frm as Form
' Loop thru the forms collection and
' unload all forms from memory
For Each frm In Forms
frm.Hide ' hide the form
Unload frm ' deactivate the form
Set frm = Nothing ' remove the object from memory
Next
- Have you ever wondered what events fire when you close a form? These events fire in this order:
- Form_QueryUnload
- Form_Unload
- Terminate
I feel you have the most control in the Form_QueryUnload event and that's my opinion.
Private Sub Form_QueryUnload(Cancel As Integer, _
UnloadMode As Integer)
' ---------------------------------------------------
' Cancel default is zero. Setting Cancel to
' a non-zero value will stop all forms from unloading.
' ---------------------------------------------------
' ---------------------------------------------------
' Based on the the UnloadMode code
' the system passes, we determine what to do.
' Place your specific code under the
' appropriate case statement.
' ---------------------------------------------------
Select Case UnloadMode
Case 0:
' User used the Control Box Menu in the upper
' left corner or the "X" in the upper right
' corner of the form. If this is one of many
' forms, I send it back to the form that called it.
' One way in, one way out.
Case 1:
' Some other code within this application is causing
' the shutdown. Usually a routine in a BAS module.
Case 2:
' Windows session is closing (i.e. Start, Shut Down)
Case 3:
' Task Manager is closing this application. (i.e.
' Ctrl+Alt+Del, End Task)
Case 4:
' The MDI parent form is closing.
End Select
End Sub
Make a Program Run When Users Login
Put a shortcut to the program in Startup directory. In Windows NT this directory is:
WinNT\Profiles\All Users\Start Menu\Programs\Startup
For an example program that shows how to create shortcuts programmatically, click here.
You can stop the program from starting automatically by removing the shortcut.
Start a Program by Double Clicking
When you double click on a file in Windows Explorer, the system looks up the program associated with that kind of file. For example, if you have Microsoft Word installed, your system probably uses it when you double click a .doc file.
You can change the program the system uses when you double click a file. For details, see the "Change Startup Extensions" topic on this page. In particular, you can make the system use a program you have written.
When your program starts in this way, it is passed the name of the file double clicked as a command-line parameter. Use the Command$ statement to see what file was clicked.
The user can also start your program by dragging files onto your executable program. In that case, the files are also passed in as command-line parameters so you should use Command$ to see which files were dragged onto your program.
Add Files to an Installation Kit
To add extra files to a program's installation kit, follow these steps.
- Run the Setup Wizard (Packaging and Deployment Wizard in VB6) as usual.
- When you get to the file summary page, click the Add button to add the extra files.
- Click the File Details button. You will see a TextBox with a value that starts with "&(AppPath)." Change this to be the directory where the file placed. For example, to create a directory beneath the program's installation directory, change this to something like "&(AppPath)\Images."
- Repeat for the other files you want to add.
Thanks to James Rushing for digging out the details.
Change Startup Extensions
When you double click on a file in Windows Explorer, the system opens the file
using a default application. To change the application used, follow these steps:
- In Windows Explorer, select the View menu's Options command. Click the File
Types tab.
- Find the file type you want to change and select it.Click the Edit button.
- In the Actions list, click on "open" and then click the Edit button.
- The "Application used to perform action" field tells what application the
system uses to open that kind of file. Change it to the new application.
If you are using a Visual Basic program to open the file, have the program use
the Command$ statement to see what file was double clicked.
Start Programs With Drag and Drop
Suppose a user drags one or more files onto your program's name in Explorer or onto your program's icon. Windows starts your program passing it the names of the files that were dropped. Use the Command statement to find the names of the files.
Test Command Line Arguments
In VB5:
Select the Project menu's Properties command (at the bottom). Click the Make tab and enter your arguments in the Command Line Arguments box.
In VB4:
Select the Tools menu's Options command. Click the Advanced tab and enter the parameters in the Command Line Arguments box.
Always Unload Forms
Common folkwisdom is that End does not always clean everything up properly. People seem to get more reliable results by unloading all forms. I don't know if anyone knows exactly why (does anyone?) but lots of people have noticed strange behavior using End.
Theo Kandiliotis writes:
I remember working on a VB5 project that did a lot of sequential access writing to ASCII files. Several different procedures had Open statements but not all had Close statements. If the program ended with End and I hadn't used Close for a file I had used Open for, I couldn't erase that file from Windows Explorer. Windows thought the file was still active and was currently being used by another application, even though I had closed the VB application!
Jim Karabatsos Adds that End does not call QueryUnload so your program cannot ensure that data is safe. He says:
That means that files can be left open, object instances can be left orphaned and (in VB5) hooks and callbacks can be left pointing to invalid code.
Only ever use END if you have a catastrophic failure (like the C: drive is dead). Otherwise, write yourself a Shutdown function like this:
Public Sub Shutdown(Optional ByVal Force As Boolean = False)
Dim I As Long
On Error Resume Next
For I = Forms.Count - 1 to 0 Step -1
Unload Forms(I) ' Triggers QueryUnload and Form_Unload
' If we aren't in Force mode and the
' unload failed, stop the shutdown.
If Not Force Then
If Forms.Count > I then
Exit Sub
End If
End If
Next I
' If we are in Force mode OR all
' forms unloaded, close all files.
If Force Or (Forms.Count = 0) Then Close
' If we are in Force mode AND all
' forms not unloaded, end.
If Force Or (Forms.Count > 0) Then End
End Sub
Tull Clancey Points out that other objects may need to be cleaned up as well. He writes:
...file handles, database controls, database objects, recordset objects, forms, and any other active links should be closed within a Form_Unload(x) procedure. If not done any links made will still be valid when the application is ended.
This is not always the case for the data control, however it is always good practice to close what you have opened. Mother always said put yesterdays toys away before playing with those today, very good advise!
An excellent point! While worrying about form closing problems, one shouldn't forget the basics.
Perform One-Time Initialization
Use a static variable to perform one-time initialization within a subroutine.
Private Sub GetValue()
Static initialized As Boolean
If Not initialized Then
' Perform one-time initialization.
:
initialized = True
End If
' Do other stuff.
:
End Sub
Right or Left Justify Output
Use the Format$ function to produce right or left justified text.
Format$(123, "@@@@@@") gives " 123"
Format$(123, "!@@@@@@") gives "123 "
Formatting Numbers
Use Format$(value, "0.00") to get 2 decimal places. Combine this with the "@@@@@" formats to get right justified numbers. For example:
Print Format$(Format$(123.45, "0.00"), "@@@@@@@@")
Print Format$(Format$(3.4, "0.00"), "@@@@@@@@")
Print Format$(Format$(12345.6, "0.00"), "@@@@@@@@")
produces the following list:
123.45
3.40
12345.60
Read and Write Arrays Quickly
You can read and write arrays quickly from files using Put and Get. This is much faster than reading and writing the array one entry at a time.
Dim arr(1 To 100000) As Long
Dim fnum As Integer
fnum = FreeFile
Open "C:\Temp\xxx.dat" For Binary As fnum
Put #fnum, , arr
Close fnum
Download Visual Basic 6, Service Pack 3
Immediate Window Tricks
(By Thomas Remkus)
I recently sent out an email to my team informing them of some things that they can do in the immediate window that they did not know of.
- You are in the design mode and you need to get the string for one of those crazy control resource numbers on a form.
- Load the form so you can look at the numbers. In the Immediate window, type "? LoadResString(x)" <return> where 'x' is replace the number and you should get the value.
- You have loaded the project and you have the form that you want to work on right in front of you. You need the name.
- Pause the project and type: "? Screen.ActiveForm.Name" <return> in the Immediate window.
- You need to get a list of the active forms that are loaded.
- Pause the project and type this all on one line in the Immediate
window: "for x = 0 to forms.count - 1: ? x, forms(x).name: next x" <return> [Note: You need to have a variable x defined in the project. To make this sort of thing easier, you may want to define x in a global bas module. If you do this, do not use any other variables named x or you may get confused -- Rod]
- You need to get a list of the resources on your form.
- Pause the project and type this all on one line in the Immediate window: "for x = 4650 to 4655: ? x, LoadResString(x): next x" <return>
- You need a list of anything or need to set values and would like to use the Immediate window but it seems to take a long time.
- Type this on one line: "for x = Y1 to Y2: : : next x" where 'Y1' and 'Y2' just put the values to loop.
- You have written a project and you want to see what the form would look like without loading the project in runtime.
- Type: "set x = new frmX: x.show vbmodal"
- You would like to add new functions so that you can use more utilities in the immediate window without having to go to runtime.
- Create a COM component with classes that are globally creatable and expose public methods. Reference the COMponent and you can use them directly.
- You want to place some text in the clipboard rather than sending through Debug.Print and then copy it.
- Type: "Clipboard.SetText X" or "Clipboard.SetData X" (check help for more info).
- You are on a control and you need to change what happens at a particular event. What you need is the name and index of the current control and you can not get out of the runtime because you don't want to stop the project.
- Set your focus to the control. Then pause the project. Type: "? Screen.ActiveControl.Name" then "? Screen.ActiveControl.Index"
- You have a function and you are going to expect a parameter that is a VB enumerated value like vbOKOnly.
- Instead of declaring the parameter as long state that it's of that enumerated type. So instead of " ... As String, alStyle As Long, ..." you can put " ... As String, alStyle As VbMsgBoxStyle, ... " and this will give other developers the intelli-sense for your values.
- You have created a function and you need to use constants for some of the values. You know that creating a set of constants is great but remembering what they are is just a load of work.
- Create a private enumeration in the top of your component (form, class, etc.) and state that your parameter's are of that type instead of a long. This will give you the intelli-sense that is so easy to work with.
Dead OCXs
By Barry Traver
Recently in the Usenet newsgroup comp.lang.basic.visual.misc, more than one Visual Basic 5 user has reported the same problem: Suddenly, for no apparent reason Visual Basic 5 refuses to load in COMDLG32.OCX, COMCTL32.OCX, or COMCT232.OCX. Instead the error message "'' could not be loaded" is given (even though such files are present in C:\WINDOWS\SYSTEM and VB 5 does know the correct names of the files!).
I myself experienced that problem, which meant that I was not able to use for several weeks in VB 5 the common dialog control (that's six controls right there!), toolbar, status bar, image list, progress bar, tree view, list view, slider, etc. . I'm writing this so that other VB 5 programmers will know what to do if they encounter the problem.
Here's an important paragraph from Microsoft Knowledge Base article Q217017, which can be found at http://support.microsoft.com/support/kb/articles/q217/0/17.asp, concerning the "'' could not be loaded" message:
This error message occurs when you install a newer version of an ActiveX control, uninstall it, and then install an older version of the same ActiveX control. The registry key for the newer version of the ActiveX control remains in the registry and Visual Basic 5.0 is now trying to use that registry key.
This could easily happen, for example, if you install (as I did) a recent shareware program and later uninstall it. The key point is that although the earlier version of the OCX is restored, the Windows registry still points to the later version of the OCX (and since it is no longer there, VB 5 is unable to load it).
To fix the problem, the Knowledge Base advises using RegClean, making the important point, "RegClean will only be effective if Comctl32.ocx is deleted from the system before running RegClean." (Although not mentioned by the article, it appears that comdlg32.ocx and comct232.ocx should also be temporarily deleted before running RegClean.)
For details on using RegClean, see Knowledge Base article Q147769, "RegClean 4.1a Description and General Issues," which can be found at http://support.microsoft.com/support/kb/articles/Q147/7/69.asp. The RegClean utility itself can be downloaded from the Microsoft site at http://support.microsoft.com/download/support/mslfiles/RegClean.exe.
Here are the specific steps I took to solve the problem:
- I downloaded RegClean from the Microsoft site.
- I sent the three com*32.ocx files to the Recycle bin.
- I unzipped and ran RegClean to repair the registry.
- I restored the three com*32.ocx files from the Recycle bin.
- I used Regsvr32.exe to register the three com*32.ocx files.
- I tested out VB to confirm that things were indeed working again.
- I reinstalled the Visual Basic 5 service pack 3 (a good idea after you use RegClean).
IMPORTANT LESSON TO BE LEARNED: If you install, say, a shareware program and then uninstall the program, it may mess up the registry for VB 5! That's because when you install the program, it may replace an older OCX with a newer version, and when you uninstall it, it will indeed replace the newer OCX with the older version, BUT IT DOESN'T FIX THE REGISTRY .
Ted Fattaleh had the same problem and has some more details:
I myself experienced that problem, which meant that I was not able to use for several weeks in VB 5
As a programmer you should know how to clean the registry manually.
The error is most common when you install VB5 the programming software
in a computer that has a VB5 program with OCXs already installed.
Search the registry for Comdlg32 or Richtx32, etc. without the extension and delete the whole key that it is in it, keep searching until you couldn't find it no more. That is it, you could use the API to register it, works much better than Regsvr32, if you do use regsvr32.exe then use it from MS-DOS prompt rather than from the Start\Run.
With Comctl32.ocx is a bit different, you need to restart computer after
deleting all traces of it in the registry.
[Note: Regclean is supposed to handle things like this. I'm not sure I trust it completely, however. A previous version did a little damage to my registry once. Hopefully the latest version is safer, but you should probably back up your registry before you begin in any case. -- Rod]
Puzzling Parentheses
Visual Basic evaluates expressions in parentheses and then works with the result. This is obvious in the following code. The program evaluates X + 3, multiplies by 2, and assigns the result to Y.
Y = (X + 3) * 2
This fact is less obvious when you call a procedure or function with extra parentheses around the parameters. In the following code, the program evaluates X and pass the result to the MySub subroutine.
MySub (X)
It is very important that the program does not pass X itself to MySub, it passes the result of evaluating the expression X. This is stored internally in a temporary variable and is never seen by the main program. In particular, if MySub changes the value of its parameter, the changes does not return to the calling routine.
For an example, run the following code.
Private Sub Command1_Click()
Dim X As Integer
X = 10
MsgBox X
ChangeValue (X) ' Note the parentheses!
MsgBox X ' X is still 10.
ChangeValue X
MsgBox X ' X is now 20.
End Sub
' Double the value of the parameter.
Private Sub ChangeValue(i As Integer)
i = i * 2
End Sub
Jack describes this as the system converting the routine ChangeValue to taking its parameter ByVal, even if it is declared ByRef or, as in this example, it defaults to ByRef.
Beware of C/C++ Syntax
Beware of statements like "A = B = C." In C/C++ this is a valid statement that assigns both A and B to the value C. Visual Basic treats "B = C" as a Boolean expression and makes A true if B equals C and false otherwise.
Understand XOR Mode Drawing
When you draw in XOR mode, the system does not XOR the color values. Instead XORs the bits representing the index of the colors in the system color palette.
The color palatte is a table listing the (probably 256) colors displayed on your system. The background color is stored in some "random" position, say 69 = &H45. The foreground color is in some other table entry, say 42 = &H2A. You XOR these and get &H42 XOR &H2A = &H68 = 104 (if I didn't mess up the math). That means you get whatever color happens to be in table position 104. Today it could be dark red. Tomorrow it could be something else.
The easiest thing to do about all this is nothing. Use vbInvert instead of XOR and don't worry about the colors you get.
The system palette includes 20 static colors that have fixed values. They live in the first 10 and last 10 palette entries. They are arranged so the inverse of one gives another that contrasts well. That means if you use those colors and you draw using vbInvert, the result usually contrasts well so you can see it.
If you really want to get exactly the color you want, you are probably better saving a copy of the image so you can erase by restoring it. Then draw copy pen mode using red or whatever. Annoying, I know.
Understanding Line Drawing
When Visual Basic draws, it seems to convert its coordinates into pixels and then place the results in short integers. If the coordinate positions in pixels are greater than 32767, the program crashes. That means you can use very large values if you work with twips but smaller values when working with pixels.
Understanding Forms
When you define a form named MyForm, Visual Basic creates one instance of the form with the name "MyForm." Many people confuse this special instance with other instances created using Dim. For example, the Caption statement in the following code sets the caption on the automatically created instance. The caption on frm is not set.
Dim frm As New MyForm
MyForm.Caption = "This is the single instance"
frm.Show
Even more confusing problems can arise if you use MyForm within the MyForm code module. In that case an instance like frm might accidentally set properties on the automatically created form not on itself. For this reason, never use MyForm within the MyForm code module.
Debug Object Creation and Destruction
It is often hard to figure out when objects are created and destroyed. When in doubt, stick Debug.Print statements in the object's Initialize and Terminate event handlers to try to see what's happening.
Get Started with the Visual Data Manager (VB5)
The Visual Data Manager (VDM) is a poorly documented program with a rather strange interface so it's a bit hard to get started. You can use it to create and manage databases.
Start the VDM from the Visual Basic 5 Add-Ins menu. To create a new Access database, File\New\Microsoft Access\Version 7.0 MDB. Then enter the name of the new .mdb file to create.
To create a new table, right click on the Database window and select New Table.
To add fields to the table, click on the Add Field button. In the dialog that pops up, enter the field information: field name, data type, size (for text), AllowZeroLength, Required, etc. When you are done with that field, click Ok. The dialog stays up so you can create more fields. Click Close when you are done adding fields to the table.
When you have closed the fields dialog, you can create indexes for the table. Click on the Add Index button. Click on the fields you want to make up the index. Be sure you select them in order. Give the index a name and click the Ok button.
When you have finished describing the table, click the Build the Table button to actually create the table. You will see it appear in the Database window. Expand the TreeView representation of the table to see the list of fields. Expand a field name to see the field's properties.
To change the table's design (add or remove fields, change field definitions, etc.), right click on the table's name and select Design. To add, remove, and edit records in the table, right click on the table's name and select Open.
This should be enough to get you started. The VDM is cumbersome but useful. Most of the features are hidden inside context menus that you must right click to find. Right click on different objects to see what else you can do.
Limiting "Shareware"
First note that shareware is supposed to be shared. You rely on the user to pay you if they use your software. If you provide a restricted version of the program and then give them the full version only when they pay you, that is not shareware. If you make the program expire after a certain time period and make the user pay for a full version, that is not shareware. (In fact, this will make users hate you if they have built data files that they can no longer access).
These examples are normal software for money, with a demo version. If you sell software like this, be honest and do not call it shareware or freeware. Call the restricted version a demo version.
There are several ways you can limit a program to produce a demo version. There are some products you can buy that make this easier. I won't talk about them here, though. You can search the Web for them if you like.
The easiest way to provide a limited demo version is to make it functionally restricted. For example, make it load its data using an internal subroutine instead of loading from a file. Or disable the save features. Because the demo version and the full license version are different executables, there is no possibility of someone with a demo version making it run in full-license mode.
A traditional method is to provide a guilt screen. Every 10 or 30 minutes, the program presents a dialog saying, "You really should send in $10" or whatever. If they do, you tell them how to deactivate this screen. This is very annoying, but at least you are being fairly true to the idea of shareware. The user does not have to pay if he is willing to live with the guilt screen.
Finally, you can provide some sort of time limit or restricted protection. Realize that it is hard to protect your software completely. For example, if you time limit the software, the user can always reset his system clock if he really cares. It is easiest to just provide a separate restricted demo version.
To implement restricted features, provide a password that includes the information you need the program to have. For example, if the software has an expiration date, include it in the password. Also include some other junk to make the password more complicated and make the program verify that the password is legal.
Now encrypt the password using your favorite technique. For a serious study of cryptography, see the book Applied Cryptography.
For example, you might make the plain text version of the password be watcher01ext20end99. This will encrypt to something apparently meaningless like 1fDjh9U34jfh8Kh8747. This is the value you give to the user. During installation, you can save it to the registry if you like so the user does not need to type it in all the time.
When the program loads, it reads this password either from the user or from the registry. It decrypts it to get the original value. It verifies that the string has the format watcher--ext--end--, and it extracts the expiration date 01/20/99. It then verifies that the date has not arrived.
You can use a similar technique to store codes that indicate which functions should be allowed. Then you can make the same program work in demo or full-feature mode using different passwords.
Understanding Error Handlers
When you use On Error GoTo and an error occurs, VB enters exception mode. The line you GoTo is supposed to be the beginning of an error handler. If an error occurs within an error handler, the program stops.
What you need to do is leave the error handler and resume normal execution. Then you can use On Error to establish a new error handler for the next error. You do this with the Resume statement. See the help for details. In this case, you can use Resume LineLabel to make the program continue execution at a specific line.
Unfortunately, executing Resume from outside an error handler generates an error. Thus you cannot place the error handler in the flow of code the way you have. You need to jump out to the error handler and jump back with Resume.
Below is a subroutine that demonstrates two error handler.
Private Sub Command1_Click()
Dim i As Integer
On Error GoTo Error1
i = 1 / 0 ' Divide by zero.
Error1Resume:
On Error GoTo Error2
i = 1000000 ' Too big--overflow.
Error2Resume:
MsgBox "Finishing."
' Do not fall through into the error handlers!
Exit Sub
Error1:
' Resume ends error handler mode.
MsgBox "First error handler."
Resume Error1Resume
Error2:
' Resume ends error handler mode.
MsgBox "Second error handler."
Resume Error2Resume
End Sub
Emptying a Collection
There are several ways you might empty a collection. This code removes items in last-in-first-out order:
Do While col.Count > 0
col.Remove col.Count
Loop
This code removes items in first-in-first-out order and is much faster:
Do While col.Count > 0
col.Remove 1
Loop
Setting the collection to Nothing is faster still:
Dim col As Collection
' Allocate the collection and use it.
Set col = New Collection
:
' Destroy the collection.
Set col = Nothing
Thanks to Amit Mukherjee.
Use Arrays Not Collections
Arrays take less memory and are faster than collections at indexed access. If you do not need to use a collection's keyed lookup, easy extensibility, and other features, use an array instead.
Create Global Properties
Did you know you can create property procedures in a .BAS module? The rest of your program can treat the "property" just like any other variable, but the property procedures can perform error checking, one-time initialization, etc.
Protect Data Within Modules
Use private variables in BAS modules for data the module's routines must access but which should be hidden from the rest of the application.
Private hidden_data As Integer
Public Function GetData() As Integer
GetData = hidden_data * 2
End Function
When Do You Use Classes Versus Code Modules?
A deep question. The difference is mainly conceptual. If you are working with things you consider objects of a certain type, they should probably be instances of a class. If they do things, those things should be class methods (subroutines or functions). The objects' properties should be implemented with property procedures.
On the other hand, if you have a routine that applies to a more general group of "thing" than these objects, or if it does not naturally belong to any kind of object, the routine should be in a standard module.
For instance, you might have a routine that averages the numbers in an array. Your program might use lots of different kinds of arrays. You could create a class that wrapped up the functionality of an array and give it an Average method, but that would probably be over elaborate.
Personally, I use a class when either:
- I need to create a bunch of similar objects
- I want to use class methods and properties to increase encapsulation and hide functionality
Otherwise I use a standard module.
Print Multi-Page Rich Text
The Printer object does not work well with the RichTextBox because it ignores the Rich Text codes. To print from the RichTextBox, use SelPrint. SelPrint is not very friendly. It prints the way it wants to and then ejects the page. To print multi-page Rich Text, find the start and end of each page, set SelStart and SelLength accordingly, and use SelPrint to print each page. Here are the steps:
- Save the edit version with SaveFile (because we're going to modifiy the
heck out of it).
- Convert the edit version to a print version:
- Run through the entire document changing SelIndent and SelRightIndent to values appropriate for the printer (indent values in the edit version are appropriate for the screen, not the printer).
- Make any other changes; in my case, add page headers and eliminate scene numbers.
- Print one page at a time with a SelPrint loop. Counting lines is tricky. Finding LFCRs is easy but I had to also account for line wraps which bump the line count, and page overflows which must not be allowed to happen.
- Delete the print version with SelRTF="".
- Reinstart the edit version with LoadFile.
Thanks to Ray White. If you find this useful, drop him a note of thanks. Visit Ray's home page.
Print MSFlexGrid
Here's a quick way to print a MSFlexGrid control's contents:
Printer.PaintPicture MSFlexGrid_Name.Picture, 0, 0
Printer.EndDoc
And if you want it to be the full length of the printer page add this before those two statements:
Dim old_width as Integer
MSFlexGrid_Name.width=printer.width
and this at the end:
MSFlexGrid_Name.width=old_width
Thanks to Edward Baisa.
Print Multiple Copies Quickly
When you print a document with TrueType fonts, the system downloads the fonts to the printer. That can take a long time. You can make printing multiple copies faster by printing all of the copies within one printer document.
For i = 1 To num_copies
' Print the document here...
Printer.NewPage
Next i
Printer.EndDoc
Print Gray Lines and Areas
Black and white printers cannot print gray lines, but they can dither to create gray areas. To create a gray border around an area, draw thin gray boxes with DrawStyle = vbInvisible (5) so they have no borders.
Set Printer Margins
Use the Printer's scale properties to set margins. Set the properties so the position (0, 0) is mapped to where you want the margins. For example, the following code makes the left margin 0.5 inches and the top margin 0.75 inches. The factor of 1440 converts inches into twips.
Printer.ScaleLeft = -0.5 * 1440
Printer.ScaleTop = -0.75 * 1440
Printer.CurrentX = 0
Printer.CurrentY = 0
Provide Print Preview
Start by making the printing routine take the object it should draw on as a parameter. For printing, pass the routine the Printer object.
For preview, pass the routine a hidden PictureBox. Then use PaintPicture to copy the hidden picture to a visible PictureBox at the desired scale. Drawing at full scale on the hidden PictureBox first allows you to scale fonts and other graphics without distortion.
Check Resources
(By Theodore Fattaleh)
When making a program, its important to check and see how much resources it is using.
Use the Resource meter and check it by running the .exe, don't check it from Visual Basic because it will not show resources usage.
To make your program use less resources:
- Use label1 instead of Textboxes if you can.
- Use less graphic.
Pictures, images, ListView and many other controls
drain resources considerably. Use them sparingly.
Textboxes are second in draining resources.
Small programs and always running programs, should use 1% to 3%.
Average program in VB should use between 4% and 5% resources.
Advanced large programs, 6% to 8%.
If your advanced large program uses more than 8% then there is something wrong.
Compare your program to Microsoft Word or Internet Explorer.
See if multiple instances do a major drain on resources.
Run all tasks in your program and see if resource usage has increased.
Check resources before opening your program, then check
resources after you closed your program. Be sure that your
program resources usage has been restored and didn't
do a permanent resources drainage that would require a
reboot to restore.
Use the
Set Form1 = Nothing
in the unload event if it does.
Installing Fonts
Robert Terblanche says you can install a font by simply copying it into the Fonts directory.
He also found some code somewhere on the net that installs a font in a more "official" way (If you recognize it, tell me where it came from originally so I can give credit to the author). To download it exactly as he got it from the net, click here.
Assign Desktop Hotkeys in Windows NT
This is not really a Visual Basic tip, but I have been asked several times so here's the answer.
Use this technique to start your favorite programs when you press some hotkey combination. For example, when you type Ctl-Alt-W, you can start Word.
- Right click on the Start menu. Select the Explore command to explore the Start menu directories.
- If the program is not already in your Start menu or one of its submenus, add it there. You might want to create a new Hotkeys folder so you know where all your hotkeys are defined.
- Locate the program in the explore window. Right click on it and select Properties.
- Click on the Shortcut tab. Click on the Shortcut Key field and press the letter you want associated with the program. For example, if you press N, the shortcut key combination will be Ctl-Alt-N.
- Click the OK button. You can also close the Explorer window started in step 1 if you like.
Creating a DLL
To create an ActiveX DLL, start a new project. Select the ActiveX DLL option from the new project dialog.
The new project comes with a class that will become the DLL's server object. Give it a reasonable name. Also give it public methods that will be available to the client application.
That's all you really need to do, but there is more that can make your ActiveX DLL easier to use. In the Project menu, select the Project1 Properties command at the bottom. In the General tab, give the project a meaningful name and description. On the Make tab, you can also enter the company name, copyright information, etc.
Click Ok and you are ready to build the DLL. In the File menu, select the Make Project_Name.dll command. That's all there is to it.
To use the DLL, create a new Standard EXE project. Select the Project menu's References command. In the dialog, find the new DLL. It will be listed using the description for the project that you entered in the project properties General tab.
Now the program can declare variables of the server class type. If your server class is named MyClass, then the program can say:
Dim obj As MyClass
Set obj = New MyClass
obj.PublicProcedure
Install Programs on Other Computers
Visual Basic is not a compiled language. Even if you create a "compiled" executable using VB5 or VB6, the result is not executable by itself. It needs several DLLs at run time to execute. If you copy the program to another computer and run it, you will probably get some sort of "Missing DLL" error.
The easiest way to ensure that the DLLs are present is to use a setup kit. Run the Setup Kit Wizard (VB5 and earlier) or the Packaging and Deployment Wizard (VB6). It will build a setup kit for the program.
When the Wizard gives you a chance to add and remove files, do not remove any files. While you may not use OLE yourself, the program needs an OLE DLL to run. The Wizard usually knows what is necessary.
Then execute the setup kit on the other computer. It will install the program and the necessary DLLs so you can run it on the other computer.
Shutdown Windows
I think this technique works in Win 3.x, Windows 95, and Windows NT. Let me know if you find that it does not work or if you try it in Windows 98.
Option Explicit
#If Win32 Then
Private Declare Function ShutdownWindows Lib "user32" Alias _
"ExitWindowsEx" (ByVal uFlags As Long, _
ByVal dwReserved As Long) As Long
#Else
Private Declare Function ShutdownWindows Lib "user" Alias _
"ExitWindows" (ByVal wReturnCode As Integer, _
ByVal dwReserved As Integer) As Integer
#End If
Private Const EWX_LOGOFF = 0
Private Const EWX_SHUTDOWN = 1
Private Const EWX_REBOOT = 2
Private Const EWX_FORCE = 4
Private Sub Command1_Click()
ShutdownWindows EWX_FORCE, 0
End Sub
Change Tooltips Background Color
To change the ToolTips background color, open the Control Panel's Display tool. Click on the Appearance tab. In the Item combo box, select ToolTip. Click on the little color box to change the color.
Breaking a color into red, green, and blue components
To break an RGB color value into its components, use:
r = color And &HFF&
g = (color And &HFF00&) \ &H100&
b = (color And &HFF0000) \ &H10000
There are some system colors that have funny values like &H8000000F&. Unfortunately they don't work this way. You can use the GetSysColor API function to find these color values. Use And to mask off the leftmost digit. Then use GetSysColor to see get the color value.
Public Declare Function GetSysColor _
Lib "user32" Alias "GetSysColor" _
(ByVal nIndex As Long) As Long
:
If color And &H80000000 Then _
color = GetSysColor(color And &HFFFFFF)
One final case occurs if you use Point to get the color of a pixel that does not exist. For example, on a form with ScaleMode = vbPixels, Point(-100, -100) returns -1 because there is no pixel at (-100, -100).
The following subroutine breaks a color into its components. If the color is -1, the routine leaves r, g, and b unchanged. Depending on your application, you may want to set them to default values such as 0 or 255.
Public Declare Function GetSysColor _
Lib "user32" Alias "GetSysColor" _
(ByVal nIndex As Long) As Long
' Break a color into its components.
Private Sub BreakColor(ByVal color As Long, _
ByRef r As Long, ByRef g As Long, _
ByRef b As Long)
If color = &HFFFFFFFF Then Exit Sub
If color And &H80000000 Then _
color = GetSysColor(color And &HFFFFFF)
r = color And &HFF&
g = (color And &HFF00&) \ &H100&
b = (color And &HFF0000) \ &H10000
End Sub
|
Thanks to Horst F. Haupt (HorstFH@aol.com) and Kenneth Biel (Kenneth.Biel@wcom.com) for their suggestions.
Understand Compressed Graphics
If you include a GIF or JPEG picture in a PictureBox or Image control, either at design time or at run time, the control stores the image in its original compressed form. That means it takes less space in the program, but it will take longer to display.
For example, if you load compressed JPEG images into a form at design time and then create a compiled executable (in VB5), the executable will be smaller than it would had you used BMP images. This lets you cram a lot of pictures into a reasonably small image.
If you use PaintPicture to copy an image, the new picture is always in the larger, faster BMP format. If you need to speed the program up, save pictures in JPEG format. Then at run time use PaintPicture to copy them into new controls so you can display them from the BMP format.
Learn Object Types
(By Rod Stephens)
If you want to know whether it is a particular type, do something like:
If TypeOf obj Is ListItem Then ...
If you want the name of the object's type, use:
MsgBox "The item is a " & TypeName(obj)
Control Infinite Loops
By Michael Vlastos
When you have trapped inside a non-ending loop, as you cannot press
any command buton or key, the only sulution is placing the command
"DoEvents" at the first line inside the loop:
Dim i As Long
For i = 1 To 1000000
DoEvents
...
Next i
[Actually DoEvents doesn't need to be the first line. Wherever you put it, it gives other parts of your program like a button that ends the loop a chance to receive events. -- Rod]
Faster Infinite Loops
By Cesare Cogliandro
Well, I have found that generally it's better to use the API function
GetInputState to check the presence of events in the app's input queue. I
don't know why the addiction of an "If" statement improves the code speed,
but it works very fine.
So you could replace the row
DoEvents
with
If GetInputState() Then DoEvents
Try to believe.
[The reason this makes things faster is that DoEvents allows all applications to process events, not just yours. This test allows DoEvents to run only when your program has events. -- Rod]
Make Select Case Easier
By Richard Sheridan
Let us say that you are creating a string, by letting the user click on various combinations of labels...
lbl(0) = "Hello "
lbl(1) = " I'm "
lbl(2) = " Happy"
lbl(3) = " Sad"
...
sub lbl_click (index as integer)
string$ = string$+lbl(index).caption
You would have a series of select case statements to evaluate the string...
select case string$
case "Hello I'm Happy"
do something
case "Hello I'm"
this would be an error...
case else
do something else
end select
I realised that this could get very complicated, especially if you had loads of labels and valid / invalid combinations.
So I created an array as large as the number of labels:
arraylbl(n) as integer
Starting with the value of 1, each cell was (value*2) of previous cell: 1 2 4 8 16 32 64... This would give a unique code to every combination of labels:
lbl(0) and lbl(1) = 1 + 2 = 3
lbl(0) and lbl(1) and lbl(4) = 1 + 2 + 16 = 19
Now, when a label is clicked, add its lblarray() code to a variable, say IsLegal (as integer). Obviously, if the label is un-selected by the user, you would have to ensure it had previously been selected, before subtracting the value from IsLegal.
You can easily set up a select case, based upon just a few valid numbers:
select case IsLegal
case 3
' the string was valid
call goodstuff
case 19
'the string was valid
call goodstuff
case else
'invalid
call nogood
end select
Converting an Excel Spreadsheet to Access
(By Tony Clifford)
I have converted an excel spreadsheet to an Access database. I will try to outline the proceedure but it is long (NOT difficult)
- Open the Excel Database.
- Save it using File/Save_As/DIF_(data Interchange Format) Give it any name except the original name.
- Open Access
- Start a new data base (any rubbish at all - not relevant to solution but just need something open.
- Goto Menu/File/Get_external_data/import
- Follow usual procedure for opening a file except to set it as Excel and find your excel file
- Click Import.
- Click Next
- Click First_row_contains_fieldnames (if true)
- Click ok to change/adapt to suitable names
- Click Next
- Click new table (?? I think I had a problem using existing table - the existing one can be dumped later ??)
- Click next
- Read instructions and make any changes you think fit - I made none!
It works! Add other new fields as necessary. Yipee!
Make New Project Templates
When you select the New Project command from Visual Basic's File menu, you get a bunch of choices. You can add new choices quite easily.
First, start a new project. Get it loaded the way you want. Add controls to the toolbox, give it multiple forms and modules, add controls to the forms, place code in the forms and modules, etc. Do whatever you like that you can do in a normal project.
Next, save it all in your the Projects directory. I'm not sure where this directory is for all systems. On mine (WindowsNT/VB6), it's:
C:\Program Files\Microsoft Visual Studio\Vb98\Template\Projects
Look for the template directory and then look inside for the Project directory. Save the project in there. Give the files meaningful names. Now when you select New Project from the File menu, the name of your project should appear in the list.
Disabling Windows
People keep asking how to disable Windows. For example, some people want to allow a program to take total control over the computer and not allow the user to switch to another application. That means preventing Ctl-Alt-Del from working, and stopping Alt-Tab from switching processes.
This goes strongly against one of the fundamental principles of Windows: that the user should always be in control. It takes control away from the user. It would also allow naughty people to create password grabbers and lock a computer against the user's wishes.
In Windows 95 you can disable Ctrl-Alt-Del and Alt-Tab using the SystemParametersInfo API function. This does not work in Windows NT (I don't know about Windows 98).
For the reasons above, I suspect it will be hard or impossible to do this in Windoes NT. This is unfortunate because I know there are some legitimate reasons why a computer might want to do this. For example, in some other operating systems, you can specify a program for the computer to run when it boots. Unless you have system privileges, you cannot stop the program or switch to another.
Jarmo Lepola wrote an interesting program that locks the computer until the user enters a password ("mouse" in the example program). In Windows NT, the program grabs the focus many times per second so the user cannot do anything useful until entering the password. [Download it]
A few other people also suggested using SystemParametersInfo including:
Bryan
Marc Strother
James Wilson
Thanks for the suggestions!
Making Help Files
This is generally a very hard topic. The Help Compiler Workshop comes with Visual Basic. Look for it in the Tools\Hcw directory on your Visual Basic CD-ROM.
The idea is you create a topic file containing the text of the help. You use an arcane set of footnotes defined by Microsoft to indicate links, topic titles, index entries, etc. You save this file in RTF (Rich Text Format), a format supported by many advanced editors including Word.
Next you create a project file that defines some topic numbers and identifies the help source file(s). You run the help compiler to produce the finished .HLP file.
Finally, your program can use the CommonDialog control's ShowHelp method to display the help file. To see an example program that does this, go to http://www.vb-helper.com/HowToBeg.htm and click the "Implement standard File and Help menu commands" link.
All in all, creating help is quite difficult. The help compiler itself is unfriendly and slow, particularly for large help files. If you have an error, the compiler is uninformative. The Help Compiler Workshop puts a front end on some of the process, but it's still hard. Some third party help systems make things easier.
My book Advanced Visual Basic Techniques contains some information on building a help file. I do not know of a book that covers only help, though there are a few introductory books that cover help to some degree.
You might also consider HTML help. Microsoft is pushing it as the method of the future. That does not mean traditional help will go away any time soon, however. I don't know how to create Microsoft's fancy compressed help files (they probably have a tool), but you can easily create your own HTML pages with hyperlinks to provide a quick and easy help system. Advanced Visual Basic Techniques also discusses this method.
Null, Empty, Nothing, and vbNullString
These strange values have slightly different meanings.
- Null. Null is a Variant subtype like Integer or String. It represents a variant that contains no valid data. This is different from zero, Nothing, Empty, or vbNullString. Most other values when combined with Null produce a Null result. For example:
Null - Null is Null not 0
Null + 7 is Null not 7
Null = Null is Null not True
You can use IsNull to determine whether an expression is Null:
If IsNull(my_variable) Then ...
- Empty. This is a variant subtype like Integer or String. It represents a variable that has not yet been initialized. This is different from Null which represents a value that specifically contains no valid data.
A variant variable that has not yet been initialized has the value Empty. You can use IsEmpty to see if a variant has been initialized.
If IsEmpty(my_variant) Then ...
- Nothing. This is an object reference that points to no object. Set an object reference to Nothing to free that reference. If no other references point to the object, Visual Basic will destroy the object.
Dim obj As Form1
:
Set obj = Nothing ' Free the object reference.
Use Is Nothing to determine whether a reference points to nothing:
If obj Is Nothing Then ...
- vbNullString. This constant represents an empty string. This is not the same as a blank string "". It is a nothing string. It is treated as an empty string "" for most Visual Basic purposes. Its real use is for passing null parameters to library functions.
Null is a strange value. It is not zero, it is not Nothing, it is not the same as vbNullString. It is something undefined.
See if a String is Blank
There are several ways to decide whether a string is blank:
Dim txt As String
Dim blank As String
blank = ""
:
If Len(txt) = 0 Then ...
If txt = vbNullString Then ...
If txt = "" Then ...
If txt = blank Then ...
The test Len(txt) = 0 is roughly 20 percent faster than the others.
Search for the last occurrence of a pattern in a string
Function LastInStr(txt As String, pattern As String)
Dim pos1 As Integer
Dim pos2 As Integer
pos2 = 0
Do
pos1 = pos2
pos2 = InStr(pos1 + 1, txt, pattern)
Loop While pos2 > 0
LastInStr = pos1
End Function
Send your Tips and Tricks to Tips@vb-helper.com.
|