Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Automatic Implementation of the Event-Based Asynchronous Pattern

, 26 Nov 2008
Implement the event-based asynchronous pattern automatically with this code generator
asyncgen_demo.zip
asyncgen_demo
ClassLibrary1
bin
Debug
ClassDiagram.cd
Properties
Client
bin
Debug
Properties
Settings.settings
Server
bin
Debug
Properties
asyncgen_src.zip
asyncgen_src
AsyncClientGeneratorLib
asyncgen.snk
ClassDiagram.cd
fxReflection.proj
Properties
Properties
TestAssembly
Properties
TestClientCpp
app.ico
TestClientCSharp
Properties
TestClientVB
My Project
Application.myapp
Settings.settings
using System;
using System.Collections.Generic;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;

namespace AsyncGen
{
    static class Program
    {
        static void Main(string[] args)
        {
            List<string> assemblies = ParseCommandLine(args);

            #region Load the specified assembly.

            if (!File.Exists(assemblies[0]))
            {
                Console.Error.WriteLine("File not found: {0}", assemblies[0]);
                return;
            }

            Assembly asm = Assembly.LoadFrom(assemblies[0]);

            #endregion

            foreach (Type serverType in asm.GetExportedTypes())
            {
                // Only treat types marked with the [GenerateAsyncClient] attribute.

                object[] asyncClientClassAttributes = serverType.GetCustomAttributes(typeof(GenerateAsyncClientClassAttribute), false);

                if (asyncClientClassAttributes.Length == 0)
                {
                    continue;
                }

#if USE_SNIPPETS
                addAccessorBodies.Clear();
                removeAccessorBodies.Clear();
#endif

                GenerateAsyncClientClassAttribute asyncClientClassAttribute = (GenerateAsyncClientClassAttribute)asyncClientClassAttributes[0];

                string asyncClientClassName =
                    string.IsNullOrEmpty(asyncClientClassAttribute.ClassName)
                    ? (serverType.IsInterface && serverType.Name.StartsWith("I") ? serverType.Name.Substring(1) : serverType.Name) + "Client"
                    : asyncClientClassAttribute.ClassName;

                CodeCompileUnit compileUnit = new CodeCompileUnit();
                CodeNamespace nameSpace = MakeNamespace(serverType, compileUnit);
                CodeTypeDeclaration asyncClientClass = MakeAsyncClientClass(serverType, asyncClientClassName, nameSpace);
                CodeConstructor ctor = MakeConstructorSkeleton();
                ctor.Parameters.Add(new CodeParameterDeclarationExpression(serverType, "server"));
                ctor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("server"));
                nameSpace.Types.Add(asyncClientClass);
                asyncClientClass.Members.Add(ctor);

                foreach (MethodInfo method in serverType.GetMethods())
                {
                    if (!method.IsPublic)
                    {
                        continue;
                    }

                    object[] asyncOperationAttributes = method.GetCustomAttributes(typeof(GenerateAsyncOperationAttribute), false);

                    if (asyncOperationAttributes.Length == 0)
                    {
                        continue;
                    }

                    GenerateAsyncOperationAttribute asyncOperationAttribute = (GenerateAsyncOperationAttribute)asyncOperationAttributes[0];

                    GenerateAsyncOperation(serverType.Name, asyncClientClass, method, ctor, asyncOperationAttribute);
                }

                switch (outputLanguage)
                {
                case OutputLanguage.CS:
                    codeDomProvider = new Microsoft.CSharp.CSharpCodeProvider();
                    break;
                case OutputLanguage.VB:
                    codeDomProvider = new Microsoft.VisualBasic.VBCodeProvider();
                    break;
#if !USE_SNIPPETS
                case OutputLanguage.CPP:
                    codeDomProvider = new Microsoft.VisualC.CppCodeProvider();
                    break;
#endif
                default:
                    Debug.Fail(string.Format("Invalid language ({0})", outputLanguage));
                    break;
                }
                string sourceFileName = asyncClientClassName + ".generated." + codeDomProvider.FileExtension;
                GenerateCode(compileUnit, codeDomProvider, sourceFileName);
                CompileGeneratedCode(assemblies, codeDomProvider, sourceFileName);
            }

            Pause();
        }

        private static List<string> ParseCommandLine(string[] args)
        {
            if (args.Length < 1)
            {
                Usage();
            }

            List<string> assemblyNames = new List<string>(args.Length);
            for (int i = 0; i < args.Length; i++)
            {
                if (!args[i].StartsWith("-") && !args[i].StartsWith("/"))
                {
                    assemblyNames.Add(args[i]);
                }
                else
                {
                    string[] words = args[i].Split(':');
                    if (5 <= words[0].Length && words[0].Substring(1, 4).ToLower() == "lang")
                    {
                        if (words.Length < 2)
                        {
                            Console.Error.WriteLine("Language identifier expected after \"/language\".");
                            Usage();
                        }
                        else
                        {
                            try
                            {
                                outputLanguage = (OutputLanguage)Enum.Parse(typeof(OutputLanguage), words[1], true);
                            }
                            catch (ArgumentException)
                            {
                                Console.Error.WriteLine("Unrecognized language identifier: {0}.", words[1]);
                                Console.Error.Write("Must be one of the following:");
                                foreach (OutputLanguage language in Enum.GetValues(typeof(OutputLanguage)))
                                {
                                    Console.Error.Write(" {0}", language);
                                }
                                Console.Error.WriteLine();
                                Usage();
                            }
                        }
                    }
                    else
                    {
                        Console.Error.WriteLine("Unrecognized option: \"{0}\".", args[i]);
                        Usage();
                    }
                }
            }

            if (0 == assemblyNames.Count)
            {
                Usage();
            }

            return assemblyNames;
        }

        private static void Usage()
        {
            Console.Error.WriteLine("USAGE: AsyncGen [ /language:<language identifier> ] <assembly> [ <additional assembly needed for compiling the generated code>+ ]");
            Pause();
            Environment.Exit(-1);
        }

        private static void Pause()
        {
            if (Debugger.IsAttached)
            {
                Console.Error.WriteLine("Press Enter to continue...");
                Console.In.ReadLine();
            }
        }

        private static CodeNamespace MakeNamespace(Type serverType, CodeCompileUnit compileUnit)
        {
            CodeNamespace value = new CodeNamespace(serverType.Namespace);
            value.Imports.Add(new CodeNamespaceImport("System"));
            value.Imports.Add(new CodeNamespaceImport("System.ComponentModel"));
            value.Imports.Add(new CodeNamespaceImport("System.Diagnostics"));
            value.Imports.Add(new CodeNamespaceImport("AsyncGen"));
            compileUnit.Namespaces.Add(value);
            return value;
        }

