If you impersonate a particular account in your ASP.NET Web
Service (.asmx) or in your Web Form (.aspx), and then you call to an
Apartment/Single-threaded Component Object Model (COM) component, the process
identity (by default, ASPNET account) is used instead of the impersonated
account. Therefore, logic errors may occur when you try to access resources
from the COM component, or you may receive the following error message:
The thread that executes your ASP.NET request is one of the
I/O threads or one of the workerThreads in the ASP.NET worker process
(aspnet_wp.exe). These threads are all Multi-Threaded Apartment (MTA) threads.
If you programmatically impersonate an account in your .aspx or .asmx code, or
if you impersonate by using <identity impersonate="true"> in Web.config
or Machine.config, then the impersonation token is held on this MTA thread. If
you then make a call into a single-threaded or an apartment-threaded COM
component, that component is accessed by an entirely different thread, which is
the single thread in its Single-Threaded Apartment (STA). Because this STA
thread does not have an impersonation token of its own, and the impersonation
token of the MTA thread is not passed to the STA thread, the STA thread then
executes under the process identity.
To work around this problem for Web Forms or for Web
Services, you can do the following:
For Web Forms (.aspx)
Use AspCompat="true" to run your page on an STA thread and to
avoid the thread switch when you call the component.
See Q308095 in
the "References" section of this article for more information.
For Web Services (.asmx)
AspCompat is not available for Web Services (.asmx) so you must
take a different approach. If the impersonated account is static, which means
that you specify a userName and a password in the <identity> tag of
Web.config or Machine.config, or if you always programmatically impersonate the
same account, you can put the STA component in a COM+ Server Application and
then set the identity of the application to the impersonated user. Note
When you call an apartment-threaded or single-threaded COM
component from a Web Service, it is already a requirement to put the component
in some type of COM+ Application.
See Q303375 in the "References"
section of this article for more information.
If the impersonated
account is dynamic (<identity impersonate="true">+ IIS Integrated or
Basic authentication), setting the identity in a COM+ server application will
not suffice. In that case, if you control the code for the STA component then
you can call the CoImpersonateClient
function at the beginning of each method in the component that
requires the impersonated identity. If you do not control the code for the STA
component, or if the code for the component is written by a third party, then
you can introduce a wrapper STA component that first calls CoImpersonateClient
and then calls in the third-party component. There are two
approaches you can use:
- If you must use early binding from your clients, you can
create a separate wrapper component for each third-party component. Each
wrapper component needs separate wrapper methods for each method in its
- If you can use late binding from your clients, you can just
create one generic wrapper component. This wrapper component may create any
third-party component by using ProgID, and it would need only one generic
Invoke method to call into the third-party component.
See the CallByName
function in the "References" section of this article for more
The following example shows how you can:
- Create a new STA thread
- Pass data to the new thread
- Cause the new thread to run under the same identity that
the Web service is impersonating
- Wait for the new thread to finish calling the STA
- Extract the results from the new thread and return the
results to the client
The intent of this sample is only to show how to use your own STA
thread to impersonate and directly access the COM component. In a production
environment, it is best to manage a pool of STA threads instead of creating and
destroying a single thread on each request.
If you use the example that follows, your STA thread is destroyed immediately after you call the COM component, and then you cannot reference any other COM objects that were created in this STA. Therefore, if your COM object returns a reference to a second COM object that is created in the same STA, you receive the following InvalidComObjectException
To make this work you must keep the STA thread alive and then pump messages to invoke the second COM object, which is beyond the scope of this example.
Create a Text File with Limited Permissions
- Create a text file that is named C:\permissions.txt. Enter
some text into the file.
- Right-click Permissions.txt in Microsoft Windows Explorer, and then click Properties.
- On the Security tab, remove all users, and then give full
control to the user who is currently logged on.
Create an STA COM Component to Access the Text File
- Create a new Microsoft Visual Basic 6 ActiveX DLL project
- Add a new Class Module that is named CMyFileSystemObject,
and then paste in the following code:
Dim m_fso As FileSystemObject
Private Sub Class_Initialize()
Set m_fso = New FileSystemObject
Public Function OpenTextFileUsingDefaults(ByVal FileName As String) As CMyTextStream
Set OpenTextFileUsingDefaults = OpenTextFile(FileName)
Public Function OpenTextFile( _
ByVal FileName As String, _
Optional ByVal IOMode As IOMode = ForReading, _
Optional ByVal Create As Boolean = False, _
Optional ByVal Format As Tristate = TristateFalse) As CMyTextStream
Dim oStream As TextStream
Set oStream = m_fso.OpenTextFile(FileName, IOMode, Create, Format)
Set OpenTextFile = New CMyTextStream
- Add a new Class Module that is named CMyTextStream, and
then paste in the following code:
Dim m_oStream As TextStream
Friend Sub Init(oStream As TextStream)
Set m_oStream = oStream
Public Function ReadAll() As String
ReadAll = m_oStream.ReadAll
- Compile FSOWrapper.dll.
Create an ASP.NET WebService to Call the STA COM Component
- Create a new C# ASP.NET WebService project in Microsoft
Visual Studio .NET.
- In the Location field, type
- In the Internet Information Services snap-in for MMC, visit
the STAWebService virtual directory, and then click Properties. Verify that the Integrated Windows authentication is the only
Authentication Method that is selected.
- Add the following under the <system.web> node of the
Web.config file: <identity impersonate="true"/>
- Paste the following code into the Service1.asmx.cs
public class Service1 : WebService
/// Create an STA thread under the current impersonated identity
/// so that it can access the STA DLL directly without losing the impersonation.
public string TestOpenFileFromSTA(string sFile)
StringBuilder returnString = new StringBuilder();
// Start the output...
// Create/init our MySTAThread object.
MySTAThread myThread = new MySTAThread(sFile);
// Start up this new STA thread.
// Wait for the new thread to finish.
bool bWaitRet = myThread.Event.WaitOne(1000,false);
// Finish the output...
FinishOutput(returnString, bWaitRet, myThread);
catch (Exception e)
returnString.Append("<Exception_in_original_thread message = \"" + e.Message + "\"/>");
private void StartOutput(StringBuilder returnString)
returnString.Append("<OriginalIdentity value = \"" + WindowsIdentity.GetCurrent().Name + "\"/>");
private void FinishOutput(StringBuilder returnString, bool bWaitRet, MySTAThread myThread)
returnString.Append("<FinalIdentity value = \"" + WindowsIdentity.GetCurrent().Name + "\"/>");
returnString.Append("<Done_within_timeout value = \"" + bWaitRet + "\"/>");
returnString.Append("<Success value = \"" + myThread.Success + "\"/>");
returnString.Append("<ImpersonatedIdentity value = \"" + myThread.ImpersonatedIdentity + "\"/>");
returnString.Append("<FileContents value = \"" + myThread.FileContents + "\"/>");
returnString.Append("<Exception value = \"" + myThread.Exception + "\"/>");
public class MySTAThread
public static extern bool RevertToSelf();
// You can use public members (or properties) for output...
public bool Success;
public string FileContents;
public string Exception;
public string ImpersonatedIdentity;
// Public event so caller can wait.
public AutoResetEvent Event;
public MySTAThread(string fileName)
// Init values;
Success = false;
Exception = "There was no error!";
this.fileName = fileName;
// Create Thread and Event.
Event = new AutoResetEvent(false);
theThread = new Thread(new ThreadStart(MySTAThreadStart));
// Intialize to an STA so that there will be no thread switch to the STA COM object.
theThread.ApartmentState = ApartmentState.STA;
public void Start()
// Hang on to the current (impersonated) Identity.
impersonatedIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
// This thread is currently impersonating so any thread you start
// will not have permission to impersonate. You must drop the
// current impersonation token so that your new thread can impersonate.
// Start up the new thread.
// Return to the original (impersonated) identity.
// Impersonate using the Token property.
WindowsImpersonationContext ctx = impersonatedIdentity.Impersonate();
// Store current name of the user for verification.
ImpersonatedIdentity = (WindowsIdentity.GetCurrent().Name);
// Access some STA COM object that requires impersonation.
CMyFileSystemObject oMyFSO = new CMyFileSystemObjectClass();
CMyTextStream oMyTxtStream = oMyFSO.OpenTextFileUsingDefaults(fileName);
FileContents = oMyTxtStream.ReadAll();
Success = true;
Exception = e.Message ;
Success = false;
// Drop the impersonation token now that you are finished with it.
// Set the Event property so that the creator can stop waiting on this thread.
Run the WebService from Internet Explorer
- Click Debug | Start Without Debugging (CTRL+F5) menu to start WebService in Internet
- Click the OpenFileFromSTA link.
- Type C:\permissions.txt for the
sFile field, and then click Invoke.
On the new page, you may see code similar to the
<?xml version="1.0" encoding="utf-8" ?>
<OriginalIdentity value="MYDOMAIN\myuser" />
<FinalIdentity value="MYDOMAIN\myuser" />
<Done_within_timeout value="True" />
<Success value="True" />
<ImpersonatedIdentity value="MYDOMAIN\myuser" />
<FileContents value="this is some stuff in the file." />
<Exception value="There was no error!" />
For more information, click the following article numbers to view the articles in the Microsoft Knowledge Base:
Creating STA components in the constructor in ASP.NET ASPCOMPAT mode negatively affects performance
XML Web services and Apartment objects
How to implement impersonation in an ASP.NET application
ASP.NET security overview
For additional information, visit the MSDN Web
Article ID: 325791 - Last Review: March 22, 2007 - Revision: 3.6
- Microsoft Web Services Enhancements for Microsoft .NET 1.1
- Microsoft Web Services (included with the .NET Framework) 1.0
- Microsoft COM+ 2.0 Standard Edition
|kbclient kbdevsecurity kbprb kbsecurity KB325791|