101 Tech Tips For VB Developers 002
101 Tech Tips For VB Developers 002
A FAWCETTE TECHNICAL PUBLICATION, FEBRUARY 1996 VOL. 6, NO. 2, INCLUDES WINDOWS PROGRAMMING & VISUAL PROGRAMMING
R
P R O G R A M M E R S
J O U R N A L
2nd Edition
Featuring
of the
HOTTEST
TECH TIPS
For VB Developers
99 TECH TIPS
For VB Developers
VB3
VB4
For example:
File_name = FName("A:\test.dat") File_name = FName("A:test.dat") File_name = FName("A:\test\test.dat")
Robert Meyering
VB4
VB3
Turn debugging/profiling on by simply doing a replace on all modules of '*dbg* with :debug.print. When you run your code, the output will be sent to the debug window. To turn off debugging/profiling, replace :debug.print with '*dbg.* Even more interesting is the fact that you can do this at the procedure level as well. For example:
Sub Form_Load () '*dbg* "form_load:" & Time;
after replacement:
Sub Form_Load () : Debug.Print "form_load:" & Time;
Set the Tag property of the control to the text you want to display as the tag tip:
Command1.TAG = "This is the TAG TIP"
Finally, if you like, you can change '*dbg* to a procedure name that could write all output to a file. Robert Meyering
Add this to the MouseMove event of the form. It turns off the tag tip when you move your mouse off the control:
Label1.Visible = FALSE
Robert Meyering
FEBRUARY 1996
99 TECH TIPS
For VB Developers
VB3
VB4
VB4
VB4
VB4
VB4
VB4
Omit either or both of the arguments to see how the Optional parameter keyword in VB 4.0 increases the versatility of such procedures. Ian Carter
99 TECH TIPS
For VB Developers
VB4
VB4
VB4
VB4
VB4
VB4
VB4
Deborah Kurata
VB4
VB4
99 TECH TIPS
For VB Developers
VB4
Case 97 To 122 Select Case Asc(Mid$(StrInp$, x - 1, 1)) Case 97 To 122 strout$ = strout$ & _ Mid$(StrInp$, x, 1) Case Else: strout$ = _ strout$ & UCase$(Mid$(StrInp$, x, 1)) End Select End Select Next TCase = strout$ End Function
VB4
VB3
VB4
VB4
99 TECH TIPS
For VB Developers
'Search through form's controls For I = 0 To frmIn.Controls.Count - 1 'See if the control is a datacontrol If TypeOf frmIn.Controls(I) Is Data Then frmIn.Controls(I).DatabaseName = sDBName frmIn.Controls(I).Enabled = True 'Refresh if there is something 'in the recordsource property If Len(frmIn.Controls(I).RecordSource) _ > 0 Then frmIn.Controls(I).Refresh End If End If Next I End Sub
App properties are set from the not-too-obvious location of the Options button of the Make EXE menu selection of the File menu. Once you set the properties they will be saved with the project and do not need to be set each time you make the EXE file. Daniel R. Nolte
VB4
VB4
Also, VB4s control arrays have LBound, Ubound, and Count properties:
If Command1.Count < Command1.Ubound - _ Command1.Lbound + 1 Then _ Msgbox "Array not contiguous"
William Storage
VB4
For graphics you can use your companys logo or put a blank PictureBox on the form, create the graphic as a separate file, and add this code to the Load Event:
Picture1.Picture = LoadPicture(App.Path & "\logo.bmp")
There are two cautions, however: Until the first time you compile your code, there will be no EXE file, so the FileLen and FileDateTime functions will fail. To avoid this problem, create a dummy file with the project name EXE. If any of the referenced App properties are not set the form will load fine in the development environment but will generate the run time error Resource with identifier Version not found when compiled and run as an EXE (the error will say Version no matter which property is not set).
19911996 Fawcette Technical Publications
William Storage
FEBRUARY 1996
99 TECH TIPS
For VB Developers
VB4
VB4
YOU CANT CREATE AN OLE DLL WITH A PUBLIC CLASS SET TO CREATABLE -- SINGLE USE
Only one copy of a DLL can exist in memory at a time. For this reason, VB doesnt allow you to create DLLs exposing single use instancing classes. An EXE, however, allows you to create OLE servers that can be instanced multiple times for multiple clients. If you need a new instance of the server for each client you must create an out-of-process server in an EXE file instead of a DLL. A. Nicklas Malik
William Storage
VB4
VB4
William Storage
VB4
WIN32
There is no way to allow a VB user to use the For-Each syntax and still keep your encapsulation complete. If you are willing to break your encapsulation, you can provide an Items property, like this:
Property Get Items() As Collection Set Items = collMyInternalCollection End Property
DoEvents spends part of its time watching for other messages in the same process. This behavior has no value in a preemptive-multitasking operating system. Sleep yields more efficiently to other processes. Sleep is declared as:
Public Declare Sub Sleep Lib "kernel32" _ Alias "Sleep" (ByVal dwMilliseconds As Long)
William Storage
The problem with this method is that your users can now invoke the Add and Remove methods directly on the exposed collection. You have lost control of the data, and your encapsulation is broken. If you are willing to trust that your users will not use either the Add or Remove properties on this object, then this is a possible workaround. A. Nicklas Malik
99 TECH TIPS
For VB Developers
VB4
VB4
VB4
after any action statements in the stored procedure. This produces a one-column, one-row result set containing the number of rows affected by the action statement. DBLib and ODBC API require the same procedure as RDO. A. Nicklas Malik
VB4
This makes it clear that you are dropping an object reference in code. A. Nicklas Malik
VB4
If you need to check for the existence of an optional parameter, use the IsMissing function to test the parameter. Crescent Tech Support
FEBRUARY 1996
99 TECH TIPS
For VB Developers
VB4
WHY DOES THE TYPENAME OF A FORM RETURN THE FORMS NAME, AND NOT ITS TYPE?
You create a routine that uses the Typename() function to find out the type of an object, and based on that type, performs certain actions. However, when used in a Form object, the value returned by Typename() is not the generic object class Form. It is, instead, the specific object class Form1. Try running this sample:
Private Sub Form_Load() Dim frm as Form set frm = Me Debug.Print TypeName(frm), frm.Name '<< prints: Form1 Form1 End Sub
The way that standard Win95 applications handle this is by having a main window that shows in the taskbar. All auxiliary windows are owned by the main window. When an owner window is minimized, owned windows will hide, not minimize. You can use code to make a VB window be owned by another VB window, but there are implications to this solution. Owned windows are always on top of the owner window. On form load of each child window, do a GetWindow API call, using GW_OWNER on the forms window handle (hwnd) to get the original owner window handle. Save this value for later use. Proceed to make the subwindow owned by the main window by calling SetWindowLong, like this:
iret = SetWindowLong(Form2.hwnd, _ GWL_HWNDPARENT, _ Form1.hwnd)
On form unload of the main window restore all remaining (owned) subwindows back to being owned by their original owner window. Otherwise, you may get an Invalid Page Fault error. A. Nicklas Malik
You expect TypeName(frm) to resolve to Form instead of Form1 but it doesnt. That is because the type of the object, in this case, is the form class itself, Form1. That you assigned it to a Form object does nothing except to prevent you from calling any of the Form1 objects methods and properties directly in code. Similarly, a control never gives a type name of Control. Instead, the type name is always something like CommandButton or PictureBox. Interestingly, while the TypeName function returns the most specific name, the If-TypeOf syntax will match either the generic class type or the specific object class. In other words, both of these If statements would return True:
Set frm = Me If TypeOf frm Is Form1 Then Debug.Print "Form1" If TypeOf frm Is Form Then Debug.Print "Form" '<< both stmts are true
VB4
DO YOU HAVE WRITE ACCESS TO A DRIVE WITHOUT OPENING A FILE ON THAT DRIVE?
Assume that you have the full path name of a file, and you need to be able to tell if the drive is writeable (that is, if it is a CD-ROM or a network drive to which you have write access). Checking the attributes of the file is not enough, because the file may not be marked read-only, but the drive may be a read-only drive. However, if you open the file for Write or Append inside VB, it will reset the files date stamp, which you dont want. To determine if a file is writeable without writing to it, get the attributes of the file with GetAttr, and then attempt to change them with SetAttr. This operation will fail if you do not have write permission on the drive. On the other hand, if you are worried about a file being locked for writing (like an EXE is while it is running under Windows), you should use the OpenFile() API call with the OF_WRITE switch. This call will generate an error if the file cannot be opened for writing, but will not change the time stamp if you simply close it right away. A. Nicklas Malik
A. Nicklas Malik
VB4
VB4
99 TECH TIPS
For VB Developers
VB4
the point is that the VB4 has become much more adaptable to your needs. Customize it to fit. Crescent Tech Support
VB4
VB4
VB4
Then add two procedures for PropertySet and PropertyLet for the form:
Public Property Let GetKey(vNewValue) msKey = vNewValue End Property Public Property Get GetKey() GetKey = msKey End Property
In your form, use the variable you declared for any usage in your code. Property Procedures can be a bit more complex. But,
19911996 Fawcette Technical Publications
Supplement to Visual Basic Programmers Journal
A. Nicklas Malik
FEBRUARY 1996 9
99 TECH TIPS
For VB Developers
VB4
Optional vDraw As Variant) If IsMissing(vDraw) Then _ vDraw = ILD_NORMAL Or ILD_TRANSPARENT ImageList_Draw img.hImageList, _ img.ListImages(vIndex).Index - 1, hDC, _ x / Screen.TwipsPerPixelX, _ y / Screen.TwipsPerPixelY, vDraw End Sub
These functions are documented on the MSDN. Take a look. You may find many other uses for them. A. Nicklas Malik
VB4
A. Nicklas Malik
VB4
With Combo1 .ZOrder 0 .Width = Toolbar1.Buttons("combo").Width .Top = Toolbar1.Buttons("combo").Top .Left = Toolbar1.Buttons("combo").Left End With End Sub
A. Nicklas Malik
10
99 TECH TIPS
For VB Developers
VB4
VB3
VB4
VB4 VB4
VB4
FEBRUARY 1996
11
99 TECH TIPS
For VB Developers
VB4
not the strings that are passed as parameters. Performance gains are two to 10 times faster, so mileage will vary. Word is one of the easiest Office applications to completely early bind, because it has so few exposed objects. To early bind the Word Basic object, follow these steps: Select References from the Tools menu. Select Microsoft WordBasic 95 Type Library or browse for WB70EN32.TLB. This file can be obtained from Microsoft on Compuserve, Internet, or MSN. In the declarations section of your code add:
[Dim|Private|Public|Global] objReference as _ Word.WordBasic
This wrapper function uses both the CommonDialog control and the Printer object:
Function FilePrintDlgProc(rprnDlg As _ CommonDialog, rRTF As _ RichTextBox) As Boolean On Local Error GoTo Error_Handler: With rprnDlg .CancelError = True .Flags = cdlPDReturnDC + cdlPDNoPageNums If rRTF.SelLength = 0 Then .Flags = .Flags + cdlPDAllPages Else .Flags = .Flags + cdlPDSelection End If .ShowPrinter On Local Error Resume Next Printer.Print "" rRTF.SelPrint Printer.hDC Printer.EndDoc FilePrintDlgProc = True End With Exit Function Error_Handler: If Err <> cdlCancel Then MsgBox "Error " & Err & "; " & Error End If End Function
Steven Mitchell
VB4
Steven Mitchell
VBA
12
99 TECH TIPS
For VB Developers
Set objXLRoot = New [_ExcelApplication] Set objXL = objXLRoot.Application objXL.Visible = True Set objWKB = objXL.Workbooks.Add objWKB.Worksheets(1).Range("A2").Formula = "Hello" objWKB.Parent.ActiveCell.Formula = "Why" End Sub
which comes with CompuServe Information Manager is my favorite. Its small, yet very readable. To change your editor font, follow these steps: WINDOWS 95: 1. Run Control Panel, then Fonts. Click Add New Font from the File menu, and locate WINCIMTE.FON in the CSERVE\WINCIM director y. When installing, make sure its copied to the Windows\System directory. 2. Restart your computer in MS-DOS mode. Change to the Windows\System directory. Copy VGAFIX.FON to VGAFIX.SAV. Copy WINCIMTE.FON to VGAFIX.FON. 3. Restart your computer. Now every program that uses the VGA Fixed System font (including Cardfile, Notepad andyou guessed itVB 3.0) will have the new, smaller font. WINDOWS 3.X: 1. Use Fonts from the Control Panel to install the C:\CSERVE\WINCIM\WINCIMTE.FON font. 2. Exit to DOS and perform the tasks in Step 2 for Windows 95. 3. Restart Windows. Now every program that uses the VGA Fixed System font will have the new, smaller font. Note that the font must be a Fixed font, or VB wont like it. Also note that if youre running at high resolution with large fonts you may need to replace 8514FIX.FON instead of VGAFIX.FON. Barry Seymour
Steven Mitchell
VBA
VB3
Steven Mitchell
VB3
FEBRUARY 1996
13
99 TECH TIPS
For VB Developers
VB4
Whenever the user clicks an item in the list, the relevant picture control will pop to the top of the stack, becoming visible. This provides tab-like functionality without the cost of a VBX or extra memory, and lets you create items with as many pictures as you want, unfettered by tab width or tab caption readability! Barry Seymour
USING COMPONENTS
Creating a new project with a lot of old components? In VB 4.0 you can drag and drop files into the project window to add them to the project. Use File Manager or Explorer to drag and drop forms, modules, classes, or resource files onto the project window. Drag and drop OCX files (32-bit) or VBX files (16-bit) onto the toolbox to add controls to your project. Barry Seymour
VB4
COMPILE ON DEMAND
The new version of Visual Basic will compile only those portions of code it expects to run. This speeds load time, but the code may not be fully checked. To fully compile your project prior to running it, with all the syntax checking youve become used to, press Ctrl+F5 instead of F5 to run your project. To set VB 4.0 permanently to compile any project fully before running it, select Options from the Tools menu and click the Advanced tab. Uncheck the Compile on Demand check box to force VB to compile your app fully before running it. Barry Seymour
VB4
Note that for a real application you wouldnt dynamically create picture controls. Youd create them at design time and fill them with the controls you need. To demonstrate the concept, the example loads the control array at Form_Load.
14
99 TECH TIPS
For VB Developers
The workaround is to update the data controls record set directly. The change will be reflected automatically in the bound grid:
datCtl.Recordset.Edit datCtl.Recordset.Fields(0) = "This is a test." datCtl.Recordset.Update
the Data control moves to a new record. The solution is to check the record sets EditMode and perform an explicit Edit method if necessary:
Private Sub cmdUpdate_Click() If datCtl.Recordset.EditMode = dbEditNone Then datCtl.Recordset.Edit End If datCtl.Recordset.Update End Sub
Phil Weber
VB4
Another workaround is to replace the Update method with the Data controls UpdateRecord method, which is equivalent functionally to performing an Edit followed by an Update. The drawback is that UpdateRecord does not fire a Validate event, so dont use it if you rely on that event to perform data validation. Phil Weber
An undocumented feature of VB4s Printer object requires that each new page be initialized before the font can be changed. This code works as expected:
' Start new page Printer.Print ' Set margins as desired Printer.ScaleMode = vbTwips Printer.CurrentY = 720 ' Now you can set the font Printer.FontName = "Arial" Printer.FontSize = 11 Printer.Print "This is a test." Printer.EndDoc
VB4
Phil Weber
VB3
VB4
VB4
The problem seems to occur because the 16-bit version of VB4, unlike VB3, does not perform an implicit Edit method when
FEBRUARY 1996
15
99 TECH TIPS
For VB Developers
VB3
VB4
VB3
To get the user name and company strings into a variable, use this code:
Sub Form_Load () Dim hInst As Integer, user As String, _ org As String, title As String, length As Integer user = Space$(256) org = Space$(256) hInst = GetModuleHandle("user.exe") length = LoadString(hInst, 514, user, Len(user)) user = Left$(user, length) length = LoadString(hInst, 515, org, Len(org)) organization = Left$(org, length) Debug.Print user Debug.Print organization End Sub
Mansoor A. Thange
VB3
Bill Reid
VB3
VB4
VB3
Bill Reid
16
99 TECH TIPS
For VB Developers
This makes the form essentially invisible. The only code in the form is:
Sub Form_Activate DoEvents Me.Hide End Sub
Gordy Blackwell
VB3
Because the form is displayed modally, the current code is suppressed while the F_DoEvents form is displayed. This form issues the DoEvents and then hides itself, returning control to the caller. I experienced no noticeable degradation in performance. Hiding the form is much faster than unloading it each time. One warning: be sure to terminate your application with an END statement or explicitly unload F_DoEvents when you have finished. Unloading the main form wont remove F_DoEvents and the application will appear to hang. Robert W. Boyd
VB3
VB4
Peter Chyan
VB3
Gordy Blackwell
For numbers:
Dim nVar As Integer nVar = 0 & ds!nField
VB3
This assumes that ds is a dynaset and sField and nField are two fields in that dynaset. Hendrick H. Heimer
FEBRUARY 1996
17
99 TECH TIPS
For VB Developers
VB3
Declare Function Setparent Lib "User.exe" _ (Byval hwndchild as integer, byval _ hwndparent as integer) as integer.
Douglas Haynes
VB3
VB3
Visual Basic fails to give focus to the Notepad session because the 16-bit Windows subsystems may not be fully available to other 16-bit programs. To work around this, use the FindWindow and SetWindowPos Windows API functions like this: 1. Start a new project in Visual Basic. Form1 is created by default. 2. Double-click on the form to open the code window. Select (general) from the Object box. Enter the following in the (general) (declarations) window:
Declare Function FindWindow% Lib "USER" _ (ByVal Class&, ByVal Caption$) ' The following Declare statement must be on one line: Declare Sub SetWindowPos Lib "user" _ (ByVal hwnd%, ByVal hwndAfter%, _ ByVal x%, ByVal y%, ByVal cx%, _ ByVal cy%, ByVal swp%)
VB3
VB4
3. Select Form from the Object box. Add the following code to the Form Click event:
Sub Form_Click () Const SWP_NOSIZE% = &H1 Const SWP_NOMOVE% = &H2 AppActivate "Notepad - (Untitled)" x = FindWindow(0, "Notepad - (Untitled)") SetWindowPos x, 0, 0, 0, 0, 0, _ SWP_NOSIZE Or SWP_NOMOVE Debug.Print Hex$(x) ' Print return code from ' FindWindow API function. End Sub
Douglas Haynes
VB3
The workaround makes the combo a child of the form, not of the index tab, during the unload. In addition to, add in the General Declarations:
4. Start Notepad in Windows NT. 5. Start the Visual Basic program, or press the F5 key. Click on the form to activate Notepad. When finished, close the form to end the Visual Basic program. Douglas Haynes
18
99 TECH TIPS
For VB Developers
VB3
VB4
VB3
VB4
ALLOW COMMAND LINE INI FILES FOR MULTIPLE USERS ON THE SAME PC
To allow different INI files for different users on the same machine, use the command line to specify the specific INI file path and file name. Add a check to see if the INI file exists, and if it doesnt, create it. A different icon specifying a different INI file on the command line can be set up for each user:
Sub Form_Click () Dim achIniFile As String Dim Msg As String If Command = "" Then ' If no command line. achIniFile = App.Path & "\DEFAULT.INI" There is currently no command-line string." Msg = "The INI file used is: '" & Command$ & "'" Else ' Put command line into message. achIniFile = App.Path & \ & Command$ Msg = "The INI file used is: '" & Command$ & "'" End If MsgBox Msg ' Display message. End Sub
VB3
VB4
Don Yasuda
VB3
VB4
A query that checks for equality of the float column to the value inserted does not show the record inserted, whereas a nonqualified query shows the record. For example, the record set for the ds1 dynaset does not show the record inserted, but ds2 dynaset will:
Set ds1 = db.CreateDynaset("SELECT * FROM test _ WHERE col1=3.9") Set ds2 = db.CreateDynaset("SELECT * FROM test")
Attach the SQL Server table and open a record set on the attached table to solve the problem. Do not use the OpenDatabase method to open the record set while a transaction is pending. SP_STATISTICS, a catalog stored procedure, retrieves information about the table on which you create the record set. SQL Server does not allow this stored procedure to run while a transaction is pending. Douglas Haynes
The difference in behavior is because, in the case of prepared execution, the ODBC driver is doing the conversion to float. In the case of nonprepared execution and DB-Lib client tools, SQL Server is doing the conversion. To work around this problem, do an explicit convert on the SQL Server using a statement similar to this:
UPDATE test SET foo= (CONVERT(FLOAT, _ CONVERT(VARCHAR, col1)))
You can do the same thing within a trigger to automatically update the value for all new records inserted. Please note that this problem does not occur using the pass-through mechanism because in that case, the conversion is done by SQL Server. Douglas Haynes
FEBRUARY 1996
19
99 TECH TIPS
For VB Developers
VB3
VB4
This can occur if any of these conditions are passed to an API: Incorrect placement of ByVal in the Declare statement. A passed string initialized to a value that is too short to receive the return value. Undefined parameters in the function declaration or invocation. Incorrect type or length of parameters in the function declaration or invocation. Douglas Haynes
VB3
To enforce the fastest possible timeout, you can set ConnectionTimeout to one. In addition, you can add this code after you close the database to make sure the connection is terminated:
db.Close Start = Timer Do ' Close database, using ' database object variable (db). This loop pauses a second to allow a time-out Tell Microsoft Access engine that program is idle. Tell Windows to do any pending events. Start + 1
' ' FreeLocks ' ' DoEvents ' ' Loop While Timer <=
VB3
This loop delays for a second after the db.Close. The FreeLocks statement tells the database engine that the user is idle. If you run the Visual Basic program with ConnectionTimeout set to one in your VB.INI or <vb_exe_app_name>.INI file, the database engine will disconnect the one-second-old connection to the server. Douglas Haynes
VB3
VB4
GPF IN VB.EXE
What do you do when you get a GPF in VB.EXE? Remember that Windows requires you to ensure memory integrity when calling API functions. A GPF in VB.EXE can be produced if an API is incorrectly called. An example of the error is:
Application error: VB caused a General Protection Fault in VB.EXE at nnnn:nnnn
20
99 TECH TIPS
For VB Developers
Declare Function SendMessage Lib "User" _ (ByVal hWnd As Integer, _ ByVal wMsg As Integer, _ ByVal wParam As Integer, _ lParam As Any) As Long
VB3
VB4
VB3
VB4
However, there is no indication that the returned string may have been truncated. This technique will accommodate any length INI string:
IniEntry = "" Do IniEntry = IniEntry + Space$(512) Result = GetProfileString(Section, _ KeyWord, "", IniEntry, Len(IniEntry)) Loop Until Right$(IniEntry, 1) = " "
If IniEntry is not long enough, the rightmost character will contain a null character, so the loop will repeat until the rightmost character remains as a space. The same technique works when retrieving all keywords of a section like this:
KeyWords = "" Do KeyWords = KeyWords + Space$(512) Result = GetProfileString(Section, 0&, "", _ KeyWords, Len(KeyWords)) Loop Until Right$(KeyWords, 1) = " "
Dan Fox
This assumes that the KeyWord parameter for GetProfileString has been declared as:
ByVal lpKeyWord As Any
Phil Parsons
FEBRUARY 1996
21
99 TECH TIPS
For VB Developers
VB3
VB4
Wx = frm.Width / s 'size of vertical steps Hx = frm.Height / s 'size of horizontal steps ' top and left are static ' while the width gradually shrinks For i = 1 To s - 1 frm.Move Lt, Tp, frm.Width - Wx Next
The Form_Load event of this form must also contain this code:
Start = GetCurrentTime
It is also possible to wipe a form from bottom to top, and from both sides to the middle, using similar routines. David Mawson
VB3
VB3
VB4
David Mawson
VB3
VB4
VB4
22
99 TECH TIPS
For VB Developers
VB3
VB4
'sFile - file name 'sSearch - string to search for 'ibyte - use byte array to search 'iUniCode - look for UniCode strings Dim iHandle As Integer Dim sTemp As String Dim lSpot As Long Dim lFind As Long Dim sSearch1 As String Dim bTemp() As Byte 'another advantage of using a byte array 'is that we can easily look for UniCode strings If iUniCode Or (Not ibyte) Then 'this line will look for unicode strings 'when using byte arrays, regular 'strings when using string variable sSearch1 = sSearch Else 'this line will look for ANSII strings 'when looking through a byte array sSearch1 = StrConv(sSearch, vbFromUnicode) End If iHandle = FreeFile Open sFile For Binary Access Read As iHandle If iHandle Then sTemp = Space$((LOF(iHandle) / 2) + 1) ReDim bTemp(LOF(iHandle)) As Byte If ibyte Then Get #iHandle, , bTemp sTemp = bTemp Else Get #iHandle, , sTemp End If Close iHandle End If Do
Generate the EXE and run it together with VB. Move the PrintSub form to a convenient desktop position. If you want a single sub of your project to be printed, set the cursor to any position in this SUB and click on the Print-Sub button. Ive tested the code with VB3 Pro. In other VB Versions (such as the German Standard edition) you might have to change the %(EC) portion. EC refers to the VB menu bar. Andreas Schilling
VB3
VB4
If ibyte Then lFind = InStrB(lSpot + 1, sTemp, _ sSearch1, 1) Else lFind = InStr(lSpot + 1, sTemp, sSearch1, 1) End If lSpot = lFind Loop Until lFind = 0 End Sub
FEBRUARY 1996
23
99 TECH TIPS
For VB Developers
VB4
Else sNew = Dir$(sTemp, 54) End If MSGLFNresume: If iHasBS Then sNew = sNew & "\" End If sLongName = sNew Exit Function MSGLFNnofile: sNew = "" Resume MSGLFNresume End Function
VB4
If Len(sShortName) = 0 Then Exit Function sTemp = sShortName If Right$(sTemp, 1) = "\" Then sTemp = Left$(sTemp, Len(sTemp) - 1) iHasBS = True End If On Error GoTo MSGLFNnofile If InStr(sTemp, "\") Then sNew = "" Do While InStr(sTemp, "\") If Len(sNew) Then sNew = Dir$(sTemp, 54) & "\" & sNew Else sNew = Dir$(sTemp, 54) If sNew = "" Then sLongName = sShortName Exit Function End If End If On Error Resume Next For iBS = Len(sTemp) To 1 Step -1 If ("\" = Mid$(sTemp, iBS, 1)) Then 'found it Exit For End If Next iBS sTemp = Left$(sTemp, iBS - 1) Loop sNew = sTemp & "\" & sNew
24
99 TECH TIPS
For VB Developers
VB3
VB4
iPos = SGBkwdInstrS(0, _ Left$(sFileName, Len(sFileName) - 1), "\") iPos0 = InStr(sFileName, ":") iPos1 = InStr(sFileName, "\") iPos2 = InStr(iPos1, _ sFileName, "\"): iPos2 = iPos1 + iPos2 iPos3 = InStr(iPos2, sFileName, _ "\"): iPos3 = iPos2 + iPos3 iPos4 = InStr(iPos3, _ sFileName, "\"): iPos4 = iPos3 + iPos4 If Len(sFileName) > iMaxLen Then If (iPos4 <> 0) And _ iPos4 +Len(Right(sFileName, iPos)) _ <= iMaxLen - 2 Then sFileName = Left$(sFileName, _ iPos4) & "..." & Right(sFileName, _ Len(sFileName) - iPos) ElseIf (iPos3 > 0) And _ iPos3 + Len(Mid$(sFileName, _ iPos)) <= iMaxLen - 2 Then sFileName = Left$(sFileName, _ iPos3) & "..." & _ Right(sFileName, Len(sFileName) - iPos) ElseIf (iPos3 > 0) And _ iPos3 + Len(Mid$(sFileName, iPos)) _ <= iMaxLen - 2 Then sFileName = Left$(sFileName, iPos2) & _ "..." & Right(sFileName, _ Len(sFileName) - iPos) Else sFileName = Left$(sFileName, iPos0 + 1) _ & "..." & Right(sFileName, _ Len(sFileName) - iPos) End If End If End If TruncatePath = Left$(sFileName, Len(sFileName) - 1) End Function Function SGBkwdInstrS(ByVal iStart As Integer, _ ByVal sTarget As String, ByVal SPattern As String) Dim IPtr As Integer, IPLen As Integer IPLen = Len(SPattern) If ((Len(sTarget) = zero) Or (IPLen = zero) Or (Len(SPattern) > Len(sTarget))) Then Exit Function If (iStart = zero) Then iStart = 1 If (iStart >= (Len(sTarget))) Then iStart = Len(sTarget) iStart = Len(sTarget) - iStart + 1 On Error Resume Next For IPtr = iStart To 1 Step True If (SPattern = Mid$(sTarget, IPtr, IPLen)) Then 'found it SGBkwdInstrS = IPtr Exit For End If Next IPtr End Function
WIN95
FEBRUARY 1996
25
99 TECH TIPS
For VB Developers
VB4
VB4
WIN95
UNDERSTANDING UNICODE
VB4 introduces the use of double-byte characters. Most of this is transparent to the programmer and requires no special consideration. When calling API functions or reading/writing to a file VB will handle the conversion for you automatically. However, there may be times when you want to force a condition that goes against VBs will. For example, you might want to write Unicode to a file, pass a Unicode string to a function, or receive a Unicode string from a routine. In these cases you will have to use VB4s new Byte declaration. A String Byte can vary between one or two bytes depending upon how it is used. A Byte-byte is exactly that: one byte. To convert a string variable into a byte array, use this code:
Redim MyByteArray(0 to len(MyString$)-1) as Byte MyByteArray() = StrConv(MyString$, vbFromUniCode)
VB4
Due to a bug or design limitation, VB4 does not allow you to convert a string to a binary array that is part of a Type structure. For example:
TYPE MyByteType Bytes( 0 to 255) as Byte END TYPE Dim MBA as MhByteType MBA.Bytes() = StrConv(MyString$, vbFromUniCode)
VB4
WIN95
26
99 TECH TIPS
For VB Developers
WIN95
cessive call, take the long names that were returned and concatenate them into a full path string. This process will work even if you begin with a long file name and path. MicroHelp UnInstaller 95 team
VB4
WIN95
VB4
WIN95
VB4
WIN95
lInst = GetWindowLong_ (Form1.hwnd, GWW_HINSTANCE) lIcon = ExtractIcon(lInst, "MYEXE.EXE", 0&) lRet = ShellAbout(Form1.hwnd, _ "My App Name", "Copyright 1995 _ My Company Name" & Chr(13) & _ Chr$(10) & "Serial # xxxxxxxxx-xxx", lIcon)
lRet will return true if the dialog was able to display and false if there was a problem. All of the required functions operate in both Windows 95 and Windows NT. MicroHelp UnInstaller 95 team
FEBRUARY 1996
27
99 TECH TIPS
For VB Developers
VB4
expected by the database (but not in the report) are found. Check the Verify on Every Print command under the Database menu before you ship the report. This feature causes the Print engine to check for differences in the database and to compensate for them. Crystal, A Seagate Software Company Tech Support
VB4
VB4
VB4
VB3
VB4
VB3
VB4
28