        private static CodeTypeDeclaration MakeAsyncClientClass(Type serverType, string asyncClientClassName, CodeNamespace nameSpace)
        {
            CodeTypeDeclaration value = new CodeTypeDeclaration(asyncClientClassName);
            value.IsPartial = true;
            value.BaseTypes.Add(new CodeTypeReference("ClientBase", new CodeTypeReference(serverType)));
            return value;
        }

        private static void GenerateAsyncOperation(string serverTypeName, CodeTypeDeclaration asyncClientClass, MethodInfo method, CodeConstructor clientCtor, GenerateAsyncOperationAttribute asyncOperationAttribute)
        {
            string operationBaseName = string.IsNullOrEmpty(asyncOperationAttribute.BaseName) ? method.Name : asyncOperationAttribute.BaseName;
            string asyncMethodName = string.IsNullOrEmpty(asyncOperationAttribute.StartMethodName) ? operationBaseName + "Async" : asyncOperationAttribute.StartMethodName;
            string cancelMethodName = string.IsNullOrEmpty(asyncOperationAttribute.CancelMethodName) ? operationBaseName + "AsyncCancel" : asyncOperationAttribute.CancelMethodName;
            string completionEventName = string.IsNullOrEmpty(asyncOperationAttribute.CompletedEventName) ? operationBaseName + "Completed" : asyncOperationAttribute.CompletedEventName;
            string outputTypeName = string.IsNullOrEmpty(asyncOperationAttribute.OutputTypeName) ? operationBaseName + "Output" : asyncOperationAttribute.OutputTypeName;
            string delegateTypeName = operationBaseName + "Delegate";
            string trackerClassName = operationBaseName + "Tracker";
            string trackerFieldName = "_" + operationBaseName.ToLower() + "Tracker";

            #region Identify special parameters.

            ParameterInfo taskIDParam = null, callbackParam = null;
            foreach (ParameterInfo param in method.GetParameters())
            {
                object[] userStateAttributes = param.GetCustomAttributes(typeof(TaskIDAttribute), false);
                if (userStateAttributes.Length > 0)
                {
                    taskIDParam = param;
                }
            }

            #endregion

            asyncClientClass.Members.Add(MakeSyncMethod(asyncClientClass, method, asyncOperationAttribute.CallbackInterface, trackerFieldName, taskIDParam));

            CodeTypeDeclaration outputType = MakeOutputType(method, outputTypeName);
            if (outputType.Members.Count > 1)
            {
                AddNewMember(asyncClientClass, outputType);
            }
            clientCtor.Statements.Add(MakeTrackerInitializationStatement(trackerClassName, trackerFieldName));
#if !USE_SNIPPETS
            clientCtor.Statements.Add(MakeAttachTrackerCompletedEventStatement(trackerFieldName, "OperationCompleted", outputType));
#endif
            asyncClientClass.Members.Add(MakeAsyncMethod(method, taskIDParam, new ParameterInfo[] { taskIDParam, callbackParam }, asyncOperationAttribute.CallbackInterface, delegateTypeName, asyncMethodName, trackerFieldName));
            if (asyncOperationAttribute.GenerateCancelMethod)
            {
                CodeMemberMethod cancelMethod = GetMethod(asyncClientClass, cancelMethodName);
                if (cancelMethod == null)
                {
                    asyncClientClass.Members.Add(MakeCancelMethod(method, cancelMethodName, trackerFieldName));
                }
                else
                {
                    CodeStatement lastStatement = cancelMethod.Statements[cancelMethod.Statements.Count - 1];
                    cancelMethod.Statements.RemoveAt(cancelMethod.Statements.Count - 1);
                    cancelMethod.Statements.Add(MakeTrackerCancelOperationMethodInvocation(trackerFieldName, taskIDParam));
                    cancelMethod.Statements.Add(lastStatement);
                }
            }
            AddNewMember(asyncClientClass, MakeCompletionEvent(completionEventName, trackerFieldName, outputType));
            asyncClientClass.Members.Add(MakeTrackerClass(method, asyncClientClass, clientCtor, serverTypeName, trackerClassName, trackerFieldName, delegateTypeName, outputType, asyncOperationAttribute));
            asyncClientClass.Members.Add(MakeTrackerField(trackerClassName, trackerFieldName));
#if !USE_SNIPPETS
            asyncClientClass.Members.Add(MakeTrackerCompletionEventHandler(completionEventName, trackerFieldName, outputType));
#endif
            asyncClientClass.Members.Add(MakeDelegateType(method, delegateTypeName));
        }

        private static CodeMemberMethod GetMethod(CodeTypeDeclaration asyncClientClass, string methodName)
        {
            foreach (CodeTypeMember member in asyncClientClass.Members)
            {
                CodeMemberMethod method = member as CodeMemberMethod;
                if (method != null && method.Name == methodName)
                {
                    return method;
                }
            }
            return null;
        }

