An Original Idea

because all great software begins with an original idea

What every phonegap developer on windows phone 7 needs

Posted by anoriginalidea on January 6, 2012

 

Error logging!  An unfortunate side effect of developing apps in Javascript in mobile IE9 is that there doesn’t appear to be any error dialogs.

Try the following Javascript:

 

// provide our own console if it does not exist, huge dev aid!
if (typeof window.console == “undefined”) {
window.console = { log: function (str) { window.external.Notify(str); } };
}

// output any errors to console log, created above.
window.onerror = function (msg,url,linenumber) {
console.log(“Error ::” + msg + ” in ” + url + ” at line ” + linenumber);
};

console.log(“Installed console ! “);

This also gives you a “console.log” for free.

 

Enjoy!

 

 

Posted in Uncategorized | Leave a Comment »

Aspect Oriented Programming in Javascript

Posted by anoriginalidea on January 1, 2012

 

In designing javascript for modularity and “separation of concerns”, I was pleasantly surprised to discover a way of implementing AOP (Aspect Oriented Programming) in Javascript.

 

AOP can be useful for automatically “wrapping” methods to provide tracing, error logging or caching.

In this example, we are “wrapping” a method to show an alert when a method starts and finishes:

$.aop.around({ target: homelinksModel, method: 'get' }
 , function (invocation) {
alert("Method Call");

var ret = return invocation.proceed(); // This line calls the original method
alert("After Call");
return ret;
});

I used it for caching using Lawnchair  (an abstraction around local storage on client browsers).  It is designed to cache the result of data calls to the server.

$.aop.around({ target: homelinksModel, method: 'get' }
 , function (invocation) {
var lc = new Lawnchair(function () { });
var keyName = 'homelinksModel' + invocation.method;
var rec;
lc.get(keyName, function (ret) {
if (ret == null) {
    res = invocation.proceed()
    lc.save({ key: keyName, value: res });
    rec = res;
 }
 else {
 rec = ret.value;
 }
});
return rec;
}); // </aop>

I’m still learning Lawnchair, so there’s probably better ways of implementing this, but I thought a “useful” example might be helpful.

Peter Chung has an excellent article about Jquery AOP if you’d like to learn more.

There’s not alot of recent “action” around AOP in the Javascript space (Google Search).   I suspect that either some of it’s functionality is automatically part of jQuery or that the Javascript world isn’t mature enough yet to care much.    Perhaps it’s the former.

Over the past few days I’ve had a marvelous time implementing the MVVM pattern in Javascript.  I’m discovering all my favourite technologies are there, such as object databases (Lawnchair), binding (Knockout) and much more.  I hope to be blogging more about some of these soon.

Links

 

 

Posted in Uncategorized | Leave a Comment »

Returning data from an MVC method with a small cute ActionResult

Posted by anoriginalidea on June 17, 2011

image

In order return data from an MVC method, Microsoft provide a useful ActionResult object called JSONResult.  JSONResult exposes a convenient Data property that you can use to serialize an object to JSON.

Here’s how it can be used:

var res = new JsonResult();
res.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
res.Data = someObject;
return res;

Pretty easy to use.   Unfortunately there’s no matching XMLResult class if you want to return a scrap of XML instead.  In the MVCContrib project on codeplex there’s an XMLResult, but it’s event signature is slightly different.

I’ve modified this class to provide the same signature. 

Here’s the usage:

var res = new XmlResult();
res.Data = someObject;
return res;

Here’s the class:

using System.Web.Mvc;
using System.Xml.Serialization;

namespace Sample
{
    /// <summary>
    /// Action result that serializes the specified object into XML and outputs it to the response stream.
    /// </summary>
    public class XmlResult : ActionResult
    {
        private object _objectToSerialize;
        private XmlAttributeOverrides _xmlAttribueOverrides;

        /// <summary>
        /// Creates a new instance of the XmlResult class.
        /// </summary>
        /// <param name="objectToSerialize">The object to serialize to XML.</param>
        public XmlResult()
        {
      
        }

        /// <summary>
        /// Creates a new instance of the XmlResult class.
        /// </summary>
        /// <param name="objectToSerialize">The object to serialize to XML.</param>
        public XmlResult(object objectToSerialize)
        {
            _objectToSerialize = objectToSerialize;
        }

