使用Julia+Win32 API获取Excel剪贴板中的表格

fig1

假设Excel中有这样一个表格,我们复制它后可以在Julia中利用该脚本粘贴。

代码如下:

用法如下,将Win32ClipboardCSV.jl放置于相同目录:

将CSV转换为DataFrame以便于访问数据。

fig2

相关讨论见http://discourse.juliacn.com/t/topic/1134/3

使用PowerShell消除Windows系统中WiFi网络名字后面的数字

我之前用过的手机热点的SSID是“Azure’s iPhone”,换了新手机之后再次打开热点,发现网络名字被Windows显示成了“Azure’s iPhone 2”,which 让我不太高兴。

解决方案:以管理员权限运行PowerShell,输入

然后输入

注意-like选项使用的是通配符,因此会匹配到iPhone而不是iPhone 2。结果看起来如下,只有一条:

确认无误后,加上Remove-Item进行删除操作:

然后把iPhone 2改回来:

-match选项使用的是正则表达式。关闭WLAN再打开,重新联网,问题解决。


由于管道中的Get-ItemProperty得到的是该注册表项下的值,而不是代表该项本身了,所以使用-PipelineVariable通用选项可以把管道前面的结果(这里是Get-ChildItem(ls)得到的项对象)存放在变量中以便后续操作,这要求PowerShell v4+。版本太低的话只好这么写了……看上去就很不pipeline了

 

在Windows上的Visual Studio Code中配置C/C++运行环境

虽然在Windows上写C++程序首推Visual Studio,但是如果想要方便地运行代码片段或者编译单个文件的话,Visual Studio Code仍然是一个好选择。

首先去扩展商店中搜索并安装“C/C++”以及“Code Runner”。安装好了之后点击重新加载,以使插件生效。

Code Runner只是一个快捷运行代码的工具,我们需要准备一个编译器。如果已经安装有Visual Studio的C/C++桌面开发相关组件的话,MSVC应该是可以直接用的(只想安装VS 2017单独的C/C++工具链的话,也需要从官网下载VS的安装程序,然后只勾选必要项即可)。mingw或者WSL中的g++、Clang之类的也可以,看个人喜好。接下来以MSVC为例,配置Code Runner的命令行参数。

点击文件-首选项-设置,找到扩展标签下的Run Code configuration,修改Executor Map项(需要手动编辑JSON文件)。

fig1

settings.json分为左右两个窗格。左边是默认设置,右边是用户/工作区设置。我们在右边新增同名的键,以覆盖左边的默认值。

以命令行方式使用MSVC(cl.exe)需要配置环境变量。最简单的方式是运行VS的开发人员命令提示符脚本,它的位置可以在开始菜单里找到。

可以直接按照上面的样子配置。VsDevCmd.bat会设置相关环境变量等,然后直接cd到当前文件所在目录,调用cl即可。

配置好了之后,保存settings.json,打开一个C/C++源文件,按下F1打开命令窗口,输入Run,找到Run Code命令,运行之:

fig2

编译运行后应该能看到如下界面:

fig3

看起来一切顺利,但是还有问题:MSVC输出的中文会变成乱码。随便拿掉一个语句分号再编译,就会发现这一点。原因在于Code Runner使用管道重定向了命令输出,然后将其作为UTF-8文本显示。这在Linux下似乎没有大问题,然而由于历史原因,多数Windows下的命令行工具使用的是本机编码(GBK)。如果Locale配置无误的话,常用字符的显示在Windows终端下倒是没问题。但是我们这种情况就比较麻烦了,因为如果直接运行cl这类程序的话,它们的MBCS字符输出会被系统的ANSI系API正确处理,然后打到屏幕上;但是这里Code Runner面临的是未经处理的字节流,而它……看什么都觉得是UTF-8。虽然我们也可以通过调整code-runner.runInTerminal设置,让命令直接在终端里运行,但是比较彻底的方式是让cl直接输出UTF-8文本,或者……帮它输出。

写一个Wrapper对于熟悉Win32编程的读者来说应该不难:

稍微需要注意的是,由于GBK编码是变长的,所以当收到的第一个字节的最高位为1时,要等待第二个字节到来,再进行转换;由于我们的程序是要被Code Runner重定向STDOUT到匿名管道的,所以我们自己写控制台时就不能使用WriteConsole了,而应该使用通用的WriteFile。

编译好了之后,将这个程序命名为GBKWrapper.exe,然后丢到随便一个%PATH%内的目录即可。

要利用这个程序,只需要修改一下编译命令行:

在cl前面加上GBKWrapper,就可以看到中文的编译器输出了:

fig4

细心的读者可能看到了cl编译器选项的/utf-8。由于VSCode默认的UTF-8是不带BOM的,因此会被cl识别为当前本机编码(GBK)的文件。设置/source-charset:utf-8即可解决这个问题。/utf-8会同时设定source charset和execution charset,所以普通的字符(串)字面值也会变成UTF-8(当然,可以用前缀控制字面值编码,比如u8、u和U)。

使用PowerShell批量生成语音TTS文件

在校对《封印者》语音包的过程中,为了确认每个技能和语音ID的对应关系,其中一个方法就是将音频文件替换为容易辨识的内容,然后在游戏里试验并记录对应关系。

本文以缇娜的至高之翼语音为例进行讲解。拆包得到的语音文件名有一定规律,例如“VOICE_USN_SI_RARE_COSTUME_3_ARMS_SKILL_1.OGG”和“VOICE_USN_SI_RARE_COSTUME_3_ARMS_SKILL_11_1.OGG”。我们要做的就是利用Windows自带的PowerShell来根据文件名合成出对应的TTS语音。

代码如下:

用法如下:

首先新建一个文件夹,例如“CloTTS”。然后将上述代码保存为ps1文件,例如“CloTTS\CloTTS.ps1”。然后在资源管理器空白处按住Shift点击鼠标右键,选择“在此处打开PowerShell窗口”。我们假设解包出的原始语音包按文件夹分类存放,目录结构如下:

C:\Tina_RareCostume2
1.ogg
2.ogg

C:\Tina_RareCostume3
1.ogg
2.ogg

在PowerShell窗口中输入

该脚本就会在CloTTS\Output下生成两个文件夹“Tina_RareCostume2”和“Tina_RareCostume3”,里面是产生的TTS WAV音频文件。然后使用你喜欢的转码工具(例如foobar 2000)将文件转换为OGG即可。其实,只要将文件夹拖进PowerShell窗口就会自动变成路径。

细节解释:

语音合成使用了Microsoft Zira Desktop。这是Windows自带的英文女声音源,你也可以换成你喜欢的(比如解锁Cortana之后用她的语音)。修改$syn.Rate可以修改语速(-10~10)。

在构造音频内容的时候,需要做一些处理,例如文件名的共用前缀可以省略。下划线要替换掉,否则语音引擎会读成“underscore”。此外,原始文件的命名可能导致语音歧义,如“VOICE_USN_SI_RARE_COSTUME_3_ARMS_SKILL_30_1”中的“30 1”,容易与31混淆。我们使用MakeVoiceString函数来做相应的处理。首先替换掉公共前缀,然后利用正则表达式将两个数字中间有空格的情况替换为“30,#1”的形式(这样语音引擎会读作“thirty, number one”),最后将其余的下划线替换为空格即可。制作其他角色的语音的时候只要针对性修改这里的规则即可。

使用Julia实现一个单链表

单链表的简单Julia实现,包含了单链表的插入、删除、遍历、反转等操作。

运行效果如下:

 

C++中的traits和SFINAE,了解一下?

同样的内容已发布到Project 1论坛。https://rpg.blue/thread-406374-1-1.html

大扎好,我系喵^3,今天为大家介绍一下【今天】的C++造轮子时与编译器玩耍用到的两个小技巧。)咳
C++是静态类型的语言,所有对象的类型要在编译期决定。这给造轮子带来了一些麻烦,比如,在编写泛型方法的时候有时需要判断目标类型是否支持对应的操作
但是,C++这语言有些问题:缺少Metaclass,语言自省能力不足,再加上编译期必须推导出所有变量的类型,导致这种“判断”不好实现。(C++的所有高级抽象原则都是在尽量不造成太大开销的情况下实现的,这也是为什么C++这么快的原因之一)
不过由于C++的模板推导能力很强大,还是可以使用一些很hack的方式实现这类要求的。
标准库提供了大量的helper设施来判断某个类型的“特性”,比如是否可平凡析构、是否是数值类型,等等。
这里我没有用到它们,而是尝试自己推导了一组,虽然实现未必漂亮但过编译还是没问题的……233

首先我们定义两个类型,Foo和Bar。

CxxTraitsSFINAE1

