Last-modified: 22-Aug-95
Hope this makes it easier for Our Regular Readers ;-)
'Put this in the general declarations of your form/module Declare Function GetModuleUsage Lib "Kernel" (ByVal hModule As Integer) As Integer 'Here's where you shell out to the other program intHandle = Shell("PROGRAM.EXE") Do While GetModuleUsage(intHandle) > 0 DoEvents LoopThe FindWindow command can also be used (search the Tips help file for "How VB Can Determine if a Specific Windows Program Is Running"). I have had to use this when the program I shelled to unloaded itself and ran a different EXE. My program thought the shell was done (since the shelled EXE ended), but it really had just "moved on" to another EXE. Generally, the code in cases like this must be customized to fit the situation. [John McGuire (jmcguire@jax.jaxnet.com)]
[Top of Page][Table of Contents][Top of FAQ]
[Top of Page][Table of Contents][Top of FAQ]
This technique is especially useful, when you want to use a FAX printer driver: Select the FAX driver, send your fax by printing to it and switch back to the normal default printer. [Hajo Schmidt (hajo@bwl.bwl.th-darmstadt.de)]
It is recommended (and polite, as we're multitasking) to send a WM_WININCHANGE (&H1A) to all windows to tell them of the change. Also, under some circumstances the printer object won't notice that you have changed the default printer unless you do this. [Nic Gibson (nic@skin.demon.co.uk)]
Declare Function SendMessage(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long Global Const WM_WININICHANGE = &H1A Global Const HWND_BROADCAST = &HFFFF ' Dummy means send to all top windows. ' Send name of changed section as lParam. lRes = SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0,ByVal "Windows")
To change between landscape and portrait orientation, a search of the Knowledge Base on "lands*" finds Article ID: Q80185 "How to Set Landscape or Portrait for Printer from VB App". This is an extract: [Ayn Shipley (ashipley@hookup.net)]
Declare Function Escape% Lib "GDI" (ByVal hDC%, ByVal nEsc%, ByVal nLen%, lpData As Any, lpOut As Any) Sub Command1_Click () Const PORTRAIT = 1 Const LANDSCAPE = 2 Const GETSETPAPERORIENT = 30 Dim Orient As OrientStructure Printer.Print "" Orient.Orientation = LANDSCAPE x% = Escape(Printer.hDC, GETSETPAPERORIENT, Len(Orient), "", Null) Print x% End Sub
[Top of Page][Table of Contents][Top of FAQ]
When SHOWing a form with lots of bound controls, have a blank frame covering everything. Then, in the Form_Activate event, set the Frame.Visible = False. This greatly speeds the display of the form and hides ugly thrashing as the data controls initialize. [Christopher Biow (biow@cs.umd.edu)]
Option Explicit Dim FirstActivation as integer Sub Form_Activate DoEvents 'Allow the _Load event to be seen on screen If FirstActivation Then ' Do all the slow loading stuff here If FillComboBox <> 0 Then Unload Me 'If it all goes horribly wrong, then you 'can call Unload from an _Activate event '(Which you can't do from the _Load event) End If FirstActivation = False End If Screen.MousePointer = DEFAULT End Sub Sub Form_Load FirstActivation = True End Sub 'Show the form by using: Screen.MousePointer = HOURGLASS frmMyForm.Show MODAL
[Top of Page][Table of Contents][Top of FAQ]
Sometimes it is not possible to simply put contents of a property into a variable. For example, if you are using a list box or you need to conserve memory. In these cases you can send the WM_SetRedraw message to the control to prevent redrawing. You can typically increase the speed 6-10 times - or even more.
'Add the following declares: Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long Const WM_SetRedraw = &HB 'Add this to your code: Result% = SendMessage(Text1.hWnd, WM_SetRedraw, 0, 0) 'redraw off 'Do your stuff here! Result% = SendMessage(Text1.hWnd, WM_SetRedraw, 1, 0) 'redraw onThis same method applies to list boxes and other controls.
[Top of Page][Table of Contents][Top of FAQ]
Some real world experience speaks volumes about this one:
I wrote some time consuming code in VB to solve a combinatorical (does this word exist in English?) problem. The code consists of one main recursive function, which calls itself very often. It took a night to compute a certain problem. I was rather disappointed and then decided to write the central routine in C++. It was a 1:1 transcription. The routine was compiled with the MS C++-Compiler. It took only 22 Minutes for the same problem. Amazing, isn't it? The routine doesn't do any floating point arithmetic, only integer, and handles some arrays. The PC was a 33MHz 486. And the second amazing thing is, that a IBM RS6000 (560)-Risc-machine needed 17 Min for the same code. I was the only one on the machine. I thought it should be much faster. The MS C++ seems to make very fast, optimized code. The optimization was configured to make fast code. [Christoph Steinbeck (steinbeck@uni-bonn.de)]
[Top of Page][Table of Contents][Top of FAQ]
'After making the following declarations... Global Const WM_USER = &H400 Global Const EM_SETREADONLY = (WM_USER + 31) Declare Function SendMessage Lib "User" (ByVal hWnd As Integer ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long 'Then Try: SendMessage(Text1.hWnd, EM_SETREADONLY, 1, 0)This will still allow the user to copy *from* the text box. If you need to disable this (why?), steal the Ctrl-C in the _KeyPress event.
[Top of Page][Table of Contents][Top of FAQ]
There are some example VBX's with C code supplied with VB3 Pro. You'll find them under the directory [VB]\CDK.
[Top of Page][Table of Contents][Top of FAQ]
'Make the following declares. Declare Function GetSystemMenu Lib "User" (ByVal hWnd As Integer, ByVal bRevert As Integer) As Integer Declare Function RemoveMenu Lib "User" (ByVal hMenu As Integer, ByVal nPosition As Integer, ByVal wFlags As Integer) As Integer Global Const MF_BYPOSITION=&H400 'Use the following code to remove the "close" option. SystemMenu% = GetSystemMenu (hWnd, 0) Res% = RemoveMenu(SystemMenu%,6, MF_BYPOSITION) '(also remove the separator line) Res% = RemoveMenu(SystemMenu%,6, MF_BYPOSITION)
Adding menu items to the control menu is more complicated, since you need to respond to the events triggered when the user selects the new options. The newest Message Blaster (msgblast.vbx, see details in beginning of FAQ about how to get files) contains example code.
[Top of Page][Table of Contents][Top of FAQ]
Declare Function mciExecute Lib "MMSystem" (ByVal FileName as String) As Integer Sub Form1_Click () iResult = mciExecute("Play c:\windows\mkmyday.wav") End SubAlso:
Playing a WAV file is covered in the VB Tips help file (there is a Windows call that is for this specificially; see below). The routine won't play MIDI files or other sound formats, however. [John McGuire (jmcguire@jax.jaxnet.com)]
Declare Function sndPlaySound Lib "MMSYSTEM.DLL" (ByVal WavFile$, ByVal Flags%) As Integer Global Const SND_SYNC = &H0 Global Const SND_ASYNC = &H1 Global Const SND_NODEFAULT = &H2 Global Const SND_LOOP = &H8 Global Const SND_NOSTOP = &H10 Global Const SND_USUAL = SND_ASYNC And SND_NODEFAULT
[Top of Page][Table of Contents][Top of FAQ]
Tip: If you edit or replace the _DEFAULT.PIF file in the Windows directory to allow execution in background, this will apply to all DOS boxes that is not run with it's own .pif!
[Top of Page][Table of Contents][Top of FAQ]
[Top of Page][Table of Contents][Top of FAQ]
'You declare these API function as usual: Declare Function GetPrivateProfileString Lib "Kernel" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As Integer 'Then in your code you do like below: strIniFile = "WIN.INI" strSection = "MyProgram" strKey = "Language" strDefault = "English" iLength = 70 strReturn = String$(iLength, " ") 'Pad the string first! iResult = GetPrivateProfileString(strSection, strKey, strDefault, strReturn, iLength, strIniFile)WARNING: Be aware that there was an ERROR in the Windows 3.1 API documentation that came with VB. Here's the scoop: Knowledge Base article Q110826 (DOCERR: GetPrivateProfileString Declaration Incorrect in API) corrects a documentation error for the GetPrivateProfileString function call as described in the Windows version 3.1 API Reference help file that shipped with Microsoft Visual Basic version 3.0 for Windows. The CORRECT declaration is as follows:
Declare Function GetPrivateProfileString Lib "Kernel" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Integer, ByVal lpFileName As String) As IntegerNote that the "ByVal" keyword was omitted from the second parameter in the online reference. This means that the function is passing the second parameter (lpKeyName) by reference. It needs to be passed by value.
The most common problem that occurs when using the incorrect declaration is that when the function is called, it returns a copy of "lpdefault" in the "lpReturnedString" parameter instead of the actual value referenced by KeyName.
OOPS: As P. Wierenga (pwiereng@sol.UVic.CA) told me, the same doc error applies to Write: DOCERR: WriteProfileString Declaration Incorrect in API Article ID: Q115328
The correct declaration is as follows:
Declare Function WritePrivateProfileString Lib "Kernel" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lplFileName As String) As Integer (all on one line of course!)
[Top of Page][Table of Contents][Top of FAQ]
'Do the following declares: Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long Global Const WM_USER = &h400 Global Const EM_UNDO = WM_USER + 23 'And in your Undo Sub do the following: UndoResult = SendMessage(myControl.hWnd, EM_UNDO, 0, 0) 'UndoResult = -1 indicates an error.
[Top of Page][Table of Contents][Top of FAQ]
[Top of Page][Table of Contents][Top of FAQ]
A bit of history: the original P-code was an instruction set for a "virtual Pascal" machine. This came with a portable Pascal compiler written at ETH in Zuerich. The portable compiler produced instructions for this phony machine which had an instruction set ideally suited to the stack and heap management of Pascal. To executed portable Pascal programs, you had two choices: either write an interpreter for P-code, or translate the small set of P- code instructions (there were about 80) into assembler; assemble it; and run it at native speed.
Thus "P-code" originally stood for "Portable" or "Pascal" code. The broader meaning, "pseudo- code" came later. P-code was widely popularized by the UCSD Pascal system, a small workstation that was implemented entirely in Pcode and interpreted. It was sold for some years, and one company even re-did the microcode for a PDP-11 microchip to interpret P-code. The original Borland Turbo Pascal had obvious similarities to the UCSD system although it was not interpreted. The dialect was virtually identical.
Today P-code is used extensively in Microsoft apps, for two reasons. First, it is much more compact than native code; so the apps are smaller. Second, having an interpreter at the core of an app makes it much easier to customize and extend. That is why VB is becoming the heart of the MS major apps. It is simply not true that P-code apps run much slower than native apps. The slowdown is determined by the granularity of the interpreted routines. If every little thing is an interpreted op, the slowdown might be as much as 3-to-1 for the 80x86 architecture, or about 2-to-1 for the Motorola 68000 family (which is better suited to writing interpreters). But in practice, modern P-code systems have large-scale instructions, each of which is executed by a big compiled subroutine. These subs run at native speed, so the overhead of the interpreter is occasional at worst. [Roger E. Ison (r_ison@csn.org)]
It is also possible that since the code may not need recompilation to run on other platforms *if* the run-time interpreter is first ported, VB applications can become very portable. This depends on Microsoft's long-term plans.
A note on the word "pseudocode": I wrote above that it is a misnomer, and I stand on that. Pseudocode is *really* the pascal- like (mostly) explanation of an algorithm that is intended for human readers, not computers. But since somehow the term pseudocode stuck to the psaudo-machine-code created by VB the word is used here.
[Top of Page][Table of Contents][Top of FAQ]
[Top of Page][Table of Contents][Top of FAQ]
If you are interested in seeing how Visual Basic can be used for NetWare programming, obtain the following files from your friendly neighboorhood Novell FTP Mirror site. Mirror sites are (according to ftp.novell.com):[Top of Page][Table of Contents][Top of FAQ]
- Novell Germany - ftp.novell.de [nivb.zip] [nwtest.zip]
- Netherlands - ftp.rug.nl [nivb.zip] [nwtest.zip]
- United Kingdom - ftp.salford.ac.uk [nivb.zip] [nwtest.zip]
- Logan, Utah - netlab2.usu.edu [nivb.zip] [nwtest.zip]
- Tuscaloosa, Alabama - risc.ua.edu [nivb.zip] [nwtest.zip]
- Ottowa, Ontario, CA - novell.nrc.ca [nivb.zip] [nwtest.zip]
novlib\11\nivb.zip Netware Interface for Visual Basic
novlib\11\nwtest.zip NetWare Test for Visual Basic
There are also two Novell App Notes on the subject of using NetWare with Visual Basic (although this is _NOT_ supported by Novell...) which are:
- October 92- Interfacing Visual Basic for Windows and NetWare
- July 93 - A NetWare Interface for Visual Basic
The AppNotes can be obtained by contacting the Novell Research Order Desk, FAX: +1 303 294-0903, Voice 800 377-4136, +1 303 297- 2725. Address as follows:
Novell Research Order DeskAppNotes are $95/year ($135 outside US). Here are a few books which might help you out to figure the calls out:
1601 Park Avenue West
Denver, CO 80216-5199
It should be mentioned that the APIs included with the NIVB are _not_ current, and for this purpose, you should get the Novell SDK kit. Also, Novell will not support NIVB, but you can sometimes get some helpfrom Compu$erve, or from others on the Infobahn
- Windows Development on NetWare Systems, Lori Gauthier and Sue Whitehead (c) 1994, Windcrest, Blue Ridge Summit, PA 17294-0850 (McGraw-Hill) $34.95 Comes with disk This book also tells you how to "upgrade" to the currently supported SDK calls
- NetWare System Interface Technical Overview, Novell (c) 1990,1989 (Addison-Wesley), $32.95 (describes Novell's C Network Compiler API's)
- Visual Basic Programmer's Guide to the Windows API, Daniel Appleman Ziff-Davis Press, 5903 Christie Ave, Emeryville, CA 94608, $34.95 Comes with disk.
Good luck!
p.s. It behooves you to become a member in the PDP (Professional Developer's Program) since you get the AppNotes (& Bullets!) for...free.
p.p.s. Novell does NOT support the NIVB...
p.p.p.s. Also, no docs come with it. You'll probably need the Client C SDK kit to be able to really _use_ the code.
p.p.p.p.s. To make things even better, the calls in NIVB are fairly old, and not of the Client C SDK kit variety. However, there are books which could help you out, e.g. NetWare System Interface Technical Overview, by Novell. ISBN:0-201-57027-0, published by Addison-Wesley Publishing co, $32.95 US, $42.95 in Canada.
Update:
AppNotes are dead, however, Develper Notes live on. There is one article about NetWare programming with Visual Basic here:
- July/Aug 94 - NetWare Programming in Visual Basic:
- Using Apiary's NetWare Client SDK for Visual Basic
With Form1 With Text1 Bold = true FontName = "New Times Roman" End With End With
For Each element In group [statements] [Exit For] [statements] Next [element]
[Top of Page][Table of Contents][Top of FAQ]
(Note: it seems this method changes the icon of ALL the active DOS boxes)
Option Explicit Global Const SWP_NOSIZE = 1 Global Const SWP_NOMOVE = 2 Global Const SWP_NOACTIVATE = &H10 Global Const SWP_SHOWWINDOW = &H40 Global Const SWP_HIDEWINDOW = &H80 Global Const SWP_FLAGS = SWP_NOMOVE Or SWP_NOSIZE Or SWP_NOACTIVATE Global Const SWP_SHOW = SWP_SHOWWINDOW Or SWP_FLAGS Global Const SWP_HIDE = SWP_HIDEWINDOW Or SWP_FLAGS Global Const HWND_BOTTOM = 1 Global Const GCW_HICON = (-14) Global Const GCW_HMODULE = (-16) Declare Function GetModuleUsage Lib "Kernel" (ByVal hWnd%) As Integer Declare Function ExtractIcon Lib "Shell" (ByVal hInst%, ByVal lpExeName$, ByVal hIcon%) As Integer Declare Function DestroyIcon Lib "User" (ByVal hIcon%) As Integer Declare Function FindWindow Lib "User" (ByVal lpClassName As Any,ByVal lpCaption As Any) as Integer Declare Function SetWindowPos Lib "User" (ByVal h%, ByVal hb%,ByVal X%, ByVal y%, ByVal cx%, ByVal cy%, ByVal F%) as Integer Declare Function GetClassWord Lib "User" (ByVal hWnd%, ByValnIndex%) As Integer Declare Function SetClassWord Lib "User" (ByVal hWnd%, ByValnIndex%,ByVal wNewWord%) As Integer Sub LaunchPif(PifFile as String, IconName as String) Dim Res As Integer 'Return value from API call Dim MyInst As Integer 'Instance handle for this app Dim PifIcon As Integer 'Pif Icon Resource name Dim OldIcon as Integer 'Original Pif Icon resource (default =0) Dim PifhWnd As Integer 'Dos Box Window handle 'Launch PIF file PifInst = Shell(PifFile, 6) 'Get Instance handle for main MDI Window (assumed to be frmMDI) MyInst = GetClassWord((frmMDI.hWnd), GCW_HMODULE) 'Create new Icon resouce by loading Icon from disk file 'Check Icon file exists If Dir$(IconName) <> "" Then 'Make sure string is null terminated IconName = IconName & Chr$(0) 'Create Icon resource in this window PifIcon = ExtractIcon(MyInst, IconName, 0) Else PifIcon = 0 End If 'Reset Default Icon on DOS Box to PifIcon 'Check PIF file still running and Icon resource created If GetModuleUsage(PifInst) <> 0 and PifIcon > 0 Then 'Get Pif Window handle PifhWnd = FindWindow(0&, "Window name in PIF file") 'Set Icon handle to PifIcon resource OldIcon = SetClassWord(PifhWnd, GCW_HICON, PifIcon) 'Hide and show window to get a redraw of the Icon Res = SetWindowPos(PifhWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_HIDE) Res = SetWindowPos(PifhWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_SHOW) End If 'Wait for PIF file to complete Do While GetModuleUsage(PifInst) <> 0 DoEvents Loop If PifIcon > 0 then 'Reset Icon to original Res = SetClassWord(PifhWnd, GCW_HICON, OldIcon) 'Tidy up by removing PIF Icon resource Res = DestroyIcon(PifIcon) Endif End Sub
[Top of Page][Table of Contents][Top of FAQ]
[Top of Page][Table of Contents][Top of FAQ]
The key is that you need to create the first instance of your_control (ie. your_control(0)) at design time. [Ayn Shipley (ashipley@hookup.net)]
[Top of Page][Table of Contents][Top of FAQ]
You need the SystemParametersInfo API function and the SPI_SETDESKWALLPAPER constant. For this you will need the following constants and function declaration...
Const SPI_SETDESKWALLPAPER = 20 Const SPIF_SENDWININICHANGE = &H2 Const SPIF_UPDATEINIFILE = &H1 Declare Function SystemParametersInfo Lib "User" (ByVal uAction As Integer, ByVal uParam As Integer, lpvParam As Any, ByVal uWinIni As Integer) As Integer 'You then call the function as follows... Result% = SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, ByVal BMPFile$, SPIFlags%) '... where SPIFlags% is 0 if the change is not to be made permanent, or '(SPIF_UPDATEINIFILE Or SPIF_SENDWININICHANGE) if it is to be carried across 'into future Windows sessions.NOTE: Please be certain to include the ByVal keyword before the bitmap filename, as this argument is declared as Any, NOT as ByVal String. [Luke Webber(webber@werple.apana.org.au)]
[Top of Page][Table of Contents][Top of FAQ]
(see Mapping Context-Sensitive Topics, Chapter 6 pg. 117 of the Professional Features Book 1 for information on setting the context ids for your help file. Many help authoring tools will generate the context ids for you.)
'Include constant.txt in your project Set App.HelpFile = "c:\yourfile.hlp" 'Declare the following function in a .bas module (make sure it is all on one line): 'Function for display windows help files Declare Function WinHelp Lib "User" (ByVal hWnd As Integer, ByVal lpHelpFile As String, ByVal wCommand As Integer, dwData As Any) As Integer
CMDialog1.HelpFile = "c:\yourfile.hlp" CMDialog1.HelpCommand = HELP_CONTEXT CMDialog1.HelpContext = 1000 CMDialog1.Action = 6
CMDialog1.HelpFile = "c:\yourfile.hlp" CMDialog1.HelpCommand = HELP_CONTENTS CMDialog1.Action = 6
CMDialog1.HelpFile = "c:\yourfile.hlp" CMDialog1.HelpCommand = HELP_PARTIALKEY CMDialog1.HelpKey = "" CMDialog1.Action = 6
CMDialog1.HelpFile = "c:\yourfile.hlp" CMDialog1.HelpCommand = HELP_QUIT CMDialog1.HelpContext = 0 CMDialog1.Action = 6
iRetCde = WinHelp(Me.hWnd, "c:\yourfile.hlp", HELP_CONTEXT, ByVal CLng(1000))
iRetCde = WinHelp(Me.hWnd, "c:\yourfile.hlp", HELP_CONTENTS, CLng(0))
iRetCde = WinHelp(Me.hWnd, "c:\yourfile.hlp", HELP_PARTIALKEY, ByVal "")
iRetCde = WinHelp(Me.hWnd, "c:\yourfile.hlp", HELP_QUIT, CLng(0))
[Top of Page][Table of Contents][Top of FAQ]