为了用电脑看电影时方便控制,我就突发其想,做一个手机app来通过无线网络远程调节电脑上的音量。后来进行尝试成功后,我就想,光是调音量似乎单调了些,就把播放/暂停,上一首,下一首,等多媒体控制功能也加上,这样好玩一点。

下面向大家简单介绍一下原理,整个解决方案的源代码我会共享给大家,以作参考。

先说服务器,因为控制命令比较简单,我直接用一个WPF应用程序来完成,这样方便运行,用socket来通信比较麻烦,我就用WCF来做服务,使用WebServiceHoset,让WP手机客户端用HTTP-POST的方式来调用。

这个相信大家都会,还有一个核心,就是如何控制系统的多媒体功能? 其实大家应该发现在你的笔记本键盘上,有一排功能按钮,可以通过按这些键来调整音量,控制播放、上一首歌曲等,还有各种功能开关,比如打开/关闭无线功能等。

也就是说,只要代码能够模拟发出这些按键就可以实现控制了,这就要用到Win32 API中的SendInput函数。在最初尝试时,我将SendInput函数导进托管代码中,但调用没有反应,不知道是不是我Import不对。

后来,我干脆用C++来写一个dll,把各个控制操作都用独立的导出函数来包装,再把自己写的dll中的导出函数在托管项目中DllImport。

自己编写的dll的头文件如下:

#pragma once
#include "stdafx.h" /* 增大音量 */
extern "C" __declspec(dllexport) void volume_up(); /* 减小音量 */
extern "C" __declspec(dllexport) void volume_down(); /* 静音/恢复 */
extern "C" __declspec(dllexport) void volume_mute(); /* 下一首 */
extern "C" __declspec(dllexport) void media_next_track(); /* 上一首 */
extern "C" __declspec(dllexport) void media_prev_track(); /* 播放/暂停 */
extern "C" __declspec(dllexport) void media_play_pause(); /* 停止 */
extern "C" __declspec(dllexport) void media_stop(); void send_input_core(WORD vkey);

__declspec(dllexport)注明的函数为导出函数,即可以在其他代码中使用,而最后的send_input_core函数只供dll内部使用,就不再导出了。

注意要加上extern "C",让函数以C语言的规范进行导出,由于C语言不支持函数重载,在编译时编译器不会改变函数的名字,所以加上extern "C"让托管代码在Dll Import时可以直接使用函数的原名,这样就不需要编写复杂的模块定义文件来重命名符号了,这种方法写dll是最方便的。

下面在cpp文件中实现send_input_core函数:

void send_input_core(WORD vkey) {
INPUT input;
input.type = INPUT_KEYBOARD;
input.ki.dwFlags = NULL;
input.ki.wVk = vkey;
SendInput(, &input, sizeof(INPUT));
}

用vkey参数来接收要模拟的按键,这样就不用重复写SendInput的调用代码了,其余的函数可以直接调用该函数,然后传递按键值就可以了。

void volume_up() {
send_input_core(VK_VOLUME_UP);
} void volume_down()
{
send_input_core(VK_VOLUME_DOWN);
} void volume_mute() {
send_input_core(VK_VOLUME_MUTE);
} void media_next_track()
{
send_input_core(VK_MEDIA_NEXT_TRACK);
} void media_prev_track() {
send_input_core(VK_MEDIA_PREV_TRACK);
} void media_play_pause() {
send_input_core(VK_MEDIA_PLAY_PAUSE);
} void media_stop()
{
send_input_core(VK_MEDIA_STOP);
}

完成dll后,就导入到C#托管代码中:

namespace DllAPIs
{
using System;
using System.Runtime.InteropServices; public sealed class MediaControlAPIs
{
// dll文件的名字
const string DLL_NAME = "mediactrllib.dll"; #region 从dll导入的API [DllImport(DLL_NAME)]
extern static void volume_up(); [DllImport(DLL_NAME)]
extern static void volume_down(); [DllImport(DLL_NAME)]
extern static void volume_mute(); [DllImport(DLL_NAME)]
extern static void media_next_track(); [DllImport(DLL_NAME)]
extern static void media_prev_track(); [DllImport(DLL_NAME)]
extern static void media_play_pause(); [DllImport(DLL_NAME)]
extern static void media_stop(); #endregion #region 公共方法 /// <summary>
/// 增大音量
/// </summary>
public static void VolumeUp()
{
volume_up();
} /// <summary>
/// 减小音量
/// </summary>
public static void VolumeDown()
{
volume_down();
} /// <summary>
/// 静音/恢复
/// </summary>
public static void VolumeMute()
{
volume_mute();
} /// <summary>
/// 下一曲目
/// </summary>
public static void MediaNextTrack()
{
media_next_track();
} /// <summary>
/// 上一曲目
/// </summary>
public static void MediaPrevTrack()
{
media_prev_track();
} /// <summary>
/// 播放/暂停
/// </summary>
public static void MediaPlayPause()
{
media_play_pause();
} /// <summary>
/// 停止播放
/// </summary>
public static void MediaStop()
{
media_stop();
} #endregion
}
}

我想,这样导入要比直接导入Win32 API要简便得多。

接着,完成WCF服务。

    [ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(UriTemplate = "invoke?action={act}")]
void Invoke(string act);
} public class WcfService : IService
{
public void Invoke(string act)
{
switch (act)
{
case "vu": //增大音量
DllAPIs.MediaControlAPIs.VolumeUp();
break;
case "vd": //减小音量
DllAPIs.MediaControlAPIs.VolumeDown();
break;
case "vm": //静音/恢复
DllAPIs.MediaControlAPIs.VolumeMute();
break;
case "mn": //下一首
DllAPIs.MediaControlAPIs.MediaNextTrack();
break;
case "mp": //上一首
DllAPIs.MediaControlAPIs.MediaPrevTrack();
break;
case "mpp": //播放/暂停
DllAPIs.MediaControlAPIs.MediaPlayPause();
break;
case "ms": //停止
DllAPIs.MediaControlAPIs.MediaStop();
break;
}
}
}

其他代码就不介绍了,大家看源码吧。

最后是实现手机客户端,其实就是用HTTP-POST向刚才的WCF服务发数据即可。

        private async Task PostActionAsync(string action)
{
string postUri = string.Format("http://{0}:{1}/invoke?action={2}", HostName, Port, action);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(postUri);
request.Method = "POST";
var rep = await request.GetResponseAsync();
}

最后看看结果:

 

源代码下载:http://files.cnblogs.com/tcjiaan/RemoteMediaControlSlsn.zip

