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:
- Create new context for invoking the DLL method.
- Invoke the DLL method in the newly created context.
- 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)
{
}
}