        /// <summary>
        /// Creates a new instance of the XMLResult class.
        /// </summary>
        /// <param name="objectToSerialize">The object to serialize to XML.</param>
        /// <param name="xmlAttributeOverrides"></param>
        public XmlResult(object objectToSerialize, XmlAttributeOverrides xmlAttributeOverrides)
        {
            _objectToSerialize = objectToSerialize;
            _xmlAttribueOverrides = xmlAttributeOverrides;
        }

        /// <summary>
        /// The object to be serialized to XML.
        /// </summary>
        public object Data
        {
            get
            { return _objectToSerialize; }
            set
            {
                _objectToSerialize = value;
            }
        }

        /// <summary>
        /// Serialises the object that was passed into the constructor to XML and writes the corresponding XML to the result stream.
        /// </summary>
        /// <param name="context">The controller context for the current request.</param>
        public override void ExecuteResult(ControllerContext context)
        {
            if (_objectToSerialize != null)
            {
                var xs = (_xmlAttribueOverrides == null) ?
                    new XmlSerializer(_objectToSerialize.GetType()) :
                    new XmlSerializer(_objectToSerialize.GetType(), _xmlAttribueOverrides);
                context.HttpContext.Response.ContentType = "text/xml";
                xs.Serialize(context.HttpContext.Response.Output, _objectToSerialize);
            }
        }
    }
}

Posted in .net Framework | Tagged: , , | Leave a Comment »

Data Uris–A way of embedding images in your html page or UIWebView

Posted by anoriginalidea on May 24, 2011

image

Currently I’m investigating ways of using  UIWebView to render a user interface.  

I’d like to re-purpose some server side html form rendering logic so that it can be resused on the iPhone, Android and Windows Phone. 

Modern forms incorporate more than simple form elements however.  They may include data, such as images.

By default, the UIWebview supports images in the “bundle” and images in the documents area. 

What if I want to create images in memory, then display these in the HTML?

An little known feature of many modern web browsers (including Webkit) is datauris.  A datauri allows data to be embedded in a webpage where a url would usually be.

Here’s an example:

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />
There are disadvantages however, which are the obvious effect this will have on caching and pagesize.
The Stackflow article Display Local UI Image On a UIWebView shows how to this, replicated here for your convenience.
This code demonstrates reading a file from the local bundle, then embedding into the background image, in the html dom of a UIWebView:

#import "NSString+DataURI.h"

#import "NSData+Base64.h"

...

-(void)webViewDidFinishLoad:(UIWebView *)webView

{

    NSString *imgPath = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"];

    NSData *imgData = [NSData dataWithContentsOfFile:imgPath];

    NSString *imgB64 = [[imgData base64Encoding] pngDataURIWithContent];

    NSString *javascript = [NSString stringWithFormat:@"document.body.style.backgroundImage='url(%@)';", imgB64];

    [webView stringByEvaluatingJavaScriptFromString:javascript];

}

This code uses the following utility functions that provide the base 64 encoding support:

NSData+Base64.h

@interface NSData (Base64)

+ (NSData *)dataWithBase64EncodedString:(NSString *)string;

- (id)initWithBase64EncodedString:(NSString *)string;

- (NSString *)base64Encoding;

- (NSString *)base64EncodingWithLineLength:(unsigned int) lineLength;

@end

NSData.Base64.m

#import "NSData+Base64.h"

static char encodingTable[64] = {

'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',

'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',

'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',

'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' };

@implementation NSData (VQBase64)

- (id)initWithString:(NSString *)string {

    if ((self = [super init])) {

        [self initWithBase64EncodedString:string];

    }

    return self;

}

+ (NSData *) dataWithBase64EncodedString:(NSString *) string {

    return [[[NSData allocWithZone:nil] initWithBase64EncodedString:string] autorelease];

}

