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 [0]  | 
 Thursday, August 25, 2005

I've just done a webcast, which was effectively a repeat of my MEDC'05 presentation on hosting ActiveX controls in CF 2.0. It can be seen here

Update (09/03/2005). It is apparently not entirely clear from the webcast, where the source code is. It can be found here

Update 2 (09/03/2005). The source code will not compile on beta 2 of Visual Studio because of Marshal.AllocHGlobal/FreeHGlobal that were not introduced until July CTP. Simply replace them with AllocCoTaskMem/FreeCoTaskMem

Update 3 (09/03/2005). TO be able to host WMP in Pocket PC application you must have WMP10. WMP9 did not have an ActiveX interface. Some (but not all) of the 2003SE device have WMP10. All WM5 devices will have WMP10

8/25/2005 4:40:44 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [3]  | 
 Thursday, August 04, 2005

I live in California. It is quite common to see things here written in both English and Spanish. I suspect that there might be even a law that mandates having Spanish translation available for certain public establishments. Having said this, I admit I did a double-take when I saw a list of services offered by the Customer Service department of out local supermarket:

Notice “Check cashing” and immediately after it “Cheque cashing”. Apparently we here have a lot of respect for our neighbors across the pond and their cute if peculilar way to spell things.

8/4/2005 6:54:18 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Tuesday, August 02, 2005

Yesterday Peter Foot has released his suite of Windows Mobile APIs for .NETCF (http://www.inthehand.com/1stAugust2005.aspx). This is an evolution of the PocketOutlook library which now incorporates Email and SMS functionality. Along with this are a selection of other assemblies for Configuration, System Status and Telephony. Very cool!

8/2/2005 9:07:56 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Friday, July 01, 2005
Looking at the flash animation - "How it works" for Verizon in-home fiber-optic internet service... They offer speeds up to 30Mbps down/5 up
At some point they go to explain why is their service so fast (compared to traditional broadband providers). Apparently it is because "the light travels so quickly". Of course! Silly me. I totally forgot that an average speed of an electron in a copper wire is about 1.5 mm/s. That explains my sluggish internet connection.
 
On a related note. SBC at some point announced the plans to deploy fiber connectivity option to the consumers. The deployment project was called "Project Lightspeed". Catchy... Then they announced plans to accelerate the deployment (after FCC lifted some restrictions). Ars Technica suggests that the project should be now named "Project FTL"
Computers | Life | Rant
7/1/2005 4:20:32 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [27]  | 
 Tuesday, May 24, 2005

... or is it Superman?

The kind folks, who administer MSDN webcasts, sent me as one of the year 2005 presenters a shirt and a ... cape. Yes, a red cape.

5/24/2005 11:42:31 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [0]  | 
 Wednesday, April 20, 2005

I've been asked for assistance with the following issue today. A developer has a CAB that he wants to install on the device without using CeAppMgr, from his own setup application. In addition he wanted to force the installation to the storage card if present.

This task involves several steps:

  1. Copy CAB to the device
  2. Detect storage card presence
  3. Launch wceload.exe to install the cab

1. Copying CAB to the device.

I would use cecopy (from Windows Mobile Developer Power Toys) or RAPI (CeCreateFile, CeWriteFile). If working with managed code, I suggest OpenNETCF.Communications library, or RapiDeploy tool

2. Detecting storage card presence.

While on the device side the preferred method is to use FindFirstFlashCard/FindNextFlashCard, these functions do not have Rapi equivalent. From the desktop side use CeFindAllFiles/FAF_FOLDERS_ONLY (or CeFindFirstFile/CeFindNextFile) to search the root directory and enumerate all files for having FILE_ATTRIBUTE_DIRECTORY and FILE_ATTRIBUTE_TEMPORARY combination of attributes (0x110).

3. Launch wceload.exe to install the cab

This is the interesting part. Here is a list of command-line switches that wceload.exe supports:

  • /delete - if value = 0 do not delete cab after install
  • /noui - perform a silent operation. Do not ask if it is ok to overwrite the exiting files
  • /nouninstall - do not create a .uninstall file. The applciation entry will not appear in Remove Programs list
  • /askdest - will force wceload to display a dialog that allows user to select installaltion location and some other things
  • /noaskdest - install specified applications to specified locations (see below)

 


Dialog displayed by wceload when /askdest is specified

The /noaskdest switch is the most inetersting of them all. When you specify it, wceload ignores the rest of the command line. Instead it checks the following registry location - [HKEY_LOCAL_MACHINE\SOFTWARE\Apps\Microsoft Application Installer\Install]

The key contents are key/value pairs:
[CAB file path] = [CAB destination directory]
e.g. \Storage Card\MyApp.CAB = \Storage Card\Program Files\My App

wceload will try to install the cab specified in the value name to the location specified in the value value. Below is the sample registry content:

To reiterate: in order to install a cab to the memory card, one needs to:

  1. Copy CAB to the device (e.g. to the memory card)
  2. Create a registry value on the device specifying the cab location and cab destination
  3. Launch wceload.exe with /noaskdest parameter
4/20/2005 5:52:05 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [5]  |