        private static CodeMemberMethod MakeSyncMethod(CodeTypeDeclaration asyncClientClass, MethodInfo method, Type callbackInterface, string trackerFieldName, ParameterInfo taskIDParam)
        {
            CodeMemberMethod value = MakeSyncMethodSkeleton(method, callbackInterface);
            CodeMethodInvokeExpression serverMethodInvocation = new CodeMethodInvokeExpression();
            serverMethodInvocation.Method.TargetObject = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "server");
            serverMethodInvocation.Method.MethodName = method.Name;
            foreach (ParameterInfo param in method.GetParameters())
            {
                serverMethodInvocation.Parameters.Add(
                    param.ParameterType == callbackInterface
                    ? (CodeExpression)new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName)
                    : (CodeExpression)MakeInvocationArgument(param));
            }

            if (method.ReturnType != typeof(void))
            {
                value.Statements.Add(new CodeVariableDeclarationStatement(method.ReturnType, "value"));
            }
            CodeMethodInvokeExpression createOperationInvocation = new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName), "CreateOperation");
            if (taskIDParam != null)
            {
                createOperationInvocation.Parameters.Add(new CodeArgumentReferenceExpression(taskIDParam.Name));
            }
            value.Statements.Add(createOperationInvocation);
            CodeTryCatchFinallyStatement tryFinallyStatement = new CodeTryCatchFinallyStatement();
            tryFinallyStatement.TryStatements.Add(
                method.ReturnType == typeof(void)
                ? (CodeStatement)new CodeExpressionStatement(serverMethodInvocation)
                : (CodeStatement)new CodeAssignStatement(new CodeVariableReferenceExpression("value"), serverMethodInvocation));
            CodeMethodInvokeExpression completeOperationInvocation = new CodeMethodInvokeExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName), "CompleteOperation");
            if (taskIDParam != null)
            {
                completeOperationInvocation.Parameters.Add(new CodeArgumentReferenceExpression(taskIDParam.Name));
            }
            tryFinallyStatement.FinallyStatements.Add(completeOperationInvocation);
            value.Statements.Add(tryFinallyStatement);
            if (method.ReturnType != typeof(void))
            {
                value.Statements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("value")));
            }
            return value;
        }

        private static CodeMemberMethod MakeSyncMethodSkeleton(MethodInfo method, Type callbackInterface)
        {
            CodeMemberMethod value = new CodeMemberMethod();
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            value.ReturnType = new CodeTypeReference(method.ReturnType);
            value.Name = method.Name;
            foreach (ParameterInfo param in method.GetParameters())
            {
                if (param.ParameterType != callbackInterface)
                {
                    value.Parameters.Add(MakeParameterDeclaration(param));
                }
            }
            return value;
        }

        private static CodeAssignStatement MakeTrackerInitializationStatement(string trackerClassName, string trackerFieldName)
        {
            return new CodeAssignStatement(
                                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName),
                                    new CodeObjectCreateExpression(trackerClassName,
                                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "server"),
                                    new CodeThisReferenceExpression()));
        }

        private static void ImplementCallbackInterface(Type callbackInterface, CodeConstructor clientCtor, CodeTypeDeclaration asyncClientClass, CodeTypeDeclaration trackerClass, string trackerFieldName, string operationBaseName)
        {
            foreach (MethodInfo interfaceMethod in callbackInterface.GetMethods())
            {
                CodeMemberMethod implementingMethod = MakeCallbackMethodSkeleton(callbackInterface, interfaceMethod);

                object[] progressEventAttributes = interfaceMethod.GetCustomAttributes(typeof(GenerateProgressEventAttribute), false);

                if (progressEventAttributes.Length == 0)
                {
                    implementingMethod.Statements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(Exception), new CodePrimitiveExpression("The operation is not implemented."))));
                }
                else
                {
                    GenerateProgressEventAttribute progressEventAttribute = (GenerateProgressEventAttribute)progressEventAttributes[0];
                    string progressEventName = progressEventAttribute.EventName == null ? operationBaseName + "ProgressChanged" : progressEventAttribute.EventName;
                    string onProgressChangedMethodName = "On" + progressEventName;
                    string intermediateResultsTypeName = progressEventAttribute.ResultsTypeName == null ? operationBaseName + "IntermediateResults" : progressEventAttribute.ResultsTypeName;
                    CodeTypeDeclaration intermediateResultsType = MakeIntermediateResultsType(interfaceMethod, intermediateResultsTypeName);
                    if (intermediateResultsType.Members.Count > 1)
                    {
                        AddNewMember(asyncClientClass, intermediateResultsType);
                    }
                    AddNewMember(trackerClass, MakeOnProgressChangedMethod(progressEventName, intermediateResultsType));
                    AddNewMember(trackerClass, MakeSimpleProgressEvent(progressEventName, intermediateResultsType));
#if USE_SNIPPETS
                    AddNewMember(asyncClientClass, 
                        MakeCustomProgressEvent(progressEventName, trackerFieldName, intermediateResultsType));
#else
                    AddNewMember(asyncClientClass,
                        MakeSimpleProgressEvent(progressEventName, intermediateResultsType));
                    AddNewMember(asyncClientClass, MakeTrackerProgressEventHandler(progressEventName, trackerFieldName, intermediateResultsType));
                    clientCtor.Statements.Add(MakeAttachTrackerProgressEventStatement(trackerFieldName, progressEventName, intermediateResultsType));
#endif
                        
                    ImplementCallbackMethod(asyncClientClass, implementingMethod, interfaceMethod, progressEventAttribute, operationBaseName, onProgressChangedMethodName, intermediateResultsType);
                }
                trackerClass.Members.Add(implementingMethod);
            }
        }

        private static void ImplementCallbackMethod(CodeTypeDeclaration asyncClientClass, CodeMemberMethod implementationMethod, MethodInfo interfaceMethod, GenerateProgressEventAttribute progressEventAttribute, string operationBaseName, string onProgressChangedMethodName, CodeTypeDeclaration intermediateResultsType)
        {
            #region Identify special parameters.

            ParameterInfo progressPercentageParam = null, taskIDParam = null, cancelFlag = null;
            foreach (ParameterInfo interfaceParam in interfaceMethod.GetParameters())
            {
                IdentifySpecialCallbackParameters(ref progressPercentageParam, ref taskIDParam, ref cancelFlag, interfaceParam);
            }
            IdentifySpecialCallbackParameters(ref progressPercentageParam, ref taskIDParam, ref cancelFlag, interfaceMethod.ReturnParameter);

            #endregion

            #region Validate attribute consistency.

            if (cancelFlag != null && (!cancelFlag.IsOut && !cancelFlag.IsRetval && !string.IsNullOrEmpty(cancelFlag.Name) || cancelFlag.ParameterType.FullName.StartsWith("Boolean")))
            {
                throw new ApplicationException("The [CancelFlag] attribute can only be applied to \"out bool\" parameters.");
            }

            //if (cancelFlag != null && progressEventAttribute.Async)
            //{
            //    throw new ApplicationException("[CancelFlag] specified but the callback is asynchronous.  Use [GenerateProgressEvent(Async = false)] to make it synchronous.");
            //}

            #endregion

            if (intermediateResultsType.Members.Count > 1)
            {
                implementationMethod.Statements.Add(new CodeVariableDeclarationStatement(intermediateResultsType.Name, "results", new CodeObjectCreateExpression(intermediateResultsType.Name)));
                foreach (ParameterInfo param in interfaceMethod.GetParameters())
                {
                    if (!param.IsOut && param != progressPercentageParam && param != taskIDParam)
                    {
                        CodeFieldReferenceExpression fieldRef = new CodeFieldReferenceExpression(new CodeVariableReferenceExpression("results"), param.Name);
                        CodeArgumentReferenceExpression argRef = new CodeArgumentReferenceExpression(param.Name);
                        implementationMethod.Statements.Add(new CodeAssignStatement(fieldRef, argRef));
                    }
                }
            }

            CodeMethodReferenceExpression sendOrPostProgressMethod = new CodeMethodReferenceExpression(
                new CodeThisReferenceExpression(),
                progressEventAttribute.Async ? "PostProgress" : "SendProgress");

            if (intermediateResultsType.Members.Count > 0)
            {
                sendOrPostProgressMethod.TypeArguments.Add(intermediateResultsType.Name);
            }

            CodeMethodInvokeExpression sendOrPostProgressInvocation =
                new CodeMethodInvokeExpression(sendOrPostProgressMethod,
                new CodeDelegateCreateExpression(
                new CodeTypeReference(typeof(SendOrPostCallback)),
                new CodeThisReferenceExpression(),
                string.Format("On{0}", progressEventAttribute.EventName)),
                new CodeArgumentReferenceExpression(progressPercentageParam.Name));

            switch (intermediateResultsType.Members.Count)
            {
            case 0:
                break;
            case 1:
                sendOrPostProgressInvocation.Parameters.Add(new CodeArgumentReferenceExpression(intermediateResultsType.Members[0].Name));
                break;
            default:
                sendOrPostProgressInvocation.Parameters.Add(new CodeVariableReferenceExpression("results"));
                break;
            }

            if (taskIDParam != null)
            {
                sendOrPostProgressInvocation.Parameters.Add(new CodeArgumentReferenceExpression(taskIDParam.Name));
            }

            implementationMethod.Statements.Add(sendOrPostProgressInvocation);

            if (cancelFlag != null)
            {
                CodeMethodInvokeExpression isOperationCancelledInvocation = new CodeMethodInvokeExpression(
                    new CodeThisReferenceExpression(), "IsOperationCancelled");

                if (taskIDParam != null)
                {
                    isOperationCancelledInvocation.Parameters.Add(new CodeArgumentReferenceExpression(taskIDParam.Name));
                }

                if (cancelFlag.IsRetval || string.IsNullOrEmpty(cancelFlag.Name))
                {
                    implementationMethod.Statements.Add(
                        new CodeMethodReturnStatement(
                        isOperationCancelledInvocation));
                }
                else
                {
                    implementationMethod.Statements.Add(
                        new CodeAssignStatement(
                        new CodeArgumentReferenceExpression(cancelFlag.Name),
                        isOperationCancelledInvocation));
                }

            }
        }

        private static void IdentifySpecialCallbackParameters(ref ParameterInfo progressPercentageParam, ref ParameterInfo taskIDParam, ref ParameterInfo cancelFlag, ParameterInfo interfaceParam)
        {
            object[] progressPercentageAttributes = interfaceParam.GetCustomAttributes(typeof(ProgressPercentageAttribute), false);
            object[] userStateAttributes = interfaceParam.GetCustomAttributes(typeof(TaskIDAttribute), false);
            object[] cancelFlagAttributes = interfaceParam.GetCustomAttributes(typeof(CancelFlagAttribute), false);
            if (progressPercentageAttributes.Length > 0)
            {
                progressPercentageParam = interfaceParam;
            }
            if (userStateAttributes.Length > 0)
            {
                taskIDParam = interfaceParam;
            }
            if (cancelFlagAttributes.Length > 0)
            {
                cancelFlag = interfaceParam;
            }
        }

        private static CodeMemberMethod MakeOnProgressChangedMethod(string progressEventName, CodeTypeDeclaration intermediateResultsType)
        {
            CodeMemberMethod value = new CodeMemberMethod();
            value.Attributes &= ~MemberAttributes.ScopeMask; // This makes it 'virtual.'
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Family;
            value.Name = "On" + progressEventName;
            value.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "args"));
            value.Statements.Add(MakeOnProgressChangedMethodBody(progressEventName, intermediateResultsType));
            return value;
        }

        private static CodeStatement MakeOnProgressChangedMethodBody(string progressEventName, CodeTypeDeclaration intermediateResultsType)
        {
            CodeDelegateInvokeExpression raiseProgressEvent = MakeRaiseProgressEventExpression(progressEventName, intermediateResultsType);
            if (OutputLanguage.CS == outputLanguage)
            {
                CodeConditionStatement value = new CodeConditionStatement();
	            value.Condition = new CodeBinaryOperatorExpression(new CodeEventReferenceExpression(new CodeThisReferenceExpression(), progressEventName), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null));
                value.TrueStatements.Add(raiseProgressEvent);
                return value;
            }
            else
            {
                return new CodeExpressionStatement(raiseProgressEvent);
            }

        }

        private static CodeDelegateInvokeExpression MakeRaiseProgressEventExpression(string progressEventName, CodeTypeDeclaration intermediateResultsType)
        {
            CodeDelegateInvokeExpression value = new CodeDelegateInvokeExpression();
            value.TargetObject = new CodeEventReferenceExpression(new CodeThisReferenceExpression(), progressEventName);
            value.Parameters.Add(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "client"));
            value.Parameters.Add(new CodeCastExpression(MakeEventArgsOrHandlerType(intermediateResultsType, false, true), new CodeArgumentReferenceExpression("args")));
            return value;
        }

        private static CodeMemberMethod MakeCallbackMethodSkeleton(Type callbackInterface, MethodInfo interfaceMethod)
        {
            CodeMemberMethod implementationMethod = new CodeMemberMethod();
            implementationMethod.Attributes &= ~MemberAttributes.AccessMask;
            if (OutputLanguage.CS == outputLanguage)
            {
                implementationMethod.Name = callbackInterface.Name + "." + interfaceMethod.Name;
            }
            else
            {
                implementationMethod.Attributes |= MemberAttributes.Public;
                implementationMethod.Name = interfaceMethod.Name;
            }
            implementationMethod.ReturnType = new CodeTypeReference(interfaceMethod.ReturnType);
            implementationMethod.ImplementationTypes.Add(callbackInterface);
            foreach (ParameterInfo interfaceParam in interfaceMethod.GetParameters())
            {
                implementationMethod.Parameters.Add(MakeParameterDeclaration(interfaceParam));
            }
            return implementationMethod;
        }

        private static CodeMemberMethod MakeTrackerCompletionEventHandler(string eventName, string trackerFieldName, CodeTypeDeclaration outputType)
        {
            return MakeTrackerEventHandler(eventName, trackerFieldName + "_OperationCompleted", MakeEventArgsOrHandlerType(outputType, true, true));
        }

        private static CodeMemberMethod MakeTrackerProgressEventHandler(string eventName, string trackerFieldName, CodeTypeDeclaration outputType)
        {
            return MakeTrackerEventHandler(eventName, trackerFieldName + "_" + eventName, MakeEventArgsOrHandlerType(outputType, false, true));
        }

        private static CodeMemberMethod MakeTrackerEventHandler(string eventName, string eventHandlerName, CodeTypeReference eventArgsType)
        {
            CodeMemberMethod value = new CodeMemberMethod();
            value.Name = eventHandlerName;
            value.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "sender"));
            value.Parameters.Add(new CodeParameterDeclarationExpression(eventArgsType, "args"));
            CodeDelegateInvokeExpression raiseEventExpression = new CodeDelegateInvokeExpression();
            raiseEventExpression.TargetObject = new CodeEventReferenceExpression(new CodeThisReferenceExpression(), eventName);
            raiseEventExpression.Parameters.Add(new CodeThisReferenceExpression());
            raiseEventExpression.Parameters.Add(new CodeArgumentReferenceExpression("args"));
            if (OutputLanguage.CS == outputLanguage)
            {
                CodeConditionStatement raiseEventIfNotNullStatement = new CodeConditionStatement();
                CodeBinaryOperatorExpression eventNotNull = new CodeBinaryOperatorExpression();
                eventNotNull.Operator = CodeBinaryOperatorType.IdentityInequality;
                eventNotNull.Left = new CodeEventReferenceExpression(new CodeThisReferenceExpression(), eventName);
                eventNotNull.Right = new CodePrimitiveExpression(null);
                raiseEventIfNotNullStatement.Condition = eventNotNull;
	            raiseEventIfNotNullStatement.TrueStatements.Add(raiseEventExpression);
                value.Statements.Add(raiseEventIfNotNullStatement);
            }
            else
            {
                value.Statements.Add(new CodeExpressionStatement(raiseEventExpression));
            }
            return value;
        }

        private static CodeAttachEventStatement MakeAttachTrackerCompletedEventStatement(string trackerFieldName, string eventName, CodeTypeDeclaration outputType)
        {
            return MakeAttachTrackerEventStatement(trackerFieldName, eventName, MakeEventArgsOrHandlerType(outputType, true, false));
        }

        private static CodeAttachEventStatement MakeAttachTrackerProgressEventStatement(string trackerFieldName, string eventName, CodeTypeDeclaration resultsType)
        {
            return MakeAttachTrackerEventStatement(trackerFieldName, eventName, MakeEventArgsOrHandlerType(resultsType, false, false));
        }

        private static CodeAttachEventStatement MakeAttachTrackerEventStatement(string trackerFieldName, string eventName, CodeTypeReference eventHandlerType)
        {
            CodeAttachEventStatement value = new CodeAttachEventStatement();
            value.Event = new CodeEventReferenceExpression();
            value.Event.TargetObject = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName);
            value.Event.EventName = eventName;
            value.Listener = new CodeDelegateCreateExpression(eventHandlerType, new CodeThisReferenceExpression(), trackerFieldName + "_" + eventName);
            return value;
        }

        private static CodeMemberField MakeTrackerField(string trackerClassName, string trackerFieldName)
        {
            return new CodeMemberField(trackerClassName, trackerFieldName);
        }

        private static CodeTypeDeclaration MakeTrackerClass(MethodInfo originalMethod, CodeTypeDeclaration asyncClientClass, CodeConstructor clientCtor, string serverTypeName, string trackerClassName, string trackerFieldName, string delegateTypeName, CodeTypeDeclaration outputType, GenerateAsyncOperationAttribute asyncOperationAttribute)
        {
            CodeTypeDeclaration value = new CodeTypeDeclaration();
            ParameterInfo taskIDParam = null;
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                object[] paramAttributes = param.GetCustomAttributes(typeof(TaskIDAttribute), false);
                if (paramAttributes.Length > 0)
                {
                    taskIDParam = param;
                }
            }
            value.Name = trackerClassName;
            value.TypeAttributes &= ~TypeAttributes.VisibilityMask;
            value.TypeAttributes |= TypeAttributes.NestedPrivate;
            CodeTypeReference baseType = new CodeTypeReference("OperationTracker", new CodeTypeReference(serverTypeName), new CodeTypeReference(asyncClientClass.Name), new CodeTypeReference(delegateTypeName));
            if (outputType.Members.Count > 0)
            {
                baseType.TypeArguments.Add(outputType.Name);
            }
            value.BaseTypes.Add(baseType);
            value.Members.Add(MakeTrackerConstructor(serverTypeName, asyncClientClass.Name));
            value.Members.Add(MakeTrackerCallEndInvokeMethod(originalMethod, delegateTypeName, outputType));
            if (asyncOperationAttribute.CallbackInterface != null)
            {
                value.BaseTypes.Add(new CodeTypeReference(asyncOperationAttribute.CallbackInterface));
                ImplementCallbackInterface(asyncOperationAttribute.CallbackInterface, clientCtor, asyncClientClass, value, trackerFieldName, asyncOperationAttribute.BaseName);
            }
            return value;
        }

        private static CodeConstructor MakeTrackerConstructor(string serverTypeName, string clientTypeName)
        {
            CodeConstructor ctor = new CodeConstructor();
            ctor.Attributes &= ~MemberAttributes.AccessMask;
            ctor.Attributes |= MemberAttributes.Public;
            ctor.Parameters.Add(new CodeParameterDeclarationExpression(serverTypeName, "server"));
            ctor.Parameters.Add(new CodeParameterDeclarationExpression(clientTypeName, "client"));
            ctor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("server"));
            ctor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("client"));
            return ctor;
        }

        private static CodeMemberMethod MakeTrackerCallEndInvokeMethod(MethodInfo originalMethod, string delegateTypeName, CodeTypeDeclaration outputType)
        {
            CodeMemberMethod value = MakeTrackerCallEndInvokeMethodSkeleton(delegateTypeName, outputType);
            CodeMethodInvokeExpression methodInvocation = new CodeMethodInvokeExpression();
            methodInvocation.Method = new CodeMethodReferenceExpression(new CodeArgumentReferenceExpression("d"), "EndInvoke");
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                if (param.ParameterType.IsByRef)
                {
                    if (!param.IsOut)
                    {
                        // Ref param
                        value.Statements.Add(
                            new CodeAssignStatement(
                            MakeOutputArgumentReferenceExpression(outputType, param.Name),
                            new CodeDefaultValueExpression(new CodeTypeReference(param.ParameterType.FullName.TrimEnd('&')))));
                    }
                    methodInvocation.Parameters.Add(new CodeDirectionExpression(
                        param.IsOut ? FieldDirection.Out : FieldDirection.Ref,
                        MakeOutputArgumentReferenceExpression(outputType, param.Name)));
                }
            }
            methodInvocation.Parameters.Add(new CodeArgumentReferenceExpression("iar"));
            if (originalMethod.ReturnType == typeof(void))
            {
                value.Statements.Add(methodInvocation);
            }
            else
            {
                value.Statements.Add(new CodeAssignStatement(MakeOutputArgumentReferenceExpression(outputType, "_ReturnValue"), methodInvocation));
            }
            return value;
        }

        private static CodeExpression MakeOutputArgumentReferenceExpression(CodeTypeDeclaration outputType, string fieldName)
        {
            if (outputType.Members.Count == 1)
            {
                return new CodeArgumentReferenceExpression("output");
            }
            else
            {
                return new CodeFieldReferenceExpression(new CodeArgumentReferenceExpression("output"), fieldName);
            }
        }

        private static CodeMemberMethod MakeTrackerCallEndInvokeMethodSkeleton(string delegateTypeName, CodeTypeDeclaration outputType)
        {
            CodeMemberMethod value = new CodeMemberMethod();
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Family;
            value.Attributes &= ~MemberAttributes.ScopeMask;
            value.Attributes |= MemberAttributes.Override;
            value.ReturnType = new CodeTypeReference(typeof(void));
            value.Name = "CallEndInvoke";
            value.Parameters.Add(new CodeParameterDeclarationExpression(delegateTypeName, "d"));
            value.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IAsyncResult), "iar"));
            if (outputType.Members.Count > 0)
            {
                CodeParameterDeclarationExpression outputParam = new CodeParameterDeclarationExpression(outputType.Name, "output");
                outputParam.Direction = FieldDirection.Out;
                value.Parameters.Add(outputParam);
            }
            return value;
        }

        private static CodeTypeDeclaration MakeOutputType(MethodInfo method, string typeName)
        {
            CodeTypeDeclaration value = new CodeTypeDeclaration();
            value.Name = typeName;
            value.IsStruct = true;
            if (method.ReturnType != typeof(void))
            {
                CodeMemberField field = new CodeMemberField(method.ReturnType, "_ReturnValue");
                field.Attributes &= ~MemberAttributes.AccessMask;
                field.Attributes |= MemberAttributes.Public;
                value.Members.Add(field);
            }
            foreach (ParameterInfo param in method.GetParameters())
            {
                if (param.ParameterType.IsByRef)
                {
                    CodeMemberField field = new CodeMemberField(param.ParameterType.FullName.TrimEnd('&'), param.Name);
                    field.Attributes &= ~MemberAttributes.AccessMask;
                    field.Attributes |= MemberAttributes.Public;
                    value.Members.Add(field);
                }
            }
            switch (value.Members.Count)
            {
            case 0:
                value.Name = typeof(void).FullName;
                break;
            case 1:
                CodeMemberField field = (CodeMemberField)value.Members[0];
                value.Name = field.Type.BaseType;
                break;
            default:
                break;
            }
            return value;
        }

        private static CodeTypeDeclaration MakeIntermediateResultsType(MethodInfo method, string typeName)
        {
            CodeTypeDeclaration value = new CodeTypeDeclaration();
            value.Name = typeName;
            value.IsStruct = true;
            foreach (ParameterInfo param in method.GetParameters())
            {
                if (!param.IsOut && param.GetCustomAttributes(false).Length == 0)
                {
                    string fieldTypeName = param.ParameterType.FullName;
                    CodeMemberField field = new CodeMemberField(param.ParameterType.IsByRef ? fieldTypeName.TrimEnd('&') : fieldTypeName, param.Name);
                    field.Attributes &= ~MemberAttributes.AccessMask;
                    field.Attributes |= MemberAttributes.Public;
                    value.Members.Add(field);
                }
            }
            switch (value.Members.Count)
            {
            case 0:
                value.Name = typeof(void).FullName;
                break;
            case 1:
                CodeMemberField field = (CodeMemberField)value.Members[0];
                value.Name = field.Type.BaseType;
                break;
            default:
                break;
            }
            return value;
        }

