C# Passing Values Between Multiple Projects using Interface

Earlier when I was doing some major update on a project I was working on for a client, I was also thinking how to optimize the code, not just by using MVP pattern and also the way of passing values between projects. Some of my class libraries where using the same type of property and then gets updated in the main form. I noticed though that setting the same value into the multiple objects with one type of property is pretty hard and unmanageable and also producing a lot of lines of codes. So I experimented a little bit and made an proof on concept how to pass values into multiple projects with minimal coding and using Interface

Let's see the sample animation below. What was shown there is we have satellite that moves in the form and there are 3 child forms. Those 3 child forms are the other 3 projects which are PlanetEarth, PlanetMars, and PlanetSaturn project and the big the form is our Main project. You'll notice that the 3 forms are getting updated everytime the satellite make its next move.

What is so interesting in using an Interface is you can share its values to whoever uses it. Like what I did on that simple P.O.S. project. Those 3 forms, PlanetEarth, PlanetMars, and PlanetSaturn is using one interface but they all get the update.

So why Interface instead of making a property for each project?

Because like I said, you can share the values between multiple projects or whoever uses it.

Let's get into the code

In this sample VS solution. We have 5 projects.

GlobalInterface class library project which contains the Interface code which have an NextPosition property signature what we're going to use later on the Main Project

public interface iSatellite
{
	Point NextPosition { get; set; }
}

Then we have 3 projects that has the same code (For demo only) which is using this iSatellite interface. These are the PlanetEarth, PlanetMars, and PlanetSaturn. Let's take PlanetEarth for example

GlobalInterface.iSatellite Satellite;

public Form1()
{
	InitializeComponent();

	timer1.Interval = 100;
	timer1.Tick += new EventHandler(timer1_Tick);
}

void timer1_Tick(object sender, EventArgs e)
{
	// update the location information of the satellite
	label2.Text = string.Format("X: {0}\r\nY:{1}", this.Satellite.NextPosition.X, this.Satellite.NextPosition.Y);
}

public void InitializeSatellite(GlobalInterface.iSatellite satellite)
{
	this.Satellite = satellite;
	this.timer1.Start();
}

As you noticed, we used Timer object to show the next location of the satellite. We could also use Event to shout that the satellite is making a next move.

InitializeSatellite(GlobalInterface.iSatellite satellite) will be called on our Main Project to bind the "this.Satellite" into our Main project.

Next is our Main project that implements iSatellite interface

public partial class MainController : Form, GlobalInterface.iSatellite
{
	TweenLibrary tween;
	Random destXY = new Random(DateTime.Now.Millisecond);
	List<Form> forms;

	public MainController()
	{
		InitializeComponent();
		InitializeControls();
		InitializeControlEvents();
	}

	public void InitializeControls()
	{
		// show our 3 forms
		PlanetEarth.Form1 earth = new PlanetEarth.Form1();
		earth.InitializeSatellite(this);
		earth.Show();

		PlanetMars.Form1 mars = new PlanetMars.Form1();
		mars.InitializeSatellite(this);
		mars.Show();

		PlanetSaturn.Form1 saturn = new PlanetSaturn.Form1();
		saturn.InitializeSatellite(this);
		saturn.Show();

		// then put them on the list to update their location
		forms = new List<Form>()
		{
			earth, mars, saturn
		};

		tween = new TweenLibrary();

		timer1.Interval = Convert.ToInt32(TimeSpan.FromSeconds(3).TotalMilliseconds);
		timer1.Tick += new EventHandler(timer1_Tick);
		timer1.Start();
	}

	public void InitializeControlEvents()
	{
		this.LocationChanged += new EventHandler(Form1_LocationChanged);
	}

	/// <summary>
	/// update child form positions
	/// </summary>
	/// <param name="sender"></param>
	/// <param name="e"></param>
	void Form1_LocationChanged(object sender, EventArgs e)
	{
		int last_y = this.Location.Y;

		foreach (Form frm in forms)
		{
			frm.Location = new Point(this.Location.X + this.Size.Width, last_y);
			last_y = frm.Location.Y + frm.Size.Height;
		}
	}

	void timer1_Tick(object sender, EventArgs e)
	{
		this.NextPosition = new Point()
		{
			X = destXY.Next(this.ClientSize.Width),
			Y = destXY.Next(this.ClientSize.Height)
		};

		tween.startTweenEvent(picSatellite, this.NextPosition.X, this.NextPosition.Y, "easeinoutcubic", 100);
	}

