Saturday, January 28, 2006

I've been encouraged to blog about a new feature available as a part of newly released beta 1 of OpenNETCF SDF - the Imaging API wrapper.

The Imaging API is an attempt to bring Image codec support from the desktop GDI+ to mobile devices. It is implemented as a set of COM interfaces available to C/C++ applications. There is a cocreatable class ImagingFactory and a bunch of interfaces that allow loading, saving and manipulating the images. Effective 1.0 SP2 Compact Framework uses this API internally to load (and in 2.0 to save) images instead of imgdecmp.dll used in CF1 SP1 and before.

In CF2 it became possible to wrap COM interfaces for use in the managed applications. The Bitmap class is also significantly richer than before. Given this, one would ask what would be the reason to try using Imaging API directly. Here is a brief list of things that are not part of the CF2 Bitmap class:

  • Access to image tags (EXIF header etc)
  • Image transformation (flip, rotate, gamma/brightness/contrast controls)
  • Thumbnails, loading parts of the large image

The Imaging wrapper is located inside OpenNetCF.Drawing components as OpenNetCF.Drawing.Imaging namespace. On top of the basic interfaces it offers a small utility layer presented as ImageUtils class. ImageUtils has the following high-level methods:

  • Bitmap RotateFlip(Bitmap bitmap, RotateFlipType type)
  • Bitmap Flip(Bitmap bitmap, bool flipX, bool flipY)
  • Bitmap IBitmapImageToBitmap(IBitmapImage imageBitmap)
  • IBitmapImage BitmapToIImageBitmap(Bitmap bitmap)
  • IBitmapImage CreateThumbnail(Stream stream, Size size)

The last method - CreateThumbnail - allows loading a small thumbnail instead of a large image. Most Pocket PC devices won't be able to load and display a 5-6 megapixel image produced by most digital cameras. At the same time one can easily load 640x480 thumbnail that will let one implement basic zoomable view.

In the next couple of days I'm going to provide a few code samples. Stay tuned.

1/28/2006 1:53:31 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]  | 
 Wednesday, January 04, 2006

Alex Y sent me a link to the digg.com, where ISO Recorder is being discussed. I'm not sure what to make of it, but I know it's better to be “digged” than to be /.ed

1/4/2006 6:08:53 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]  | 
 Tuesday, November 01, 2005

Few days ago (Oct 26) in San Francisco I spotted what must be a part of the upcoming Visual Studio/SQL Server/Biztalk launch event. The picture is a bit grainy, but then whatever JAM is, a great camera it is not.

These are TX1s from London Taxi Company

11/1/2005 2:55:33 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Thursday, October 27, 2005

In what is widely regarded as a shrewd move a popular low-cost brokerage firm Charles Schwab started a new advertising campaign clamoring for customers to “Talk to Chuck”. I think not. This is if you ask me a clear case of sheer idiocy. “Charles Schwab” sounded respectable, somewhat European. Now, why would I want to have a broker whose name is Chuck? Why not Billy Bob?

In entirely unrelated news: a popular low-cost chain of kiddie restaurants in an attempt to bolster its image has renamed itself to “Charles E. Cheese”

10/27/2005 11:58:24 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Wednesday, October 12, 2005

I've been reading about the new Microsoft Streets and Trips 2006 and came across a feature comparison chart

This reminded me of one of the reasons why I dislike marketing people. Here are 10 rules “How to create a favorable comparison chart“

  1. Pick your competition wisely. If possible, compare to unrelated products
    or products marketed for a different niche
  2. Try limiting your comparison to only those features that exist in your
    product. Never mind the competition has something that you don't - you are
    playing on the home field and you are setting the rules.
  3. Never hesitate to use meaningless "features" as sales points. "Only our
    product is built on the powerful XYZ technology". So if the competitor used ZYX
    technology, that's his problem. We are not here to discuss whose technology
    is better. Suffice it to say that they did not use ours.
  4. Be charitable. If you can throw in a few features that exist in all
    competing products do it, but do it sparingly. You are not going to look
    good if there are too many of those.
  5. "New!" next to the feature description works very well. It is especially
    good when done in red bold font with a little star ornament. And no, you do
    not have to explain that "new" refers to something that is new to this
    release of *your* product, even if competition had it for ages.
  6. It really helps to compare your almost released product to competitor's
    last year release even if they just announced a new version. After all you
    haven't seen it - it may very well not exist. Remember, other people create
    vaporware - we have bold plans and imminent upcoming releases.
  7. If you can't think of 10 rules, make it 6.
