// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
using System;
using System.Globalization;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Microsoft.Storage;
using System.Text;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices.ComTypes;
using System.IO;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Reflection;
[assembly: CLSCompliant(false)]
namespace Microsoft.Samples.Fsrm.PowerShellHostClassifier
{
///
/// The exposed class that implements the interface of a FSRM classifier
/// The classifier allows a powershell scripts to be run per rule
///
[ComVisible( true ), Guid( "2573306a-2519-4e82-9617-5f99c6137c51" )]
public class HostingClassifier : IFsrmClassifierModuleImplementation
{
public const int FSRM_E_NO_PROPERTY_VALUE = unchecked((int) 0x80045351);
//cached module definitions
private IFsrmPipelineModuleDefinition m_moduleDefinition;
//cached property bag
private IFsrmPropertyBag m_propertyBag;
//cached dictionary of guids to PowerShellRuleHosters
private Dictionary m_dictAllRules;
//cached dictionary of guids to IFsrmPropertyDefinitions
private Dictionary m_dictAllProperties;
///
/// Constructor
///
public HostingClassifier()
{
}
///
/// Destructor
///
~HostingClassifier()
{
}
///
/// Called once when loading the module, will not necessarily be called once per classificaiton run
///
/// The definition for this module
///
public void OnLoad(
IFsrmPipelineModuleDefinition moduleDefinition,
out FsrmPipelineModuleConnector moduleConnector
)
{
moduleConnector = new FsrmPipelineModuleConnector();
moduleConnector.Bind( moduleDefinition, this );
m_moduleDefinition = moduleDefinition;
}
///
/// Called during module unload
///
public void OnUnload()
{
foreach (PowerShellRuleHoster rule in m_dictAllRules.Values) {
rule.UnloadRule();
}
m_dictAllRules.Clear();
}
public object LastModified
{
get
{
return Decimal.Zero;
}
}
///
/// Called once per classification run with the rule list and the property definition list
///
///
///
public void UseRulesAndDefinitions(
IFsrmCollection Rules,
IFsrmCollection propertyDefinitions
)
{
m_dictAllRules = new Dictionary();
m_dictAllProperties = new Dictionary();
Dictionary dictPropertyName = new Dictionary();
foreach (IFsrmPropertyDefinition fsrmPropertyDefinition in propertyDefinitions)
{
m_dictAllProperties.Add(fsrmPropertyDefinition.id, fsrmPropertyDefinition);
dictPropertyName[fsrmPropertyDefinition.Name] = fsrmPropertyDefinition;
}
foreach (IFsrmClassificationRule fsrmClsRule in Rules) {
if (fsrmClsRule.RuleType == _FsrmRuleType.FsrmRuleType_Classification)
{
Guid ruleId = fsrmClsRule.id;
PowerShellRuleHoster newRule = new PowerShellRuleHoster(
m_moduleDefinition,
fsrmClsRule,
dictPropertyName[fsrmClsRule.PropertyAffected]);
m_dictAllRules.Add( ruleId, newRule );
}
}
}
///
/// This is called on the begining of each new file
///
/// It should be noted that if rules are being processed in this function and if each rule can modify the propertyBag and affect other rules
/// then the rules should be processed synchronously such that the first rule processed contains the longest scope that matches the file's path
/// To avoid trying to do this on your own you can just process the rules in the DoesPropertyValueApply / GetPropertyValueToApply
///
/// The fsrm property bag representing the current file
/// An array of strings containg the string form of the guids that apply to this file, to convert them to guid's do new Guid(string)
public void OnBeginFile(
IFsrmPropertyBag propertyBag,
object[] arrayRuleIds
)
{
m_propertyBag = propertyBag;
}
///
/// States whether or not this rule should apply the predefined value to the predefined property
/// The caller will handle aggregation if multiple rules
///
/// *Note
/// this need not be implemented if not supporting yes no classifiers
///
/// The name of the property that this rule can modify
/// The value to set that will be applied to the property if applyValue is true
/// True if the caller should set Value to property, False if the caller should not set Value to property
/// The id of the rule to process
/// The id of the property definition that this rule can modify
public void DoesPropertyValueApply(
string property,
string Value,
out bool applyValue,
Guid idRule,
Guid idPropDef
)
{
PowerShellRuleHoster rule = m_dictAllRules[idRule];
// run the powershell pipeline for this value
rule.StepPipeline(m_propertyBag);
// if the rule doesn't apply force the value to false
if (rule.RuleNoApply)
{
applyValue = false;
}
else
{
try
{
applyValue = (bool)rule.PropertyValue;
}
catch (Exception ex)
{
//Assuming this error is due to casting
String message = String.Format( CultureInfo.InvariantCulture,
"PowerShell Classifier for rule [{0}] and file [{1}], failed to convert the property recieved from powershell of type [{2}] and value [{3}] into the a boolean value for yes/no classification, with error [{4}]",
rule.RuleName,
m_propertyBag.VolumeName + m_propertyBag.RelativePath + "\\" + m_propertyBag.Name,
rule.PropertyValue.GetType().ToString(),
rule.PropertyValue.ToString(),
ex.Message );
m_propertyBag.AddMessage( message );
throw new COMException( message, ex.InnerException );
}
}
}
///
/// Gets the value for the property and rule to apply
/// The caller will handle aggregating multiple values for a single property
///
/// *Note
/// This function need not be implemented if only supporting yes no classifiers
///
/// The name of the property that this rule can modify
/// The value to set the property to
/// The id of the rule to process
/// The id of the definition that this rule can modify
public void GetPropertyValueToApply(
string property,
out string Value,
Guid idRule,
Guid idPropDef
)
{
PowerShellRuleHoster rule = m_dictAllRules[idRule];
// run the powershell script over the propertyBag
rule.StepPipeline(m_propertyBag);
// If the rule does not modify the property value throw an error that specifies this
// You must specify the HR that is being specified below if you wish to not modify the rule's value
if (rule.RuleNoApply)
{
throw new COMException("Value does not apply", FSRM_E_NO_PROPERTY_VALUE );//, FSRM_E_NO_PROPERTY_VALUE);
}
//convert the powershell value into the approrpiate value
try
{
IFsrmPropertyDefinition propertyDefinition = m_dictAllProperties[idPropDef];
if (propertyDefinition.Type == _FsrmPropertyDefinitionType.FsrmPropertyDefinitionType_Bool)
{
//convert from true/false to "1","0"
Value = (bool)rule.PropertyValue ? "1" : "0";
}
else if (propertyDefinition.Type == _FsrmPropertyDefinitionType.FsrmPropertyDefinitionType_Date)
{
// convert from datetime to str
DateTime time = (DateTime)rule.PropertyValue;
long filetime = time.ToFileTimeUtc();
Value = filetime.ToString(CultureInfo.InvariantCulture);
}
else if (propertyDefinition.Type == _FsrmPropertyDefinitionType.FsrmPropertyDefinitionType_Int)
{
Value = rule.PropertyValue.ToString();
}
else
{
Value = (string)rule.PropertyValue;
}
}
catch (Exception ex)
{
String message = "";
if (rule.PropertyValue == null)
{
message = String.Format(CultureInfo.InvariantCulture,
"PowerShell Classifier for rule [{0}] and file [{1}], received null value from powershell, with error [{2}]",
rule.RuleName,
m_propertyBag.VolumeName + m_propertyBag.RelativePath + "\\" + m_propertyBag.Name,
ex.Message);
}
else
{
//Assuming this error is due to casting
message = String.Format(CultureInfo.InvariantCulture,
"PowerShell Classifier for rule [{0}] and file [{1}], failed to convert the property recieved from powershell of type [{2}] and value [{3}] into the property [{4}], with error [{5}]",
rule.RuleName,
m_propertyBag.VolumeName + m_propertyBag.RelativePath + "\\" + m_propertyBag.Name,
rule.PropertyValue.GetType().ToString(),
rule.PropertyValue.ToString(),
property,
ex.Message);
}
m_propertyBag.AddMessage(message);
throw new COMException( message, ex.InnerException );
}
}
///
/// Sample code does nothing here but you can put any per file cleanup code here
///
public void OnEndFile()
{
m_propertyBag = null;
}
}
public static class HRESULTS
{
public const int PS_CLS_E_TOO_MANY_VALUES = unchecked( (int)0x80045401 );
}
}