在 Linux 下做开发和调试任务的时候,有些情况会动态去跟踪一些日志的变化来调试问题。Linux 下使用 tail -f 就可以达到需求了,但 Windows 下一直没有找到类似的好用工具,在 github 上也有一些开源项目,不是项目相对陈旧界面丑陋,就是功能不完善不能让人专注于分析日志。索性自己做了一个,预览图如下:

继续阅读

Cmder 是 Windows 下替换原有 cmd 的绝佳工具,但是与默认的 cmd 一样,都与 git 命令行存在一些兼容性问题,比如中文乱码问题。在 Cmder 安装目录下的 config/user-profile.cmd 中添加如下代码,可以解决中文乱码的问题。

set LANG=zh_CN.UTF8

添加后的配置文件

添加后重新打开 Cmder 的效果

将 Cmder 加入到右键菜单,到 Cmder 目录下执行如下命令即可:

Cmder.exe /REGISTER ALL

添加后的效果:

另外你还可以修改终端起始符号 λ 改成 $,修改 vendor/clink.lua 文件,将

"\x1b[1;32;40m{cwd} {git}{hg}{svn} \n\x1b[1;39;40m{lamb} \x1b[0m"

替换为 (实际就是将 {lamb} 替换为 $ )

"\x1b[1;32;40m{cwd} {git}{hg}{svn} \n\x1b[1;39;40m$ \x1b[0m"

参考:https://blog.csdn.net/qq_33304418/article/details/79918123

最近要用到 libeay32.lib、ssleay32.lib 两个静态库文件,因为以前项目中其中一个文件在 64 位编译时选择的不是 MTd,而导致我引入该库以后提示运行时库和其他库声明冲突,其实实际原因就是生成选项不一样。最终我还是决定自己编译 openssl 的库来使用。

下载所需工具

准备工作

安装 Perl 和 NASM,默认下一步下一步就可以了。Perl 安装的时候记得勾选将执行程序添加到系统环境变量中。NASM 安装时没有选项,需要在完成后要将执行程序添加到系统的环境变量中。如下图所示:

解压 openssl-1.0.2p.tar.gz 到任意目录,比如 D:\openssl-1.0.2p

开始编译

打开 VS 的命令行工具(我这里安装的是 VS2013),所以目录在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts 下,如果想编译 32 位的静态库就使用 VS2013 x86 本机工具命令提示,如果想编译 64 位,就使用 VS2013 x64 本机工具命令提示

打开后切换到 D:\openssl-1.0.2p 目录,执行如下命令生成 makefile 文件。

perl configure VC-WIN32

如果是生成 64 位则执行

perl configure VC-WIN64A

成功后如下所示:

如果你要编译 debug 版本,则修改 ms/do_nasm.bat 文件,将原来

perl util\mkfiles.pl >MINFO
perl util\mk1mf.pl nasm VC-WIN32 >ms\nt.mak
perl util\mk1mf.pl dll nasm VC-WIN32 >ms\ntdll.mak
perl util\mk1mf.pl nasm BC-NT >ms\bcb.mak

perl util\mkdef.pl 32 libeay > ms\libeay32.def
perl util\mkdef.pl 32 ssleay > ms\ssleay32.def

修改为:

perl util\mkfiles.pl >MINFO
perl util\mk1mf.pl nasm debug VC-WIN32 >ms\nt.mak
perl util\mk1mf.pl dll nasm debug VC-WIN32 >ms\ntdll.mak
perl util\mk1mf.pl nasm BC-NT >ms\bcb.mak

perl util\mkdef.pl 32 libeay > ms\libeay32.def
perl util\mkdef.pl 32 ssleay > ms\ssleay32.def

就是将第二行和第三行编译选项增加了 debug。修改完成后执行 ms/do_nasm.bat

ms\do_nasm.bat

运行后结果如下:

修改完成执行如下命令开始编译(如果想编译成 dll,则执行 nmake -f ms\ntdll.mak,编译前要修改 ms\ntdll.mak 将 CFLAG 的 /MD 属性修改为 /MT,与你调用项目匹配):

nmake -f ms\nt.mak

如果没有错误,几分钟后编译后的文件就会生成于 D:\openssl-1.0.2p\out32 目录下。

最近接了一个用 Qt 做跨平台截图工具的任务,主要功能有截图、绘制图案、马赛克、毛玻璃、文字能效果,其中马赛克功能时参考网上的文献并自己研究制作出来的,这里特意给大家分享一下。有需要的朋友可以作为借鉴。

实现原理

首先要开始实现之前,我们首先要来说一下我自己实现马赛克功能的原理。

  1. 为了可以执行撤销操作,我们不能直接对图片进行修改。将图片附加到窗口上以后,需要在窗口上覆盖一层透明的窗口,在这个窗口使用画笔等工具来绘制马赛克,覆盖后面的图片来实现一个视觉效果,最终保存的时候将图片和透明窗口上绘制的数据合并得到一个绘制了马赛克效果的图片。
  2. 对窗口进行区块划分,比如当我们想让每个马赛克的块大小是 10px,那么我们以图片左上角为 0,0 点,给图片划分成一块一块 10px*10px 的小块。
  3. 当鼠标点击透明窗口的某个区域时要到实际后面图片的响应坐标取这个坐标对应像素的颜色值(QColor),如果条件允许可以将这个坐标周围的色值也都取出来做一个颜色混合得到平均的颜色色彩。
  4. 计算当前坐标对应的 10px10px 小块的起始点坐标,然后根据已经取出来的颜色绘制一个 10px10px 像素的方块。
  5. 鼠标移动过程中时时计算坐标,如果当前鼠标所在坐标已经绘制了一个图形那么不再绘制,如果没有绘制则重复 3、4 步。

大体步骤就是这样的,接下来我们来拆分每一个步骤的实现方式。

实现细节

这个透明窗口派生于 QWidget 类,在构造函数中设置窗口为透明的,这样在我们将绘制的马赛克和图片混合的时候就不会有窗口背景色了。

// 让窗口背景透明,在获取窗口绘制的数据时不显示背景色
setAttribute(Qt::WA_TranslucentBackground, true);

对窗口划分区域并根据鼠标得出马赛克绘制方块。在绘制前你要监听鼠标在窗口上按下、弹起、移动等消息,在鼠标按下前,我们要先初始化一个 image 对象来提供我们获取颜色,并根据图片大小初始化一个坐标数组,比如我们有一个 100*100 的图片,想让马赛克的大小是 10 像素,那么我们就要将这个图片宽和高都划分为 10 个 10 像素的小方块。记录下所有图片的像素坐标点。

// 将截取的未知转为 iamge 对象,用来获取坐标像素的颜色
image_ = originPainting_->toImage();
// 初始化每个像素点的坐标位置,鼠标移动过程中记录坐标并通过数组下标
// 直接访问到数据查看某个区块是否已经被绘制上了马赛克。
pointArray.reset(new QVector<QVector<bool>>);
pointArray->resize(parentWidget()->width());
for (int i = 0; i < parentWidget()->width(); i++) {
    // 高度像素
    (*pointArray)[i].resize(parentWidget()->height());
}
for (int i = 0; i < parentWidget()->width(); i++) {
    for (int j = 0; j < parentWidget()->height(); j++) {
        (*pointArray)[i][j] = false;
    }
}

在按下的时候根据背景图像取像素的点,计算并记录像素坐标点归属那一块我们划分出来的小方块:

int x = endPoint_.x() - endPoint_.x() % mosaicSize_;
int y = endPoint_.y() - endPoint_.y() % mosaicSize_;

if (x < 0) x = 0;
if (y < 0) y = 0;

// 防止越界崩溃
if (x > pointArray->size() - mosaicSize_ ||
    y > (*pointArray)[0].size() - mosaicSize_) {
    break;
}

if ((*pointArray)[x][y] == false) {
    (*pointArray)[x][y] = true;

    MosaicData mosaic;

    // 为适配 retina 屏幕从全屏界面中取像素颜色
    QColor color = image_.pixel(realX, realY);

    QPen pen;
    pen.setColor(color);

    mosaic.color_ = color;
    mosaic.pen_ = pen;
    mosaic.point_ = { x, y };

    drawMosaic_.push_back(mosaic);
}