#if USE_SNIPPETS

        private static CodeSnippetTypeMember MakeCompletionEvent(string completedEventName, string trackerFieldName, CodeTypeDeclaration outputType)
        {
            CodeSnippetTypeMember value = new CodeSnippetTypeMember();
            value.Name = completedEventName;
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            string argsType = MakeEventArgsOrHandlerType("AsyncCompletedEventArgs", outputType);
            string handlerType = MakeEventArgsOrHandlerType("AsyncCompletedEventHandler", outputType);
            StringBuilder addAccessorBody, removeAccessorBody;
            if (!addAccessorBodies.TryGetValue(completedEventName, out addAccessorBody))
            {
                addAccessorBody = new StringBuilder();
                addAccessorBodies.Add(completedEventName, addAccessorBody);
            }
            if (!removeAccessorBodies.TryGetValue(completedEventName, out removeAccessorBody))
            {
                removeAccessorBody = new StringBuilder();
                removeAccessorBodies.Add(completedEventName, removeAccessorBody);
            }
            value.Text =
                MakeEventDeclarationStart(completedEventName, handlerType) +
                MakeEventAccessor('+', handlerType, trackerFieldName, "OperationCompleted", addAccessorBody) +
                MakeEventAccessor('-', handlerType, trackerFieldName, "OperationCompleted", removeAccessorBody) +
                //MakeEventAccessor('^', handlerType, trackerFieldName, "OperationCompleted") +
                MakeRaiseEventAccessor(argsType, completedEventName) +
                MakeEventDeclarationEnd();
            return value;
        }

