Posted on 05-09-2011
Filed Under (技术) by waterlin

在 Visual Studio 2008 MFC 工程中,利用 Berkeley DB 来构建数据存储引擎时,在编译 db.h 文件时出现编译错误,错误提示内容如下:

错误    3       error C2143: 语法错误 : 缺少"}"(在"("的前面)        e:\water\berkeleydb\include\db.h        1226

微软的 MSDN 上有对 error C2143 的编译器错误进行解释,不过基本上没有太多可读性、可借鉴性,大意应该是一些宏定义、命名出错等。

最后,还是通过万能的 Google 大神找到了解答方法。错误的原因是 DB_TYPE, DB_UNKNOWN 类型已经在 MFC 系统头文件中被定义过,解决办法之一是在 db.h 中定义 DB_TYPE, DB_UNKNOWN 的语句之前加上如下语句即可:

#ifdef DB_UNKNOWN
#undef DB_UNKNOWN
#endif
#ifdef DBTYPE
#undef DBTYPE
#else
#define DBTYPE BDBTYPE
#endif

看来,C 和 C++ 混在一块,命名、类型定义真是一个大问题。以后碰到类似的问题,也可以采用类似的解决办法。

参考资料:

  1. 解决在vs2008的mfc工程中编译BerkeleyDB出错问题
  2. VS2005中的MFC程序使用BerkeleyDB
  3. MSDN Visual Studio 2010 Compiler Error C2143
(0) Comments    Read More   
Posted on 24-08-2011
Filed Under (技术) by waterlin

今天为这件事情折腾了一天!

我用 Java 写的中间件,会根据用户的需要,启动一个子进程,这个进程是用 C++ 写的,正好用到了 Log4Cxx 来记录日志以便分析。这个进程,单独从控制终端运行,没有任何问题;但是一旦用 Java 启动子进程的方式来启动,则这个子进程刚一启动就阻塞不动了,然后大概等上几十分钟到两个小时不等,该子进程则又能顺利执行下去。

第一次碰到这样的问题,很怪异,经过反复测试及万能的 Google 帮忙,找到Java调用外部程序挂起原因,Log4Cxx 原来是罪魁祸首,因为它重定向了大量的日志信息到控制台,由于 Java 进程没有清空程序写到缓冲区的内容,结果导致程序一直在等待。

解决这个问题有两个办法:

  1. 按上文中的说法,在遇到 Java 启动外部程序而导致线程阻塞的时候,可以考虑使用两个线程来同时清空 process 获取的两个输入流;
  2. 直接把 Log4Cxx 重定向到控制台的日志消息给取消掉,这个可以通过 Log4Cxx 的配置文件来实现

问题搞定了,世界又变得很美好了!

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

在写客户端的时候,经常要传输一些文件,有一些服务器就是用 FTP 来搭建的,这个时候,如何用客户端来发起 FTP 网络连接呢?

如果是用 MFC 来发起 FTP 文件传输请求,和使用 MFC 来发起 HTTP 请求类似,非常简单。理论基础可以仔细阅读 MSDN 上的官方文档 Steps in a Typical FTP Client Application浏忙绪绪在这里,就贴上我自己写的代码,与大家分享一下。

样例代码如下:

/*
 * 通过 ftp 方式来拿文件
 * @param  relativePath, 服务器保存该视频摘要文件夹、相对于 ftp 跟路径的相对路径
 * @notice relativePath 应该是类似于 /test 这样相对于 ftp 根路径的相对路径
 *                      应该以 / 开头
 * @param  savePath, 从远程服务器下载文件后,在本地保存的文件夹路径
 */