鼠标移动的时候同样走上面的流程,记录下鼠标移动的所有的坐标。此时在透明窗口的 paintEvent 中,你已经可以根据 drawMosaic_ 里面记录的坐标开始绘制一个一个 mosaicSize_ 大小的小方块了。最后在保存图片的时候,你可以先将透明窗口图片保存为一个 pixmap,然后将 pixmap 合并到图片中就可以了。

QRect imageRect = CommonHelper::getRetinaRect(currentRect_);

// 创建一副空背景图片
QImage resultImg = QImage(imageRect.width(), imageRect.height(), QImage::Format_ARGB32);
std::shared_ptr<QPainter> painter;
painter.reset(new QPainter(&resultImg));

// 先绘制截图的内容
painter->drawPixmap(QRect(0, 0, imageRect.width(), imageRect.height()), *originPainting_, imageRect);

// 再绘制图形数据
for (auto window : drawItemList_) {
    // 跳过空文本窗体
    if (window->getDrawModel() == DrawUnit::MODEL::Text && window->getDrawText().size() == 0) {
        continue;
    }

    // 获取窗口数据到 pixmap 中
    QPixmap pixmap = window->grab();
    // 获取窗口实际大小
    QRect windowRect = window->rect();
    // retina 兼容
    QRect windowImageRect = CommonHelper::getRetinaRect(windowRect);

    painter->drawPixmap(QRect(currentRect_.width() - windowRect.width(), currentRect_.height() - windowRect.height(),
        windowImageRect.width(), windowImageRect.height()), pixmap, windowImageRect);

    window->close();
}

return resultImg;

最终效果

下载

离线安装包:http://download.qt.io/archive/qt/5.11/5.11.1/

安装

安装过程中设置好路径,安装选项中记得勾选 Source 和 MinGW 5.3.0 32bit。

初始化

用终端(Windows 下 cmd)进入你安装的 Qt\Qt5.11.1\5.11.1\Src 目录下,执行如下命令。请注意 -prefix "D:\Documents\Qt\Qt5.11.1_MinGW_Static" 参数,这里指定了最终生成的静态编译文件的位置,最后一步 mingw32-make install 的时候会复制文件到这个目录下。

Configure.bat -confirm-license -opensource -platform win32-g++ -mp -debug-and-release -static -prefix "D:\Documents\Qt\Qt5.11.1_MinGW_Static" -qt-sqlite -qt-zlib -qt-libpng -qt-libjpeg -opengl desktop -qt-freetype -no-qml-debug -no-angle -nomake tests -nomake examples

编译

执行如下命令开始编译,-j8 代表使用 8 个线程,根据你 CPU 的情况而定。大概编译时间 2 – 3 小时。

mingw32-make -j8

安装

会复制到你之前设置的 -prefix "D:\Documents\Qt\Qt5.11.1_MinGW_Static" 目录下。

mingw32-make install

配置 Qt

打开 Qt Creator 增加静态库配置,打开工具->选项菜单,进入构建和运行->Qt Versions 选项卡。添加你刚才编译好的静态库目录下的 qmake.exe。

添加完成后切换到构建和套件选项卡,接着添加一个构建套件,注意调试器、编译器什么的都要选择好,不然无法使用:

静态编译项目

打开你的项目在项目设置中就能看到可以使用的静态构建套件了。

配置好静态编译的构建套件后,项目下方就可以选择这个套件来生成项目了。

有些测试场景需要知道在真实实体手机上访问我们开发的页面时产生了哪些不同的请求、访问了哪些链接。这个事情在 Windows 上做起来很简单,工具也有很多。浏览器自带的监控工具、HttpWatch 等工具都可以实现,今天介绍的主角 charles-proxy 也可以实现,但是 charles-proxy 强大的地方在于它可以建立一个临时代理服务器,手机上设置使用这个代理即可在 charles-proxy 中查看所有的浏览记录。

继续阅读

你还在一条条命令复制粘贴搭建 SVN 服务器?快别麻烦了,一个 docker 镜像就都帮你搞定了。如果你的服务器安装了 docker,直接使用以下命令(要修改你自己的账户和密码信息哦)拉取一个已经配置好的 VPN 镜像就可以直接使用了,不用那么那麻烦的自己又改配置文件又要安装软件。