10/12/2005 5:55:39 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Monday, September 12, 2005

Update: I'm getting reports that this build of ISO Recorder crashes on the recent Vista builds. I pulled the download for now. Expect a new drop for the Vista Beta 2 timeframe

With certain amount of help from other Vista beta testers I have released an early build of ISO Recorder for Windows Vista.

I want to thank David Smith and Jonathan Rosenberg for their assistance with testing and willingnes to help prove that 3rd time is indeed a charm. Apparently working with beta OS (Vista) and beta compiler (VS2005) can be ... tricky sometimes

9/12/2005 12:56:36 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Friday, September 09, 2005

If you have tried to install VS2005 August CTP on Windows Vista 64-bit edition you probably found out that it is not possible to do because 64-bit .Net framework fails to install. The reason it fails to install is quite silly. It's a bug in the launch condition in the installation package.

Here is how to get around this. You will need the msidb.exe tool found in the bin directory of the Platform SDK

  1. Create a temporary directory and copy \VS\WCU\dotnetframework\x64\NetFX64.exe from the Visual Studio DVD into this directory. Copy msidb.exe into the same directory (or ensure that it is in the path)
  2. Explode it using a command
    NetFx64.exe /Q /C /T:%CD%
  3. Extract the LaunchCondition decriptor by running:
    msidb -e -dnetfx.msi -f%CD% LaunchCondition
  4. Use notepad to edit LaunchCondtion.idt. Add OR (VersionNT=600) to the condition as shown below:
    (Version9X >= 410) OR ((VersionNT = 500) AND (ServicePackLevel >= 3)) OR (VersionNT = 501) OR ((VersionNT = 502) AND (ServicePackLevel >= 1)) OR (VersionNT=600) [LocProductName] is not supported on Windows 95, Windows NT, Windows 2000 without Service Pack 3 or greater, and Windows Server 2003 without Service Pack 1 or greater.
  5. Use MSIDB again to import the updated launch condition:
    msidb -i -dnetfx.msi -f%CD% LaunchCondition.idt
  6. Now launch install.exe and complete the framework installation. At this point you can relaunch Studio setup and successfully complete it.
9/9/2005 12:33:53 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Sunday, August 28, 2005

I've been lazily browsing the registry on my computer (it seems I tend to do it a lot) when I noticed a familiar name - System.Collections.Queue. Wait a minute. Isn't it a .NET class? Indeed, what I was looking at was a COM registration for a class System.Collections.Queue, pointing to mscoree.dll and mscorlib.dll as its server. It sounded like someone has kindly registered the mscorlib (parts of it anyway) with COM, and it was sitting there ready to be used. To test my hypothesis, I quickly create a simple VB script:

' CollTest.vbs
set coll = CreateObject("System.Collections.Queue")
coll.Enqueue "Bob"
coll.Enqueue "Joe"
WScript.Echo coll.Dequeue()
WScript.Echo coll.Dequeue()

When I ran it, lo and behold “Bob“ and “Joe“ were the 2 strings printed. When you stop to think about it, of course it should have worked, but nevertheless it felt as a nice surprise. After all the VB Script stock object set is quite limited and getting a free extension (.NET 1.1 is on every machine I work with) was quite welcome.

Of course I went to explore it further. From the System.Collections namespace we apparently have:

  • Queue
  • Stack
  • ArrayList
  • SortedList
  • Hashtable

I'd say it makes a rather nice addition to the existing VB script (and javascript as well) toolset. But wait, there is more...

System.IO namespace is represented by two useful classes:

  • StringWriter
  • MemoryStream

Finally, there are System.Text.StringBuilder and System.Random classes.

Why would I be interested in StringWriter (or StringBuilder)? Because frankly, VBScript memory allocator sucks. Anyone who ever tried to build a large string concatenating small ones knows that after 65536 characters performance drops drastically. Concatenating first 65536 characters one by one takes about 1.5 sec on my 3.5 GHz machine. Appending the next 65536 takes about 4 sec. The following 65536 characters take 33 sec. And you don't want to know what happens after that. Well, in case you do, appending together the string representations of numbers 1 through 100000 takes over 2 minutes.

