none
How to pass the Current Thread Identity to a worker thread handling events from a COM Interop object ? RRS feed

  • Question

  • Hi,

    Here after is a very simple piece of code to open Word using "Microsoft Office Interop Word" 1.14 and to execute some code when a document is opened within this instance of word.

    The issue I experience is that the Identity set on the Main Thread is not passed on the Worker Thread executing the event handler.

    I read that "delegates" run under the caller's security permissions, not the declarer's permissions. I presume therefore that Word being invoked through Interop, it does possibly not run with the Identity set on my application. If this is correct. How can I force Word to run with that Identity ?

    Otherwise, what can I do to run the delegate under the current user Identity

    using System;
    using System.Windows.Forms;
    using Microsoft.Office.Interop.Word;
    using System.Diagnostics;
    using System.Threading;
    using System.Security.Principal; 
    namespace WordInterop
    {
        public partial class Form1 : Form
        {
            private ApplicationClass objWordApplication;
            public Form1()
            {
                InitializeComponent();
     
                Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("It's me"), new string[] { });            
            }
     
            private void button1_Click(object sender, EventArgs e)
            {
                objWordApplication = new ApplicationClass();
                objWordApplication.DocumentOpen += new ApplicationEvents4_DocumentOpenEventHandler(MyDelegateOpen);
     
                objWordApplication.Visible = true;
                objWordApplication.Activate();
            }
     
            private void MyDelegateOpen(_Document Doc)
            {
                Debug.Print("Delegate Thread Identity= " + Thread.CurrentPrincipal.Identity.Name);
            }
     
            private void Form1_Deactivate(object sender, EventArgs e)
            {
                Debug.Print("Main Thread Identity= " + Thread.CurrentPrincipal.Identity.Name);
            }
        }
    }

    To run this code in a Form, you need to add a reference on Microsoft.Office.Interop.Word, open the Properties tab for that reference in Visual Studio and set "Embed Interop Types" to "False".



    Valéry Letroye



    Tuesday, May 7, 2013 9:10 AM

All replies

  • Hi Valery,

    Welcome to the MSDN Forum.

    I reproduced your scenario with your code sample. I am trying to involve some other one into this thread, it will take some time, thank you for your patience.

    Best regards,


    Mike Feng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    Wednesday, May 8, 2013 8:18 AM
    Moderator
  • Hi Valéry,

    From your code, you're set a custom principal object to the Thread.CurrentPrincipal, that won't be automatically populated to some background thread (for callbacks) or custom created threads. It is the windows identity (can be get via WindowsIdentity.GetCurrent() that will be the same between multiple threads running the same application account.

    To make things simple, you can consider use Control.Invoke method (use the Form object of the main windows form as Control instance) so as to marshal the callback function call to the main winform UI thread.


    #Control.Invoke Method (Delegate)
    http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

     


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, May 8, 2013 9:44 AM
  • Thx for this information.

    I noticed indeed that the WindowsIdentity was still available , but it may not be used (except by those of our applications deciding to use a SSO mechanism). We actually use a custom identity "OurCompanyIdentity" which is created by our custom "Authentication Providers" (Eg.: one taking user credentials as input, one taking client certificates, one trusting other custom Identities, ...).

    Once a user has been authenticated, a Generic Principal is created with an "OurCompanyIdentity" as a parameter (+ roles got from our systems) and set on the current Thread (actually depending on the technology: web site, fat application versus wcf service).

    And why do we need our custom Identity on the Current Thread in the event handler: because in that part of the code, we call a secured wcf service using a custom framework component that flows the current Identity (if and only if it's a "OurCompanyIdentity") to the service.

    Voilà for the details...

    Now, I already tried to call the main UI thread to handle the event, but the problem was still there. Only notice that I was using BeginInvoke... (we Invoke a control created to avoid the know "Cross-thread operation not valid in Windows Forms" issue)

    Here is the code, a bit more complex (sorry) as used for our other testing purpose:

    using System;
    using System.Windows.Forms;
    using System.Diagnostics;
    using System.Threading;
    using System.Security.Principal;
     
    namespace WordInterop
    {
        public partial class Form1 : Form
        {
            public Document objWordApplication;
     
            public static Control invokerControl;
            public static MyDelegate simpleDelegate;
            public delegate void MyDelegate();
     
            public Form1()
            {
                InitializeComponent();
     
                Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity("It's me"), new string[] { });
     
                Document.MyEvent += DocumentEventHandler;
                invokerControl = new Control();
                invokerControl.CreateControl();
     
                simpleDelegate = new MyDelegate(MainThreadEventHandler);
            }
     
            private void button1_Click(object sender, EventArgs e)
            {
                 objWordApplication = new Document();
            }
     
            private void Form1_Deactivate(object sender, EventArgs e)
            {
                Debug.Print("Main Thread Identity= " + Thread.CurrentPrincipal.Identity.Name);
            }
     
            public void DocumentEventHandler(object sender)
            {
                Document.CheckThread("EventHandler");
                invokerControl.BeginInvoke(simpleDelegate);
            }
     
            public static void MainThreadEventHandler()
            {
                Document.CheckThread("MainThread EventHandler");
            }
     
        }
    }
    
    using Microsoft.Office.Interop.Word;
    using System.Diagnostics;
    using System.Threading;
    using System.Security.Permissions;
     
    namespace WordInterop
    {
        public delegate void MyEventHandler(object sender);
     
        public class Document
        {
            public static event MyEventHandler MyEvent;
            private ApplicationClass objWordApplication;
     
            public Document()
            {
                objWordApplication = new ApplicationClass();
                objWordApplication.DocumentOpen += new ApplicationEvents4_DocumentOpenEventHandler(WordEventHandler);
     
                objWordApplication.Visible = true;
                objWordApplication.Activate();
            }
     
            private void WordEventHandler(_Document Doc)
            {
                CheckThread("Delegate");
     
                MyEvent(this);
            }
     
            public static void CheckThread(string from)
            {
                if (System.Windows.Forms.Application.OpenForms[0].InvokeRequired)
                {
                    Debug.Print(from + ": This is not the Main Thread");
                }
                else
                {
                    Debug.Print(from + ": This is the Main Thread");
                }
                Debug.Print(from + ": Thread Identity= " + Thread.CurrentPrincipal.Identity.Name);
            }
        }
    }
    

    When executed, here is the output, where we can see that back to the main thread, the execution context does not contain the Identity anymore...


    Main Thread Identity= It's me
    Delegate: This is not the Main Thread
    Delegate: Thread Identity=
    EventHandler: This is not the Main Thread
    EventHandler: Thread Identity=
    MainThread EventHandler: This is the Main Thread
    MainThread EventHandler: Thread Identity=

    Is there anything wrong in the code above ? Notice that using "static" simpleDelegate and invokerControl or not static results in the same behaviour.


    Valéry Letroye



    Wednesday, May 8, 2013 11:54 AM
  • Hi Bill,

    I went through the code shared and I would like to let you know that Office always should work in the Logged In user’s context as Office.

    Office applications assume a user identity when the applications are run, even when Automation starts the applications. The applications try to initialize toolbars, menus, options, printers, and some add-ins based on settings in the user registry hive for the user who launches the application. Many services run under accounts that have no user profiles (such as the SYSTEM account or the IWAM_[servername] accounts). Therefore, Office may not initialize correctly on startup. In this situation, Office returns an error on the CreateObject function or the CoCreateInstance function. Even if the Office application can be started, other functions may not work correctly if no user profile exists.

    Please refer to http://support.microsoft.com/kb/257757 were considerations of "User Identity" is explained.  Please let me know if you have any concerns.

    Thanks,

    Sreerenj G Nair


    Tuesday, May 14, 2013 9:36 PM
  • Thx a lot for your answer!

    Notice that our application (creating the Office object) is a WinForm Application, which calls a WCF service from the Office's events handler. So Office is not executed from a Service.

    From the information you provide, I may only conclude that whenever the authenticated user is not the Logged In User, we should use impersonation before running Office, so the right user profile is accessed (as far a such a profile exists - ok).

    However, it does not help to pass our custom Identity to the worker thread.

     

    We have a "quick & dirty" workarround... as far as this application is not prompting the user to get his credentials but actually use our "Single Sign on" provider (which trusts the current Windows Identity), we can recall that provider from the event handler (on the worker thread). This would not be acceptable for fat applications prompting the user to get his credentials (this is sometimes used by old applications still connected to their own user database...)


    Valéry Letroye

    Wednesday, May 15, 2013 11:19 AM