Peer.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / SystemNet / Net / PeerToPeer / Collaboration / Peer.cs / 1305376 / Peer.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

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; 

    ///  
    /// This is the object changed event args class we give back when 
    /// we have have an object changed event fired by native
    ///  
    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; 
            }
        }
    }
 
    /// 
    /// This is the presence changed event args class we give back when 
    /// we have have presence changed event fired by native 
    /// 
    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;
            } 
        }
    }

    ///  
    /// This is the event args class we give back when
    /// we have completed the subscribeasync call 
    ///  
    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; 
            }
        }
    }
 
    /// 
    /// This is the event args class we give back when 
    /// we have completed the refreshendpoint async call 
    /// 
    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; 
            }
        } 
    }

    /// 
    /// This is the event args class we give back when 
    /// we have completed the inviteasync call
    ///  
    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;
            } 
        } 
    }
 
    /// 
    /// Has the common interface for PeerNearMe and PeerContact which derive from it
    /// 
    [Serializable] 
    public abstract class Peer : IDisposable, IEquatable, 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; 
            }
        } 

        /// 
        /// Gets and set the object used to marshall event handlers calls for stand alone
        /// events 
        /// 
        [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
        { 
            // 
            //  
            //  
            // 
            [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); 
        }

        /// 
        /// Constructor to enable serialization 
        /// 
        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
        // 
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        [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
        //
        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        public PeerObjectCollection GetObjects()
        { 
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();

            return InternalGetAllObjects(Guid.Empty, false);
        } 

        // 
        // Gets specific object for all the endpoints 
        //
        //  
        // 
        // 
        [System.Security.SecurityCritical]
        public PeerObjectCollection GetObjects(Guid objectId) 
        {
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); 
 
            return InternalGetAllObjects(objectId, true);
        } 

        internal abstract void RefreshIfNeeded();

        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        private PeerObjectCollection InternalGetAllObjects(Guid objectId, bool guidSupplied)
        { 

            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalGetAllObjects() with ObjectId {0}.", objectId);

            Dictionary mergedObjects = new Dictionary(); 
            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.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
        //
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        [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. 
        //
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        [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 m_inviteCompleted;
        public event EventHandler 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 m_inviteAsyncHelperList = new Dictionary(); 
        #endregion

        // 
        //  
        // 
        [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 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); 
        } 

        //  
        // 
        // 
        [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);
        }

        //  
        // 
        //  
        [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);
        } 

        ///  
        /// This is made virtual so that derived types can be implemented correctly 
        /// 
        ///  
        /// 
        [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;
            }
        } 
    }
 
    ///  
    /// Helps in all the async invites sent from Peer, PeerContact and PeerNearMe
    ///  
    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 m_responses = new Collection();
 
        // 
        // 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()
                ); 

        } 
 
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        [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.");
        } 

        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        [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
        //
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        [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.");
 
       }
 
        //  
        // 
        //  
        // 
        // 
        [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."); 

        } 
 
        // 
        //  
        // 
        [System.Security.SecurityCritical]
        public void CancelAsync(object state)
        { 
            //
            //Defer the work to a callback 
            // 

            ThreadPool.QueueUserWorkItem(new WaitCallback(ContinueCancelCallback), state); 
        }

        // 
        //  
        // 
        [System.Security.SecurityCritical] 
        public void Dispose() 
        {
            Dispose(true); 
            GC.SuppressFinalize(this);
        }

        //  
        // 
        //  
        //  
        // 
        [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;
            }
        }
 
    }
 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

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; 

    ///  
    /// This is the object changed event args class we give back when 
    /// we have have an object changed event fired by native
    ///  
    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; 
            }
        }
    }
 
    /// 
    /// This is the presence changed event args class we give back when 
    /// we have have presence changed event fired by native 
    /// 
    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;
            } 
        }
    }

    ///  
    /// This is the event args class we give back when
    /// we have completed the subscribeasync call 
    ///  
    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; 
            }
        }
    }
 
    /// 
    /// This is the event args class we give back when 
    /// we have completed the refreshendpoint async call 
    /// 
    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; 
            }
        } 
    }

    /// 
    /// This is the event args class we give back when 
    /// we have completed the inviteasync call
    ///  
    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;
            } 
        } 
    }
 
    /// 
    /// Has the common interface for PeerNearMe and PeerContact which derive from it
    /// 
    [Serializable] 
    public abstract class Peer : IDisposable, IEquatable, 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; 
            }
        } 

        /// 
        /// Gets and set the object used to marshall event handlers calls for stand alone
        /// events 
        /// 
        [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
        { 
            // 
            //  
            //  
            // 
            [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); 
        }

        /// 
        /// Constructor to enable serialization 
        /// 
        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
        // 
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        [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
        //
        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        public PeerObjectCollection GetObjects()
        { 
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand();

            return InternalGetAllObjects(Guid.Empty, false);
        } 

        // 
        // Gets specific object for all the endpoints 
        //
        //  
        // 
        // 
        [System.Security.SecurityCritical]
        public PeerObjectCollection GetObjects(Guid objectId) 
        {
            PeerCollaborationPermission.UnrestrictedPeerCollaborationPermission.Demand(); 
 
            return InternalGetAllObjects(objectId, true);
        } 

        internal abstract void RefreshIfNeeded();

        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        private PeerObjectCollection InternalGetAllObjects(Guid objectId, bool guidSupplied)
        { 

            Logging.P2PTraceSource.TraceEvent(TraceEventType.Information, 0, "Entering InternalGetAllObjects() with ObjectId {0}.", objectId);

            Dictionary mergedObjects = new Dictionary(); 
            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.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
        //
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        [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. 
        //
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        [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 m_inviteCompleted;
        public event EventHandler 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 m_inviteAsyncHelperList = new Dictionary(); 
        #endregion

        // 
        //  
        // 
        [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 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); 
        } 

        //  
        // 
        // 
        [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);
        }

        //  
        // 
        //  
        [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);
        } 

        ///  
        /// This is made virtual so that derived types can be implemented correctly 
        /// 
        ///  
        /// 
        [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;
            }
        } 
    }
 
    ///  
    /// Helps in all the async invites sent from Peer, PeerContact and PeerNearMe
    ///  
    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 m_responses = new Collection();
 
        // 
        // 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()
                ); 

        } 
 
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        [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.");
        } 

        // 
        // 
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        //  
        // 
        [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
        //
        //  
        // 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        [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.");
 
       }
 
        //  
        // 
        //  
        // 
        // 
        [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."); 

        } 
 
        // 
        //  
        // 
        [System.Security.SecurityCritical]
        public void CancelAsync(object state)
        { 
            //
            //Defer the work to a callback 
            // 

            ThreadPool.QueueUserWorkItem(new WaitCallback(ContinueCancelCallback), state); 
        }

        // 
        //  
        // 
        [System.Security.SecurityCritical] 
        public void Dispose() 
        {
            Dispose(true); 
            GC.SuppressFinalize(this);
        }

        //  
        // 
        //  
        //  
        // 
        [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;
            }
        }
 
    }
 
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK