1. Advanced Questions/Issues With Visual Basic
  1. General Questions
  1. How do I make my window always stay on top?

Making a form, or window, stay above all other windows requires a little API calling. The function needed is the SetWindowPos function. To make this function available for use in your program, you must have a public module in your application. In the General Declarations section of the public module, add these lines:

Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal _
hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx _
As Long, ByVal cy As Long, ByVal wFlags As Long) As Long

Public Const HWND_TOPMOST = -1
Public Const SWP_NOMOVE = &H2
Public Const SWP_NOSIZE = &H1
Public Const SWP_NOZORDER = &H8

Next, add the following public subroutine to your module:

Public Sub SetFormTopmost(TheForm As Form)

SetWindowPos TheForm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, _
SWP_NOZORDER + SWP_NOMOVE + SWP_NOSIZE

End Sub

Finally, set the form to topmost by calling the function SetFormTopmost with the name of the form to be set topmost as the parameter. For example, if frmMyForm is the form to be set to topmost:

SetFormTopmost frmMyForm.

By calling the SetWindowPos function, we send windows a message saying – take this window, put it on top, ignore the size and position parameters, and don’t allow it to lose its topmost position. It should be noted that in the API viewer which comes with Visual Basic incorrectly defines the value of SWP_NOZORDER as &H4. SWP_NOZORDER is &H8, and must be set as such in your project for this function to work. For more information on the SetWindowPos function and its other uses, and you have internet access, take a look at: http://www.microsoft.com/msdn/sdk/platforms/doc/sdk/win32/func/src/f84_2.htm

- Charles Haeberle

[ Return to Table of Contents ]

  1. How can I set a minimum size a user can resize my form to?

In the forms Resize event, if the width or height has gone below or above the desired ranges, they can be set there. For example, to prevent a form from being made shorter than 1000 units or taller than 10000:

Private Sub MyForm_Resize()

Select Case Me.Height
Case Is < 1000
Me.Height = 1000
Case Is > 10000
Me.Height = 10000
End Select

End Sub

- Charles Haeberle

[ Return to Table of Contents ]

  1. How do I shutdown/restart windows?

16-bit Windows uses the ExitWindows() API function and 32-bit Windows uses ExitWindowsEx(). The 32-bit version has many more options than its 16-bit equivalent including the ability to logoff and shutdown.

***** 16-bit Sample *****
In a project with 1 commandbutton, place the following code:

Declare Function ExitWindows Lib "user" (ByVal uFlags As Long, ByVal _
dwReserved As integer) As integer
Const EW_REBOOTSYSTEM = &H43
Const EW_RESTARTWINDOWS = &H42

Sub Command1_Click()

Dim iAns As Integer
Dim rVal As Integer
Dim iButtonType as Integer

iButtonType = 4 + 32 ' vbYesNo + vbQuestion

' Ask if the user is sure they want to exit.
iAns = MsgBox("Are you sure you want to exit windows?", iButtonType, _
"Exit Windows")
If iAns = 6 Then ' Yes pressed
' Call the exit function to Reboot.
rVal = ExitWindows(EW_REBOOTSYSTEM, 0)
End If

End Sub

***** 32-bit Sample *****
In a project with 1 commandbutton, place the following code:

Private Declare Function ExitWindowsEx Lib "user32" (ByVal uFlags As Long, _
ByVal dwReserved As Long) As Long
Private Const EWX_LOGOFF = 0
Private Const EWX_SHUTDOWN = 1
Private Const EWX_REBOOT = 2
Private Const EWX_FORCE = 4

Private Sub Command1_Click()

Dim iAns As Integer
Dim rVal As Long

' Ask if the user is sure they want to exit.
iAns = MsgBox("Are you sure you want to exit windows?", vbQuestion Or _
vbYesNo, "Exit Windows")
If iAns = vbYes Then ' Yes pressed
' Call the exit function to ShutDown.
rVal = ExitWindowsEx(EWX_SHUTDOWN, 0&)
End If 

End Sub

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I create/delete/change a shortcut?

Creating a shortcut (a Shell Link) is rather easy with Visual Basic, because Microsoft created a simple API function in the setupkit DLL called fCreateShellLink. Deleting a Shell Link is even easier, because all that need be done is delete the correct .LNK file. Alas, changing a shortcut is not directly available with Visual Basic because you must go through the Windows95 IShellLink interface to access the different parameters of a Shell Link.

To create a shell link, add the following code to the default form of a new project:

Private Declare Function fCreateShellLink Lib "STKIT432.DLL" (ByVal _
lpstrFolderName As String, ByVal lpstrLinkName As String, ByVal _
lpstrLinkPath As String, ByVal lpstrLinkArgs As String) As Long

Sub Command1_Click()

Dim lReturn As Long

'Add to Desktop
lReturn = fCreateShellLink("..\..\Desktop", _
"Shortcut to Calculator", "c:\windows\calc.exe", "")
'Add to Program Menu Group
lReturn = fCreateShellLink("", "Shortcut to Calculator", _
"c:\windows\calc.exe", "")
'Add to Startup Group
lReturn = fCreateShellLink("\Startup", "Shortcut to Calculator", _
"c:\windows\calc.exe", "")

End Sub

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I work with the dialup networking stuff?

Dial-up networking is accessible through the Windows API and again our friend Microsoft has come up with a very nice demo program for all of us which uses a remote automation server and VB4.

ftp://ftp.microsoft.com/softlib/mslfiles/vb32ras.exe

This demo program is complete and very interesting. It not only shows how to access DUN, it is a good example of using an OLE Server in VB4.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I make help files?

A Help file is really nothing more than an RTF-formatted file with special tags that let a help compiler turn it into a .hlp file.

The first step is to find a program which will compile help files. Hey! What do you know? Visual Basic comes with one: HC.EXE

Next you have to create the help source files...I recommend getting a commercial package as these generally make the best help files...they automate a lot of the grunt-work associated with help files like the help topics, indexing, popups, etc.

If you cannot afford a commercial package, the next step is a shareware or freeware package. These will be somewhat difficult to find generally, but most search engines return hits to the more common ones.

After you have designed and compiled your .HLP file, you have to add the tags to your Visual Basic program so that when the user presses F1 on your forms, something actually happens.

Most objects in VB have a .HelpContextID property which is just a Long Integer. Many help design packages will output a .BAS file for you to import into your project which contains a list of Public Constants that is all of the ContextIDs in the help file. You can use these constants in your code (for example a HelpInit subroutine called from Form_Load) to set these objects.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do you duplicate forms like mIRC does?

Duplicating forms seems more complicated then it really is. Create a new project, and on form1 place a command button. Next add the code below to the command button.

Private Sub Command1_Click()

Dim Form2 As New Form1
Form2.Show

End Sub

This shows a very basic method used to duplicate a form then show it. In a real world application you would probably wish to change the properties, such as the form’s caption and position, of this new form before you show it.

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I make my program not close when someone pushes the x?

In Visual Basic there are two unload events for every form. They are executed whenever the form is about to be unloaded. The major difference is the QueryUnload event allows one to cancel the process and even tell the program what was done to cause the form to start being unloaded. To cancel the close/unload of a form put the following in the QueryUnload event of the form.

Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)

Cancel = -1

End Sub

- Dave Sherman

[ Return to Table of Contents ]

  1. How do I make the mouse cursor invisible?

There are two real methods one can use to accomplish this. One is by using the ShowCursor() API. Both accomplish this by the same method, they set the mouse cursor to an invisible cursor. The mouse cursor is still there and you can still click, but you can’t see the cursor so the user won’t know where the pointer is or know where to click.

Declare Function ShowCursor Lib "user32" Alias "ShowCursor" (ByVal bShow As _
Long) As Long ‘ Place this in the declare section of a module

