显示标签为“Windows Programming”的博文。显示所有博文
显示标签为“Windows Programming”的博文。显示所有博文

2009年3月16日星期一

Lnk2001: unresolved external symbol _com_util::ConvertBSTRToString

Today my colleague encountered a strange Lnk2001 error while using VS2003.net to build a project.

Here is the error snippet:

error LNK2019: unresolved external symbol "unsigned short * __stdcall _com_util::ConvertStringToBSTR(char const *)" (?ConvertStringToBSTR@_com_util@@YGPA_WPBD@Z) referenced in function "public: __thiscall _bstr_t::Data_t::Data_t(char const *)" (??0Data_t@_bstr_t@@QAE@PBD@Z)

error LNK2019: unresolved external symbol "char * __stdcall _com_util::ConvertBSTRToString(unsigned short *)" (?ConvertBSTRToString@_com_util@@YGPADPA_W@Z) referenced in function "public: char const * __thiscall _bstr_t::Data_t::GetString(void)const " (?GetString@Data_t@_bstr_t@@QBEPBDXZ)

I checked if the correct lib (comsuppw.lib) is included. But he already did.

The same code can pass build in VC2005. But in VC2003, it can’t. That’s interesting.

One possibility is the environment of VC2003 differs from VC2005. By making a rough search, I found they are almost same. After I googled this bug, a post inspires me. I forget to check the method’s return type and argument type.

Let take a look at the same errors in VC2005:
Error    5     error LNK2001: unresolved external symbol "char * __stdcall _com_util::ConvertBSTRToString(wchar_t *)" (?ConvertBSTRToString@_com_util@@YGPADPA_W@Z) XLRCreatorDll.obj
Error    6     error LNK2001: unresolved external symbol "wchar_t * __stdcall _com_util::ConvertStringToBSTR(char const *)" (?ConvertStringToBSTR@_com_util@@YGPA_WPBD@Z) XLRCreatorDll.obj


Compare the error msg in VC2005 and VC2003, we can see the return type and argument type is different:

char * __stdcall _com_util::ConvertBSTRToString(unsigned short *)    ----VC2003

char * __stdcall _com_util::ConvertBSTRToString(wchar_t *) ----VC2005


What does this mean?

Yes, the wchar_t in VC2003 project is treated as unsigned short instead of wchar_t itself. We know in some old OS implementation, wchar_t is only typedef of unsigned short. But in new release, wchat_t is definitely a native data type to support Unicode.

Then we know, in VC2003, the compiler still treats wchar_t as unsigned short rather than a native data type. But the prototype in comsuppw.lib, it requires wchar_t.

Thus comes the very simple solution: add /Zc:wchar_t to command line of compiler.

After adding this option, the annoying Lnk2001 disappears.

2009年2月8日星期日

log to console when programming windows application

我们在开发Windows界面程序的时候往往需要打印一些运行过程中的调试信息。很多人都使用OutputDebugString来把信息打印到WINDOWS内部的调试器上。然后通过SysInternal的DebugView来查看。

今天我来介绍另外一个非常有用的方法--打开一个控制台窗口

Impossible! 你是在开发窗口程序,怎么可能打开控制台呢?或许有人会这样说。如果不可能,我还写这么多干什么?事实上只要2行代码就可以搞定:

AllocConsole();
freopen( "CONOUT$" , "a" , stdout );
这两行代码打开了一个和当前应用程序共享同一个进程空间的控制台。因此你可以简单的用cout来输出任何你想输出的信息。如下图:

不过一般在这两句话只会用在debug版本下,因此最好在外面加上对DEBUG版本的判断:
#ifdef _DEBUG
AllocConsole();
freopen( "CONOUT$" , "a" , stdout );
#endif
最后提一下,这两个窗口无论关掉哪一个,都会同时关闭另外一个,相当于同时退出进程。

该技巧在编写WinForm的时候也是适用的。所要做的只是把项目设置(Project Setting)中的Output Type改为"Console Application", 如下图:

有了这个Console,你就可以在程序中随意的使用System.Console.WriteLine输出调试信息了。

写windows程序的朋友不妨试一下。

WM_PAINT no end

今天在写程序的时候,发现一个自定义控件的WM_PAINT被不断的调用,以至于CPU占用率上升到40%以上。

我觉得很奇怪,我并没有移动窗口或者做什么引起重绘的操作,WM_PAINT为什么会被不断调用了。之后我发现一开始该控件没有不断重绘,只有第一次重绘之后才开始不断调用。
我的重绘代码是这样写的:

HDC hdc = GetDC(m_hWnd);
....do some drawing
Release(m_hWnd, hdc);
我很老实的遵守了调用DC的对称性规则,有创建也有释放。看来问题不在这里。

再一次仔细看了WM_PAINT的说明其中有几句引起了我的注意:
a WM_PAINT message may have been caused by both a non-NULL update region
仔细体会一下,就得出了这个问题的背后黑手--update region。

我曾在Windows绘图概述一文中写道:
事实上WM_PAINT产生的条件就是有部分窗口失效了。如果不出现这种情况,就不会产生WM_PAINT
当窗口的Client Area发生改变的时候,系统给应用程序发送WM_PAINT消息。系统仅在消息队列中没有其他消息的时候发送该消息给应用程序。一般的用法是在WM_PAINT中调用BeginPaint获得DC,然后进行任何GDI绘图,最后通过EndPaint释放DC
BeginPaint会把需要更新的区域设为NULL,以防连续导致发送WM_PAINT消息。
这里的文字已经很清楚的告诉了我原因,很可惜,这些文字是翻译自MSDN,没有经过自己亲身经历,因此没有深刻的体会。而这次,我正是犯了这个错误。

我接收到的WM_PAINT的消息是由InvalidateRect(NULL)导致的。该函数把整个Client区域都变成了update region.而我在WM_PAINT中又恰恰用了GetDC而不是BeginPaint函数来获取DC,因此导致处理完WM_PAINT后,失效区域没有更新,让Window以为我一直没有重绘,因此DefWindowProc中不断的重复发送WM_PAINT到message queue中,导致了无限循环。

解决的方法有两个,一个是在WM_PAINT中用BeginPaint和EndPaint来处理。另外一种是在WM_PAINT中使用ValidateRect函数。

Bjarne Stroustrup说过,一知半解的程序员是危险的,这个问题没有花去我很多时间,但是它告诉我,不要小看任何一个小小的函数。它或许牵涉到很大的问题。