gyro永不抽风

ああああああああああああああああおおおおおおおおおおおおおおおお

题目链接

https://iai.sh.cn/problem/88

题目大意

给定$n$个区间,请挑出$k$个区间,使得他们的交集长度达到最大。区间的长度定义为这个区间的右端点和左端点的差。

题解

  • 不难得出,我们可以先按照区间的开始进行排序。
  • 然后维护一个有$k$个元素的堆(小根堆),里面存的是区间的结束
  • 统计答案:当堆里有$k$个元素时,答案就是堆顶减去当前区间的开始(因为区间的开始已经是升序的了)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream> 
#include <algorithm>
#include <queue>

using namespace std;

const int maxn = 100005;

pair<int, int> t[maxn];
priority_queue<int, vector<int>, greater<int> > q;

int main() {
int n, k;
int ans = 0;
cin >> n >> k;
for (int i = 0; i < n; i ++)
cin >> t[i].first >> t[i].second;
sort(t, t + n);
for (int i = 0; i < n; i ++) {
q.push(t[i].second);
if (q.size() > k)
q.pop();
if (q.size() == k)
ans = max(ans, q.top() - t[i].first);
}
cout << ans << endl;
return 0;
}

介绍

之前想到:博客一次写作,多地发布,于是边有了这个项目:选择性同步Hexo与博客园。

GitHub链接:https://github.com/JeffersonQin/hexo-cnblogs-sync

魔改Next模板,增加标题栏提醒

themes/next/layout/_macro/post.swig中,选择合适的地方加入:

1
2
3
4
5
6
7
8
9
{% if post.cnblogs %}
<br>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="fas fa-rss"></i>
</span>
<span class="post-meta-item-text" id="cnblogs_sync_text"><a href="https://www.cnblogs.com/jeffersonqin/">博客园</a> 同步已开启</span>
</span>
{% endif %}

这样的话,如果要同步,只需要控制文件头就可以了,类似于本文:

1
2
3
4
5
6
7
8
9
10
11
12
13
---
title: 博客园与Hexo同步发布的方法
date: 2020-09-15 00:31:57
tags:
- Hexo
- Blog
- cnblogs
- python
- html
- css
categories: Hexo
cnblogs: true
---

BS4微调Hexo生成的HTML

先扫描一遍public文件夹里的输出文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
orig_dir = "../public/"
repo_dir = "../public_cnblogs/"
ignore_files = ["../public/index.html"]
ignore_dirs = ["../public/page/", "../public/archives"]
# Filtering the files needed to post
for root, dirs, files in os.walk(orig_dir):
for file in files:
try:
file_name = str(file)
file_path = os.path.join(root, file)
file_dir = root.replace('\\', '/')
file_path = file_path.replace('\\', '/')

flag = True

for ignore_dir in ignore_dirs:
if file_dir.startswith(ignore_dir):
flag = False
break
for ignore_file in ignore_files:
if file_path == ignore_file:
flag = False
break

if (file_name == 'index.html' and flag):
index_files.append(file_path)
print('\033[42m\033[37m[LOGG]\033[0m File Detected:', file_path)

except Exception as e:
raise e

print('\033[44m\033[37m[INFO]\033[0m File Detection Ended.')

然后使用BS4来操作HTML和CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# Filter the articles that are to be synced

resource_dict = {}

for index_file in index_files:
post_body = ""
with open(index_file, 'r', encoding='utf-8') as f:
html_doc = f.read()
soup = BeautifulSoup(html_doc, "html.parser")
check_msg = soup.select('span[id=cnblogs_sync_text]')
if (len(check_msg) == 0): continue
post_body_list = soup.select('div[class=post-body]')
if (len(post_body_list) == 0): continue
print('\033[42m\033[37m[LOGG]\033[0m Target Detected:', index_file)
for child in soup.descendants:
if (child.name == 'img'):
if ('data-original' in child.attrs and 'src' in child.attrs):
child['src'] = 'https://gyrojeff.moe' + child['data-original']
elif ('src' in child.attrs):
child['src'] = 'https://gyrojeff.moe' + child['src']
if (child.name == 'a'):
if ('href' in child.attrs):
if (str(child['href']).startswith('/') and not str(child['href']).startswith('//')):
child['href'] = 'https://gyrojeff.moe' + child['href']
post_body = soup.select('div[class=post-body]')[0]
math_blocks = post_body.select('script[type="math/tex; mode=display"]')
for math_block in math_blocks:
math_string = str(math_block).replace('<script type="math/tex; mode=display">', '<p>$$').replace('</script>', '\n$$</p>')
math_block.replace_with(BeautifulSoup(math_string, 'html.parser'))
save_dir = os.path.join(repo_dir, index_file[len(orig_dir):-len("index.html")])
if not os.path.exists(save_dir): os.makedirs(save_dir)
copyright_div = str(soup.select('div[class=my_post_copyright]')[0])
with open(save_dir + 'index.html', 'w', encoding='utf-8') as f:
f.write(header + '\n' + copyright_div + '\n' + str(post_body))
tags = soup.select('div[class=post-tags]')
tags_text = []
if len(tags) != 0:
tags_div = tags[0].select('a')
if len(tags_div) > 0:
for tag in tags_div:
tags_text.append(tag.contents[1][1:])

resource_dict[save_dir + 'index.html'] = {
'tags': tags_text,
'title': soup.select('meta[property="og:title"]')[0]['content']
}
print('\033[44m\033[37m[INFO]\033[0m File Generated:', save_dir + 'index.html')

print('\033[44m\033[37m[INFO]\033[0m File Generation Ended.')

上面的大多数代码基本上都需要具体情况具体分析,分析生成的HTML和我们需要的代码之间的关系。值得注意的是,math_blocks的那一段代码是巧妙地解决数学公式mathjax只渲染到<script>的问题的。(直接匹配再用$$$$替换,这样可以直接使用博客园的markdown进行渲染,毕竟markdown可以兼容html)这里面,header是我根据这套Next主题自己扒的,源码在我的GitHub上有(链接在本文文末)

MetaWeblog发布博文

博客园可以使用MetaWeblog接口,不过categories有点问题,不太能使用。

原作:https://github.com/1024th/cnblogs_githook

但是在删除接口方面存在问题,这里进行了更改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
import xmlrpc.client as xmlrpclib
import json
import datetime
import time
import getpass

'''
配置字典:
type | description(example)
str | metaWeblog url, 博客设置中有('https://rpc.cnblogs.com/metaweblog/1024th')
str | appkey, Blog地址名('1024th')
str | blogid, 这个无需手动输入,通过getUsersBlogs得到
str | usr, 登录用户名
str | passwd, 登录密码
str | rootpath, 博文存放根路径(添加git管理)
'''

'''
POST:
dateTime dateCreated - Required when posting.
string description - Required when posting.
string title - Required when posting.
array of string categories (optional)
struct Enclosure enclosure (optional)
string link (optional)
string permalink (optional)
any postid (optional)
struct Source source (optional)
string userid (optional)
any mt_allow_comments (optional)
any mt_allow_pings (optional)
any mt_convert_breaks (optional)
string mt_text_more (optional)
string mt_excerpt (optional)
string mt_keywords (optional)
string wp_slug (optional)
'''

class MetaWebBlogClient():
def __init__(self, configpath):
'''
@configpath: 指定配置文件路径
'''
self._configpath = configpath
self._config = None
self._server = None
self._mwb = None

def createConfig(self):
'''
创建配置
'''
while True:
cfg = {}
for item in [("url", "MetaWebBlog URL: "),
("appkey", "博客地址名(网址的用户部分): "),
("usr", "登录用户名: ")]:
cfg[item[0]] = input("输入" + item[1])
cfg['passwd'] = getpass.getpass('输入登录密码: ')
try:
server = xmlrpclib.ServerProxy(cfg["url"])
userInfo = server.blogger.getUsersBlogs(cfg["appkey"], cfg["usr"], cfg["passwd"])
print(userInfo[0])
# {'blogid': 'xxx', 'url': 'xxx', 'blogName': 'xxx'}
cfg["blogid"] = userInfo[0]["blogid"]
break
except:
print("发生错误!")
with open(self._configpath, "w", encoding="utf-8") as f:
json.dump(cfg, f, indent=4, ensure_ascii=False)

def existConfig(self):
'''
返回配置是否存在
'''
try:
with open(self._configpath, "r", encoding="utf-8") as f:
try:
cfg = json.load(f)
if cfg == {}:
return False
else:
return True
except json.decoder.JSONDecodeError: # 文件为空
return False
except:
with open(self._configpath, "w", encoding="utf-8") as f:
json.dump({}, f)
return False