	/// <summary>
	/// Implement GlobalInterface.iSatellite property
	/// </summary>
	public Point NextPosition { get; set; }
}

As you can see just below on that code, we implemented iSatellite interface with NextPosition property with a Point type which we're going to use to update the location of the satellite. In the timer1_Tick event, this gets triggered every 3 seconds and you can see, we update the "NextPosition" X and Y values. The 3 projects that is bound in the interface are automatically updated.

So what is the benefit of the Interface in this type of problem about passing values between multiple visual studio projects?

  • Lesser code
  • We do not need to update the values for each project.
  • Easy to manage
  • More structured.

Comparing the usage of Property vs Interface

Let's say each projects PlanetEarth, PlanetMars, and PlanetSaturn are using NextPosition Property

void timer1_Tick(object sender, EventArgs e)
{
	Point newXY = new Point()
	{
		X = destXY.Next(this.ClientSize.Width),
		Y = destXY.Next(this.ClientSize.Height)
	};
	
	this.earth.NextPosition = newXy;
	this.mars.NextPosition = newXy;
	this.saturn.NextPosition = newXy;
}

But when we use Interface

void timer1_Tick(object sender, EventArgs e)
{
	this.NextPosition = new Point()
	{
		X = destXY.Next(this.ClientSize.Width),
		Y = destXY.Next(this.ClientSize.Height)
	};

	tween.startTweenEvent(picSatellite, this.NextPosition.X, this.NextPosition.Y, "easeinoutcubic", 100);
}

We only update the property we implemented from iSatellite and all 3 projects are automatically updated as well.

Hope you learned about another special way of implementing Interface in your projects, not only in Windows Forms, but in Windows Phone, Windows 8, Silverlight, and WPF projects as well.

Here's the a sample project that you can play with.

Click here to expand the blog post

Session Aware Web Client

I tried looking for some free session aware web clients but I couldn't find a better one so I made my own instead.

/*
 * Session Aware Web Client Library v1.3
 * ---------------
 * Date: 12/17/2010
 * Author: Jayson Ragasa
 * http://wall.jaysonragasa.net
 * License: Common Development and Distribution License (CDDL)
 */

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Web;

namespace Nullstring.Modules.WFWebClient
{
    public class SessionAwareWebClientLibrary
    {
        #region vars

        string _referer = string.Empty;

        ArrayList _params;
        CookieContainer cookieko = new CookieContainer();
        HttpWebRequest req = null;
        HttpWebResponse resp = null;
        Uri uri = null;

        #endregion

        #region properties
        public string Referer
        {
            get { return _referer; }
            set { _referer = value; }
        }

        bool _list_cookies = false;
        public bool ListCookiesInOutputWindow
        {
            get { return _list_cookies; }
            set { _list_cookies = value; }
        }

        public CookieContainer Cookie
        {
            get { return cookieko; }
            set { cookieko = value; }
        }
        #endregion

        #region constructor
        public SessionAwareWebClientLibrary()
        {
            _params = new ArrayList();
            cookieko = new CookieContainer();
        }
        #endregion

        #region methods
        public void ClearParameter()
        {
            _params.Clear();
        }

        public void AddParameter(string key, string value)
        {
            _params.Add(string.Format("{0}={1}", WebTools.URLEncodeString(key), WebTools.URLEncodeString(value)));
        }

        void SetupWebRequests(string URL)
        {
            uri = new Uri(URL);
            req = (HttpWebRequest)WebRequest.Create(uri);
			
            req.AllowAutoRedirect = true;
            req.KeepAlive = true;
            req.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            req.UserAgent = "Mozilla/5.0 (Windows NT 6.1; rv:8.0) Gecko/20100101 Firefox/8.0";
			
			// handle respnose cookie
            req.CookieContainer = cookieko;
			
			// if we made the first request
            if (resp != null)
            {
				// just reuse it
                req.CookieContainer.Add(resp.Cookies);
            }			
			
			// some optional properties
			//req.MaximumAutomaticRedirections = 20;
            //req.Timeout = 300;
            //req.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
            //req.UseDefaultCredentials = true;
			//req.Referer = this._referer;
			
			// get rid of 100 service bug
            req.ServicePoint.Expect100Continue = false;
        }

