How to: Use Automation in Trusted Applications

Microsoft Silverlight will reach end of support after October 2021. Learn more.

Trusted applications can integrate with some native functionality of the host operating system. Starting in Silverlight 4, this native integration includes the ability to access Automation APIs on Windows operating systems.

Automation is a technology that applications use to expose functionality to scripting tools and other applications. For example, you can use Automation to add Office features to your Silverlight-based applications.

An application or component that exposes Automation APIs is called an Automation server, while an application that accesses these APIs is called an Automation client. The following topic describes how to enable your Silverlight-based applications to work as Automation clients.

NoteNote:

Only trusted applications running on Windows can access Automation APIs, and only those exposed by components or applications that are already installed. In Silverlight 4 and earlier, trusted applications must also run outside the browser. Starting in Silverlight 5, system administrators can enable trusted applications to run inside the browser. For more information, see Trusted Applications and AutomationFactory.

Silverlight for Windows Phone Silverlight for Windows Phone and Silverlight 3 do not support trusted applications.

To determine whether Automation is available

  • Use the AutomationFactory.IsAvailable property as demonstrated in the following code example. The IsAvailable property indicates whether the application is trusted and is running outside the browser on a Windows operating system.

    Public Sub New()
    
        InitializeComponent()
    
        If AutomationFactory.IsAvailable Then
    
            ' Use Outlook on a background thread to keep the UI responsive.
            Dim worker As New BackgroundWorker()
            AddHandler worker.DoWork,
                Sub(sender, e)
                    If InitializeOutlook() Then
                        SearchEmail()
                    Else : Dispatcher.BeginInvoke(
                        Sub()
                            MessageBox.Show("Outlook is not available.")
                        End Sub)
                    End If
                End Sub
            worker.RunWorkerAsync()
    
        Else : MessageBox.Show("Automation is not available.")
        End If
    
    End Sub
    
    public MainPage()
    {
        InitializeComponent();
        if (AutomationFactory.IsAvailable)
        {
            // Use Outlook on a background thread to keep the UI responsive.
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += (sender, e) =>
            {
                if (InitializeOutlook()) SearchEmail();
                else Dispatcher.BeginInvoke(() =>
                {
                    MessageBox.Show("Outlook is not available.");
                });
            };
            worker.RunWorkerAsync();
        }
        else
        {
            MessageBox.Show("Automation is not available.");
        }
    }
    

To create or retrieve an Automation object

  • Call the AutomationFactory.CreateObject or AutomationFactory.GetObject method with a programmatic identifier (progID) that indicates the Automation server to use. These methods throw a NotSupportedException if Automation is not available, and throw an Exception if the specified Automation server is not available.

    In general, CreateObject will start a new instance of the Automation server and GetObject will retrieve the most recently started instance. However, the exact behavior of CreateObject and GetObject differs for different components, so be sure to check the component documentation and test your usage thoroughly.

    The example for this topic searches the user's Outlook inbox. This requires Outlook to be open, so the following code first attempts to retrieve a running instance by calling GetObject. If an exception is thrown, the code attempts to open Outlook by calling CreateObject and displaying the user's inbox. If a second exception is thrown, then Outlook is probably not installed.

    The reference to the Automation server is stored in a variable of type Object in Visual Basic and type dynamic in C#. This is necessary because the type information is not available until run time. However, the lack of type information also prevents the use of IntelliSense and compile-time debugging, making the component documentation even more important.

    Private outlook As Object
    
    Private Function InitializeOutlook() As Boolean
    
        Try
            ' If GetObject throws an exception, then Outlook is 
            ' either not running or is not available.
            outlook = AutomationFactory.GetObject("Outlook.Application")
            Return True
        Catch
            Try
                ' Start Outlook and display the Inbox, but minimize 
                ' it to avoid hiding the Silverlight application.
                outlook =
                    AutomationFactory.CreateObject("Outlook.Application")
                outlook.Session.GetDefaultFolder(6).Display() ' 6 = Inbox
                outlook.ActiveWindow.WindowState = 1  ' minimized
                Return True
            Catch
                ' Outlook is unavailable.
                Return False
            End Try
        End Try
    
    End Function
    
    private dynamic outlook;
    
    private bool InitializeOutlook()
    {
        try
        {
            // If GetObject throws an exception, then Outlook is 
            // either not running or is not available.
            outlook = AutomationFactory.GetObject("Outlook.Application"); 
            return true;
        }
        catch (Exception)
        {
            try
            {
                // Start Outlook and display the Inbox, but minimize 
                // it to avoid hiding the Silverlight application.
                outlook = 
                    AutomationFactory.CreateObject("Outlook.Application");
                outlook.Session.GetDefaultFolder(6 /* Inbox */).Display();
                outlook.ActiveWindow.WindowState = 1; // minimized
                return true;
            }
            catch (Exception)
            {
                // Outlook is unavailable.
                return false;
            }
        }
    }
    

