Accessing unmanaged code in separate context in WPF

By on March 1, 2010

In order for Windows Forms controls to work correctly in .NET Framework it is necessary to access their methods and properties from the same context that they were created in, otherwise they may display unpredictable behaviour. Usually this is a concern when using multiple threads, some of which need to modify a control’s state. All of this is also relevant for Windows Presentation Foundation (WPF) technology. However, in WPF such an issue may also arise when using a DLL with unmanaged code — this does not occur in WinForms. We’ll show an example how one can solve this issue by calling methods from the DLL in a separate context.

Let’s say we have a DLL written in C, called “native.dll”, and we need to call method getSomeList() of the DLL, which returns a list of items to populate a drop-down control in WPF. In order to implement this, we need to take the following key steps:

  1. Create new context for invoking the DLL method.
  2. Invoke the DLL method in the newly created context.
  3. Access the control’s properties and methods in the context that the control was created in.

The sample code below demonstrates this in more detail.

///<summary>

///Interaction logic for Window1.xaml

///</summary>

public partial class Window1 : Window

{

   // Contextfor accessing controls

   private System.Threading.SynchronizationContext originalContext = null;

 

   //We need to import the function

   [DllImport(“native.dll”, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]

   static public extern byte getSomeList(int no, StringBuilder name);

 

   // Constructor

   public Window1()

   {

      InitializeComponent();

 

      // Obtain the context in which the controls were created

      originalContext = System.Threading.SynchronizationContext.Current;

   }

 

   // Populate the list on window load event

   private void Window_Loaded(object sender, RoutedEventArgs e)

   {

      try

      {

         // Create context for invocation of getSomeList() from native.dll

         System.Threading.SynchronizationContext sc = new System.Threading.SynchronizationContext();

         object cr = null;

         // In this new context, call the list method

         sc.Post(ActualGetSomeList, cr);

      }

      catch (System.Exception ex)

      {

      }

   }

 

   // Method for receiving the list and accessing the control

   private void ActualGetSomeList(object obj)

   {

      try

      {

         // Receive the list

         string[] listItems = GetSomeList();

         // Call method, which populates the drop-down cbSomeList

         // in the context in which it was created

         originalContext.Post(SetListItems, listItems);

      }

      catch (System.Exception ex)

      {

      }

   }

 

   // Method for populating the drop-down

   private void SetListItems(object obj)

   {

      try

      {

         //populate the drop-down cbSomeList

         string[] listItems = (string[])obj;

         if (listItems!= null && listItems.Length > 0)

         {

            cbSomeList.ItemsSource = listItems;

         }

      }

      catch (System.Exception ex)

      {

      }

   }

 

   // Method receives a list from native.dll

   publicstring[] GetSomeList()

   {

      try

      {

         byte nErrorCode = 0;

         List<string> list = new List<string>(10);

         for (int i = 1; i < 10; i++)

         {

            StringBuilder sbName = new StringBuilder(100);

            nErrorCode = getSomeList(i, sbName);

            if (nErrorCode == 0)

            {

               string tmp_ = sbName.ToString();

               if (!string.IsNullOrEmpty(tmp_))

               {

                  list.Add(tmp_);

               }

            }

            else throw;

         }

         return list.ToArray();

      }

      catch (Exception e)

      {

      }

   }