最近突然想起了一个学长的一个利用手机控制ppt播放的一个创意,并想将其在windows phone7上实现一下。
经过几天的努力已经可以控制ppt的播放,暂停,上一张,下一张了,并且电脑会将当前ppt的截图发送到手机端这里。
在代码的编写过程中,参考了IT黄老邪的Windows Phone开发(46):与Socket有个约会 进行服务端与wp客户端的socket通讯的编写,并加入了键盘消息模拟,服务端截屏,图片发送,与客户端的图片接受。
代码如下:
WP客户端
1、新建Windows Phone应用程序项目。(我这里的名称是叫PhoneApp2)
2、打开MainPage.xaml文件,输入以下代码。
<phone:PhoneApplicationPage x:Class="PhoneApp2.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <!--LayoutRoot 是包含所有页面内容的根网格--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel 包含应用程序的名称和页标题--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="我的应用程序" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="PPT播放" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - 在此处放置其他内容--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" VerticalAlignment="Center" Text="服务器IP:" /> <TextBox Name="txtServerIP" Grid.Column="1" Text="" /> <Button Grid.Column="2" Content="连接" Click="onConnect"/> </Grid> <Button Content="播放" Click="onPlay" Width="154" Margin="54,0,248,463" Grid.Row="1" /> <Button Content="下一张" Click="onPlayNext" Margin="0,78,21,358" Grid.Row="1" HorizontalAlignment="Right" Width="197" /> <Button Content="上一张" Click="onPlayBack" Margin="6,78,248,358" Grid.Row="1" /> <Button Content="暂停" Click="onPause" Width="143" Margin="237,0,75,463" Grid.Row="1" /> <TextBlock Name="txtbInfo" Margin="6,475,0,6" Grid.Row="1" /> <Image Grid.Row="1" Height="281" HorizontalAlignment="Left" Margin="12,170,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="423" Tap="image1_Tap" /> </Grid> </Grid> <!--演示 ApplicationBar 用法的示例代码--> <!--<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="按钮 1"/> <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="按钮 2"/> <shell:ApplicationBar.MenuItems> <shell:ApplicationBarMenuItem Text="菜单项 1"/> <shell:ApplicationBarMenuItem Text="菜单项 2"/> </shell:ApplicationBar.MenuItems> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>--> </phone:PhoneApplicationPage>
3、打开MainPage.xaml.cs,输入以下代码。
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using System.Net.Sockets; using System.Threading; using System.Text; using System.IO; using System.Windows.Media.Imaging; namespace PhoneApp2 { public partial class MainPage : PhoneApplicationPage { Socket mySocket = null; ManualResetEvent MyEvent = null; Byte[] imgbyte = new Byte[262144]; // 构造函数 public MainPage() { InitializeComponent(); } protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); if (mySocket == null) { mySocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); mySocket.ReceiveBufferSize = 262144; } if (MyEvent == null) { MyEvent = new ManualResetEvent(false); } } protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) { try { if (mySocket != null) { mySocket.Shutdown(SocketShutdown.Both); mySocket.Close(); } base.OnNavigatedFrom(e); } catch { base.OnNavigatedFrom(e); } } private void onConnect(object sender, RoutedEventArgs e) { if (mySocket != null) { SocketAsyncEventArgs connArg = new SocketAsyncEventArgs(); if(this.txtServerIP.Text=="") { txtbInfo.Text="请输入正确的ip"; return; } // 要连接的远程服务器 connArg.RemoteEndPoint = new DnsEndPoint(this.txtServerIP.Text, 1377); // 操作完成后的回调 connArg.Completed += (sendObj, arg) => { if (arg.SocketError == SocketError.Success) //连接成功 { Dispatcher.BeginInvoke(() => txtbInfo.Text = "连接成功。"); imgbyte = arg.Buffer; } else { Dispatcher.BeginInvoke(() => { txtbInfo.Text = "连接失败,错误:" + arg.SocketError.ToString(); }); } // 向调用线程报告操作结束 MyEvent.Set(); }; // 重置线程等待事件 MyEvent.Reset(); txtbInfo.Text = "正在连接,请等候……"; // 开始异连接 mySocket.ConnectAsync(connArg); // 等待连接完成 MyEvent.WaitOne(6000); } } Byte[] imgInfo; private void onPlayNext(object sender, RoutedEventArgs e) { SendCommand("playNext"); receiveImg(); } private void receiveImg() { try { imgInfo = ReceiveInfo(); MemoryStream ms = new MemoryStream(imgInfo); BitmapImage bitmapImage = new BitmapImage(); bitmapImage.SetSource(ms); WriteableBitmap writeableBitmap = new WriteableBitmap(bitmapImage); this.image1.Source = writeableBitmap; } catch { SendCommand("newimg"); } } private void onPlayBack(object sender, RoutedEventArgs e) { SendCommand("playBack"); receiveImg(); } private void onPlay(object sender, RoutedEventArgs e) { SendCommand("play"); receiveImg(); } private void onPause(object sender, RoutedEventArgs e) { SendCommand("pause"); receiveImg(); } int i = 0; byte[] received = new Byte[1024 *9]; private byte[] ReceiveInfo() { if (mySocket != null && mySocket.Connected) { SocketAsyncEventArgs receiveArg = new SocketAsyncEventArgs(); byte[] buffer = new byte[1024]; receiveArg.SetBuffer(buffer, 0, buffer.Length); receiveArg.Completed += new EventHandler<SocketAsyncEventArgs>(receiveArg_Completed); MyEvent.Reset(); mySocket.ReceiveAsync(receiveArg); MyEvent.WaitOne(500); } return received; } void receiveArg_Completed(object sender, SocketAsyncEventArgs mArg) { if (mArg.SocketError == SocketError.Success) { if (i < 9) { Buffer.BlockCopy(mArg.Buffer,0,received,i * 1024, 1024); i++; ReceiveInfo(); } MyEvent.Set(); mArg.Dispose(); } } private void SendCommand(string txt) { i = 0; if (mySocket != null && mySocket.Connected) { SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs(); byte[] buffer = System.Text.Encoding.UTF8.GetBytes(txt); sendArg.SetBuffer(buffer, 0, buffer.Length); // 发送完成后的回调 sendArg.Completed += (objSender, mArg) => { // 如果操作成功 if (mArg.SocketError == SocketError.Success) { Dispatcher.BeginInvoke(() => txtbInfo.Text = "发送成功。"); } else { Dispatcher.BeginInvoke(() => { this.txtbInfo.Text = "发送失败,错误:" + mArg.SocketError.ToString(); }); } // 报告异步操作结束 MyEvent.Set(); }; // 重置信号 MyEvent.Reset(); txtbInfo.Text = "正在发送,请等候……"; // 异步发送 mySocket.SendAsync(sendArg); // 等待操作完成 MyEvent.WaitOne(6000); } } private void image1_Tap(object sender, GestureEventArgs e) { SendCommand("newimg"); receiveImg(); } } }
1、新建WPF应用程序项目。(我这里的名称是叫做WpfApplication4)
2、打开MainWindow.xaml文件,输入以下XAML代码。
<Window x:Class="WpfApplication4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="服务器端" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <TextBlock Name="txtDisplay2" Grid.Row="1" Margin="240,0,12,0" /> <TextBlock Name="txtDisplay" Grid.Row="1" Margin="0,0,261,0" /> <TextBlock Height="181" TextWrapping="Wrap" HorizontalAlignment="Left" Margin="88,60,0,0" Name="textBlock1" VerticalAlignment="Top" FontSize="25" Width="319" > 打开客户端,输入服务器ip后,将程序最小化,打开需要的ppt。 </TextBlock> </Grid> </Window>
3.MainWindow.xaml.cs的代码如下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Forms; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Media.Animation; using System.Net; using System.Net.Sockets; using System.Threading; using System.Drawing; using System.IO; using System.Drawing.Imaging; namespace WpfApplication4 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { Socket Server; Byte[] imgInfo=new Byte[1024*5]; public MainWindow() { InitializeComponent(); InitialTray(); // 从资源中把Key为std的Storyboard读出来 // 声明用于监听连接请求的Socket Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // string strHostName = Dns.GetHostName(); //得到本机的主机名 IPHostEntry ipEntry = Dns.GetHostEntry(strHostName); ////取得本机IP IPAddress strAddr = ipEntry.AddressList[7];//.ToString(); txtDisplay2.Text = "服务器IP:" + strAddr.ToString();//假设本地主机为单网卡 // IPEndPoint local = new IPEndPoint(IPAddress.Any, 1377); //监听所有网络接口上的地址 Server.Bind(local);// 绑定本地终结点 Server.Listen(100);// 侦听连接请求 // 开始异步接受传入的连接请求 Server.BeginAccept(new AsyncCallback(this.AcceptSocketCallback), Server); } private System.Windows.Forms.NotifyIcon notifyIcon = null; private void InitialTray() { //设置托盘的各个属性 notifyIcon = new System.Windows.Forms.NotifyIcon(); notifyIcon.BalloonTipText = "程序开始运行"; notifyIcon.Text = "ppt服务端"; notifyIcon.Icon = new System.Drawing.Icon("Downloads.ico"); notifyIcon.Visible = true; notifyIcon.ShowBalloonTip(2000); notifyIcon.MouseClick += new System.Windows.Forms.MouseEventHandler(notifyIcon_MouseClick); //退出菜单项 System.Windows.Forms.MenuItem exit = new System.Windows.Forms.MenuItem("exit"); exit.Click += new EventHandler(exit_Click); //关联托盘控件 System.Windows.Forms.MenuItem[] childen = new System.Windows.Forms.MenuItem[] { exit }; notifyIcon.ContextMenu = new System.Windows.Forms.ContextMenu(childen); //窗体状态改变时候触发 this.StateChanged += new EventHandler(SysTray_StateChanged); } /// /// 窗体状态改变时候触发 /// /// /// private void SysTray_StateChanged(object sender, EventArgs e) { if (this.WindowState == WindowState.Minimized) { this.Visibility = Visibility.Hidden; } } /// /// 退出选项 /// /// /// private void exit_Click(object sender, EventArgs e) { if (System.Windows.MessageBox.Show("确定要关闭吗?", "退出", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.Yes) { notifyIcon.Dispose(); System.Windows.Application.Current.Shutdown(); } } /// /// 鼠标单击 /// /// /// private void notifyIcon_MouseClick(object sender, System.Windows.Forms.MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) { if (this.Visibility == Visibility.Visible) { this.Visibility = Visibility.Hidden; } else { this.Visibility = Visibility.Visible; this.Activate(); } } } // /// <summary> /// 接受传入的Socket的回调 /// </summary> private void AcceptSocketCallback(IAsyncResult ia) { Socket _socket = ia.AsyncState as Socket; Socket accptSocket = _socket.EndAccept(ia); try { IPEndPoint remote = (IPEndPoint)accptSocket.RemoteEndPoint; // 显示客户端的IP Dispatcher.BeginInvoke(new Action<string>(this.SetIPForText), remote.Address.ToString()); StateObject so = new StateObject(); so.theSocket = accptSocket; // 开始异步接收消息 accptSocket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, new AsyncCallback(this.ReceiveCallback), so); } catch { } // 继续接受连接请求 _socket.BeginAccept(new AsyncCallback(this.AcceptSocketCallback), _socket); } /// <summary> /// 接收消息的回调 /// </summary> private void ReceiveCallback(IAsyncResult ia) { StateObject _so = ia.AsyncState as StateObject; Socket _socket = _so.theSocket; try { int n = _socket.EndReceive(ia);//n就是接收到的字节数 string msg = Encoding.UTF8.GetString(_so.Buffer, 0, n); // 判断客户端发送了啥命令 switch (msg) { case "playNext": System.Windows.Forms.SendKeys.SendWait("{Right}"); SendCommand(_socket, CatchScreen()); break; case "playBack": System.Windows.Forms.SendKeys.SendWait("{Left}"); SendCommand(_socket, CatchScreen()); break; case "pause": System.Windows.Forms.SendKeys.SendWait("{B}"); SendCommand(_socket, CatchScreen()); break; case "play": System.Windows.Forms.SendKeys.SendWait("{F5}"); SendCommand(_socket, CatchScreen()); break; case "newimg": SendCommand(_socket, CatchScreen()); break; default: break; } } catch { } _so = new StateObject(); _so.theSocket = _socket; // 继续接收消息 _socket.BeginReceive(_so.Buffer, 0, _so.Buffer.Length, SocketFlags.None, new AsyncCallback(this.ReceiveCallback), _so); } private void SendCommand(Socket handler,byte[] imginfo) { try { handler.BeginSend(imginfo, 0, imginfo.Length, 0, new AsyncCallback(SendCallBack), handler); } catch { } } private void SendCallBack(IAsyncResult ar) { try { Socket handler = (Socket)ar.AsyncState; int bytesSend = handler.EndSend(ar); if (bytesSend > 0) { Console.WriteLine("Succeed"); } } catch (Exception e) { Console.WriteLine(e.ToString()); } } private byte[] CatchScreen() { Screen scr = Screen.PrimaryScreen; System.Drawing.Rectangle rc = scr.Bounds; int iWidth = rc.Width; int iHeight = rc.Height; //创建一个和屏幕一样大的Bitmap System.Drawing.Image myImage = new Bitmap(iWidth, iHeight); //从一个继承自Image类的对象中创建Graphics对象 Graphics g = Graphics.FromImage(myImage); g.CopyFromScreen(new System.Drawing.Point(0, 0), new System.Drawing.Point(0, 0), new System.Drawing.Size(iWidth, iHeight)); return ImgToByt(myImage.GetThumbnailImage(iWidth / 6, iHeight/6, new System.Drawing.Image.GetThumbnailImageAbort(CallBack), IntPtr.Zero)); } private static bool CallBack() { return false; } public byte[] ImgToByt(System.Drawing.Image img) { MemoryStream ms = new MemoryStream(); byte[] imagedata = new Byte[1024 * 9];// null; byte[] imagedata2 = null; img.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); imagedata2 = ms.GetBuffer(); Buffer.BlockCopy(imagedata2, 0, imagedata, 0, imagedata2.Length); return imagedata; } /// <summary> /// 字节流转换成图片 /// </summary> /// <param name="byt">要转换的字节流</param> /// <returns>转换得到的Image对象</returns> public System.Drawing.Image BytToImg(byte[] byt) { MemoryStream ms = new MemoryStream(byt); System.Drawing.Image img = System.Drawing.Image.FromStream(ms); return img; } /// <summary> /// 显示客户端的IP /// </summary> private void SetIPForText(string ip) { this.txtDisplay.Text = "客户端IP:" + ip; } } /// <summary> /// 用于异步Socket操作传递的状态对象 /// </summary> public class StateObject { private const int BUFFER_SIZE = 512; public byte[] Buffer { get; set; } public Socket theSocket { get; set; } /// <summary> /// 构造函数 /// </summary> public StateObject() { this.Buffer = new byte[BUFFER_SIZE]; } } }
4.新建一个叫做KeyboardToolkit.cs的类,用于模拟键盘消息,代码如下。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Input; using System.Runtime.InteropServices; using System.Security; using System.Security.Permissions; using System.ComponentModel; namespace WpfApplication4 { class KeyboardToolkit { internal static class NativeMethods { #region User32 // Various Win32 constants internal const int KeyeventfKeyup = 0x0002; internal const int KeyeventfScancode = 0x0008; internal const int InputKeyboard = 1; // Various Win32 data structures [StructLayout(LayoutKind.Sequential)] internal struct INPUT { internal int type; internal INPUTUNION union; }; [StructLayout(LayoutKind.Explicit)] internal struct INPUTUNION { [FieldOffset(0)] internal MOUSEINPUT mouseInput; [FieldOffset(0)] internal KEYBDINPUT keyboardInput; }; [StructLayout(LayoutKind.Sequential)] internal struct MOUSEINPUT { internal int dx; internal int dy; internal int mouseData; internal int dwFlags; internal int time; internal IntPtr dwExtraInfo; }; [StructLayout(LayoutKind.Sequential)] internal struct KEYBDINPUT { internal short wVk; internal short wScan; internal int dwFlags; internal int time; internal IntPtr dwExtraInfo; }; // Importing various Win32 APIs that we need for input [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] internal static extern int GetSystemMetrics(int nIndex); [DllImport("user32.dll", CharSet = CharSet.Auto)] internal static extern int MapVirtualKey(int nVirtKey, int nMapType); [DllImport("user32.dll", SetLastError = true)] internal static extern int SendInput(int nInputs, ref INPUT mi, int cbSize); [DllImport("user32.dll", CharSet = CharSet.Auto)] internal static extern short VkKeyScan(char ch); #endregion User32 } public static class Keyboard { /// <summary> /// Presses down a key. /// </summary> /// <param name="key">The key to press.</param> public static void Press(Key key) { SendKeyboardInput(key, true); } /// <summary> /// Releases a key. /// </summary> /// <param name="key">The key to release.</param> public static void Release(Key key) { SendKeyboardInput(key, false); } /// <summary> /// Resets the system keyboard to a clean state. /// </summary> public static void Reset() { foreach (Key key in Enum.GetValues(typeof(Key))) { if (key != Key.None && (System.Windows.Input.Keyboard.GetKeyStates(key) & KeyStates.Down) > 0) { Release(key); } } } /// <summary> /// Performs a press-and-release operation for the specified key, which is effectively equivallent to typing. /// </summary> /// <param name="key">The key to press.</param> public static void Type(Key key) { Press(key); System.Threading.Thread.Sleep(100); Release(key); } [PermissionSet(SecurityAction.Assert, Name = "FullTrust")] private static void SendKeyboardInput(Key key, bool press) { PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); permissions.Demand(); NativeMethods.INPUT ki = new NativeMethods.INPUT(); ki.type = NativeMethods.InputKeyboard; ki.union.keyboardInput.wVk = (short)KeyInterop.VirtualKeyFromKey(key); ki.union.keyboardInput.wScan = (short)NativeMethods.MapVirtualKey(ki.union.keyboardInput.wVk, 0); int dwFlags = 0; if (ki.union.keyboardInput.wScan > 0) { dwFlags |= NativeMethods.KeyeventfScancode; } if (!press) { dwFlags |= NativeMethods.KeyeventfKeyup; } ki.union.keyboardInput.dwFlags = dwFlags; ki.union.keyboardInput.time = 0; ki.union.keyboardInput.dwExtraInfo = new IntPtr(0); if (NativeMethods.SendInput(1, ref ki, Marshal.SizeOf(ki)) == 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } } } }
先打开服务端
在手机客户端,输入服务器端的IP地址,点击连接,连接成功后就可以进行操作了