RetFunct = ShowCursor(False) ‘ This is the call to hide the mouse cursor
RetFunct = ShowCursor(True) ‘ This is the call to show the mouse cursor again

The other method involves including an icon that is made up of nothing but invisible pixels, then set the MouseIcon to that picture, and then setting the MousePointer property to 99.

- Dave Sherman

[ Return to Table of Contents ]

  1. Can a VB Application be an OLE server?

Yes, to create an OLE server:

  1. Decide what objects you need to define and their relationship to one another. You also need to decide just what properties and methods each of your classes must have.
  2. Define the individual classes in the OLE server.
  3. In the Project tab of the Options dialog box, set the Project Name, StartMode, and Application Description options for the OLE server.
  4. Prepare the initialization of the OLE server. If the OLE server doesn't display any forms and only provides access to the objects in code, set the Startup Form option in the Project tab of the Options dialog box to Sub Main. Any initializing code for the OLE server should go in a Sub Main procedure in a standard module.

- Guy Thompson

[ Return to Table of Contents ]

  1. How do I remove my program from the tasklist/taskbar?

In a module declare:

Public Const SW_HIDE = 0
Public Const GW_OWNER = 4
Declare Function GetWindow Lib "user32" Alias "GetWindow" (ByVal hwnd As _
Long, ByVal wCmd As Long) As Long
Declare Function ShowWindow Lib "user32" Alias "ShowWindow" (ByVal hwnd _
As Long, ByVal nCmdShow As Long) As Long

Then in your Form_Load() place the following code:

Dim rc As Long
Dim OwnerhWnd As Long

'make the form invisible
Me.Visible = False
'now remove it from the Task Manager List

OwnerhWnd = GetWindow(Me.hWnd, GW_OWNER)
rc = ShowWindow(OwnerhWnd, SW_HIDE)

- Guy Thompson

[ Return to Table of Contents ]

  1. Coding Techniques
  1. Why should I use Option Explicit?

Option Explicit is used in the General Declarations section of any module (Class, Form, General, etc.) to direct the compiler not to allow hot variable declaration. Hot, or on-the-fly, variable declaration is the practice of inventing new variables within code without any formal declaration statements. While this can be very convenient, ultimately it can lead to many more troubles than those which are solved by requiring formal declarations.

First of all, there’s simple syntax checking. Nobody is a perfect typist. We all make mistakes. When you are programming, mistakes can lead to unpredictable results. If you are properly using self documenting variable names, like LoopIndex, ClassroomCount, or MaximumOribitCircumference, to name a few examples, then the risk of misspelling one of these variables becomes much greater. However, rather than use this as an argument for using simple variable names like x, y and z, it becomes an argument for Option Explicit. When variables must be declared, the Visual Basic compiler registers an error if you use an unrecognized variable name. So if you type Fiend when you meant Friend, instead of your program acting like the former, it will instead warn you at compile that the variable Fiend is undeclared. This might be a bit of brutal truth, but what are Friends for?

In addition to the hours of debugging that can be avoided by using Option Explicit, proper variable declaration helps to save resources, time, and encourages more efficient programming by forcing the programmer to plan out procedure operations in advance.

There’s one other benefit to Option Explicit which should be mentioned here. If you don’t use it, your life expectancy may be prematurely decreased in a significant fashion by whoever takes over code maintenance after you move on to your next project. :-)

- Charles Haeberle

[ Return to Table of Contents ]

  1. What is passing by reference and by value?

When a variable is passed to a procedure, it may be passed in one of two ways: By Reference or By Value.

A variable is really a block of memory that contains a value, and has a name by which it may be referred to (the variable name). Passing a variable by value means that the number or string stored in memory is passed to the procedure, but the original variable in the calling procedure can not be affected. Visual Basic actually creates a new instance of that variable for use by the receiving procedure.