docker run \
    --name ipsec-vpn-server \
    --restart=always \
    -e VPN_IPSEC_PSK=你的PSK密码 \
    -e VPN_USER=你的VPN用户名 \
    -e VPN_PASSWORD=你的VPN密码 \
    -p 500:500/udp \
    -p 4500:4500/udp \
    -v /lib/modules:/lib/modules:ro \
    -d --privileged \
    hwdsl2/ipsec-vpn-server

一直想做一个类似 Windows 命令行中 del 命令删除文件的功能,它支持 环境变量通配符可以递归,后来发现自己写这么一个小功能还真的不是一件容易的事情,没办法为了着急使用先临时做了一个小版本。代码有些缺憾。

  • 不支持环境变量
  • 不支持固定后缀文件递归删除
// example.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <windows.h>
#include <iostream>


BOOL DeleteFiles(const std::wstring file_full_path)
{
    BOOL no_error = TRUE;
    WIN32_FIND_DATA win32_find_data = { 0 };

    std::wstring dir = file_full_path.substr(0, file_full_path.rfind(_T("\\"))).c_str();
    std::wstring file = file_full_path.substr(file_full_path.rfind(_T("\\")) + 1, file_full_path.length());

    if (dir.size() == 0 || file.size() == 0)
    {
        return FALSE;
    }

    HANDLE handle = FindFirstFile(file_full_path.c_str(), &win32_find_data);
    if (INVALID_HANDLE_VALUE == handle)
    {
        return no_error;
    }

    do
    {
        // 如果是目录递归操作
        if (win32_find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
            // 排除 . 和 .. 两个文件夹
            if (_tcsicmp(_T("."), win32_find_data.cFileName) != 0 &&
                _tcsicmp(_T(".."), win32_find_data.cFileName) != 0)
            {
                // 根目录加上搜索出来的目录
                std::wstring new_full_path = dir;
                new_full_path += _T("\\");
                new_full_path += win32_find_data.cFileName;

                // 备份搜索出来的目录完整路径用以删除
                std::wstring new_dir = new_full_path;

                // 再加上要删除的文件名
                new_full_path += _T("\\");
                new_full_path += file;

                // 开始删除
                if (DeleteFiles(new_full_path))
                {
                    // 删除子文件后删除整个目录
                    RemoveDirectory(new_dir.c_str());
                }
            }
        }
        else
        {
            std::wstring full_file_name = dir;
            full_file_name += _T("\\");
            full_file_name += win32_find_data.cFileName;

            // 去除只读文件的只读属性
            DWORD file_attr = GetFileAttributes(full_file_name.c_str());
            if ((file_attr & FILE_ATTRIBUTE_READONLY) != 0)
            {
                SetFileAttributes(full_file_name.c_str(), file_attr & (~FILE_ATTRIBUTE_READONLY));
            }

            BOOL del_res = DeleteFiles(full_file_name.c_str());
            if (del_res)
            {
                std::wcout << full_file_name.c_str() << std::endl;
            }

            // 如果有一个文件删除失败则返回上层,上层若发现有删除失败的文件则不删除其斧文件夹
            if (del_res == FALSE && no_error == TRUE)
            {
                no_error = FALSE;
            }
        }
    } while (FindNextFile(handle, &win32_find_data) != 0);

    FindClose(handle);
    return no_error;
}

int _tmain(int argc, _TCHAR* argv[])
{
    _wsetlocale(LC_ALL, L"chs");

    DeleteFiles(_T(R"(C:\Users\ADMINI~1\AppData\Local\Temp\*)"));

    return 0;
}

Notepad++ 默认的制表符宽度是 4 个空格的大小,一个规模比较大的代码段或者 xml 等文件,小屏幕打开时看到的情景真的和让人着急,拖来拖去!有两种方案可以解决这种情况。

修改缩进为空格

这种我们不太推荐,但是有些公司或者团队会刻意要求就使用空格作为缩进。在一些前端开发的团队中比较多见。

修改缩进为2个空格的大小(依然使用 Tab 缩进)

这种情况是缩进依然使用制表符,但是宽度明显减小。

以上两种设置方法,都可以通过 设置->首选项->语言 界面中,通过勾选是否使用空格替换制表符和设置缩进宽度来达到你的需求。如下图: