Here's
a nifty little widget you can drop right into your VB5 and VB6 projects.
It's written to demonstrate the asynchronous download capability of Classic VB
UserControls, by periodically downloading an image from the internet, comparing
it's bits to the previous download, and updating the onscreen display whenever
the image has changed. Please note that there is a conditional constant defining
which version of VB it's running under -- you must change this value as
appropriate. I haven't tried this in VBA, but if you were to compile it to an OCX
it'd probably work there as well.
' Set this conditional constant
' to False if you're using this
' in a VB5 project.
#Const VB6 = True
There are actually a number of interesting things to be learned from this
sample. Perhaps foremost however is just how simple VB5/6 make it for you to
download files asynchronously from the Internet! The technique used here will be
useful for just about anything you need to quick-grab that doesn't require
credentials.
You may also find the BorderStyle and Appearance property handling to be of
interest, if you ever create your own UserControls. These are some tricks I
picked up back in the old CCRP
days. You may set the Appearance property to Flat, 3Dsunken, or 3Draised, and
the BorderStyle options are None, FixedSingle, and Raised3D. Makes for some
interesting options!
Bonus Material
Ever wondered whether an array was initialized or not? Here's a really simple
routine that totally avoids error-trapping by examining the associated SAFEARRAY
structure. We pull this of by first wrapping the array variable within a
Variant (happens automatically as you pass it to this routine), and then peeking through its descriptor a bit:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(Destination As Any, Source As Any, ByVal Length As Long)
Private Function ArrayInitialized(vArray As Variant) As Boolean
Dim pSA As Long
' Make sure an array was passed in:
If IsArray(vArray) Then
' Get the pointer out of the Variant:
CopyMemory pSA, ByVal VarPtr(vArray) + 8, 4&
If pSA Then
' Try to get the descriptor:
CopyMemory pSA, ByVal pSA, 4
' Array is initialized only if we got
' the SAFEARRAY descriptor:
ArrayInitialized = (pSA <> 0)
End If
End If
End Function
There's also a really slick routine, first published by
Brad Martinez, that
can load an image into a StdPicture object directly from memory. This allows
things such as reading graphic image from the internet, or a resource file, as a
series of bytes and transforming those to a Picture without ever needing to
write to disk first. In other words, you can also use it to load GIF or JPG
images straight from a resource file!
 |
' ********************************************************************
' This function inspired by the work of Brad Martinez.
' http://btmtz.mvps.org/_misc/index.htm
' ********************************************************************
' Download complete project (below) for API declarations.
' ********************************************************************
Public Function LoadImage(ImageBytes() As Byte) As StdPicture
Dim nBytes As Long
Dim hMem As Long
Dim lpMem As Long
Dim oStream As IUnknown
Dim oPicture As IPicture
Dim IID_IPicture As GUID
Const sIID_IPicture As String = "{7BF80980-BF32-101A-8BBB-00AA00300CAB}"
' GIGO: return Nothing.
If ArrayInitialized(ImageBytes) = False Then Exit Function
' Calculate the array size
nBytes = UBound(ImageBytes) - LBound(ImageBytes) + 1
' Create and lock a memory buffer for image bytes.
hMem = GlobalAlloc(GMEM_MOVEABLE, nBytes)
If hMem Then
lpMem = GlobalLock(hMem)
If lpMem Then
' Copy image bytes to memory buffer, and unlock.
Call CopyMemory(ByVal lpMem, ImageBytes(LBound(ImageBytes)), nBytes)
Call GlobalUnlock(hMem)
' Create an IStream object in global memory buffer.
If (CreateStreamOnHGlobal(hMem, False, oStream) = S_OK) Then
' Translate CLSID string to IPicture interface ID.
If (CLSIDFromString(StrPtr(sIID_IPicture), IID_IPicture) = S_OK) Then
' Create an IPicture from the IStream (the docs say the call does not
' AddRef its last param, but it looks like the reference counts are correct..)
Call OleLoadPicture(ByVal ObjPtr(oStream), nBytes, False, IID_IPicture, oPicture)
End If
End If
End If
' Release global memory object.
Call GlobalFree(hMem)
End If
' Return results.
Set LoadImage = oPicture
End Function
Anyway, this is kind of a fun one. I've used it to create a good handful of
funky little utilities. Two of my favorites are a Mt. St. Helens screensaver and
an I-5 traffic cam monitor for my local region. I'd enjoy hearing how you use
it.