BitmapCom - an ASP component for manipulating bitmaps on the server. Developed in Visual C++ / ATL 3 (no mfc). These pages document the development of the component as it takes place.
First steps:
Building a com component is incredibly simple with Visual C++ / ATL3 - walk through a wizard to set up the bare bones project, then add a component to the project. (If you have ever had to do this by hand you will know why this still impresses me).
Although i would like to jump in and start playing with the component it will probably help if the component actually does something useful. I like to have my COM classes wrap an existing class rather than being the class itself, this practice ensures far simpler testing of functionality before tending to specific COM issues. Time to write a simple bitmap class that supports loading, saving etc. BMP file format is supported because it is trivial to implement. JPEG file format support is made simpler by using the "Independent JPEG Groups JPEG software". This library works well, supports most JPEG formats, and does not require royalty payments. As the library is written in C, I added a simple C++ layer hooking into their error and stream abstractions, hiding their implementation.
Now that the internal bitmap class is complete some functionality can be added to BitmapCom. For initial testing of the component I have added one member function and two properties:
Not an awful lot can be done with this level of functionality, but we have to start somewhere.
Test javasript code:
| function TestFunc1( FilenameStr )
try { var oBitmapCom = Server.CreateObject('BitmapComponent.BitmapCom'); var filename = Server.MapPath( FilenameStr ); oBitmapCom.LoadFromFile(filename); Response.Write('Filename=' + FilenameStr + ' Width=' + oBitmapCom.Width + ' Height=' + oBitmapCom.Height); } catch(e) { Response.Write('exception: ' + e.description + '<br>'); } |
Test output:
BuildStr: BitmapComponents build: 105 : copyright 2002 Daniel HordernLogPath: C:\sites\danielhordern.com\home\databases\bitmapcomlog.txt
LogErrors: false
LogAccess: false
| Filename=images/bitmapcom_1.jpg Width=340 Height=227 |
There is only one issue left to resolve before the component can become even remotely useful - displaying the bitmap! My initial approach at adding this functionality was the obvious approach. Internally all bitmap loading and saving is done to a stream interface - very similar to the model used by the COM IStream interface. A memory stream was implemented and viola! bitmaps can be saved to memory. To make this available to the scripting client the memory stream is copied to a variant array and returned to the caller.
Problems! - javascript does not have enough safe-array support to be able to handle this return type. Testing using vbscript proved the method worked however I prefer javascript where possible. Time to find another solution.
When I use the insert ATL object wizard in VC++ I had selected Simple Object. By selecting ActiveX Server Component the object gets two extra interface members OnStartPage() and OnEndPage() complete with implementation These members are called whenever the object is hosted by IIS, and retrieve pointers to the IIS page objects (Response, Request etc). This solves the javascript issue, and allows the COM object to directly call Response.BinaryWrite() to send the bitmap directly to the client.
Coding didnt take long, testing this solution was a real ball-breaker - no bitmap was being displayed on the screen. I knew all was working internally, a vb test application allowed me to trace into the COM object and follow code execution, but still no luck. Many hours later, just before throwing in the towel I searched the msdn knowledge base - a small article mentioned that Response.BinaryWrite() would not work as expected if client-side script debugging is enabled!!! Damn, 10 seconds later the bitmap was being displaying in the browser window!
Three methods have been added to the COM object, the first two are still in existence as they may be useful later? these are the vbscript specific members. The third method is the simplest to use, and provides a mechanism to specific output width and height.
Test javasript code:
| // source required to display bitmap in this page
// <img src='GetBitmapJs.asp?action=display&filename="images/bitmapcom_1.jpg"&width=0&height=0'> // excerpt from GetBitmapJs.asp // <% // retrieve params // var actionStr = new String(Request( 'action' ) ); var filename = _Safe_GetQueryStr('filename'); var width = _Safe_GetQueryStr('width'); var height = _Safe_GetQueryStr('height'); if( actionStr == 'undefined' ) actionStr = ''; actionStr = actionStr.toLowerCase(); if( actionStr == 'display' ) DisplayBitmap( filename, width, height ); // worker function // function DisplayBitmap(FilenameStr, Width, Height) { try { var oBitmapCom = Server.CreateObject('BitmapComponent.BitmapCom'); FilenameStr = Server.MapPath( FilenameStr ); oBitmapCom.LoadFromFile(FilenameStr); oBitmapCom.Write2ASPResponse( Width, Height ); } catch(e) { Response.Write('DisplayBitmap(): ' + e.description + '<br>'); } } %> |
Test output:

Note: Setting width and height to zero when calling Write2ASPResponse results in the bitmap being output at its current size. If width and height are specificed a internally a copy of the bitmap is resampled to the requested width and height, then output.
Now that basic functionality is working, whats next?