[C#]通过HttpListener联动IOS快捷指令操作本地计算机
这篇文章内容已经光速过时,因为后面发现这个功能完全可以用Quicker简单地实现,如果有在使用Quicker的话,可以参考新的文章[Quicker]通过联动IOS快捷指令关闭显示器

我可真是受够了这群半夜踩键盘把电脑屏幕踩亮的臭猫咪了!

——忍无可忍·无能狂怒·我

为什么要写这玩意

想必你也一定有过这样的烦恼,当你躺在床上社会性睡眠的时候,忽然猫咪跳到了桌子上,把熄屏的电脑屏幕踩亮

而你恰好:

  • 晚上睡觉没有关闭电脑的习惯
  • 上床之前没有手动关闭显示器电源的习惯
  • 显示器的自动休眠时间非常长
  • 浑身懒癌发作不愿意爬起来去关屏幕
  • 并且你养的懒猫也不愿意抬抬爪子帮你关一下显示器

……好吧,我相信大多数人应该不会有这样的烦恼,但是总之,在半夜被显示器最大亮度糊脸失眠几十次以后,我终于忍无可忍,写出了这么个小玩意。

实现方式

虽然家里用的是米家的智能家居,但是很不幸,米家并没有提供PC的可自定义操作客户端,所以用小爱同学关显示屏这条路是走不通了,拿智能插座控制显示器的话,等于是直接给显示器拔电源,伤显示器不说,再想用的时候还得让小爱同学重新给显示器通电。

最好的方法还是让操作系统自己熄屏,这样再使用的时候,只要晃动鼠标就能自然唤醒。想要通过代码关闭显示屏并不难,主要的问题就是如何触发了。好在IOS的快捷指令现在足够强大,可以执行HTTP请求,并且可以被Siri语音调用,甚至可以用Apple Watch脱离手机执行。正好本来我在每天晚上睡觉时就需要佩戴Apple Watch进行睡眠监测,那么实现起来就很简单了。

这里通过.net自带的HttpListener在本地30001端口开启监听,接到特定HTTP请求后发送消息关闭电脑的显示器。

首先新建一个HTTPServer.cs实现监听部分功能

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows;

namespace ScreenOff
{
    public delegate void ReceivedRequestEventHandler(string payload);
    public delegate void OnExceptionEventHandler(Exception ex);
    class HttpServer
    {
        private Thread _serverThread;
        private HttpListener _listener;
        public int Port { get; private set; }

        public event ReceivedRequestEventHandler ReceivedCommandRequest;
        public event OnExceptionEventHandler OnException;

        /// <summary>
        ///     在指定端口启动监听
        /// </summary>
        /// <param name="port">要启动的端口</param>
        public HttpServer(int port)
        {
            Initialize(port);
        }

        /// <summary>
        ///     停止监听并释放资源
        /// </summary>
        public void Stop()
        {
            //_serverThread.Abort();
            _listener.Stop();
        }

        private void Listen()
        {
            try
            {
                _listener = new HttpListener();
                _listener.Prefixes.Add("http://*:" + Port + "/");
                _listener.Start();
            }
            catch (Exception ex)
            {
                OnException?.Invoke(ex);
                return;
            }

            ThreadPool.QueueUserWorkItem(o => {
                try
                {
                    while (_listener.IsListening)
                        ThreadPool.QueueUserWorkItem(c =>
                        {
                            if (c is not HttpListenerContext context)
                                throw new ArgumentNullException(nameof(context));
                            try
                            {
                                DoAction(ref context);
                            }
                            catch (Exception ex)
                            {
                                MessageBox.Show(ex.Message);
                                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                            }
                            finally
                            {
                                context.Response.OutputStream.Close();
                            }
                        }, _listener.GetContext());
                }
                catch
                {
                    // ignored
                }
            });
        }

        private void DoAction(ref HttpListenerContext context)
        {

            var command = new StreamReader(context.Request.InputStream, Encoding.UTF8).ReadToEnd();

            ReceivedCommandRequest?.Invoke(command);

            var buf = Encoding.UTF8.GetBytes(command);
            context.Response.ContentLength64 = buf.Length;
            context.Response.OutputStream.Write(buf, 0, buf.Length);
            context.Response.StatusCode = (int)HttpStatusCode.OK;
            context.Response.OutputStream.Flush();
        }

        private void Initialize(int port)
        {
            Port = port;
            _serverThread = new Thread(Listen);
            _serverThread.Start();
        }

    }
}

这个模块在鲶鱼精邮差里已经用过了。虽然其实用Nancy之类的包也能方便地实现需要的功能,不过这里就不大炮打蚊子了。

MainWindow.cs中初始化HttpServer,并将接收到指令时进行的操作和错误处理事件委托给ReceivedCommandRequest和OnException

private void ServerStart(object sender = null, EventArgs e = null)
{
    try
    {
        _httpServer = new HttpServer(30001);
        _httpServer.ReceivedCommandRequest += DoTextCommand;
        _httpServer.OnException += OnException;
    }
    catch (Exception ex)
    {
        OnException(ex);
    }
}
private void DoTextCommand(string command)
{
    switch (command.ToLower(CultureInfo.CurrentCulture))
    {
        case "screen=off":
            _ = PostMessage(-1, 0x0112, 0xF170, 2);//hwnd,WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)2);
            return;
        default:
            return;
    }
}
private void OnException(Exception ex)
{
    string errorMessage = $"无法在{_httpServer.Port}端口启动监听\n{ex.Message}";
    MessageBox.Show(errorMessage);
}

这就几乎是全部的功能性代码了,完整代码详见Natsukage/ScreenOff: 通过IOS快捷指令联动关闭显示器 (github.com)

这玩意能干什么

配合IOS的捷径功能,就可以实现对着Apple Watch或者手机 “Hi Siri,关闭显示器” 隔空语音关闭显示器了

当然关闭显示器这个动作实际上只需要一条PostMessage指令就可以实现。这里完全可以自由扩展,根据指令的不同来执行不同的操作,只是我目前还完全没有想到其他需求。

毕竟社畜除了睡觉以外,其他时间都是坐在电脑前的。

在IOS的快捷指令中按照如图所示进行设置,通过HTTP请求将screen=off参数Post至PC的30001端口即可触发软件中写好的指令,通过发送消息关闭显示器。

重要注意事项

这里注意,由于IOS的捷径似乎并不能随心所欲的设定Post的内容,必须是按照标准格式?A=a&B=b&C=c这样进行提交。因此其实用Get方法可能更自由一些。Post方法的话,指令的内容就只能是A=B的这种样式。例如这里就设定为了screen=off。

这里增加了一条判断,即只有当当前wifi是自家wifi时候才会执行指令,实际是没必要的。而且如果这么判断了,后面还需要加一条判断,即如果设备是apple watch的话 那么就不考虑wifi名执行快捷指令。因为手表在和手机连接时,自己是没有连接wifi的,所以获取wifi名称对比的这步判断也会直接判非。

实际真正重要的只有第四行的获取文本内容这条,只要这一句指令就足以控制显示器关闭了。

此外,这里的监听注册了http://*:30001/,而按照windows默认的安全规则,注册localhost以外的监听是需要管理员权限的。因此要么每次都使用管理员权限启动程序,要么用管理员权限打开CMD,输入netsh http add urlacl url=http://*:30001/ user=Everyone授权所有用户即可一劳永逸

此外,如果测试时快捷指令卡住,记得关掉Windows防火墙再试。windows自带防火墙默认是会阻止这样的请求的,而且阻止时不会有提示。我也没有在程序里自动添加防火墙规则,你需要手动放行TCP 30001端口才可以。

本文源码链接:
GitHub last commit

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