How To: Get Correct Messages for Exceptions Fired from .Net Compact Framework Classes

Sometimes message from exception that occurred inside .Net CF classes doesn’t look handly at all:

An error message cannot be displayed because an optional resource assembly containing it cannot be found.

The thing is that error messages are actually in the resource assembly which is not installed on the device. So to fix the problem you just need to install this assembly:

  1. Navigate to CompactFramework folder in the SDK which is “%Program Files%\Microsoft.NET\SDK\CompactFramework”.
  2. Navigate to Diagnostics folder under desired .Net CF version (“v3.5\WindowsCE\Diagnostics” for 3.5 or “v2.0\WindowsCE\Diagnostics” for 2.0).
  3. Copy resource CAB file to device according to the language you want and install it.

How To: Rotate Screen on Windows Mobile Device

Below is a snapshot that shows how to change screen orientation from normal to 90 degree rotated or from 90 degree back to normal depending on current screen state:

if (SystemSettings.ScreenOrientation == ScreenOrientation.Angle0) {
	SystemSettings.ScreenOrientation = ScreenOrientation.Angle90;
}
else {
	SystemSettings.ScreenOrientation = ScreenOrientation.Angle0;
}

To make this code compile don’t forget to import Microsoft.WindowsCE.Forms namespace.

How To: Refresh Home Screen on Windows Mobile 6.5

New home screen available in Windows Mobile 6.5 should be refreshed differently for Professional and Standard editions of OS. It can be done using the same API methods as for earlier versions of Windows Mobile.

For Professional edition it can be done like this:

[DllImport("coredll.dll")]
private static extern IntPtr PostMessage(int hWnd, int msg, uint wParam, uint lParam);

private const int HWND_BROADCAST = 0xFFFF;
private const int WM_WININICHANGE = 0x001A;

public static void Refresh() {
	PostMessage(HWND_BROADCAST, WM_WININICHANGE, 0xF2, 0);
}

For Standard edition the trick is to provide correct GUID to API. This GUID can be found in SlidingPanel.home.xml file located in “\Application Data\Home” folder. Here is the code:

[DllImport("aygshell.dll")]
public extern static uint SHOnPluginDataChange(ref Guid clsid);

public static void Refresh() {
	Guid guid = new Guid("{E9267CAB-02EE-4f37-8216-6BF6A8FF5A71}");
	SHOnPluginDataChange(ref guid);
}

How To: Load Image from Embedded Resource

Here is a code for loading embedded image named MyImage.png stored directly in project directory:

Assembly assembly = Assembly.GetExecutingAssembly();
string iconFileName = string.Format("{0}.MyImage.png", assembly.GetName().Name);
using (Stream io = assembly.GetManifestResourceStream(iconFileName)) {
	return new Bitmap(io);
}

Name of resource to load contains three elements divided by dots:

  • Assembly name;
  • Path where resource is stored inside of a project (must be omitted if resource is in project directory);
  • File name of resource.

Path to resource should be created using names of folders. For example, if resource is in “Resources/Images” folder than path will be “Resources.Images”.

How To: Make File Writable on Windows Mobile

Making read-only file writable with full .Net Framework is a piece of cake: there is FileInfo.IsReadOnly. Just set it to false and it’s done.

With Compact Framework we need to use FileInfo.Attributes and a solid knowledge of bitwise operations:

FileInfo info = new FileInfo(pathToReadonlyFile);
info.Attributes &= ~FileAttributes.ReadOnly;

How To: Check That Application is Running Under Windows Mobile 6.5

As I wrote in Linkpool: Windows Mobile 6.1 and 6.2 Build Numbers they have the same version (which by the way is 5.2). So checking if application is running under WM 6.5 must be based on build number. Like this:

Version osVersion = Environment.OSVersion.Version;
if (osVersion.Major == 5 && osVersion.Minor == 2 && osVersion.Build >= 21139) {
    // It's Windows Mobile 6.5
}