        void BuildPostData()
        {
            string Parameters = String.Join("&", (String[])this._params.ToArray(typeof(string)));

            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] loginDataBytes = encoding.GetBytes(Parameters);

            req.Method = "POST";
            req.ContentType = "application/x-www-form-urlencoded";
            req.ContentLength = loginDataBytes.Length;

            Stream stream = req.GetRequestStream();
            stream.Write(loginDataBytes, 0, loginDataBytes.Length);
            stream.Close();
        }

        StringBuilder GetWebResponse()
        {
            StringBuilder response = new StringBuilder();

            resp = (HttpWebResponse)req.GetResponse();

            Stream resStream = resp.GetResponseStream();

            int bytesReceived = 0;
            string tempString = null;
            int count = 0;
            byte[] buf = new byte[8192];

            do
            {
                count = resStream.Read(buf, 0, buf.Length);

                if (count != 0)
                {
                    bytesReceived += count;
                    tempString = Encoding.UTF8.GetString(buf, 0, count);
                    response.Append(tempString);
                }
            }
            while (count > 0);

            return response;
        }

        public string GetResponse(string URL)
        {
            StringBuilder response = new StringBuilder();

            try
            {
                SetupWebRequests(URL);
                req.Method = "GET";
                response = GetWebResponse();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message, ex);
            }

            if (this._list_cookies)
            {
                ListCookieHeaders(URL);
            }

            return response.ToString();
        }

        public string GetResponse(string URL, bool UsePostData)
        {
            StringBuilder response;

            try
            {
                SetupWebRequests(URL);

                if (UsePostData)
                {
                    BuildPostData();
                }
                else
                {
                    req.Method = "GET";
                }

                response = GetWebResponse();
            }
            catch (Exception ex)
            {
                response = new StringBuilder(string.Empty);
            }

            if (this._list_cookies)
            {
                ListCookieHeaders(URL);
            }

            return response.ToString();
        }

		// test
        void SetCookieContainer(CookieContainer cc, string url)
        {
            IEnumerator ie = cookieko.GetCookies(new Uri(url)).GetEnumerator();
            while (ie.MoveNext())
            {
                string[] cookie = ie.Current.ToString().Split('=');
                cc.Add(new Cookie(cookie[0], cookie[1]));
            }
        }

        void ListCookieHeaders(string url)
        {
            System.Diagnostics.Debug.WriteLine("listing cookies");

            IEnumerator ie = cookieko.GetCookies(new Uri(url)).GetEnumerator();
            while (ie.MoveNext())
            {
                System.Diagnostics.Debug.WriteLine(ie.Current.ToString());
            }

            System.Diagnostics.Debug.WriteLine("listing cookies: done");
        }
        #endregion
    }
}
Click here to expand the blog post

Sound Fx Windows Mobile Application

A simple application that plays WAV files.

Download the application here

Discuss it here in XDA-Developers

Click here to expand the blog post

C# Recurse through selected folder and populate treeview with folders and files

Using the same code I posted before, I just added a little way to add the folders and files in TreeView control with file extension filtering. Here's the code sample

void SelectSourceFolder()
{
	using (FolderBrowserDialog fbd = new FolderBrowserDialog())
	{
		fbd.SelectedPath = string.Empty;
		fbd.ShowDialog();

		if (fbd.SelectedPath != string.Empty)
		{
			ScanFolder(fbd.SelectedPath);
		}
	}
}

void ScanFolder(string source_folder)
{
	List<string> allowed_extension = new List<string>
	{
		".jpg"
	};

	GetFilesRecursive(source_folder, allowed_extension);
}

public List<string> GetFilesRecursive(string b, List<string> allowedextension)
{
	List<string> result = new List<string>();
	Stack<string> stack = new Stack<string>();

	stack.Push(b);

	TreeNode parentNode = tvFolderStructure.Nodes.Add(b, "root");
	
	while (stack.Count > 0)
	{
		string dir = stack.Pop();
		parentNode = (tvFolderStructure.Nodes.Find(dir, true))[0];

		try
		{
			//result.AddRange(Directory.GetFiles(dir, "*.*"));

			foreach (string dn in Directory.GetDirectories(dir))
			{
				stack.Push(dn);
				DirectoryInfo di = new DirectoryInfo(dn);

				parentNode.Nodes.Add(di.FullName, di.Name, 0);
			}

			foreach (string fn in Directory.GetFiles(dir, "*.*"))
			{
				FileInfo fi = new FileInfo(fn);
				if (allowedextension.Count == 0)
				{
					result.Add(fi.Name);
					parentNode.Nodes.Add(fi.Name, fi.Name, 1);
				}
				else
				{
					if (allowedextension.Contains(fi.Extension.ToLower()))
					{
						result.Add(fi.Name);
						parentNode.Nodes.Add(fi.Name, fi.Name, 1);
					}
				}
			}
		}
		catch
		{
			;
		}
	}

	return result;
}
Click here to expand the blog post

