Tracking High Scores on Windows Phone

Another frequent question I come across in the user forums is related to how some one implements local high scores. The question has come up frequently enough for me to conclude that its to the benifit of the community to have an implementation available that can be used in Silverlight or XNA that is ready to be used with very little setup.

So I’ve made a solution for others to use. By default the component will keep track of up to 10 high scores and will take care of loading and saving itself. If you add a score the component will take care of ensuring the score is in it’s proper place and removing scores that are nolonger one of the top. For persisting the score information I’ve made use of the DataSaver<T> code from a previous blog post. I hope others will find the solution easy to use.

To get started with using the component add a reference to my component to your project. You’ll want to instantiate HighScoreList passing an optional file name that it will use to save score information. It’s possible to keep track of more than one high score list as long as your instances have different file names. One might want to do this if they keep track of scores in different modes separately from each other (Ex: a score list for Difficult mode, a score list for Easy mode, and so on).

HighScoreList _highScoreList = new HighScoreList("MyScores");

Upon instantiation the component will take care of loading any previous high scores without you doing anything more.

To add a score create a new instance of ScoreInfo and populate its PlayerName and Score fields. (There is also a ScoreDate field that automatically gets populated with the current date and time). Then use the AddScore(ScoreInfo) method on the HighScoreList instance to add it to the score list.

ScoreInfo scoreInfo = new ScoreInfo(){PlayerName = "Jack", Score = 1048576};
_highScoreList.AddScore(scoreInfo);

And that’s it, there’s nothing more for you to do. When you make that call the score gets added to the high score list, scores that are no longer in the top 10 (or what ever you set the limit to be) will fall off the list, and the list will automatically be persisted back to IsolatedStorage so that it is available the next time your game runs. Easy, right?

As a test project I’ve created a Silverlight application that allows you to enter new scores and see the behaviour of the component.

Score Keeper Screenshot

The main bits of the source code are below. First the ScoreInfo class which is nothing more than a serializable collection of three properties

/// <summary>
/// ScoreInfo contains information on a single score
/// </summary>
[DataContract]
public class ScoreInfo : INotifyPropertyChanged
{

    // PlayerName - generated from ObservableField snippet - Joel Ivory Johnson
        private string _playerName = String.Empty;

    /// <summary>
    /// The name of the player that made this score
    /// </summary>
        [DataMember]
        public string PlayerName
        {
        get { return _playerName; }
            set
            {
                if (_playerName != value)
                {
                    _playerName = value;
                    OnPropertyChanged("PlayerName");
                }
            }
        }
        //-----

    // Score - generated from ObservableField snippet - Joel Ivory Johnson
        private int _score = 0;

    /// <summary>
    /// The score that the player made
    /// </summary>
        [DataMember]
        public int Score
        {
        get { return _score; }
            set
            {
                if (_score != value)
                {
                    _score = value;
                    OnPropertyChanged("Score");
                }
            }
        }
        //-----

    // ScoreDate - generated from ObservableField snippet - Joel Ivory Johnson
        private DateTime _scoreDate = DateTime.Now;