#else

        private static CodeMemberEvent MakeCompletionEvent(string completedEventName, string trackerFieldName, CodeTypeDeclaration outputType)
        {
            CodeMemberEvent value = new CodeMemberEvent();
            value.Name = completedEventName;
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            value.Type = MakeEventArgsOrHandlerType(outputType, true, false);
            return value;
        }

#endif

#if USE_SNIPPETS

        private static CodeSnippetTypeMember MakeCustomProgressEvent(string progressEventName, string trackerFieldName, CodeTypeDeclaration intermediateResultsType)
        {
            CodeSnippetTypeMember value = new CodeSnippetTypeMember();
            value.Name = progressEventName;
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            string handlerType = MakeEventArgsOrHandlerType("ProgressChangedEventHandler", intermediateResultsType);
            string argsType = MakeEventArgsOrHandlerType("ProgressChangedEventArgs", intermediateResultsType);
            StringBuilder addAccessorBody, removeAccessorBody;
            if (!addAccessorBodies.TryGetValue(progressEventName, out addAccessorBody))
            {
                addAccessorBody = new StringBuilder();
                addAccessorBodies.Add(progressEventName, addAccessorBody);
            }
            if (!removeAccessorBodies.TryGetValue(progressEventName, out removeAccessorBody))
            {
                removeAccessorBody = new StringBuilder();
                removeAccessorBodies.Add(progressEventName, removeAccessorBody);
            }
            value.Text =
                MakeEventDeclarationStart(progressEventName, handlerType) +
                MakeEventAccessor('+', handlerType, trackerFieldName, progressEventName, addAccessorBody) +
                MakeEventAccessor('-', handlerType, trackerFieldName, progressEventName, removeAccessorBody) +
                //MakeEventAccessor('^', handlerType, trackerFieldName, progressEventName) +
                MakeRaiseEventAccessor(argsType, progressEventName) +
                MakeEventDeclarationEnd();
            return value;
        }