Foo有两个double类型的成员taroxd和kuerlulu,而Bar只有一个taroxd,还是int类型的。

CxxTraitsSFINAE2

然后写一个函数fun,并分别用Foo和Bar类型对象作为参数调用它,经过一些蜜汁操作之后,我们可以区分fun中的抽象类型Ty是不是Foo类型,以及它有没有kuerlulu这个值(通过给Foo打了一个has_kuerlulu实现的,其实可以修改一下,直接去判断它有没有这个成员)

运行输出如下

CxxTraitsSFINAE7


看懂了嘛?懂了可以Ctrl+W了,不懂的话不着急,冷静分析.webp
为了实现fun的效果,我们需要在编译的时候计算出Ty的具体类型、以及它有没有kuerlulu这个成员,并调用正确的重载函数。如果不这么做,对于Bar这个类型,是不存在Bar::kuerlulu的,因此fun过不了编译。
因为一切的判断都是静态的,是充分利用编译器类型推导的结果,所以思维方式要转变一下,这里的“C++”其实并不是跑起来的C++,更像是一个函数式语言…
(顺便一提,利用模板类型推导可以进行计算,并且被证明是图灵完全的,感兴趣的可以写个编译期阶乘之类的~小心不要把编译器给爆栈了哦)

首先做两个辅助类型

CxxTraitsSFINAE3

其实就是两个空的结构体,里面的using是在结构体内部的可见域里定义类型的别名(例如using uint = unsigned int;),并不是成员啦。
还有一个括号运算符重载,用来获得对应的布尔值true/false
注意到static_false里没有is_true,static_true里没有is_false,具体有什么用后面再说。

CxxTraitsSFINAE4

然后做一个is_foo类型来判断。这里利用了C++的模板特化
假设有一个模板结构类型is_foo<T>,然后我们特化了一个版本叫is_foo<Foo>,那么编译器在模板实例化时会选择“更完美匹配”的版本,这里的效果就是其他类型会匹配到第一个,Foo类型会匹配到第二个。
自然,如果用int实例化,is_foo<int>里面的value就定义为static_false类型了,所以在编译期【is_foo<int>::value】这个类型就是static_false
大概就是这种思路,我们用类型定义来代替运行期的“变量”的概念

现在已经搞定了fun前半部分的is_foo操作了。由于is_foo<int>::value是一个类型,is_foo<int>::value()就是默认构造了一个is_foo<int>::value类型的对象的意思,is_foo<int>::value()()就是调用了这个对象的operator(),返回一个bool值。根据这个进行if选择就好了。
我们在写泛型轮子的时候,就可以利用这类手段去针对特定类型执行对应操作。

那教练…有没有更给力的?

我们永远不会知道我们的用户会塞进来什么样的类型呀,指明类型的名字显然不是一个好主意。更好的做法是判断一个类型是否具有某种“特性”,这就是traits,可以理解为特性萃取
事实上标准库已经这么做了。比如算法库会根据你传进来的迭代器类型,查询iterator_traits里的信息,比如你这迭代器是否支持随机访问,迭代器解引用之后的类型是什么…然后选择效率最高的算法实现。没错,都是编译期决定的。

所以我们也来写一个吧。

CxxTraitsSFINAE5

foo_traits_base类型是用来保存某个信息对应的两个traits默认值的。value_type定义为Ty类型里的tag_value_type的别名,而has_kuerlulu定义为static_false类型的别名。
普通版的foo_traits<Ty>直接继承了base,而Foo这个类型可以定义自己的foo_traits<Foo>特化(通常这个定义和你的Foo类是一块提供的,我是为了更清楚的叙述逻辑才放到这里的)
还是和is_foo类似的思路,foo_traits<Bar>::has_kuerlulu就是static_false类型的别名。只有Foo这个类型的has_kuerlulu是static_true的别名。这就相当于你提供一个类型Ty,然后foo_traits这个类型就可以把Ty的一些“特性”提取出来。
而value_type我们希望它表示Ty里面的成员的类型(Foo是double而Bar是int),它是从这两个结构定义里的tag_value_type获得的。可以翻上去看看

接下来就是上面这堆东西的真正用法了。

CxxTraitsSFINAE6