    /// <summary>
    /// The date and time that the player made the score. If this field is not
    /// assigned a value it will automatically be assigned with the date and time
    /// that the score isntance was created
    /// </summary>
        [DataMember]
        public DateTime ScoreDate
        {
        get { return _scoreDate; }
            set
            {
                if (_scoreDate != value)
                {
                    _scoreDate = value;
                    OnPropertyChanged("ScoreDate");
                }
            }
        }
        //-----
    protected void OnPropertyChanged(String propertyName)
    {
        if(PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #region INotifyPropertyChanged Members

    public  event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

And then the HighScoreList class, which is a collection class that contains the .

using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.Serialization; namespace J2i.Net.ScoreKeeper { public class HighScoreList : ObservableCollection<ScoreInfo>, INotifyPropertyChanged { static DataSaver<HighScoreList> MyDataSaver = new DataSaver<HighScoreList>(); public HighScoreList() { } public HighScoreList(string fileName):this() { this.ScoreFileName = fileName; HighScoreList temp = MyDataSaver.LoadMyData(fileName); if(temp!=null) { foreach(var item in temp) { Add(item); } } } // MaxScoreCount - generated from ObservableField snippet - Joel Ivory Johnson private int _maxScoreCount = 10; [DataMember] public int MaxScoreCount { get { return _maxScoreCount; } set { if (_maxScoreCount != value) { _maxScoreCount = value; OnPropertyChanged("MaxScoreCount"); } } } //----- // ScoreFileName - generated from ObservableField snippet - Joel Ivory Johnson private string _scoreFileName = "DefaultScores"; [DataMember] public string ScoreFileName { get { return _scoreFileName; } set { if (_scoreFileName != value) { _scoreFileName = value; OnPropertyChanged("ScoreFileName"); } } } //----- // AutoSave - generated from ObservableField snippet - Joel Ivory Johnson private bool _autoSave = true; [DataMember] public bool AutoSave { get { return _autoSave; } set { if (_autoSave != value) { _autoSave = value; OnPropertyChanged("AutoSave"); } } } //----- static int ScoreComparer(ScoreInfo a, ScoreInfo b) { return b.Score - a.Score; } public void SortAndDrop() { List<ScoreInfo> temp = new List<ScoreInfo>(this.Count); foreach(var item in this) { temp.Add(item); } if (temp.Count > MaxScoreCount) { temp.RemoveRange(MaxScoreCount - 1, (temp.Count) - (MaxScoreCount)); } temp.Sort(ScoreComparer); this.Clear(); temp.ForEach((o)=>Add(o)); } public void Save() { if(String.IsNullOrEmpty(ScoreFileName)) throw new ArgumentException("A file name wasn't provided"); MyDataSaver.SaveMyData(this, ScoreFileName); } public void AddScore(ScoreInfo score) { this.Add(score); SortAndDrop(); if(AutoSave) Save(); } protected void OnPropertyChanged(String propertyName) { if(PropertyChanged!=null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion } } 

Calculating Distance from GPS Coordinates

I’ve been carrying this equation around forever and a day and thought I would share it. With this equation you can calculate the distance between GPS coordinates. I tend to use SI units, but you should be able to easily adjust it for units of your choosing.

using System; 
using System.Device.Location; 
 
namespace J2i.Net.GPS 
{ 
    public static class DistanceCalculator 
    { 
 
        public const double EarthRadiusInMiles = 3956.0; 
        public const double EarthRadiusInKilometers = 6367.0; 
        public const double EarthRadiusInMeters = EarthRadiusInKilometers*1000; 
 
        public static double ToRadian(double val) { return val * (Math.PI / 180); } 
        public static double ToDegree(double val) { return val * 180 / Math.PI; } 
        public static double DiffRadian(double val1, double val2) { return ToRadian(val2) - ToRadian(val1); } 
 
 
 
        public static double CalcDistance(GeoCoordinate p1, GeoCoordinate p2) 
        { 
            return CalcDistance(p1.Latitude, p1.Longitude, p2.Latitude, p2.Longitude, EarthRadiusInKilometers); 
        } 
 
        public static Double Bearing(GeoCoordinate p1, GeoCoordinate p2) 
        { 
            return Bearing(p1.Latitude, p1.Longitude, p2.Latitude, p2.Longitude); 
        } 
 
        public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, double radius) 
        { 
 
            return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) 
                + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0))))); 
        } 
 
        public static Double Bearing(double lat1, double lng1, double lat2, double lng2) 
        { 
 
            { 
                var dLat = lat2 - lat1; 
                var dLon = lng2 - lng1; 
                var dPhi = Math.Log(Math.Tan(lat2 / 2 + Math.PI / 4) / Math.Tan(lat1 / 2 + Math.PI / 4)); 
                var q = (Math.Abs(dLat) > 0) ? dLat / dPhi : Math.Cos(lat1); 
 
                if (Math.Abs(dLon) > Math.PI) 
                { 
                    dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon); 
                } 
                //var d = Math.Sqrt(dLat * dLat + q * q * dLon * dLon) * R; 
                var brng = ToDegree(Math.Atan2(dLon, dPhi)); 
                return brng; 
            } 
        } 
 
    } 
} 
 

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Bluesky: @j2i.net

File x.inf contains DirIDs, which are not supported

I was compiling a cab for a Windows Mobile install when I came across an unexpected error.

Windows CE CAB Wizard    ?Error: Line  92 - unsupported DirID 0  Error: File c:\users\joeljo~1\appdata\local\temp\wizb912.inf contains DirIDs, which are not supported

It took a little while to figure out what this means. Searching on the web I found this can happen if you package more than 1000 files in a cab (or more than 262 in some cases). That wasn’t the cause of the problem that I was encountering though. It took a little while and I finally figured out what was causing it. One of the registry keys had a typographical error in it. I had typed “%InstallDir” instead of “%InstallDir%”. So if you ever encounter this problem remember to double check your registry key names.


