爲了用電腦看電影時方便控制,我就突發其想,作一個手機app來經過無線網絡遠程調節電腦上的音量。後來進行嘗試成功後,我就想,光是調音量彷佛單調了些,就把播放/暫停,上一首,下一首,等多媒體控制功能也加上,這樣好玩一點。服務器
下面向你們簡單介紹一下原理,整個解決方案的源代碼我會共享給你們,以做參考。網絡
先說服務器,由於控制命令比較簡單,我直接用一個WPF應用程序來完成,這樣方便運行,用socket來通訊比較麻煩,我就用WCF來作服務,使用WebServiceHoset,讓WP手機客戶端用HTTP-POST的方式來調用。app
這個相信你們都會,還有一個核心,就是如何控制系統的多媒體功能? 其實你們應該發如今你的筆記本鍵盤上,有一排功能按鈕,能夠經過按這些鍵來調整音量,控制播放、上一首歌曲等,還有各類功能開關,好比打開/關閉無線功能等。socket
也就是說,只要代碼可以模擬發出這些按鍵就能夠實現控制了,這就要用到Win32 API中的SendInput函數。在最初嘗試時,我將SendInput函數導進託管代碼中,但調用沒有反應,不知道是否是我Import不對。async
後來,我乾脆用C++來寫一個dll,把各個控制操做都用獨立的導出函數來包裝,再把本身寫的dll中的導出函數在託管項目中DllImport。函數
本身編寫的dll的頭文件以下:post
#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內部使用,就再也不導出了。spa
注意要加上extern "C",讓函數以C語言的規範進行導出,因爲C語言不支持函數重載,在編譯時編譯器不會改變函數的名字,因此加上extern "C"讓託管代碼在Dll Import時能夠直接使用函數的原名,這樣就不須要編寫複雜的模塊定義文件來重命名符號了,這種方法寫dll是最方便的。code
下面在cpp文件中實現send_input_core函數:orm
void send_input_core(WORD vkey) { INPUT input; input.type = INPUT_KEYBOARD; input.ki.dwFlags = NULL; input.ki.wVk = vkey; SendInput(1, &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