def readConfig(self):
'''
读取配置
'''
if not self.existConfig():
self.createConfig()

with open(self._configpath, "r", encoding="utf-8") as f:
self._config = json.load(f)
self._server = xmlrpclib.ServerProxy(self._config["url"])
self._mwb = self._server.metaWeblog

def getUsersBlogs(self):
'''
获取博客信息
@return: {
string blogid
string url
string blogName
}
'''
userInfo = self._server.blogger.getUsersBlogs(self._config["appkey"], self._config["usr"], self._config["passwd"])
return userInfo

def getRecentPosts(self, num):
'''
读取最近的博文信息
'''
return self._mwb.getRecentPosts(self._config["blogid"], self._config["usr"], self._config["passwd"], num)

def newPost(self, post, publish):
'''
发布新博文
@post: 发布内容
@publish: 是否公开
'''
while True:
try:
postid = self._mwb.newPost(self._config['blogid'], self._config['usr'], self._config['passwd'], post, publish)
break
except:
time.sleep(5)
return postid

def editPost(self, postid, post, publish):
'''
更新已存在的博文
@postid: 已存在博文ID
@post: 发布内容
@publish: 是否公开发布
'''
self._mwb.editPost(postid, self._config['usr'], self._config['passwd'], post, publish)

def deletePost(self, postid, publish):
'''
删除博文
'''
return self._server.blogger.deletePost(self._config['appkey'], postid, self._config['usr'], self._config['passwd'], publish)

def getCategories(self):
'''
获取博文分类
'''
return self._mwb.getCategories(self._config['blogid'], self._config['usr'], self._config['passwd'])

def getPost(self, postid):
'''
读取博文信息
@postid: 博文ID
@return: POST
'''
return self._mwb.getPost(postid, self._config['usr'], self._config['passwd'])

def newMediaObject(self, file):
'''
资源文件(图片,音频,视频...)上传
@file: {
base64 bits
string name
string type
}
@return: URL
'''
return self._mwb.newMediaObject(self._config['blogid'], self._config['usr'], self._config['passwd'], file)

def newCategory(self, categoray):
'''
新建分类
@categoray: {
string name
string slug (optional)
integer parent_id
string description (optional)
}
@return : categorayid
'''
return self._server.wp.newCategory(self._config['blogid'], self._config['usr'], self._config['passwd'], categoray)

GitPython版本管理

在生成新的html文档之前,先删除旧的(除了.git目录):

1
2
3
4
5
6
if os.path.exists(repo_dir):
for sub_dir in os.listdir(repo_dir):
if os.path.isdir(os.path.join(repo_dir, sub_dir)) and sub_dir != '.git':
shutil.rmtree(os.path.join(repo_dir, sub_dir))
if os.path.isfile(os.path.join(repo_dir, sub_dir)):
os.remove(os.path.join(repo_dir, sub_dir))

下面是封装好的gitpythonclass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import git
import os

class RepoScanner():

def __init__(self, repopath):
self._root = repopath
try:
self._repo = git.Repo(self._root)
except:
# TODO: color log
print('\033[44m\033[37m[INFO]\033[0m Fail to open git repo at: %s' % (repopath))
while (True):
in_content = input('\033[44m\033[37m[INFO]\033[0m Try to create a new repo? [y/n]: ')
if (in_content == 'y' or in_content == 'Y'):
break
if (in_content == 'n' or in_content == 'N'):
return
try:
self._repo = git.Repo.init(path=self._root)
except Exception as e:
raise e

def getNewFiles(self):
return self._repo.untracked_files

def scan(self):
diff = [ item.a_path for item in self._repo.index.diff(None) ]
deleted = []
changed = []
for item in diff:
if not os.path.exists(os.path.join(self._root, item)):
deleted.append(item)
else: changed.append(item)
return {'new': self.getNewFiles(), 'deleted': deleted, 'changed': changed}

剩下还有一些细节:包括本地化文章ID等就不在本文内过多赘述,这里只提供大致思路,具体代码详见Github

注意

如果发现中文转码出现问题,记得运行下面这行命令:

1
git config --global core.quotepath false

GitHub

https://github.com/JeffersonQin/hexo-cnblogs-sync

自从被生下来开始,就是欠别人的。

要是没被生下来就好了呢。

自己如何努力到头来会被一句“还不是因为我 xxx”全部掩盖

就算努力让自我提高了

这样活着好累啊。

好累啊。

提前社会教育对吧。

