Since I’m learning so much doing the port of Pyxis/Netby I thought I’d create a thread where we could all post helpful tips to each other.
First tip:
NETMF supports bit shifting, don’t be afraid to use it. If you’re saving data that doesn’t need a value all the way to 255 you can store multiple items within a single Byte. I use this for anti-aliased fonts in Pyxis 2 Beta 2 (release coming).
Let’s take a look at an example.
In Font2 I only need 3 Bits to determine the alpha value. This leaves 5 bits left in the Byte. I use these to tell me RLE (Run-Length-Encoding); so I can have 1 value repeat itself up to 31 times (binary 11111).
curByte = myChar.Data[j]; // Current Byte
vv = (byte)(curByte >> 5); // Indexed Alpha Value to use
ll = (byte)(curByte << 3); // Length shifted left 3 to remove Alpha Index
ll = (byte)(ll >> 3); // Now shifted right 3 to give us the correct value
You can easily launch an application in a new domain by using Marshalling. It is important to note that ANYTHING passed in or out of your app MUST have a Marshalled interface.
Let’s look at the AppLauncher Pyxis 2 Beta 2 uses:
using System;
using System.IO;
using System.Reflection;
using Microsoft.SPOT;
namespace Skewworks.Pyxis2.Kernel
{
public class DynamicLoading
{
/// <summary>
/// The interface for a standlone application
/// </summary>
interface IApplication
{
/// <summary>
/// A sample method to invoke
/// </summary>
/// <returns></returns>
bool StartExecution(Controller controller, string path);
}
/// <summary>
/// A class that can be loaded across applicaiton domains which
/// implements the IApplicaiton interface
/// </summary>
public class Application : MarshalByRefObject, IApplication
{
/// <summary>
/// we need a defautl consturctor to create an instance of this object
/// across an application domain
/// </summary>
public Application()
{
}
/// <summary>
/// Implementation of the corresponding IApplication method
/// </summary>
/// <returns>Always true</returns>
bool IApplication.StartExecution(Controller controller, string path)
{
Debug.Print("Current Domain: " + AppDomain.CurrentDomain.FriendlyName);
string sDir = Path.GetDirectoryName(path);
string[] sFiles = Directory.GetFiles(sDir);
FileStream iFile;
byte[] data;
Debug.Print("Containing Folder: " + sDir);
// Load Supporting PEs
Debug.Print("Loading supporting assemblies...");
for (int i = 0; i < sFiles.Length; i++)
{
Debug.Print("File #" + i + " type: " + Path.GetExtension(sFiles[i]));
if (Path.GetExtension(sFiles[i]).ToUpper() == ".PE")
{
Debug.Print("Loading Assembly");
iFile = new FileStream(sFiles[i], FileMode.Open);
data = new byte[iFile.Length];
iFile.Read(data, 0, data.Length);
iFile.Close();
Assembly.Load(data);
}
}
Debug.Print("Support assemblies loaded");
Debug.Print("Loading from External...");
iFile = new FileStream(path, FileMode.Open);
data = new byte[iFile.Length];
iFile.Read(data, 0, data.Length);
iFile.Close();
RunAppFromBytes(controller, data);
return true;
}
private void RunAppFromBytes(Controller controller, byte[] bytes)
{
// Attempt to execute DLL
try
{
Assembly asm;
MethodInfo[] m;
asm = Assembly.Load(bytes);
if (asm == null)
{
return;
}
Type[] t = asm.GetTypes();
for (var j = 0; j < t.Length; j++)
{
m = t[j].GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
for (int i = 0; i < m.Length; i++)
{
if (m[i].Name == "PyxisApp")
{
//Debug.Print("xResult: " + (String)m[i].Invoke(asm, new object[] {controller}));
AppStartup AppInfo = (AppStartup)m[i].Invoke(asm, new object[] {controller});
Debug.Print("App: " + AppInfo.title);
}
}
}
}
catch (Exception)
{
// Do nothing
}
}
}
/// <summary>
/// The executable entry point.
/// </summary>
public void RunApp(Controller controller, string path)
{
//string asmName = typeof(DynamicLoading).Assembly.FullName;
//LaunchNewApplicationAndTrasferData("pyxis2.apps", asmName, bytes);
Debug.Print("Preparing to launch: '" + path + "'");
// create a new application domain
Debug.Print("Creating AppDomain");
AppDomain ad = AppDomain.CreateDomain("pyxis2.apps");
// create an instance of the IApplication type
// please note that the following call will also load the assembly that contains that type
// which we pass as first parameter
Debug.Print("Creating App Launcher Instance...");
IApplication app;
try
{
app = (IApplication)ad.CreateInstanceAndUnwrap(typeof(IApplication).Assembly.FullName, typeof(Application).FullName);
}
catch (Exception e)
{
string strInner = e.InnerException.ToString();
string strStack = e.StackTrace.ToString();
Debug.Print("FAILED: " + e.Message + "\n" + strInner + strStack);
return;
}
Debug.Print("Launching App..");
try
{
app.StartExecution(controller,path);
}
catch (Exception e)
{
string strInner = e.InnerException.ToString();
string strStack = e.StackTrace.ToString();
Debug.Print("FAILED: " + e.Message + "\n" + strInner + strStack);
return;
}
}
}
}
Here you can see we first create an interface for a “Launcher” this is because whatever you first load into a new AppDomain bubbles up to the default domain; so we use an object in the new domain to do the actual launching of the app.
You can see what it takes to make an interface in this example.
The actual App launching is done here:
AppStartup AppInfo = (AppStartup)m[i].Invoke(asm, new object[] {controller});
We’ve found a method we recognize and invoke it. The first variable is just the assembly we’re loading from. The second is any variables passed to that method. If there are none just put in null.
When you load applications into a new AppDomain it is important to remember that if you don’t destroy all AppDomains before attempting a reboot from the IDE the debugger will detach.
Why is this? When the Debugger tries to restart the device it kills the default AppDomain and waits for a reset, but if there is another AppDomain active it becomes the new default and your device will not reset properly.
If you run into an error and aren’t able to properly destroy the domains, simply unplug your device to reset the system.
If you’re running Windows 7, the inbox calculator program has a “programmer” View (View|Programmer on the menu, or Alt+F3) that’s very handy, which I use all the time.
Do you need to do per-pixel alpha blending with FEZ? Here’s an easy way to compute an alpha value using a Forecolor, Backcolor, and Alpha level 0 (transparent) to 1 (opaque)
If you’re loading assemblies in debug mode you may notice it takes for Freakin’ ever. Want to make it go faster? As soon as you see the debugger begin to load the assembly press the pause button, and then the play button. This allows the FEZ to do it’s work loading the assembly without having to tell the debugger every little step it takes to do so and you’ll load the assembly much faster.
I’ve been a professional programmer for 12yrs. 2 rules. BACKUP & name smart. Even if it’s just a little project you’ll probably throw away give it a meaningful name; you never know you may really want it later.
This tip sponsored by my desktop failing in the most epic way I’ve ever seen. Last backup? Yesterday. Only 10hrs work lost as opposed to weeks or months.
And my tip: create several “versions”. As soon as you are working on a complex piece of software, or with a lot of code, you should always backup your working parts, before continuing working on the next part of the software. This can save a LOT of time correcting bugs (Happened to me…)
You have two choices: SVN & Git; I chose SVN because it’s simpler and has better tooling support for windows.
And I use this Visual Studio add-in [url]http://ankhsvn.open.collab.net/[/url] which lets you use SVN directly from Visual Studio. The best feature it’s that it automatically adds new files to SVN (adds, you still need to commit them) and only adds files that should be on SVN : it does not add bin/ & obj/. Sadly it’s not available for Express editions because those editions don’t support add-ins
I also use ankhsvn, although I use it with my own SVN server.
As a general rule, SVN works well, but tryign to do anything tricky with files has a tendency to really mess it up. For example, renaming a file in your project then committing has caused me quite a bit of grief.
At work we use SVN for our project source control, but at home I feel that it is bit overkill. So at home I stick to Dropbox as;
[ulist]
It’s a permanent offline backup
Every file can be restored at any save point so if you have messed up your code, restore an earlier save.
Deleted files and projects can be restored.
Every computer I work on will be synchronized automatically.
[/ulist]
So for any of you out there that won’t bother with setting up a SVN server, -get a free Dropbox and you will have; an offline backup, synchronized projects on all your computers, all saved versions are available, deleted files can be restored.
You don’t need to set up an SVN server to use it at home. It works just fine with a local repository. I set up my local depository on my Windows Homer Server which is continuously backed up to IDrive. Works great…
Can also use a free SkyDrive and mount drive local with GladiNet. Still need some kind of VS plug in to enum projects and send to drive. As a side benefit, you have Office docs next to your project files and use the web office. Can also setup Groups/permissions for your own adhoc project repository. Pretty good for free.
[url]http://www.newbtech.com/2009/04/live-skydrive-how-to-set-up-as-network.html[/url]