From the Blog Engine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Handling Cookies with Redirects and HttpWebRequest

The HttpWebRequest handles redirects automatically. That’s usually a nice feature but it can actually get in the way when the web server is setting cookies in the same response in which it is sending a redirect. For some odd reason the HttpWebRequest object will totally discard cookies that are set with a redirect response. I ran into a problem with this when I was working in some code that interfaced to Google Voice and it was driving me crazy since I didn’t know where the problem was coming from.

Once I figured out what was happening the solution to the problem was simple. The first step is to disable the class’s automatic response to redirect request. When a response is returned from the class it’s necessary to check the response to see if it includes a redirect and if so create a new request. Since a redirect could occur more than once it is necessary to do this in a loop. With each new WebRequest that is created it is necessary to set the CookiesContainer member.

A simplified version of my code looks like the following:

using System.Net;

HttpWebRequest GetNewRequest(string targetUrl) { 
	HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(targetUrl); 
	request.CookieContainer = SessionCookieContainer; 
	request.AllowAutoRedirect = false; 
	return request; 
}
HttpWebRequest request = GetNewRequest(targetUrl); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 
while (response.StatusCode == HttpStatusCode.Found)
{
	response.Close(); 
	request = GetNewRequest(response.Headers["Location"]); 
	response = (HttpWebResponse)request.GetResponse();
}
//--At this point the redirected response is ready to be used  

Trying to perform this same thing on the .Net Compact Framework is a little more challenging since it doesn’t support cookies at all. I’ll discuss the solution I used in another post within the next few days.


From the Blog Engine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Making Designer Friendly Controls

I discovered this blog entry by way ofย SmartMobiDevice.comย and thought that it was something I should share. If you’ve ever made your own controls for Windows Mobile you know that if the control is making any P/Invoke calls to native methods then it will not render in the designer. Simon Hart has aย simple solutionย for this. The identity of the application domain is different on the desktop and in Windows Mobile devices and using that identity he conditionally makes the P/Invoke call which results in a design friendly control. To see his example source codeย read on.


From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Popular Misconception: The override that was not

I was interviewing for jobs this past week and at all of the interviews I was presented with questions about object oriented techniques and C#. When asked abouit overriding a method on a class that wasn’t marked as virtual I informed the interviewers that it can’t be done. I didn’t realize that the interviewers were considering my answer to be wrong until yesterday when an interviewer presented to me the answer that he was looking for. He told me that I could override an otherwise unoverridable method by using the new keyword on it. That started a discussion which resulted in the interviewer wondering whether or not the method was really effectively overridden. After I had the discussion with him I realized the earlier interviewers may have also thought that my answer was incorrect.

The misconception comes from an observation of what happens when one uses the new keyword on a method. To make this discussion a little more concrete I’ll share some example code.

class MyBase
{
    public virtual string MethodA() 
    { 
        return "Hello from MethodA"; 
    }
    public string MethodB() 
    { 
        return "Hello from MethodB"; 
    }
}

What this class does is obvious.It has two methods both of which return a unique string. The class is simple enough that I’m sure you’ll trust me when I say it reliably does its job without error. One of the methods is marked as virtual, so it is overridable. The other is not. So now I derrive a class from this base class.

class Derived : MyBase 
{ 
	public override string MethodA() 
	{ 
		return "Hello from Derrived::MethodA"; 
	} 
	public new string MethodB() { 
		return "Hello from Derived::MethodB"; 	
	} 
}

In this derrived class I have overriddenย MethodA. Sinceย MethodBย is not marked as virtual I could not override it so I used theย newย keyword. Let’s test the program to see what type of output it produces.

using System;

static void Main(string[] args) 
{ 
	var a = new Derived(); 
	var b = new MyBase(); 
	Console.Out.WriteLine(b.MethodA()); 
	Console.Out.WriteLine(b.MethodB()); 
	Console.Out.WriteLine(a.MethodA()); 
	Console.Out.WriteLine(a.MethodB()); 
}

The output from running this is what one would expect. When I callย MethodAย andย MethodBย the the strings derrived in the derrived class are displayed.

Hello from MethodA  
Hello from MethodB
Hello from Derrived::MethodA
Hello from Derived::MethodB

