|
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 = TrueThere 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 FunctionThere'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 FunctionAnyway, 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.
This sample hasn't been published anywhere except here on this website, but first rights to such publication are jealously guarded - you have been warned. <g>
This sample uses the following API calls:
Module Library Function NetCam.ctl kernel32
ole32
olepro32
user32GetTickCount
GlobalAlloc
GlobalFree
GlobalLock
GlobalUnlock
RtlMoveMemory
CLSIDFromString
CreateStreamOnHGlobal
OleLoadPicture
GetWindowLong
SetWindowLong
SetWindowPosDon't see what you're looking for? Here's a complete API cross-reference.
Please, enjoy and learn from this sample. Include its code within your own projects, if you wish. But, in order to insure only the most recent code is available to all, I ask that you don't share the sample by any form of mass distribution. Download NetCam.zip, 14Kb, Last Updated: Tuesday, April 10, 2007
The following resources may also be of interest:
- NetGrab - Shows how to use the AsyncRead method of VB5/6 UserControls to download anything to memory or file.