WRITE AN ACTIVEX USERCONTROL TO POST DESKTOP SCREEN CAPTURES TO A REMOTE DATABASE WITH XMLHTTP

By Peter A. Bromberg, Ph.D.

Peter Bromberg  


Have you ever been in a programming situation where you wanted to be able to have users do an ALT/PrintScreen or CTRL/PrintScreen to capture information from their desktop and send it up to a database to be saved?

This is very common in bug-tracking and other types of reporting software where a user may be entering a bug report, and they need to be able to store a picture along with the report that shows the error "in flagrante delicto" (caught in the act). There is nothing like a picture to tell the whole story to the BugMeister!

The following little Visual Basic project will show how easy it can be to write an ActiveX usercontrol that can be hosted in a web page to accomplish this. We will create an ActiveX usercontrol, provide for capture from the clipboard to a picturebox on the control, then serialize the picture into a byte array with a little PropertyBag trick. Next, we'll fire up XMLHTTP and shoot the picture over the wire to our server, where we'll conveniently store it in an Access database. Finally, as a "Proof of Concept", we'll retrieve the stored picture from the database and send it back over the wire to our HTML Page on the client side, and display it back into the Picture Box in our usercontrol!



First, fire up Visual Basic 6.0 and create a new project of type ActiveX Control. That's this guy right here:
Drop one PictureBox called Image1 and one TextBox called Text1 on to your control's visible working surface. Now open the control so you can see the code area, and past in the following code:

Option Explicit
Private m_varURL As Variant
Public Property Let URL(myURL As Variant)
m_varURL = Text1.Text
End Property

Public Function CaptureClipboard(oPictureBox As Object) As Boolean
On Error GoTo Errhandler

Set oPictureBox.Picture = Clipboard.GetData(0)
Clipboard.Clear
CaptureClipboard = True
Exit Function
Errhandler:
Err.Raise Err.Number, "Capture", Err.Description
End Function

Private Sub Image1_Click()
CaptureClipboard Image1
' Convert StdPicture object to byte array using PropertyBag:
Dim pb As New PropertyBag
pb.WriteProperty "pic", Image1.Picture

MsgBox "Click OK to erase pic before send."
Set Image1.Picture = Nothing

Dim vmypic As Variant
vmypic = pb.ReadProperty("pic")

Dim XMLHTTP As MSXML2.XMLHTTP30
Set XMLHTTP = New MSXML2.XMLHTTP30
m_varURL = Text1.Text

XMLHTTP.open "POST", m_varURL, False
XMLHTTP.setRequestHeader "PICNAME", "MyPic"
XMLHTTP.send vmypic
vmypic = ""
MsgBox XMLHTTP.statusText
pb.WriteProperty "pic", XMLHTTP.responseBody
Text1.Text = XMLHTTP.getResponseHeader("PICNAME")

Set Image1.Picture = pb.ReadProperty("pic")
Set XMLHTTP = Nothing

End Sub

What we are doing here is this:

1) We set a property so that it can hold a user - entered value for the URL to send our captured pictures to.
2) Then we create a function Image1_Click() that will copy any image that the user is holding in the Windows clipboard into our PictureBox when the user cliicks on the visible surface of our control in their browser page.
3) We then need to convert this picture to a byte array in order to send the binary data over the wire. One of the fastest ways to do this is to simply save it into a VB PropertyBag object, which automatically makes this conversion for us.
4) Then we show a messagebox that will erase the picture from the picturebox control so the user will see it's really gone.
5) We then retrieve our byte array from the PropertyBag object and assign it to a variant suitable for sending over the wire with XMLHTTP.
6)We fire up an instance of XMLHTTP, set the open method to the URL , set a custom requestHeader "PICNAME" so that we can identify this picture when it's saved in our database, and BOOM --before you can say "W3C six times fast" , - it's outta here!