#endif

        private static CodeMemberEvent MakeSimpleProgressEvent(string progressEventName, CodeTypeDeclaration intermediateResultsType)
        {
            CodeMemberEvent value = new CodeMemberEvent();
            value.Name = progressEventName;
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            value.Type = MakeEventArgsOrHandlerType(intermediateResultsType, false, false);
            return value;
        }

#if USE_SNIPPETS

        private static string MakeRaiseEventAccessor(string argsType, string eventName)
        {
            return string.Format(
                OutputLanguage.VB == outputLanguage
                // VB requires this accessor.
                ? "RaiseEvent(ByVal sender As Object, ByVal args As {0})\r\nDebug.Fail(\"The event {1} should not be raised directly.\")\r\nEnd RaiseEvent\r\n"
                // C# doesn't support this accessor yet.
                : string.Empty,
                argsType, eventName);
        }

        private static string MakeEventAccessor(char op, string handlerType, string trackerFieldName, string eventName, StringBuilder accessorBody)
        {
            string verb = OutputLanguage.VB == outputLanguage
                ? (op == '+' ? "AddHandler" : op == '-' ? "RemoveHandler" : op == '^' ? "RaiseEvent" : null)
                : (op == '+' ? "add" : op == '-' ? "remove" : op == '^' ? "raise" : null);

            string accessorDeclarationStart = string.Format(
                OutputLanguage.VB == outputLanguage
                ? "{0}(ByVal value As {1})\r\n"
                : "{0} {{ ",
                verb, handlerType);

            accessorBody.AppendFormat(
                OutputLanguage.VB == outputLanguage
                ? "{1} Me.{2}.{3}, value\r\n"
                : "this.{2}.{3} {0}= value; ",
                op, verb, trackerFieldName, eventName);

            string accessorDeclarationEnd = string.Format(
                OutputLanguage.VB == outputLanguage
                ? "End {0}\r\n"
                : "}} ",
                verb);

            return accessorDeclarationStart + accessorBody + accessorDeclarationEnd;
        }

        private static string MakeEventDeclarationStart(string completedEventName, string handlerType)
        {
            return string.Format(
                OutputLanguage.VB == outputLanguage
                ? "Public Custom Event {0} As {1}\r\n"
                : "public event {1} {0} {{ ",
                completedEventName, handlerType);
        }

        private static string MakeEventDeclarationEnd()
        {
            return (OutputLanguage.VB == outputLanguage ? "End Event" : "}");
        }

        private static string MakeEventArgsOrHandlerType(string baseTypeName, CodeTypeDeclaration typeArgument)
        {
            string value = baseTypeName;
            if (typeArgument.Name != typeof(void).FullName)
            {
                value += string.Format(OutputLanguage.VB == outputLanguage ? "(Of {0})" : "<{0}>", typeArgument.Name);
            }
            return value;
        }