To handle Automation events

  • Use one of the techniques demonstrated in the following code example. For more information, see the AutomationEvent class.

    NoteNote:

    Events with return values are not supported.

    Private Sub SearchEmail()
    
        UpdateStatusMessage("Searching Inbox for 'Silverlight'...")
    
        ' The following code demonstrates three ways to handle Automation 
        ' events. In Visual Basic, all three ways use the AutomationEvent class. 
    
        searchEvent =
            AutomationFactory.GetEvent(outlook, "AdvancedSearchComplete")
    
        ' The first way is demonstrated by the Handles clause of the 
        ' SearchEvent_EventRaised method, which requires the WithEvents modifier
        ' on the searchEvent variable declaration.
    
        ' The second way uses the AddHandler syntax with the EventRaised event,
        ' and does not require the WithEvents modifier. 
        ' AddHandler searchEvent.EventRaised, AddressOf SearchEvent_EventRaised
    
        ' The third way uses the AutomationEvent.AddEventHandler method, and
        ' requires the use of a delegate with an API signature that matches the
        ' Automation event. 
        ' searchEvent.AddEventHandler( 
        '   New AdvancedSearchCompleteDelegate(AddressOf SearchComplete))
    
        ' Begin the search.
        outlook.AdvancedSearch("Inbox",
            "urn:schemas:mailheader:subject ci_phrasematch 'Silverlight'",
            True, "SubjectSearch")
    
    End Sub
    
    Private WithEvents searchEvent As AutomationEvent
    
    Sub SearchEvent_EventRaised(ByVal sender As Object,
        ByVal e As AutomationEventArgs) Handles searchEvent.EventRaised
    
        SearchComplete(e.Arguments(0))
    
    End Sub
    
    ' Required only with the second two ways of handling Automation events. 
    ' Private Delegate Sub AdvancedSearchCompleteDelegate(ByRef search As Object)
    
    ' Note: Visual Basic does not support the use of custom delegates for 
    ' events with optional parameters. 
    
    Private Sub SearchComplete(ByRef search As Object)
    
        Dim searchResults As New List(Of String)
        For Each result As Object In search.Results
            searchResults.Add(result.Subject)
        Next
        SetResultsList(searchResults)
    
    End Sub
    
    private void SearchEmail()
    {
        UpdateStatusMessage("Searching Inbox for 'Silverlight'...");
    
        // The following code demonstrates three ways to handle Automation 
        // events. The first two ways use the AutomationEvent class. 
        // The last two ways require the use of a delegate with 
        // an API signature that matches the Automation event. 
    
        AutomationEvent searchEvent = AutomationFactory
            .GetEvent(outlook, "AdvancedSearchComplete");
    
        // The first way: 
        searchEvent.EventRaised += (sender, e) =>
        {
            SearchComplete(e.Arguments[0]);
        };
    
        // The second way:
        // searchEvent.AddEventHandler(
        //    new AdvancedSearchCompleteDelegate(SearchComplete));
    
        // The third way:
        // outlook.AdvancedSearchComplete +=
        //    new AdvancedSearchCompleteDelegate(SearchComplete);
    
        // Begin the search. 
        outlook.AdvancedSearch("Inbox",
            "urn:schemas:mailheader:subject ci_phrasematch 'Silverlight'",
            true, "SubjectSearch");
    }
    
    // Required only with the second two ways of handling Automation events. 
    private delegate void AdvancedSearchCompleteDelegate(dynamic search);
    
    // To use custom delegates for events with optional parameters, you
    // must specify default values in the delegate signature as shown here:
    // private delegate void MyEventHandlerDelegate(int i, float f=77);
    
    private void SearchComplete(dynamic search)
    {
        List<String> searchResults = new List<String>();
        foreach (dynamic result in search.Results) 
        { 
            searchResults.Add(result.Subject);
        }
        SetResultsList(searchResults);
    }
    

Example

The following code provides the helper methods and user-interface XAML used by the preceding code. To run the code in this topic, create a new Silverlight application project and then add all the code to the MainPage class.

<Grid x:Name="LayoutRoot" Background="White">
    <TextBlock x:Name="message"/>
    <ListBox x:Name="items" Visibility="Collapsed"/>
</Grid>
Private Sub UpdateStatusMessage(ByVal text As String)
    UpdateUserInterface(Sub() message.Text = text)
End Sub

Private Sub SetResultsList(ByVal results As IEnumerable(Of String))
    UpdateUserInterface(
        Sub()
            message.Visibility = System.Windows.Visibility.Collapsed
            items.Visibility = System.Windows.Visibility.Visible
            items.ItemsSource = results
        End Sub)
End Sub

Private Sub UpdateUserInterface(ByVal performUpdate As Action)
    ' Marshal to the UI thread, if necessary.
    If Dispatcher.CheckAccess Then
        performUpdate()
    Else
        Dispatcher.BeginInvoke(performUpdate)
    End If
End Sub
private void UpdateStatusMessage(string text)
{
    // Update the message on the UI thread.
    UpdateUserInterface(() => { message.Text = text; });
}

private void SetResultsList(IEnumerable<String> results)
{
    // Hide the message and populate the results list on the UI thread.
    UpdateUserInterface(() =>
    {
        message.Visibility = System.Windows.Visibility.Collapsed;
        items.Visibility = System.Windows.Visibility.Visible;
        items.ItemsSource = results;
    });
}

private void UpdateUserInterface(Action performUpdate)
{
    // Marshall to the UI thread, if necessary.
    if (Dispatcher.CheckAccess()) performUpdate();
    else Dispatcher.BeginInvoke(performUpdate);
}

Compiling the Code

To use the C# dynamic keyword, your project must include a reference to Microsoft.CSharp.dll.

Security

For security considerations in trusted applications, see Trusted Applications.