Upon seeing this behaviour it seems that the developers I spoke to last week thought this to be the functional equivalent of overriding. But the difference shows up when the instance of the class is handled through either an interface or a base class reference. Let’s say I appended the following to the above code.

var c = a as MyBase;    
Console.Out.WriteLine(c.MethodA());  
Console.Out.WriteLine(c.MethodB());  

The output is not consistent with what we would expect an overridden method would produce.

Hello from Derrived::MethodA  
Hello from MethodB

The above output demonstrates that when a method has been overridden then the method will be called regardless of the interface used to interact with the object instance. MethodA had been overridden so even though a variable whose type is of the base class is used the overridden implementation is invoked. MethodB was never truly overriden so when a base class reference is used the base class implementation is called.

So then what did the new keyword really do? It allowed some one to create a method that has the same signature and name as an existing method. While the method is called with the same notation that would have been used to call the original method it is not actually performing an override. It is only hiding it. For confirmation one can also look at the C# documentation for the new keyword on MSDN which refers to this as name hidinghttp://msdn.microsoft.com/en-us/library/51y09td4(VS.71).aspx#vclrfnew_newmodifier

Name hiding through inheritance takes one of the following forms:

  • A constant, field, property, or type introduced in a class or struct hides all base class members with the same name.
  • A method introduced in a class or struct hides properties, fields, and types, with the same name, in the base class. It also hides all base class methods with the same signature. For more information, seeย 3.6 Signatures and overloading.
  • An indexer introduced in a class or struct hides all base class indexers with the same signature.

Digging deeper into the documentation we find the following:

The scope of an entity typically encompasses more program text than the declaration space of the entity. In particular, the scope of an entity may include declarations that introduce new declaration spaces containing entities of the same name. Such declarations cause the original entity to become hidden. Conversely, an entity is said to be visible when it is not hidden.The conclusion: The new keyword is not performing an override, it is a scoping operator.


From the BlogEngine Archive

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Landing Page Removed

I’ve had a landing page on my site for some time now that would allow you to get to either the old site content or the newer site content. I’m going to remove that page now and just post the link to the old site content here. Eventually the old site content will go away. Some of the more popular content with in it has already been replaced with redirects to repost of the same content within the newer site. You can get to the old site content here:ย http://j2i.net/blogs/blogs/home/default.aspx.


From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Caxixi now available for the Omnia II

Samsung has a percussion application for their phones known as Caxixi (pronounced Ca-shee-shee) . The application makes use of the touch screen and the accelerometer to allow you to control up to 5 percussion instruments at once. If you’ve never seen it before take a look at thisย YouTubeย video.

Samsung has recently updated the application so it is now available for the Omnia II. You can download it from samsungcaxixi.com.


From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

WCF Guidance for Windows Mobile on Codeplex

I was looking for a Windows Mobile specific WCF reference since I knew that the Compact Framework doesn’t support all of the same WCF profiles that the desktop framework does. I was searching for a webcast that was done on the topic when I stumbled upon a document that had the information I was seeking on the CodePlex. You can see the document for yourself here. The document was created by Michele Bustamante aka That Indigo Girl.

The document contains a lot of good information and is written for the developer that has no experience with the compact framework or WCF. Only experience with the .Net framework is required. It will walk one through the steps of setting up their development environment for mobile development.

By the way, I did find a listing of the limitations for which I was looking.

FeatureSupport
StreamingNot Supported. Contract can use Stream parameters but they will not be buffered or streamed.
SessionsTransport sessions not supported. Service contract can use SessionMode.Allowed. Service should use InstanceContextMode.PerCall behaviour.
DuplexServices designed for duplex communications with callback contracts cannot be called by mobile clients. Duplex requires a transport session.
TransactionsService contract cannot require transaction for any service operations.
Data Contracts and Serializable TypesCan freely use. The mobile client will use XmlSerializer types that are wire compatible
Message ContractsCan freely use. If the message contract includes custom headers proxy generation will not work. Mobile client requires additional custom code to work with headers.
Fault ContractsCan include in the service contract. Proxy generation will not include fault contracts so additional custom code for mobile client is required to work with faults.

There are quite a few more limitations to know about for WCF in the Compact Framework. I won’t name all of them here since I think the document does an excellent job of detailing the limitations. At a length of only 72 pages including diagrams its a document that a developer should be able to easily get through.


From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Provoking Post