Passing a variable by reference, on the other hand, allows the procedure which receives the variable to actually change the variable’s value in the calling function. Visual Basic passes the address in memory of the variable to the procedure, and changes made to that value are reflected in the function which called it.

Here’s an example.

Private Sub NoChange(ByVal ThisVal as Variant)

Debug.Print ThisVal ‘ Received literal 5
ThisVal = ThisVal + 1

End Sub

Private Sub DoesChange(ByRef ThisVal as Variant)

Debug.Print ThisVal ‘ Received address of ThisVal
ThisVal = ThisVal + 1

End Sub

Sub Main()

Dim x as Integer
X = 5
NoChange X
Debug.Print X ‘ X still = 5
DoesChange X
Debug.Print X ‘ X now = 6

End Sub

By default, Visual Basic passes all variables by Reference, not by value. However, it is generally not recommended that functions change the values they receive, because if the change to the parameter is not known by the calling function, there can be unpredictable results. Typical examples of functions which would properly change a parameter would be: StripSpaces(thestring), ChangeDblQuotetoSingleQuote(TheString), IncrementByOne(TheValue).

- Charles Haeberle

[ Return to Table of Contents ]

  1. Why should/shouldn't I use gosub and goto?

Goto and Gosub are the wicked step children left over from the early days of BASIC when it was not nearly as structured or object-oriented as it is now. These statements violate normal, logical program flow (if you've ever seen a nice, complicated program written in BASIC, you'll know what the term "spaghetti code" really means.)

I have never needed to use Goto in Visual Basic; there are always alternatives. I seriously recommend staying away from Gosub and Goto except for one case. Error Trapping--trapping errors in Visual Basic is not something that follows structured programming rules.

When you error trap, you have the option whether to use Goto:

*** Code fragment ***

On Error Resume Next
' Do something which may cause an error
' Test for errors & react to them if necessary
' Continue processing

*** or ***

On Error Goto MyErrorTrap
' Do something which may cause an error

' If an error was raised, program flow jumps down to the error trap.
' Continue processing

Exit Sub
MyErrorTrap:
' decide what error was raised & do something about it.

Resume Next

*** End Code fragment ***

The differences in the two techniques are minimal...one conveniently negates the need for Goto; the other is more structured. It will ultimately be your decision to pick a method...whichever you are more comfortable with.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. Windows API
  1. What is the Windows API?

The Windows API is, at its heart, a set of DLLs that contain functions and subroutines that are used in common throughout the system. Generally speaking, those are the User, GDI, Kernel and Shell services. There are many other minor services like OLE, DDEML, Multimedia, LZW (compression), ToolHelp and Registration, but these aren't used quite as often.

User functions deal with the management of windows, message dispatching, threads, string handling and all types of user input device management.

Kernel functions handle the more low-level functions such as: memory allocation (real, heap and virtual), process management, loading/unloading resources and disk and comm I/O control.

GDI functions are the workhorse functions which draw and manage the graphical elements of the user-interface. Fonts, palettes, device contexts, pens, brushes, bitmaps, metafiles, regions, polygons, windows and viewports are some of the many object types handled by the GDI.

The Shell API contains many useful routines that are of a much higher-level type than the other routines. They generally encapsulate an idea rather than a small, logical step in a process. For instance, the GDI function LineTo draws a line from one point to another, while the Shell function SHFileOperation, manipulates files and folders (copy, move, rename, etc.) including the animation, progress bar and cancel button, all from one short function call. This makes the Shell functions much more powerful, but less flexible. Other objects controlled by the Shell API are icons, shell links (shortcuts), environment handling and drag-and-drop control.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. How do I use the Windows API?

To call an API function in VB, you must first Declare it in your code. In VB5 the API function declarations will be vb\winapi\APILOAD.EXE. In VB4, the API function declarations can be found in your vb\winapi\ directory using a program called APILODxx.EXE (where the xx is 16 or 32). If you don't have this program because you have the Standard version of VB4, you can try searching the Microsoft Knowledge Base. In VB3, the API is shown in a help file called win31api.hlp, but no actual VB declarations are shown, just the original C/C++ equivalent.