#endif

        private static CodeTypeReference MakeEventArgsOrHandlerType(CodeTypeDeclaration typeArgument, bool makeCompletionEvent, bool makeArgsType)
        {
            CodeTypeReference value = new CodeTypeReference(string.Format("{0}.{1}Event{2}",
                typeArgument.Members.Count == 0 ? "System.ComponentModel" : "AsyncGen",
                makeCompletionEvent ? "AsyncCompleted" : "ProgressChanged",
                makeArgsType ? "Args" : "Handler"));
            if (typeArgument.Members.Count > 0)
            {
                value.TypeArguments.Add(typeArgument.Name);
            }
            return value;
        }

        private static CodeConstructor MakeConstructorSkeleton()
        {
            CodeConstructor value = new CodeConstructor();
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            return value;
        }

        private static CodeMemberMethod MakeAsyncMethod(MethodInfo originalMethod, ParameterInfo taskIDParam, ICollection<ParameterInfo> specialParams, Type callbackInterface, string delegateTypeName, string methodName, string trackerFieldName)
        {
            CodeMemberMethod value = MakeAsyncMethodSkeleton(originalMethod, methodName, callbackInterface);
            value.Statements.Add(MakeTrackerCreateOperationtMethodInvocation(originalMethod, trackerFieldName, taskIDParam));
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                if (param.IsOut)
                {
                    value.Statements.Add(new CodeVariableDeclarationStatement(param.ParameterType.FullName.TrimEnd('&'), param.Name));
                }
            }
            value.Statements.Add(MakeDelegateDeclaration(originalMethod, delegateTypeName));
            value.Statements.Add(MakeBeginInvokeInvocation(originalMethod, trackerFieldName, taskIDParam, callbackInterface));
            return value;
        }

        private static CodeMemberMethod MakeCancelMethod(MethodInfo originalMethod, string methodName, string trackerFieldName)
        {
            CodeMemberMethod value = new CodeMemberMethod();
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            value.Name = methodName;
            value.ReturnType = new CodeTypeReference(typeof(void));
            ParameterInfo taskIDParam = null;
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                object[] paramAttributes = param.GetCustomAttributes(typeof(TaskIDAttribute), false);
                if (paramAttributes.Length > 0)
                {
                    if (taskIDParam == null)
                    {
                        taskIDParam = param;
                    }
                    else
                    {
                        throw new ApplicationException("Only one parameter may have the [TaskID] attribute.");
                    }
                }
            }
            if (taskIDParam != null)
            {
                value.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "userState"));
            }
            value.Statements.Add(MakeTrackerCancelOperationMethodInvocation(trackerFieldName, taskIDParam));
            value.Statements.Add(new CodeThrowExceptionStatement(new CodeObjectCreateExpression(typeof(ArgumentException))));
            return value;
        }

        private static CodeStatement MakeTrackerCancelOperationMethodInvocation(string trackerFieldName, ParameterInfo taskIDParam)
        {
            CodeMethodInvokeExpression invokeTrackerCancelOperation = new CodeMethodInvokeExpression();
            invokeTrackerCancelOperation.Method.TargetObject = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName);
            invokeTrackerCancelOperation.Method.MethodName = "TryCancelOperation";
            if (taskIDParam != null)
            {
                invokeTrackerCancelOperation.Parameters.Add(new CodeArgumentReferenceExpression(taskIDParam.Name));
            }
            return new CodeConditionStatement(invokeTrackerCancelOperation, new CodeMethodReturnStatement());
        }

        private static CodeVariableDeclarationStatement MakeDelegateDeclaration(MethodInfo originalMethod, string delegateTypeName)
        {
            CodeVariableDeclarationStatement value = new CodeVariableDeclarationStatement();
            value.Type = new CodeTypeReference(delegateTypeName);
            value.Name = "d";
            CodeDelegateCreateExpression delegateCreation = new CodeDelegateCreateExpression();
            delegateCreation.DelegateType = new CodeTypeReference(delegateTypeName);
            delegateCreation.TargetObject = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "server");
#if !USE_SNIPPETS
            if (OutputLanguage.CPP == outputLanguage)
            {
                // The CppCodeProvider needs to be reminded of the type of the 'server' field, or it will emit bad code.
                delegateCreation.TargetObject = new CodeCastExpression(new CodeTypeReference(originalMethod.DeclaringType), delegateCreation.TargetObject);
            }
