相信大家都有过演讲的经历,当我们演讲时,一手拿着windows phone手机按按键就可以操作powerPoint幻灯片的播放,是不是显得比较高端?你如果想了解这种互动是怎么实现的,那么你可以继续往下看。
使用windows phone操作PC的PPT播放并不是一件多难的事儿,同样是对于android和IOS移动端,主要涉及到两个方面:通信和调用PowerPoint 的API。对于第一方面,既然手机端可以控制PC端,那么他们之间必然有信息传递,你可以使用手机的蓝牙和电脑的蓝牙进行通信,不过考虑到现在的PC还有很多不支持蓝牙,再加上蓝牙传输可能并不稳定,所以在这里我们可以使用Socket进行通信;对于第二方面,也很简单,我们只需要在PC端去调用PowerPoint的API就OK,主要涉及的操作包括:切换幻灯片到第一张、切换幻灯片到下一张、切换幻灯片到上一张、切换幻灯片到最后一张。
举个例子来说,我们当用手机点击上一页按钮时,会传递一个数字a到服务端,服务端会判断这个数字a是要执行切换到上一页操作的命令,所以我们就调用切换到上一页的API,就能成功实现切换,所以如果想实现这个功能不仅仅需要手机客户端,还需要一个PC的服务端用来接收手机端发来的消息并进行处理,当然手机端需要输入PC端IP和端口来和PC端通信,我在这里仅实现了最基本的功能,其他功能可以在此基础上进行扩展。
下面首先介绍服务端调用PowerPoint API 的代码:(参考了貌似是一位微软工程师的博客)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.InteropServices; 5 using System.Text; 6 using System.Threading.Tasks; 7 using PPt = Microsoft.Office.Interop.PowerPoint; 8 namespace ServerPPT 9 { 10 class PPTServer 11 { 12 // 定义PowerPoint应用程序对象 13 PPt.Application pptApplication; 14 // 定义演示文稿对象 15 PPt.Presentation presentation; 16 // 定义幻灯片集合对象 17 PPt.Slides slides; 18 // 定义单个幻灯片对象 19 PPt.Slide slide; 20 21 // 幻灯片的数量 22 int slidescount; 23 // 幻灯片的索引 24 int slideIndex; 25 //检查幻灯片是否已经打开 26 public void CheckPPT() 27 { 28 // 必须先运行幻灯片,下面才能获得PowerPoint应用程序,否则会出现异常 29 // 获得正在运行的PowerPoint应用程序 30 try 31 { 32 pptApplication = Marshal.GetActiveObject("PowerPoint.Application") as PPt.Application; 33 34 // 成功获取了PowerPoint程序时,使UI按钮可用 35 } 36 catch 37 { 38 // MessageBox.Show("请先启动遥控的幻灯片", "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error); 39 } 40 if (pptApplication != null) 41 { 42 //获得演示文稿对象 43 presentation = pptApplication.ActivePresentation; 44 // 获得幻灯片对象集合 45 slides = presentation.Slides; 46 // 获得幻灯片的数量 47 slidescount = slides.Count; 48 // 获得当前选中的幻灯片 49 try 50 { 51 // 在普通视图下这种方式可以获得当前选中的幻灯片对象 52 // 然而在阅读模式下,这种方式会出现异常 53 slide = slides[pptApplication.ActiveWindow.Selection.SlideRange.SlideNumber]; 54 } 55 catch 56 { 57 // 在阅读模式下出现异常时,通过下面的方式来获得当前选中的幻灯片对象 58 slide = pptApplication.SlideShowWindows[1].View.Slide; 59 } 60 } 61 } 62 63 //第一页 64 public void FirstPage() 65 { 66 try 67 { 68 // 在普通视图中调用Select方法来选中第一张幻灯片 69 slides[1].Select(); 70 slide = slides[1]; 71 } 72 catch 73 { 74 // 在阅读模式下使用下面的方式来切换到第一张幻灯片 75 pptApplication.SlideShowWindows[1].View.First(); 76 slide = pptApplication.SlideShowWindows[1].View.Slide; 77 } 78 } 79 //上一页 80 public void PreviousPage() 81 { 82 slideIndex = slide.SlideIndex - 1; 83 if (slideIndex >= 1) 84 { 85 try 86 { 87 slide = slides[slideIndex]; 88 slides[slideIndex].Select(); 89 } 90 catch 91 { 92 // 在阅读模式下使用下面的方式来切换到上一张幻灯片 93 pptApplication.SlideShowWindows[1].View.Previous(); 94 slide = pptApplication.SlideShowWindows[1].View.Slide; 95 } 96 } 97 else 98 { 99 // MessageBox.Show("已经是第一页了"); 100 } 101 } 102 103 //下一页 104 public void NextPage() 105 { 106 slideIndex = slide.SlideIndex + 1; 107 if (slideIndex > slidescount) 108 { 109 //MessageBox.Show("已经是最后一页了"); 110 } 111 else 112 { 113 try 114 { 115 slide = slides[slideIndex]; 116 slides[slideIndex].Select(); 117 } 118 catch 119 { 120 // 在阅读模式下使用下面的方式来切换到下一张幻灯片 121 pptApplication.SlideShowWindows[1].View.Next(); 122 slide = pptApplication.SlideShowWindows[1].View.Slide; 123 } 124 } 125 } 126 //最后一页 127 public void LastPage() 128 { 129 try 130 { 131 slides[slidescount].Select(); 132 slide = slides[slidescount]; 133 } 134 catch 135 { 136 // 在阅读模式下使用下面的方式来切换到最后幻灯片 137 pptApplication.SlideShowWindows[1].View.Last(); 138 slide = pptApplication.SlideShowWindows[1].View.Slide; 139 } 140 } 141 } 142 }
其次介绍服务端PC和和客户端(windows phone)使用Socket通信的代码,服务端等待接收消息代码如下所示:
1 Socket listenner = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 2 private Control control = new Control(); 3 4 public Form1() 5 { 6 InitializeComponent(); 7 TextBox.CheckForIllegalCrossThreadCalls = false; 8 9 } 10 //打印提示信息 11 public void showMessage(String strMsg) 12 { 13 output.AppendText(strMsg + " "); 14 } 15 private static AutoResetEvent _flipFlop = new AutoResetEvent(false); 16 private void start_Click(object sender, EventArgs e) 17 { 18 19 20 IPAddress address = IPAddress.Parse(iptext.Text.Trim());//创建IP对象 21 22 IPEndPoint localEP = new IPEndPoint(address,int.Parse(porttext.Text.Trim())); 23 24 // MessageBox.Show("运行中"); 25 showMessage("本地IP地址和端口是"+localEP); 26 listenner.Bind(localEP); 27 listenner.Listen(10); 28 Thread threadlisten;//定义监听线程 29 threadlisten = new Thread(startlisten); 30 threadlisten.IsBackground = true;//创建线程时默认false,为false时,主线程退出,仍然执行,直到结束 31 threadlisten.Start(); 32 33 } 34 private void startlisten() { 35 while (true) 36 { 37 // Console.WriteLine("等待windows phone客户端的连接。。。。"); 38 showMessage("等待windows phone客户端的连接"); 39 listenner.BeginAccept(AcceptCallback, listenner); 40 _flipFlop.WaitOne(); 41 } 42 } 43 private void AcceptCallback(IAsyncResult ar) 44 { 45 Socket listener = (Socket)ar.AsyncState; 46 Socket socket = listener.EndAccept(ar); 47 // Console.WriteLine("连接到windows phone 客户端"); 48 showMessage("连接到windows phone 客户端"); 49 _flipFlop.Set(); 50 var state = new StateObject(); 51 state.Socket = socket; 52 socket.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state); 53 } 54 55 private void ReceiveCallback(IAsyncResult ar) 56 { 57 StateObject state = (StateObject)ar.AsyncState; 58 Socket socket = state.Socket; 59 int read = socket.EndReceive(ar); 60 if (read > 0) 61 { 62 string chunk = Encoding.UTF8.GetString(state.Buffer, 0, read); 63 state.StringBuilder.Append(chunk); 64 if (state.StringBuilder.Length > 0) 65 { 66 string result = state.StringBuilder.ToString(); 67 // Console.WriteLine("收到客户端传来的消息:{0}", result); 68 control.Dispatcher(int.Parse(result)); 69 //showMessage("收到客户端传来的消息:"+result); 70 //Send(socket, "s" + result); 71 //Console.WriteLine("发给客户端的消息:{0}", "s" + result); 72 //showMessage("发给客户端的消息"+result); 73 } 74 } 75 }
客户端发起连接的代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Net; 5 using System.Windows; 6 using System.Windows.Controls; 7 using System.Windows.Navigation; 8 using Microsoft.Phone.Controls; 9 using Microsoft.Phone.Shell; 10 using SocketClient.Resources; 11 using System.Net.Sockets; 12 using System.Text; 13 14 namespace SocketClient 15 { 16 public partial class MainPage : PhoneApplicationPage 17 { 18 19 public MainPage() 20 { 21 InitializeComponent(); 22 23 } 24 25 private void Send(string str) 26 { 27 if (string.IsNullOrEmpty(Host.Text) || string.IsNullOrEmpty(Port.Text)) { 28 MessageBox.Show("请输入以下主机IP和端口号,谢谢"); 29 return; 30 } 31 32 string host = Host.Text.Trim(); 33 34 int port = Convert.ToInt32(Port.Text.Trim()); 35 36 DnsEndPoint hostEntry = new DnsEndPoint(host, port); 37 Socket sock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); 38 39 SocketAsyncEventArgs socketEventArg = new SocketAsyncEventArgs(); 40 byte[] buffer = Encoding.UTF8.GetBytes(str); 41 socketEventArg.SetBuffer(buffer, 0, buffer.Length); 42 socketEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(SocketAsyncEventArgs_Completed); 43 socketEventArg.RemoteEndPoint = hostEntry; 44 socketEventArg.UserToken = sock; 45 try 46 { 47 sock.ConnectAsync(socketEventArg); 48 } 49 catch (SocketException ex) { 50 throw new SocketException((int)ex.ErrorCode); 51 } 52 53 54 } 55 private void SocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) 56 { 57 if(e.SocketError!=SocketError.Success){ 58 if (e.SocketError == SocketError.ConnectionAborted) { 59 Dispatcher.BeginInvoke(()=>MessageBox.Show("连接超时请重试"+e.SocketError)); 60 } 61 else if (e.SocketError == SocketError.ConnectionRefused) 62 { 63 Dispatcher.BeginInvoke(() => MessageBox.Show("服务器端启动" + e.SocketError)); 64 } 65 else { 66 Dispatcher.BeginInvoke(() => MessageBox.Show("出错了" + e.SocketError)); 67 } 68 69 if (e.UserToken != null) { 70 Socket sock = e.UserToken as Socket; 71 sock.Shutdown(SocketShutdown.Both); 72 sock.Close(); 73 } 74 return; 75 } 76 77 switch (e.LastOperation) { 78 case SocketAsyncOperation.Connect: 79 if (e.UserToken != null) { 80 Socket sock = e.UserToken as Socket; 81 bool completeAsynchronously = sock.SendAsync(e); 82 if (!completeAsynchronously) { 83 SocketAsyncEventArgs_Completed(e.UserToken, e); 84 } 85 }; 86 break; 87 case SocketAsyncOperation.Send: 88 Dispatcher.BeginInvoke(()=>{ 89 //listBox1.Items.Add("客户端"+DateTime.Now.ToShortTimeString()+"发送的消息:"+Message.Text); 90 } 91 ); 92 if (e.UserToken != null) 93 { 94 Socket sock = e.UserToken as Socket; 95 bool completesAsynchronously = sock.ReceiveAsync(e); 96 if (!completesAsynchronously) { 97 SocketAsyncEventArgs_Completed(e.UserToken,e); 98 } 99 } 100 break; 101 case SocketAsyncOperation.Receive: 102 if (e.UserToken != null) { 103 string dataFromServer = Encoding.UTF8.GetString(e.Buffer,0,e.BytesTransferred); 104 Socket sock = e.UserToken as Socket; 105 Dispatcher.BeginInvoke(() => 106 { 107 108 109 // listBox1.Items.Add("服务器端" + DateTime.Now.ToShortTimeString() + "传过来的消息:" + dataFromServer); 110 }); 111 } 112 break; 113 } 114 115 } 116 117 private void check_Click(object sender, RoutedEventArgs e) 118 { 119 Send("1"); 120 } 121 122 private void First_Click(object sender, RoutedEventArgs e) 123 { 124 Send("2"); 125 } 126 127 private void previous_Click(object sender, RoutedEventArgs e) 128 { 129 Send("3"); 130 } 131 132 private void next_Click(object sender, RoutedEventArgs e) 133 { 134 Send("4"); 135 } 136 137 private void last_Click(object sender, RoutedEventArgs e) 138 { 139 Send("5"); 140 } 141 142 } 143 144 145 146 147 }
源代码我已经上传到百度网盘链接: http://pan.baidu.com/s/1mg5HAsS 密码: i4a9,欢迎大家学习,并进行二次开发。