For any mobile operating system that you can name you can find some one that will point out what they feel to be a flaw in the operating System.ย  Mikeย  Calligaro of the Windows Embedded team knows this all to well.ย  If you read through the reader responses to many of his post you’ll find a plethora of emotionally charged responsesย  For some of the responses I get the feeling the reader didn’t actually read the entire post.ย  For others some readers make pretty good arguments. In either case I decided to collect what I thought to be the most interesting post addressing features of Windows Mobile that many have questioned.

Interesting Post

Quotes from Mike:

In response to “why can’t you fix the damn bugs

As much as I love the job, there are certainly frustrations as well. One of them is that more than half of the code on one of our phones is written by other people (OEMs, ISVs contracted by OEMs, ISVs contracted by Mobile Operators, etc) and any failing in any of that code is usually blamed on us.


From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Why Can’t I Create a Smart Device Project?

So you’ve installed Visual Studio and you are ready to create a Smart Device project for your Windows Mobile device.  But when you go to create a new project the Smart Device templates are no where to be found.  Why is that?  Let’s look at the possible causes for this problem. 

Is your Visual Studio Version an Express Edition?

Presently Microsoft doesn’t have a version of Visual Studio Express that can be used to create smart device projects. If you are running an Express Edition of Visual Studio you will need to replace it with a professional version to develop for Windows Mobile. 


From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

24 Hours of Windows Mobile : two thirds complete

As of today the “24 Hours of Windows Mobile” presentations are 2/3 complete.  I’ve been keeping track of the presentations.  If you would like to see any of them the links are below. 

TitleLevelDescription
Introduction to Windows Mobile Development200High level introduction to developing managed applications for mobile devices
Interoperability Between Native and Managed Code300Basic’s of P/Invoke and using COM objects
Creating Adaptive Applications300Creating applications that work with a wide variety of formfactors
Using Pocket Outlook Data within a Managed Application300Retrieving and manipulating data stored within Pocket Outlook
Live Update from PDC300Sharing news announced at PDC time and other demonstrations
Developing Battery Friendly Applications for Windows Mobile300Demonstration of the State and Notifications broker and other features of Windows Mobile that can assist in developing applications that consume less power
Unit Testing for Mobile Devices300Explore using the unit testing functionality of Windows Mobile for devices
Testing Your Mobile Applications300Strategies for testing your Mobile Application
IPC and True Push300Asynchronous programming techniques inside your managed application
Windows Mobile Networking300Negotiating and establishing a network connection
Location Awareness300Location services available for Windows Mobile
Windows Mobile Power Toys200Tools to assist you in diagnosing information about your running applications.
App Dev with VS 2008 and SDK tools300what is new in the Microsoft .NET Compact Framework 3.5 and how to develop Windows Mobile client applications effectively.
Device Security300Explains how the Windows Mobile Security model works, and they show you how you can mimic device security using the emulator.
Tips for App Devs300Tips to improve your productivity in developing for Windows Mobile in Visual Studio.
Asynchronous Development Techniques300Learn how to properly start and terminate threads, update user interface controls inside multiple threads, and call Web services asynchronously.

From the BlogEngine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Sneak Peak of Windows Mobile 6.5

Michael Herald of Mobility Today has posted a video of Windows Mobile 6.5 with IE6 running on his HTC Diamond.ย  It runs incredibly well.ย  See theย Mobility Todayย site for more details.


From the Blog Engine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon: @j2inet@masto.ai
Instagram: @j2inet
Facebook: @j2inet
YouTube: @j2inet
Telegram: j2inet
Twitter: @j2inet

Custom Actions in a Cab Installation

Creating a cab installer for a Windows Mobile program is easy.  After making a cab project you add your files and Visual Studio does everything else for you.  But when you uninstall your application what happens to all of the registry entries and data files that your application has made?  They get left behind unless you erase them yourself. Chances are your program won’t ever receive any notification that it is about to be uninstalled, so your program won’t be able to cleanup after itself.  Instead you will need to create a DLL that contains custom actions.   The installer build into the Windows Mobile device will call your DLL once before installation, once after installation, once before deinstallation, and right after deinstallation.  So how to you make this custom DLL?

The custom DLL will be a Win32 DLL smart device project. The DLL must define four functions,ย Install_Init,ย Install_Exit,ย Uninstall_Init, andย Uninstall_Exit. To cleanup after your program has been uninstalled you will place cleanup code inย Uninstall_Exitย Here is the code for the custom actions DLL that I wrote. I’ve added code to clean up the registry.ย  If there are other programs that I’ve written still on the device they will be using the same registry hive to store their keys.ย  If other programs are found the program will leave behing the \HKCU\Software\J2i.Net key as not to accidentally erase the keys that my other programs use.ย  If there are no other programs found then J2i.Net will be removed.