BOOL CGetRemoteFile::GetFtpfiles(CString relativePath, CString savePath)
{
    //通过 http GET 协议来获取并保存文件
    CString remotefile   = L"";
    CString saveFilename = L"";

    CInternetSession session;
    session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, 1000 * 20);
    session.SetOption(INTERNET_OPTION_CONNECT_BACKOFF, 1000);
    session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1);

    /* 替换成相应的 FTP 用户名和密码 */
    CFtpConnection* pFtp = session.GetFtpConnection( m_strServerIP,
                                                     m_strUsername,
                                                     m_strPassword,
                                                     (INTERNET_PORT)m_iSeverPort);

    bool result = pFtp->SetCurrentDirectory(relativePath);

    if (!result)
    {
        AfxMessageBox(L"Can't get ftp file");
        return false;
    }

    CFtpFileFind finder(pFtp);
    BOOL bFind = finder.FindFile( L"*", INTERNET_FLAG_RELOAD );
    while (bFind)
    {
        bFind = finder.FindNextFile();
        CString strPath = (LPCTSTR)finder.GetFileURL();
        CString strFile = (LPCTSTR)finder.GetFileName();
        CString ftpPath;
        ftpPath.Format(L"正在下载文件:%s/%s\n", strPath, strFile);
        TRACE(ftpPath);

        CInternetFile* pFile = pFtp->OpenFile(strFile);//注意,这里只需要传文件名

        int len = pFile->GetLength();
        char buf[2000];
        int numread;

        CString filepath;
        filepath.Format(L"%s\\%s", savePath, strFile);
        TRACE(L"保存为文件%s",filepath);

        CFile myfile( filepath, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
        while ((numread = pFile->Read(buf,sizeof(buf))) > 0)
        {
            strFile += buf;
            myfile.Write(buf, numread);
        }
        myfile.Close();

        pFile->Close();
        delete pFile;
    }

    session.Close();

    return true;
}
(0) Comments    Read More   
Posted on 05-08-2011
Filed Under (技术) by waterlin

Emgu CV 是 OpenCV 跨平台的 C# 封装包,主要是为了方便在 C# 里使用 OpenCV 的库函数,下载和安装都很简单,新建一个 C# 控制窗口程序后,Hello World 例子代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Drawing;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;

namespace OpenCVTester
{
    class Program
    {
        static void Main(string[] args)
        {
            //The name of the window
            String win1 = "Test Window";

            //Create the window using the specific name
            CvInvoke.cvNamedWindow(win1);

            //Create an image of 400x200 of Blue color
            using (Image<Bgr, Byte> img = new Image<Bgr, byte>(400, 200, new Bgr(255, 0, 0)))
            {
                //Create the font
                MCvFont f = new MCvFont(Emgu.CV.CvEnum.FONT.CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0);
                //Draw "Hello, world." on the image using the specific font
                img.Draw("Hello, world", ref f, new Point(10, 80), new Bgr(0, 255, 0));

                //Show the image
                CvInvoke.cvShowImage(win1, img.Ptr);
                //Wait for the key pressing event
                CvInvoke.cvWaitKey(0);
                //Destory the window
                CvInvoke.cvDestroyWindow(win1);
            }
        }
    }
}

这里需要注意的是,如果用 Visual Studio 2008 来调试上面的代码,则记得要把 OpenCV 相关的动态链接库放到你测试工程的这个目录里:

\bin\Debug

否则在跑到下面这句代码

CvInvoke.cvNamedWindow(win1);

的时候,就会弹出一个莫名其妙的出错框,提示如下错误:

未处理的"System.TypeInitializationException"类型的异常出现在 OpenCVTester.exe 中。

其他信息: "Emgu.CV.CvInvoke"的类型初始值设定项引发异常

看来,C#调试时的程序动态链接库读取位置,还比较特殊呀~~

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

OpenCV2.1 生成视频选择编码的 bug

最近使用 OpenCV2.1 来生成视频,在 Windows 下碰到这样的问题:不管我用什么样的编码,都没有办法直接生成视频,并且会导致程序崩溃。代码如下:

_writer = cvCreateVideoWriter( video.c_str(),
                               CV_FOURCC('X','V','I','D'),
                               _fps,
                               cvSize(frameW,frameH),
                               isColor );

但是,如果我把编码换上 -1 选项来手工选取视频格式,则能顺利生成视频。代码如下:

_writer = cvCreateVideoWriter( video.c_str(),
                               -1,
                               _fps,
                               cvSize(frameW,frameH),
                               isColor );

这个应该是 OpenCV2.1 里的一个 Bug,换成 OpenCV2.2 就没有这个问题了。

OpenCV2.2 的 Visual Studio 2008 问题

官方的 OpenCV2.2 是在 Visual Studio 2010 下编译的二进制包,所以,当你把 OpenCV2.2 嵌入到 Visual Studio 2008 里,可以正常编译相关的代码,但是一运行,一定会提示找不到动态链接库 msvcp100d.dll 或是 msvcr100d.dll

这个时候,你可以把这两个 Visual Studio 2010 的动态链接库拷贝到编译好的可执行文件目录里。但是,最好的方式,是你在 Visual Studio 2008 里重新编译一下 OpenCV2.2

OpenCV 读取视频帧与 DirectShow 读取视频的步调不一致

有一些视频,我用 OpenCV 跳转到指定的时间,和用 DirectShow 跳转到视频指定的时间,理论上来说,应该是一样的。可是,在我这里,偏偏就出现了跳转视频位置不同的情况。而且,原始视频越长,这种误差越大。

是什么原因呢?

仔细研究了一下,我用 OpenCV 读取参数,会显示是 29 帧/秒,大约有 276168 帧;但是用 DirectShow 解码后显示为 30 帧/秒,计算后约为 268946 帧。

解码后的度量时间标准不同,也就导致了上述误差。但是,真实的原因是什么呢?

(0) Comments    Read More   
Posted on 14-07-2011
Filed Under (技术) by waterlin

在 Linux 下,如果你写好了自己的动态链接库,需要在其它程序里调用,则需要让这些程序能找到这些动态链接库。如果设置不对,会出现类似如下的错误:

test: error while loading shared libraries: libexampleso.so.0: cannot open shared object file: No such file or directory

这是因为没有把动态链接库的安装路径(例如说是 /usr/local/lib )放到变量 LD_LIBRARY_PATH 里。

这时,可以用命令 export 来临时测试确认是不是这个问题:

export LD_LIBRARY_PATH=/usr/local/lib

在终端里运行上面这行命令,再运行这个可执行文件,如果运行正常就说明是这个问题。

接下来的问题是:以上做法,只是临时设置变量 LD_LIBRARY_PATH ,下次开机,一切设置将不复存在;如何把这个值持续写到 LD_LIBRARY_PATH 里呢?

我们可以在 ~/.bashrc 或者 ~/.bash_profile 中加入 export 语句,前者在每次登陆和每次打开 shell 都读取一次,后者只在登陆时读取一次。我的习惯是加到 ~/.bashrc 中,在该文件的未尾,可采用如下语句来使设置生效:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

修改完后,记得关掉当前终端并重新打开一个新的终端,从而使上面的配置生效。

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

我的笔记本电脑因为之前分区的原因,没有办法安装双系统,导致调试 Linux 程序诸多不便。如果你也有这种情况,或是有很多事情都需要同时在 Windows 和 Linux 下处理,可以和我一样,在本地装一个 VirtualBox 虚拟 Ubuntu 环境,配好相应的开发环境后,从事 Linux 代码的开发与调试工作。

主要思路

把这个 VirtualBox 里的 Linux 当成服务器,用 putty 连上去,慢慢调试;本地文件,可以用 Eclipse 编辑好,通过文件共享或是 SCP 的方式,传到虚拟机里的 Linux 服务器里。

主要配置步骤

  1. 根据VirtualBox 与宿主机器有四种网络设置方式的介绍,把 VirtualBox 里的 Linux 上网方式设置成为 Bridged networking 模式,这样,你在宿主机器里就可以把这个虚拟机当成普通服务器;
  2. 根据你自己的需要,配置 Linux 服务器环境;
  3. 配置并开启虚拟机里 Ubuntu 的 SSH 服务

    详细的 SSH 配置方法可以看一下在 Ubuntu 下开户 ssh 服务里的介绍。

  4. 同步文件到 Ubuntu 服务器

    可以用 WinSCP 同步 Windows 系统里的文件到 Ubuntu 里,当然,也可以采用 FTP 或是 SFTP 之类的软件来同步文件。

PS:如果你觉得用 WinSCP 来同步文件略显麻烦,你可以在 Windows 里设置一个供 Ubuntu 使用的共享目录,然后你直接把东西扔到这个目录,就可以在 Ubuntu 里访问相关的文件;当然,使用这种方法,你要小心保护好你的文件,不要轻易地删除共享目录里的文件。

出于模拟及方便的角度,我依然推荐使用 WinSCP 的方式来同步文件。

(0) Comments    Read More   
Posted on 09-07-2011
Filed Under (技术) by waterlin

用 OpenCV 来读取视频,经常需要从指定帧序号的片断开始读取,这个时候,就需要用 cvSetCaptureProperty 结合参数 CV_CAP_PROP_POS_FRAMES 来设定,例子代码如下:

bool PickSomeFrames(const char* filename, int start, int end, char* savePath)
{
    CvCapture* capture = cvCaptureFromAVI(filename);  //读取视频文件

    cvSetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES, start);

    int count = start;
    while( cvGrabFrame(capture) && count <= end )
    {
        IplImage* pFrame = cvRetrieveFrame(capture);// 获取当前帧

        char test[100];
        sprintf(test,"%s\\%d%s",savePath,count,".jpg");
        cvSaveImage(test,pFrame);

        count++;
    }

    cvReleaseCapture(&capture);

    return false;
}