【WP8.1开发】用手机来控制电脑的多媒体播放的更多相关文章

  1. Andy - 又一款速度流畅的免费安卓 Android 模拟器 (支持手机无线控制电脑模拟器)

    随着 Genymotion.BlueStacks 等电脑上的 Android 模拟器流行起来之后,似乎很多人都发现在电脑上运行使用安卓APP软件.畅玩手机游戏确实很有乐趣. 今天我们又发现了一款全新免 ...

  2. 苹果如何控制android手机,安卓手机怎么控制苹果?

    小编经常通过手机远程控制别人手机,帮助他人解决一些电脑问题,另外还经常需要通过远程电脑控制服务器,管理脚本之家的服务器等等,可能这些对大家都没有什么诱惑,今天笔者为大家带来一个非常有趣的手机控制电脑的 ...

  3. 自己动手开发手机APP控制西门子200smart 教程(原创干货)

    自己动手开发手机APP控制西门子200smart 教程(原创干货)   自己动手开发手机APP控制西门子200smart 教程(原创干货) 2020-02-09 19:06:45 自己动手开发手机AP ...

  4. 手机控制电脑,在WIFI局域网下(关机,重启,遥控)

    这个软件叫百变遥控:http://blog.sina.com.cn/s/blog_9abc7dbc0101hmut.html 今天周末,在家里看电影,家里用的是台式电脑,我自己买了一个投影仪来专门看视 ...

  5. 手机控制电脑定时关机,重启WiFi

    需求 晚上上床,电脑开着WiFi让手机上网.要么上床之前就给电脑设置定时关机:要么就电脑开通宵:要么就待会下来关电脑.这3种情况都非常不好,要么麻烦,要么浪费. 最无奈的是电脑刚开好WiFi,上床后才 ...

  6. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 网格系统实例:手机、平板电脑、台式电脑

    <!DOCTYPE html> <html> <head> <title>Bootstrap 实例 - 手机.平板电脑.台式电脑</title&g ...

  7. 1_1准备工作[wp8特色开发与编程技巧]

    1准备工作 大家好,我是徐文康,今天我要开始带大家玩转windowsphone8 app的开发 在这一套视频中,我将带大家从零开始学习编程.在互联网时代熟悉编程是非常有必要的.差异化竞争将变成趋势,那 ...

  8. wp8.1 VS2013部署手机发生 DEP6100 6200错误

    wp8.1 VS2013部署手机发生 DEP6100 6200错误 ,提示未发现手机. 可能是数据线有问题 建议更换 本人的电脑有很多次,提示DEP6100 6200错误,每一次都需要重做系统.. 然 ...

  9. C# Windows Phone 8 WP8 高级开发,制作不循环 Pivot ,图片(Gallery)导览不求人! 内附图文教学!!

    原文:C# Windows Phone 8 WP8 高级开发,制作不循环 Pivot ,图片(Gallery)导览不求人! 内附图文教学!! 一般我们在开发Winodws Phone APP 的时候往 ...

随机推荐

  1. js判断图片是否加载完成

    var img = new Image(); //新建一个图片对象:img.src = ...; //图片地址是你准备要加载的地址:if(img.complete){ //表示图片已经加载完成}

  2. 解决python编码格式错误问题

    一:前言 遇到问题:print输入汉字时提示错误信息 UnicodeDecodeError: 'ascii' codec can't decode byte 0x?? in position 1: o ...

  3. Files 的值“<<<<<<< .mine”无效。路径中具有非法字符

    解决冲突,告诉SVN这个问题已解决(Resolved). 一般更简单些:在你的工程OBJ/DEBUG目录下,找到 工程名.csproj.FileListAbsolute.txt的文件打开并删除含有'& ...

  4. display:none与visible:hidden的区别

    display:none和visible:hidden都能把网页上某个元素隐藏起来,但两者有区别: display:none ---不为被隐藏的对象保留其物理空间,即该对象在页面上彻底消失,通俗来说就 ...

  5. java排序学习笔记

    前面写了js的排序实现,总得玩玩java的哈. 同样,冒泡.选择.快速(这三个之前实现过也写过文章).堆排序,然后做比较. 主要遇到的难点: - -||想轻松点写个封装计时的逻辑,不想每调用一个排序就 ...

  6. 为什么我如此热爱这样一个比赛(转自vici)

    为什么我如此的热爱这样一个比赛呢?因为它总能带给我一个目标,让我去努力实现它.因为可以看到胜利的希望,于是不断的去追逐.虽然其中的过程可能是比较艰辛的.   对于天才选手,作为天生的冠军,大概凭借天赋 ...

  7. 解决:View调用invalidate()后不刷新onDraw()

    近来学android图片处理,按照例子来,自定义一个View,之后在Activity里面手动调用该View的invalidate()后,一直无法刷新onDraw() 上网搜了一下,有两种解决办法: 一 ...

  8. DAO模型

    DAO模型 前面我们在使用JDBC时解决的都是一些很简单的问题,例如登录,注册等等,所以有些例直接把代码写在了main方法中.这种写法很容易出现代码臃肿,耦合度高,不能模块化开发等诸多弊端,特别是将来 ...

  9. JsonUtil

    package com.test.base.util.json; import java.beans.IntrospectionException; import java.beans.Introsp ...

  10. Unity3D 模型导入Error

    在导入3d max模型的时候报错: 3ds Max could not be found.Make sure that 3ds Max is installed and the max file ha ...