NetCam

Description

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:

Color-coded with vbMarkUp - try it today!
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!

Color-coded with vbMarkUp - try it today!
' ********************************************************************
'  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.

Published

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>

APIs Usage

This sample uses the following API calls:

Module Library Function
NetCam.ctl kernel32





ole32

olepro32
user32

GetTickCount
GlobalAlloc
GlobalFree
GlobalLock
GlobalUnlock
RtlMoveMemory
CLSIDFromString
CreateStreamOnHGlobal
OleLoadPicture
GetWindowLong
SetWindowLong
SetWindowPos

Don't see what you're looking for? Here's a complete API cross-reference.

Download

Download NetCam.zip   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

See Also

The following resources may also be of interest: