Posted on 15-02-2017
Filed Under (技术) by waterlin

Visual Studio 2015 可以作为Linux开发和调试的利器了,很好用,按这篇文章配好环境就可以开始愉快的编码了。

但是如果在这里用 CMake 来构建工程,可能要注意一下:

1. 需要把 cmake 工程文件设置为自动拷贝到服务器

vc-linux-cmake-1.png

2. 需要配置一个编译命令和路径,因为 VS 默认的工作目录和我们在Linux上时是不一样的

比如,针对 Debug 模式可以是这样的:

vs-linux-cmake-2.png

上面红线的内容是类似这样的:

cd ~/projects/LiveQualityMonitor/debug; cmake -DCMAKE_BUILD_TYPE=Debug ..; make
~/projects/LiveQualityMonitor/debug/LiveQualityMonitor

针对 Release 模式是这样的:

vc-linux-cmake-3.png

上面红线的内容是类似这样的:

cd ~/projects/LiveQualityMonitor/release; cmake -DCMAKE_BUILD_TYPE=Release ..; make
~/projects/LiveQualityMonitor/release/LiveQualityMonitor

这样就可以用 cmake 来构建工程了,如果别人不用 Visual Studio,也同样可以用原来的 cmake 方法直接上 Linux 服务器去手动编译。

(0) Comments    Read More   
Posted on 06-01-2015
Filed Under (技术) by waterlin

在我的电脑上,如果开了麦克风增强,会导致自己用耳机听音乐的时候,有一股浓浓的沙沙声。如果使用 YY 或是其它语音通讯工具,对方也会听到一股重重的沙沙声。这个声音就和电流声很像。

如果碰到这个现象,只需要按下述步骤把麦克风增强这个选项关掉:

  1. 在右下角的声音标志上点右键,打开“录音设备”

    ./images/win7-audio-sasa-1.png

  2. 在“录音”标签页上的麦克风选项上点击“属性”,打开属性对话框

    ./images/win7-audio-sasa-2.png

  3. 在“麦克风属性”对话框上,点击级别标签页,把“麦克风增强”设置为 0 即可。

    ./images/win7-audio-sasa-3.png

(0) Comments    Read More   
Posted on 04-01-2015
Filed Under (技术) by waterlin

最近在使用定时器的时候,被一个小细节坑了,偶尔导致 coredump,费了好大的力气才找到原因,现在整理一下备忘。

我采用了如下方式来生成了定时器:

if (!CreateTimerQueueTimer( &m_hTimer, m_hTimerQueue, 
    (WAITORTIMERCALLBACK)videoTimer_proc, this, interval, 0, 0))
{
    printf("CreateTimerQueueTimer failed (%d)", GetLastError());
    return false;
}

然后,我就不断地调用上面的代码来生成定时器,并干相应的的事情,比如说视频帧的刷新等操作。但是,我犯了一个错误,在我需要结束定时操作后,我采用了如下的方式来结束定时器:

if (m_hTimer)
{
    DeleteTimerQueueTimer(m_hTimerQueue, m_hTimer, NULL);
    m_hTimer = NULL;
}

就是因为最后一个参数的原因,偶尔会出现崩溃:即定时器并没有因为这一行代码而完全销毁,从而导致这个类在销毁后,定时器依然触发了回调信息。

要解决这个问题很简单,只需要把上述代码里的 NULL 参数修改为 INVALID_HANDLE_VALUE 即可:

if (m_hTimer)
{
    DeleteTimerQueueTimer(m_hTimerQueue, m_hTimer, INVALID_HANDLE_VALUE);
    m_hTimer = NULL;
}

看看微软官方文档对这两个参数区别的说明:

If this parameter is INVALID_HANDLE_VALUE, the function waits for any running timer callback functions to complete before returning.

If this parameter is NULL, the function marks the timer for deletion and returns immediately. If the timer has already expired, the timer callback function will run to completion. However, there is no notification sent when the timer callback function has completed. Most callers should not use this option, and should wait for running timer callback functions to complete so they can perform any needed cleanup.

