File: net\PeerToPeer\Collaboration\Peer.cs
Project: ndp\fx\src\SystemNet\System.Net.csproj (System.Net)
//------------------------------------------------------------------------------
// <copyright file="Peer.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Net.PeerToPeer.Collaboration
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Net.Sockets;
    using System.Net.Mail;
    using System.ComponentModel;
    using System.Threading;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.IO;
 
    /// <summary>
    /// This is the object changed event args class we give back when 
    /// we have have an object changed event fired by native
    /// </summary>
    public class ObjectChangedEventArgs : EventArgs
    {
        private PeerEndPoint m_peerEndPoint;
        private PeerContact m_peerContact;
        private PeerChangeType m_peerChangeType;
        private PeerObject m_peerObject;
 
        internal ObjectChangedEventArgs(PeerEndPoint peerEndPoint, PeerContact peerContact,
                                        PeerChangeType peerChangeType, PeerObject peerObject)
        {
            m_peerEndPoint = peerEndPoint;
            m_peerContact = peerContact;
            m_peerChangeType = peerChangeType;
            m_peerObject = peerObject;
        }
 
        public PeerEndPoint PeerEndPoint
        {
            get
            {
                return m_peerEndPoint;
            }
        }
 
        public PeerContact PeerContact
        {
            get
            {
                return m_peerContact;
            }
        }
 
        public PeerChangeType PeerChangeType
        {
            get
            {
                return m_peerChangeType;
            }
        }
 
        public PeerObject PeerObject
        {
            get
            {
                return m_peerObject;
            }
        }
    }
 
    /// <summary>
    /// This is the presence changed event args class we give back when 
    /// we have have presence changed event fired by native
    /// </summary>
    public class PresenceChangedEventArgs : EventArgs
    {
        private PeerEndPoint m_peerEndPoint;
        private PeerContact m_peerContact;
        private PeerChangeType m_peerChangeType;
        private PeerPresenceInfo m_peerPresenceInfo;
 
        internal PresenceChangedEventArgs(PeerEndPoint peerEndPoint, PeerContact peerContact,
                                        PeerChangeType peerChangeType, PeerPresenceInfo peerPresenceInfo)
        {
            m_peerEndPoint = peerEndPoint;
            m_peerContact = peerContact;
            m_peerChangeType = peerChangeType;
            m_peerPresenceInfo = peerPresenceInfo;
        }
 
        public PeerEndPoint PeerEndPoint
        {
            get
            {
                return m_peerEndPoint;
            }
        }
 
        public PeerContact PeerContact
        {
            get
            {
                return m_peerContact;
            }
        }
 
        public PeerChangeType PeerChangeType
        {
            get
            {
                return m_peerChangeType;
            }
        }
 
        public PeerPresenceInfo PeerPresenceInfo
        {
            get
            {
                return m_peerPresenceInfo;
            }
        }
    }
 
    /// <summary>
    /// This is the event args class we give back when 
    /// we have completed the subscribeasync call
    /// </summary>
    public class SubscribeCompletedEventArgs : AsyncCompletedEventArgs
    {
        private PeerNearMe m_peerNearMe;
        private PeerContact m_peerContact;
 
        internal SubscribeCompletedEventArgs(PeerNearMe peerNearMe,
                                        PeerContact peerContact,
                                        Exception error,
                                        bool cancelled,
                                        object userToken)
            : base(error, cancelled, userToken)
        {
            m_peerNearMe = peerNearMe;
            m_peerContact = peerContact;
        }
 
        public PeerNearMe PeerNearMe
        {
            get
            {
                return m_peerNearMe;
            }
        }
 
        public PeerContact PeerContact
        {
            get
            {
                return m_peerContact;
            }
        }
    }
 
    /// <summary>
    /// This is the event args class we give back when 
    /// we have completed the refreshendpoint async call
    /// </summary>
    public class RefreshDataCompletedEventArgs : AsyncCompletedEventArgs
    {
        private PeerEndPoint m_peerEndPoint;
        internal RefreshDataCompletedEventArgs(PeerEndPoint peerEndPoint,
                                                        Exception error,
                                                        bool cancelled,
                                                        object userToken)
            : base(error, cancelled, userToken)
        {
            m_peerEndPoint = peerEndPoint;
        }
 
        public PeerEndPoint PeerEndPoint
        {
            get
            {
                return m_peerEndPoint;
            }
        }
    }
 
    /// <summary>
    /// This is the event args class we give back when 
    /// we have completed the inviteasync call
    /// </summary>
    public class InviteCompletedEventArgs : AsyncCompletedEventArgs
    {
        private PeerInvitationResponse m_peerInvResponse;
        internal InviteCompletedEventArgs(PeerInvitationResponse peerInvResponse,
                                                        Exception error,
                                                        bool cancelled,
                                                        object userToken)
            : base(error, cancelled, userToken)
        {
            m_peerInvResponse = peerInvResponse;
        }
 
        public PeerInvitationResponse InviteResponse
        {
            get
            {
                return m_peerInvResponse;
            }
        }
    }
 
    /// <summary>
    /// Has the common interface for PeerNearMe and PeerContact which derive from it
    /// </summary>
    [Serializable]
    public abstract class Peer : IDisposable, IEquatable<Peer>, ISerializable
    {
        private PeerEndPointCollection m_peerEndPoints = new PeerEndPointCollection();
        private ISynchronizeInvoke m_synchronizingObject;
 
        public virtual PeerEndPointCollection PeerEndPoints
        {
            get {
                if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
 
                return m_peerEndPoints; 
            }
        }
 
        public bool IsOnline
        {
            get{
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Get Isonline called.");
                if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
 
                bool isOnline = false;
                PeerPresenceInfo presenceInfo;
 
                foreach (PeerEndPoint peerEndPoint in PeerEndPoints){
                    presenceInfo = null;
 
                    try{
                        presenceInfo = GetPresenceInfo(peerEndPoint);
                    }
                    catch (Exception e){
                        Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "Exception thrown {0}", e.Message);
                    }
                    if ((presenceInfo != null) && (presenceInfo.PresenceStatus == PeerPresenceStatus.Online)){
                        isOnline = true;
                        break;
                    }
                }
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving Isonline called with {0}.", isOnline);
                return isOnline;
            }
        }
 
        /// <summary>
        /// Gets and set the object used to marshall event handlers calls for stand alone 
        /// events
        /// </summary>
        [Browsable(false), DefaultValue(null), Description(SR.SynchronizingObject)]
        public ISynchronizeInvoke SynchronizingObject
        {
            get
            {
                if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
                return m_synchronizingObject;
            }
            set
            {
                if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
                m_synchronizingObject = value;
            }
        }
 
        static internal Guid CurrentApplicationGuid
        {
            // <SecurityKernel Critical="True" Ring="0">
            // <SatisfiesLinkDemand Name="Process.GetCurrentProcess():System.Diagnostics.Process" />
            // <SatisfiesLinkDemand Name="Process.get_ProcessName():System.String" />
            // </SecurityKernel>
            [System.Security.SecurityCritical]
            get{
                Guid guid = Guid.Empty;
 
                //
                // Get path and args of app
                //
                string path = Path.Combine( Environment.CurrentDirectory, Process.GetCurrentProcess().ProcessName + ".exe");
                string arguments = null;
                string[] argsArray = Environment.GetCommandLineArgs();
                int length = argsArray.Length;
                if (length > 1){
                    StringBuilder argsBuilder = new StringBuilder();
                    for (int i = 1; i < length; ++i){
                        argsBuilder.Append(argsArray[i]);
                        if (i != (length - 1)) argsBuilder.Append(' ');
                    }
 
                    arguments = argsBuilder.ToString();
                }
 
                //
                // Find a matching registered application and return its guid
                //
                PeerApplicationCollection peerApplications = PeerCollaboration.GetLocalRegisteredApplications();
 
                foreach (PeerApplication peerApplication in peerApplications){
                    if ((peerApplication.CommandLineArgs == arguments) &&
                        (peerApplication.Path == path))
                        return peerApplication.Id;
                }
                return guid;
            }
        }
 
        internal Peer(){
            OnInviteCompletedDelegate = new SendOrPostCallback(InviteCompletedWaitCallback);
        }
 
        /// <summary>
        /// Constructor to enable serialization 
        /// </summary>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        protected Peer(SerializationInfo serializationInfo, StreamingContext streamingContext):this()
        {
            m_peerEndPoints = (PeerEndPointCollection)serializationInfo.GetValue("_PeerEndPoints", typeof(PeerEndPointCollection));
        }
 
        //
        // Gets the presence info from collab for a specific endpoint
        //
        // <SecurityKernel Critical="True" Ring="0">
        // <SatisfiesLinkDemand Name="GCHandle.Alloc(System.Object,System.Runtime.InteropServices.GCHandleType):System.Runtime.InteropServices.GCHandle" />
        // <SatisfiesLinkDemand Name="GCHandle.AddrOfPinnedObject():System.IntPtr" />
        // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
        // <SatisfiesLinkDemand Name="Marshal.PtrToStructure(System.IntPtr,System.Type):System.Object" />
        // <SatisfiesLinkDemand Name="GCHandle.Free():System.Void" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabGetPresenceInfo(System.IntPtr,System.Net.PeerToPeer.Collaboration.SafeCollabData&):System.Int32" />
        // <ReferencesCritical Name="Local presenceInfo of type: SafeCollabData" Ring="1" />
        // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public PeerPresenceInfo GetPresenceInfo(PeerEndPoint peerEndPoint)
        {
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "GetPresenceInfo()called.");
            if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
 
            if (peerEndPoint == null)
                throw new ArgumentNullException("peerEndPoint");
 
            if (peerEndPoint.EndPoint == null)
                throw new ArgumentException(SR.GetString(SR.Collab_NoEndPointInPeerEndPoint));
 
            if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Getting presence for the following endpoint.");
                peerEndPoint.TracePeerEndPoint();
            }
 
            SafeCollabData presenceInfo = null;
            PeerPresenceInfo peerPresenceInfo = null;
            int errorCode;
 
            PEER_ENDPOINT pep = new PEER_ENDPOINT();
            pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint);
 
            //
            // Pin all the data to pass to native
            //
            GCHandle pepName = new GCHandle();
 
            if (peerEndPoint.Name != null){
                pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned);
                pep.pwzEndpointName = pepName.AddrOfPinnedObject();
            }
            
            GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned);
            IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject();
 
            //
            // Refresh data for getting presence info
            //
            RefreshIfNeeded();
 
            try{
                errorCode = UnsafeCollabNativeMethods.PeerCollabGetPresenceInfo(ptrPeerEP, out presenceInfo);
                if (errorCode != 0){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabGetPresenceInfo returned with errorcode {0}", errorCode);
                    throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetPresenceFailed), errorCode);
                }
 
                IntPtr ptrPeerPresenceInfo = presenceInfo.DangerousGetHandle();
                PEER_PRESENCE_INFO ppi = (PEER_PRESENCE_INFO)Marshal.PtrToStructure(ptrPeerPresenceInfo, typeof(PEER_PRESENCE_INFO));
                peerPresenceInfo = new PeerPresenceInfo();
                peerPresenceInfo.PresenceStatus = ppi.status;
                peerPresenceInfo.DescriptiveText = ppi.descText;
            }
            finally{
                if (pepName.IsAllocated) pepName.Free();
                if (peerEP.IsAllocated) peerEP.Free();
                if (presenceInfo != null) presenceInfo.Dispose();
            }
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving GetPresenceInfo().");
 
            return peerPresenceInfo;
        }
 
        //
        // Gets all the objects for all the endpoints
        //
        // <SecurityKernel Critical="True" Ring="2">
        // <ReferencesCritical Name="Method: InternalGetAllObjects(Guid, Boolean):PeerObjectCollection" Ring="2" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public PeerObjectCollection GetObjects()
        {
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
            
            return InternalGetAllObjects(Guid.Empty, false);
        }
 
        //
        // Gets specific object for all the endpoints
        //
        // <SecurityKernel Critical="True" Ring="2">
        // <ReferencesCritical Name="Method: InternalGetAllObjects(Guid, Boolean):PeerObjectCollection" Ring="2" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public PeerObjectCollection GetObjects(Guid objectId)
        {
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
            
            return InternalGetAllObjects(objectId, true);
        }
 
        internal abstract void RefreshIfNeeded();
 
        // <SecurityKernel Critical="True" Ring="1">
        // <ReferencesCritical Name="Method: InternalGetObjects(Guid, Boolean, PeerEndPoint):PeerObjectCollection" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        private PeerObjectCollection InternalGetAllObjects(Guid objectId, bool guidSupplied)
        {
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalGetAllObjects() with ObjectId {0}.", objectId);
 
            Dictionary<Guid, PeerObject> mergedObjects = new Dictionary<Guid, PeerObject>();
            PeerObjectCollection peerObjectCollection;
 
            //
            // Refresh the data at the endpoint before calling get objs
            //
            RefreshIfNeeded();
 
            foreach (PeerEndPoint peerEndPoint in PeerEndPoints)
            {
                peerObjectCollection = InternalGetObjects(objectId, guidSupplied, peerEndPoint);
 
                //
                // Special case. If we have already found an endpoint with the user guid then
                // we just return it
                //
                if (guidSupplied && peerObjectCollection.Count != 0)
                    return peerObjectCollection;
 
                foreach (PeerObject peerObject in peerObjectCollection)
                {
                    mergedObjects[peerObject.Id] = peerObject;
                }
            }
 
            //
            // Return the object collection from the dictionary
            //
 
            Dictionary<Guid, PeerObject>.ValueCollection objects = mergedObjects.Values;
            peerObjectCollection = new PeerObjectCollection();
            foreach (PeerObject peerObject in objects)
            {
                peerObjectCollection.Add(peerObject);
            }
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving InternalGetAllObjects(). " +
            "Returning collection with {0} objects.", peerObjectCollection.Count);
 
            return peerObjectCollection;
        }
 
        //
        // Gets specific objects for an endpoint
        //
        // <SecurityKernel Critical="True" Ring="0">
        // <UsesUnsafeCode Name="Local pObjects of type: IntPtr*" />
        // <UsesUnsafeCode Name="Local pPeerObject of type: PEER_OBJECT*" />
        // <UsesUnsafeCode Name="Method: IntPtr.op_Explicit(System.IntPtr):System.Void*" />
        // <SatisfiesLinkDemand Name="GCHandle.Alloc(System.Object,System.Runtime.InteropServices.GCHandleType):System.Runtime.InteropServices.GCHandle" />
        // <SatisfiesLinkDemand Name="GCHandle.AddrOfPinnedObject():System.IntPtr" />
        // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
        // <SatisfiesLinkDemand Name="Marshal.Copy(System.IntPtr,System.Byte[],System.Int32,System.Int32):System.Void" />
        // <SatisfiesLinkDemand Name="GCHandle.Free():System.Void" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabEnumObjects(System.IntPtr,System.IntPtr,System.Net.PeerToPeer.Collaboration.SafeCollabEnum&):System.Int32" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerGetItemCount(System.Net.PeerToPeer.Collaboration.SafeCollabEnum,System.UInt32&):System.Int32" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerGetNextItem(System.Net.PeerToPeer.Collaboration.SafeCollabEnum,System.UInt32&,System.Net.PeerToPeer.Collaboration.SafeCollabData&):System.Int32" />
        // <ReferencesCritical Name="Local handlePeerEnum of type: SafeCollabEnum" Ring="1" />
        // <ReferencesCritical Name="Local objectArray of type: SafeCollabData" Ring="1" />
        // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        internal static PeerObjectCollection InternalGetObjects(Guid objectId, bool guidSupplied, PeerEndPoint peerEndPoint)
        {
            if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalGetObjects() with the following PeerEndPoint");
                peerEndPoint.TracePeerEndPoint();
            }
            
            PeerObjectCollection peerObjectColl = new PeerObjectCollection();
            SafeCollabEnum handlePeerEnum = null;
            UInt32 objectCount = 0;
            int errorCode = 0;
 
 
            GCHandle guidHandle = new GCHandle();
            IntPtr guidPtr = IntPtr.Zero;
 
            if (guidSupplied){
                GUID guid = CollaborationHelperFunctions.ConvertGuidToGUID(objectId);
                guidHandle = GCHandle.Alloc(guid, GCHandleType.Pinned);
                guidPtr = guidHandle.AddrOfPinnedObject();
            }
 
            PEER_ENDPOINT pep = new PEER_ENDPOINT();
            pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint);
 
            //
            // Pin data to pass to native
            //
 
            GCHandle pepName = new GCHandle();
 
            if (peerEndPoint.Name != null){
                pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned);
                pep.pwzEndpointName = pepName.AddrOfPinnedObject();
            }
            GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned);
            IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject();
 
            try{
                //
                // Enumerate through the objects for the endpoint
                //
 
                errorCode = UnsafeCollabNativeMethods.PeerCollabEnumObjects(ptrPeerEP, guidPtr, out handlePeerEnum);
 
                if (errorCode != 0){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabEnumObjects returned with errorcode {0}", errorCode);
                    throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetObjectsFailed), errorCode);
                }
 
                errorCode = UnsafeCollabNativeMethods.PeerGetItemCount(handlePeerEnum, ref objectCount);
                if (errorCode != 0){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerGetItemCount returned with errorcode {0}", errorCode);
                    throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetObjectsFailed), errorCode);
                }
 
                if (objectCount == 0){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "No PeerObjects found.");
                    return peerObjectColl;
                }
                unsafe
                {
                    SafeCollabData objectArray;
                    errorCode = UnsafeCollabNativeMethods.PeerGetNextItem(handlePeerEnum, ref objectCount, out objectArray);
                    if (errorCode != 0){
                        Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerGetNextItem returned with errorcode {0}", errorCode);
                        throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_GetObjectsFailed), errorCode);
                    }
 
                    IntPtr pPEER_OBJECT = objectArray.DangerousGetHandle();
                    IntPtr* pObjects = (IntPtr*)pPEER_OBJECT;
 
                    //
                    // Loop through the applications array from native
                    //
                    for (ulong i = 0; i < objectCount; i++){
                        PEER_OBJECT* pPeerObject = (PEER_OBJECT*)pObjects[i];
                        byte[] data = null;
 
                        if (pPeerObject->data.cbData != 0){
                            data = new byte[pPeerObject->data.cbData];
                            Marshal.Copy(pPeerObject->data.pbData, data, 0, (int)pPeerObject->data.cbData);
                        }
 
                        PeerObject peerObject = new PeerObject(CollaborationHelperFunctions.ConvertGUIDToGuid(pPeerObject->guid), data, (PeerScope)pPeerObject->dwPublicationScope);
                        
                        if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){
                            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Retrieved following Object");
                            peerObject.TracePeerObject();
                        }
                        
                        peerObjectColl.Add(peerObject);
                    }
                }
            }
            finally{
                if (guidHandle.IsAllocated) guidHandle.Free();
                if (pepName.IsAllocated) pepName.Free();
                if (peerEP.IsAllocated) peerEP.Free();
                if (handlePeerEnum != null) handlePeerEnum.Dispose();
            }
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Leaving InternalGetApplications(). " +
            "Returning collection with {0} objects.", peerObjectColl.Count);
            
            return peerObjectColl;
        }
 
        public abstract PeerInvitationResponse Invite();
 
        public abstract PeerInvitationResponse Invite(PeerApplication applicationToInvite, string message, byte[] invitationData);
 
        //
        // Invites an endpoint with passed data. Includes a contact if it was passed in.
        //
        // <SecurityKernel Critical="True" Ring="0">
        // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
        // <SatisfiesLinkDemand Name="Marshal.Copy(System.Byte[],System.Int32,System.IntPtr,System.Int32):System.Void" />
        // <SatisfiesLinkDemand Name="GCHandle.Alloc(System.Object,System.Runtime.InteropServices.GCHandleType):System.Runtime.InteropServices.GCHandle" />
        // <SatisfiesLinkDemand Name="GCHandle.AddrOfPinnedObject():System.IntPtr" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <SatisfiesLinkDemand Name="SafeHandle.get_IsInvalid():System.Boolean" />
        // <SatisfiesLinkDemand Name="Marshal.PtrToStructure(System.IntPtr,System.Type):System.Object" />
        // <SatisfiesLinkDemand Name="GCHandle.Free():System.Void" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabInviteContact(System.Net.PeerToPeer.Collaboration.PEER_CONTACT&,System.IntPtr,System.Net.PeerToPeer.Collaboration.PEER_INVITATION&,System.Net.PeerToPeer.Collaboration.SafeCollabData&):System.Int32" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabInviteEndpoint(System.IntPtr,System.Net.PeerToPeer.Collaboration.PEER_INVITATION&,System.Net.PeerToPeer.Collaboration.SafeCollabData&):System.Int32" />
        // <ReferencesCritical Name="Local data of type: SafeCollabMemory" Ring="1" />
        // <ReferencesCritical Name="Local safeResponse of type: SafeCollabData" Ring="1" />
        // <ReferencesCritical Name="Local safeCredentials of type: SafeCollabMemory" Ring="1" />
        // <ReferencesCritical Name="Method: SafeCollabMemory..ctor(System.Int32)" Ring="1" />
        // <ReferencesCritical Name="Method: CollaborationHelperFunctions.ConvertPeerContactToPEER_CONTACT(System.Net.PeerToPeer.Collaboration.PeerContact,System.Net.PeerToPeer.Collaboration.SafeCollabMemory&):System.Net.PeerToPeer.Collaboration.PEER_CONTACT" Ring="1" />
        // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        internal static PeerInvitationResponse InternalInviteEndPoint(Guid applicationToInviteGuid,
                                                                string message, byte[] invitationData,
                                                                PeerEndPoint peerEndPoint, PeerContact peerContact)
        {
 
            if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalInviteEndPoint() with the following information.");
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Invitation Message: ", message);
                if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Verbose) && (invitationData != null)){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "\tInvitation data:");
                    Logging.DumpData(Logging.P2PTraceSource, TraceEventType.Verbose, Logging.P2PTraceSource.MaxDataSize, invitationData, 0, invitationData.Length);
                }
                else
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Invitation Data length: ", (invitationData != null ? invitationData.Length : 0));
 
                if (peerEndPoint != null) peerEndPoint.TracePeerEndPoint();
            }
 
            PEER_INVITATION pi = new PEER_INVITATION();
            pi.applicationId = CollaborationHelperFunctions.ConvertGuidToGUID(applicationToInviteGuid);
            pi.pwzMessage = message;
 
            SafeCollabMemory data = null;
            pi.applicationData.cbData = (invitationData != null) ? (UInt32)invitationData.Length : 0;
 
            //
            // Marshal Invitation Data
            //
            if ((invitationData != null) && (invitationData.Length > 0))
            {
                data = new SafeCollabMemory(invitationData.Length);
                pi.applicationData.pbData = data.DangerousGetHandle();
                Marshal.Copy(invitationData, 0, pi.applicationData.pbData, invitationData.Length);
            }
            else
                pi.applicationData.pbData = IntPtr.Zero;
 
 
            PEER_ENDPOINT pep = new PEER_ENDPOINT();
            pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint);
 
            //
            // Pin data to pass to native
            //
            GCHandle pepName = new GCHandle();
 
            if (peerEndPoint.Name != null){
                pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned);
                pep.pwzEndpointName = pepName.AddrOfPinnedObject();
            }
            GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned);
            IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject();
 
            SafeCollabData safeResponse = null;
            PeerInvitationResponse peerInvResponse = null; 
            int errorCode;
 
            try{
                //
                // Make native call with endpoint with/without contact
                //
                if (peerContact != null){
 
                    //
                    // Generate native contact
                    //
                    SafeCollabMemory safeCredentials = null;
                    PEER_CONTACT pc = CollaborationHelperFunctions.ConvertPeerContactToPEER_CONTACT(peerContact, ref safeCredentials);
 
                    try{
                        errorCode = UnsafeCollabNativeMethods.PeerCollabInviteContact(ref pc,
                                                                                        ptrPeerEP,
                                                                                        ref pi,
                                                                                        out safeResponse);
                    }
                    finally{
                        if (safeCredentials != null) safeCredentials.Dispose();
                    }
                }
                else
                    errorCode = UnsafeCollabNativeMethods.PeerCollabInviteEndpoint(ptrPeerEP, ref pi, out safeResponse);
 
                if (errorCode != 0){
                    if ((errorCode == UnsafeCollabReturnCodes.PEER_E_TIMEOUT) || (errorCode == UnsafeCollabReturnCodes.ERROR_TIMEOUT)){
                        Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0,
                            "Timed out. Leaving InternalInviteEndPoint() with InvitationResponseType expired.");
                        
                        return new PeerInvitationResponse(PeerInvitationResponseType.Expired);
                    }
 
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, ((peerContact != null) ? "PeerCollabInviteContact" : "PeerCollabInviteEndpoint")
                        + " returned with errorcode {0}", errorCode);
                    throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_InviteFailed), errorCode);
                }
 
                if (!safeResponse.IsInvalid){
                    PEER_INVITATION_RESPONSE pir = (PEER_INVITATION_RESPONSE)Marshal.PtrToStructure(safeResponse.DangerousGetHandle(),
                                                                                                    typeof(PEER_INVITATION_RESPONSE));
                    peerInvResponse = new PeerInvitationResponse(pir.action);
                }
            }
            finally{
                if (safeResponse != null) safeResponse.Dispose();
                if (pepName.IsAllocated) pepName.Free();
                if (peerEP.IsAllocated) peerEP.Free();
            }
 
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0,
                "Leaving InternalInviteEndPoint() with InvitationResponse {0}.", peerInvResponse);
 
            return peerInvResponse;
        }
 
 
        private event EventHandler<InviteCompletedEventArgs> m_inviteCompleted;
        public event EventHandler<InviteCompletedEventArgs> InviteCompleted
        {
            add{
                if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
                PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
 
                m_inviteCompleted += value;
            }
            remove{
                if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
                PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();
 
                m_inviteCompleted -= value;
            }
        }
 
        public abstract void InviteAsync(Object userToken);
 
        public abstract void InviteAsync(   PeerApplication applicationToInvite, string message,
                                            byte[] invitationData, Object userToken);
 
 
        #region Invite Async variables
        SendOrPostCallback OnInviteCompletedDelegate;
 
        internal Dictionary<object, InviteAsyncHelper> m_inviteAsyncHelperList = new Dictionary<object, InviteAsyncHelper>();
        #endregion
 
        // <SecurityKernel Critical="True" Ring="1">
        // <ReferencesCritical Name="Method: InviteAsyncHelper.InviteAsync():System.Void" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        internal void InternalInviteAsync(Guid applicationToInviteGuid,
                                        string message, byte[] invitationData,
                                        PeerEndPointCollection peerEndPoints, PeerContact peerContact,
                                        Object userToken)
        {
            InviteAsyncHelper inviteAsyncHelper = null;
 
            // 
            //The userToken can't be duplicate of what is in the 
            //current list. These are the requriments for the new Async model 
            //that supports multiple outstanding async calls
            // 
            int newTraceEventId = NewTraceEventId;
 
            lock (m_inviteAsyncHelperList){
                if (m_inviteAsyncHelperList.ContainsKey(userToken)){
                    throw new ArgumentException(SR.GetString(SR.DuplicateUserToken));
                }
 
                inviteAsyncHelper = new InviteAsyncHelper(  peerContact, this, peerEndPoints, 
                                                            applicationToInviteGuid, message,
                                                            invitationData, userToken, newTraceEventId);
                m_inviteAsyncHelperList[userToken] = inviteAsyncHelper;
            }
 
            try{
                //
                //Start resolution on that resolver
                //
                inviteAsyncHelper.InviteAsync();
            }
            catch{
                //
                //If an exception happens clear the userState from the 
                //list so that that token can be reused
                //
                lock (m_inviteAsyncHelperList){
                    m_inviteAsyncHelperList.Remove(userToken);
                }
                throw;
            }
 
        }
        
        protected virtual void OnInviteCompleted(InviteCompletedEventArgs e)
        {
            EventHandler<InviteCompletedEventArgs> handlerCopy = m_inviteCompleted;
 
            if (handlerCopy != null){
                handlerCopy(this, e);
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Fired the invite completed event callback.");
            }
        }
 
        void InviteCompletedWaitCallback(object operationState)
        {
            OnInviteCompleted((InviteCompletedEventArgs)operationState);
        }
 
        internal void PrepareToRaiseInviteCompletedEvent(AsyncOperation asyncOP, InviteCompletedEventArgs args)
        {
            lock (m_inviteAsyncHelperList){
                InviteAsyncHelper helper = m_inviteAsyncHelperList[args.UserState];
                if (helper == null){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, 0, "userState for which we are about to call Completed event does not exist in the pending async list");
                }else{
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, helper.TraceEventId,
                         "userState {0} is being removed from the pending async list", args.UserState.GetHashCode());
                    m_inviteAsyncHelperList.Remove(args.UserState);
                }
            }
            asyncOP.PostOperationCompleted(OnInviteCompletedDelegate, args);
        }
        
        // <SecurityKernel Critical="True" Ring="2">
        // <ReferencesCritical Name="Method: InviteAsyncHelper.CancelAsync(System.Object):System.Void" Ring="2" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public void InviteAsyncCancel(Object userToken)
        {
            if (userToken == null)
                throw new ArgumentNullException("userToken");
 
            InviteAsyncHelper helper;
            lock (m_inviteAsyncHelperList){
                if (!m_inviteAsyncHelperList.TryGetValue(userToken, out helper)){
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Warning, 0, "InviteAsyncCancel called with a userState token that is not in the pending async list - returning");
                    return;
                }
            }
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, helper.TraceEventId,
                    "Proceeding to cancel the pending async");
            helper.CancelAsync(userToken);
        }
 
        //
        // Used to track inviteasynchelpers
        //
        private static int s_TraceEventId = 1;
        internal static int NewTraceEventId
        {
            get{
                Interlocked.CompareExchange(ref s_TraceEventId, 0, int.MaxValue);
                Interlocked.Increment(ref s_TraceEventId);
                return s_TraceEventId;
            }
        }        
 
        public bool Equals(Peer other)
        {
            if (other != null){
                if (other.PeerEndPoints != null){
                    return other.PeerEndPoints.Equals(PeerEndPoints);
                }
            }
            return false;
        }
 
        public override string ToString()
        {
            return PeerEndPoints.ToString();
        }
 
        private bool m_Disposed;
 
        public void Dispose()
        {
 
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        // <SecurityKernel Critical="True" Ring="0">
        // <SatisfiesLinkDemand Name="GetObjectData(SerializationInfo, StreamingContext):Void" />
        // </SecurityKernel>
        [SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.Net.dll is still using pre-v4 security model and needs this demand")]
        [System.Security.SecurityCritical]
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter, SerializationFormatter = true)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            GetObjectData(info, context);
        }
 
        /// <summary>
        /// This is made virtual so that derived types can be implemented correctly
        /// </summary>
        /// <param name="serializationInfo"></param>
        /// <param name="streamingContext"></param>
        [SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("_PeerEndPoints", PeerEndPoints);
        }
 
        protected virtual void Dispose(bool disposing)
        {
            if (!m_Disposed){
                m_Disposed = true;
            }
        }
    }
 
    /// <summary>
    /// Helps in all the async invites sent from Peer, PeerContact and PeerNearMe
    /// </summary>
    internal class InviteAsyncHelper : IDisposable
    {
        internal object m_userState;
        internal SafeCollabInvite m_SafeCollabInvite;
        internal AutoResetEvent m_InviteEvent = new AutoResetEvent(false);
 
        //
        //The WaitHandle that hooks up a callback to the 
        //event
        //
        internal RegisteredWaitHandle m_RegisteredWaitHandle;
 
        //
        //Disposed or not
        //
        internal bool m_Disposed;
 
        internal bool m_Cancelled;
 
        //
        //Async operation to ensure synchornization
        //context
        //
        AsyncOperation m_AsyncOp;
 
        //
        //A link to the resolver to avoid 
        //circular dependencies and enable GC 
        //
        WeakReference m_peerWeakReference;
 
        //
        //Lock to make sure things don't mess up stuff
        //
        object m_Lock = new Object();
 
        //
        //EventID or Just a tracking id
        //
        int m_TraceEventId;
 
        //
        // Store the latest exception
        //
        Exception m_latestException;
 
        //
        // Stores reponses from all endpoints
        //
        Collection<PeerInvitationResponseType> m_responses = new Collection<PeerInvitationResponseType>();
 
        //
        // Callback called
        //
        bool m_Completed;
 
        //
        // Used to ensure only on thread calls the callback
        //
        bool m_aboutToFireCallback;
        object m_aboutToFireCallbackLock = new object();
 
        //
        // Number of reponses received
        //
        int m_numberOfResponses;
 
        PeerContact m_peerContact;
        PeerEndPointCollection m_peerEndPoints;
        Guid m_applicationId;
        string m_message;
        byte[] m_inviteData;
 
        internal InviteAsyncHelper( PeerContact peerContact, Peer parentPeer, PeerEndPointCollection peerEndPoints,
                                    Guid applicationId, string message, byte[] inviteData,
                                    object userState, int NewTraceEventId)
        {
            m_userState = userState;
            m_peerContact = peerContact;
            m_applicationId = applicationId;
            m_message = message;
            m_inviteData = inviteData;
            m_peerEndPoints = peerEndPoints;
            m_TraceEventId = NewTraceEventId;
            m_peerWeakReference = new WeakReference(parentPeer);
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "New InviteAsyncHelper created with TraceEventID {0}", m_TraceEventId);
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
                "\tPeerContact: {0}, App Guid: {1}, userState {2}, ParentReference {3}",
                (m_peerContact != null ? m_peerContact.ToString() : "null"),
                applicationId.ToString(),
                userState.GetHashCode(),
                m_peerWeakReference.Target.GetHashCode()
                );
 
        }
 
        // <SecurityKernel Critical="True" Ring="0">
        // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
        // <SatisfiesLinkDemand Name="Marshal.Copy(System.Byte[],System.Int32,System.IntPtr,System.Int32):System.Void" />
        // <SatisfiesLinkDemand Name="SafeHandle.get_IsInvalid():System.Boolean" />
        // <SatisfiesLinkDemand Name="SafeHandle.get_IsClosed():System.Boolean" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <ReferencesCritical Name="Local data of type: SafeCollabMemory" Ring="1" />
        // <ReferencesCritical Name="Method: InviteCallback(Object, Boolean):Void" Ring="1" />
        // <ReferencesCritical Name="Method: SafeCollabMemory..ctor(System.Int32)" Ring="1" />
        // <ReferencesCritical Name="Method: InviteAsyncEndPoint(PeerEndPoint, PEER_INVITATION):Void" Ring="1" />
        // <ReferencesCritical Name="Field: m_SafeCollabInvite" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        internal void InviteAsync()
        {
            if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName);
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
            "InviteAsync called");
            //
            //First wire up a callback
            //
            
            m_RegisteredWaitHandle = ThreadPool.RegisterWaitForSingleObject(m_InviteEvent, //Event that triggers the callback
                                                    new WaitOrTimerCallback(InviteCallback), //callback to be called 
                                                    null, //state to be passed
                                                    -1,   //Timeout - aplicable only for timers not for events 
                                                    false //call us everytime the event is set not just one time
                                                    );
 
            //
            //Now call the native API to start the resolution 
            //process save the handle for later
            //
            
            PEER_INVITATION pi = new PEER_INVITATION();
            pi.applicationId = CollaborationHelperFunctions.ConvertGuidToGUID(m_applicationId);
            pi.pwzMessage = m_message;
 
            SafeCollabMemory data = null;
            pi.applicationData.cbData = (m_inviteData != null) ? (UInt32)m_inviteData.Length : 0;
 
            if ((m_inviteData != null) && (m_inviteData.Length > 0)){
                data = new SafeCollabMemory(m_inviteData.Length);
                pi.applicationData.pbData = data.DangerousGetHandle();
                Marshal.Copy(m_inviteData, 0, pi.applicationData.pbData, m_inviteData.Length);
            }
            else
                pi.applicationData.pbData = IntPtr.Zero;
 
            foreach (PeerEndPoint peerEndPoint in m_peerEndPoints)
            {
                try{
                    InviteAsyncEndPoint(peerEndPoint, pi);
                }
                catch (PeerToPeerException){
                    if (!m_SafeCollabInvite.IsInvalid && !m_SafeCollabInvite.IsClosed){
                        m_SafeCollabInvite.Dispose();
                    }
                    m_RegisteredWaitHandle.Unregister(null);
                    m_RegisteredWaitHandle = null;
                    throw;
                }
            }
 
            //
            //Create an async operation with the given 
            //user state
            //
            m_AsyncOp = AsyncOperationManager.CreateOperation(m_userState);
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId,
            "Leaving InviteAsync.");
        }
 
        // <SecurityKernel Critical="True" Ring="0">
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabAsyncInviteContact(System.Net.PeerToPeer.Collaboration.PEER_CONTACT&,System.IntPtr,System.Net.PeerToPeer.Collaboration.PEER_INVITATION&,Microsoft.Win32.SafeHandles.SafeWaitHandle,System.Net.PeerToPeer.Collaboration.SafeCollabInvite&):System.Int32" />
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabAsyncInviteEndpoint(System.IntPtr,System.Net.PeerToPeer.Collaboration.PEER_INVITATION&,Microsoft.Win32.SafeHandles.SafeWaitHandle,System.Net.PeerToPeer.Collaboration.SafeCollabInvite&):System.Int32" />
        // <SatisfiesLinkDemand Name="GCHandle.Alloc(System.Object,System.Runtime.InteropServices.GCHandleType):System.Runtime.InteropServices.GCHandle" />
        // <SatisfiesLinkDemand Name="GCHandle.AddrOfPinnedObject():System.IntPtr" />
        // <SatisfiesLinkDemand Name="WaitHandle.get_SafeWaitHandle():Microsoft.Win32.SafeHandles.SafeWaitHandle" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <SatisfiesLinkDemand Name="GCHandle.Free():System.Void" />
        // <ReferencesCritical Name="Local safeCredentials of type: SafeCollabMemory" Ring="1" />
        // <ReferencesCritical Name="Method: CollaborationHelperFunctions.ConvertPeerContactToPEER_CONTACT(System.Net.PeerToPeer.Collaboration.PeerContact,System.Net.PeerToPeer.Collaboration.SafeCollabMemory&):System.Net.PeerToPeer.Collaboration.PEER_CONTACT" Ring="1" />
        // <ReferencesCritical Name="Field: m_SafeCollabInvite" Ring="1" />
        // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        internal void InviteAsyncEndPoint(PeerEndPoint peerEndPoint, PEER_INVITATION pi)
        {
            if (Logging.P2PTraceSource.Switch.ShouldTrace(TraceEventType.Information)){
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "InviteAsyncEndPoint() is called with the following Info");
                peerEndPoint.TracePeerEndPoint();
            } 
            
            PEER_ENDPOINT pep = new PEER_ENDPOINT();
            pep.peerAddress = CollaborationHelperFunctions.ConvertIPEndpointToPEER_ADDRESS(peerEndPoint.EndPoint);
 
            //
            // Pin all the data to pass to native
            //
            GCHandle pepName = new GCHandle();
            
            if (peerEndPoint.Name != null){
                pepName = GCHandle.Alloc(peerEndPoint.Name, GCHandleType.Pinned);
                pep.pwzEndpointName = pepName.AddrOfPinnedObject();
            }
            GCHandle peerEP = GCHandle.Alloc(pep, GCHandleType.Pinned);
            IntPtr ptrPeerEP = peerEP.AddrOfPinnedObject();
 
            int errorCode;
            try{
                //
                // Make native call with endpoint with/without contact
                //
                if (m_peerContact != null){
 
                    //
                    // Generate native contact
                    //
                    SafeCollabMemory safeCredentials = null;
                    PEER_CONTACT pc = CollaborationHelperFunctions.ConvertPeerContactToPEER_CONTACT(m_peerContact, ref safeCredentials);
 
                    try{
                        errorCode = UnsafeCollabNativeMethods.PeerCollabAsyncInviteContact(  ref pc,
                                                                                        ptrPeerEP,
                                                                                        ref pi,
                                                                                        m_InviteEvent.SafeWaitHandle,
                                                                                        out m_SafeCollabInvite);
                    }
                    finally{
                        if (safeCredentials != null) safeCredentials.Dispose();
                    }
                }
                else
                    errorCode = UnsafeCollabNativeMethods.PeerCollabAsyncInviteEndpoint(ptrPeerEP, ref pi,
                                                                                m_InviteEvent.SafeWaitHandle,
                                                                                out m_SafeCollabInvite);
 
            }
            finally
            {
                if (pepName.IsAllocated) pepName.Free();
                if (peerEP.IsAllocated) peerEP.Free();
            }
 
            if (errorCode != 0){
                throw PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_AsyncInviteFailed), errorCode);
            }
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving InviteAsyncEndPoint.");
 
        }
 
        //
        // Invite callback. Will fire only if i has at least one accepted or when it has all the responses
        // from all the endpoints
        //
        // <SecurityKernel Critical="True" Ring="0">
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabGetInvitationResponse(System.Net.PeerToPeer.Collaboration.SafeCollabInvite,System.Net.PeerToPeer.Collaboration.SafeCollabData&):System.Int32" />
        // <SatisfiesLinkDemand Name="SafeHandle.DangerousGetHandle():System.IntPtr" />
        // <SatisfiesLinkDemand Name="Marshal.PtrToStructure(System.IntPtr,System.Type):System.Object" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <ReferencesCritical Name="Local response of type: SafeCollabData" Ring="1" />
        // <ReferencesCritical Name="Field: m_SafeCollabInvite" Ring="1" />
        // <ReferencesCritical Name="Method: PeerToPeerException.CreateFromHr(System.String,System.Int32):System.Net.PeerToPeer.PeerToPeerException" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        internal void InviteCallback(object state, bool timedOut)
        {
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Entering InviteCallback.");
            
            SafeCollabData response = null;
 
            int errorCode = 0;
            InviteCompletedEventArgs inviteCompletedArgs = null;
            Peer peer = null;
            bool fireCallback = false;
            PEER_INVITATION_RESPONSE pir = new PEER_INVITATION_RESPONSE();
 
            try
            {
                lock (m_Lock){
                    if (m_Cancelled || m_Completed){
                        Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Invite cancelled({0}) or completed({1}). Returning without doing anything.", m_Cancelled, m_Completed);
                        return;
                    }
                    errorCode = UnsafeCollabNativeMethods.PeerCollabGetInvitationResponse(m_SafeCollabInvite, out response);
                }
 
                if ((errorCode != 0) && (errorCode != UnsafeCollabReturnCodes.PEER_E_TIMEOUT)){
                    m_latestException = PeerToPeerException.CreateFromHr(SR.GetString(SR.Collab_AsyncInviteException), errorCode);
                    Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got an exception {0}. Storing it in latest exception.", m_latestException);
                }
                else
                {
                    pir = (PEER_INVITATION_RESPONSE)Marshal.PtrToStructure(response.DangerousGetHandle(),
                                                    typeof(PEER_INVITATION_RESPONSE));
 
                    //
                    // Store the responses
                    //
                    lock (m_responses)
                        m_responses.Add(pir.action);
 
                    if (pir.action == PeerInvitationResponseType.Accepted){
                        inviteCompletedArgs = new InviteCompletedEventArgs(new PeerInvitationResponse(pir.action), null, false,
                                                                            m_AsyncOp.UserSuppliedState);
                        fireCallback = true;
 
                        //
                        // Got an accepted. unregister callback to disable all othe other endpoint callbacks
                        //
                        
                        Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Found an accepted. About to fire callback.");
 
                        m_RegisteredWaitHandle.Unregister(null);
                    }
                }
 
 
                Interlocked.Increment(ref m_numberOfResponses);
 
                if ((!fireCallback) && (m_numberOfResponses == m_peerEndPoints.Count) && (!m_aboutToFireCallback)){
                    //
                    // Two threads can be here at the same time when all the responses have been 
                    // received and only one should be allowed to call the callback
                    //
                    lock (m_aboutToFireCallbackLock){
                        if (m_aboutToFireCallback)
                            return;
 
                        m_aboutToFireCallback = true;
 
                        bool foundDeclined = false;
                        bool foundExpired = false;
 
                        fireCallback = true;
 
                        //
                        // Got all responses; make a decision
                        //
 
                        foreach (PeerInvitationResponseType responseType in m_responses)
                        {
                            if (responseType == PeerInvitationResponseType.Expired){
                                foundExpired = true;
                            }
                            else if (responseType == PeerInvitationResponseType.Declined){
                                foundDeclined = true;
                                break;
                            }
                        }
 
                        //
                        // If at least one is declined, return declined.  
                        //
 
                        if (foundDeclined){
                            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got a declined invite response.");
                            inviteCompletedArgs = new InviteCompletedEventArgs(new PeerInvitationResponse(PeerInvitationResponseType.Declined), null, false,
                                                        m_AsyncOp.UserSuppliedState);
                        }
                        else if (foundExpired){
                            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got an expired invite response but no accepted or declined.");
                            inviteCompletedArgs = new InviteCompletedEventArgs(new PeerInvitationResponse(PeerInvitationResponseType.Expired), null, false,
                                                        m_AsyncOp.UserSuppliedState);
                        }
                        else{
                            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Got all error responses");
                            inviteCompletedArgs = new InviteCompletedEventArgs(null, (m_latestException != null ? m_latestException : new PeerToPeerException("InviteAsync failure.")), false,
                                                        m_AsyncOp.UserSuppliedState);
                        }
                    }
                }
                //
                //Last chance to prevent the callback 
                //
                if (fireCallback){
                    peer = m_peerWeakReference.Target as Peer;
                    if (!m_Completed && (peer != null)){
                        lock (m_Lock){
                            //
                            // Async op may be cancelled already
                            //
                            if (!m_Completed && !m_Cancelled){
                                
                                //
                                //Mark as completed so that this gets fired only once
                                //
                                m_Completed = true;
                                Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Firing callback with response type {0}.", inviteCompletedArgs.InviteResponse);    
                                peer.PrepareToRaiseInviteCompletedEvent(m_AsyncOp, inviteCompletedArgs);
                            }
                        }
                    }
                }
            }
            finally{
                if (response != null) response.Dispose();
            }
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving InviteCallback.");
 
       }
 
        // <SecurityKernel Critical="True" Ring="0">
        // <CallsSuppressUnmanagedCode Name="UnsafeCollabNativeMethods.PeerCollabCancelInvitation(System.Net.PeerToPeer.Collaboration.SafeCollabInvite):System.Int32" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <ReferencesCritical Name="Field: m_SafeCollabInvite" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public void ContinueCancelCallback(object state)
        {
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Entering ContineCancelCallback.");
            
            try{
                lock (m_Lock){
                    if (m_Completed) return;
                    m_Cancelled = true;
 
                    int errorCode = UnsafeCollabNativeMethods.PeerCollabCancelInvitation(m_SafeCollabInvite);
                    if (errorCode != 0){
                        Logging.P2PTraceSource.TraceEvent(TraceEventType.Error, 0, "PeerCollabCancelInvitation returned with errorcode {0}", errorCode);
                    }
 
                    m_SafeCollabInvite.Dispose();
                }
 
                Peer peer = m_peerWeakReference.Target as Peer;
                if (peer != null){
                    InviteCompletedEventArgs e = new InviteCompletedEventArgs(null, null, true, m_AsyncOp.UserSuppliedState);
                    peer.PrepareToRaiseInviteCompletedEvent(m_AsyncOp, e);
                }
            }
            catch (ObjectDisposedException ex){
                Logging.P2PTraceSource.TraceEvent(TraceEventType.Critical, 0, "Exception while cancelling the call {0}", ex);
            }
 
            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, m_TraceEventId, "Leaving ContineCancelCallback.");
 
        }
 
        // <SecurityKernel Critical="True" Ring="1">
        // <ReferencesCritical Name="Method: ContinueCancelCallback(Object):Void" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public void CancelAsync(object state)
        {
            //
            //Defer the work to a callback
            //
 
            ThreadPool.QueueUserWorkItem(new WaitCallback(ContinueCancelCallback), state);
        }
 
        // <SecurityKernel Critical="True" Ring="1">
        // <ReferencesCritical Name="Method: Dispose(Boolean):Void" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        // <SecurityKernel Critical="True" Ring="0">
        // <SatisfiesLinkDemand Name="SafeHandle.get_IsInvalid():System.Boolean" />
        // <SatisfiesLinkDemand Name="SafeHandle.Dispose():System.Void" />
        // <ReferencesCritical Name="Field: m_SafeCollabInvite" Ring="1" />
        // </SecurityKernel>
        [System.Security.SecurityCritical]
        public void Dispose(bool disposing)
        {
            if (!m_Disposed){
                if (!m_SafeCollabInvite.IsInvalid){
                    m_SafeCollabInvite.Dispose();
                }
 
                if (m_RegisteredWaitHandle != null){
                    m_RegisteredWaitHandle.Unregister(null);
                    m_RegisteredWaitHandle = null;
                }
 
                if (m_InviteEvent != null){
                    m_InviteEvent.Close();
                }
            }
            m_Disposed = true;
        }
 
        public override int GetHashCode()
        {
            return m_TraceEventId;
        }
 
        internal int TraceEventId
        {
            get{
                return m_TraceEventId;
            }
        }
 
    }
 
}