Windows Mobile 6.1 and 6.2 Build Numbers

Both Windows Mobile 6.1 and 6.5 have the same version (5.2), but they differ in build numbers. Today I found some useful resources that describe their build numbers:

How To: Determine What Version of .Net CF is Installed on Device

List of .Net Compact Framework versions installed on device are available in registry under:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETCompactFramework

This key contains a set of DWORD values with names in format X.Y.BBBB.mm that correspond to build numbers of .Net CF (build numbers can be found in Wikipedia). Each of these values can have data of 0 (installed in RAM) or 1 (installed in ROM). So to verify if specific version of .Net CF is installed we need to check if value with corresponding name exists.

Here is a method that checks for .Net CF 2  (I wrote it today to verify available .Net CF versions in setup.dll of cab installer):

BOOL IsNetCF2Installed() {
	BOOL bNetCF2Found = FALSE;

	HKEY hKey = NULL;
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\Microsoft\\.NETCompactFramework"), 0, KEY_READ | KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
		DWORD dwIndex = 0;
		TCHAR szName[255];
		DWORD cName = 255;
		while ((!bNetCF2Found) && (::RegEnumValue(hKey, dwIndex, szName, &cName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)) {
			if (szName[0] == '2') {
				bNetCF2Found = TRUE;
			}
			dwIndex++;
		}

		RegCloseKey(hKey);
	}

	return bNetCF2Found;
}

Updating Application That Has Custom Actions in Setup.dll

Recently I was creating installer for application that is suppose to be updated regulary. Also cab installer for this application used setup.dll to perform custom actions during install/uninstall.

Since cab installer doesn’t have “update” feature installing update is done as a sequence: uninstall_old > install_new. Calls to setup.dll will be as follows:

  1. Install_Init() from new setup.dll is called with fFirstCall=true and fPreviouslyInstalled=true.
  2. Old installation is removed. This leads to calls to  Uninstall_Init() and Uninstall_Exist() from old setup.dll (which is stored in \windows\AppMgr).
  3. Install_Init() from new setup dll is called with fFirstCall=false and fPreviouslyInstalled=false.
  4. New files are extracted by cab installer.
  5. Install_Exist() is called.

To control updating process I needed to use some kind of a marker that will be available at all of those steps.

Originally I tried to use named Mutex (mainly because it is an in-memory object), but for some reason it didn’t work as expected. So I considered key in registry and a file. Creating/removing file just to use it as a marker is not very good idea and my final solution was to use a key in registry:

#include "windows.h"
#include <string.h>

///////////////////////////////////////////////////////////
// Methods to work with marker.

void SetInstallingStartFlag() {
	HKEY hKey = NULL;
	DWORD dwDisposition;
	if (::RegCreateKeyEx(HKEY_CURRENT_USER, _T("\\SOFTWARE\\MySoftwareInstall"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisposition) == ERROR_SUCCESS) {
		::RegCloseKey(hKey);
	}
}
void RemoveInstallingStartFlag() {
	HKEY hKey = NULL;
	if (::RegOpenKeyEx(HKEY_CURRENT_USER, _T(""), 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
		::RegDeleteKey(hKey, _T("\\SOFTWARE\\MySoftwareInstall"));
		::RegCloseKey(hKey);
	}
}
BOOL OpenFlagKey() {
	BOOL bOpened = FALSE;

	HKEY hKey = NULL;
	if (::RegOpenKeyEx(HKEY_CURRENT_USER, _T("\\SOFTWARE\\MySoftwareInstall"), 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
		bOpened = TRUE;
		::RegCloseKey(hKey);
	}

	return bOpened;
}
BOOL IsRemovingDuringUpdate() {
	return OpenFlagKey();
}
BOOL IsInstallingDuringUpdate() {
	return (OpenFlagKey() == FALSE);
}

I’m Tweeting

Follow

Get every new post delivered to your Inbox.