属实强。

但做了那么多有用吗。

结果还不是一样吗。

达不到目的发泄在我身上。

我从一开始就没什么指望。

从最开始就已经看到了结束。

所以我什么都没说。

但是不需要我说还是发生了。

所以都无所谓了。


本来都决定了要好好学习的呢。

被骂了以后又不想学了。

明明是自己的事情才对。

为什么那么容易被别人影响呢。

好烦啊。

为什么出生之前没有人询问过我的意见问我想不想被生下来。

没被生下来就好了呢。

什么都不用做什么都不用想。

消失了就好了呢。

引言

之前使用Valine,但是一直苦于别人评论我并不能第一时间知道,便倒腾了一个早上的Valine邮件提醒功能。但但但但是,免费版的Valine会强制休眠,我便另寻他道。

Gitalk

项目主页:https://github.com/gitalk/gitalk

引用:

Gitalk is a modern comment component based on GitHub Issue and Preact.

Hexo & Next 适配

打开themes/next/_config.yml,找到Gitalk部分:

1
2
3
4
5
6
7
8
9
10
11
12
gitalk:
enable: true
github_id: <repo-owner> # GitHub repo owner
repo: <repo-name> # Repository name to store issues
client_id: <client-id> # GitHub Application Client ID
client_secret: <app-secret> # GitHub Application Client Secret
admin_user: <admin> # GitHub repo owner and collaborators, only these guys can initialize gitHub issues
distraction_free_mode: true # Facebook-like distraction free mode
# Gitalk's display language depends on user's browser or system environment
# If you want everyone visiting your site to see a uniform language, you can set a force language value
# Available values: en | es-ES | fr | ru | zh-CN | zh-TW
language: zh-CN

  • github_id:储存项目的用户的GitHub用户名
  • repo:储存issuerepo
  • client_id & client_secret这里申请

submodule介绍

submodule,即子模块,在项目中添加一个submodule就相当于引用了一个别的repo,记下来我将分别针对不同的情形展开讨论

添加submodule

main project当中:

1
git submodule add <submodule_url> <path>

其中,submodule_urlsubmodulerepo地址,path为接下来clone的地址。运行之后,会发现目录下多出了.gitmodule文件。

已经clone了一个项目,添加为submodule

和上面一样:

1
git submodule add <submodule_url> <path>

改动了submodule内的代码

首先明确一个概念:submodule是一个独立、完整的repo。这意味着,你可以像往常一样在submodule的根目录下进行add/commit/push,这都是完全没有问题的。但是,如果submodule没有commit,主项目是无法commit的,所以如果要commit主项目,首先要确保submodule内各个项目都没有问题。在此之外,当submodule内已经好了之后,我们便可以commit主项目,并可以发现主项目当中submodule是作为一个整体来计算的。

删除submodule

写在一个子模块:

1
git submodule deinit

如果:
1
git submodule deinit --force

那么即使本地repo有未提交的更改也会被移除. 上述命令是删除.git/config中的配置文件,若要继续删除本地的文件:
1
git rm <submodule>

来删除。

操作完毕之后我们发现主项目就有更改了,进行commit来提交更改:

1
git commit -m "Deleted the submodule project."

submodule有远程更新

1
2
cd <path>
git pull origin master

如果有多个submodule,可以:

1
git submodule foreach 'git pull origin master'

clone带有submodule的项目

1
git clone <repo-url> --recurse-submodules

或者:

1
2
3
git clone <repo-url>
git submodule init
git submodule update

绪论

之前在做文件备份的时候,对于博客文件夹总是十分烦恼。由于一直在更改,百度云热备可能会出问题,然而我这个人冷备这种事绝对会忘,遂出现了本文。

思路

一开始我是准备整个项目直接扔到GitHub上面去的,但是很不巧,出现了报错,提示了submodule问题。出现了这个问题之后,我就决定好好整理之前杂乱不堪的工程文件。

步骤

第一步 重新Hexo init

按照之前的教程(在Hexo分类下的建站指南),新建Hexo项目

第二步 重新导入之前的配置

包括_config.yml等,同步一下,注意版本之间的差异

第三步 git init

在博客的工程文件下,直接进行git init,到现在位置的话应该还不会产生问题。

第四步 Fork Next主题

由于Next主题是开源的,我们不妨直接Fork。这里建议是私有Clone一下,因为毕竟要储存一些密钥啥的。

第五步 添加Submodule

