Monday, October 02, 2006

I have a lot of respect for MSDN technical articles. They are more often than not a source of knowledge one would be hard pressed to obtain from official documentation. Kraig Brockschmidt's treatise on OLE internals, Nancy Winnick Clutz explanation of TAPI, Icon internals by John Hornick - all of these are precious gems of knowledge. Heck, I even wrote a few myself. Over the years there were some that are brilliant and concise, other that were less interesting. Almost every article there had some code posted with it. The code samples would also vary in quality, but not so much as to raise an eyebrow.

On several occasions I had this conversation with my teenage daughter, the gist of which was that even though the Algebra lesson is not an English lesson, it does not mean she can disregard the grammar completely, while doing her math homework. Apparently it was not obvious to her, that writing properly is not something you do only when you absolutely have to. Similarly, I suppose when you write code, even throwaway code, you should still be conscious of how you do it.

When I first came across this, my first reaction was - this is good stuff. It'll teach developers not to use an old, outdated control and show, how to replace it with alternative modern controls. And then I saw the following gem:

If you must search compiled code, you can look certain patterns that represent the GUIDs. For example, the following GUID:

{ABCDEFGH-IJKL-MNOP-QRST-UVWXYZ012345}

becomes the following hexadecimal sequence in binary:

GH EF CD AB KL IJ OP MN QR ST UV WX YZ 01 23 45

 

Er, what hexadecimal sequence?
But following it was a code sample

// Compile and execute:  "FindGUIDs YourApplication.exe"
using System;
using System.Text;
using System.IO;

namespace FindGUIDs {
class Program {
  static void Main(string[] args) {
    FileStream    fs = File.OpenRead(args[0]);
    StringBuilder sb = new StringBuilder();
    do {
      Int32 b = fs.ReadByte();
      if (-1 == b) {
        break;
      }
      sb.AppendFormat("{0:X2}", b);
    } while (true);
    fs.Close();
    String s = sb.ToString();
    if (s.Contains("E0A58D4371F1D011984E0000F80270F8"))
      Console.Out.WriteLine("GUID for TriEditDocument Class detected.");
    if (s.Contains("DFA58D4371F1D011984E0000F80270F8")) {
      Console.Out.WriteLine(
        "GUID for ITriEditDocument Interface detected.");
      }
      if (s.Contains("0002362DF5FFd1118D0300A0C959BC0A")) {
        Console.Out.WriteLine("GUID for DHTMLEdit Class detected.");
      }
      if (s.Contains("91B504CE1F2Bd2118D1E00A0C959BC0A")) {
        Console.Out.WriteLine("GUID for IDHTMLEdit Interface detected.");
      }
    }
  }
}

Basically, what's happening here is that the application is trying to find occurences of a GUID in a binary file. I've seen many approaches to searching a binary string in a file. Some were simpler to implement, the other are more efficient, but harder to understand. This one takes the cake. In a nutshell, this code reads a binary file, byte by byte, and converts each byte into its string hexadecimal represenation. Then this string is appended to a StringBuilder. Once the entire file is loaded into StringBuilder (consuming filesize * 4 bytes of memory), the StringBuilder is used to produce a string (another memory allocation of the same size edited: no, this is actually done in place. Thanks, Dunkan!) and the string is being searched for a GUID substring.

I won't even go into the efficiency of string search as used above. My point is that you either do things right, or you sidestep the whole issue by not providing the code sample (not really needed in the context of this article) and leaving it as an excercise for the reader.

Some screens later in the article we find the following gem of regular expression (JScript, searching an HTML document for tag):

var rex = new RegExp("]*>", "i");

Er, what happened to the non-greedy qualifiers? What is that \s doing inside []? This feels like something written by a person, who does not use javascript day-to-day.

Please, please let's keep MSDN technical library standards high. After all we all benefit from better written code.

10/2/2006 1:23:53 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [3]  | 
 Thursday, September 28, 2006

Several people have hit a problem while installing the service pack on Windows Server 2003 machines. The installation (that takes obscenely long time) eventually fails with a message:

Error 1718: The file xxxxxxxx.msp was rejected by digital signature policy.

Two things with repsect to that:

1) I had this problem and was eventually able to complete installation (on the second try). What has changed is that I moved the SP file from a network location to a local drive.

