These are the questions I most want answered from Developer.com, my emails,
and other sources. Some I don't have time to dig into deeply enough to give
a good answer. Some I have seen solutions for but don't remember where.
Others I have no clue about.
If you know how to accomplish these tasks, please
Better still, if you can, zip up an example program and
send that as a binary attachment (no uuencodings please).
Click the items in the list to see peoples' solutions.
- How can I retrieve a metafile from a resource file?
- How can I create a floating toolbar?
- How can I access the History list of an embedded browser?
- How can I load fonts in VB? (By this I mean install a new font on the system, not just use different fonts)
- How can I call an ActiveX exe from Visual C++?
- How can I pass a Form object from an In-Process DLL back to the main Application?
- How can I open a password protected database?
- How can I open an Access 95 database that is password protected using VB?
- How can I use ADO in a multi-user environment?
- How can I list the programs in the task bar?
- How can I change the color of a title bar?
- How can I make a gradient color title bar?
- How can I install multiple programs in one setup program?
- How can I empty the wastebasket?
- How can I delete temporary internet files?
- How can I print a preformated file directly to printer?
If you know how to accomplish these tasks, please
Better still, if you can, zip up an example program and
send that as a binary attachment (no uuencodings please).
1. How can I retrieve a metafile from a resource file?
You have to add the metafile as a custom resource, then when you need the file, u have to do something like this:
Dim bData() As Byte
bData() = LoadResData(101, "CUSTOM")
Open App.path & "\file.wmf" For Binary As 45
Put #45, , bData
You have to rerplace "101" with the number of the resource, "CUSTOM" with the name of the type of resource, and "file.wmf" with the name that you want to use.
2. How can I create a floating toolbar?
My solution is quite simple. In fact, it's so simple I'm wondering if I
haven't missed something or maybe misunderstood your question.
All I did was to add a form to my application, and set the BorderStyle to 4
- Fixed ToolWindow. On this form I created the toolbar that I would normally
have created on my main application form. When I show the form, I set the
owner to the main form by saying:
That's it. Nothing to it. The Toolbar will now always be on top of my
application, but not on top of other applications. The toolbar I used is
just a simple example, using an aligned picturebox with command buttons on
it, but you can of course place any toolbar in the toolbar form that you
wish. You can of also totally remove the toolbar form's title bar, and add
all sorts of fancy rearrangement of buttons when the toolbar form resizes.
An elegant solution. Note that for MDI applications, the toolbox must be a child of the MDI form, not a child form. See also:
4. How can I load fonts in VB?
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.
Eric Kiersky pointed out this useful Knowledge Base article
HOWTO: Add a Scalable Font to Windows from Visual Basic.
5. How can I call an ActiveX exe from Visual C++?
Robert Rottermann has these tips:
My company is producing software for
CAD-users, so we do most of our programming in C++ but we intend to
create the UI in VB. I therefore face the challenge of linking VB and
C++ programs. As long as this only consists in putting a control that
is written in the "foreign" language this is (often) painless. But as
soon as you have to do something more complicated or there are problems
it seems to be rather difficult to get answers to questions you might
A couple of scenarios are possible
- The Server is a control:
This is very often painless. Just put the control on a form in the VC++
resource editor. There is one caveat though, the containers VB offers
(Forms, picture controls and the like) offer a richer "environment" in
which the control lives and with which it can communicate. In VB
parlance this environment is called Extender and Ambient properties.
They do not exist for non VB containers or offer a reduced set of
properties one can call. If such a control is put on a VC++ form (aka
resource), the IDE will create a class with the necessary interface
glue as soon as you create a member variable for the control the first
time. If you change the interface of the control (the public stuff),
things become messy, because the IDE just keeps on using the old
"interface glue", which of course has become out of date. I have yet to
find a way how to remedy this situation without scanning the MFC
created files and deleting all references to the control.
I created a mini MFC application "RRMfcControll" that hosts a simple VB
created control, you can set its properties and react to its events.
[This is the way I would probably try to do it. I should have thought of this. -- Rod]
- The server is a DLL or a an EXE
To incorporate an ActiveX server that is not a control (it also works
for controls by the way) MFC offers the #import compiler directive.
Using it you get all needed interface glue created and wrapped into a
set of smart pointers which are very easy to use. You just call your
server properties in a very VB like manner. The nice thing is, that you
can change your interface in VB as often and thoroughly as you like.
The glue will always be up to date since it is recreated when ever MFC
detects an change in the file dates. However, there is a down side too.
You do not get a interface map created, so there is no "automatic"
communication with your server. You have to do it yourself. As
mentioned before, this is a trivial task as long as the communication
is VC++ -> VB. Vice versa matters are a bit more complicated. Raising
an event in VB that is serviced by a peer actually means calling an
function from VB that this peer is exposing. This calls for a two way
communication to be built up between peer and VB. MFC generates this
two way channel automatically for you when it hosts a control on a
resource by creating an message map and an event sink map. Both these
maps are actually wrappers around functions that subclass the windows
involved. Since nobody creates them for us we have to do it for
ourselves. Luckily subclassing comes naturally to C++ and is easy to
I have included two samples demonstrating the communication of C++ with
an ActiveX server. RRConBarBone is a minimal console application
calling an ActiveX server RRMfcTwoWays demonstrates the use of a "hand
made" message map to build up a two way communication.
My hand crafted "message map" is of course a much simplified version of
what is going on when two com objects talk to each other. I tried to
understand Don Box's "Essential Com" but failed. That is why I can
offer no better solution.
6. How can I pass a Form object from an In-Process DLL back to the main Application?
Create a property in your DLL that returns a Variant. Set the Property to return the Form object.
Public Property Get DLLForm() as Variant
Set DLLFrom = Form1
This trick will work with any object that normally gives that annoying message that it can't be passed back from a DLL.
7. How can I open a password protected database?
8. How can I open an Access 95 database that is password protected using VB?
Dim Dbpath as String
Dim DB as database
DBPassword as String
DBEngine.SystemDB = DBPath & "\MyDB.mdw"
DBPassword = "database password"
Set DB = DBEngine.Workspaces(0).OpenDatabase( _
DBPath & "\MyDB.mdb", False, False, ";pwd=" & DBPassword)
For both questions this will work:
dim db as database
set db = opendatabase(mydatabase,false,false,";pwd=mypassword")
Data1.DatabaseName = "C:\mydb.mdb"
Data1.Connect = ";pwd=thepassword"
Data1.Recordsource = "My Table"
9. How can I use ADO in a multi-user environment?
Kevin B. Castleberry
This topic deserves a book. I have been dealing with this for a few months.
I have a specific answer to a specific question that drove me nuts for a
couple of weeks. I am in the
middle of a project that I have to get done right now so don't have time to
work all that up. If there is enough interest, I will consider doing this
in the next few months.
The question is: In an N-tiered environment (based on Lhotka's VB 6
Business Objects book) where I have an Activex server that uses ADO to talk
to an Access .mdb via Jolt 3.51 how do you get reliable error messages
returned when two users try to update the same record. Here is the solution
(I understand it also works with SQL Server 7.0):
If I use a clientside cursor, with batchoptimistic locking and batchupdate
I can get reliable locking results that I can trap. Now when the second
client tries to .UpdateBatch (assuming they get past the chgcount check) I
get the following error consistently.
Case -2147217864 'The specified row could not be located for updating: Some
values may have been changed since it was last read.
In the context of my app this does not make a lot of sense. That is I have
the PersistLayer as an activex server running on the same machine as the MS
Access .mdb. So I don't really see a reason to have to use clientside
cursors. Also I am only dealing with one record in this example so the Batch
part does not make since either. But hey at this point if it works I'll use
What I am concluding is that with ADO using Jolt 3.51 you cannot get
reliable (same errors, same place) with serverside cursors. A friend has
also demonstrated this technique works with SQL Server 7.0. He has not gone
to the trouble of seeing if the situations that don't seem to work (that I
think should using serverside cursors) will work with SQL Server 7.0.
10. How can I list the programs in the task bar?
Click here to download an example program. It seems mostly correct, though in my initial tests it missed a couple tasks.
12. How can I make a gradient color title bar?
Subclass and use an owner draw window. See this example.
13. How can I install multiple programs in one setup program?
When you are using the auto setup maker in VB, near the end of the process
before it starts compiling (the last place where you can check things on and
off), you can click on an "add" button, this will compress any other program
you add to it. The only drawback is that when it is installed the programs you
added manually will be in the same directory as the main program.
The source code for the setup program should be in your VB directory. On my
computer it is at:
D:\Program Files\Microsoft Visual Studio\VB98\Wizards\PDWizard\Setup1\Setup1.vbp
You can change the Form_Load event of the form Setup.frm to alter the setup
procedure, or you can edit the file Setup.LST to change the files that will
The Setup1.vbp application is just another VB project, so you can change it
to install seven and a half applications, run dos programs, draw fractals
and beep incessantly through the PC speaker. ...If that is what you want.
So you have full control over the setup process.
I made a multiple application setup file by writing down each project's references after running the setup wizard. After finishing the list, I made a setup for one of the programs and then added the files I wrote down. I set the proper directories for these files using the "file details" button. It might be a hassle if you have lots of references to include, but it works for me.
14. How can I empty the wastebasket?
Although maybe not the most reliable way:
In Windows, a hidden directory exists which contains the data from the
recycle bin. The "Recycle Bin" on the desktop is only a link to its
contents. My "Recycle Bin" directory is "c:\recycled\" so the command Kill
"c:\recycled\*.*" works for me.
Use the SHEmptyRecycleBin function.
Private Sub ClearRecycleBin( _
Optional ByVal RootPath As String, _
Optional ByVal hwndParent As Long, _
Optional ByVal NoConfirmation As Boolean, _
Optional ByVal NoProgress As Boolean, _
Optional ByVal NoSound As Boolean)
Dim nOptions As Long
Const SHERB_NOCONFIRMATION = &H1
Const SHERB_NOPROGRESSUI = &H2
Const SHERB_NOSOUND = &H4
If NoConfirmation Then nOptions = SHERB_NOCONFIRMATION
If NoProgress Then nOptions = nOptions Or SHERB_NOPROGRESSUI
If NoSound Then nOptions = nOptions Or SHERB_NOSOUND
On Error Resume Next
SHEmptyRecycleBin hwndParent, RootPath, nOptions
Click here to download an example program.
15. How can I delete temporary internet files?
Herv Sherd found a solution by
Eduardo Morcillo at
Edanmo's VB Page.
16. How can I print a preformated file directly to printer?
There are two Microsoft references that address this. The best one is
Q154078: HOWTO: Send Raw Data to a Printer Using the Win32 API from Visual Basic,
which has an example using the WIN API to print directly to a printer device.
But the other one is more important -- if you use the FileCopy solution to copy
to a printer device name that contains
spaces, it will fail under NT in VB5 and VB6. This is illustrated in
Q252607: PRB: The CopyFile Method Does Not Work for Printer DeviceName with Spaces on NT.
To use FileCopy you'd have to remove any spaces from the NT
printer devicename, therefore it's not a robust solution.
Use the API solution -- it's better!
When you make use of the Printer object, VB will control said printer
through the installed printer driver. This leads to the attempted
translation of data that does not need translating, causing the problems
which you have no doubt already experienced.
The trick is to open the printer as a file, and then simply copy the data
from your preformatted file to the printer "file". VB thinks it is a file
and does not do any translation. If the printer is connected to your LPT1
port, you can open the port itself as a file, e.g.
Open MyFile For Input As #1
Open "LPT1" For Output As #2
TempString = Input(LOF(#1), #1)
Print #2, TempString;
Some operating systems will require a colon after LPT1, so you might have to
open "LPT1:" instead of "LPT1". If you want to print to a printer connected to a serial port, you can open "COM1" or "COM2" or whatever (or "COM1:"). If you want to print to a remote printer, then I'm stuck. I'm afraid I
haven't tried it yet.
I have had mixed results with this technique. My printer starts to print, and then stops. This may just be a configuration issue on my system.
I got the same results with a network printer specifying the printer file name like this:
Open "//Beauty/Digital" For Output As #2
Here the printer named Digital is being served by the computer Beauty. Presumably this will work, but I have that problem mentioned above.
Ben Rigsby adds:
One fundamental error (hopefully just a typo) is that the slashes
should be backslashes, i.e. \\PrintServer\Printer, which may work on its
own, but if it doesn't, the answer is to use a virtual port. For example,
assign your remote printer to a virtual port, such as LPT2: or LPT3:
through a DOS window, like this...
C:>NET USE LPTn: \\PrintServer\Printer ...
then simply open LPTn: as a file and proceed as Robert suggests for the local
printer. For example:
Open "LPT2:" For Output As #1
Print #1, "Hello, world!"
Use caution when working with LPT1 if using Windows NT since LPT1 is a
physical port; that is "actual hardware," which NT protects with absolute
security permissions. In some, if not all, versions of NT, a failed attempt at
directly accessing LPT1 results in a hardware lockout which renders LPT1
unavailable from that point on, unless and until you reboot the OS.
Tim Ayling adds:
This used to be a similar problem when I was trying to print preformatted files to a printer over a Novell network. The solution was to copy the file to the relevant captured port using the DOS binary switch 'COPY /b'.
In VB, the 'Filecopy sourcefile, destfile' method does the same. The reason Robert Terblanche is having problems is that the Input# and Print# methods try to interpret the data on-route (it doesn't say so, but it does).
The following simple code works perfectly:
Private Sub Form_Load()
FileCopy "c:\tims.prn", "\\Server\HP"
Where "c:\tims.prn" is the preformatted file and "\\Server\HP" is our networked HP printer.
NOTE: The files that are preformatted in this way are usually produced by a software package that has a default printer selected, then the file is printed with the user selecting 'Print to File' option. The .PRN file produced will then contain all the formatting codes relevant to the selected printer. Sending this ouput file to another printer will confuse the *?*?*?* out of the destination printer if it doesn't understand the control codes.
Sergio Perciballi has another approach:
I had this problem on a network. Admin had disabled
MS-DOS commands and VB exe's. We found that notepad can print with the /p flag on the command line.
These execute notepad/write(wordpad) and print the file you provide.
notepad.exe /p filepath
write.exe /p filepath
This uses winword(word) to execute a macro
"PathToWinword\winword.exe" "Path to Doc" /mFilePrintDefault
(DefaultPrintFile) but the program doesn't shut down unlike notepad/write which do. I have not tried FilePrint (/mFilePrint). I had to do this when using oracle forms 4.5 (a really complicated/frustrating program).
In the Dr. Dobb's web site (ddj.com), under Programmer's Resources,
under BASIC, there are some utilities that do a great job of printing.
If you have a preformatted file that's coded for a particular printer, use
PRNT, which simply spools characters to the printer.
For various plain-text files, PSET decides the best fit to the paper you
use, and optimizes the printing as much as possible to fit.
Note: These are DOS programs called from a SHELL command. Of course,
you can import the code into VB and make them VB programs.
I've used PSET extensively on every combination of network and PC,
from handheld and serial port to NT and network. As long as the
drivers etc. are in place, it works perfectly.