DropToWin v2.1

A small tool I made while creating my first ever custom Windows Mobile ROM.

What this tool basically do is to copy files or folder directly to Windows directory or import registry keys based from .REG and .PROVXML. It's a good tool actually if you are a WM modder like me, and make a lot of changes in your WM phone.

 

Here are the screen shots

 

Features:

  • Restart Sense/Manila
  • Stop Sense
  • Start Sense
  • Soft Reset
  • Copy Files and Folders without the nag of question to overwrite files/folders.
  • Can accept parameters - thanks to @ai6908 for his thoughts about this
  • Import .REF
  • Import .PROVXML
  • Execute the file selected from the list
  • Also embeded in the EXE itself is dotFred Task Manager v3.3

Parameters:

  • rs = Restart Sense
    ex: DropToWin.exe rs
  • ts = Stop Sense
    ex: DropToWin.exe ts
  • ss = Start Sense
    ex: DropToWin.exe ss
  • sr = Soft Reset
    ex: DropToWin.exe sr
  • copy = copy a file from source to desitination
    ex: DropToWin.exe copy \sourcefile.txt \windows\newfile.txt

Experimental File Parameters:

 

1. Deleting a file in \Windows directory directly here in DropToWin

Instructions:

-- 1.1 Make a dummy file

------ e.g. If you want to delete ManilaFull.xml in \Windows directory

------ 1.1.1 Create a dummy file and name it like this >> q=del;ManilaFull.xml

------ 1.1.2 Then copy that file where DropToWin.exe is located then click DROP button!

2. Renaming a file in \Windows directory directly here in DropToWin

Instructions:

-- 2.1 Make a dummy file

------ e.g. If you want to rename ManilaFull.xml in \Windows directory

------ 2.1.1 Create a dummy file and name it like this >> q=ren~New_File_Name_Of_ManilFull.xml;ManilaFull.xml

------ 2.1.2 Then copy that file where DropToWin.exe is located then click DROP button!

 

RELEASE HISTORY:

v2.1

  • ADDED
  • New "copy" parameter
  • Run the selected file
  • Import .REG and .PROVXML files
  • Install dotFred Task Manager v3.3

v1.6

  •  Fixed sync when loading large files.

v1.5

  • Added 2 new parameter
  • Added 5 shortcuts in Start Menu\Gambit Tools
  • Added HQ icons for those 5 shortcuts

v1.3

  •  Added parameter support
  •  Experimental file parameters

v1.2

  • Initial Release

Click here to expand the blog post

C# Run application and wait to exit

I've been using this sometimes and works as expected. For example, I want to run an application and wait for it to exit before going to the next statement or running another application, we could do it this way.

Requires System.Diagnostics

string windir = "\\Windows";
string sel = "application.exe";

ProcessStartInfo psi = new ProcessStartInfo()
{
	FileName = Path.Combine(windir, sel),
	UseShellExecute = true,
	WorkingDirectory = windir
};

Process p = new Process()
{
	StartInfo = psi,
	EnableRaisingEvents = true,
};

Debug.WriteLine("Executing " + sel + " and waiting for it to close");

p.Start();
p.WaitForExit();

Debug.WriteLine("Done. EXIT CODE" + p.ExitCode.ToString());

ProcesStartInfo is a class that we can use to instruct the Process class about the informations about the file that we want to execute, such as, what are the Parameters and the working directory

Process on the other hand is class that executes the file that we want to run and also contains some important methods about what we're trying to do here such as WaitForExit().

 

One great example of it is if you want to develop an application that will install a batch of CAB in Windows Mobile.

Click here to expand the blog post

Move DataGridView Row Up or Down

Standard DataGridView in Windows Forms does not support dragging rows so we had to make a simple code solution.