- (id) initWithBase64EncodedString:(NSString *) string {

    NSMutableData *mutableData = nil;

    if( string ) {

        unsigned long ixtext = 0;

        unsigned long lentext = 0;

        unsigned char ch = 0;

        unsigned char inbuf[4], outbuf[3];

        short i = 0, ixinbuf = 0;

        BOOL flignore = NO;

        BOOL flendtext = NO;

        NSData *base64Data = nil;

        const unsigned char *base64Bytes = nil;

        // Convert the string to ASCII data.

        base64Data = [string dataUsingEncoding:NSASCIIStringEncoding];

        base64Bytes = [base64Data bytes];

        mutableData = [NSMutableData dataWithCapacity:[base64Data length]];

        lentext = [base64Data length];

        while( YES ) {

            if( ixtext >= lentext ) break;

            ch = base64Bytes[ixtext++];

            flignore = NO;

            if( ( ch >= 'A' ) && ( ch <= 'Z' ) ) ch = ch - 'A';

            else if( ( ch >= 'a' ) && ( ch <= 'z' ) ) ch = ch - 'a' + 26;

            else if( ( ch >= '0' ) && ( ch <= '9' ) ) ch = ch - '0' + 52;

            else if( ch == '+' ) ch = 62;

            else if( ch == '=' ) flendtext = YES;

            else if( ch == '/' ) ch = 63;

            else flignore = YES;

            if( ! flignore ) {

                short ctcharsinbuf = 3;

                BOOL flbreak = NO;

                if( flendtext ) {

                    if( ! ixinbuf ) break;

                    if( ( ixinbuf == 1 ) || ( ixinbuf == 2 ) ) ctcharsinbuf = 1;

                    else ctcharsinbuf = 2;

                    ixinbuf = 3;

                    flbreak = YES;

                }

                inbuf [ixinbuf++] = ch;

                if( ixinbuf == 4 ) {

                    ixinbuf = 0;

                    outbuf [0] = ( inbuf[0] << 2 ) | ( ( inbuf[1] & 0x30) >> 4 );

                    outbuf [1] = ( ( inbuf[1] & 0x0F ) << 4 ) | ( ( inbuf[2] & 0x3C ) >> 2 );

                    outbuf [2] = ( ( inbuf[2] & 0x03 ) << 6 ) | ( inbuf[3] & 0x3F );

                    for( i = 0; i < ctcharsinbuf; i++ )

                        [mutableData appendBytes:&outbuf[i] length:1];

                }

                if( flbreak )  break;

            }

        }

    }

    self = [self initWithData:mutableData];

    return self;

}

#pragma mark -

- (NSString *) base64Encoding {

    return [self base64EncodingWithLineLength:0];

}

- (NSString *) base64EncodingWithLineLength:(unsigned int) lineLength {

    const unsigned char     *bytes = [self bytes];

    NSMutableString *result = [NSMutableString stringWithCapacity:[self length]];

    unsigned long ixtext = 0;

    unsigned long lentext = [self length];

    long ctremaining = 0;

    unsigned char inbuf[3], outbuf[4];

    unsigned short i = 0;

    unsigned short charsonline = 0, ctcopy = 0;

    unsigned long ix = 0;

    while( YES ) {

        ctremaining = lentext - ixtext;

        if( ctremaining <= 0 ) break;

        for( i = 0; i < 3; i++ ) {

            ix = ixtext + i;

            if( ix < lentext ) inbuf[i] = bytes[ix];

            else inbuf [i] = 0;

        }

        outbuf [0] = (inbuf [0] & 0xFC) >> 2;

        outbuf [1] = ((inbuf [0] & 0x03) << 4) | ((inbuf [1] & 0xF0) >> 4);

        outbuf [2] = ((inbuf [1] & 0x0F) << 2) | ((inbuf [2] & 0xC0) >> 6);

        outbuf [3] = inbuf [2] & 0x3F;

        ctcopy = 4;

        switch( ctremaining ) {

            case 1:

                ctcopy = 2;

                break;

            case 2:

                ctcopy = 3;

                break;

        }

        for( i = 0; i < ctcopy; i++ )

            [result appendFormat:@"%c", encodingTable[outbuf[i]]];

        for( i = ctcopy; i < 4; i++ )

            [result appendString:@"="];

        ixtext += 3;

        charsonline += 4;

        if( lineLength > 0 ) {

            if( charsonline >= lineLength ) {

                charsonline = 0;

                [result appendString:@"\n"];

            }

        }

    }

    return [NSString stringWithString:result];

}

@end

  

NSString+DataURI.h