NOTE: There is a lot of good information in the text file, "vb4dll.txt". VB3 has a DLL text file, too, but I don't know its name.

Below are two example API function declarations; GetCurrentDirectory() and Sleep().

Declare Function GetCurrentDirectory Lib "kernel32" (ByVal nBufferLength As Long, _
ByVal lpBuffer As String) As Long

Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

GetCurrentDirectory() will retrieve the current directory in the same way that the DOS 'CD' command will. The Sleep() subroutine will pause your program for the specified number of milliseconds (while still allowing other applications to run normally).

You place these declarations in the General Declarations section of either a form or a .BAS module. If you want the function to be available to the entire program, you would place it in the .BAS module and prefix the declare with either 'Public' or 'Global' (vb5/vb4 & vb3). If you want it only to be available to a specific form, you use 'Private' (vb5/vb4).

- .BAS module
(vb5/vb4) - Public Declare Function ...
(vb3) - Global Declare Function ...

- .FRM file
(vb5/vb4) - Private Declare Function ...
(vb3) - Declare Function ...

Calling API functions can be tricky if you don't understand how they work. Since they follow the C/C++ style of calling, you have to follow their rules; the rules you know for calling VB functions and subs won't always work.

For example, the Sleep() API function is just one call with one numeric parameter and no return value, so is very straightforward.

Call Sleep(5000) ' Pause for 5 seconds

On the other hand, a function like GetCurrentDirectory can get complicated. If you look at the declare, you'll see it takes two parameters, nBufferLength and lpBuffer (a long and a string) and returns a long value.

Dim rVal As Long ' the return value
Dim sDir As String ' the current directory

sDir = Space$(255)
rVal = GetCurrentDirectory(255, sDir)
sDir = Left$(sDir, rVal)

In the code sample above, two variables are declared: rVal and sDir

rVal will hold the return value of the function (in this case it will be the length, in characters of the current directory)

sDir is initialized to 255 space characters in order to give the API function room to work with. sDir, when the function is finished, will hold the current directory. (And that's the tricky part, we are just passing in a large, empty string for the function to fill in.)

After the function completes, we trim off the excess right-hand spaces.

Conclusion:

These two examples barely scratch the surface of what the API does. The best reference I have seen for API functions is:

The Visual Basic Programmer's Guide to the Win32 API by Daniel Appleman

You have to know what the API can do before you can decide what you want to do with the API. Read as much as you can find out about these routines. Browse through the API viewer.

Remember that its not required you use these functions, Visual Basic has very highly optimized internal functions and methods which in many cases work better (i.e. faster) than their API equivalents.

- Robert Houghtaling

[ Return to Table of Contents ]

  1. What is a callback?

The callback attribute declares a static callback function that exists on the client side of the distributed application. Callback functions provide a way for the server to execute code on the client.

The callback function is useful when the server must obtain information from the client. If server applications were supported on Windows 3.x, the server could make a call to a remote procedure on the Windows 3.x server to obtain the needed information. The callback function accomplishes the same purpose and lets the server query the client for information in the context of the original call.

Callbacks are special cases of remote calls that execute as part of a single thread. A callback is issued in the context of a remote call. Any remote procedure defined as part of the same interface as the static callback function can call the callback function.

Only the connection-oriented and local protocol sequences support the callback attribute. If an RPC interface uses a connectionless (datagram) protocol sequence, calls to procedures with the callback attribute will fail.

Handles cannot be used as parameters in callback functions. Because callbacks always execute in the context of a call, the binding handle used by the client to make the call to the server is also used as the binding handle from the server to the client.

Callbacks can nest to any depth.

- Guy Thompson

[ Return to Table of Contents ]