Of course I decided to check if I could do it faster with StringWriter. But there was a catch. If you check the lsit of methods of the StringWriter class in the Visual Studio object browser, or rather the TextWriter class, from which StringWriter derives all of the Write methods, you would notice that Write() has 17 overloads, and WriteLine() has 18. Obviously you would want to use a particlular overload. How do you choose one. As it happens, the .NET framework deals with this problem in a straightforward, if inelegant way. If you have 18 overloaded methods called Write, they are exposed as Write, Write_2, Write_3 ... Write_18. How do you find out which one is which? If you look into the class browser (or Ildasm), the order of the overloads is reverse to what you see. E.g. for StringWriter we have:

  • System.IO.TextWriter.Write(string, params object[]) // Invoke as Write_17
  • System.IO.TextWriter.Write(string, object, obejct, object) // Invoke as Write_16
  • System.IO.TextWriter.Write(string, object,object) // Invoke as Write_15
  • System.IO.TextWriter.Write(string, object) // Invoke as Write_14
  • System.IO.TextWriter.Write(object) // Invoke as Write_13
  • System.IO.TextWriter.Write(string) // Invoke as Write_12
  • System.IO.TextWriter.Write(decimal) // Invoke as Write_11
  • System.IO.TextWriter.Write(double) // Invoke as Write_10
  • System.IO.TextWriter.Write(float) // Invoke as Write_9
  • System.IO.TextWriter.Write(ulong) // Invoke as Write_8
  • System.IO.TextWriter.Write(long) // Invoke as Write_7
  • System.IO.TextWriter.Write(uint) // Invoke as Write_6
  • System.IO.TextWriter.Write(int) // Invoke as Write_5
  • System.IO.TextWriter.Write(bool) // Invoke as Write_4
  • System.IO.TextWriter.Write(char[], int, int) // Invoke as Write_3
  • System.IO.TextWriter.Write(char[]) // Invoke as Write_2
  • System.IO.TextWriter.Write(char) // Invoke as Write

This means that if we want to write a string into a stringwriter, we would call:

s = “Bob“
Set wrt = CreateObject("System.IO.StringWriter")
wrt.Write_12 s

To test the performance of a StringWriter as compared to concatenating a VB string I ran the following script:

'stringtest.vbs
WScript.Echo Now
s = ""
for i = 1 to 100000
s = s & CStr(i)
next
WScript.Echo Now


set wrt = CreateObject("System.IO.StringWriter")
for i = 1 to 100000
s = CStr(i)
wrt.Write_12 s
next
s = wrt.GetStringBuilder().ToString()
WScript.Echo Now

And here are the results:

8/28/2005 1:02:14 AM
8/28/2005 1:04:07 AM
8/28/2005 1:04:08 AM

As you can see, concatenating a VB string took nearly 2 minutes, while .NET string was only a second.

But wait, there is more...

Let's take a look at the StringBuilder class. The most important feature of it (at least in the context of what we are doing here) is formatting. There are 5 AppendFormat methods:

AppendFormat ( System.IFormatProvider provider , System.String format , params object[] args )
AppendFormat ( System.String
format , params object[] args )
AppendFormat ( System.String
format , System.Object arg0 , System.Object arg1 , System.Object arg2 )
AppendFormat ( System.String format , System.Object arg0 , System.Object arg1 )
AppendFormat ( System.String
format , System.Object arg0 )

Let's see if we could easily use one of them:

'sbtest.vbs
set sb = CreateObject("System.Text.StringBuilder")
sb.AppendFormat_5 Nothing, "{0} is a {1} number" & vbCrLf, Array(1, "loneliest")
sb.AppendFormat_5 Nothing, "{0} is a {1} number" & vbCrLf, Array(2, "happiest")
WScript.Echo sb.ToString()

Prints:

1 is a loneliest number
2 is a happiest number

Weee!! We have formatting! In VBScript.

Oh, yes, did I mention it also works in ASP?

8/28/2005 1:21:02 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [1]  |