#import <Foundation/Foundation.h>

@interface NSString(DataURI)

- (NSString *) pngDataURIWithContent;

- (NSString *) jpgDataURIWithContent;

@end

NSString+DataURI.m

#import "NSString+DataURI.h"

@implementation NSString(DataURI)

- (NSString *) pngDataURIWithContent;

{

    NSString * result = [NSString stringWithFormat: @"data:image/png;base64,%@", self];

    return result;

}

- (NSString *) jpgDataURIWithContent;

{

    NSString * result = [NSString stringWithFormat: @"data:image/jpg;base64,%@", self];

    return result;

}

@end

  

 

Posted in Uncategorized | Tagged: , , , , | Leave a Comment »

Setting the default browser in Visual Studio 2010 in an MVC Application

Posted by anoriginalidea on May 12, 2011

image

To set the default browser used for debugging in an ASP.Net web application you need to bring up the “Browse With…” option on an aspx web page.  In a default MVC application you won’t have this option.

To do this temporarily add a “default.aspx” form to the root of your project, then the “Browse With…” context menu option will appear.  Choose your favourite browser, then remove the “default.aspx”.

Posted in Uncategorized | Leave a Comment »

Updating Twitter via SuperTweet using VBA

Posted by anoriginalidea on April 28, 2011

image

In my previous article I showed how to update a twitter status, prior to the twitter decision to remove the basic authentication api.  This article shows a way of getting around this.  The SuperTweet service allows you to continue to use basic authentication.  This is particularly useful for non-visual processes that use VBA.

I am currently using Twitter to update a private tweet-stream of my activities (such as sending email) from an Outlook 2010 macro.  This stream is then fed into the iPhone application Momento, which provides me with an “autodiary” based on my various twitter feeds and online services.

To use the SuperTweet, there are just three steps:

  1. Sign In with OAuth to the SuperTweet.net  (Sign-Up/Sign-In Now)
  2. Create a password for your applications to use with the SuperTweet.net API when they want to Tweet: Learn more.
  3. Change your application to use http://api.supertweet.net instead of Twitter.com.  See the sample below on how to do this using WinHttp in VBA. ( Learn more.)

The VBA code below will allow you to do an update to Twitter.  The UrlEncode implementation came from this StackOverflow post.

Function PostToTwitter(statusUpdate As String, username As String, password As String) As Boolean
    Const HTTPREQUEST_SETCREDENTIALS_FOR_SERVER = 0 
    On Error GoTo error_handler
   
    Dim WinHttpReq As New WinHttpRequest
 
     ' Assemble an HTTP Request.
    WinHttpReq.Open "POST", _
      "http://api.supertweet.net/1/statuses/update.xml", False
    WinHttpReq.SetCredentials username, password, HTTPREQUEST_SETCREDENTIALS_FOR_SERVER
    ' Send the HTTP Request.
    WinHttpReq.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded"
    WinHttpReq.Send "status=" & URLEncode(statusUpdate)
    If WinHttpReq.Status <> 200 Then
      
       GoTo error_handler
    End If
    DoEvents
    Debug.Print "Posted - " & statusUpdate
   
    PostToTwitter = True
   
    Exit Function

error_handler:

    PostToTwitter = False
   
    Exit Function
       
End Function

Public Function URLEncode( _
   StringToEncode As String, _
   Optional UsePlusRatherThanHexForSpace As Boolean = False _
) As String

  Dim TempAns As String
  Dim CurChr As Integer
  CurChr = 1

  Do Until CurChr - 1 = Len(StringToEncode)
    Select Case Asc(Mid(StringToEncode, CurChr, 1))
      Case 48 To 57, 65 To 90, 97 To 122
        TempAns = TempAns & Mid(StringToEncode, CurChr, 1)
      Case 32
        If UsePlusRatherThanHexForSpace = True Then
          TempAns = TempAns & "+"
        Else
          TempAns = TempAns & "%" & Hex(32)
        End If
      Case Else
        TempAns = TempAns & "%" & _
          Right("0" & Hex(Asc(Mid(StringToEncode, _
          CurChr, 1))), 2)
    End Select

    CurChr = CurChr + 1
  Loop

  URLEncode = TempAns
End Function