即,如果使用 INVALID_HANDLE_VALUE 这个参数,则 DeleteTimerQueueTimer 会等待定时器销毁以及回调函数结束后才返回;如果使用 NULL 则把定时器立即标记为销毁,但是实际上的运行并不受控制,如果有定时器正处在回调函数里并正在执行,有可能导致异常情况。

我的崩溃就是由于这个参数引起的,不容易出现,很不好找原因。

(1) Comment    Read More   
Posted on 24-10-2014
Filed Under (技术) by waterlin

最近在产品开发中,有用到 QProcess 启动一个进程,并且与之通信。产品开发完了提交给测试组测试,发现产品的内存会不断地增长,但是这个增长速度十分缓慢,大概 12 小时 20M 左右。

怎么看都不像是常规的内存泄漏!

然后是各种找原因,先是把产品中有关视频展示及渲染的部分关掉进行测试,然后逐步把一些其它部分屏蔽掉,最后关得只剩下 socket 这个环节了,可是内存还在缓慢增长。Qt 的 socket 很靠谱啊?找来找去,终于觉得是 QProcess 惹的祸了。仔细地读了读文档,看到下面这一句话:

void QProcess::closeReadChannel ( ProcessChannel channel )
Closes the read channel channel. After calling this function, QProcess will no longer receive data on the channel. Any data that has already been received is still available for reading.
Call this function to save memory, if you are not interested in the output of the process.

其实 QProcess 是会把你启动进程的标准输出重定向到你的程序里来,从而占用你程序的内存。原来我这个缓慢增长就是由于 QProcess 启动进程后的 stdout 输出没有及时清理导致的。

如果不需要 QProcess 启动进程的标准输出,只需要在使用 QProcess 启动进程前调用 closeReadChannel 这个函数关闭输出即可。如果需要 QProcess 导过来的进程输出,则只需要在有输出数据的时候,读取干净即可。

(0) Comments    Read More   
Posted on 21-08-2014
Filed Under (技术) by waterlin

最近在 PC 上需要生成一个二维码图片,准备使用 libqrencode 这个 QR 二维码生成库。Windows 下有一个它的移植版本 qrencode-win32,除了有源代码以外,还有一个 Windows 下的应用程序,可以直接用来生成 QR 二维码。

我使用的是 libqrencode 3.4.2 版本,对应于 Windows 下的版本是使用 VC8 来构建工程的,而我本地只有 VC9 的版本,我升级工程后进行编译,提示如下错误:

Error   2       error C3163: '_vsnprintf': attributes inconsistent with previous declaration    c:\program files (x86)\microsoft visual studio 9.0\vc\include\stdio.h   358

这是因为在 VC9 CRT 里已经定义了 vsnprintf 这个函数,而在 VC8 里却没有,所以在很多 VC8 或更早的代码里,会添加类似下面的定义:

#define vsnprintf _vsnprintf

对于 libqrencode 来说,也是这个问题,只需要把 config.h 里的这一句代码注释掉即可。

如果还需要在 VC8 和 VC9 里使用,则可以使用下面的方式来灵活定制:

#if     _MSC_VER < 1500
#define vsnprintf _vsnprintf
#endif
(0) Comments    Read More   
Posted on 20-08-2014
Filed Under (技术) by waterlin

Visual C++ 2008 SP1 compiler error C1859

有一些工程,在编译好后再重新编译,会提示类似如下错误(Windows7下容易出现):

Error   1       fatal error C1859: 'release\\testproject.pch' unexpected precompiled header error, simply rerunning the compiler might fix this problem

这个就是 Visual C++ 2008 SP1 compiler error C1859 的问题,解决办法就是安装一个补丁包

除了安装上述安装包以后,还可以使用如下方式来修复该问题:

  • Disable /analyze (if enabled).
  • Invoke a clean build.
  • Reboot your machine.
  • Disable PCH files.

使用 std::copy 时的 C4996 警告

