OdeToCode IC Logo

No Love from Word

Wednesday, June 7, 2006 by scott

One of the features in Word 2007 Beta 2 is the ability to author blog posts. Joe Friend announced the feature, and there have been a number of follow up posts:
  Word Blog HTML
  Quality XHTML In Word 2007's blogging tool
  Word XHTML - Compliance and Styles

Authoring blog posts from Word is a great feature, but I hope Word 2007 will also include the ability to export any document into a clean, trim XHTML file. A tall order, I'm sure, but I'd take this feature over PDF export any day of the week (and twice on Sundays).

I tried to setup a blog account in Word 2007, but the following dialog popped up during configuration.

This dialog resisted every attempt at closing. I tried to enter good credentials and bad credentials. I clicked OK. I clicked Cancel. I tried Alt+F4. I clicked the big X. I clicked rapidly, and I clicked slowly. I said kind words, and bad words - but all for naught. The dialog remained front and center on my screen, like a bad movie. Like War of the Worlds, to be exact. I had no choice but to kill word.exe. Bummer.

Then I came back to my trusty copy of Word 2003, which, knowing its days are numbered must have been feeling spurned and emotional. Never before have I seen the dialog it presented.

Ok, I tried a newer version. Did it really have to insult my grammar?

.NET 3.0

Tuesday, June 6, 2006 by scott

General

Tuesday, June 6, 2006 by scott

ASP.NET Articles

Tuesday, June 6, 2006 by scott

SQL Server Reporting Services Articles

Tuesday, June 6, 2006 by scott

SQL Server Articles

Tuesday, June 6, 2006 by scott

Part II of How To Screw Up HandleExternalEvent Activities in Windows Workflow

Sunday, June 4, 2006 by scott

We left off in Part I with a contract, an event args class, and all the metadata we need to raise events to a workflow. As Captain Hazelwood once said, what could possibly go wrong?

Let's start with an implementation of our payment processing contract. The code has a subtle problem, and will cause an exception.

class PaymentProcessingService : IPaymentProcessingService
{
    
public void ProcessPayment(Guid id, double amount)
    {
        
// ... do some work work work
        
        
// ... then raise an event to let everyone know
        PaymentProcessedEventArgs args;
        args =
new PaymentProcessedEventArgs(id, amount);  
      
        
EventHandler<PaymentProcessedEventArgs> evh;
        evh = PaymentProcessed;
        
if (evh != null)
            evh(
this,  args); // boom!
    }

    
public event EventHandler<PaymentProcessedEventArgs>
        PaymentProcessed;
}

The exception is an EventDeliveryFailedException, and the Message property will read like "Event PaymentProcessed on interface type IPaymentProcessingService for instance ID [GUID] cannot be delivered". The message doesn't yield any obvious clues, and we need to dig deeper to find more information.

If we look at the InnerException property, we get closer to an answer. The inner exception is an InvalidOperationException with the Message of "EventArgs not serializable". This exception is a little confusing, because we did make our EventArgs serializable! Notice in the dialog below, the current exception ($exception in the 2005 debugger) is wrapping yet another inner exception, with the precise cause of failure.

Debugger watch window on serialization exception

The value of the message is curt off, but says "Type PaymentProcessingService is not marked as serializable". It appears every parameter going into the event must be serializable, including the sender parameter. We pass a this reference, which points to our payment processing service. The workflow instance doesn't actually need a reference to our payment service (if it needs to invoke a method on the service, it can use the CallExternalEventActivity), so we can fix this problem by leaving the sender parameter as null or Nothing.

EventHandler<PaymentProcessedEventArgs> evh;
evh = PaymentProcessed;
if (evh != null)
    evh(
null,  args);

If you are seeing an event delivery failure, drill into the inner exceptions to find the exact type causing the problem. You event args might contain an object graph with an non-serializable type inside.

Setting Up The Workflow Runtime

I did jump ahead just a bit, because before the event will even throw an exception we need to configure the workflow runtime to handle events from our service. We do this by layering the ExternalDataExchangeService into the workflow runtime. The ExternalDataExchangeService manages the host's local communication services, like our payment processing service.

WorkflowRuntime workflowRuntime = new WorkflowRuntime();

ExternalDataExchangeService dataExchangeService;
dataExchangeService =
new ExternalDataExchangeService();
workflowRuntime.AddService(dataExchangeService);

PaymentProcessingService paymentProcessing;
paymentProcessing =
new PaymentProcessingService();
dataExchangeService.AddService(paymentProcessing);

// ...

Here is a version of the above code with a bug that took me quite some time to track down.

PaymentProcessingService paymentProcessing;
paymentProcessing =
new PaymentProcessingService();
workflowRuntime.AddService(paymentProcessing);
// this is WRONG!

We have to add our service to the ExternalDataExchangeService, and not directly to the workflow runtime. My service would fire an event, but nothing would happen. Looking at the event in the debugger showed a null value, meaning nobody was subscribing to the event. It's the ExternalDataExchangeService that reflects on the incoming service, looking for metadata like the ExternalDataExchange attribute, and subscribing to events.

Summary

Raising events to a workflow can be finicky. If the events appear to fire into the empty vacuum of space, make sure the ExternalDataExchanceService and the local communications services are properly configured and added to the runtime. If the workflow runtime is throwing exceptions, the exception will most likely wrap the juicy details in its InnerException property (which in turn might have an InnerException). Hopefully, these two tips can save someone some time.