936 lines
45 KiB
C#
936 lines
45 KiB
C#
|
namespace System.Workflow.ComponentModel.Compiler
|
||
|
{
|
||
|
#region Imports
|
||
|
|
||
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Specialized;
|
||
|
using System.Collections.Generic;
|
||
|
using System.CodeDom;
|
||
|
using System.ComponentModel;
|
||
|
using System.ComponentModel.Design;
|
||
|
using System.CodeDom.Compiler;
|
||
|
using System.Reflection;
|
||
|
using System.Xml;
|
||
|
using System.Globalization;
|
||
|
using System.IO;
|
||
|
using System.Text;
|
||
|
using System.Diagnostics;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using Microsoft.CSharp;
|
||
|
using Microsoft.VisualBasic;
|
||
|
using System.Workflow.ComponentModel.Design;
|
||
|
using System.Workflow.ComponentModel.Serialization;
|
||
|
using Microsoft.Win32;
|
||
|
using System.ComponentModel.Design.Serialization;
|
||
|
using System.Configuration;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Workflow.Interop;
|
||
|
using System.Diagnostics.CodeAnalysis;
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
internal static class XomlCompilerHelper
|
||
|
{
|
||
|
#region Data members
|
||
|
|
||
|
internal static object LineNumber = new object();
|
||
|
internal static object ColumnNumber = new object();
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Main compilation helper
|
||
|
|
||
|
internal static void InternalCompileFromDomBatch(string[] files, string[] codeFiles, WorkflowCompilerParameters parameters, WorkflowCompilerResults results, string localAssemblyPath)
|
||
|
{
|
||
|
// Check all the library paths are valid.
|
||
|
foreach (string libraryPath in parameters.LibraryPaths)
|
||
|
{
|
||
|
if (!XomlCompilerHelper.CheckPathName(libraryPath))
|
||
|
{
|
||
|
WorkflowCompilerError libPathError =
|
||
|
new WorkflowCompilerError(string.Empty, 0, 0, ErrorNumbers.Error_LibraryPath.ToString(CultureInfo.InvariantCulture), string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.LibraryPathIsInvalid), libraryPath));
|
||
|
libPathError.IsWarning = true;
|
||
|
results.Errors.Add(libPathError);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
IList<AuthorizedType> authorizedTypes = null;
|
||
|
if (parameters.CheckTypes)
|
||
|
{
|
||
|
//If we dont find the list of authorized types then return.
|
||
|
authorizedTypes = WorkflowCompilationContext.Current.GetAuthorizedTypes();
|
||
|
if (authorizedTypes == null)
|
||
|
{
|
||
|
ValidationError error = new ValidationError(SR.GetString(SR.Error_ConfigFileMissingOrInvalid), ErrorNumbers.Error_ConfigFileMissingOrInvalid);
|
||
|
results.Errors.Add(CreateXomlCompilerError(error, parameters));
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ITypeProvider typeProvider = WorkflowCompilationContext.Current.ServiceProvider.GetService(typeof(ITypeProvider)) as ITypeProvider;
|
||
|
ArrayList activities = new ArrayList();
|
||
|
|
||
|
using (PDBReader pdbReader = new PDBReader(localAssemblyPath))
|
||
|
{
|
||
|
// Validate all the compiled activities in the assembly.
|
||
|
foreach (Type type in typeProvider.LocalAssembly.GetTypes())
|
||
|
{
|
||
|
if (!TypeProvider.IsAssignable(typeof(Activity), type) || type.IsAbstract)
|
||
|
continue;
|
||
|
|
||
|
// Fetch file name.
|
||
|
string fileName = string.Empty;
|
||
|
WorkflowMarkupSourceAttribute[] sourceAttrs = (WorkflowMarkupSourceAttribute[])type.GetCustomAttributes(typeof(WorkflowMarkupSourceAttribute), false);
|
||
|
if (sourceAttrs != null && sourceAttrs.Length > 0)
|
||
|
{
|
||
|
fileName = sourceAttrs[0].FileName;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ConstructorInfo ctorMethod = type.GetConstructor(Type.EmptyTypes);
|
||
|
if (ctorMethod != null)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
uint line = 0, column = 0;
|
||
|
pdbReader.GetSourceLocationForOffset((uint)ctorMethod.MetadataToken, 0, out fileName, out line, out column);
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
// We don't want errors if the user has written their own custom
|
||
|
// activity and simply inherited the constructor
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// In case of VB, if the ctor is autogenerated the PDB will not have symbol
|
||
|
// information. Use InitializeComponent method as the fallback. Bug 19085.
|
||
|
if (String.IsNullOrEmpty(fileName))
|
||
|
{
|
||
|
MethodInfo initializeComponent = type.GetMethod("InitializeComponent", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null);
|
||
|
if (initializeComponent != null)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
uint line = 0, column = 0;
|
||
|
pdbReader.GetSourceLocationForOffset((uint)initializeComponent.MetadataToken, 0, out fileName, out line, out column);
|
||
|
|
||
|
if (!String.IsNullOrEmpty(fileName))
|
||
|
{
|
||
|
if (fileName.EndsWith(".designer.cs", StringComparison.OrdinalIgnoreCase))
|
||
|
fileName = fileName.Substring(0, fileName.Length - ".designer.cs".Length) + ".cs";
|
||
|
else if (fileName.EndsWith(".designer.vb", StringComparison.OrdinalIgnoreCase))
|
||
|
fileName = fileName.Substring(0, fileName.Length - ".designer.vb".Length) + ".vb";
|
||
|
}
|
||
|
}
|
||
|
catch
|
||
|
{
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Create the activity.
|
||
|
Activity activity = null;
|
||
|
try
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
Activity.ActivityType = type;
|
||
|
activity = Activator.CreateInstance(type) as Activity;
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
Activity.ActivityType = null;
|
||
|
}
|
||
|
activity.UserData[UserDataKeys.CustomActivity] = false;
|
||
|
if (activity is CompositeActivity)
|
||
|
{
|
||
|
CompositeActivity compositeActivity = activity as CompositeActivity;
|
||
|
if (compositeActivity.CanModifyActivities)
|
||
|
results.Errors.Add(CreateXomlCompilerError(new ValidationError(SR.GetString(SR.Error_Missing_CanModifyProperties_False, activity.GetType().FullName), ErrorNumbers.Error_CustomActivityCantCreate), parameters));
|
||
|
}
|
||
|
if (sourceAttrs.Length > 0)
|
||
|
{
|
||
|
DesignerSerializationManager manager = new DesignerSerializationManager(WorkflowCompilationContext.Current.ServiceProvider);
|
||
|
Activity instance2 = null;
|
||
|
using (manager.CreateSession())
|
||
|
{
|
||
|
WorkflowMarkupSerializationManager xomlSerializationManager = new WorkflowMarkupSerializationManager(manager);
|
||
|
xomlSerializationManager.LocalAssembly = parameters.LocalAssembly;
|
||
|
using (XmlReader reader = XmlReader.Create((sourceAttrs[0].FileName)))
|
||
|
instance2 = new WorkflowMarkupSerializer().Deserialize(xomlSerializationManager, reader) as Activity;
|
||
|
}
|
||
|
if (instance2 is CompositeActivity)
|
||
|
ActivityMarkupSerializer.ReplaceChildActivities(activity as CompositeActivity, instance2 as CompositeActivity);
|
||
|
}
|
||
|
}
|
||
|
catch (TargetInvocationException tie)
|
||
|
{
|
||
|
// For TypeInitializationException, the message is available at the inner Exception
|
||
|
if (tie.InnerException is TypeInitializationException && tie.InnerException.InnerException != null)
|
||
|
results.Errors.Add(CreateXomlCompilerError(new ValidationError(SR.GetString(SR.Error_CustomActivityCantCreate, type.FullName, tie.InnerException.InnerException.ToString()), ErrorNumbers.Error_CustomActivityCantCreate), parameters));
|
||
|
else if (tie.InnerException.InnerException != null)
|
||
|
results.Errors.Add(CreateXomlCompilerError(new ValidationError(tie.InnerException.InnerException.ToString(), ErrorNumbers.Error_CustomActivityCantCreate), parameters));
|
||
|
else
|
||
|
results.Errors.Add(CreateXomlCompilerError(new ValidationError(SR.GetString(SR.Error_CustomActivityCantCreate, type.FullName, tie.InnerException.ToString()), ErrorNumbers.Error_CustomActivityCantCreate), parameters));
|
||
|
continue;
|
||
|
}
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
results.Errors.Add(CreateXomlCompilerError(new ValidationError(SR.GetString(SR.Error_CustomActivityCantCreate, type.FullName, e.ToString()), ErrorNumbers.Error_CustomActivityCantCreate), parameters));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Work around : another set of work arounds.
|
||
|
activity.SetValue(ActivityCodeDomSerializer.MarkupFileNameProperty, fileName);
|
||
|
activity.SetValue(WorkflowMarkupSerializer.XClassProperty, type.FullName);
|
||
|
|
||
|
// Run the validators.
|
||
|
ValidateActivity(activity, parameters, results);
|
||
|
activities.Add(activity);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Add all type load errors to compiler results.
|
||
|
foreach (KeyValuePair<object, Exception> entry in typeProvider.TypeLoadErrors)
|
||
|
{
|
||
|
WorkflowCompilerError compilerError = new WorkflowCompilerError(string.Empty, 0, 0, ErrorNumbers.Error_TypeLoad.ToString(CultureInfo.InvariantCulture), entry.Value.Message);
|
||
|
compilerError.IsWarning = true;
|
||
|
results.Errors.Add(compilerError);
|
||
|
}
|
||
|
results.CompiledUnit = WorkflowCompilerInternal.GenerateCodeFromFileBatch(files, parameters, results);
|
||
|
|
||
|
WorkflowCompilationContext context = WorkflowCompilationContext.Current;
|
||
|
if (context == null)
|
||
|
{
|
||
|
throw new Exception(SR.GetString(SR.Error_MissingCompilationContext));
|
||
|
}
|
||
|
// Fix standard namespaces and root namespace.
|
||
|
WorkflowMarkupSerializationHelpers.ReapplyRootNamespace(results.CompiledUnit.Namespaces, context.RootNamespace, CompilerHelpers.GetSupportedLanguage(context.Language));
|
||
|
WorkflowMarkupSerializationHelpers.FixStandardNamespacesAndRootNamespace(results.CompiledUnit.Namespaces, context.RootNamespace, CompilerHelpers.GetSupportedLanguage(context.Language));
|
||
|
if (!results.Errors.HasErrors)
|
||
|
{
|
||
|
// ask activities to generate code for themselves
|
||
|
CodeGenerationManager codeGenerationManager = new CodeGenerationManager(WorkflowCompilationContext.Current.ServiceProvider);
|
||
|
codeGenerationManager.Context.Push(results.CompiledUnit.Namespaces);
|
||
|
foreach (Activity activity in activities)
|
||
|
{
|
||
|
// Need to call code generators associated with the root activity.
|
||
|
if (activity.Parent == null)
|
||
|
{
|
||
|
foreach (System.Workflow.ComponentModel.Compiler.ActivityCodeGenerator codeGenerator in codeGenerationManager.GetCodeGenerators(activity.GetType()))
|
||
|
codeGenerator.GenerateCode(codeGenerationManager, activity);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If only ccu needed then return.
|
||
|
if (!parameters.GenerateCodeCompileUnitOnly || parameters.CheckTypes)
|
||
|
{
|
||
|
// Convert all compile units to source files.
|
||
|
SupportedLanguages language = CompilerHelpers.GetSupportedLanguage(parameters.LanguageToUse);
|
||
|
CodeDomProvider codeDomProvider = CompilerHelpers.GetCodeDomProvider(language, parameters.CompilerVersion);
|
||
|
ArrayList ccus = new ArrayList((ICollection)parameters.UserCodeCompileUnits);
|
||
|
ccus.Add(results.CompiledUnit);
|
||
|
|
||
|
ArrayList sourceFilePaths = new ArrayList();
|
||
|
sourceFilePaths.AddRange(codeFiles);
|
||
|
sourceFilePaths.AddRange(XomlCompilerHelper.GenerateFiles(codeDomProvider, parameters, (CodeCompileUnit[])ccus.ToArray(typeof(CodeCompileUnit))));
|
||
|
|
||
|
// Finally give it to Code Compiler.
|
||
|
CompilerResults results2 = codeDomProvider.CompileAssemblyFromFile(parameters, (string[])sourceFilePaths.ToArray(typeof(string)));
|
||
|
results.AddCompilerErrorsFromCompilerResults(results2);
|
||
|
results.PathToAssembly = results2.PathToAssembly;
|
||
|
results.NativeCompilerReturnValue = results2.NativeCompilerReturnValue;
|
||
|
|
||
|
if (!results.Errors.HasErrors && parameters.CheckTypes)
|
||
|
{
|
||
|
foreach (string referenceType in MetaDataReader.GetTypeRefNames(results2.CompiledAssembly.Location))
|
||
|
{
|
||
|
bool authorized = false;
|
||
|
foreach (AuthorizedType authorizedType in authorizedTypes)
|
||
|
{
|
||
|
if (authorizedType.RegularExpression.IsMatch(referenceType))
|
||
|
{
|
||
|
authorized = (String.Compare(bool.TrueString, authorizedType.Authorized, StringComparison.OrdinalIgnoreCase) == 0);
|
||
|
if (!authorized)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!authorized)
|
||
|
{
|
||
|
ValidationError error = new ValidationError(SR.GetString(SR.Error_TypeNotAuthorized, referenceType), ErrorNumbers.Error_TypeNotAuthorized);
|
||
|
results.Errors.Add(CreateXomlCompilerError(error, parameters));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//this line was throwing for the delay sign case. besides, copying PathToAssembly should do the same...
|
||
|
if (!results.Errors.HasErrors && !parameters.GenerateCodeCompileUnitOnly && parameters.GenerateInMemory &&
|
||
|
(string.IsNullOrEmpty(parameters.CompilerOptions) || !parameters.CompilerOptions.ToLower(CultureInfo.InvariantCulture).Contains("/delaysign")))
|
||
|
results.CompiledAssembly = results2.CompiledAssembly;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Service Helpers
|
||
|
|
||
|
internal static string ProcessCompilerOptions(string options, out bool noCode, out bool checkTypes)
|
||
|
{
|
||
|
if (string.IsNullOrEmpty(options))
|
||
|
{
|
||
|
noCode = false;
|
||
|
checkTypes = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
string compilerSwitchValue;
|
||
|
|
||
|
noCode = ExtractCompilerOptionSwitch(ref options, WorkflowCompilerParameters.NoCodeSwitch, out compilerSwitchValue);
|
||
|
checkTypes = ExtractCompilerOptionSwitch(ref options, WorkflowCompilerParameters.CheckTypesSwitch, out compilerSwitchValue);
|
||
|
}
|
||
|
|
||
|
return options;
|
||
|
}
|
||
|
|
||
|
static bool ExtractCompilerOptionSwitch(ref string options, string compilerSwitch, out string compilerSwitchValue)
|
||
|
{
|
||
|
int switchPos = options.IndexOf(compilerSwitch, StringComparison.OrdinalIgnoreCase);
|
||
|
if (switchPos != -1)
|
||
|
{
|
||
|
int switchValueStart = switchPos + compilerSwitch.Length;
|
||
|
int switchValueLength = 0;
|
||
|
while ((switchValueStart + switchValueLength < options.Length) && !char.IsWhiteSpace(options[switchValueStart + switchValueLength]))
|
||
|
{
|
||
|
switchValueLength++;
|
||
|
}
|
||
|
if (switchValueLength > 0)
|
||
|
{
|
||
|
compilerSwitchValue = options.Substring(switchValueStart, switchValueLength);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
compilerSwitchValue = string.Empty;
|
||
|
}
|
||
|
RemoveCompilerOptionSwitch(ref options, switchPos, compilerSwitch.Length + switchValueLength);
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
compilerSwitchValue = string.Empty;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
static void RemoveCompilerOptionSwitch(ref string options, int startPos, int length)
|
||
|
{
|
||
|
if ((startPos > 0) && char.IsWhiteSpace(options[startPos - 1]))
|
||
|
{
|
||
|
options = options.Remove(startPos - 1, length + 1);
|
||
|
}
|
||
|
else if ((startPos == 0) && (startPos + length + 1 < options.Length) && char.IsWhiteSpace(options[startPos + length + 1]))
|
||
|
{
|
||
|
options = options.Remove(startPos, length + 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
options = options.Remove(startPos, length);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static CompilerParameters CloneCompilerParameters(WorkflowCompilerParameters sourceParams)
|
||
|
{
|
||
|
bool noCode;
|
||
|
bool checkTypes;
|
||
|
|
||
|
CompilerParameters clonedParams = new CompilerParameters();
|
||
|
clonedParams.CompilerOptions =
|
||
|
ProcessCompilerOptions(sourceParams.CompilerOptions, out noCode, out checkTypes);
|
||
|
|
||
|
foreach (string embeddedResource in sourceParams.EmbeddedResources)
|
||
|
clonedParams.EmbeddedResources.Add(embeddedResource);
|
||
|
|
||
|
clonedParams.GenerateExecutable = sourceParams.GenerateExecutable;
|
||
|
clonedParams.GenerateInMemory = sourceParams.GenerateInMemory;
|
||
|
clonedParams.IncludeDebugInformation = sourceParams.IncludeDebugInformation;
|
||
|
foreach (string linkedResource in sourceParams.LinkedResources)
|
||
|
clonedParams.LinkedResources.Add(linkedResource);
|
||
|
|
||
|
clonedParams.MainClass = sourceParams.MainClass;
|
||
|
clonedParams.OutputAssembly = sourceParams.OutputAssembly;
|
||
|
foreach (string referencedAssembly in sourceParams.ReferencedAssemblies)
|
||
|
clonedParams.ReferencedAssemblies.Add(referencedAssembly);
|
||
|
|
||
|
clonedParams.TreatWarningsAsErrors = sourceParams.TreatWarningsAsErrors;
|
||
|
clonedParams.UserToken = sourceParams.UserToken;
|
||
|
clonedParams.WarningLevel = sourceParams.WarningLevel;
|
||
|
clonedParams.Win32Resource = sourceParams.Win32Resource;
|
||
|
clonedParams.CoreAssemblyFileName = sourceParams.CoreAssemblyFileName;
|
||
|
return clonedParams;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region References helpers
|
||
|
|
||
|
internal static void FixReferencedAssemblies(WorkflowCompilerParameters parameters, WorkflowCompilerResults results, StringCollection libraryPaths)
|
||
|
{
|
||
|
Debug.Assert(parameters.MultiTargetingInformation == null, "Shouldn't come here if opted to MT support");
|
||
|
|
||
|
// First add all WinOE assemblies
|
||
|
foreach (string assemblyPath in XomlCompilerHelper.StandardAssemblies)
|
||
|
{
|
||
|
bool shouldAdd = true;
|
||
|
//first check if also user referenced this standard WinOE assemblies
|
||
|
foreach (string userAssembly in parameters.ReferencedAssemblies)
|
||
|
{
|
||
|
if (null != userAssembly && userAssembly.Length > 0)
|
||
|
{
|
||
|
string userAssemblyFileName = Path.GetFileName(userAssembly);
|
||
|
string standardAssemblyFileName = Path.GetFileName(assemblyPath);
|
||
|
if (null != userAssemblyFileName && null != standardAssemblyFileName && 0 == string.Compare(userAssemblyFileName, standardAssemblyFileName, StringComparison.OrdinalIgnoreCase))
|
||
|
{
|
||
|
//we will use the user-provided assembly path instead of the standard one
|
||
|
shouldAdd = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (shouldAdd)
|
||
|
parameters.ReferencedAssemblies.Add(assemblyPath);
|
||
|
}
|
||
|
|
||
|
// Resolve all the references.
|
||
|
StringCollection resolvedAssemblyReferences = ResolveAssemblyReferences(parameters.ReferencedAssemblies,
|
||
|
GetCompleteLibraryPaths(libraryPaths), results);
|
||
|
parameters.ReferencedAssemblies.Clear();
|
||
|
foreach (string resolvedAssemblyReference in resolvedAssemblyReferences)
|
||
|
{
|
||
|
if (!parameters.ReferencedAssemblies.Contains(resolvedAssemblyReference))
|
||
|
parameters.ReferencedAssemblies.Add(resolvedAssemblyReference);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static StringCollection standardAssemblies = null;
|
||
|
private static StringCollection StandardAssemblies
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (standardAssemblies == null)
|
||
|
{
|
||
|
StringCollection specialAssemblies = new StringCollection();
|
||
|
specialAssemblies.Add("System.Workflow.ComponentModel.dll");
|
||
|
specialAssemblies.Add("System.Workflow.Runtime.dll");
|
||
|
specialAssemblies.Add("System.Workflow.Activities.dll");
|
||
|
specialAssemblies.Add("System.dll");
|
||
|
specialAssemblies.Add("System.Transactions.dll");
|
||
|
specialAssemblies.Add("System.drawing.dll");
|
||
|
specialAssemblies.Add("System.Web.dll");
|
||
|
specialAssemblies.Add("System.Web.Services.dll");
|
||
|
standardAssemblies = specialAssemblies;
|
||
|
}
|
||
|
return standardAssemblies;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static char[] trimCharsArray = null;
|
||
|
internal static string TrimDirectorySeparatorChar(string dir)
|
||
|
{
|
||
|
if (trimCharsArray == null)
|
||
|
{
|
||
|
trimCharsArray = new char[] { Path.DirectorySeparatorChar };
|
||
|
}
|
||
|
return dir.TrimEnd(trimCharsArray);
|
||
|
}
|
||
|
|
||
|
private static StringCollection GetCompleteLibraryPaths(StringCollection userLibraryPaths)
|
||
|
{
|
||
|
StringCollection libraryPaths = new StringCollection();
|
||
|
|
||
|
// Add the current directory.
|
||
|
libraryPaths.Add(Environment.CurrentDirectory);
|
||
|
|
||
|
// Add the CLR system directory, for example: "C:\WINDOWS\Microsoft.NET\Framework\v1.2.30703"
|
||
|
libraryPaths.Add(TrimDirectorySeparatorChar(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()));
|
||
|
|
||
|
// Add /lib paths.
|
||
|
string[] stringArray = new String[userLibraryPaths.Count];
|
||
|
userLibraryPaths.CopyTo(stringArray, 0);
|
||
|
libraryPaths.AddRange(stringArray);
|
||
|
|
||
|
// Add the LIB environment variable paths.
|
||
|
string libLibraryPaths = Environment.GetEnvironmentVariable("LIB");
|
||
|
if ((libLibraryPaths != null) && (libLibraryPaths.Length > 0))
|
||
|
{
|
||
|
string[] libLibraryPathsArray = Environment.GetEnvironmentVariable("LIB").Split(new char[] { ',', ';' });
|
||
|
libraryPaths.AddRange(libLibraryPathsArray);
|
||
|
}
|
||
|
|
||
|
return libraryPaths;
|
||
|
}
|
||
|
|
||
|
private static StringCollection ResolveAssemblyReferences(StringCollection originalReferences, StringCollection libraryPaths, WorkflowCompilerResults results)
|
||
|
{
|
||
|
StringCollection resolvedReferences = new StringCollection();
|
||
|
|
||
|
// Resolve listed references.
|
||
|
foreach (string reference in originalReferences)
|
||
|
{
|
||
|
string fullReferenceName;
|
||
|
if (CheckFileNameUsingPaths(reference, libraryPaths, out fullReferenceName))
|
||
|
resolvedReferences.Add(fullReferenceName);
|
||
|
else
|
||
|
{
|
||
|
WorkflowCompilerError compilerError = new WorkflowCompilerError(string.Empty, 0, 0, ErrorNumbers.Error_InvalidReferencedAssembly.ToString(CultureInfo.InvariantCulture), SR.GetString(SR.Error_ReferencedAssemblyIsInvalid, reference));
|
||
|
results.Errors.Add(compilerError);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return resolvedReferences;
|
||
|
}
|
||
|
|
||
|
private static void ResolveReferencedAssemblies(CompilerParameters parameters, CodeCompileUnit cu)
|
||
|
{
|
||
|
if (cu.ReferencedAssemblies.Count > 0)
|
||
|
foreach (string assemblyName in cu.ReferencedAssemblies)
|
||
|
if (!parameters.ReferencedAssemblies.Contains(assemblyName))
|
||
|
parameters.ReferencedAssemblies.Add(assemblyName);
|
||
|
}
|
||
|
|
||
|
private static bool CheckFileNameUsingPaths(string fileName, StringCollection paths, out string fullFileName)
|
||
|
{
|
||
|
fullFileName = null;
|
||
|
|
||
|
string realFileName = fileName.Trim(new char[] { '"' });
|
||
|
FileInfo info = new FileInfo(realFileName);
|
||
|
|
||
|
// If some path is specified, it should resolve to the file.
|
||
|
if (realFileName.Length != info.Name.Length)
|
||
|
{
|
||
|
if (info.Exists)
|
||
|
fullFileName = info.FullName;
|
||
|
|
||
|
return info.Exists;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Just a file name is specified, look under all directories in paths.
|
||
|
foreach (string path in paths)
|
||
|
{
|
||
|
string trialFileName = path + Path.DirectorySeparatorChar + realFileName;
|
||
|
FileInfo trialInfo = new FileInfo(trialFileName);
|
||
|
if (trialInfo.Exists)
|
||
|
{
|
||
|
fullFileName = trialFileName;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
internal static bool CheckPathName(string pathName)
|
||
|
{
|
||
|
string fullPathName = pathName.Trim(new char[] { '"' });
|
||
|
fullPathName = fullPathName.TrimEnd(new char[] { Path.DirectorySeparatorChar });
|
||
|
return Directory.Exists(fullPathName);
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Error helpers
|
||
|
|
||
|
internal static WorkflowCompilerError CreateXomlCompilerError(ValidationError error, WorkflowCompilerParameters parameters)
|
||
|
{
|
||
|
WorkflowCompilerError compilerError;
|
||
|
compilerError = new WorkflowCompilerError(GetFileName(error), (int)GetValue(error, XomlCompilerHelper.LineNumber), (int)GetValue(error, XomlCompilerHelper.ColumnNumber), string.Empty, GetPrettifiedErrorText(error));
|
||
|
|
||
|
if (!parameters.TreatWarningsAsErrors)
|
||
|
compilerError.IsWarning = error.IsWarning;
|
||
|
|
||
|
compilerError.ErrorNumber = "WF" + error.ErrorNumber.ToString(CultureInfo.InvariantCulture);
|
||
|
|
||
|
if (error.UserData != null)
|
||
|
{
|
||
|
foreach (DictionaryEntry entry in error.UserData)
|
||
|
{
|
||
|
if (entry.Key == (object)typeof(Activity) && entry.Value is Activity)
|
||
|
compilerError.UserData[entry.Key] = ((Activity)entry.Value).QualifiedName;
|
||
|
else
|
||
|
compilerError.UserData[entry.Key] = entry.Value;
|
||
|
}
|
||
|
}
|
||
|
return compilerError;
|
||
|
}
|
||
|
|
||
|
internal static ValidationErrorCollection MorphIntoFriendlyValidationErrors(IEnumerable<ValidationError> errors)
|
||
|
{
|
||
|
ValidationErrorCollection friendlyErrors = new ValidationErrorCollection();
|
||
|
|
||
|
foreach (ValidationError error in errors)
|
||
|
{
|
||
|
if (error == null)
|
||
|
continue;
|
||
|
|
||
|
if (error.GetType() == typeof(ValidationError))
|
||
|
{
|
||
|
ValidationError error2 = new ValidationError(GetPrettifiedErrorText(error), error.ErrorNumber, error.IsWarning);
|
||
|
friendlyErrors.Add(error2);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
friendlyErrors.Add(error);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return friendlyErrors;
|
||
|
}
|
||
|
|
||
|
private static string GetFileName(ValidationError error)
|
||
|
{
|
||
|
Activity activity = error.UserData[typeof(Activity)] as Activity;
|
||
|
while (activity != null && activity.Parent != null)
|
||
|
activity = activity.Parent;
|
||
|
|
||
|
string fileName = string.Empty;
|
||
|
if (activity != null)
|
||
|
fileName = activity.GetValue(ActivityCodeDomSerializer.MarkupFileNameProperty) as string;
|
||
|
|
||
|
if (fileName == null)
|
||
|
fileName = string.Empty;
|
||
|
|
||
|
return fileName;
|
||
|
}
|
||
|
|
||
|
private static string GetPrettifiedErrorText(ValidationError error)
|
||
|
{
|
||
|
string errorText = error.ErrorText;
|
||
|
Activity activity = error.UserData[typeof(Activity)] as Activity;
|
||
|
if (activity != null)
|
||
|
{
|
||
|
// get the ID
|
||
|
string identifier = (Helpers.GetRootActivity(activity) != activity) ? activity.QualifiedName : activity.GetType().Name;
|
||
|
|
||
|
if ((identifier == null) || (identifier.Length == 0))
|
||
|
identifier = SR.GetString(SR.EmptyValue);
|
||
|
|
||
|
// prettify error text
|
||
|
if (error.IsWarning)
|
||
|
errorText = SR.GetString(SR.Warning_ActivityValidation, identifier) + " " + errorText;
|
||
|
else
|
||
|
errorText = SR.GetString(SR.Error_ActivityValidation, identifier) + " " + errorText;
|
||
|
}
|
||
|
return errorText;
|
||
|
}
|
||
|
|
||
|
private static uint GetValue(ValidationError error, object key)
|
||
|
{
|
||
|
Activity activity = error.UserData[typeof(Activity)] as Activity;
|
||
|
while (activity != null && activity.Parent != null)
|
||
|
activity = activity.Parent;
|
||
|
|
||
|
uint value = 0;
|
||
|
if (activity != null && activity.UserData[key] != null)
|
||
|
value = (uint)activity.UserData[key];
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
internal static bool HasCodeWithin(Activity rootActivity)
|
||
|
{
|
||
|
bool hasCodeWithin = false;
|
||
|
Walker documentWalker = new Walker();
|
||
|
documentWalker.FoundActivity += delegate(Walker walker, WalkerEventArgs e)
|
||
|
{
|
||
|
Activity currentActivity = e.CurrentActivity;
|
||
|
if (!currentActivity.Enabled)
|
||
|
{
|
||
|
e.Action = WalkerAction.Skip;
|
||
|
return;
|
||
|
}
|
||
|
CodeTypeMemberCollection codeCollection = currentActivity.GetValue(WorkflowMarkupSerializer.XCodeProperty) as CodeTypeMemberCollection;
|
||
|
if (codeCollection != null && codeCollection.Count != 0)
|
||
|
{
|
||
|
hasCodeWithin = true;
|
||
|
e.Action = WalkerAction.Abort;
|
||
|
return;
|
||
|
}
|
||
|
};
|
||
|
documentWalker.Walk(rootActivity as Activity);
|
||
|
return hasCodeWithin;
|
||
|
}
|
||
|
|
||
|
internal static void ValidateActivity(Activity activity, WorkflowCompilerParameters parameters, WorkflowCompilerResults results)
|
||
|
{
|
||
|
ValidationErrorCollection errors = null;
|
||
|
ValidationManager validationManager = new ValidationManager(WorkflowCompilationContext.Current.ServiceProvider);
|
||
|
|
||
|
foreach (Validator validator in validationManager.GetValidators(activity.GetType()))
|
||
|
{
|
||
|
// Validate recursively.
|
||
|
try
|
||
|
{
|
||
|
errors = validator.Validate(validationManager, activity);
|
||
|
foreach (ValidationError error in errors)
|
||
|
{
|
||
|
if (!error.UserData.Contains(typeof(Activity)))
|
||
|
error.UserData[typeof(Activity)] = activity;
|
||
|
results.Errors.Add(CreateXomlCompilerError(error, parameters));
|
||
|
}
|
||
|
}
|
||
|
catch (TargetInvocationException tie)
|
||
|
{
|
||
|
Exception e = tie.InnerException ?? tie;
|
||
|
ValidationError error = new ValidationError(SR.GetString(SR.Error_ValidatorThrewException, e.GetType().FullName, validator.GetType().FullName, activity.Name, e.ToString()), ErrorNumbers.Error_ValidatorThrewException);
|
||
|
results.Errors.Add(CreateXomlCompilerError(error, parameters));
|
||
|
}
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
ValidationError error = new ValidationError(SR.GetString(SR.Error_ValidatorThrewException, e.GetType().FullName, validator.GetType().FullName, activity.Name, e.ToString()), ErrorNumbers.Error_ValidatorThrewException);
|
||
|
results.Errors.Add(CreateXomlCompilerError(error, parameters));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Code generation Helpers
|
||
|
|
||
|
internal static string[] GenerateFiles(CodeDomProvider codeDomProvider, CompilerParameters parameters, CodeCompileUnit[] ccus)
|
||
|
{
|
||
|
CodeGeneratorOptions options = new CodeGeneratorOptions();
|
||
|
options.BracingStyle = "C";
|
||
|
string[] filenames = new string[ccus.Length];
|
||
|
for (int i = 0; i < ccus.Length; i++)
|
||
|
{
|
||
|
ResolveReferencedAssemblies(parameters, ccus[i]);
|
||
|
filenames[i] = parameters.TempFiles.AddExtension(i + codeDomProvider.FileExtension);
|
||
|
Stream temp = new FileStream(filenames[i], FileMode.Create, FileAccess.Write, FileShare.Read);
|
||
|
try
|
||
|
{
|
||
|
using (StreamWriter sw = new StreamWriter(temp, Encoding.UTF8))
|
||
|
{
|
||
|
codeDomProvider.GenerateCodeFromCompileUnit(ccus[i], sw, options);
|
||
|
sw.Flush();
|
||
|
}
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
temp.Close();
|
||
|
}
|
||
|
}
|
||
|
return filenames;
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
|
||
|
#region Class MetaDataReader
|
||
|
|
||
|
// The GUIDs, enums, structs and interface definitions in this class are from
|
||
|
// cor.h and corhdr.h in the SDK\v2.0\include folder of the .net Framework SDK.
|
||
|
internal static class MetaDataReader
|
||
|
{
|
||
|
private static class Guids
|
||
|
{
|
||
|
public const string CLSID_MetaDataDispenser = "E5CB7A31-7512-11d2-89CE-0080C792E5D8";
|
||
|
public const string IID_IMetaDataDispenser = "809C652E-7396-11d2-9771-00A0C9B4D50C";
|
||
|
public const string IID_IMetaDataImport = "7DAC8207-D3AE-4c75-9B67-92801A497D44";
|
||
|
public const string IID_IMetaDataAssemblyImport = "EE62470B-E94B-424e-9B7C-2F00C9249F93";
|
||
|
}
|
||
|
|
||
|
enum MetadataTokenType
|
||
|
{
|
||
|
ModuleRef = 0x1a000000,
|
||
|
AssemblyRef = 0x23000000
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
struct OsInfo
|
||
|
{
|
||
|
uint osPlatformId;
|
||
|
uint osMajorVersion;
|
||
|
uint osMinorVersion;
|
||
|
}
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
struct AssemblyMetadata
|
||
|
{
|
||
|
public ushort majorVersion;
|
||
|
public ushort minorVersion;
|
||
|
public ushort buildNumber;
|
||
|
public ushort revisionNumber;
|
||
|
[SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources", Justification = "This points to memory and not to a native handle. So leaking will result in a memory leak at worst")]
|
||
|
public IntPtr locale;
|
||
|
public uint localeSize;
|
||
|
public IntPtr processorIds;
|
||
|
public uint processorIdCount;
|
||
|
public IntPtr osInfo;
|
||
|
public uint osInfoCount;
|
||
|
};
|
||
|
|
||
|
[ComImport(), Guid(Guids.CLSID_MetaDataDispenser)]
|
||
|
private class MetaDataDispenser
|
||
|
{
|
||
|
}
|
||
|
|
||
|
[ComImport(), Guid(Guids.IID_IMetaDataDispenser), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||
|
private interface IMetaDataDispenser
|
||
|
{
|
||
|
void DefineScope();
|
||
|
int OpenScope([In, MarshalAs(UnmanagedType.LPWStr)]string scopeName, uint openFlags, [In]ref Guid riid, [Out, MarshalAs(UnmanagedType.IUnknown)] out object unknown);
|
||
|
void OpenScopeOnMemory();
|
||
|
|
||
|
}
|
||
|
|
||
|
[ComImport(), Guid(Guids.IID_IMetaDataImport), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||
|
private interface IMetaDataImport
|
||
|
{
|
||
|
void CloseEnum([In] IntPtr enumHandle);
|
||
|
void CountEnum();
|
||
|
void ResetEnum();
|
||
|
void EnumTypeDefs();
|
||
|
void EnumInterfaceImpls();
|
||
|
int EnumTypeRefs([In, Out] ref IntPtr enumHandle, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] uint[] rTypeRefs, uint cMax, ref uint typeRefs);
|
||
|
void FindTypeDefByName();
|
||
|
void GetScopeProps();
|
||
|
void GetModuleFromScope();
|
||
|
void GetTypeDefProps();
|
||
|
void GetInterfaceImplProps();
|
||
|
int GetTypeRefProps([In] uint typeRefToken, [Out] out uint resolutionScopeToken, IntPtr typeRefName, uint nameLength, [Out] out uint actualLength);
|
||
|
/*....*/
|
||
|
}
|
||
|
|
||
|
[ComImport(), Guid(Guids.IID_IMetaDataAssemblyImport), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||
|
private interface IMetaDataAssemblyImport
|
||
|
{
|
||
|
void GetAssemblyProps();
|
||
|
int GetAssemblyRefProps([In] uint assemblyRefToken, [Out] out IntPtr publicKeyOrToken, [Out] out uint sizePublicKeyOrToken, IntPtr assemblyName, [In] uint assemblyNameBufferSize, [Out] out uint assemblyNameSize, [Out] out AssemblyMetadata assemblyMetaData, [Out] out IntPtr hashValueBlob, [Out] out uint hashValueSize, [Out] out uint assemblyRefFlags);
|
||
|
/*....*/
|
||
|
}
|
||
|
|
||
|
private static MetadataTokenType TokenTypeFromToken(uint token)
|
||
|
{
|
||
|
return (MetadataTokenType)(token & 0xff000000);
|
||
|
}
|
||
|
|
||
|
internal static IEnumerable GetTypeRefNames(string assemblyLocation)
|
||
|
{
|
||
|
IMetaDataDispenser metaDataDispenser = new MetaDataDispenser() as IMetaDataDispenser;
|
||
|
|
||
|
if (metaDataDispenser == null)
|
||
|
{
|
||
|
throw new InvalidOperationException(String.Format(SR.GetString(SR.Error_MetaDataInterfaceMissing), assemblyLocation, "IMetaDataDispenser"));
|
||
|
}
|
||
|
|
||
|
Guid guidMetaDataImport = new Guid(Guids.IID_IMetaDataImport);
|
||
|
object metaDataImportObj = null;
|
||
|
|
||
|
NativeMethods.ThrowOnFailure(metaDataDispenser.OpenScope(assemblyLocation, 0, ref guidMetaDataImport, out metaDataImportObj));
|
||
|
IMetaDataImport metaDataImport = metaDataImportObj as IMetaDataImport;
|
||
|
if (metaDataImport == null)
|
||
|
{
|
||
|
throw new InvalidOperationException(String.Format(SR.GetString(SR.Error_MetaDataInterfaceMissing), assemblyLocation, "IMetaDataImport"));
|
||
|
}
|
||
|
|
||
|
IntPtr enumHandle = new IntPtr();
|
||
|
uint[] typeRefs = new uint[20];
|
||
|
uint typeRefCount = 0;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
NativeMethods.ThrowOnFailure((metaDataImport.EnumTypeRefs(ref enumHandle, typeRefs, (uint)typeRefs.Length, ref typeRefCount)));
|
||
|
for (int typeRefIndex = 0; typeRefIndex < typeRefCount; typeRefIndex++)
|
||
|
{
|
||
|
IntPtr typeRefNamePtr = IntPtr.Zero;
|
||
|
uint typeRefNameLength;
|
||
|
uint resolutionScopeToken;
|
||
|
|
||
|
NativeMethods.ThrowOnFailure(metaDataImport.GetTypeRefProps(typeRefs[typeRefIndex], out resolutionScopeToken, typeRefNamePtr, 0, out typeRefNameLength));
|
||
|
if (typeRefNameLength > 0)
|
||
|
{
|
||
|
string typeRefName = String.Empty;
|
||
|
typeRefNamePtr = Marshal.AllocCoTaskMem((int)(2 * (typeRefNameLength + 1)));
|
||
|
try
|
||
|
{
|
||
|
NativeMethods.ThrowOnFailure(metaDataImport.GetTypeRefProps(typeRefs[typeRefIndex], out resolutionScopeToken, typeRefNamePtr, typeRefNameLength, out typeRefNameLength));
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
typeRefName = Marshal.PtrToStringUni(typeRefNamePtr);
|
||
|
Marshal.FreeCoTaskMem(typeRefNamePtr);
|
||
|
}
|
||
|
|
||
|
IMetaDataAssemblyImport metaDataAssemblyImport = metaDataImportObj as IMetaDataAssemblyImport;
|
||
|
if (metaDataAssemblyImport == null)
|
||
|
{
|
||
|
throw new InvalidOperationException(String.Format(SR.GetString(SR.Error_MetaDataInterfaceMissing), assemblyLocation, "IMetaDataAssemblyImport"));
|
||
|
}
|
||
|
|
||
|
if (TokenTypeFromToken(resolutionScopeToken) == MetadataTokenType.AssemblyRef)
|
||
|
{
|
||
|
AssemblyMetadata assemblyMetadata;
|
||
|
IntPtr publicKeyOrToken = IntPtr.Zero;
|
||
|
uint publicKeyOrTokenSize;
|
||
|
IntPtr assemblyName = IntPtr.Zero;
|
||
|
uint assemblyNameSize;
|
||
|
IntPtr hashValueBlob = IntPtr.Zero;
|
||
|
uint hashValueSize;
|
||
|
uint assemblyRefFlags;
|
||
|
|
||
|
NativeMethods.ThrowOnFailure(metaDataAssemblyImport.GetAssemblyRefProps(resolutionScopeToken, out publicKeyOrToken, out publicKeyOrTokenSize, assemblyName, 0, out assemblyNameSize, out assemblyMetadata, out hashValueBlob, out hashValueSize, out assemblyRefFlags));
|
||
|
|
||
|
if (assemblyNameSize > 0)
|
||
|
assemblyName = Marshal.AllocCoTaskMem((int)(2 * (assemblyNameSize + 1)));
|
||
|
|
||
|
if (assemblyMetadata.localeSize > 0)
|
||
|
assemblyMetadata.locale = Marshal.AllocCoTaskMem((int)(2 * (assemblyMetadata.localeSize + 1)));
|
||
|
|
||
|
try
|
||
|
{
|
||
|
if (assemblyNameSize > 0 || assemblyMetadata.localeSize > 0)
|
||
|
{
|
||
|
NativeMethods.ThrowOnFailure(metaDataAssemblyImport.GetAssemblyRefProps(resolutionScopeToken, out publicKeyOrToken, out publicKeyOrTokenSize, assemblyName, assemblyNameSize, out assemblyNameSize, out assemblyMetadata, out hashValueBlob, out hashValueSize, out assemblyRefFlags));
|
||
|
}
|
||
|
|
||
|
String publicKeyString = String.Empty;
|
||
|
for (int pos = 0; pos < publicKeyOrTokenSize; pos++)
|
||
|
{
|
||
|
publicKeyString += String.Format("{0}", Marshal.ReadByte(publicKeyOrToken, pos).ToString("x2"));
|
||
|
}
|
||
|
|
||
|
yield return String.Format("{0}, {1}, Version={2}.{3}.{4}.{5}, Culture={6}, PublicKeyToken={7}", typeRefName, Marshal.PtrToStringUni(assemblyName), assemblyMetadata.majorVersion, assemblyMetadata.minorVersion, assemblyMetadata.buildNumber, assemblyMetadata.revisionNumber, String.IsNullOrEmpty(Marshal.PtrToStringUni(assemblyMetadata.locale)) ? "neutral" : Marshal.PtrToStringUni(assemblyMetadata.locale), String.IsNullOrEmpty(publicKeyString) ? "null" : publicKeyString);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (assemblyName != IntPtr.Zero && assemblyNameSize > 0)
|
||
|
{
|
||
|
Marshal.FreeCoTaskMem(assemblyName);
|
||
|
assemblyName = IntPtr.Zero;
|
||
|
}
|
||
|
|
||
|
if (assemblyMetadata.locale != IntPtr.Zero && assemblyMetadata.localeSize > 0)
|
||
|
{
|
||
|
Marshal.FreeCoTaskMem(assemblyMetadata.locale);
|
||
|
assemblyMetadata.locale = IntPtr.Zero;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
while (typeRefCount > 0);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
metaDataImport.CloseEnum(enumHandle);
|
||
|
}
|
||
|
yield break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|