AutorunMgr moved to Codeplex

Autorun Manager


Occasionally I need to have an application auto-run on a Windows CE platform (when I plug in a USB disk or CF card).  I wrote a simple utility called Autorun Manager several years ago that gives a lot of flexibility for auto-running apps.  Well someone just asked me about adding the capability to their platform, which tells me it’s not easy enough to find.  In hopes of making it a little more discoverable, I’ve moved it over to Codeplex.

Getting Native File Info in the Compact Framework

Getting file version info in the Compact Framework is pretty simple – provided the file you want info on is a managed assembly.  But what if you want to get the version info for a native file?  Well it’s not quite so nice.  As usual, you have to drop down and P/Invoke several Win32 APIs.  In this case we need to call GetFileVersionInfoSize, GetFileVersionInfo, and VerQueryValue.  We then have to parse the results from these files which, if you’re a beginner or not familar or comfortable with raw memory access (i.e. pointer fun), can be really challenging.


I put together a quick example class that extracts not only the file version, but the entire string table, so you can get things like the Company Name and Copyright info as well.  Here’s the code:

public static class NativeFile
{
public struct NativeFileInfo
{
public Version Version;
public NameValueCollection StringTable;
}
public unsafe static NativeFileInfo GetFileInfo(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException();
}

IntPtr handle;

var size = GetFileVersionInfoSize(path, out handle);
var buffer = Marshal.AllocHGlobal(size);
try
{
if (!GetFileVersionInfo(path, handle, size, buffer))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}

IntPtr pVersion;
int versionLength;
VerQueryValue(buffer, “\”, out pVersion, out versionLength);

var versionInfo = (VS_FIXEDFILEINFO)Marshal.PtrToStructure(pVersion, typeof(VS_FIXEDFILEINFO));

var version = new Version((int)versionInfo.dwFileVersionMS >> 16,
(int)versionInfo.dwFileVersionMS & 0xFFFF,
(int)versionInfo.dwFileVersionLS >> 16,
(int)versionInfo.dwFileVersionLS & 0xFFFF);

// move to the string table and parse
var pStringTable = ((byte*)pVersion.ToPointer()) + versionLength;
var strings = ParseStringTable(pStringTable, size – versionLength);

return new NativeFileInfo
{
Version = version,
StringTable = strings
};
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}

private unsafe static NameValueCollection ParseStringTable(byte *pStringTable, int length)
{
NameValueCollection nvc = new NameValueCollection();

byte *p = pStringTable;
short stringFileInfoLength = (short)*p;
byte* end = pStringTable + length;

p += (2 + 2 + 2); // length + valuelength + type
// verify key
var key = Marshal.PtrToStringUni(new IntPtr(p), 14);
if (key != “StringFileInfo”) throw new ArgumentException();

// move past the key to the first string table
p += 30;
short stringTableLength = (short)*p;
p += (2 + 2 + 2); // length + valuelength + type
// get locale info
key = Marshal.PtrToStringUni(new IntPtr(p), 8);

// move to the first string
p += 18;

while (p < end)
{
short stringLength = (short)*p;
p += 2;
short valueChars = (short)*p;
p += 2;
short type = (short)*p;
p += 2;

if (stringLength == 0) break;

if ((valueChars == 0) || (type != 1))
{
p += stringLength;
continue;
}

var keyLength = stringLength – (valueChars * 2) – 6;
key = Marshal.PtrToStringUni(new IntPtr(p), keyLength / 2).TrimEnd(”);
p += keyLength;
var value = Marshal.PtrToStringUni(new IntPtr(p), valueChars).TrimEnd(”);
p += valueChars * 2;

if ((int)p % 4 != 0) p += 2;

nvc.Add(key, value);
}
return nvc;
}

private const string COREDLL = “coredll.dll”;

[DllImport(COREDLL, SetLastError = true)]
private static extern int GetFileVersionInfoSize(string lptstrFilename, out IntPtr lpdwHandle);

[DllImport(COREDLL, SetLastError = true)]
private static extern bool GetFileVersionInfo(string lptstrFilename, IntPtr dwHandle, int dwLen, IntPtr lpData);

[DllImport(COREDLL, SetLastError = true)]
private static extern bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out int puLen);

[StructLayout(LayoutKind.Sequential)]
private struct VS_FIXEDFILEINFO
{
public DWORD dwSignature;
public DWORD dwStrucVersion;
public DWORD dwFileVersionMS;
public DWORD dwFileVersionLS;
public DWORD dwProductVersionMS;
public DWORD dwProductVersionLS;
public DWORD dwFileFlagsMask;
public DWORD dwFileFlags;
public FileOS dwFileOS;
public FileType dwFileType;
public DWORD dwFileSubtype;
public DWORD dwFileDateMS;
public DWORD dwFileDateLS;
};

public enum FileOS : uint
{
Unknown = 0x00000000,
DOS = 0x00010000,
OS2_16 = 0x00020000,
OS2_32 = 0x00030000,
NT = 0x00040000,
WindowsCE = 0x00050000,
}

public enum FileType : uint
{
Unknown = 0x00,
Application = 0x01,
DLL = 0x02,
Driver = 0x03,
Font = 0x04,
VXD = 0x05,
StaticLib = 0x07
}
}


One item that might not be intuitive is that I’m using an alias for the DWORD type, so up at the top of the code page add this:

using DWORD = System.UInt32;

I then created a quick sample app that pulled the info from the corecon file on my device:

class Program
{
static void Main(string[] args)
{
string target = “\FlashFX Disk\ARMv4i\conmanclient2.exe”;
var version = NativeFile.GetFileInfo(target);

Console.WriteLine(string.Format(“File: {0}”, Path.GetFileName(target)));
Console.WriteLine(string.Format(“Version: {0}”, version.Version.ToString(4)));
foreach (var key in version.StringTable.AllKeys)
{
Console.WriteLine(string.Format(“{0}: {1}”, key, version.StringTable[key]));
}

Console.ReadLine();
}

}


And this is the output:


 

Getting the same MD5 hash from Native and Managed code

I’m working on a Windows CE project where we’re storing an MD5 hash of a password in a SQL Compact database.  The solution is a mix of both native and managed code, and moth need to have access to both generating and verifying a password hash, so it’s pretty important that we have functions for both that generate the same hash output given the same string input.  Here are the functions I’m using:


Managed


private static string MD5Hash(string str)
{
  MD5 hash = MD5.Create();
  byte[] h = hash.ComputeHash(Encoding.Unicode.GetBytes(str));

  StringBuilder sb = new StringBuilder();
  foreach (byte b in h)
  {
    sb.Append(b.ToString(“x2”));
  }

  return sb.ToString();
}


Native


#include “wincrypt.h”

#define MD5LEN 16

DWORD MD5Hash(TCHAR *input, DWORD inLength, TCHAR *output, DWORD *outLength)
{
  HCRYPTPROV hContext = 0;
  HCRYPTHASH hHash = 0;
  DWORD status = 0;
  TCHAR hexDigits[] = _T(“0123456789abcdef”);
  DWORD hashLen = MD5LEN;
  BYTE hash[MD5LEN];
  TCHAR pair[2];

  if(*outLength < (MD5LEN + 1) * sizeof(TCHAR)) return ERROR_INSUFFICIENT_BUFFER;

  // Get handle to the crypto provider
  if (!CryptAcquireContext(&hContext,
                           NULL,
                           NULL,
                           PROV_RSA_FULL,
                           CRYPT_VERIFYCONTEXT))
  {
    status = GetLastError();
    return status;
  }

  if (!CryptCreateHash(hContext, CALG_MD5, 0, 0, &hHash))
  {
    status = GetLastError();
    CryptReleaseContext(hContext, 0);
    return status;
  }

  if (!CryptHashData(hHash, (BYTE*)input, inLength, 0))
  {
    status = GetLastError();
    CryptReleaseContext(hContext, 0);
    CryptDestroyHash(hHash);
    return status;
  }

  if (CryptGetHashParam(hHash, HP_HASHVAL, hash, &hashLen, 0))
  {
    memset(output, 0, *outLength);
    for (DWORD i = 0; i < hashLen; i++)
    {
      _stprintf(pair, _T(“%c%c”), hexDigits[hash[i] >> 4], hexDigits[hash[i] & 0x0F]);
      _tcsncat(output, pair, 2);
    }

    *outLength = (_tcslen(output) + 1) * sizeof(TCHAR);
  }
  else
  {
    status = GetLastError();
  }

  DEBUGMSG(TRUE, (_T(“MD5 hash of %s: %srn”), input, output));

  CryptDestroyHash(hHash);
  CryptReleaseContext(hContext, 0);

  return status;
}


Both of these will probably work on the desktop as well, but I haven’t actually tested there, so YMMV. 


This is also a great example of why I prefer C# for helping keep things simple.  Generally speaking, fewer lines of code means fewer bugs.

Debugging Smart Device MSTEST Unit Tests

If you’ve ever done mstest unit testing with a Smart Device project, then you’re painfully aware of how badly Microsoft dropped the ball on this one.  Debugging a unit test requires making device registry modifications, adding a call to Debugger.Break in your code, then telling Studio to Attach to Remote Process once the breakpoint has been hit.  Seriously, that’s their officially published answer to how you debug a Smart Device unit test!


If you know anything about testing, you know that keeping the cycle time for a test to a minimum.  The longer it takes a developer to go from “start testing” to a break point where they can step, then the less productive they’re going to be.  Even worse, if the process is painful, slow and convoluted (check, check and check for Microsoft’s recommendation), they’re likely to just skip writing tests altogether.


Internally we get around this by using our own test runner which uses Reflection to load up and run tests.  I’ve decided to once again give back to the community and publish this gem as part of Project Resistance (it will get checked in to the IoC Framework as well).


It does not support everything that mstest does, but it’s got enough to get you going, and I think it’s at least reasonably easy to modify if it doesn’t meet your needs.  The currently supported attributes are:



It also might now be obvious how to set it up for your own app.  You need to add a reference to your test assemblies (so VS will deploy them – for some stupid reason you can’t tell it to do so via the Configuration Manager) and make sure all projects are set to deploy to the same place.


As usual, if you have feedback or updates, please let me know.  Submitting a patch right on one of the project portals is probably the easiest way (hint, hint).


It’s probably worth noting here that the code for this is the CFTestRunner project, and you have to pull it from the source tab on the project site (it’s not in the release download yet).

Cannot find secchk.lib in CE project

Today I came back to a project that I hadn’t worked on in a few months.  I got latest from source control and tried to build, and the linker complained “Error 1 fatal error LNK1104: cannot open file ‘secchk.lib’ AudioControl AudioControl”.


Now WTF?  I knew this was working before.  The error remindinded me vaguely of a previous problem I had, so I searched my blog and found this one.  The problem that time was that I was building for PPC03 and the project for some reason had not added secchk.lib to my platform.  THis time, however, I am building for a generic Windows CE device, and for some reason it *is* adding it.  Well CE doesn’t have this lib, and I don’t want it.  A quick scan through the project settings didn’t turn up anything obvious, so I went with the more heavy-handed approach of adding the following to a common header file:


#pragma comment(linker, “/nodefaultlib:secchk.lib”)
#pragma comment(linker, “/nodefaultlib:ccrtrtti.lib”)


Done and done.  (the second is required because when you add the first and rebuild, the linker will then complain about not finding ccrtrtti.lib). No idea why I get the error (I’d love to know the root cause) but I don’t have time to dig around.  Fortunately the #pragma directive overrides whatever project settings you have, which can be handy at times.