1

I have a problem that at first seems to be permission. On localhost works just right but when goes to a server error occurs:

[UnauthorizedAccessException: Retrieving the COM class factory for component with CLSID {000209FF-0000-0000-C000-000000000046} failed due to the Following error: 80070005.]

I use IIS7
I gave permission to iis user but to no avail, then I spread "try catch" in the code and found that the error is related to the moment it tries to create an instance of Microsoft.Office.Interop.Word

example

using Word = Microsoft.Office.Interop.Word;

private bool Exemple(SqlDataReader dbReader, string strFilePath)
{ 
    Word.Application oApp = new Word.Application ();   // error here
    ...
    ...
    ...
}

Microsoft.Office.Interop.Word this dll in the Bin folder of the server, does anyone have any idea what might be happening?

Thank you.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Huh? Does your server have Word installed? – KingCronus Apr 11 '12 at 14:26
  • Check service from client side, may be problem with access rights – Likurg Apr 11 '12 at 14:29
  • 2
    You shouldn't use the Office interop-library for server applications. It is against Microsoft's recommendation, as there might be deadlocks and other unpleasantries that will foil your application entirely. – Leonard Apr 11 '12 at 14:31

3 Answers3

4

While you should not use the Office interop libraries on a server. Microsoft does not recommend or support that kind of implementation but if you're begging for punishment to get past your error you need to open up the DCOM configuration on the server and set the identity to the user your app pool is running under and grant them access to launch and activate permission. Again, you do not want to setup Office interop libraries on a server.

Justin
  • 2,093
  • 1
  • 16
  • 13
0

(Though it is not recommended to use office interop in server, but sometimes we have to...)

The issue is related to the security. The user identity for the ASP.NET process has not the access rights to use office app, and the identity should have only the minimal permissions required to run your applications. You can use code impersonation to raise the permissions at runtime, without setting up Impersonation in web.config or using a high privilige account for my ASP.NET user account. I have used it in server and I like this approach.

You can google about it to know more. Here is the code from http://support.microsoft.com/kb/306158:

public class CodeImpersonate
{
    /// <summary>
    /// This logon type is intended for users who will be interactively using the computer, such as a user being logged on by a terminal server, 
    /// remote shell, or similar process. This logon type has the additional expense of caching logon information for disconnected operations; therefore,
    /// it is inappropriate for some client/server applications, such as a mail server.
    /// </summary>
    public const int LOGON32_LOGON_INTERACTIVE = 2;
    /// <summary>
    /// Use the standard logon provider for the system. The default security provider is negotiate, 
    /// unless you pass NULL for the domain name and the user name is not in UPN format. In this case, the default provider is NTLM.
    /// Windows 2000: The default security provider is NTLM.
    /// </summary>
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
                                        String lpszDomain,
                                        String lpszPassword,
                                        int dwLogonType,
                                        int dwLogonProvider,
                                        ref IntPtr phToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);      

    public bool ImpersonateValidUser(String userName, String domain, String password)
    {
        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
        return false;
    }

    public void UndoImpersonation()
    {
        if (impersonationContext != null)
            impersonationContext.Undo();
    }

}

Copy the above code in a file. Here is a sample use of it:

CodeImpersonate codeImpersonate = null;
try
{
    codeImpersonate = new CodeImpersonate();
    var isLoggedIn = codeImpersonate.ImpersonateValidUser(AppConfigs.OfficeUser, AppConfigs.OfficeUeerDomnia, AppConfigs.OfficeUserPass);

    if (isLoggedIn)
    {
        //Do your office work....
    }
    else
        throw new InvalidOperationException("Login failed for office user.");
}
finally
{
    if (codeImpersonate != null)
        codeImpersonate.UndoImpersonation();
    codeImpersonate = null;
}

To use the above code in several places, here is a facade:

public class ImpersonateServices
{
    public ImpersonateServices(String userName, String domain, String password)
    {
        this.UserName = userName;
        this.Domain = domain;
        this.Password = password;
    }

    public string UserName { get; private set; }

    public string Domain { get; private set; }

    public string Password { get; private set; }

    public void Execute(Action privilegedAction)
    {
        CodeImpersonate codeImpersonate = null;
        try
        {
            codeImpersonate = new CodeImpersonate();
            var isLoggedIn = codeImpersonate.ImpersonateValidUser(this.UserName, this.Domain, this.Password);

            if (isLoggedIn){
                privilegedAction();
            }
            else
                throw new InvalidOperationException("Login failed for office user.");
        }
        finally
        {
            if (codeImpersonate != null)
                codeImpersonate.UndoImpersonation();
            codeImpersonate = null;
        }
    }
}

Using it like:

var impersonateServices = new ImpersonateServices(AppConfigs.OfficeUser, 
                                              AppConfigs.OfficeUserDomain, 
                                              AppConfigs.OfficeUserPass);
impersonateServices.Execute(() => { 
    //Do your Office work...
});

The office-User should be a valid local or domain account. To test the office user account working, login to the server using this user account credential and start the MS word application. If the Word is being opened for the first time, some settings related popup might have appeared. Resolve them otherwise they may create issues during programmatic access.

If you keep the user account credential in config file, please consider to encrypt the values. See the below SO Q&A:

How to encrypt one entry in web.config

Encrypting appSettings in web.config

Hope this helps...

Community
  • 1
  • 1
Kibria
  • 1,865
  • 1
  • 15
  • 17
0

Under Application Pools in IIS, setting Identity field to NetworkService resolved this issue in my setup.

BrainCoder
  • 5,197
  • 5
  • 30
  • 33