Thursday, February 12, 2009

Licensing Components in .NET - Part 2

I reckon the only reason LicFileLicenseProvider is part of the framework is to get the point across the licensing greenhorns. All of a sudden, brains start ticking: you can load the license from anywhere. You begin crafting nefarious schemes using public key cryptography. It's brilliantly academic. But something's wrong. It would be easier just to hack the code. Steal the intellectual property. Hey, maybe that's why Microsoft stopped at the LicFileLicenseProvider? Maybe, and here's a thought: maybe they should have.

Another crazy piece of the (even crazier) licensing puzzle: licenses.licx files and lc.exe.

lc.exe is a tool written in .NET by Microsoft, which is used transparently by msbuild when your Visual Studio projects are compiling. Looking inside the assembly's resource strings, we discover it:

Generates a .NET Licenses file and adds it to the manifest of the given assembly
Usage:
lc /target:TargetAssembly /complist:filename [/outdir:path] [/i:modules] [/v] [/nologo]

Options:
/target: Target assembly for the generated licenses file
/complist: Licensed component list file
/outdir: Output directory for the generated licenses file
/i: Specify modules to load
/v Verbose output
/nologo Suppress the display of the startup banner

The entry point into this assembly is the Main method of the System.Tools.LicenseCompiler class. Of (arguably) most importance is the /target: switch. This is the name of the assembly into which the compiled resource will be added. In Elsie.Target.exe this would be a resource named "Elsie.Target.exe.licenses", containing a binary stream representation of a serialized .NET object. More to come...

If you add a an empty text file named "licenses.licx" to your project, Visual Studio automatically sets its BuildAction:EmbeddedResource and CopyToOutput:DoNotCopy. It also calls lc.exe before calling csc.exe (yes, I'm a C#-a-holic). It makes the decision based on the .licx extension and you can have as many .licx files as you want in a single project (ok, that may not be true, but why would you want that many? Anyway, it will generate one /complist:[filename.licx] for each licx file in your project)

So what do you type in this/these text file(s)? If you really care, we'll have to make a 3rd installment.

Licensing Components in .NET - Part 1

I'm going to wear three hats here: the fantastic component developer who's written a third party library, and the poor sod who has to make the aforementioned fantastic component (not developer) work with the existing build environment. Finally, I'll be that guy who has to run this application and explain to his boss why it's suddenly stopped working.

Unsurprisingly, Microsoft do have a how-to for developing licensed components. There's also a nice diagram here. Some of the information presented in these tutorials is a little misleading, so I figured I'd get back to trying on my hats.

Hat 1

I've developed the one component that could change the world. That's a little c, not big C for component. There's no requirement for my class to fit into Microsoft's ComponentModel namespace (which is, incidentally, where all the bog standard licensing classes reside). So, I apply the [LicenseProvider(typeof(LicFileLicenseProvider))] to my fantastic class, and somewhere in the class I make a call to the static method LicenseManager.Validate(passing in the type of my class, as this is how the manager figures out that I wanted it to use the LicFileLicenseProvider). There are two overloads for Validate:
a) public static License Validate(Type type, object instance)
b) public static void Validate(Type type)
Option #1 offers the fullest functionality and it makes sense to make the call in my class's constructor - after all, the error message (of the LicenseException that's thrown if the call to Validate fails for some reason) WANTS me to do this: "An instance of type 'Elsie.Proprietary.Fantastic' was being created, and a valid license could not be granted for the type 'Elsie.Proprietary.Fantastic'. Please, contact the manufacturer of the component for more information." Because the call to Validate returns a new disposable License object, I'm responsible at least for ensuring it gets cleaned up properly. I'll assign it to an instance field, and make my class implement IDisposable.
Option #2 is a little less messy - I don't have to pass in an instance of my class, I don't have to worry about managing a Licence object's lifetime. "A valid license cannot be granted for the type Elsie.Proprietary.Fantastic. Contact the manufacturer of the component for more information."
That's it. I don't even have to create a license file.

Hat 2

I'm going to use the Fantastic class, so I mock up a new project of my own (which I call Elsie.Target.exe) and I add an assembly reference to it. Then I create (probably in notepad2.exe) a one line txt file: inside it I type "Elsie.Propietary.Fantastic is a licensed component". I make sure the file is called "Elsie.Propietary.Fantastic.lic" and I make sure it's copied to my working directory (probably by setting BuildAction:Content, and CopyToOutput:CopyAlways). Inside my application, I call the Fantastic constructor (within a using statement, because the class implements IDisposable, because the component deveoper was a responsible guy after all). Hidden inside the constructor, Fantastic checks if I'm allowed to use it by loading the .lic file. If the checks are successful, I go on my way to being a superstar developer. Otherwise, an exception will be thrown and it's back to the streets for me!

Hat 3

I'm in the London office at 7am. I deployed Elsie.Target.exe, along with Elsie.Propietary.dll and Elsie.Propietary.Fantastic.lic last night. While I've been sleeping, everyone in APAC has been delighted with just how fantastic the application is. In my excitement, I forget about being a cheeky monkey and changing the .lic file contents to read "... is *not* a licensed component". This is lucky for me, because it would BREAK the application!

Other examples:
Good: "Elsie.Proprietary.Fantastic is a licensed component."
Good: "Elsie.Proprietary.Fantastic is a licensed component. Yes it is!"
Good: "Elsie.Proprietary.Fantastic is a licensed component. NOT!"
Bad: "Elsie.Proprietary.Fantastic is a licensed component"
Bad: "Elsie.Proprietary.Fantastic is not a licensed component"
It turns out that the Key is valid if the text in the file starts with the fully qualified type name followed by " is a licensed component."

This is crazy! So crazy, in fact, that it might just work...

Wednesday, December 03, 2008

C# Verbatim Identifier

I've known for a long time that C# identifiers could have an optional @ sign in front of them, but until recently I thought that the character became part of the identifier.

int foo = 3;
int @foo = 4; //<-- error here: identifier 'foo' already in scope

So, it's really a way to call into code that may have been written in another CLR language that has identifiers that clash with C#'s reserved and keywords.

By way of example, to call the following VB.NET function

Public Class VerbatimIdentifierTest
Public Shared Function int(ByVal value As Double) As Integer
Return Convert.ToInt32(value)
End Function
End Class

from C# you'd invoke:

VerbatimIdentifierTest.@int(1.0);

Saturday, October 25, 2008

Amazon EC2

Amazon's Elastic Cloud Compute (EC2) is a nice idea, but it's important not to overlook the "elastic" in the name. If there's an obvious temporal pattern to your service's usage (examples: 1. it could easily consume 100% CPU of 5 machines between 7 am and 11 am, but slow to a dawdle for the rest of the day, or 2. a large system could lie unused on weekends and holidays), and you design your application with parallelism in mind, this could be a cost effective strategy for hosting the service. As they bill you per "instance-hour" (loosely defined as an "instance" available for all - or part of - an hour), an available _idle_ instance will cost the same in a month as an available _fully-utilised_ instance. Costs start to fall as soon as you turn off your under-utilised instances dynamicall; which is something you can't yet do when renting physical space in a data center (AFAIK).

For small business without the need for elasticity (note: does that spell "unsuccessful"?), I'm not convinced it would be as cost effective as renting some tin in a data center and running multiple virtualized instances on some technology like Hyper-V, ESX or Xen.

Wednesday, October 22, 2008

Assert.That(actual, Is.EqualTo(expected).Within(tolerance))

New to NUnit 2.4 back in March 2007:

using NUnit.Framework.SyntaxHelpers;

I really like this idea as it can make some unit test code much more legible!

Saturday, October 18, 2008

Language Optimization Observations

Progamming languages are becoming prolific. Most recently the trend of domain specific languages has emerged. On any given project, multiple languages will be employed (to varying degrees of success). Their design can be influenced by many factors. Some - if not most - languages enable one to succinctly and elegantly define a solution to a problem, speeding up initial development and subsequent modifications. However, as the languages get more abstract they appear to get slower and slower to run. Maybe it's that they're solving bigger problems? Maybe it's that their runtime environments have been designed for the general case, and literally "karpullions" of CPU cycles are being wasted maintaining state in which we [for a given application] are not interested. The smart money is on over-generalized and sometimes poorly implemented runtime environments.

Thursday, October 16, 2008

Duct Typing

defn: A portmanteau of duck-typing and duct-taping. Used to describe the effects of defining .NET extension methods in your own code to give existing objects "new" functionality.

Closures in C#

If I'm using the wrong terminology: sorry; you lose. The C# compiler is a wonderful thing. Among it's many gifts to us are iterator blocks (think: yield return) and anonymous methods (think: delegate literals). Neither of these constructs has a direct parallel in MSIL, the common intermediate language to which all C# code is compiled. Instead the C# compiler defines new types to encapsulate their behaviours.

Iterator Blocks

An iterator block becomes a state machine; a disposable enumerator. Code defined in C# as this:

public static IEnumerable PublicEnumerable()
{
yield return 1;
yield return 2;
yield return 3;
}

is compiled down to the MSIL equivalent of this:

public static IEnumerable PublicEnumerable()
{
return new d__0(-2);
}

The class d__0 is a closure, and if we take a peek in Reflector we see the following definition:

[CompilerGenerated]
private sealed class d__0 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable
{
// Fields
private int <>1__state;
private int <>2__current;

// Methods
[DebuggerHidden]
public d__0(int <>1__state);
private bool MoveNext();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator();
[DebuggerHidden]
void IEnumerator.Reset();
void IDisposable.Dispose();

// Properties
int IEnumerator.Current { [DebuggerHidden] get; }
object IEnumerator.Current { [DebuggerHidden] get; }
}

The object instance can be returned to the caller of PublicEnumerable where it will behave like the iterator block we know and love.

Anonymous Methods

Anonymous methods can access local variables defined in the same scope. When a delegate literal contains a reference to a method local variable (see: i in the following example):

public static void DelegateLiteralTest()
{
Int32 i = 5;
Action add = delegate(Int32 value) { i += value; };
Console.WriteLine(i);
add(1);
Console.WriteLine(i);
add(2);
Console.WriteLine(i);
}

the C# compiler generates a new type (closure) that resembles the following:

[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public int i;

// Methods
public <>c__DisplayClass1();
public void b__0(int value);
}

Notice how the local variable is now scoped to the instance of the closure, and how it's an instance of this closure that's used inside the method:

public static void DelegateLiteralTest()
{
<>c__DisplayClass1 __closure = new <>c__DisplayClass1();
__closure.i = 5;
Console.WriteLine(__closure.i);
__cloure.b__0(1);
Console.WriteLine(__closure.i);
__closure.b__0(2);
Console.WriteLine(__closure.i);
}

I think this is pretty darn cool.

Sunday, October 12, 2008

Singleton Pattern vs. C# Static Class

At the most basic level, both constructs are useful in limiting the number of instances of an object that can be created. There are numerous subtle differences at the implementation level, but the single biggest difference is at an object-oriented level: static classes do not support polymorphism. This stems from a combination of factors at the language level; in short, a static class cannot inherit from a base class (abstract or concrete) nor can it implement any interface methods. Conversely, you can neither define static virtual methods on a class, nor can you define them on an interface. So, if you're ever using dependency injection [insert link here] - a technique that relies heavily on polymorphism - you will likely find that static classes will be inadequate, limiting, and plain frustrating.

There are a number of other runtime level differences, but these are all secondary to the polymorphism issue. The simplest is that a static class can never be instantiated, and all methods are executed against the type instance. Contrast this with the singleton, of which exactly one instance is created, and where methods are executed against that instance.

Next time you're involved in object design and you come across a potential application for the singleton pattern, don't forget this posting!

Monday, October 06, 2008

Delegate vs. Event

What's the difference between a delegate and an event in C#, you ask? An event provides better encapsulation (read: a more object orientated approach) while a delegate is "merely" a immutable multicast type-safe function pointer. In other words, an event is an encapsulated delegate, where only the += and -= functions are non-private. That means that although we can add new subscribers to the event from another class, we cannot invoke it. The add and remove accessors are the only members to retain the event's declared access (e.g. public).

It's easier to explain delegates first.

Delegates


  • Function pointer: a delegate acts as a layer of indirection allowing references to methods to be passed between objects. The target methods are invoked when the delegate is invoked.
  • Type-safe: unlike function pointers in C++, a delegate knows the type of the target object, and the signature of the target method.
  • Multicast: a delegate maintains a list of methods; it isn't limited to invoking just one
  • Immutable: delegate instances - like strings - cannot be changed. When you add/remove another target method to/from a delegate instance, you're really creating a completely new delegate instance with a superset/subset of target methods.

    A delegate example


    Define the delegate type, or use one of the many predefined types shipped with the FCL.
    public delegate void Function(string value);
    Define the target methods that we can call. These will have to conform to the signature of the chosen delegate type.

    static void WriteOut(string value)
    {
    Console.Out.WriteLine(value);
    }

    static void WriteError(object value)
    {
    Console.Error.WriteLine(value);
    }

    Define a class with a public delegate field (bad object-oriented encapsulation, but it's just for the purpose of example).

    class DF
    {
    public Function function;
    }

    Construct a new instance of this class, add a couple of target methods and invoke them through the delegate:

    DF df = new DF();
    df.function += new Function(WriteOut);
    df.function += new Function(WriteError);
    df.function("Delegate");


    Events


    An event is a construct that is functionally similar to a delegate, but provides more encapsulation. Specifically, adding/removing target methods is given the accessibility of the event (e.g. public/protected/internal). Everything else is given the accessibility modifier of "private". This allows subscribers to be notified of an event, but not to trigger it themselves.

    An event example


    Again, pick a delegate type from the FCL or define your own. For this example, I will reuse the Function delegate type, and the two Function methods defined above.
    Define a class with an Event:

    class EF
    {
    public event Function function;
    }

    Construct a new instance of this class, add a couple of target methods:

    EF ef = new EF();
    ef.function += new Function(WriteOut);
    ef.function += new Function(WriteError);

    Because of the encapsulation, we cannot call the following method from outside the declaring type:

    //ef.function("Event"); // ... can only appear on the left hand side of += or -= (unless used from within the event's declaring type)


    A good technical resource:
    Difference between a delegate and an event.
  • Wednesday, October 01, 2008

    Indispensable Software Engineering Tools for .NET

    Requirements
    =
    JIRA: rudimentary but ok, not so great for managing project plans
    -
    Source Control
    =
    Subversion: all check-ins must have a reference back to the original requirement.
    Everything included in the release must be held in the versioned repository.
    Everything means everything... environment specific config files.
    (Don't store usernames and passwords in source control, but that's ok because you didn't hard code them into your configuration anyway, did you!?)
    -
    Build
    =
    CruiseControl.NET:
    > get latest from source control, tag so that the build can be repeated.
    > ensure compiled assemblies are versioned so they can be traced back to the build.
    > build, run unit tests and other code metric tools.
    Code Metric Tools:
    >FxCop
    >NUnit
    >NCover
    >anything for cyclomatic complexity?
    Post Build
    =
    FishEye: great for seeing all metrics, committed changes etc. Useful when you need to see what changes went into a build.
    -

    Friday, September 26, 2008

    Pulsed Threads

    It happened quite a while back, but I thought I'd put a note up here as a reminder: a pulsed thread is still idle and another thread could acquire the lock. This blog entry is not about how best to re-write the producer/consumer problem as an event-driven model, it's to show how naive multi-threaded operations are - among other things - low hanging fruit when it comes to finding bugs...

    Imagine a group of producer threads [P0..Pn] and a group of consumer threads [C0..Cn] writing to and reading from a queue of arbitrary size. Access to the queue needs to be thread safe, but the consumer threads could conceivably take a long time to process the items they consume from the queue. It is an error to attempt to read from an empty queue. For this example, we do not consider throttling the producers.

    For a first (naive) attempt in C#:

    01: // consumer
    02: while (running)
    03: {
    04: Int64 value;
    05: lock (queue)
    06: {
    07: if (queue.Count == 0)
    08: {
    09: Monitor.Wait(queue);
    10: }
    11: value = queue.Dequeue();
    12: }
    13: Console.WriteLine("{0}:{1}", threadId, value);
    14: }

    01: // producer
    02: while (running)
    03: {
    04: lock (queue)
    05: {
    06: Int64 item = Interlocked.Increment(ref seed);
    07: queue.Enqueue(item);
    08: Console.WriteLine("{0}:{1}", threadId, item);
    09: Monitor.Pulse(queue);
    10: }
    11: }

    The result? After several thousand iterations we get a "Queue empty" exception on line 10 of the consumer. Puzzled we look a little closer: it's not immediately obvious. Of the 5 consumer threads (call it C1), one has acquired the lock, tested the size of the queue and put itself in a wait state until a producer thread (call it P1) has added an item. P1 pulses the queue to signal an item has been added. An extract from MSDN says:
    When the thread that invoked Pulse releases the lock, the next thread in the ready queue (which is not necessarily the thread that was pulsed) acquires the lock.
    So, while we were expecting C1 (waiting on the monitor) to acquire the lock, we were wrong. C0 was next in the queue for the lock.

    To get the application running correctly, we'd need to change line 6 of the consumer to:

    06: while (queue.Count == 0)

    Kids, always read the label carefully!

    Sunday, September 14, 2008

    Coupling: Interfaces and Abstract Classes

    When "loosely coupled" is la mode du jour, nobody really likes an abstract class. Well nobody on my team. I suspect there's more going on here than the simple increase in coupling (e.g. the number of assumptions a caller must make) over calling an interface method.

    Deriving from an abstract class:
    1) forces you to inherit from an object hierarchy, and
    2) forces you to provide an implementation for a set of abstract methods, and
    3) usually means that some behaviour is "thrown in for free" in the base class(es).

    Implementing an interface
    1) forces you to provide an implementation for a set of method signatures.

    So... use more interfaces; achieve a lower level of coupling, and you'll also implicitly be choosing composition over inheritance.

    Friday, August 08, 2008

    Exceptions

    If only the System.Exception class were abstract or only contained protected constructors... This would force people to define their own custom exceptions (a good thing), but still wouldn't solve humankind's insatiable appetite for the catch-all exception handler.

    Wednesday, August 06, 2008

    System.OutOfMemoryException Part 2

    We've been seeing loads of these recently, not as a result of memory leaks (the usual suspects) but the result of trying to do too much at one time. Now that 64-bit operating systems are here, couldn't we take advantage of the extra virtual address space? Microsoft have a comparison of 32-bit and 64-bit memory architecture for 64-bit editions of Windows XP and Windows Server 2003
    outlining the changes between the two memory architectures. Next step is to see whether the 64-bit operating systems are approved.

    UnhandledException

    From .NET 2.0 onwards, unhandled exceptions on any thread cause the process to terminate. This is - according to a number of experts - the "correct" behaviour, and is different to how they were handled back in .NET 1.1.

    The AppDomain.UnhandledException event allows you to perform some kind of diagnostics (such as logging the exception and stack trace, or performing a mini-dump) after such an exception is thrown, but the CLR is going to exit - whether you like it or not - just as soon as all event handlers have run.

    So... if you are a responsible application developer who is spawning new threads (or queueing user work items to the thread pool) please please please ensure that _if_ you can handle the exception, that it is caught and not rethrown. Even if it means storing the exception and letting another thread handle it, as in the case of the asynchronous programming model (APM).

    Wednesday, June 25, 2008

    Goodbye to Awkward Dictionaries in .NET

    I take enjoyment in analytic work, and often find myself writing code to empirically test a bunch of XML files to see if I've got the "implied schema" right. For example, I might have 500 files of similar structure, and I'd want to see the possible values (and count) of the "/Reports/Globals/@Client" node's value (using XPath). Usually this would entail using a Dictionary and every access would have to test if the dictionary already contains the key ... until now. After seeing that Ruby's designers allow you to specify a default value for a Hash, I took another step forward, allowing a user of the class to provide a default function, allowing more complex defaults such as empty lists.

    using System;
    using System.Collections.Generic;

    namespace ConsoleApplication6
    {
    class Program
    {
    public delegate T DefaultFunction<T>();

    public class RubyDictionary<TKey, TValue>
    {
    private IDictionary<TKey, TValue> inner = new Dictionary<TKey, TValue>();
    private DefaultFunction<TValue> defaultFunction;

    private static TValue Default()
    {
    return default(TValue);
    }

    public RubyDictionary()
    {
    defaultFunction = Default;
    }

    public RubyDictionary(TValue defaultValue)
    {
    defaultFunction = delegate()
    {
    return defaultValue;
    };
    }

    public RubyDictionary(DefaultFunction<TValue> defaultFunction)
    {
    this.defaultFunction = defaultFunction;
    }

    public TValue this[TKey key]
    {
    get
    {
    if (!inner.ContainsKey(key))
    {
    inner.Add(key, defaultFunction());
    }
    return inner[key];
    }
    set
    {
    inner[key] = value;
    }
    }
    }

    static void Main(string[] args)
    {
    try
    {
    RubyDictionary<string, int> dict = new RubyDictionary<string, int>();
    Console.WriteLine(dict["9"]);
    dict["8"] += 1;
    Console.WriteLine(dict["8"]);
    dict["7"] = 5;
    Console.WriteLine(dict["7"]);
    dict["6"]++;
    Console.WriteLine(dict["6"]);

    dict["5"]--; dict["5"]--;
    Console.WriteLine(dict["5"]);


    RubyDictionary<string, IList<string>> lists = new RubyDictionary<string, IList<string>>(delegate() { return new List<string>(); });
    lists["shopping"].Add("bread");
    lists["shopping"].Add("milk");
    lists["todo"].Remove("write this dictionary class");

    foreach (string item in lists["shopping"])
    {
    Console.WriteLine(item);
    }

    RubyDictionary<int, int> foo = new RubyDictionary<int, int>(777);
    Console.WriteLine(foo[8] - 111);
    }
    catch (Exception exception)
    {
    Console.Error.WriteLine(exception);
    }
    finally
    {
    if (System.Diagnostics.Debugger.IsAttached)
    {
    Console.WriteLine("Press enter to continue...");
    Console.ReadLine();
    }
    }
    }
    }
    }

    Saturday, May 31, 2008

    IEnumerable vs IEnumerator

    IEnumerator<T> generic interface defines "cursor" methods for iterating over a collection:
    T Current { get; }
    bool MoveNext();
    void Reset();
    void Dispose();

    IEnumerable<T> generic interface defines just one method:
    IEnumerator<T> GetEnumerator();

    C# supports iterator blocks. These are blocks of code (i.e. method or property getter blocks) in which you "yield" a return value at various points in the code. The compiler uses hidden magic to create a nested type that implements both IEnumerable<T> and IEnumerator<T>; the body of your original method is stubbed out to call into this new object, and all your original logic is implemented in the IEnumerator<T>'s MoveNext method.

    The C# foreach keyword is quite flexible and operates on any object that exposes a public GetEnumerator() method (the 'collection' pattern) - the object doesn't have to implement IEnumerable or IEnumerable<T>.

    Thursday, May 29, 2008

    Experiment in the Fidelity of UTC DateTime

    // make sure your local clock is not displaying GMT

    DateTime a = new DateTime(2008, 7, 6, 4, 3, 2, DateTimeKind.Utc);
    DateTime b = new DateTime(2008, 7, 6, 4, 3, 2, DateTimeKind.Local);
    DateTime c = new DateTime(2008, 7, 6, 4, 3, 2, DateTimeKind.Unspecified);

    // no surprises here...
    Console.WriteLine(a);
    Console.WriteLine(b);
    Console.WriteLine(c);

    // and again, no surprises. this makes it that much more explicit.
    Console.WriteLine("-");
    Console.WriteLine(a.ToUniversalTime());
    Console.WriteLine(b.ToUniversalTime());
    Console.WriteLine(c.ToUniversalTime());

    // notice the Z(ulu) for UTC, the time zone for local, and the (nothing) for unspecified
    Console.WriteLine("-");
    XmlSerialize(a);
    XmlSerialize(b);
    XmlSerialize(c);

    // ooh! our first loss of fidelity
    Console.WriteLine("-");
    DataSet dataSet = new DataSet();
    DataTable dataTable = dataSet.Tables.Add();
    DataColumn dataColumn = dataTable.Columns.Add("DateTime", typeof(DateTime));
    dataTable.Rows.Add(a);
    dataTable.Rows.Add(b);
    dataTable.Rows.Add(c);
    Console.WriteLine(dataSet.GetXml());

    // those .net framework guys fight back with the DateTimeMode property (new in 2.0)
    // understandably, weird stuff starts happening when not all input dates are of the same kind
    Console.WriteLine("-");
    DataSet dataSet2 = new DataSet();
    DataTable dataTable2 = dataSet2.Tables.Add();
    DataColumn dataColumn2 = dataTable2.Columns.Add("DateTime", typeof(DateTime));
    dataColumn2.DateTimeMode = DataSetDateTime.Utc;
    dataTable2.Rows.Add(a);
    dataTable2.Rows.Add(b);
    dataTable2.Rows.Add(c);
    Console.WriteLine(dataSet2.GetXml());

    //you can't just get the bytes for a DateTime, it's a struct.
    //Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(a)));
    //Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(b)));
    //Console.WriteLine(BitConverter.ToString(BitConverter.GetBytes(c)));

    Console.ReadKey();

    Tuesday, May 27, 2008

    Flex interaction with JavaScript (and ASP.NET)

    It just doesn't quite work out of the box.

    First, a flash object within an HTML form element fails when the flash runtime tries to register the callbacks you defined in your Flex app (e.g. a callback added in ActionScript using ExternalInterface.addCallback("someFunction", someFunctionPointer) is evaluated in the browser as __flash__addCallback(objectId, "someFunction"). A hack to get around this would be to assign your Flash object to window.objectId as soon as you've created it: hey presto! you've worked around one limitation.

    Second. There appears to be a race condition if you need to initialize your Flex app with JavaScript data as soon as both are ready. At first, I thought I had a choice... push from JavaScript or pull from Flex. To push, we need to know that the callback has been added in the browser: either by polling or by registering an event. I refuse to poll, and I can't find a way to register for the event. The remaining option is to pull. The question is, will the source data (JavaScript) have been assigned by the time my Flex Application's applicationComplete event has fired? No idea, really.