Below is a sample code demonstrating how to move the DataGridView Row going Up or Down

public Form1()
{
	InitializeComponent();
	InitializeControlEvents();
	InitializeControls();
	
	PopulateDataGridView();
}

void InitializeControls()
{
	songsDataGridView.MultiSelect = false;
	
	songsDataGridView.ColumnCount = 5;
	songsDataGridView.Columns[0].Name = "Release Date";
	songsDataGridView.Columns[1].Name = "Track";
	songsDataGridView.Columns[2].Name = "Title";
	songsDataGridView.Columns[3].Name = "Artist";
	songsDataGridView.Columns[4].Name = "Album";
}

void InitializeControlEvents()
{
	btnMoveRowUp.Click += new EventHandler(btnMoveRowUp_Click);
	btnMoveRowDown.Click += new EventHandler(btnMoveRowDown_Click);
}

private void PopulateDataGridView()
{
	string[] row0 = { "11/22/1968", "29", "Revolution 9", 
		"Beatles", "The Beatles [White Album]" };
	string[] row1 = { "1960", "6", "Fools Rush In", 
		"Frank Sinatra", "Nice 'N' Easy" };
	string[] row2 = { "11/11/1971", "1", "One of These Days", 
		"Pink Floyd", "Meddle" };
	string[] row3 = { "1988", "7", "Where Is My Mind?", 
		"Pixies", "Surfer Rosa" };
	string[] row4 = { "5/1981", "9", "Can't Find My Mind", 
		"Cramps", "Psychedelic Jungle" };
	string[] row5 = { "6/10/2003", "13", 
		"Scatterbrain. (As Dead As Leaves.)", 
		"Radiohead", "Hail to the Thief" };
	string[] row6 = { "6/30/1992", "3", "Dress", "P J Harvey", "Dry" };

	songsDataGridView.Rows.Add(row0);
	songsDataGridView.Rows.Add(row1);
	songsDataGridView.Rows.Add(row2);
	songsDataGridView.Rows.Add(row3);
	songsDataGridView.Rows.Add(row4);
	songsDataGridView.Rows.Add(row5);
	songsDataGridView.Rows.Add(row6);
}
		
void btnMoveRowDown_Click(object sender, EventArgs e)
{
	if (songsDataGridView.SelectedRows.Count > 0)
	{
		DataGridViewRow row = songsDataGridView.SelectedRows[0];
		int selRowIndex = row.Index;

		if ((selRowIndex + 1) == songsDataGridView.Rows.Count - 1) { return; }

		songsDataGridView.Rows.Remove(row);
		songsDataGridView.Rows.Insert(selRowIndex + 1, row);
		songsDataGridView.Rows[selRowIndex + 1].Selected = true;
	}
}

void btnMoveRowUp_Click(object sender, EventArgs e)
{
	if (songsDataGridView.SelectedRows.Count > 0)
	{
		DataGridViewRow row = songsDataGridView.SelectedRows[0];
		int selRowIndex = row.Index;

		if (selRowIndex == 0) { return; }

		songsDataGridView.Rows.Remove(row);
		songsDataGridView.Rows.Insert(selRowIndex - 1, row);
		songsDataGridView.Rows[selRowIndex - 1].Selected = true;
	}
}
Click here to expand the blog post

Enumerating InputMethods and Setting a Default InputMethod

Here's a simple way to enumrate InputMethods in Windows Mobile and also setting a default InputMethod.

 

Requres Microsoft.WindowsCE.Forms

InputPanel inpanel = new InputPanel();
List<InputMethod> list_im = new List<InputMethod>();

foreach (InputMethod im in inpanel.InputMethods)
{
	list_im.Add(im);
	MessageBox.Show(im.Name);
}

and setting a default InputMethods

// for example 0 is our FingerKeyb
// http://forum.xda-developers.com/showthread.php?t=501434
inpanel.CurrentInputMethod = list_im[0]

 

simple as that. I made an application for changing the default InputMethod and retains even after restart. 

 

It's called SIP Changer.

Below is the screenshot (click to enlarge)

SIP Changer will make a new file in Root named "SIP.txt" which contains the Name of the InputMethod as well as the GUID.

Instructions:

  1. Choose your InputMethod
  2. Click Set Selection as Default then a message will pop-up "Installed" meaning it also created a shortcut link in \Windows\StartUp folder