2) Take a look at the setup log. It can be found under %temp%\VS80sp1-KB918525-X86-Beta-ENU (this path can be pasted into Run box to open the folder)

9/28/2006 2:08:37 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Friday, September 15, 2006

It's been pointed out quite a few times how Google AdSense sometimes produces the results that are not exactly what was intended. But the Google is not the only one that suffers from this. Here is a screen scrap of a new article that tells about a not-so-bright Tennessee town mayor who unknowingly agreed to shooting a scene of a movie called Thong Girl 3 in his office. I mean I understand he might not have read the script, but what about the title???

Anyway, the good part is actually the ad inside the article.

9/15/2006 3:42:16 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Wednesday, August 09, 2006

Today I went on to research the process of formatting a storage card programmatically. I decided to do this after I tried to advise someone in the MSDN forums, without clearly understanding what's involved.

After digging around for a while I came up (thanks to XDA-Developers) with the following:

int _tmain(int argc, _TCHAR* argv[])
{

 STOREINFO fd = {0};
 fd.cbSize = sizeof(fd);
 DWORD dw = GetFileAttributes(_T("
\\StoreMgr"));
 HANDLE hFind = FindFirstFileW(L"
\\StoreMgr", (LPWIN32_FIND_DATAW)&fd);

 WCHAR szBuffer[255];
 wcscpy(szBuffer, L"
\\StoreMgr\\");
 wcscat(szBuffer, fd.szDeviceName);
 HANDLE hStore = CreateFile(szBuffer, GENERIC_WRITE|GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
 DWORD dwErr = GetLastError();

 FindClose(hFind);

 STOREINFO si = {0};
 si.cbSize = sizeof(si);
 PSLGetStoreInfo(hStore, &si);

 BOOL bfmt = PSLDismountStore(hStore);
 bfmt = PSLFormatStore(hStore);

 return 0;
}

What can I tell you... Kids, don't try this at home. What I have totally forgotten is that the typical PPC device contains several flash memory stores, some of which hold the ROM (that's why you can “flash” device, right?) and some - OS snapshot (on WM5 where RAM is persisted in the flash).

Stepping in the debugger through the above I got to the line that calls PSLFormatStore. It failed. The device froze. Essentially, I cleared the ROM on the running device - typically the first step when flashing the ROM update. This is very similar to running format c: on DOS.

Soft reset - nothing (just a blue Imate boot screen)

Hard reset - nothing. It did say something about formatting the flash memory, but that was the extent of it.

Fortunately I had a ROM update sitting around and that worked.

Here is the corrected code:

#include "stdafx.h"

#define FIRST_METHOD 0xF0010000
#define APICALL_SCALE 4
#define HANDLE_SHIFT 8
#define HT_FIND 8
#define HT_FILE 7

int _tmain(int argc, _TCHAR* argv[])
{
 /*
 Store enumeration
 STOREINFO storeinfo = {0};
 storeinfo.cbSize = sizeof(STOREINFO);
 HANDLE hFindStore = FindFirstFile(L"
\\StoreMgr", (LPWIN32_FIND_DATAW)&storeinfo);
 while( FindNextFile(hFindStore, (LPWIN32_FIND_DATAW)&storeinfo) )
 {
 }
 */

 WCHAR szBuffer[255];
 wcscpy(szBuffer, L"
\\StoreMgr\\");
 wcscat(szBuffer, L"DSK1:"); // DSK1: is hardcoded for simplicity
 HANDLE hStore = CreateFile(szBuffer, GENERIC_WRITE|GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
 DWORD dwErr = GetLastError();

 BOOL bfmt = PSLDismountStore(hStore);
 bfmt = PSLFormatStore(hStore);

 STOREINFO si = {0};
 si.cbSize = sizeof(si);
 PSLGetStoreInfo(hStore, &si);


 bfmt = PSLCreatePart(hStore, L"PART00", 0, si.snBiggestPartCreatable >> 32, si.snBiggestPartCreatable, TRUE);
 HANDLE hPart = PSLOpenPartition(hStore, L"PART00");
 bfmt = PSLFormatPart(hPart, 0, TRUE);
 bfmt = PSLMountPartition(hPart);

 PARTINFO pi = {0};
 pi.cbSize = sizeof(pi);
 HANDLE hFind = PSLFindFirstPartition(hStore, &pi);
 while( PSLFindNextPartition(hFind, &pi) )
 {
 }

 PSLFindClosePartition(hFind);

 CloseHandle(hStore);

 return 0;
}

 

8/9/2006 1:23:53 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [1]  | 
 Tuesday, April 04, 2006

On April 18, 10 AM PST I am doing an MSDN webcast on IMAPI v2 and using it to burn CDs and DVDs. As you may know, I've been to some extent successful in uitilizing IMAPI v1 (found in Windows XP) in my own applications. Now with the advent of Windows Vista it is finally possible to write CDs and DVDs even from VB Script. Join me for this webcast to see how this techology can be utilized from .NET languages.

Watch this post for the web cast code samples link.

4/4/2006 3:07:24 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Friday, February 03, 2006

As promised, here are some details on the OpenNETCF.Drawing.Imaging namespace. I'm going to demonstrate how to accomplish several tasks listed in the previous post as not supported by the CF Bitmap class.

0. Preface. helper classes

In the wrapper we introduce 2 helper classes - StreamOnFile and ImageUtils. The latter is simply a collection of high-level image proverssing methods. The former is an IStream implemented over .NET Stream (including FileStream). The implementation is not complete, but sufficient for the Imaging API methods that expect an IStream parameter.

1. Thumbnails, loading parts of the large image

Loading an image in Imaging API is achieved via calls to decoders - COM objects implementing IImageDecoder interface. The basic imaging interface IImage uses decoders to load image data. Most of the decoders support loading partial image, dicarding the unnecessary data. E.g. if you need to load a 3000x2000 image into a 300x200 PictureBox control, it is obvious that you don't need all 6MP of data taking a whopping 18 MB of RAM (24bpp). Moreover, most devices will simply throw an OutOfMemoryException fi you try something like this. Decoder can be instructed to load an image of the required size so that it will skip over those pixels that don't make it (or factor them into interpolation process to scale the image more smoothly). Here is how we achieve it.


        static public IBitmapImage CreateThumbnail(Stream stream, Size size)
        {
            IBitmapImage imageBitmap;
            ImageInfo ii;
            IImage image;

            ImagingFactory factory = new ImagingFactoryClass();
            factory.CreateImageFromStream(new StreamOnFile(stream), out image);
            image.GetImageInfo(out ii);
            factory.CreateBitmapFromImage(image, (uint)size.Width, (uint)size.Height, 
          ii.PixelFormat, InterpolationHint.InterpolationHintDefault, out imageBitmap);
            return imageBitmap;
        }

After we got IBitmapImage object, we can convert it to the .NET Bitmap:

             Bitmap bm = ImageUtils.IBitmapImageToBitmap(imageBitmap);

2. Image transformation (flip, rotate, gamma/brightness/contrast controls)

Imaging library offers a limited set of the image operations exposed via interface IBasicBitmapOps. These are also wrapped in the ImageUtils class so that you get the following methods:

public Bitmap RotateFlip(Bitmap bitmap, RotateFlipType type)
public Bitmap Rotate(Bitmap bitmap, float angle)
public Bitmap Flip(Bitmap bitmap, bool flipX, bool flipY)

Of course you are welcome to use the IBasicBitmapOps directly.

3. Image tags

TBD

4. Transparency and alpha blending

If you have a PNG image with alpha channel information and you load it into a Bitmap object, the transparency is immediately lost. Not so, if using IImage class.

ImagingFactory factory = new ImagingFactoryClass();
IImage
img;
factory.CreateImageFromFile(
"rgba8.png", out img);

Bitmap imageBackground = new Bitmap(“MyImage.bmp“);
Graphics
g = Graphics.FromImage(imageBackground);

IntPtr hDC = g.GetHdc();

RECT rc = RECT.FromXYWH(200, 200, width, h
eight);
img.Draw(hDC, rc,
null
);
g.ReleaseHdc(hDC);

The above code will transparently draw rgba8.png over the specified bitmap.

2/3/2006 5:13:30 PM (Pacific Standard Time, UTC-08:00)  #    Comments [7]  | 
 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]  |