博客工程文件的git里面add submodule,详细用法参加之前写过的一篇git submodule文章(在git分类下)

第六步 重新配置Next

由于升级之后和原来的版本差异较大,这里就可以利用git的版本控制优势,良好地筛选出配置文件变动很大的地方。

第七步 文件全部导入

就是把之前所有的文件直接copy/paste,不会遇到什么大问题。

版本控制

接下来其实就和平时使用git没什么区别了,只不过submodule也是一个独立的git,所以我们要分别进行版本控制。

Hardcodet.NotifyIcon.Wpf

这个是使用系统托盘图标来完成通知的推送的,前半段请参照这里

推送通知的例子:

1
App.TaskbarIcon.ShowBalloonTip("UIToy Notification", "Window Rect is null.", Hardcodet.Wpf.TaskbarNotification.BalloonIcon.Error);

这里我直接把TaskbarIcon当作全局的静态变量定义在App.xaml.cs里面了。

Notifications.Wpf

具体用法可以参见作者GitHub:https://github.com/Federerer/Notifications.Wpf

例子:

1
2
3
4
5
6
7
MaskWindowManager.notificationManager.Show(
new NotificationContent
{
Title = "UIToy Notification",
Message = "UI Analyzation Started.",
Type = NotificationType.Information
}, areaName: "WindowArea");

引言

在项目中想要直接调用系统的截图工具,毕竟这样感觉有原生的流畅丝滑般的感觉(雾),但是几经查找并未找到API,所以便萌生了直接模拟系统快捷键进行实现。在Windows10当中,截图工具的快捷键为Shift + Win + S,故模拟即可。为了以后方便调用,我将这些函数抽象成了工具类

VirtualKey

由于在C#当中,系统提供的Key的数值和ASCII码里的不太一样(当初就在这里被坑了),所以就Enumerate了一下。(码分之所以前后有区别是因为前半段是使用代码直接生成的,C++的语法糖好好吃)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System;
using System.Collections.Generic;
using System.Text;