Parameters:

SIPChanger.exe <InputMethod name. case not sensitive>

e.g. SIPChanger.exe finGerKebD

 

and here's the file 

SIPChanger.exe (24.00 kb)

Click here to expand the blog post

Implementing JSON.NET in Windows Mobile NETCF 2.0 or 3.5

I was building an application in Windows Mobile and part of it is making a simple script which will be read upon running the application. As the script grows, it become more rubbish and tedious so I thought of changing the idea into something more beautiful and more readable. One thing that came off in my mind was XML, but immediately thought of JSON. So I done a little research if there are any libraries available for NET CF and turns out that the it self does supports NET CF.

 

There are some few problems though in the latest release which deserialization spits back an "MethodNotFound" error in DeserializeObject<T>() method which bugs me because the Doc it self said it supports Compact Framework. The error doesn't mean DeserializeObject<T>()  does not exists in Newtonsoft.Json.JsonConvert but inside that method. I got a little worried, but instead I look for an alternative, but luckily, the old version of JSON.NET is much "safer"(?). Thanks to Gunter Spranz for his work about his modified JSON.NET (skydrive link)

 

Here's a sample code I tried while doing some tests

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Newtonsoft.Json;
using System.Diagnostics;

namespace json_test
{
    class Program
    {
        static void Main(string[] args)
        {
            //string file = Path.Combine(Gambit.IO.Utilities.StartupPath(), "json.txt");
            //using (StreamReader reader = new StreamReader(file))
            //{
            //    file = reader.ReadToEnd();
            //}

            string file = @"{
	""wceload_param"": ""/delete 1 /noui /silent "{0}""",
	""final_message"": ""Installation is done. Have fun with Gambit ROM!"",
	""batch"": [
		{
			""file"": ""FingerKeyb-2.1.408.VGA.(Arrows).by.Giannis86.cab"",
			""isimportant"": ""true"",
			""friendly_name"": ""FingerKeyboard 2.1 with Arrows"",
			""ismarked"": ""true"",
			""unchecked_message"": """",
			""readme"": """"
		},
		{
			""file"": ""FingerKeybdDefault.lnk"",
			""isimportant"": ""true"",
			""friendly_name"": ""Set FingerKeyb as default SIP"",
			""ismarked"": ""true"",
			""unchecked_message"": """",
			""readme"": """"
		},
	]
}";

            script s = JsonConvert.DeserializeObject<script>(file);

            Debug.WriteLine(s.wceload_param);
            Debug.WriteLine(s.final_message);
            foreach (batch b in s.batch)
            {
                Debug.WriteLine(b.friendly_name);
            }

            script ss = new script();
            ss.final_message = "final message";
            ss.wceload_param = "wceload param";
            ss.batch = new List<batch>();
            ss.batch.Add(new batch()
            {
                friendly_name = "hello world"
            });
            ss.batch.Add(new batch()
            {
                friendly_name = "hello earth"
            });

            string json = JsonConvert.SerializeObject(ss, Formatting.Indented);
            Debug.WriteLine(json);
        }
    }

    public class script
    {
        public string wceload_param
        {
            get;
            set;
        }

        public string final_message
        {
            get;
            set;
        }

        public List<batch> batch
        {
            get;
            set;
        }
    }

    public class batch
    {
        public string file
        {
            get;
            set;
        }

        public string isimportant
        {
            get;
            set;
        }

        public string friendly_name
        {
            get;
            set;
        }

        public string ismarked
        {
            get;
            set;
        }

        public string unchecked_message
        {
            get;
            set;
        }

        public string readme
        {
            get;
            set;
        }
    }
}

 

in case the file doesn't exists any more in Gunter Spranz's Skydrive, you can download that file directly in this site

Json.Net CF 2.0.zip (205.50 kb)

Click here to expand the blog post

Final Customization for Windows Mobile Chefs

Final Customization

 

What is it?

It's another chef application for having a final step on customizing their ROM. Final Customization runs after AutoRun, SDAutoRun, UC has finished and made a soft reset to the phone.

 

Basic Flow

First Boot

-> Screen Calibration(skipped in other ROMs)

----> Customize

------> Soft-Reset

--------> Final Customization 1st run and will popup application choices

--------> and the installation begins. Then after the isntallations, it will

--------> ask if the user want's to soft-reset or not.