Now we need to switch gears and jump over to the webserver where we sent this thing, to look at the neat ASP page I've whipped up to handle this puppy:

<!--METADATA NAME="Microsoft ActiveX Data Objects 2.5 Library" TYPE="TypeLib" UUID="{00000205-0000-0010-8000-00AA006D2EA4}"-->

<%
'receivepic.asp
'Get the name of the image that was sent from the custom request header so we can save it with the picture
imageName=Request.ServerVariables("HTTP_PICNAME")
' Now binary read the entire request object which represents the entire image...
mypic =Request.BinaryRead(Request.totalBytes)

' At this point, the variant "mypic" holds a byte array consisting of the binary image that was sent.
' We can now save it to a database, etc.
' In this case we'll save it to an Access table, and just turn around, pull it back out,
' and send it back as a sort of "proof of concept".

Dim strconn
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & server.mappath("PICS.MDB") & ";User Id=admin;Password=;"

dim rs
set rs = Server.CreateObject("ADODB.Recordset")
'Note we set the OpenKeyset and LockOptimistic to give ourselves an updatable recordset ...
rs.Open "Select * from Pictures",strConn ,adOpenKeyset, adLockOptimistic
' Now all we need to do is add a new record, stick our stuff in there, and call the update method...
rs.AddNew
rs("PICNAME")=imageName
rs("PICDATA")=mypic
rs.Update
rs.close
set rs=nothing

' Now start all over as if this were a new page, and send the pic and its name right back from out of the database...
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & server.mappath("PICS.MDB") & ";User Id=admin;Password=;"

set rs = Server.CreateObject("ADODB.Recordset")
rs.Open "Select * from Pictures",strConn ,adOpenKeyset, adLockOptimistic
rs.MoveLast

' Let's put the picture name into a custom response header so it can be read and used by the receiver...
Response.addHeader "PICNAME", rs("PICNAME")
' This is binary image data, so we need to write it out binary
Response.binarywrite rs("PICDATA")

' Outta here!
rs.close
set rs=nothing

%>

Ok! You've got that all memorized, right? Here's what we are doing:

1) We set a metadata reference to the ADO type library so we don't have to include or remember all those silly ADO constants.
2) We get the name of the picture by reading the custom header: imageName=Request.ServerVariables("HTTP_PICNAME")
3) we read our entire picture into a variable, in the same form it was sent - a binary byte array: mypic =Request.BinaryRead(Request.totalBytes)
4) We save it, and the picture name, into our database by using the convenience of an updateable recordset (We could also create a new recordset on the fly if we wanted).
5) As a "Proof of concept", we get the record back out, set a new Response Header with the picture name, and binary write the picture data to the receiver. See the inline comments in the ASP page code above for more.

Now, back to VB, where as you remember, we've asked XMLHTTP to send out this screen-pop picture. XMLHTTP, also , on every Send(), will patiently wait for as long as we like (provided we set the async property to False), to get some return data. When it does, we simply read the ResponseBody, which by definition is a byte array - exactly what we need - and we also read the picture name from the header.

All that's left to do is show the picture name in the Text1.Text and the actual picture we got back out of the database in the Image1.Picture property, and we're done:


The beauty of this is that in an Intranet environment or a trusted site environment, you can use the Package and Deployment wizard to create an Internet CAB download and automatically download, register and display your control in the users' browsers. Now obviously this is a pretty simplistic example. But I've kept it that way so that it would be easy to understand. Now it's up to you to build on this and create unique solutions that more specifically suit your needs. The download has all the VB code, the ASP page, the Access Database in Access 97 format, and even an HTML page with the OBJECT Tag and the CAB file to self install the control on the client.

Donwload the code that accompanies this article

 

Peter Bromberg is an independent consultant specializing in distributed .NET solutionsa Senior Programmer /Analyst at in Orlando and a co-developer of the EggheadCafe.com developer website. He can be reached at pbromberg@yahoo.com

Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.


Search

search