以上这种方法,支持用多线程的方式,来同时读写视频不同帧序号开始的片断。以上这些代码,在采用多线程来加速视频处理时,特别有用。

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

趁着今天有空,把 WordPress 维护了一把,不小心,碰到如下两个问题:

错误1:Call to undefined function wp_get_current_user()

我准备在后台把 WordPress 从当前版本升级到 3.1.3,可是却出现了如下错误提示:

Fatal error: Call to undefined function wp_get_current_user() in /home1/cookwhyc/public_html/wp-includes/capabilities.php on line 1028

上 WordPress 官方论坛找了找几处说法,原因应该是一些插件还停留在旧版本的 WordPress 架构上,还会用旧方法调用 wp_get_current_user() 这类函数,引用一位人士的解释:

Esmi - the problem isn't that it isn't there. Its that current_user_can is being called by the plugin and for some reason although capabilities.php has been loaded (which contains the current_user_can function) that function calls wp_get_current_user which is defined in pluggable.php but hasn't been loaded when the call is made

I think its actually a bug in WordPress - the solution is to add a require_once call to the top of capabilities.php.

解决办法可以在 /wp-includes/capabilities.php 文件正式代码的开头,添加下面这行语句:

require_once('pluggable.php');

希望以后的 WordPress 版本自动兼容这些问题。

错误2:You do not have permission to access this page.

在解决了上述问题后,我在控制面板点击升级到 3.1.3 这个链接,却又出现了错误页面,提示以下错误:

You do not have permission to access this page.

这个问题比较怪异,除了无法通过 Network Admin 部分来管理站点外,其它一切都正常,包括博客的前端页面及各个独立博客的管理面板。

即使我手动升级 WordPress 到了 3.1.3,依然没有办法解决这个没有权限访问的问题。

这个问题应该是由于插件不兼容新版本的 WordPress 造成的,手动删除了该插件即可解决此类问题。

想要手动删除某个插件,直接到下面这个目录删除该插件即可:

/wp-content/plugins

在我这里,是由插件 Ad Inserter 造成的。不知道为什么,Ad Inserter 在我的 3.1.3 版本 WordPress 里使用就会有这个问题。

经验总结:升级 WordPress 之前,一定要先把所有的插件都关闭,然后仔细地检查一下所有的插件是否兼容新版本的 WordPress,否则就很难找到具体是哪个插件出了问题。

(0) Comments    Read More   

最近被一个问题折磨了好几天,VC++2008 编译出来的 Debug 版本程序,拷到目标机器上,没有办法运行。我用 VC++2008 编译的文件在自己电脑上可以运行,可一放到别人电脑上就显示程序配置有问题,试了几台电脑都这样,拿到另一台装了 VC++2008 的电脑上又正常了。以前用 VC++6.0 的时候没这么多事,这是怎么回事呢?

类似这种情况下,按理说应该是少了某个动态链接库,但是我确定第三方的动态链接库都拷贝到目标机器上了。

在目标机器上安装 Microsoft Visual C++ 2008 Redistributable Package (x86) 后,程序依然不能运行。但是如果我装给目标机器装上 VS2008 ,程序就可以顺利执行。

这个问题和 Debug 版本有关吗?还是 VS2008 的问题?编译成 Release 版本能不能解决这个问题呢?

我用静态编译的方式,编译出的 release 版本就更奇怪了,在我自己的电脑上运行,都提示如下错误:

"无法启动程序,因为计算机中丢失 MSVCP90.dll。尝试重新安装该程序以解决此问题。

这个库我怎么可能会没有?

上 MSDN 仔细找了找原因,出现类似上面的问题,有以下几点需要注意:

  1. 按道理来说,编译成 release 版本后,只要在目标机器上安装相应版本的 vc_redist 就可以了;
  2. 对于 VS2008 版本,光把编译生成的可执行文件及 Dll 拷贝到目标机器上是不行的,要加上 manifest 文件;
  3. 动态链接的程序,需要在文件目录里复制 MFC90.dll, MSVCR90.dll 和 manifest 文件,不加上这个 manifest 是运行不了滴。

在寻找答案的过程中,也在 StackOverflow 里提问获得了帮助,原文是 Where is msvcp90d.dll supposed to come from?,笔记一下以备查阅。

如果需要检测程序依赖的动态链接库有哪些,可以使用 Dependency Walker 这个工具。

PS: 看来 Stackoverflow 里,问题的回答质量是相当高的,以后可以尝试多用。

(0) Comments    Read More