Just for Fun

给未来留点痕迹

通常来说,一个Git仓库代表了一个项目,Git仓库会和该项目的生命周期保持一致,但在某些情况下,我们可能需要将多个代码仓库进行合并,又或者我们需要将一个仓库进行拆分。最粗暴的方式就是直接将仓库文件移动到全新仓库,但这样做会导致历史提交信息丢失,这些提交信息往往对于后续的代码阅读和问题分析是一笔宝贵的资料。我们需要寻找在不丢失提交信息的前提下的仓库合并/拆分方式。

阅读全文 »

cpr是一个基于libcurl的C++封装库,提供了非常便于使用的上层接口。对于大量数据的传输操作,其提供了同步和异步两种接口接口。

1
2
Response Download(const WriteCallback& write);
AsyncResponse DownloadAsync(const WriteCallback& write);

其中,AsyncResponse是一个类似std::future的对象,应用需要在 合适的时机 调用get()方法获取Response对象。

在业务开发中,我们需要实现异步下载功能,函数签名如下:

1
void DownloadAsync(const std::string& url, const std::string& file_path, std::function<void(bool)> callback);

应用在主线程调用该方法,传输下载链接和下载文件路径,并提供一个回调在下载完成/出错时进行回调。

阅读全文 »

前言

断断续续接触 JNI 开发也有三年多了,一直都想做些记录,但又觉得难以写出新意,网上随便一搜非常多的 JNI 入门教程。近期需要给同事做一个 JNI 开发的分享,因此通过本文做些记录。本文不是一个 JNI 入门教程,而是会侧重于 C++ 进行 Android JNI 开发的一些最佳实践以及辅助工具的介绍,帮助大家更好的学习 JNI。

在使用 JNI 作为中间层将 C++ 和 JAVA 粘合起来时,最关键的是实现两个语言间类与类的交互。如果有一个 JAVA 类,该类实现了某些功能,我们需要在 C++ 中访问该类的方法,通常我们会创建一个与之对应的 C++ 适配类,然后通过该 C++ 类来调用对应的 JAVA 类的方法,反之亦然。下面我们根据适配类调用实现类的方向分成 JAVA->C++ 和 C++->JAVA 两部分来介绍。

阅读全文 »

示例代码

对于很多开发者来说,Qt仅仅是一个用于开发GUI程序的库,但实际上,Qt官方一直在致力于将Qt打造成一个跨平台的开发框架。Qt中提供了大量的基础设施和非GUI库可供我们在开发非GUI程序时所用,如网络相关的QNetwork模块,音视频相关的QMutilMedia模块,还有核心的QtCore模块。这些模块都已经相当成熟和完善,而且都具有优秀的跨平台性能。在和其他语言,如Java,python相比,库的相对不够丰富是C++的一个缺陷所在,除了标准库,boost等之外,Qt其实也是一个可以考虑的强大的补充。

如果我们只是打算使用诸如QString,QTL等基础设施,只需要链接QtCore这个库即可。然而Qt很多更为强大的特性和类,如信号槽,QTimer等,必须依赖Qt事件循环才能正常工作。如果使用我们开发的库的可执行程序恰好也是基于Qt开发,那么一切工作正常,然而,作为库的开发者,我们不能对库的使用者做出假设,因此如果我们需要使用信号槽等依赖Qt事件循环的特性时,我们就需要在库中开启Qt事件循序。

阅读全文 »

每个接触过Windows系统的开发人员都会接触到环境变量,相当一部分软件安装完成后必做的一步操作就是设置环境变量。Windows下的环境变量分为用户环境变量和系统环境变量。本文主要分享如何正确设置系统环境变量。

GUI界面

这是我们最常用的方式,即右键此电脑->属性->高级系统设置->环境变量,然后在系统变量栏中修改系统环境变量。该方法十分简单,但我们有时候可能需要通过程序来修改,又该怎么做呢?

环境变量

命令行

CMD中,我们可以通过set命令来设置环境变量,powershell中也可以通过$ENV:key=value的形式设置环境变量,但这只能影响当前命令行环境,命令行程序退出后就失效了。我们可以通过SetX来修改系统环境变量。官方描述如下:

在用户或系统环境创建或修改环境变量。能基于参数、注册表项或文件输入设置变量。

默认情况下,该命令是修改当前用户的的环境变量,如果我们需要修改系统环境变量,可以使用/M参数。但正如官方描述所说,该命令只能 创建或修改 环境变量,也就是说该命令无法用于删除一个环境变量。

注册表

根据微软的官方文档, Windows系统环境变量其实是保存在注册表中的,路径为HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment,我们可以手动或通过代码修改注册表的方式来修改系统环境变量。

To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string “Environment”. This allows applications, such as the shell, to pick up your updates.

