Sunday, 3 February 2013

Accessing UI from a background thread in WinForms and WPF/Silverlight

Introduction

Updating UI in both WinForms and WPF/Silverlight can only be performed from the UI thread. Any attempt to update visual controls from a background thread will fail with UnauthorizedAccessException. Also an attempt to update an object bound to visual control will fail, if it results in updating a visual control.

Unfortunately scenarios where it's necessary to update UI from another thread are quite common. For example accessing a Web Service asynchronously relies on a callback methods which execute in the background. Note that in case of Silverlight, asynchronous is the only way to access a Web Service.

Example

Let's use Echo Web Service Demo at http://www.aspspider.info/burzyn/echoservice.svc as an example. Assuming that its service reference has been added with Generate Asynchronous operations option enabled, the following two methods are present in IEchoService interface

IAsyncResult BeginDescription(System.AsyncCallback callback, object asyncState);
string EndDescription(System.IAsyncResult result);

They are equivalent to a synchronous function with the signature of:
string Description()

Typically asynchronous calls to a Web Service use callbacks to complete the operation, as illustrated below.

IEchoService echoService;
void Init() 
{   ...
    echoService.BeginDescription(DescriptionCallback, echoService);
    ...
}
void DescriptionCallback(IAsyncResult ar)
{            
    string description = (ar.AsyncState as IEchoService).EndDescription(ar);
    // this will fail with UnauthorizedAccessException
    lblDescription.Content = description;
}

This fails because DescriptionCallback is trying to access UI while executing in a background thread.

Solution

In case of WPF/Silverlight, it can be resolved by using the System.Windows.Threading.Dispatcher namespace. Every control class has the Dispatcher property, which can be used to render changes from any thread. Using the Dispatcher property of the current Page, DescriptionCallback needs to be modified as follows.

private delegate void DispatchHandler(string arg);

private void UpdateDescription(string description)
{
    lblDescription.Content = description;
}

void DescriptionCallback(IAsyncResult ar)
{            
    string description = (ar.AsyncState as IEchoService).EndDescription(ar);
    Dispatcher dispatcher = this.Dispatcher;
    DispatchHandler handler = new DispatchHandler(UpdateDescription);  
    Object[] args = { description };
    this.Dispatcher.BeginInvoke(handler, args);
}



BeginInvoke takes two parameters - delegate to be executed in the UI thread and array of objects used as parameters of the delegate. Parameters are matched with object in the array by position. This is rather error prone solution - there's no type safety and changes to the number or type of the parameters might break the code. It can be done in much safer and clearer way by using an anonymous method.
 
private delegate void DispatchHandler();
echoService.BeginEchoString(txtString.Text, delegate(IAsyncResult ar) 
{
    Dispatcher dispatcher = this.Dispatcher;
    DispatchHandler handler = delegate()
            {
                lblDescription.Content = echoService.EndEchoString(ar);
            };
    dispatcher.BeginInvoke(handler, null);                                 
}, null);

Note that it's also shorter and more readable. Of course, it can be even further shortened by nesting anonymous methods.

{
   echoService.BeginDescription(delegate(IAsyncResult ar)
           {
               string description = echoService.EndDescription(ar);
               DispatchHandler handler = new DispatchHandler(delegate()
               {
                  lblDescription.Content = description;
               });
               this.Dispatcher.BeginInvoke(handler, null); 
           }  
   null);      
}

How about WinForms?


Although the WinForms controls don't have the Dispatcher property, they expose Invoke and BeginInvoke methods, which can be used in the same manner.

 echoService.BeginEchoString(txtString.Text, delegate(IAsyncResult ar) 

 {
    string description = echoService.EndDescription(ar);

    DispatchHandler handler = delegate()
        {
            lblDescription.Text = description;
        };
        this.BeginInvoke(handler);
 }

Additionally WinForms controls expose InvokeRequired property to determine if a call in on a different thread than the control, essentially meaning if the control can be access directly. You can take advantage of the InvokeRequired property using the like below.

    if (lblDescription.InvokeRequired)
    {
        DispatchHandler handler = delegate()
            {
                lblDescription.Text = description;
            };
            this.BeginInvoke(handler);
    }
    else
       lblDescription.Text = description;

Summary

Although updating UI from background might look like an issue, but it's fairly easy to find the way around it. Using anonymous methods allows for clearer and less error prone code.

Quick note on Delphi. Within VCL, access to UI from background thread is not thread safe, but it's not blocked. It's up to a developer to resolve any potential problems. It's recommended to wrap any code executed in a background thread in the Synchronize procedure of the TThread class.



Monday, 21 January 2013

Interoperable WCF

Introduction

Web Services have been designed for interoperability, so you would expect that clients and servers created  using different tools interoperate smoothly. In reality it's far from being that simple. Multiple and evolving standards and features created the situation where no two systems are the same. It led to creation of Web Services Interoperability Organization with the purpose to establish best practices for interoperability for select group of standards. Further information can be found at http://www.ws-i.org

WS-I Basic Profile

As the name suggests,WS-I Basic Profile defines minimal set of standards a Web Service should support. Unfortunately, the Basic Profile specification has number of versions which are not necessarily interoperable. For example, system compliant with WS-I version 1.0 might not be able to interoperate with a system compliant with WS-I version 2.0.

Windows Communication Foundation supports WS-I with BasicHttpBinding which conforms to WS-I version 1.1. Basically, it means compliance with SOAP 1.1, WSDL 1.2 and UDDI 2.0. It is also understood that internet standards like XML Schema, SSL, X.509 etc are also supported.

Delphi client

Although Delphi introduced support for Web Services early, it doesn't have many advanced features and interoperability with Web Services created with other systems has always  been limited. It's getting better with every new version of Delphi, though. 

It's fairly easy to consume WCF service configured to use BasicHttpBinding. Let's use Interoperable Web Service Demo at http://aspspider.info/burzyn/echoservice.svc as an example. The WSDL document can be generated using the following link http://aspspider.info/burzyn/echoservice.svc?WSDL

To import the Echo Service into a Delphi project select File | New | Others | WebServices |  WSDL Importer and paste http://aspspider.info/burzyn/echoservice.svc?WSDL into the WSDL Source.
Also in Delphi XE2 or newer, there's a new option in the Components menu, which is an alternative way to import WSDL. Use Components | Import WSDL to create a unit to be shared by multiple projects.

WSDL document generated from WCF service usually contains two file, the main file and the second file referenced by using the following line.
<wsdl:import location="http://aspspider.info/Burzyn/EchoService.svc?wsdl=wsdl0"/>
Delphi older then 2009 will not be able to import it correctly, as it processes just the main file and information from this file alone might not be sufficient. This has been remedied in .NET Framework 4.5 by providing an option to generate WSDL document as a single file. Single file WSDl document is accesible at http://aspspider.info/burzyn/echoservice.svc?singleWSDL

Summary

This is just the beginning. Services based on BasicHttpBinding might not be sufficient for every requirement. Connecting to WCF services built using other bindings might be a bigger challenge, but it's not impossible. It might require significant SOAP expertise, though.


Thursday, 17 January 2013

Interoperable Web Service Demo

Common scenario

I've been involved in interfacing to WCF using other tools than Visual Studio (Delphi) more than once. Short discussion can be found at Interoprable WCF. I thought it would be a good idea to have a reference service available and this is how the idea of Echo Service was born. Echo Service illustrates typical issues associated with accessing web services - calling a method with parameters of simple and complex type, handling declared and undeclared exception, as well as downloading a large binary data.

The Implementation

The Interoperable Web Service Demo is currently hosted in http://aspspider.info/burzyn/echoservice.svc. Apart from BasicHttpBinding, it exposes WsHttpBinding and WebHttpBinding at http://aspspider.info/burzyn/echoservice.svc/ws and http://aspspider.info/burzyn/echoservice.svc/web respectively. WebHttpBinding can be accessed using the browser with the syntax like:
http://aspspider.info/burzyn/echoservice.svc/web/echostring?value=test1.

Here's the the 'service.model/ group from web.config:
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="Metadata" name="Altaira.Demos.EchoService">
        <endpoint name="BindingInterop" address="" binding="basicHttpBinding" bindingConfiguration="" contract="Altaira.Demos.IEchoService"/>
        <endpoint name="BindingREST" address="web" binding="webHttpBinding" behaviorConfiguration="WebBehavior" contract="Altaira.Demos.IEchoService"/>
        <endpoint name="BindingStandard" address="ws" binding="wsHttpBinding" contract="Altaira.Demos.IEchoService"/>
        <host>
          <baseAddresses>
            <add baseAddress="'http://aspspider.info/burzyn/echoservice.svc"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="WebBehavior">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="Metadata">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>
 Note that includeExceptionDetailinFaults is set to 'false'. It' intended for testing the scenario when an undeclared exception is raised on the server side, so that the CommunicationException is raised rather than  FaultException. You can test it calling UndeclaredFault() method.

Further information is available at request.




Friday, 30 November 2012

Accessing .NET code from Delphi using COM Interoperability

Introduction

When .NET Framework has been introduced, it included number of options to interoperate with unmanaged code. From Delphi developer perspective most notable seems to be COM Interoperability. COM Interoperability allows to use COM object within managed code and well as managed code can be exposed as COM and used in COM capable native application. In other words - COM library created with Delphi can used within .Net application as well as .Net code can be accessed as COM within Delphi.

While importing COM libraries created with Delphi is relatively simple, using .NET assemblies in Delphi can be tricky. Firstly, .NET assembly needs to follow certain guidelines, then there's an issue of importing the assembly into a Delphi project and finally careful consideration needs to be put into deployment. Here's a routine which I believe is best in most scenarios. Apart from providing an effective solution, it's intended to reduce unnecessary clutter in purpose to achieve clean and easy to maintain code.

Preparing .NET assembly

Only public classes, interfaces and enumeration can be exposed to be used in COM. What is actually exposed can be controlled by ComVisible attribute, which can be applied to assemblies, interfaces, classes, structures, delegates, enumerations, fields, methods and properties. I believe it's better to disable COM visibility at the assembly level, so nothing is exported, unless explicitly specified. This arrangement allows for fine tuning resulting COM library according to the principle of minimal clutter.

To disable COM visibility for an assembly make sure the following line exists in AssemblyInfo.cs
[assembly: ComVisible(false)].
Note that ticking off "Make assembly COM-Visible" checkbox in the Project Properties modifies AssemblyInfo.cs accordingly.

All parameters and return values must be either a simple type or a type with its ComVisible attribute set to true. Interoperable classes should have a public parameterless constructor.

GuidAttribute can be applied to assemblies, interfaces, classes, enumerations, structures, or delegates and it's used to assign Guid to an exported COM entity. Although not strictly required, it's needed to access interfaces and classes by Guid. If it's not present, then the Guid is assigned at the registration time, so it's different every time registration takes place.

Important feature of COM Interoperability are class interfaces.  When a class is "ComVisible", its class interface exposes all public methods, properties and events of the class. How class interface is exposed is controlled by ClassInterface attribute. It's better to define an explicit interface rather than use the class interface. Firstly, if you make changes to the class, there won't be necessary to re-import. Also, while using explicit interfaces, only methods that are needed are exposed. If class interfaces are not used, they should be excluded from the type library by applying the ClassInterface attribute with ClassInterfaceType.None parameter.

As an example, let's try to access .Net Tracing using Delphi. ComVisible, Guid and ClassInterface attributes are used according to the guidelines above.

namespace Altaira.Utils
{
    [ComVisible(true)]
    [Guid("4A537FBB-8123-48A0-8E37-7D0560198927")]
    public interface IDTrace
    {
        void WriteLine(string message);
        void TraceError(string message);
        void TraceWarning(string message);
    }

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [Guid("9A263B40-93FC-4AA3-876A-B8BFDBA2C55D")]
    public class DTrace : IDTrace
    {
        public void WriteLine(String message)
        {
            Trace.WriteLine(message);
        }

        public void TraceError(string message)
        {
            Trace.TraceError(message);
        }

        public void TraceWarning(string message)
        {
            Trace.TraceWarning(message);
        }
    }
}

Registering and importing the assembly

There's number of ways to register an assembly as COM library. The simplest is to tick the Register for COM Interop checkbox in the Project Properties - the assembly will then be registered as part of the build process.
To be able to manage registration in various scenarios, it's best to utilise the command line utility called Regasm. It's part of the .NET framework and can be found at c:\windows\microsoft.net\framework\(version). It doesn't matter which version of the .NET Framework do you use, as long as Regasm version is not lesser than CLR version. For example, it's not possibly to register assembly build with .NET Framework 4.0 using Regasm from version 2.0.

For example, the following command registers DTraceUtils.dll assembly as COM

<Path>\Regasm DTraceUtils.dll

Regasm can also create a type library by using the /tlb option. The following command creates and registers type library called DTraceUtils.tlb (in addition to registering DTraceUtils.dll assembly):

<Path>\Regasm /tlb DTraceUtils.dll

Note that the same could be achieved using tlibexp utility.

Once a Type Library is created and registered, it's all the same as regular COM. From the Component menu select Import Component, then Import Type Library. Note the Palette Page dropdown on Import Component dialog. It determines the page on the Component Palette, where VCL component wrapper will be installed; if left blank, the component wrapper will not be generated. Leaving it blank is the preferred option, as except for certain specific scenarios, there's no benefit in creating a component wrapper.

Following the above guidelines results in neat and easy to manage code.

  IDTrace = interface(IDispatch)
    ['{4A537FBB-8123-48A0-8E37-7D0560198927}']
    procedure WriteLine(const message: WideString); safecall;
    procedure TraceError(const message: WideString); safecall;
    procedure TraceWarning(const message: WideString); safecall;
  end;
  CoDTrace = class
    class function Create: IDTrace;
    class function CreateRemote(const MachineName: string): IDTrace;
  end;
It's important to remember to recreate Type Library every time every interface signature changes.

Delphi code

Assuming IDTrace is a reference to explicit interface as per following declaration
var
  Trace: IDTrace;

DTrace can be instanciated using the following code.
  Trace := CoDTrace.Create;

Note the simplicity of instancing the interface using CoClass. If VCL component wrapper was used instead, you would have much less control over creation and lifetime of COM objects.
The interface reference can be used to access the Trace functionality
  Trace.WriteLine('Using System.Diagnostics from Delphi code');

This is actually quite useful example, assuming more methods are added to DTrace class.
To view logged messages you should create MyApp.exe.config (where myApp.exe is the Delphi app) which looks similar to following:
<?xml version="1.0" encoding="utf-8">
<configuration>
  <system.diagnostics>
    <trace autoflush="true">
      <listeners>
        <add name="text"
                    type="System.Diagnostics.TextWriterTraceListener"
             initializeData="c:\projects\data\TextOutput.log"/>
       </listeners>
    </trace>
  </system.diagnostics>
</configuration>

Deployment

Major concern while deploying an interoperable assembly is registering it with COM. The easy option is to make use of a setup project. After the assembly is added to the project, set the Register property determines COM registration options and installation process takes care of it.

If you need flexibility and control, it's better to use Regasm utility.

It's very important to realise that registration by default doesn't include the full path to the assembly executable. When COM is accessed, the system is looking for the assembly in the current directory then in the Global Assembly Cache. Sometimes it might be better to put the assembly in the GAC, but if assembly is not in GAC it needs to be in the current directory. Some might consider it a limitation, but I personally don't think it's the case.


Wednesday, 24 October 2012

Welcome

Hi All,

As a Delphi supporter for many years, I'm happy to notice, that for the first time in many years, new developments in Delphi world looks actually quite promising. It's has always been the best tool for desktop development, but one wouldn't necessarily choose it to develop a web service. Delphi can find its niche, but should able to fit into heterogeneous environment and interoperate smoothly with other software development frameworks, like .Net or Java.

I'd like to share ideas on COM Interop, WCF in Delphi, WinForms and other technologies related to both Delphi and .Net.

To be continued.