World Clock

Today, I spent some minutes to enhance my desktop. I installed AveDesk 1.2 and the World Clock v0.2.0 docklet.  Just rename the zipped file to .aveinst and double click it. Also ensure, that SysStats is installed before that. After spending some more time with setting up the clocks, I got only one black clock without any map in it. It took a while to find out, where the problem was. The problem is already known and depends on the regional settings of your windows. Using the German settings the system expects some floats using a format like this xxx.xxx.xxx,xx while the US layout the docklet was written for, uses xxx,xxx,xxx.xx. Unlike in the article before the World.vba there does not work, because the french settings use numbers with a format like xxx xxx xxx,xx.

This additional function and the two changes in MoveMap() should be enough for the German settings:

Function stringToFloat(strMeters)

  if isNull(strMeters) then
    stringToFloat = 0
    exit function
   end if

  'US xxx,xxx.xx to DE xxx.xxx,xx
  strMeters = replace(strMeters, ".", "foobar")
  strMeters = replace(strMeters, ",", ".")
  strMeters = replace(strMeters, "foobar", ",")

  stringToFloat = strMeters

End Function
Function MoveMap()

 centerX = SysStatsModel.Width/2
 centerY = SysStatsModel.Height/2

 latitude = stringToFloat(SysStatsModel.Meters("Latitude"))
 longitude = stringToFloat(SysStatsModel.Meters("Longitude"))

 ' ...

End Function

You can download the modified World.vbs (as it is, without any warranty, usage on your own risk, just rename the file from World.txt to World.vbsl). Now copy the file to the folder DockletsSysStatsscriptsworld within your AveDesk directory. If you try to add more then one instances of the world clock on your desktop, copy the World.ini file from the DockletsSysStatsconfigs directory for each clock and rename it. Assign to each clock its own .ini file. Otherwise all the docklets will be the same after the next start of AveDesk.

After spending this afternoon to find out how it works, it looks pretty nice:

WorldClock

International System of Units (SI)

I found a quite useful overview of international system unit prefixes:

Factor Name Symbol Origin Derivation
2^10 kibi Ki kilobinary: (2^10)^1 kilo: (10^3)^1
2^20 mebi Mi megabinary: (2^10)^2 mega: (10^3)^2
2^30 gibi Gi gigabinary: (2^10)^3 giga: (10^3)^3
2^40 tebi Ti terabinary: (2^10)^4 tera: (10^3)^4
2^50 pebi Pi petabinary: (2^10)^5 peta: (10^3)^5
2^60 exbi Ei exabinary: (2^10)^6 exa: (10^3)^6

Examples and comparisons with system units prefixes

one kibibit 1 Kibit = 2^10 bit = 1024 bit
one kilobit 1 kbit = 10^3 bit = 1000 bit
one mebibyte 1 MiB = 2^20 B = 1 048 576 B
one megabyte 1 MB = 10^6 B = 1 000 000 B
one gibibyte 1 GiB = 2^30 B = 1 073 741 824 B
one gigabyte 1 GB = 10^9 B = 1 000 000 000 B

P/Invoke Data Type Matching

Creating this table took a while, but saved me hours…

Windows Data Type .NET Data Type
BOOL, BOOLEAN Boolean or Int32
BSTR String
BYTE Byte
CHAR Char
DOUBLE Double
DWORD Int32 or UInt32
FLOAT Single
HANDLE IntPtr, UintPtr or HandleRef
HRESULT Int32 or UInt32
INT Int32
LANGID Int16 or UInt16
LCID Int32 or UInt32
LONG Int32
LPARAM IntPtr, UintPtr or Object
LPCSTR String
LPCTSTR String
LPCWSTR String
LPSTR String or StringBuilder*
LPTSTR String or StringBuilder
LPWSTR String or StringBuilder
LPVOID IntPtr, UintPtr or Object
LRESULT IntPtr
SAFEARRAY .NET array type
SHORT Int16
TCHAR Char
UCHAR SByte
UINT Int32 or UInt32
ULONG Int32 or UInt32
VARIANT Object
VARIANT_BOOL Boolean
WCHAR Char
WORD Int16 or UInt16
WPARAM IntPtr, UintPtr or Object

Standard Numeric Format Strings

It usually takes ages until I find the page with the format strings on MSDN. So here’s a short overview:

C or c = Currency
D or d = Decimal
E or e = Scientific (exponential)
F or f = Fixed-point
G or g = General
N or n = Number
P or p =  Percent
R or r = Round-trip
X or x = Hexadecimal

Drinks in AD

Today, I got the hint to take a deeper look into the AD. Surprisingly I found the following entry in the Active Directory Schema:

drink Attribute

Sounds like a joke, doesn’t it? If you have a look into RFC 1274 search for section 9.3.5. There you will find

9.3.5. Favourite Drink

The Favourite Drink attribute type specifies the favourite drink of
an object (or person).

favouriteDrink ATTRIBUTE
WITH ATTRIBUTE-SYNTAX
caseIgnoreStringSyntax
(SIZE (1 .. ub-favourite-drink))
::= {pilotAttributeType 5}

Seems like it is no joke at all…

NUnit .NET 2.0 compliant

The current iteration release of NUnit 2.2.2 should work fine with assemblies build in Visual Studio .NET 2005. Nevertheless some problems occur after installing Visual Studio .NET 2005 Beta 2. Check out the installation folder of NUnit, usually C:\Program Files\NUnit 2.2.2\bin. Here you should modify the file nunit-gui.exe.config.

<startup>
  <supportedRuntime version="v2.0.50215" />
  <supportedRuntime version="v1.1.4322" />
  <supportedRuntime version="v1.0.3705" />
  <requiredRuntime version="v1.0.3705" />
</startup>

The version of .NET 2.0 on your system is slightly different due to the installation of Visual Studio .NET Beta 2.

WaitOne and PInvoke

Unfortunately, there is no way to copy a .NET desktop application to a compact framework based system. The compact framework does only support a subset of the .NET library.

Two typical problems while migrating a multi-threaded application to the Compact Framework can be solved as following:

The first issue appears when using the Sleep-method of threads.

System.TimeSpan yield = new System.TimeSpan(10);
System.Threading.Thread.Sleep(yield);

The compact framework does not support any signature accepting TimeSpan as parameter. Simply convert the value to an integer using the milliseconds solves this one.

System.TimeSpan ts = new System.TimeSpan(10);
int yield = ts.Milliseconds;
System.Threading.Thread.Sleep(yield);

More complex is the usage of the WaitOne-method. WaitOne only provides a signature without any parameters on the compact Ffamework. A simple solution is the usage of the following PInvoke

[System.Runtime.InteropServices.DllImport("CoreDll.dll")]
private extern static Int32 WaitForSingleObject(System.IntPtr Handle,System.Int32 Wait);

Using this you can substitute calls similar to this

foo.WaitOne((int)((bar - System.DateTime.Now.Ticks), false);

by the flowing lines to achieve the same result:

System.IntPtr hProcess = foo.Handle;
System.Int32 wait = (int)((bar - System.DateTime.Now.Ticks));
WaitForSingleObject(hProcess, wait);