#endif
            delegateCreation.MethodName = originalMethod.Name;
            value.InitExpression = delegateCreation;
            return value;
        }

        private static CodeMethodInvokeExpression MakeBeginInvokeInvocation(MethodInfo originalMethod, string trackerFieldName, ParameterInfo taskIDParam, Type callbackInterface)
        {
            CodeMethodInvokeExpression value = new CodeMethodInvokeExpression();
            value.Method.TargetObject = new CodeVariableReferenceExpression("d");
            value.Method.MethodName = "BeginInvoke";
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                value.Parameters.Add(
                    param.ParameterType == callbackInterface
                    ? (CodeExpression)new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName)
                    : (CodeExpression)MakeInvocationArgument(param));
            }
            CodeDelegateCreateExpression trackerAsyncCallbackDelegate = new CodeDelegateCreateExpression();
            trackerAsyncCallbackDelegate.DelegateType = new CodeTypeReference(typeof(AsyncCallback));
            trackerAsyncCallbackDelegate.TargetObject = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName);
            trackerAsyncCallbackDelegate.MethodName = "PostOperationCompleted";
            value.Parameters.Add(trackerAsyncCallbackDelegate);
            value.Parameters.Add(
                taskIDParam == null
                ? (CodeExpression)new CodePrimitiveExpression(null)
                : (CodeExpression)new CodeArgumentReferenceExpression(taskIDParam.Name));
            return value;
        }

        private static CodeExpressionStatement MakeTrackerCreateOperationtMethodInvocation(MethodInfo originalMethod, string trackerFieldName, ParameterInfo taskIDParam)
        {
            CodeMethodInvokeExpression value = new CodeMethodInvokeExpression();
            value.Method = new CodeMethodReferenceExpression(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), trackerFieldName), "CreateOperation");
            if (taskIDParam != null)
            {
                value.Parameters.Add(new CodeArgumentReferenceExpression(taskIDParam.Name));
            }
            return new CodeExpressionStatement(value);
        }

        private static CodeMemberMethod MakeAsyncMethodSkeleton(MethodInfo originalMethod, string asyncMethodName, Type callbackInterface)
        {
            CodeMemberMethod value = new CodeMemberMethod();
            value.Attributes &= ~MemberAttributes.AccessMask;
            value.Attributes |= MemberAttributes.Public;
            value.Name = asyncMethodName;
            value.ReturnType = new CodeTypeReference(typeof(void));
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                CodeParameterDeclarationExpression paramDeclaration = MakeParameterDeclaration(param);
                if (!param.IsOut && param.ParameterType != callbackInterface)
                {
                    paramDeclaration.Direction = FieldDirection.In;
                    value.Parameters.Add(paramDeclaration);
                }
            }
            return value;
        }

        private static CodeTypeDelegate MakeDelegateType(MethodInfo originalMethod, string delegateTypeName)
        {
            CodeTypeDelegate value = new CodeTypeDelegate(delegateTypeName);
            value.TypeAttributes &= ~TypeAttributes.VisibilityMask;
            value.TypeAttributes |= TypeAttributes.NestedPrivate;
            value.ReturnType = new CodeTypeReference(originalMethod.ReturnType);
            foreach (ParameterInfo param in originalMethod.GetParameters())
            {
                CodeParameterDeclarationExpression paramDeclaration = new CodeParameterDeclarationExpression(param.ParameterType.FullName.TrimEnd('&'), param.Name);
                paramDeclaration.Direction = !param.ParameterType.IsByRef ? FieldDirection.In : param.IsOut ? FieldDirection.Out : FieldDirection.Ref;
                value.Parameters.Add(paramDeclaration);
            }
            return value;
        }

        private static CodeDirectionExpression MakeInvocationArgument(ParameterInfo param)
        {
            CodeDirectionExpression value = new CodeDirectionExpression();
            value.Expression = new CodeArgumentReferenceExpression(param.Name);
            value.Direction = param.IsOut ? FieldDirection.Out : param.ParameterType.IsByRef ? FieldDirection.Ref : FieldDirection.In;
            return value;
        }

        private static CodeParameterDeclarationExpression MakeParameterDeclaration(ParameterInfo param)
        {
            CodeParameterDeclarationExpression value = new CodeParameterDeclarationExpression(param.ParameterType.FullName.TrimEnd('&'), param.Name);
            value.Direction = param.IsOut ? FieldDirection.Out : param.ParameterType.IsByRef ? FieldDirection.Ref : FieldDirection.In;
            return value;
        }

        private static void AddNewMember(CodeTypeDeclaration typeDecl, CodeTypeMember newMember)
        {
            foreach (CodeTypeMember member in typeDecl.Members)
            {
                if (member.Name == newMember.Name)
                {
                    typeDecl.Members.Remove(member);
                    typeDecl.Members.Add(newMember);
                    return;
                }
            }
            typeDecl.Members.Add(newMember);
        }

        private static void GenerateCode(CodeCompileUnit compileUnit, CodeDomProvider provider, string sourceFileName)
        {
            CodeGeneratorOptions opt = new CodeGeneratorOptions();
            opt.BracingStyle = "C";
            using (TextWriter tw = new StreamWriter(sourceFileName))
            {
                provider.GenerateCodeFromCompileUnit(compileUnit, tw, opt);
            }
            provider.GenerateCodeFromCompileUnit(compileUnit, Console.Out, opt);
        }

        private static void CompileGeneratedCode(List<string> assemblies, CodeDomProvider provider, string sourceFileName)
        {
            string[] allAssemblies = new string[2 + assemblies.Count];
            allAssemblies[0] = "System.dll";
            allAssemblies[1] = "AsyncGenLib.dll";
            assemblies.CopyTo(allAssemblies, 2);
            CompilerParameters cp = new CompilerParameters(allAssemblies);
            cp.GenerateInMemory = true;
            cp.TreatWarningsAsErrors = true;
            cp.WarningLevel = 4;
            try
            {
                CompilerResults cr = provider.CompileAssemblyFromFile(cp, sourceFileName);
                foreach (CompilerError error in cr.Errors)
                {
                    Console.Error.WriteLine(error);
                }
            }
            catch (NotImplementedException ex)
            {
                Console.Error.WriteLine("Can't compile the generated code: {0}", ex.Message);
            }
        }

#if USE_SNIPPETS
        private static Dictionary<string, StringBuilder> addAccessorBodies = new Dictionary<string, StringBuilder>();
        private static Dictionary<string, StringBuilder> removeAccessorBodies = new Dictionary<string, StringBuilder>();
#endif
        private static CodeDomProvider codeDomProvider = null;
        private static OutputLanguage outputLanguage = OutputLanguage.CS;

        private enum OutputLanguage
        {
            CS = 0,
            VB,
#if !USE_SNIPPETS
            CPP,
#endif
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ron A Inbar
Software Developer (Senior) Philips Healthcare
Israel Israel
I got my B.Sc. in Mathematics and Computer Science from Tel Aviv University in 1997. Since then I have developed software in UNIX, Win32 and .NET. I currently live in Haifa.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 26 Nov 2008
Article Copyright 2008 by Ron A Inbar
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid