Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / TrustUi / MS / Internal / documents / RightsManagementManager.cs / 1 / RightsManagementManager.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // // Description: // DocumentRightsManagementManager is an internal API for Mongoose to deal // with Rights Management. // // History: // 06/14/05 - [....] created // //--------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings using System; using System.Collections.Generic; using System.IO; using System.IO.Packaging; using System.Security; using System.Security.Permissions; using System.Security.RightsManagement; using System.Text; using System.Threading; using System.Windows.Forms; using System.Windows.Threading; using System.Windows.TrustUI; using MS.Internal.Documents.Application; using MS.Internal.PresentationUI; namespace MS.Internal.Documents { ////// RightsManagementManager is a internal Avalon class used to expose the RM Document API . /// ////// This class serves as the controller that is between the UI and the facade for /// the RM APIs. /// [FriendAccessAllowed] internal sealed class DocumentRightsManagementManager { #region Constructors //----------------------------------------------------- // Constructors //----------------------------------------------------- ////// A constructor that takes the RM provider to use /// ////// Critical - Since the rmProvider is considered critical data. /// [SecurityCritical] private DocumentRightsManagementManager(IRightsManagementProvider rmProvider) { if (rmProvider != null) { _rmProviderCache.Value = rmProvider; } else { throw new ArgumentNullException("rmProvider"); } //Create dictionary for Credential Management //used to map between CredManResources and RM Users _userMap = new Dictionary(); // notify the documentmanager when the license changes PublishLicenseChange += DocumentManager.OnModify; } #endregion Constructors #region Internal Methods //------------------------------------------------------ // Internal Methods //----------------------------------------------------- /// /// Initializes a new singleton instance of the DocumentRightsManagementManager /// /// The provider underlying the manager ////// Critical - Creating a new manager and saving the current instance /// are both critical operations. /// [SecurityCritical] internal static void Initialize(IRightsManagementProvider rmProvider) { Trace.SafeWrite(Trace.Rights, "Initializing RightsManagementManager"); System.Diagnostics.Debug.Assert( _currentManager.Value == null, "RightsManagementManager initialized twice."); if (_currentManager.Value == null) { _currentManager.Value = new DocumentRightsManagementManager(rmProvider); } } ////// Decrypts the encrypted document using the appropriate credentials. /// Returns null if the document is not actually encrypted or the /// encrypted document could not be decrypted. /// ///A decrypted stream containing an XPS document. internal Stream DecryptPackage() { // If the RM client is not installed, prompt the user to install it. // If the user accepts, this will cause a navigation to the install site // in a separate window. if (!IsRMClientInstalled()) { PromptToInstallRM(); // Return null to abort document loading. return null; } Stream returnPackage = null; if (_rmProvider.IsProtected) { Trace.SafeWrite(Trace.Rights, "Document is protected."); // Try to get a set of credentials so we can try to decrypt // the document. If we can't choose any credentials at all, // we can't decrypt the document. if (ChooseCredentials(true)) { // See if the set of credentials chosen has permission to // view the document. RightsManagementLicense license = GetUseLicense(); if (license != null && license.HasPermission(RightsManagementPermissions.AllowView)) { Trace.SafeWrite( Trace.Rights, "User has enough rights; decrypting package."); try { returnPackage = _rmProvider.DecryptPackage(); } catch (RightsManagementException exception) { RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Decrypt, exception); } } } } return returnPackage; } ////// Forces an Evaluate which will result in RMPolicy event being fired. This is /// only valid after a decrypted Package is available (i.e. either the protected /// package has been decrypted, or the package was never protected). /// ////// Critical /// 1) Determines the current RMPolicy and issues an event with the /// resulting policy. /// TreatAsSafe /// 1) The resulting policy is either hardcoded or acquired from the /// _rmProvider, which is critical for set. /// [SecurityCritical, SecurityTreatAsSafe] internal void Evaluate() { RightsManagementStatus calcRMStatus = RightsManagementStatus.Unprotected; RightsManagementPolicy calcRMPolicy = RightsManagementPolicy.AllowView; //Check to see if document is protected. if (_rmProvider.IsProtected) { calcRMStatus = RightsManagementStatus.Protected; // Get the rights granted from the RM license RightsManagementLicense rmLicense = GetUseLicense(); if (rmLicense == null) { calcRMPolicy = RightsManagementPolicy.AllowNothing; } else { calcRMPolicy = rmLicense.ConvertToPolicy(); } } else { Trace.SafeWrite( Trace.Rights, "Evaluate: Document is not protected; using default policy."); //There is no RM policy applied to this document. calcRMPolicy = RightsManagementPolicy.AllowAnnotate | RightsManagementPolicy.AllowCopy | RightsManagementPolicy.AllowSign | RightsManagementPolicy.AllowPrint | RightsManagementPolicy.AllowView; } //Fire the events. OnRMStatusChange(calcRMStatus); OnRMPolicyChange(calcRMPolicy); } ////// Sets the EncryptedPackageEnvelope on the provider to a new value /// and invalidates the saved publish and use licenses if necessary. /// The package passed can be null to indicate that the new package is /// not protected. /// /// The new encrypted package (possibly null) /// ////// Critical /// 1) Calling SecurityCritical function SetEncryptedPackage on the /// provider with an external parameter. /// [SecurityCritical] internal void SetEncryptedPackage(EncryptedPackageEnvelope newPackage) { bool publishLicenseChanged = true; try { _rmProvider.SetEncryptedPackage( newPackage, out publishLicenseChanged); } catch (RightsManagementException exception) { RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Decrypt, exception); // On an exception here we can't continue; rethrow it in an // XPSViewer exception throw new XpsViewerException( SR.Get(SRID.XpsViewerRightsManagementException), exception); } if (_rmProvider.IsProtected) { // Get a use license for the new package RightsManagementLicense license = GetUseLicense(); if (license == null || !license.HasPermission(RightsManagementPermissions.AllowView)) { throw new XpsViewerException( SR.Get(SRID.RMProviderExceptionNoRightsToDocument)); } } if (publishLicenseChanged) { // Fire the event to indicate a new publish license OnPublishLicenseChange(); } // Evaluate the status of the new package (to fire events) Evaluate(); } ////// Displays the Credential Management UI. /// internal void ShowCredentialManagementUI() { ShowCredentialManagementUI(false); } ////// Displays the Credential Management UI. /// /// Whether the dialog is being shown in the /// process of decrypting the document for the first time ///The result of the credential management dialog ////// Critical /// 1) Calls critical methods GetCredentialManagerResourceList and /// GetDefaultCredentialManagementResource to get the list of user /// accounts to pass to the dialog. /// 2) Elevation to show a top level window. /// 3) Sets _credManagerDialog which is critical /// TreatAsSafe /// 1) The lists are passed to the Credential Manager dialog, which is /// a WinForms dialog. The handle to the dialog is critical for /// get. /// 2) The window is the blessed internal Credential Manager dialog, /// which uses Windows Forms instead of Avalon. As above, the /// handle is saved in a critical for get member variable. /// 3) _credManagerDialog is set to an actual CredentialManagerDialog. /// [SecurityCritical, SecurityTreatAsSafe] internal DialogResult ShowCredentialManagementUI(bool decrypting) { DialogResult result = DialogResult.Cancel; IListaccountList = GetCredentialManagementResourceList(); string userAccount = GetDefaultCredentialManagementResource(); RightsManagementUser previousDefaultUser = _rmProvider.GetDefaultCredentials(); _credManagerDialog = null; (new UIPermission(UIPermissionWindow.AllWindows)).Assert(); //BlessedAssert try { _credManagerDialog = new CredentialManagerDialog(accountList, userAccount, this); result = _credManagerDialog.ShowDialog(); } finally { UIPermission.RevertAssert(); if (_credManagerDialog != null) { _credManagerDialog.Dispose(); } } RightsManagementUser newDefaultUser = _rmProvider.GetDefaultCredentials(); // When a new rm account is added a new SecureEnvironment is created in order // to validate the new account. If a document is currently protected this will // mean that the existing SecureEnvironment is destroyed, which puts the document // in an unreliable state. The only way to correct this is to reload the document. // Check if the document is done decrypting, is protected, and if the // SecureEnvironment is in an unreliable state or the default user has been // changed. If so reload the document. if (!decrypting && _rmProvider.IsProtected && (!_isSecureEnvironmentReliable || !previousDefaultUser.Equals(newDefaultUser))) { DocumentManager.CreateDefault().Reload(null); } // Check if the default user has changed, and setup an environment for this user. // This case will only be hit for unprotected, or undecrypted documents (by the // previous if statement) where it is reliable to create a new SecureEnvironment. else if (!previousDefaultUser.Equals(newDefaultUser)) { try { _rmProvider.InitializeEnvironment(newDefaultUser); } catch (RightsManagementException exception) { bool handled = RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Other, exception); } } return result; } /// /// ShowEnrollment: Displays the RM enrollment. /// ////// Critical - Elevation to show a top level window. /// TreatAsSafe - The window is a blessed internal window that uses purely Windows Forms rather than Avalon. /// [SecurityCritical, SecurityTreatAsSafe] internal void ShowEnrollment() { DialogResult result = DialogResult.Cancel; EnrollmentAccountType enrollmentAccountType = EnrollmentAccountType.None; (new UIPermission(UIPermissionWindow.AllWindows)).Assert(); //Blessed try { // skip first page if already enrolled if (_rmProvider.CurrentUser != null || _rmProvider.GetDefaultCredentials() != null) { result = DialogResult.OK; } else { //Show the First page using (RMEnrollmentPage1 page1 = new RMEnrollmentPage1()) { result = page1.ShowDialog(); } } if (result == DialogResult.OK) { using (RMEnrollmentPage2 page2 = new RMEnrollmentPage2()) { //Show the second page result = page2.ShowDialog(); enrollmentAccountType = page2.AccountTypeSelected; } } } finally { UIPermission.RevertAssert(); } if (result == System.Windows.Forms.DialogResult.OK) { //It is now time to enroll. Enroll(enrollmentAccountType); } } ////// Enroll. /// /// The account type to enroll ///Whether or not enrollment succeeded ////// Critical - Elevation to show a top level window. /// [SecurityCritical] internal bool Enroll(EnrollmentAccountType accountType) { bool rtn = false; RMEnrollmentPage3 rmEnrollmentPage3 = null; //User has selected to proceed. Now we need to Enroll. This will //Require two thing. 1) start the blocking enroll call //(call QueueWorkItem) and 2) showing a status/progress Dialog while //we are enrolling. (new UIPermission(UIPermissionWindow.AllWindows)).Assert(); //BlessedAssert try { rmEnrollmentPage3 = new RMEnrollmentPage3(); } finally { UIPermission.RevertAssert(); } RightsManagementEnrollThreadInfo rmEnrollThreadInfo = new RightsManagementEnrollThreadInfo(); //Setup Fields rmEnrollThreadInfo.AccountType = accountType; rmEnrollThreadInfo.ProgressForm = rmEnrollmentPage3; // Pass work off so UI doesn't block. // We use WaitCallback here because that is the delegate that is // supposed to be passed to QueueUserWorkItem if (ThreadPool.QueueUserWorkItem(new WaitCallback(RMEnrollThreadProc), rmEnrollThreadInfo)) { (new UIPermission(UIPermissionWindow.AllWindows)).Assert(); //BlessedAssert try { rmEnrollmentPage3.ShowDialog(); } finally { UIPermission.RevertAssert(); } rtn = (_rmProvider.CurrentUser != null); } // Since we've called the enrollment, the current SecureEnvironment is not reliable. _isSecureEnvironmentReliable = false; return rtn; } ////// Launches the My Permissions UI. /// ////// Critical /// 1) Elevation to show a top level window. /// 2) Calls _rmProvider.GetAllAccessRights. /// 3) Constructs an RMPermissions dialog using critical data. /// TreatAsSafe /// 1) The window is an internal window that uses purely Windows Forms /// instead of Avalon. The data shown in the window is also /// retrieved directly from the provider and protected. /// 2,3) The data returned from GetAllAccessRights is passed directly /// to the UI (rmPermissionsPage). /// [SecurityCritical, SecurityTreatAsSafe] internal void ShowPermissions() { RightsManagementLicense rmLicense = GetUseLicense(); // A null license means the user has no rights to the document, and // this should never have been called. Invariant.Assert( rmLicense != null, "Permissions dialog requested with no rights on the document."); // If this is the owner call ShowPublishing instead. if (rmLicense.HasPermission(RightsManagementPermissions.AllowOwner)) { ShowPublishing(); } else { // User wants to view the My Permissions dialog. RMPermissionsDialog rmPermissionsPage = null; new UIPermission(UIPermissionWindow.AllWindows).Assert(); //BlessedAssert try { rmPermissionsPage = new RMPermissionsDialog(rmLicense); rmPermissionsPage.ShowDialog(); } finally { UIPermission.RevertAssert(); if (rmPermissionsPage != null) { rmPermissionsPage.Dispose(); } } } } ////// Displays the publishing UI. /// ////// Critical /// 1) Elevation to show a top level window. /// 2) Call to critical GetAllAccessRights function in the provider /// 3) Call to critical CurrentUser property of the provider /// 4) Calls to critical SaveCurrentLicenses and RevertToSavedLicenses /// on the provider /// 5) Call to critical Licenses property of RMPublishingDialog /// 6) Call to critical ProcessRMLicenses /// 7) Call to critical Template property of the RMPublishingDialog /// 8) Call to critical ProcessRMTemplate /// TreatAsSafe /// 1) The window is an internal window that uses purely Windows /// Forms instead of Avalon. The data shown in the window is /// also retrieved directly from the provider and protected. /// 2) The data from this call is not saved anywhere. It is only /// passed to the critical constructor of the RM publishing /// dialog. /// 3) The data is propagated only to the critical constructor of /// an internal dialog. /// 4) These calls are used for their intended purpose -- reverting to /// the previous state after an error. /// 5) Licenses to grant are passed directly into the provider and /// not saved at this level or exposed. /// 6) Data going into this call comes directly from the UI and /// is then validated. /// 7) Template uri is taken directly from the dialog, and not /// stored or exposed. /// 8) The template filename acquired through critical means on the /// RMPublishingDialog is used to open a text file and read in data. /// The data is validated by the RM API's. /// [SecurityCritical, SecurityTreatAsSafe] internal void ShowPublishing() { if (!ChooseCredentials(false)) { // If there are no credentials, and we can't select any, we // should just bail out. If there was an error somewhere // during choosing credentials, an explanatory message box // should already have been shown. return; } if (_rmProvider.IsProtected) { RightsManagementLicense license = GetUseLicense(); if (license == null || !(license.HasPermission(RightsManagementPermissions.AllowOwner))) { throw new InvalidOperationException( SR.Get(SRID.RMProviderExceptionNotOwnerOfDocument)); } } IDictionaryallRights = null; if (_rmProvider.IsProtected) { allRights = _rmProvider.GetAllAccessRights(); } // Create the publishing dialog outside of the while loop so that it is only // constructed once, rather than on each loop. RMPublishingDialog rmPublish = null; bool statusChanged = false; try { // Elevate to create the actual dialog. new UIPermission(UIPermissionWindow.AllWindows).Assert(); //BlessedAssert try { rmPublish = new RMPublishingDialog(_rmProvider.CurrentUser, allRights); } finally { UIPermission.RevertAssert(); } // Keep showing the dialog until it succeeds or the user cancels while (true) { DialogResult result = DialogResult.Cancel; // Display the dialog to the user. result = rmPublish.ShowDialog(); // If the user canceled out of the dialog, break out of the loop and // do not apply changes. if (result != DialogResult.OK) { break; } bool publishingSuccess = false; bool publishLicenseChanged = false; bool exitDialog = false; // Save the current set of licenses for rollback _rmProvider.SaveCurrentLicenses(); // If a template exists on the publishing dialog then user has selected // a template rather than individual settings. if (rmPublish.Template != null) { publishingSuccess = ProcessRMTemplate(rmPublish.Template, out exitDialog); publishLicenseChanged = true; } // Template was not selected, so carry on with individual permissions. else { publishingSuccess = ProcessRMLicenses( rmPublish.Licenses, out exitDialog, out publishLicenseChanged); } // Check if a signed publish license was successfully created. // If not take the appropriate action. if (!publishingSuccess) { // If exitDialog is true, then an error has happened that is severe // enough to stop running the Publish dialog. if (exitDialog) { return; } // Otherwise continue with running the dialog for another attempt // to get the settings correct. else { continue; } } // If the publish license actually changed during the // publishing operation, fire the event to indicate this if (publishLicenseChanged) { OnPublishLicenseChange(); } // Save the protected document. DocumentManager docManager = DocumentManager.CreateDefault(); bool saveSuccess = false; if (docManager != null) { if (rmPublish.IsSaveAs) { saveSuccess = docManager.SaveAs(null); } else { saveSuccess = docManager.Save(null); } } if (publishingSuccess && !saveSuccess) { _rmProvider.RevertToSavedLicenses(); continue; } statusChanged = publishingSuccess && saveSuccess && publishLicenseChanged; break; } // end while(true) } finally { if (rmPublish != null) { rmPublish.Dispose(); } } // If the status changed, call Evaluate to re-evaluate the RM // status of the document and fire the appropriate events if (statusChanged) { Evaluate(); } } /// /// Get User account from credentialManagementResources and sets as default. /// ////// Critical /// 1) Method uses critical data stored in _userMap. /// 2) Calls critical function SetDefaultCredentials with the user /// object corresponding to a string passed in as an argument. /// [SecurityCritical] internal void OnCredentialManagementSetDefault(string defaultAccount) { //Get user from user name RightsManagementUser user = _userMap[defaultAccount]; //if found set default if (user != null) { _rmProvider.SetDefaultCredentials(user); } } ////// Removes user from available user list. /// ////// Critical /// 1) Uses critical data stored in _userMap. /// 2) Calls critical function RemoveCredentials with a user object /// created from a string passed in as an argument. /// 3) Calls critical methods GetCredentialManagementResourceList and /// GetDefaultCredentialManagementResource. /// 4) Calls a method on critical _credManagerDialog, but does not /// read or save any critical user information from it. /// [SecurityCritical] internal void OnCredentialManagementRemove(string accountName) { //Get user from hash RightsManagementUser user = _userMap[accountName]; //if found remove (if this user isn't in use, if it is in use warn user) if (user != null && !user.Equals(_rmProvider.CurrentUser)) { try { _rmProvider.RemoveCredentials(user); if (_credManagerDialog != null) { //Set the data source for the listbox _credManagerDialog.SetCredentialManagementList( GetCredentialManagementResourceList(), GetDefaultCredentialManagementResource()); } } catch (RightsManagementException exception) { RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Other, exception); // If the remove failed just bail out return; } } else { System.Windows.MessageBox.Show( SR.Get(SRID.RightsManagementWarnErrorFailedToRemoveUser), SR.Get(SRID.RightsManagementWarnErrorTitle), System.Windows.MessageBoxButton.OK, System.Windows.MessageBoxImage.Exclamation ); } } ////// Calls the Enrollment and refreshes the listbox. /// ////// Critical /// 1) Calls critical methods GetCredentialManagementResourceList and /// GetDefaultCredentialManagementResource. /// 4) Calls a method on critical _credManagerDialog, but does not /// read or save any critical user information from it. /// [SecurityCritical] internal void OnCredentialManagementShowEnrollment() { ShowEnrollment(); if (_credManagerDialog != null) { //Set the data source for the listbox _credManagerDialog.SetCredentialManagementList( GetCredentialManagementResourceList(), GetDefaultCredentialManagementResource()); } } ////// Prompts the user to see if he or she would like to install the RM Client. /// If the user so chooses, we will then start a top-level navigation to /// the external install location for the RM Client. /// ////// Critical /// 1) Makes the determination if we should navigate to an external Uri. /// 2) Kicks off a top-level navigation to a known external Uri. /// TreatAsSafe /// 1) The top level navigation is user initiated /// 2) The navigation is to a predefined, hardcoded Uri. /// [SecurityCritical, SecurityTreatAsSafe] internal void PromptToInstallRM() { // Notify the user that RM is not installed, and ask // if they would like to install it now. System.Windows.MessageBoxResult dialogResult = System.Windows.MessageBox.Show( SR.Get(SRID.RightsManagementWarnErrorRMNotInstalled), SR.Get(SRID.RightsManagementWarnErrorTitle), System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxImage.Warning); if (dialogResult == System.Windows.MessageBoxResult.Yes) { // Navigate to the install location const string rmClientInstallLocation = "http://go.microsoft.com/fwlink/?LinkID=18134"; NavigationHelper.NavigateToExternalUri(new Uri(rmClientInstallLocation)); } } ////// Gets a List of CredentialManagmentResources. /// ////// Critical /// 1) Calling critical code RMProvider::GetAvailableCredentials. /// 2) Calling critical code GetCredentialManagementResources /// 3) Setting critical data _userMap from the list of users returned /// by GetAvailableCredentials /// 4) Returns a list of user names /// [SecurityCritical] internal IListGetCredentialManagementResourceList() { _userMap.Clear(); //create list for CredentialManagementResources IList accountList = new List (); //Loop through each user and create listbox string and add to return list foreach (RightsManagementUser user in _rmProvider.GetAvailableCredentials()) { string accountName = RightsManagementResourceHelper.GetCredentialManagementResources(user); if (!accountList.Contains(accountName)) { //Add to map and to list. _userMap.Add(accountName, user); accountList.Add(accountName); } } return accountList; } /// /// Gets default CredentialManagmentResources. /// ////// Critical /// 1) Calling critical code RMProvider::GetDefaultCredentials. /// 2) Calling critical code GetCredentialManagementResources /// [SecurityCritical] internal string GetDefaultCredentialManagementResource() { string userAccount = null; RightsManagementUser user = _rmProvider.GetDefaultCredentials(); if (user != null) { userAccount = RightsManagementResourceHelper.GetCredentialManagementResources(user); } return userAccount; } #endregion Internal Methods #region Internal Properties //------------------------------------------------------ // Internal Properties //------------------------------------------------------ ////// Gets the current singleton instance of the DocumentRightsManagementManager /// internal static DocumentRightsManagementManager Current { get { return _currentManager.Value; } } ////// Gets the current publish license associated with the document. /// internal PublishLicense PublishLicense { // Commented out since not used, but left to show that a set only property // was implemented by design. // get { return _rmProvider.CurrentPublishLicense; } set { if (_rmProvider.CurrentPublishLicense != value) { _rmProvider.CurrentPublishLicense = value; OnPublishLicenseChange(); } } } ////// Gets whether or not the user has permission to save the document. /// ////// Critical /// 1) Makes a policy enforcement decision /// TreatAsSafe /// 1) The data used for the decision comes from the critical for set /// provider. /// internal bool HasPermissionToSave { [SecurityCritical, SecurityTreatAsSafe] get { return !_rmProvider.IsProtected || GetUseLicense().HasPermission(RightsManagementPermissions.AllowCopy); } } ////// Gets whether or not the user has permission to edit the document. /// ////// Critical /// 1) Makes a policy enforcement decision /// TreatAsSafe /// 1) The data used for the decision comes from the critical for set /// provider. /// internal bool HasPermissionToEdit { [SecurityCritical, SecurityTreatAsSafe] get { return !_rmProvider.IsProtected || GetUseLicense().HasPermission(RightsManagementPermissions.AllowEdit); } } ////// Returns true if we find the RM Client is installed on the machine. /// ////// We only make the actual check if RM is installed /// once per application instance. Is this okay? Or should we keep /// checking to see if this gets installed over the course of the /// application? /// internal bool IsRMInstalled { get { if (! _determinedRMInstallState) { // Find out if the RM Client is installed. _isRMInstalled = IsRMClientInstalled(); _determinedRMInstallState = true; } return _isRMInstalled; } } #endregion Internal Properties #region Internal Event //----------------------------------------------------- // Internal Event //------------------------------------------------------ internal event RMStatusChangeHandler RMStatusChange; internal event EventHandler PublishLicenseChange; internal event RMPolicyChangeHandler RMPolicyChange; #endregion Internal Event #region Internal Delegate //----------------------------------------------------- // Internal Delegate //----------------------------------------------------- internal delegate void RMStatusChangeHandler(object sender, RightsManagementStatusEventArgs args ); internal delegate void RMPolicyChangeHandler(object sender, RightsManagementPolicyEventArgs args ); #endregion Internal Delegate #region Private Methods //----------------------------------------------------- // Private Methods //------------------------------------------------------ ////// Saves the use license back into the package. /// private void SaveUseLicense() { //This action should occur after other work has been completed. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, (DispatcherOperationCallback)delegate(object arg) { DocumentManager docManager = DocumentManager.CreateDefault(); if (docManager.CanSave) { Trace.SafeWrite( Trace.Rights, "Re-opening document for edit before saving Use License"); docManager.EnableEdit(null); } // Check if saving is still possible (i.e. the file could // be reopened for edit). // // This breaks encapsulation -- we happen to know that // docManager.CanSave indicates only that the file could // be opened writeable. We also know that saving an RM // protected document will save the use license back to // the file. if (docManager.CanSave) { Trace.SafeWrite( Trace.Rights, "Opened original file writeable; saving Use License"); docManager.Save(null); } return null; }, null /* DispatcherOperation argument, none specified */); } ////// Determines if the RM client is installed by looking for /// MSDRM.DLL in the user's system folder. /// ///Whether RM Client DLL, msdrm.dll, was found ////// Critical /// 1) Asserts for FileIOPermission to get the system path. /// 2) Asserts for FileIOPermission to check if the file, MSDRM.DLL exists. /// /// TreatAsSafe /// 1) The system path value does not leave this method. /// 2) This path information used to determine if the file exists /// never leaves this method. /// [SecurityCritical, SecurityTreatAsSafe] private bool IsRMClientInstalled() { bool foundRMClient = false; string systemPath = string.Empty; // Build the path for the MSDRM.DLL // Environment.GetFolderPath() requires FileIOPermission - PathDiscovery FileIOPermission getSystemPath = new FileIOPermission(PermissionState.None); getSystemPath.AllLocalFiles = FileIOPermissionAccess.PathDiscovery; getSystemPath.Assert(); //BlessedAssert try { systemPath = Environment.GetFolderPath(Environment.SpecialFolder.System); } finally { FileIOPermission.RevertAssert(); } // Append the DLL filename. const string msdrmDLLName = "MSDRM.DLL"; string msdrmdllPath = Path.Combine(systemPath, msdrmDLLName); // Check if the DLL exists. new FileIOPermission(FileIOPermissionAccess.Read, msdrmdllPath).Assert(); // BlessedAssert try { foundRMClient = File.Exists(msdrmdllPath); } finally { FileIOPermission.RevertAssert(); } return foundRMClient; } ////// Attempts to automatically choose the credentials to use to access /// the RM-protected document or add RM protection to an existing /// document. /// /// Whether or not we are choosing credentials /// to decrypt or encrypt a document ///Whether or not credentials were successfully chosen ////// Critical /// 1) accesses Critical property CurrentUser /// 2) accesses Critical function GetDefaultCredentials /// 3) calls SecurityCritical function InitializeEnvironment(User) /// TreatAsSafe /// 1) The value returned from CurrentUser is not saved or propagated; /// it is only checked for null. /// 2) The returned values from GetDefaultCredentials are not saved. /// They are only passed to the critical function /// InitializeEnvironment. /// 3) Parameter comes directly from the list of credentials available /// to the user returned from critical call GetDefaultCredentials /// [SecurityCritical, SecurityTreatAsSafe] private bool ChooseCredentials(bool decrypting) { bool foundCredentials = false; // If there already is a user selected there is no need to select // credentials again if (_rmProvider.CurrentUser != null) { return true; } //Get default user RightsManagementUser user = _rmProvider.GetDefaultCredentials(); // Save whether or not RM has been initialized so that we don't do // it twice bool initialized = false; //Check to see if we have a user. If we don't we need to enroll. if (user == null) { //Show enrollment UI ShowEnrollment(); // Get the current user. If we don't have one after enrollment // that means the user cancelled enrollment or something else // went wrong. user = _rmProvider.CurrentUser; // If the enrollment was successful if (user != null) { // Set the enrolled user as the default user in the registry _rmProvider.SetDefaultCredentials(user); initialized = true; } } // Now we will see if this user account has rights for the document while (user != null) { if (!initialized) { try { // Attempt to initialize the environment _rmProvider.InitializeEnvironment(user); initialized = true; } catch (RightsManagementException exception) { bool handled = RightsManagementErrorHandler.HandleOrRethrowException( decrypting ? RightsManagementOperation.Initialize : RightsManagementOperation.Other, exception); // If the error couldn't be handled cleanly, give up if (!handled) { break; } } } // If the initialization succeeded and the document is // protected by RM, check if the current user can view the // document if (initialized && _rmProvider.IsProtected) { RightsManagementLicense license = GetUseLicense(); // Check if the user has permissions to view the document if (license != null && license.HasPermission(RightsManagementPermissions.AllowView)) { foundCredentials = true; break; } // The current user doesn't have permissions else { // Ask the user whether we should try a different set of // credentials or just give up. System.Windows.MessageBoxResult result = System.Windows.MessageBox.Show( SR.Get(SRID.RightsManagementWarnErrorNoPermission), SR.Get(SRID.RightsManagementWarnErrorTitle), System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxImage.Exclamation ); // If the user didn't want to continue, bail out if (result != System.Windows.MessageBoxResult.Yes) { break; } } } // If the document is not protected, there is no need to check // whether the user can view the document as long as the // environment was properly initialized else if (initialized) { foundCredentials = true; break; } // The current default user doesn't have permission to view // this container, or there was an error when attempting to // initialize the environment with the current default user. // // Show the CredMan to allow user to change default account if(ShowCredentialManagementUI(decrypting) != DialogResult.OK) { break; } //Get new default user. user = _rmProvider.GetDefaultCredentials(); // The provider's CurrentUser property is set only when an // environment has been initialized. As a result comparing the // credentials we will use to the provider's CurrentUser can // tell us whether or not the provider has been initialized for // that user. initialized = (user.Equals(_rmProvider.CurrentUser)); } return foundCredentials; } ////// Retrieves the use license for the document and returns the rights granted to /// the current user. Returns null if the user does not have rights to the /// document. /// ///The rights granted to the user. ////// Critical - 1) accesses Critical property CurrentUser /// TreatAsSafe - 1) The value returned from CurrentUser is not saved or /// propagated; it is only checked for null. /// [SecurityCritical, SecurityTreatAsSafe] private RightsManagementLicense GetUseLicense() { if (_rmProvider.IsProtected && _rmProvider.CurrentUseLicense == null) { Invariant.Assert( _rmProvider.CurrentUser != null, "No user for whom to get a license."); try { if (_rmProvider.LoadUseLicense()) { _rmProvider.BindUseLicense(); } else if (_rmProvider.AcquireUseLicense()) { // We have just acquired a new use license; this needs to be stored back // into the package. _rmProvider.BindUseLicense(); SaveUseLicense(); } else { // No use license was found, which means that the // current user has no rights to the document. We // return null and let the caller decide what to do. } } catch (RightsManagementException exception) { // Whether or not the exception was handled is irrelevant, // since if the use license wasn't bound, the function will // return null RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Decrypt, exception); } } return _rmProvider.CurrentUseLicense; } ////// Checks if the grants listed in the licenses passed in as an argument are /// different from the ones already granted on the document. /// /// A list of new grants to compare with the existing /// set ////// Can be thrown by the RM APIs when there is an error when getting /// all existing access rights on the document ///Whether or not the new grants are different ////// Critical - 1) Calls Critical function GetAllAccessRights to compare rights /// passed in as an argument to existing rights. /// TreatAsSafe - 1) The list of existing grants is not saved or exposed. /// [SecurityCritical, SecurityTreatAsSafe] private bool HasPublishLicenseChanged(IListnewGrants) { // If the desired state is that the document will not be protected (i.e. no // licenses are specified), we check if the document was already not // protected. if (newGrants == null || newGrants.Count == 0) { return _rmProvider.IsProtected; } // Check if we are adding protection to an unprotected document if (!_rmProvider.IsProtected) { return true; } // This might throw, but it will be caught in the caller IDictionary allRights = _rmProvider.GetAllAccessRights(); // Check if the number of licenses is different if (newGrants.Count != allRights.Count) { return true; } // Look through each license to see if it is different from the old ones. // We already know that the number of licenses is the same in both lists of // licenses, so any other differences will be detected here. foreach (RightsManagementLicense newLicense in newGrants) { if (!allRights.ContainsKey(newLicense.LicensedUser)) { return true; } RightsManagementLicense existingLicense = allRights[newLicense.LicensedUser]; // Check if both licenses grant the same permissions if (newLicense.LicensePermissions != existingLicense.LicensePermissions) { return true; } if (DateTime.Compare( newLicense.ValidUntil, existingLicense.ValidUntil) != 0) { return true; } if (!Uri.Equals( newLicense.ReferralInfoUri, existingLicense.ReferralInfoUri)) { return true; } } return false; } /// /// The thread operation that enrolls a new Rights Management user. /// private void RMEnrollThreadProc(Object stateInfo) { Trace.SafeWrite(Trace.Rights, "Enrollment thread started."); //Get Thread Info RightsManagementEnrollThreadInfo rmEnrollThreadInfo = (RightsManagementEnrollThreadInfo)stateInfo; //Enroll try { _rmProvider.InitializeEnvironment(rmEnrollThreadInfo.AccountType); //Enrollment is done. Let's close the Progress dialog. rmEnrollThreadInfo.ProgressForm.Invoke(new MethodInvoker(rmEnrollThreadInfo.ProgressForm.Close)); } catch (RightsManagementException exception) { rmEnrollThreadInfo.ProgressForm.Invoke(new MethodInvoker(rmEnrollThreadInfo.ProgressForm.Close)); RightsManagementOperation operation = RightsManagementOperation.Other; if (rmEnrollThreadInfo.AccountType == EnrollmentAccountType.NET) { operation = RightsManagementOperation.PassportActivation; } RightsManagementErrorHandler.HandleOrRethrowException( operation, exception); } // Close the progress dialog in the event of any exception. This prevents the // dialog from remaining open when the error page appears. We cannot use a finally // block here because in the case of an uncaught exception the unhandled exception // handler will get the exception (and show the error page) before the block runs. catch { rmEnrollThreadInfo.ProgressForm.Invoke(new MethodInvoker(rmEnrollThreadInfo.ProgressForm.Close)); throw; } Trace.SafeWrite(Trace.Rights, "Enrollment thread ended."); } ////// Creates an appropriate EventArgs and sends the RMStatusChange event. /// /// The RM status to put in the EventArgs /// ////// This sends an event to update the UI which displays whether or not /// a document is signed, which may be used for a trust decision by the /// user. However, since the UI is currently displayed in an Avalon /// control, making the events themselves critical does not add any /// protection. /// private void OnRMStatusChange(RightsManagementStatus newStatus) { Trace.SafeWrite(Trace.Rights, "RightsManagementStatus changed."); RightsManagementStatusEventArgs args = new RightsManagementStatusEventArgs( newStatus); RaiseRMStatusChange(args); } ////// Raises an RMStatusChange event with the given arguments. /// /// The event arguments private void RaiseRMStatusChange(RightsManagementStatusEventArgs args) { if (RMStatusChange != null) { RMStatusChange(this, args); } } ////// Creates an appropriate EventArgs and sends the RMPolicyChange event. /// /// The RM policy to put in the EventArgs /// ////// Critical /// 1) The RaiseRMPolicyChange event is fired, and the parameter /// newPolicy is used within the EventArgs. /// [SecurityCritical] private void OnRMPolicyChange(RightsManagementPolicy newPolicy) { Trace.SafeWrite(Trace.Rights, "RightsManagementPolicy changed."); RightsManagementPolicyEventArgs args = new RightsManagementPolicyEventArgs(newPolicy); RaiseRMPolicyChange(args); } ////// Raises an RMPolicyChange event with the given arguments. /// /// The event arguments private void RaiseRMPolicyChange(RightsManagementPolicyEventArgs args) { if (RMPolicyChange != null) { RMPolicyChange(this, args); } } ////// Fires the event that indicates that the publish license has changed. /// private void OnPublishLicenseChange() { Trace.SafeWrite(Trace.Rights, "Publish License changed."); RaisePublishLicenseChange(null); } ////// Raises the PublishLicenseChange with the given arguments. /// /// The event arguments private void RaisePublishLicenseChange(EventArgs args) { if (PublishLicenseChange != null) { PublishLicenseChange(this, args); } } ////// Using the provided list of licenses, attempt to setup a signed /// publish license for the document. /// /// The licenses to process. /// True if an error has occurred that should /// exit the RMPublishing dialog /// True if the grants passed in /// are different from the ones in the document ///True on success, false on failure. ////// Critical /// 1) Call to critical GenerateUnsignedPublishLicense function in the /// provider /// 2) Calls IRightsManagementProvider.GenerateUnsignedPublishLicense /// 3) Calls IRightsManagementProvider.SignPublishLicense /// [SecurityCritical] private bool ProcessRMLicenses( IListlicenses, out bool exitDialog, out bool publishLicenseChanged) { // By default exitDialog is true so that on unhandled errors the publish // dialog will not reload. exitDialog = true; publishLicenseChanged = true; try { publishLicenseChanged = HasPublishLicenseChanged(licenses); } catch (RightsManagementException exception) { bool handled = RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Other, exception); // If there was an exception (that could not be // handled) that occurred in detecting whether the // publish license changed, bail out if (!handled) { return false; } } // If the new publish license is unchanged don't bother // doing anything else if (publishLicenseChanged) { if (licenses != null && licenses.Count > 0) { try { _rmProvider.GenerateUnsignedPublishLicense(licenses); _rmProvider.SignPublishLicense(); } catch (RightsManagementException exception) { bool handled = RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.Other, exception); // If the exception was handled, don't exit the dialog if (handled) { exitDialog = false; } // Stop processing and post dialog. return false; } } else { this.PublishLicense = null; } } // If we made it this far then it is considered success exitDialog = false; return true; } /// /// Using the provided template filename, attempt to load the template from /// disk and setup a signed publish license from it. /// /// The template file to load. /// Used to determine if the RMPublishing /// dialog should exit. ///True on success, false on failure. ////// Critical /// 1) Calls GetTemplateFromFile. /// 2) Calls IRightsManagementProvider.GenerateUnsignedPublishLicense /// 3) Calls IRightsManagementProvider.SignPublishLicense /// [SecurityCritical] private bool ProcessRMTemplate(Uri templateFilename, out bool exitDialog) { string template = string.Empty; // With the current template system we should never bail on the dialog // as we will inform the user and allow them to select again. exitDialog = false; try { // Load the template from file. template = GetTemplateFromFile(templateFilename); } // This generic catch allows all failures of loading the file to be // directed to our central error handler which will show the // appropriate error message to the user. Since this operation is // classified as a critical operation, the error handler will re-throw // the exception to allow it to be handled higher on the stack if it // is fatal (which includes all exceptions not specifically handled // by the error handler). #pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions.. catch (Exception exception) { // This exception will be thrown if there is a problem // reading the template from the file. // We need to handle the method and determine the // appropriate action. RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.TemplateAccess, exception); // Since any exception means the file was not loaded, bail out. return false; } // Ensure that template was actually loaded. if (!string.IsNullOrEmpty(template)) { try { // Generate the license from the template. _rmProvider.GenerateUnsignedPublishLicense(template); _rmProvider.SignPublishLicense(); } catch (RightsManagementException exception) { // This exception will be thrown if there is a problem // either parsing/loading the template, or signing it. // In either case a message should be displayed informing // the user about the invalid template. RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.TemplateAccess, exception); // Since any exception means the file was not loaded, bail out. return false; } } else { // The template could not be loaded correctly from disk // (could be that it was removed, permissions are incorrect, // network down, etc), so display an error message. RightsManagementErrorHandler.HandleOrRethrowException( RightsManagementOperation.TemplateAccess, new FileFormatException()); // Since any exception means the file was not loaded, bail out. return false; } // Set exitDialog to false to continue with the signing process. exitDialog = false; return true; } ////// Open a template file and attempt to load it into _template. /// /// The filename (with path) to load. ///The text string read from the file if successful, otherwise /// string.Empty ////// Critical /// 1) Asserts for FileIOPermission. /// [SecurityCritical] private string GetTemplateFromFile(Uri templateFilename) { // Set the default value, this value will remain if any of the file // loading fails. string template = string.Empty; // If a template exists on the publishing dialog then user has selected // a template rather than individual settings. if (templateFilename != null) { // Save the path local to this method to guarantee it does not // change between the assert and the file access string templateLocalPath = templateFilename.LocalPath; // Assert permission and read in template. (new FileIOPermission( FileIOPermissionAccess.Read, templateLocalPath)) .Assert(); //BlessedAssert try { // The RM client requires that templates are in UTF16 // (Encoding.Unicode) format. template = File.ReadAllText(templateLocalPath, Encoding.Unicode); } finally { FileIOPermission.RevertAssert(); } } return template; } #endregion Private Methods #region Private Properties //----------------------------------------------------- // Private Properties //------------------------------------------------------ ////// Setup as a clr property to transparently use CriticalDataForSet. /// private IRightsManagementProvider _rmProvider { get { return _rmProviderCache.Value; } } #endregion Private Properties #region Private Fields //------------------------------------------------------ // Private Fields //----------------------------------------------------- private static SecurityCriticalDataForSet_currentManager; private SecurityCriticalDataForSet _rmProviderCache; /// /// A handle to a currently open instance of the credential manager dialog /// ////// Critical /// 1) This is a pointer to a WinForms dialog which could contain PII. /// [SecurityCritical] private CredentialManagerDialog _credManagerDialog; ////// True if we have yet determined whether the RM client is installed. /// We cache this to keep us from checking if the RM Client is installed /// over and over. /// private bool _determinedRMInstallState = false; ////// True if the RM client is installed. /// private bool _isRMInstalled = false; ////// False if the SecureEnvironment is not in a reliable state (ie need to reload). /// private bool _isSecureEnvironmentReliable = true; ////// A dictionary mapping user names to RightsManagementUser objects. /// ////// Critical /// 1) hash table holds critical data. /// [SecurityCritical] private IDictionary_userMap; #endregion Private Fields #region Nested Class //------------------------------------------------------ // Nested Class //----------------------------------------------------- /// /// RMStatusEventArgs, object used when firing RMStatus change. /// ////// This is used to determine what the UI shows and may be the basis for /// a trust decision by the user. However, it is not useful to make /// this class critical because the actual UI controls are in Avalon and /// can be modified directly. /// public class RightsManagementStatusEventArgs : EventArgs { #region Constructors //----------------------------------------------------- // Constructors //----------------------------------------------------- ////// The constructor /// /// Rights Management status /// Resources containing other information /// relevant to the document RM status public RightsManagementStatusEventArgs( RightsManagementStatus rmStatus) { _rmStatus = rmStatus; _statusResourcesLoaded = false; } #endregion Constructors #region Public Properties //------------------------------------------------------ // Public Properties //----------------------------------------------------- ////// The Rights Management status /// public RightsManagementStatus RMStatus { get { return _rmStatus; } } ////// Property to get the StatusResources. To improve performance the resources are not loaded /// until the first time they are accessed. /// public DocumentStatusResources StatusResources { get { if (!_statusResourcesLoaded) { _statusResources = RightsManagementResourceHelper.GetDocumentLevelResources(_rmStatus); _statusResourcesLoaded = true; } return _statusResources; } } #endregion Public Properties #region Private Fields //------------------------------------------------------ // Private Fields //------------------------------------------------------ private RightsManagementStatus _rmStatus; private DocumentStatusResources _statusResources; private bool _statusResourcesLoaded; #endregion Private Fields } ////// RMPolicyEventArgs, object used when firing RMPolicy change. /// public class RightsManagementPolicyEventArgs : EventArgs { #region Constructors //----------------------------------------------------- // Constructors //------------------------------------------------------ ////// The constructor /// ////// Critical - The rmPolicy parameter contains critical data. /// [SecurityCritical] internal RightsManagementPolicyEventArgs(RightsManagementPolicy rmPolicy) { _rmPolicy = rmPolicy; } #endregion Constructors #region Public Properties //----------------------------------------------------- // Public Properties //----------------------------------------------------- ////// Property to get/set the RMPolicy /// ////// Critical - RMPolicy contains critical data. /// TreatAsSafe - RMPolicy is safe for read. /// public RightsManagementPolicy RMPolicy { [SecurityCritical, SecurityTreatAsSafe] get { return _rmPolicy; } } #endregion Public Properties #region Private Fields //----------------------------------------------------- // Private Fields //------------------------------------------------------ ////// Critical - _rmPolicy contains critical data. /// [SecurityCritical] private RightsManagementPolicy _rmPolicy; #endregion Private Fields } ////// The data passed to the Rights Management enrollment thread. /// private struct RightsManagementEnrollThreadInfo { ////// The account type to enroll /// public EnrollmentAccountType AccountType; ////// A handle to the form that displays enrollment progress /// public Form ProgressForm; } #endregion Nested Class } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- EventToken.cs
- PeerObject.cs
- XmlSerializableWriter.cs
- ScrollItemPattern.cs
- BamlLocalizabilityResolver.cs
- WindowsGraphicsWrapper.cs
- SplayTreeNode.cs
- SecUtil.cs
- TheQuery.cs
- arclist.cs
- MenuItemBinding.cs
- ClientProxyGenerator.cs
- TableCell.cs
- WindowsListViewGroupSubsetLink.cs
- BooleanSwitch.cs
- ConfigurationManagerHelper.cs
- ListSortDescriptionCollection.cs
- AttributeQuery.cs
- GlyphRun.cs
- IItemProperties.cs
- TiffBitmapEncoder.cs
- CodeLabeledStatement.cs
- DataRowCollection.cs
- XpsFilter.cs
- CryptoConfig.cs
- Symbol.cs
- DataGridViewSelectedCellsAccessibleObject.cs
- ResourcePermissionBase.cs
- DataSourceComponent.cs
- ImageKeyConverter.cs
- Validator.cs
- SystemIPAddressInformation.cs
- DataListItem.cs
- SetStoryboardSpeedRatio.cs
- Annotation.cs
- DeclarationUpdate.cs
- ProxyAssemblyNotLoadedException.cs
- ButtonChrome.cs
- ColorDialog.cs
- RSACryptoServiceProvider.cs
- TextureBrush.cs
- DateTime.cs
- DataKey.cs
- Attributes.cs
- Brushes.cs
- DodSequenceMerge.cs
- KeyEvent.cs
- DataGridPagerStyle.cs
- XmlSchemaChoice.cs
- CounterSetInstanceCounterDataSet.cs
- InstallerTypeAttribute.cs
- QueryOutputWriter.cs
- WindowsTreeView.cs
- ReliableSessionBindingElementImporter.cs
- HttpListenerTimeoutManager.cs
- BigInt.cs
- InstanceDataCollectionCollection.cs
- SafeNativeMethodsOther.cs
- FreezableCollection.cs
- PtsHost.cs
- DataContractJsonSerializerOperationBehavior.cs
- Int64AnimationBase.cs
- TextEditorLists.cs
- AspNetSynchronizationContext.cs
- ViewStateException.cs
- StdRegProviderWrapper.cs
- HttpResponseBase.cs
- PEFileEvidenceFactory.cs
- UserControlParser.cs
- WebPartCatalogAddVerb.cs
- ControlIdConverter.cs
- FactoryMaker.cs
- TextRangeBase.cs
- smtppermission.cs
- DescriptionAttribute.cs
- PageRanges.cs
- DataControlFieldCell.cs
- ComUdtElement.cs
- OrderByLifter.cs
- SecuritySessionSecurityTokenProvider.cs
- SkinBuilder.cs
- LoadedOrUnloadedOperation.cs
- TableItemPatternIdentifiers.cs
- HeaderUtility.cs
- LineSegment.cs
- Baml2006SchemaContext.cs
- DesignTimeParseData.cs
- XmlArrayItemAttributes.cs
- OdbcUtils.cs
- Brush.cs
- FileDetails.cs
- SplayTreeNode.cs
- SynchronizedDispatch.cs
- EntityType.cs
- NativeObjectSecurity.cs
- FileSystemEventArgs.cs
- ApplicationInterop.cs
- ListMarkerSourceInfo.cs
- HatchBrush.cs
- HighlightComponent.cs