namespace Elegant.Keyboard
{
public enum VirtualKey
{
VK_a = 97,
VK_b = 98,
VK_c = 99,
VK_d = 100,
VK_e = 101,
VK_f = 102,
VK_g = 103,
VK_h = 104,
VK_i = 105,
VK_j = 106,
VK_k = 107,
VK_l = 108,
VK_m = 109,
VK_n = 110,
VK_o = 111,
VK_p = 112,
VK_q = 113,
VK_r = 114,
VK_s = 115,
VK_t = 116,
VK_u = 117,
VK_v = 118,
VK_w = 119,
VK_x = 120,
VK_y = 121,
VK_z = 122,
VK_A = 65,
VK_B = 66,
VK_C = 67,
VK_D = 68,
VK_E = 69,
VK_F = 70,
VK_G = 71,
VK_H = 72,
VK_I = 73,
VK_J = 74,
VK_K = 75,
VK_L = 76,
VK_M = 77,
VK_N = 78,
VK_O = 79,
VK_P = 80,
VK_Q = 81,
VK_R = 82,
VK_S = 83,
VK_T = 84,
VK_U = 85,
VK_V = 86,
VK_W = 87,
VK_X = 88,
VK_Y = 89,
VK_Z = 90,
VK_0 = 48,
VK_1 = 49,
VK_2 = 50,
VK_3 = 51,
VK_4 = 52,
VK_5 = 53,
VK_6 = 54,
VK_7 = 55,
VK_8 = 56,
VK_9 = 57,
VK_LBUTTON = 0x01,
VK_RBUTTON = 0x02,
VK_CANCEL = 0x03,
VK_MBUTTON = 0x04,
VK_BACK = 0x08,
VK_TAB = 0x09,
VK_CLEAR = 0x0C,
VK_RETURN = 0x0D,
VK_SHIFT = 0x10,
VK_CONTROL = 0x11,
VK_MENU = 0x12,
VK_PAUSE = 0x13,
VK_CAPITAL = 0x14,
VK_KANA = 0x15,
VK_HANGEUL = 0x15,
VK_HANGUL = 0x15,
VK_JUNJA = 0x17,
VK_FINAL = 0x18,
VK_HANJA = 0x19,
VK_KANJI = 0x19,
VK_ESCAPE = 0x1B,
VK_CONVERT = 0x1C,
VK_NONCONVERT = 0x1D,
VK_ACCEPT = 0x1E,
VK_MODECHANGE = 0x1F,
VK_SPACE = 0x20,
VK_PRIOR = 0x21,
VK_NEXT = 0x22,
VK_END = 0x23,
VK_HOME = 0x24,
VK_LEFT = 0x25,
VK_UP = 0x26,
VK_RIGHT = 0x27,
VK_DOWN = 0x28,
VK_SELECT = 0x29,
VK_PRINT = 0x2A,
VK_EXECUTE = 0x2B,
VK_SNAPSHOT = 0x2C,
VK_INSERT = 0x2D,
VK_DELETE = 0x2E,
VK_HELP = 0x2F,
VK_LWIN = 0x5B,
VK_RWIN = 0x5C,
VK_APPS = 0x5D,
VK_NUMPAD0 = 0x60,
VK_NUMPAD1 = 0x61,
VK_NUMPAD2 = 0x62,
VK_NUMPAD3 = 0x63,
VK_NUMPAD4 = 0x64,
VK_NUMPAD5 = 0x65,
VK_NUMPAD6 = 0x66,
VK_NUMPAD7 = 0x67,
VK_NUMPAD8 = 0x68,
VK_NUMPAD9 = 0x69,
VK_MULTIPLY = 0x6A,
VK_ADD = 0x6B,
VK_SEPARATOR = 0x6C,
VK_SUBTRACT = 0x6D,
VK_DECIMAL = 0x6E,
VK_DIVIDE = 0x6F,
VK_F1 = 0x70,
VK_F2 = 0x71,
VK_F3 = 0x72,
VK_F4 = 0x73,
VK_F5 = 0x74,
VK_F6 = 0x75,
VK_F7 = 0x76,
VK_F8 = 0x77,
VK_F9 = 0x78,
VK_F10 = 0x79,
VK_F11 = 0x7A,
VK_F12 = 0x7B,
VK_F13 = 0x7C,
VK_F14 = 0x7D,
VK_F15 = 0x7E,
VK_F16 = 0x7F,
VK_F17 = 0x80,
VK_F18 = 0x81,
VK_F19 = 0x82,
VK_F20 = 0x83,
VK_F21 = 0x84,
VK_F22 = 0x85,
VK_F23 = 0x86,
VK_F24 = 0x87,
VK_NUMLOCK = 0x90,
VK_SCROLL = 0x91,
VK_LSHIFT = 0xA0,
VK_RSHIFT = 0xA1,
VK_LCONTROL = 0xA2,
VK_RCONTROL = 0xA3,
VK_LMENU = 0xA4,
VK_RMENU = 0xA5,
VK_PROCESSKEY = 0xE5,
VK_ATTN = 0xF6,
VK_CRSEL = 0xF7,
VK_EXSEL = 0xF8,
VK_EREOF = 0xF9,
VK_PLAY = 0xFA,
VK_ZOOM = 0xFB,
VK_NONAME = 0xFC,
VK_PA1 = 0xFD,
VK_OEM_CLEAR = 0xFE
}
}

VirtualKeyHelper

直接使用char来获得VirtualKey

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using System.Collections.Generic;
using System.Text;

namespace Elegant.Keyboard
{
public static class VirtualKeyHelper
{
public static VirtualKey GetVirtualKey(char Key)
{
if (Key > 127) { throw new Exception("Not Valid Char."); }
return (VirtualKey)Key;
}
}
}

KeyboardSimulator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace Elegant.Keyboard
{
public static class KeyboardSimulator
{
#region Win32API调用
[DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
#endregion

#region 常量声明
public const int KEYEVENTF_KEY_DOWN = 0;
public const int KEYEVENTF_KEY_UP = 2;
#endregion

#region 模拟方法
public static void KeyPress(VirtualKey Key)
{
keybd_event((byte)Key, 0, KEYEVENTF_KEY_DOWN, 0);
}

public static void KeyRelease(VirtualKey Key)
{
keybd_event((byte)Key, 0, KEYEVENTF_KEY_UP, 0);
}
#endregion
}
}

实现截图

1
2
3
4
5
6
KeyPress(VirtualKey.VK_LSHIFT);
KeyPress(VirtualKey.VK_LWIN);
KeyPress(VirtualKey.VK_S);
KeyRelease(VirtualKey.VK_S);
KeyRelease(VirtualKey.VK_LWIN);
KeyRelease(VirtualKey.VK_LSHIFT);