Visual C++ 2008 里如果把警告当成错误处理时进行编译,下面这个警告就会被当成错误,很扎眼,而你,则一定需要解决这个问题。

Warning 2       warning C4996: 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'

这是在使用 Visual C++ 自带的标准库时,Visual C++ 的编译器会加强检查,标准库里的一些方法会认为在使用上是不安全的。Visual C++ 对此提出警告、不建议使用这些函数,并且 Windows 提供了一些其它以 _s 结尾的函数用来替代这些方法。std::copy 就是这个现象的重灾区。

如果是你自己的代码碰到这个问题,那很好解决,你只需要把这些函数替换成 Windows 提供的更安全的函数即可。但是,如果是你引用的第三方库的代码问题,而你又在 Visual Studio 里把安全级别设置成不允许有警告,这个时候,就只能通过定义一个 _SCL_SECURE_NO_WARNINGS 宏来解决此问题。

(0) Comments    Read More   
Posted on 03-03-2014
Filed Under (技术) by waterlin

最近家里的电脑换了一个 Windows7 系统,再安装 Visual Studio2008 & sp1,以及 Qt 4.7.1 等一系列的开发工具后,以前能正常编译的代码,现在一编译就提示说 mt.exe 已停止工作错误。

如果单纯地把 Embeded Manifest 关闭,虽然可以正确地编译生成可执行文件,但是在你需要执行该可执行文件的地方,却需要手动拷贝与之相关的动态链接库,相当麻烦,所以,最好还是保证 mt.exe 能正常工作。

之前能正常编译的东西无法编译了,只能说明是系统更新造成的影响。仔细查了一下,原来应该是系统自带的 Microsoft .NET Framework 4 Client Profile 这个程序带来的影响,把这个程序卸载掉后, mt.exe 与 Qt 就可以一起正常工作了。

Qt 为啥会和 Microsoft .NET Framework 4 Client Profile 有冲突?

(0) Comments    Read More   
Posted on 20-02-2014
Filed Under (技术) by waterlin

Windows 有一个东西叫做 URL Protocol,即注册一个自定义的协议,使得可以使用某一个程序来自动打开某种格式的链接。

举个例子来讲,对于我们经常使用的 Evernote 来说,每一篇文章都有一个内部链接,形式如下:

evernote:///view/52572/s1/98223c5d-84a1-438d-a4c7-f60dde858f87/98223c5d-84a1-438d-a4c7-f60dde858f87/

在你正确安装了 Evernote 后,你可以在 Firefox 或是 IE 的地址栏里输入这个内部链接,则 Windows 系统会自动调用 Evernote 来打开这个链接。而 Evernote 被调用来打开这一链接的表现,恰恰是直接跳转到该篇文章,并显示出来。

对于不同的 URL Protocol 响应程序,这种表现是各自编程处理的,这取决于提供 URL Protocol 的程序员的想法。

但是不知道出于安全或是其它原因的考虑,Chrome 并不支持 URL Protocol 来打开第三方应用,我在 Chrome 的设置里找了很久,虽然提到在 Content settings 里有一个 Handlers,但是这个设置却永远是一个空白。

Chrome 虽然没有设置 URL Protocol 的入口,不过我们却可以直接编辑 Chrome 的配置文件来实现定义这一功能。这一配置文件是 Chrome 的 Local State 文件,这个文件的保存路径根据操作系统有所不同:

  • Windows7 上是 %LOCALAPPDATA%\Google\Chrome\User Data\Local State 这个文件;
  • Mac OS X 10.8 上是 ~/Library/Application Support/Google/Chrome/Local State 这个文件;
  • Ubuntu 12.04 上是 ~/.config/google-chrome/Local State 这个文件。

为了打开 Evernote 的 URL Protocol,找到这个文件后,我们就可以按如下步骤进行编辑:

  1. 关闭 Google Chrome;
  2. 打开文件 Local State 并找到 excluded_schemes 一节,在 protocol_handler 下;
  3. 添加一行内容:
    "evernote": false,
    
  4. 重启 Chrome。