--------> if YES, the phone will soft-reset. If NO, Final Customization exists and

--------> show the final message.

 

Why need such another Customization?

Final Customization gives users a chance to decide what application they want to install from the ROM and "from" the ROM means a CAB or REG that is cooked in the chef's cooked ROM. 

 

But why they have to decide which application they need to install?

Simply because not all users like the application we included in our ROM. Me my self is a Chef, and I know some users don't like my application, so I'd like to give them a chance to decide if they want to install that application.

 

Another benefit for the chefs is you can control the Manila/Sense.

- You can Stop the Manila/Sense then install the cooked cab or reg and Start the Manila/Sense.

- You can Restart the sense after you installed the cooked cab.

 

So what does it support?

Final Customization can install CAB and REG(requires dotFred Task Manager) and you can execute any files as long as your ROM supports it like running a Mort Script if you have Mort Script included in your ROM, or you can execute EXE files, Play an audio, video, etc!

 

How does it execute these files?

Final Customization can understand some commands such as:

reset
soft-reset the device. Final Customization knows if it's not yet done installing the user selected apps.

has_manila
Some of your CABs or REG entries might require Manila/Sense to load first. So I made a way to monitor the Manila/Sense if its already loaded and running.

stop_manila
This of course is associated with has_manila command. You can stop the Manila/Sense then install the CAB or execute a REG then start the Manila/Sense after the installation.

start_manila
After you the CAB or REG installed. You can start the Manila.

restart_manila
You might want to execute some REG that doesn't really requires the Manila/Sense to stop and start before and after the installation. You can just execute the REG and restart_manila.

 

SAMPLE COMMANDS


S3VideoDrivers.cab!|Install 3D Drivers;
reset!|Soft-Reset and Apply Video Drivers;
WiMoSpeed_614mhz.lnk|Overclock CPU to 614.4Mhz;
paint.cab|MSPaint Like Application;
gremotesetup.cab;
reset|Soft-Reset but optional;
has_manila;
	stop_manila;
	DefaultSenseWallpaper.CAB|Default Sense Wallpaper;
	InstallSenseQuickLinks.reg;
	start_manila;

these commands (not case sensitive) are saved in a file called cablist.txt. As you can see, they all had semi-colon at the end that acts as a separator between the commands. You can also add TAB or Spaces to make your code cleaner..

 

Other thing you'll notice is the PIPE character which separates the command and the friendly name that will show in Installation Choices window.

 

and the "!" after the file, which means, the file is important and cannot be unselected from the choice lists.

 

format:

command|Friendly Name

or

command!|Friendly Name

 

The .CAB, .REG, and .LNK you saw there are files.

 

Okay, so how do I really use it?

I will provide the EXT package which contains the initial files needed from Final Customization then you can add whatever you want to include such as CAB or .REG or probably .PROVXML but make sure your ROM supports .PROVXML file types.

 

cablist.txt file is also included then you can just update it.

 

another file is included named FinalizeSetup.app.config which contains the parameter used by WCELOAD.exe and your final message after the installation is done. Be careful on WCELOAD parameters! Here are some WCELOAD parameter informations.

<appSettings>
  <add key="wceload_param" value="/delete 1 /noui /silent "{0}""/>
  <add key="final_word" value="Installation is done. Have fun!"/>
</appSettings>

AHA! Finally the preview. I just made a sample video for you guys to watch. Sorry but I have to keep silent (again)

 

A possible issues of Final Customization.

It cannot respond to custom actions when installing a CAB. In regular CAB installation, some installation popups a window aobut agreement and stuff and you have to accept the agreement. So Final Customization cannot handle that. 

 

If you think you can handle that via WCELOAD parameter. Please let me know.

 

Final Customization can also execute control panel applets such as running Regional Settings but the problem I saw was when executing 2 applets, Final Customization did not wait for the 1st one to finish but instead, it executes the next applet.

 

Sending a bug report

Final Customization has a logging feature for bug hunting references. It is located in \GambitLogs folder. Please send all the contents to me 

 

check the full discussion here in XDA and Modaco

Click here to expand the blog post

About Jayson Ragasa

Jayson Ragasa is an Applications Developer and founder of CAPPLOUD.

Badges

Free Page Rank Tool

wall.jaysonragasa.net Webutation
Uptime Report for http://wall.jaysonragasa.net/: Last 30 days