修改完成后我们需要广播一个消息类型为WM_SETTINGCHANGElParam参数为"Environment"的窗口消息,该消息将会通知应用程序获取我们的更新。经过测试我发现,目前Windows下的explorer.exe是会处理这个消息然后更新其环境变量。这也就解释了之前一直以来的一个困惑,就是在修改完系统环境变量后,新启动的程序有的可以获取到更新后的环境变量信息,而有的必须则不行。这其实是因为新进程是会继承父进程的环境变量,如果父进程处理了上述的窗口消息且主动更新了,那么子进程就会使用更新后的环境变量信息,例如通过explorer.exe(这是Windows的窗口管理器,包括开始菜单,桌面等都是由该程序管理)启动的程序。而如果某些程序没有处理这个窗口消息,那么其不会更新自己的环境变量,进而导致通过其启动的新进程也就是旧的环境变量信息。例如我使用的一款启动器软件就没有处理这个消息,导致如果我通过其启动的软件都是旧环境变量信息,只能重新启动该启动器或重启系统。

在发送这条窗口消息时,还有两个需要注意的问题。

  1. 如何将一个字符串设置给lParam参数?

Windows下发送窗口消息的几种方法中,参数lParam都是是LPARAM类型,在Win10 x64系统下,其定义如下

1
2
typedef LONG_PTR            LPARAM;
typedef _W64 long LONG_PTR, *PLONG_PTR;

也就是说lParam是一个整型,那如何将一个字符串赋值给整型呢?其实通过中间的LONG_PTR我们可以看出,这里Windows是用一个整型来存储字符串的地址。我们可以直接将字符串强转成一个整型。

1
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, LPARAM("Environment"));
  1. 调用无效?

上面的代码调用看上去已经满足文档上的要求了,但在实际测试时,在Win10 中文版系统上,发送该消息后,再通过explorer.exe启动新程序,新的程序还是旧的环境变量信息。难道文档错了么?这个问题困扰了我非常久,后来才发现这是由于Windows为了兼容不同的字符编码环境,绝大部分API都提供了三套方法,除了普通的形式外,还有一个以A结尾,一个以W的方法,其中A代表ASCII码,即窄字符串版本,W代表宽字符版本。普通形式的方法实际上会根据是否有定义__UNICODE__这个宏来define为以A结尾或以W结尾的方法。如SendMessage的定义如下:

1
2
3
4
5
#ifdef UNICODE
#define SendMessage SendMessageW
#else
#define SendMessage SendMessageA
#endif // !UNICODE

在我的电脑环境下是有定义UNICODE宏的,因此SendMessage方法实际上是调用了SendMessageW方法,然而第四个参数"Environment"是一个ASCII字符串,这就导致参数和方法不匹配,进而导致该消息没有被正确处理。下面是几种可行的修改方式

1
2
3
SendMessageA(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, LPARAM("Environment")); // 明确指定使用
SendMessageW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, LPARAM(L"Environment")); // 明确指定使用宽字符版本
SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, LPARAM(_T"Environment")); // _T是一个根据UNICODE宏调整的宏,分别会被定义为空或L

总结

下面是修改系统环境变量的几种方法的对比

方式 支持程序调用 支持手动修改 添加 修改 删除 即时生效
GUI界面
命令行
修改注册表并发送窗体消息

踩坑

最近部门新采购了一台Mac Mini用作Jenkins自动构建服务器,由于之前搭建过Windows构建服务器,因此这次的Mac构建服务器的任务自然落在我的身上。开始一切都非常顺利,安装好必须的软件,配置好环境,但在正式上线,构建一个跨平台项目时,却发现macOS服务器始终无法从公司自建的Gitlab代码托管平台上克隆代码,而同一项目的其他平台(Windows和Linux)服务器却一切正常。macOS服务器的出错提示信息如下(部分信息用*代替)。

1
2
3
4
5
6
7
8
9
10
11
12
13
> git rev-parse --is-inside-work-tree # timeout=10
Setting origin to xxx.com:user/repo.git
> git config remote.origin.url xxx.com:path/repo.git # timeout=10
Fetching origin...
Fetching upstream changes from origin
> git --version # timeout=10
using GIT_SSH to set credentials **** private ssh key for git submodule
> git fetch --tags --progress origin +refs/heads/*:refs/remotes/origin/*
hudson.plugins.git.GitException: Command "git fetch --tags --progress origin +refs/heads/*:refs/remotes/origin/*" returned status code 128:
stdout:
stderr: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights

由于这个项目是多个平台同时构建,各个平台的Jenkins项目配置是完全一致的,因此可以排除掉Jenkins配置问题,难道是macOS服务器配置错了?

阅读全文 »

最近在学习 wireshark 抓包进行网络分析,粗略地翻阅了几本相关的书籍后总感觉影响不够深刻,对于工具性的技术,最好的办法就是在实践中运用。刚好一直对于一直在用的一个工具Dukto的工作原理一直比较好奇,今天就来通过 wireshark 一探究竟。
Dukto是一款用于局域网文本/文件传输的小工具,自带局域网发现功能。本文主要分析Dukto如何实现局域网发现和数据传输。

阅读全文 »
0%