Posted in Outlook | Tagged: , , , , | Leave a Comment »

Bringing a Windows Application to the Foreground using .net

Posted by anoriginalidea on April 1, 2011

image

It’s surprising that there’s very little information available about bringing another windows application to the foreground.

The SetForegroundWindow api call can be useful in this regard, but it can’t be used unless you know the windows handle of the main window of the application you’re activating.  If the application you’re activating has many child windows, simply using the “MainWindowHandle” property on the Process object is not enough.

The code fragment and sample will show how to find a process, enumerate it’s child windows, then send these the foreground.

 

BringAppToForeground(“notepad”)

………

using System.Diagnostics;
using System.Runtime.InteropServices;

……….

[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]

public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,

 string windowClass, string windowTitle);


private void BringAppToForeground(string appName)
{

           // Find Parent 
           IntPtr parenthWnd = GetProcessWindowHandle(appName);
           if (parenthWnd == IntPtr.Zero) return;

           // Get list of child windows 
           List<IntPtr> loChildWindows = GetChildWindowHandles(parenthWnd);

           // Bring Windows to Front 
           BringWindowsToFront(loChildWindows.Reverse<IntPtr>());

}

private static IntPtr GetProcessWindowHandle(string processName)
{
           IntPtr parenthWnd = IntPtr.Zero;
           foreach (Process loProcess in Process.GetProcessesByName(processName))
           {
               parenthWnd = loProcess.MainWindowHandle;
               if (parenthWnd != IntPtr.Zero) break;
           }
           return parenthWnd;
}

private static void BringWindowsToFront(IEnumerable<IntPtr> windows)
{
           // Go through each and bring to front 
           foreach (IntPtr fronthWnd in windows)
           {
               SetForegroundWindow(fronthWnd);
           }
}

private static List<IntPtr> GetChildWindowHandles(IntPtr parenthWnd)
{
           IntPtr hWnd = IntPtr.Zero;
           List<IntPtr> loChildWindows = new List<IntPtr>();
           do
           {
               hWnd = FindWindowEx(parenthWnd, hWnd, null, null);
               if (hWnd == IntPtr.Zero) break;
               loChildWindows.Add(hWnd);
           }
           while (hWnd != IntPtr.Zero);
           return loChildWindows;
}


To understand how to use this in a winforms project, take a look at the sample project.

Download Sample Project

Posted in .net Framework | Tagged: , | Leave a Comment »

Optimising Winforms Dropdown list combos for touch

Posted by anoriginalidea on February 27, 2011

image

The optimisation of the iPhone and the iPad for touch has contributed to it’s success. 

One example of this kind of optimisation is “drop down combos”.  Rather than sticking with the traditional touch UI of a big combo with a fat scrollbar (that would mess up web pages), they do something completely different.  As shown above a neat “roller” control is shown which makes combos a pleasure to use.

On the iPad the experience is more like a traditional combo, but it is not the same.

In the creation of Windows 7 Tablet user interfaces I am sure our users would prefer this kind of experience.

In Winforms it turns out that it’s possible to improve the touch experience markedly using the “OwnerDrawVariable” style on comboboxes.

As you can see, when the combo box looks relatively normal, not taking up much screen real estate….

image

Yet when it’s clicked, the control becomes bigger, so the normal flick scrolling features of Windows 7 tablet can work better.

image

The following code sample is a class called TouchCombo that can be used to cause combos to take on the new style.  Here’s the code for the sample form:

Public Class Form1
    Private Sub mPopulate(ByVal combo As ComboBox)
        combo.Items.Add("Alpha")
        combo.Items.Add("Beta")
        combo.Items.Add("Gamma")
        combo.Items.Add("Delta")
        combo.Items.Add("Epsilon")
        combo.SelectedIndex = 0

    End Sub

    Private moTouchCombo As New TouchCombo
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        mPopulate(ComboBox1)
        mPopulate(ComboBox2)
        moTouchCombo.Connect(ComboBox2)
    End Sub
End Class

 

Here’s the code for the TouchCombo class:

 