这两个版本的get_kuerlulu_if是在fun里面调用的函数重载候选。我们需要根据给fun的Ty类型里有没有kuerlulu来决定调用哪个版本。所谓的SFINAE就是“替换失败不是错误”,意思是编译器用实际类型X在匹配重载的模板候选P时,如果将模板参数Ty替换为X时会导致病式(ill-formed),那么不会产生编译错误,而是将候选P从重载候选中移除。我们就利用这个规则,让编译器在没有kuerlulu时拒绝候选2,从而接受候选1,什么都不做;当Ty拥有kuerlulu时,拒绝候选1而接受候选2。

利用SFINAE有很多种形式,比如在函数参数里加一个类型,让它在特定条件下为病式,但是这样就多了一个参数,看起来很不好(编译器会不会把它优化掉另说…)
所以这里我们选择从返回值做文章。这两个版本的返回值都是void。
Sb在实例化的时候我们放上了foo_traits<Ty>::has_kuerlulu。再强调一遍,它是个类型别名,可能是static_false<>或者static_true<> (我把它们写成了模板,默认参数其实有一个Ty=void)
为了帮助理解,我们分类讨论。
假设Sb的类型是static_false<void>:
那么根据定义,对第一个版本,static_false<void>::is_false就是void的别名,函数返回值就被替换为void,完美。
对第二个版本,由于static_false<void>中不存在is_true这个定义,因此为病式,这个版本被拒绝。
假设Sb的类型是static_true<void>,类似地,第一个版本被拒绝,第二个版本匹配上了。

到此为止,就有了fun中的那套神奇操作。


我的看法:
有效吗?有效。
为什么写法这么丑,就像做数学证明题?因为这套操作并不是语言有意设计的,纯属这个语言的类型推导过于牛逼。
有更好的写法吗?比如给C++增加更多的语言特性,编译期反射啊元类啊Concepts啊…这个就要等待语言的发展了,目前C++是一个过于复杂的语言,拥有巨量的语言特性(和坑),导致这个语言极难掌握、极难精通。←“精通C++”已经成为一个梗了,类似“PHP是最好的语言”一样的存在。作为萌新的我自己连“熟练”都不敢说呢…听说有人想21天?)雾
所以再增加新特性的话必须慎之又慎(不考虑兼容性的话甚至还要砍吧),期待那群语言律师在C++20(如果不鸽的话)能拿出更棒的设计来,让这个语言写起来更靠谱、更友好。

 

Win32控制台数独

鼠标或键盘方向键移动光标
输入1~9填入数字,Backspace清除
从同文件夹data.txt读取题目,每行读取9个字符,空格用0或者其他字符代替

 

RGSS/Ruby 运行x86原生代码的一种方法

今天研究RPG Maker中使用API callback的时候想到的一种方法。将代码加载到虚拟内存中,给予RWE权限,然后使用EnumWindows回调启动我们的代码。
例如以下代码

编译后反汇编,得到

省事起见,字符串和MessageBoxA的地址用Ruby做后续修正。大概过程为

写成Ruby:

运行后即可弹出消息框。

Windows 10 1709 后台服务持续消耗CPU的解决方法

升级到Windows 10 1709(秋季创意者更新,FCU)后,任务管理器中可见一服务宿主svchost进程(服务主机:本地服务(无网络))持续消耗CPU资源。
该宿主内含三个服务:Base Filtering Engine、CoreMessaging、Windows Defender Firewall。
尝试杀死该进程会导致蓝屏(错误代码:关键进程中止)。
BFE服务与系统防火墙过滤规则相关。
如果尝试打开“设置”中的移动热点的话,发现开关打开后没有响应,关闭窗口后再次查看发现移动热点并未实际打开。
推测是另一个服务“Internet Connection Sharing (ICS)”启动失败导致CPU高占用,并且与防火墙规则相关。
进入“服务”MMC后发现ICS服务的控制选项全部为灰色。可以将启动类型设置为禁用(应用时可能会无响应很久),并手动结束ICS的服务宿主。

解决方法:
1 首先尝试停止ICS服务的运行,并禁用。
2 进入设置-更新和安全-Windows Defender-打开 Windows Defender 安全中心-防火墙和网络保护,将所有网络的防火墙关闭(域、专用、公用),然后点击“将防火墙还原为默认设置”、“还原默认值”。
3 此时应该可以观察到CPU占用恢复正常。移动热点应该可以正常启动了(ICS的服务类型会被改为手动(触发器启动))。

Win32控制台扫雷

之前用C++实现了一个使用鼠标操作的Win32 Console扫雷游戏。

源代码如下,使用Visual Studio 2017编译通过。