BOOL APIENTRY DllMain(HANDLE hModule,
	DWORD ul_reason_for_call,
	LPVOID lpReserved
	)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

void RemoveRegistryKeys();
///////////////////////////////////////////////////////////////
codeINSTALL_INIT Install_Init
(
	HWND hWndParent,
	BOOL fFirstCall,
	BOOL fPreviouslyInstalled,
	LPCSTR pszInstallDir
)
{
	MessageBeep(0);
	return codeINSTALL_INIT_CONTINUE;
}

///////////////////////////////////////////////////////////////
codeINSTALL_EXIT Install_Exit
(
	HWND hWndParent,
	LPCSTR pszInstallDir,
	WORD cFailedDirs,
	WORD cFailedRegKeys,
	WORD cFailedRegVals,
	WORD cFailedShortcuts
)
{
	MessageBeep(0);
	return codeINSTALL_EXIT_DONE;
}

///////////////////////////////////////////////////////////////
codeUNINSTALL_INIT Uninstall_Init
(
	HWND hwndParent,
	LPCSTR pszInstallDir
)
{
	MessageBeep(0);
	return codeUNINSTALL_INIT_CONTINUE;
}

///////////////////////////////////////////////////////////////
codeUNINSTALL_EXIT Uninstall_Exit
(
HWND hwndParent
)
{
RemoveRegistryKeys();
MessageBeep(0);
return codeUNINSTALL_EXIT_DONE;
}
void RemoveRegistryKeys()
{
	HKEY hCompanyKey = NULL;
	HKEY hSoftwareKey = NULL;
	TCHAR buffer[MAX_PATH];
	DWORD bufferSize = MAX_PATH;
	bool canEraseCompanyKey = false;
	//Delete the application key
	RegOpenKeyEx(HKEY_CURRENT_USER, L"Software", 0, 0, &hSoftwareKey);
	if (hSoftwareKey)
	{
		RegOpenKeyEx(hSoftwareKey, L"J2i.Net", 0, 0, &hCompanyKey);
		if (hCompanyKey)
		{
			RegDeleteKey(hCompanyKey, L"MyColourPreferences");
			canEraseCompanyKey = (ERROR_SUCCESS != RegEnumKeyEx(hCompanyKey, 0, buffer, &bufferSize, 0, 0, 0, 0));
			RegCloseKey(hCompanyKey);
			RegCloseKey(hCompanyKey);
			if (canEraseCompanyKey)
				RegDeleteKey(hSoftwareKey, L"J2i.Net");
		}
		if (hSoftwareKey)
			RegCloseKey(hSoftwareKey);
	}
}

To export the four functions the DLL will also need to contain a Module Definition file that exports the four functions. The contents of that file must look like the following.

EXPORTS
Install_Init
Install_Exit
Uninstall_Init
Uninstall_Exit

That’s all that needs to be in the DLL. When you create your installer you will need to add both the primary output of your program and this DLL to the project. The last thing that you need to do is change the CE Setup DLL setting on the CAB project. Select the CAB project from the Solutions Explorer and you will see the CE Setup DLL setting in the property editor. Click on the dropdown to the right of the setting and select Browse. Navigate to the native DLL that you created and your done. All you need to do is compile and build the CAB.

As an example there is a Visual Studio project attached to this entry.  The project contains a program of trivial simplicity.  All that you need to know about that program is that it will save the information you enter to the registry.  Without the custom action when you uninstall this program that information would be left in the registry.   But thanks to the custom action it will be removed.

Apple Patenting Ambient Noise Volume Adjustment, Also Available on Windows Mobile

This isn’t the first observation of this type that I’ve seen, but Apple hasย filed for a patentย for adjusting phone volume based on ambient noise.ย  As the noise in the environment goes up so will the phones volume.ย  Oddly enough I’ve seen a similar system published in anย MSDN magazineย for Windows Mobile phones.


From the Blog Engine Archives

Posts may contain products with affiliate links. When you make purchases using these links, we receive a small commission at no extra cost to you. Thank you for your support.

Mastodon:ย @j2inet@masto.ai


Instagram:ย @j2inet

Facebook:ย @j2inet

YouTube:ย @j2inet

Telegram:ย j2inet

Twitter:ย @j2inet