这样,我们再到 Chrome 里打开形如 evernote:/// 的链接,Evernote 就会自动跳出来并把相应的文章呈现在你眼前了。

如果需要打开其它自定义的 URL Protocol,则只需要把 evernote 关键词换成对应的关键词即可。

(0) Comments    Read More   
Posted on 26-12-2012
Filed Under (技术) by waterlin

以前的旧笔记,在硬盘里睡了好久了,略作整理发上来,也算是对自己的一种交待了。

  1. 安装 VS2005 SP1
  2. 安装 WinCE 6.0

    注意,WinCE 6.0 R2 和 WinCE 6.0 R3 并不是 WinCE6.0 的完全升级版,只是一个升级的增量包,所以,你还是需要先安装 WinCE 6.0。

    安装的时候,记得选择你需要的 cpu 类型。

  3. 申请免费用的 key。
  4. WinCE 开发者分两类,一类是嵌入式系统开发人员,一类是应用程序开发人员。
  5. 全部安装好后,剩下的事情,就和 Win32 开发差不多了。
(0) Comments    Read More   
Posted on 12-06-2012
Filed Under (技术) by waterlin

在客户端开发中,都涉及到大量类似的需求:从剪贴板里拷贝内容出来,复制内容到剪贴板,甚至有的时候,还需要针对富文本格式(如 Html 格式)来进行操作。用 C# 来实现此类功能特别简单,下面就对此类操作进行简单概述,主要是在 C# 的 Clipboard 类上做文章。

如果你直接从网页上复制了一段 Html 内容到剪贴板,则可以通过 C# 用下面的方式来获取 html 源文件

string format = "TextDataFormat.Html";
if (Clipboard.ContainsText(TextDataFormat.Html))
{
    String returnHtmlText = null;
    returnHtmlText = Clipboard.GetText(TextDataFormat.Html);
}

但是直接用上述代码,在处理中文 Html 的时候,获取的内容可能会是乱码。这个时候,我们可以人工转换编码来解决这个问题,例子代码如下:

if (Clipboard.ContainsText(TextDataFormat.Html))
{
    String returnHtmlText = null;

    MemoryStream vMemoryStream = Clipboard.GetData("Html Format") as MemoryStream;
    vMemoryStream.Position = 0;

    byte[] vBytes = new byte[vMemoryStream.Length];
    vMemoryStream.Read(vBytes, 0, (int)vMemoryStream.Length);

    returnHtmlText = Encoding.UTF8.GetString(vBytes); 
}

但是,如果你直接把通过上面获取的 Html 代码存成 Html 文件,那么打开这个 html 文件后,有可能依然会获得一个乱码文件,因为你没有给这个文件加上合适的 Html 头来让浏览器获取正确的编码格式。因为通过 Clipboard 获取的 Html 文本比较正规,对获取的文本加 Html 头的操作,可以简单地使用一个字符串替换来实现:

//adding <header> in html
string header = @"<html><head><meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8"" /></head>";

returnHtmlText = returnHtmlText.Replace("<html>", header);

把所有代码合并在一起,就形成一个完整的获取剪贴板 Html 内容的程序:

if (Clipboard.ContainsText(TextDataFormat.Html))
{
    String returnHtmlText = null;

    MemoryStream vMemoryStream = Clipboard.GetData("Html Format") as MemoryStream;
    vMemoryStream.Position = 0;
    
    byte[] vBytes = new byte[vMemoryStream.Length];
    vMemoryStream.Read(vBytes, 0, (int)vMemoryStream.Length);

    returnHtmlText = Encoding.UTF8.GetString(vBytes); 
                
    //adding <header> in html
    string header = @"<html><head><meta http-equiv=""Content-Type"" content=""text/html; charset=utf-8"" /></head>";
    returnHtmlText = returnHtmlText.Replace("<html>", header);
}

对上述获得的 Html 字符串还可以进行后续操作,比如说显示、保存为网页文件等。当然,上述代码只是一个原型,需要更多的加工才能被正常使用在实际项目中。

(0) Comments    Read More