Public Class TouchCombo

    Public Sub Connect(ByVal combo As ComboBox)
        combo.DrawMode = DrawMode.OwnerDrawVariable
        combo.DropDownHeight = 200
        combo.DropDownWidth = combo.Width * 1.5
        AddHandler combo.MeasureItem, AddressOf mMeasureItem
        AddHandler combo.DrawItem, AddressOf mDrawItem
        AddHandler combo.DropDown, AddressOf mDropDown
        AddHandler combo.DropDownClosed, AddressOf mDropDownClosed
        AddHandler combo.Disposed, AddressOf mDisposed
    End Sub

    Private Sub mDrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs)

        Dim loCombo As ComboBox = sender

        Using loBrush = New System.Drawing.SolidBrush(e.ForeColor)

            ' Draw the normal Background
            e.DrawBackground()

            ' If it's not dropped down, make it look normal
            If Not loCombo.DroppedDown OrElse e.State = DrawItemState.ComboBoxEdit OrElse e.State = DrawItemState.Default Then
                e.Graphics.DrawString(loCombo.Items(e.Index), loCombo.Font, loBrush, e.Bounds.X, e.Bounds.Y)
            Else
                ' Otherwise draw it big
                Using loFont As New System.Drawing.Font("Arial", 15, FontStyle.Bold)
                    e.Graphics.DrawString(loCombo.Items(e.Index), loFont, loBrush, e.Bounds.X, e.Bounds.Y)
                End Using
            End If

            e.DrawFocusRectangle()

        End Using

    End Sub
    Private Sub mMeasureItem(ByVal sender As Object, ByVal e As System.Windows.Forms.MeasureItemEventArgs)
        e.ItemHeight = 35
    End Sub
    Private Sub mDropDown(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim loCombo As ComboBox = sender
        loCombo.ItemHeight = 35
    End Sub

    Private Sub mDropDownClosed(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim loCombo As ComboBox = sender
        loCombo.ItemHeight = 15
    End Sub

    Private Sub mDisposed(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim loCombo As ComboBox = sender
        RemoveHandler loCombo.MeasureItem, AddressOf mMeasureItem
        RemoveHandler loCombo.DrawItem, AddressOf mDrawItem
        RemoveHandler loCombo.DropDown, AddressOf mDropDown
        RemoveHandler loCombo.DropDownClosed, AddressOf mDropDownClosed
        RemoveHandler loCombo.Disposed, AddressOf mDisposed
    End Sub
End Class

 

Download the project.

Posted in .net Framework, Code, iPhone, Software Development, Windows Forms | Tagged: , , | 3 Comments »

PowerPoint Inception Deck template for the Agile Samurai

Posted by anoriginalidea on January 27, 2011

 

image

Recently I read the “Agile Samurai” a great introduction to Agile software development.

The first part of the book talks about an artefact called the “Inception Deck”, a number of cards or powerpoint presentation that is used to help understand and scope a software project.

The inception deck elaborates on:

  1. Why are we here?
  2. Create an elevator pitch
  3. Product Box
  4. Create a NOT list
  5. Meet your neighbours
  6. Show the solution
  7. What keeps us up at night
  8. Size it up
  9. Be clear on what’s going to give
  10. Show what it’s going to take

I couldn’t find a powerpoint template to start me off, so I created one for anyone who is interested.  It’s pretty basic, but it’s nice to start somewhere.

Download here.

Links

Learn more from the Agile Warrior himself here:

http://agilewarrior.wordpress.com/2010/11/06/the-agile-inception-deck/

Posted in Software Development | Tagged: , | 3 Comments »

Capturing a still image from a Webcam in .net

Posted by anoriginalidea on October 4, 2010

image 

What if you want to capture a single image from a webcam?  It turns out that this is quite straight forward to do using a couple of Win32 api calls to the avicap32.dll library.

I encapsulated these api calls in a simple class.

To write a file called “snapshot.jpg” out to the desktop from a single webcam, simply declare the class and call a the TakePicture method.

Dim loSnap As New WebcamSnap
loSnap.TakePicture(0)

The devices collection shows a list of all webcam devices on your machine so you can populate a combo with them.

Note: This example has no decent error handling, which I suspect will be needed in the “real world” of multiple devices.

Here’s the WebcamSnap class:

Option Explicit On
Imports System.IO
Imports System.Runtime.InteropServices
''' <summary>
''' This class takes a snapshot from any or all attached webcams
''' </summary>
''' <remarks></remarks>
Public Class WebcamSnap
    Const WM_CAP As Short = &H400S

    Const WM_CAP_DRIVER_CONNECT As Integer = WM_CAP + 10
    Const WM_CAP_DRIVER_DISCONNECT As Integer = WM_CAP + 11
    Const WM_CAP_EDIT_COPY As Integer = WM_CAP + 30
    Const WS_CHILD As Integer = &H40000000
    Const WS_VISIBLE As Integer = &H10000000
    Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
        (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, _
        <MarshalAs(UnmanagedType.AsAny)> ByVal lParam As Object) As Integer

    Declare Function capCreateCaptureWindowA Lib "avicap32.dll" _
        (ByVal lpszWindowName As String, ByVal dwStyle As Integer, _
        ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, _
        ByVal nHeight As Short, ByVal hWndParent As Integer, _
        ByVal nID As Integer) As Integer

    Declare Function capGetDriverDescriptionA Lib "avicap32.dll" (ByVal wDriver As Short, _
        ByVal lpszName As String, ByVal cbName As Integer, ByVal lpszVer As String, _
        ByVal cbVer As Integer) As Boolean

    Public Devices As New List(Of String)
    Public Height As Integer = 480
    Public Width As Integer = 640
    Public OutputPath As String = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)
    Public FilenamePrefix As String = "snapshot"
    Public Sub New()
        mLoadDeviceList()
    End Sub
    Private Sub mLoadDeviceList()
        Dim lsName As String = Space(100)
        Dim lsVers As String = Space(100)
        Dim lbReturn As Boolean
        Dim x As Integer = 0

        Do
            '   Get Driver name and version
            lbReturn = capGetDriverDescriptionA(x, lsName, 100, lsVers, 100)

            ' If there was a device add device name to the list
            If lbReturn Then Devices.Add(lsName.Trim)
            x += 1
        Loop Until lbReturn = False
    End Sub
    Public Sub TakePicture()
        For i = 0 To Me.Devices.Count - 1
            Dim lsFilename As String = Path.Combine(OutputPath, Me.FilenamePrefix & i & ".jpg")
            TakePicture(i, lsFilename)
        Next
    End Sub
    Public Sub TakePicture(ByVal iDevice As Integer)
        Me.TakePicture(iDevice, Path.Combine(OutputPath, Me.FilenamePrefix & ".jpg"))
    End Sub
    Public Sub TakePicture(ByVal iDevice As Integer, ByVal filename As String)

        Dim lhHwnd As Integer ' Handle to preview window

        ' Create a form to play with
        Using loWindow As New System.Windows.Forms.Form

            ' Create capture window
            lhHwnd = capCreateCaptureWindowA(iDevice, WS_VISIBLE Or WS_CHILD, 0, 0, Me.Width, _
               Me.Height, loWindow.Handle.ToInt32, 0)

            ' Hook up the device
            SendMessage(lhHwnd, WM_CAP_DRIVER_CONNECT, iDevice, 0)
            ' Allow the webcam apeture to let enough light in
            For i = 1 To 10
                Application.DoEvents()
            Next

            ' Copy image to clipboard
            SendMessage(lhHwnd, WM_CAP_EDIT_COPY, 0, 0)

            ' Get image from clipboard and convert it to a bitmap
            Dim loData As IDataObject = Clipboard.GetDataObject()
            If loData.GetDataPresent(GetType(System.Drawing.Bitmap)) Then
                Using loBitmap As Image = CType(loData.GetData(GetType(System.Drawing.Bitmap)), Image)
                    loBitmap.Save(filename, Imaging.ImageFormat.Jpeg)
                End Using
            End If

            SendMessage(lhHwnd, WM_CAP_DRIVER_DISCONNECT, iDevice, 0)

        End Using

    End Sub

End Class


Links

I found the original code sample here:

http://www.experts-exchange.com/Programming/Languages/.NET/Visual_Basic.NET/Q_22482234.html

(Although I’ve seen it all over the web in various guises)

Posted in .net Framework, Software Development, VB.Net, Windows Forms | Tagged: , , , | Leave a Comment »

 
Follow

Get every new post delivered to your Inbox.

Join 108 other followers