天天看點

【WP8.1開發】用手機來控制電腦的多媒體播放

為了用電腦看電影時友善控制,我就突發其想,做一個手機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(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();
        }      

最後看看結果:

【WP8.1開發】用手機來控制電腦的多媒體播放
【WP8.1開發】用手機來控制電腦的多媒體播放

源代碼下載下傳:https://files.cnblogs.com/tcjiaan/RemoteMediaControlSlsn.zip