We've made our connect call, but much like after our find hosts call, we don't really know what the outcome of that call was. Naturally, there is an event to handle this as well. Add the following handler to our InitializeDirectPlay method: connection.ConnectComplete += new ConnectCompleteEventHandler(OnConnectComplete); We also need the event handler method implementation, so add this as well: private void OnConnectComplete(object sender, ConnectCompleteEventArgs e) { // Check to see if we connected properly if (e.Message.ResultCode == ResultCode.Success) { this.BeginInvoke(new AddTextCallback(AddText), new object[] { "Connect Success." }); connected = true; this.BeginInvoke(new EnableCallback(EnableSendDataButton), new object[] { true } ); } else { this.BeginInvoke(new AddTextCallback(AddText), new object[] { string.Format("Connect Failure: {0}", e.Message.ResultCode) }); connected = false; this.BeginInvoke(new EnableCallback(EnableSendDataButton), new object[] { false } ); } } So this is where we can check to see if our connection call actually succeeded. The result code will tell us the result of the operation, and if that was a success, we've been connected to the session. In this case, we want to update our UI, and enable the send data button. If the connect call failed, we still update our UI; however, instead we disable the send data button, and set our connected variable to false. Setting this to false allows the FindHosts handler to try to connect to other sessions that it finds. You'll also notice that we don't have the delegate to enable or disable our send data button declared. You can declare it as follows: private delegate void EnableCallback(bool enable); You can run two instances of the application now and get them to connect to one another and be joined in the same session. On the first instance, click the Host button, and it should inform you that you are currently hosting the session. You can click the Connect button on the second instance, and after the dialog asking you about the remote host, you should see that it found your session and connected to it. What we need now is a way to actually send some type of data to the session. We already have our send data button that currently doesn't do anything, so let's add this event handler for it: private void button3_Click(object sender, System.EventArgs e) { NetworkPacket packet = new NetworkPacket(); packet.Write(byte.MaxValue); connection.SendTo((int)PlayerID.AllPlayers, packet, 0, SendFlags.Guaranteed); } When sending data to a session in DirectPlay, it is common to use a network packet. You can write any information into this packet you want, providing it is a value type, an array of value types, or a string. You cannot pass in reference types to this method. We don't really care what we pass in for this application; we just want to send some value, so we'll just send in a single byte. The actual send call is relatively straightforward. The first parameter is the player identifier of the user we want to send the data to. You can use any player or group identifier you wish, or (as we've done here) send it to all players. Don't worry; we'll discuss groups later. We naturally will also want to specify the network packet we will be sending. The third parameter is the timeout value for this packet. Values of zero (like we pass here) will never time out, unless of course the session itself drops. The last parameter we use for this call is one or more members of the SendFlags enumeration. Valid values for this enumeration are in Table 18.4:
As you can infer from the comments on the NoCopy flag, there are several overloads that take a GCHandle and buffer size variables. Since this is an asynchronous method, there are also overloads that return the asynchronous handle via an out parameter. Some of the overloads also include an application-defined context variable as well. Now that data is being sent when we click our button, we will obviously need to include an event handler for when the data is received. Add this with the rest of the event hooks: connection.Receive += new ReceiveEventHandler(OnDataReceive); Here is the code for the actual event handler: private void OnDataReceive(object sender, ReceiveEventArgs e) { // We received some data, update our UI string newtext = string.Format ("Received message from DPlay UserId: 0x{0}", e.Message.SenderID.ToString("x")); this.BeginInvoke(new AddTextCallback(AddText), new object[] { newtext }); } For this simple application, we don't actually care what the data we've received is, although we can get it from the ReceiveEventArgs object. Instead, we will just get the DirectPlay user identifier and update our UI to notify the user that a message was received. Get two different instances of the application running (either on the same machine or on separate machines, it really doesn't matter). Have one be the host, while the other connects to it. Clicking the Send Data button should update the UI on both the sender as well as the receiver. This happens because we did not pass in the NoLoopBack flag when we called our SendTo method. The data is sent to everyone, including you. Now, try this. While both instances are still running, quit the session that is the host. Now click the Send Data button on the remaining instance. You'll notice that an unhandled exception has occurred, which will cause your application to crash. |