Basic, single-threaded implementation of SynchronizationContext
You don't need to implement Dispose pattern if you have no unmanaged resources to dispose. Unless you get paid per line of code, that is :)
You should not dispose objects you did not create (unless you implement a wrapper). In your case, you should not dispose reset event in WorkItem class, since you did not create it there. Instead, you should use

using(var reset = new ManualResetEventSlim())
{
    _workItems.Enqueue (new WorkItem (d, state, reset));
    ...
}

this code:

while (true) 
{
    synchronizationContext.Send(_ =&gt; Console.WriteLine("Hello!"), null)

    while (synchronizationContext.HasWorkItems) {
        synchronizationContext.ExecuteWorkItem ();
    }
}


will hang your application. You should either remove resetEvent from Send method and simply execute all queued WorkItems there (bad approach) or move this loop:

    while (synchronizationContext.HasWorkItems) {
        synchronizationContext.ExecuteWorkItem ();
    }


to sepatrate thread (better approach). Might look like:

public sealed class CustomSynchronizationContext : SynchronizationContext, IDisposable
{
     public CustomSynchronizationContext()
     {
         _thread = new Thread(() =&gt; 
               {
                   while(true)
                   {
                       _itemQueuedEvent.WaitOne();
                       if (_disposed) return;
                       ExecuteWorkItem();
                   }
               });
         _thread.Start();
     }

 ........
}


You will probably encounter some synchronization issues, but nothing that can not be solved.