激烈振动

Visit My MSN Space

2006年3月24日 #

ATL3.0组件注册bug的解决方法

ATL3.0编写的组件在注册时,如果组件所在目录包含中文路径,不能在注册表生产正确的路径,从而产生虽然注册成功,却不能使用的问题,因为在注册表记录的DLL路径中文部分有乱码。这个问题很久以前就发现,一直没有解决。前段时间在网上搜索到解决方案,并转载在blog上,却一直没有实际试用。今天企图使用,却发现不成功,昏倒,试了几次都不行。看来网上的代码还是不能轻信。不知道文章的作者有没有实际调试成功,不过思路是没错的,因此自己改了下,调通了,这里记录一下。

解决方案:修改STAREG.H文件,修改196行开始的AddChar 和 AddString函数,修改后的代码如下:

        BOOL AddChar(const TCHAR* pch)
        {
            
//if (nPos == nSize) // realloc
            
//fix register bug with chinese path
            if (nPos == nSize - 1 )
            {
                nSize 
*= 2;
                p 
= (LPTSTR) CoTaskMemRealloc(p, nSize*sizeof(TCHAR));
            }
            p[nPos
++= *pch;
#ifndef _UNICODE
            
if (IsDBCSLeadByte(*pch))
                p[nPos
++= *(pch + 1);
#endif
            
return TRUE;
        }
        BOOL AddString(LPCOLESTR lpsz)
        {
            USES_CONVERSION;
            LPCTSTR lpszT 
= OLE2CT(lpsz);
            
while (*lpszT)
            {
                AddChar(lpszT);
#ifndef _UNICODE
                
//fix bug with chinese path
                if (IsDBCSLeadByte(*lpszT))
                    lpszT
++;
#endif
                lpszT
++;
            }
            
return TRUE;
        }
编译时必须使用_ATL_STATIC_REGISTRY,即静态链接ATL代码,而不使用ATL.dll,否则无效,因为正是ATL.dll的代码出了问题。

posted @ 2006-03-24 13:09 vibration 阅读(600) 评论(2) 编辑

2005年11月16日 #

奇怪的引用错误及解决方法

碰到一个奇怪的问题。

症状
在一个.net workspace中包含多个project,其中两个project应用了同一个.net assamply。这两个project其中一个编译正常,另一个则报错:
error CS0246: The type or namespace name '***' could not be found (are you missing a using directive or an assembly reference?)
居然说我没有正确引用,可是检查项目设置都没有问题。关闭workspace在打开,好了。第二天再编译,又有问题,晕了

错误原因

google半天未果,自己研究。发现被引用的项目有一个Copy Local属性,默认为true,就是把应用的assamply拷贝到输出目录下。原来两个project都企图把同一个assamply拷贝过来,而拷贝成功后还锁定了这个文件。这样第一个项目操作成功并锁定文件后,第二个项目拷贝就失败了,因为无法覆盖被锁定的文件。

解决方法
原因找到了,解决就不难了,把其中一个项目中的引用assambly的Copy Local属性改成false,再编译,就一切OK了 同理,如果有多个project引用同一assamply,除了其中一个的Copy Local属性为true,其他改成false就行了。GAC中的assambly不存在此问题,因为默认Copy Local属性为false。

posted @ 2005-11-16 15:52 vibration 阅读(397) 评论(0) 编辑

2005年11月4日 #

OLE2T在VS2003中转换中文失败的问题及解决方法

以前VC6的代码,从xml文件中读取属性文字(中文),返回BSTR类型,用OLE2T转换,然后显示,一直工作很正常,用来做本地化,比修改Res文件要方便的多。

同样的代码,在VS2003里面居然不能工作了,调试,发现BSTR返回正常,而通过OLE2T转换居然就全转成“???”了,晕。

做了无数尝试,发现用unicode编译就正常。可是原来的VC6项目不论是否Unicode编译都正常的,要把一个项目全部转成Unicode还是有相当工作量的,似乎也并没有必要。

寻找解决方法,看了一堆ATL的代码,ATL7提供了OLE2T的代替品COLE2T,用一个重载是COLE2T(bstr, codepage),发现给把第二个参数设置成CP_ACP,即ANSI code page就转换正常了。原来是codepage的问题,ATL搞了一大堆代码来获取转换用的codepage,看得人晕,而我要的只是ANSI code page。

继续找,终于找到了,ATL的代码通过一条宏定义进行了分支。如果定义了宏_CONVERSION_DONT_USE_THREAD_LOCALE,则对当前code page的请求简单返回CP_ACP,否则,就搞了一大堆代码从当前线程中查询。那么我们要做的就是定义这个宏就可以了。试了一下,果然如此

最终解决方案,在stdafx.h的所有包含文件前面加上:
#define _CONVERSION_DONT_USE_THREAD_LOCALE

搞定

posted @ 2005-11-04 17:03 vibration 阅读(751) 评论(1) 编辑

2005年9月20日 #

标题栏按钮的WTL实现

起因

上次完成了CAppBar的代码后,就一直想在标题栏的关闭按钮前面加一个按钮,来控制自动隐藏的设置。在标题栏上加按钮并不算特别麻烦,主要是控制WM_NCPAINT,把想要的效果画上去。当然,原理不复杂,要实现的好却不容易。上网搜了一下,有一些简单的例子用来讲述原理,没有找到比较好的实现,更谈不上可复用的代码了,那么只好自己实现了。

原理

首选必须明白原理,想在标题栏上创建CButton这样窗口按钮的努力是徒劳的。标题栏属于Non-Client区域,不能在其上创建子窗口,唯一的方法就是响应WM_NCPAINT消息,通过Window DC画上去,当然,还需要处理一些其他的鼠标消息以得到按钮的效果。这样,实现的思路就和CAppBar差不多了,再创建一个模板类,于是,CCaptionButton<T>就诞生了。 先看看效果

按钮图片

因为是画上去的按钮,我们需要准备一些按钮图片。一个按钮至少需要3个图片来表示3个状态:

  • 正常状态
  • 按下状态
  • 鼠标停留状态
另外,如果需要下面两种额外状态,就需要一共提供5个按钮图片
  • Checked状态
  • Disabled状态

我们通过一个ImageList来组织这些按钮图片。一幅象下面这样的图片资源可以产生一个按钮需要的ImageList,当然也可以通过其他方式实现。

当添加一个标题栏按钮时,我们需要这些按钮图片,参数的类型是HIMAGELIST

添加标题栏按钮

我们需要支持多个标题栏按钮,因此在CCaptionButton类中,通过一个vector来维护所有按钮的信息。具体实现不多说了,就讲怎么使用吧,添加按钮的方法就是:

 int AddButton(UINT uID, int cx, int cy, HIMAGELIST himl, LPCTSTR lpszHint=NULL);

其中,uID是按钮的ID,当按钮被点击时,会有一个WM_COMMAND消息发送给窗口,uID为参数。因此,处理标题栏按钮点击的方法和处理普通按钮完全相同。cx和cy定义了按钮的宽度和高度,这个尺寸必须和按钮图片相符。himl则包含了3-5幅图片,表示按钮不同状态下的样子。最后,lpszHint是按钮的tooltip提示文字。

控制按钮位置

加入的按钮显示在标题栏的什么地方呢?自动控制按钮的位置似乎是一个可重用的类应该完成的任务。在类的实现中,我尝试自动计算标题栏上的空白区域,让我们添加的标题栏按钮靠窗口右边已有按钮排列。然而,我发现,由于存在许多不同的窗口样式,如是否有最小化按钮,是否有问号按钮,是否窄标题栏(即ToolWindow),自动计算很容易产生错误。不如让继承类来决定如何排放按钮位置。因此,提供了下面这个函数供继承类重载:

 POINT GetButtonPos(int index);

CCaptionButton为这个函数提供了一个默认的实现,但是,这个实现比较简单,只有在窗口为ToolWindow,并且没有任何系统按钮的情况下才能正常显示。通过重载GetButtonPos函数,实际上我们得到了比自动计算强的多的功能,即我们可以在任意位置显示按钮,甚至可以控制按钮水平或者垂直排列,就象例子程序中看到的那样。

使用方法

总结一下使用方法

  • 把CCaptionButton作为一个父类继承
  • 使用CHAIN_MSG_MAP把消息传递到CCaptionButton类
  • 调用AddButton函数添加一个或多个按钮
  • 重载GetButtonPos函数提供每个按钮的显示位置
  • 处理标题栏按钮产生的Command消息
  • 可以调用CheckButton改变按钮的Check状态
  • 可以调用EnableButton改变按钮的Enable状态

示例程序

示例程序中使用了5个标题按钮,并配合使用了CAppBar类,以实现桌面停靠的功能。5个标题按钮的排列使用了普通方式和垂直排列的方式,通过继承GetButtonPos函数实现。其中图钉按钮控制AppBar窗口的自动隐藏设置;其他4个按钮使窗口停靠到桌面的一边;并且把停靠到底部的按钮设置成Disabled,可以看到不同的效果

下载示例程序

posted @ 2005-09-20 10:45 vibration 阅读(2015) 评论(3) 编辑

2005年9月14日 #

AppBar的WTL实现

关于Docking Window的文章有很多,基本都是讲主程序内部的工具栏小窗口的Docking,看看代码,一般都很复杂。关于桌面Docking的文章并不多见。实际上从Windows95开始,Win32 API就提供了一个叫做SHAppBarMessage的函数,用于实现完全等同于Windows Task Bar的功能。即可以停靠在屏幕在任意一边,并把自己排除到桌面空间之外。当其他应用程序窗口最大化的时候,不占据Task Bar的空间。同时,也可以设置成自动隐藏,让出所有的桌面空间。这样的窗口,就称为AppBar。

象QQ这样的程序可以停靠在窗口的一边,也可以自动隐藏,但似乎和AppBar的行为还是有不少差距的。本文提供的是对标准AppBar的一个实现。

利用SHAppBarMessage函数实现一个AppBar并不困难,这篇文章介绍了几乎所有实现细节,但所提供的例子却是SDK风格的代码,基本没有实际使用价值。但是对于SDK文档是一个很好的补充,很多SDK中没有说清楚的问题在文中给出了解释。

另一个问题是,SHAppBarMessage函数并没有提供我想当然认为应该实现的功能。比如对拖动的支持,比如自动隐藏窗口的隐藏和出现功能都必须额外的代码实现。SHAppBarMessage函数所做的主要工作似乎是划分屏幕,使其他窗口不会占用AppBar的空间。当然,能实现这个也足够了,毕竟,没有SHAppBarMessage函数,其他功能肯定都可以实现,而这个划分屏幕的功能我不知道该怎么做。

写一个可重用的AppBar基础类的愿望马上出现在我头脑中。WTL由于其超低的耦合性成为我必然的选择。模板类CAppBar<T>就是工作的结果。这个类不只是对SHAppBarMessage函数的简单封装,同时提供了通过拖动实现停靠或脱离屏幕的功能,并提供自动隐藏的实现。

任何从CWindowImpl直接或间接继承的弹出式窗口类都可以使用CAppBar。使用的方法很简单:
    1、把CAppBar作为一个父类继承
    2、使用CHAIN_MSG_MAP把消息传递给CAppBar
    3、在窗口创建时调用InitAppBar函数,参数可选
这样,这个窗口就具备了AppBar的功能了,下面你就可以通过拖动把它停靠在屏幕边上了。
当然,也可以通过调用DockAppBar停靠或脱离;或者调用SetAutoHide改变自动隐藏的设置。
如果在停靠状态改变时需要有特殊操作,可以重载OnDockingChanged函数。

基本就是这样,代码很简单,就象其他WTL类一样。希望下一版的WTL可以收录它

下载例子程序

posted @ 2005-09-14 17:05 vibration 阅读(2323) 评论(6) 编辑

2005年9月9日 #

招聘C++和C#开发工程师

摘要: 招人了,需要比较扎实的C++基础,并能够使用C#开发,C++基础好的话,C#学一下也挺快,所以C#经验不作强制要求。工作地点在上海浦东,靠近科技馆。月薪应该在5k-8k左右。欢迎把简历投递到:atahr@ata.net.cn 并抄送给我:yaozhifeng@ata.net.cn 有任何问题可email给我。下面是发布在51job的广告,直接拷贝过来了。 招聘职位 系统开发中心 地址:浦东民生路...阅读全文

posted @ 2005-09-09 15:04 vibration 阅读(1100) 评论(1) 编辑

2005年9月7日 #

ATL组件中文路径注册问题(转载)

摘要: ATL组件注册的一个很糟糕的BUG,以至于需要通过修改ATL源码来解决。记录在此,方便下次重装机器后使用。 我曾用ATL写过一个COM组件(MBCS下编译),如果安装在中文路径下的话,注册就会失败. 为什么会失败? 打开ATL的源文件statreg.h,可以找到函数BOOL AddString(LPCOLESTR lpsz),他被组件的UpdateRegistry所调用,他又调用了BOOL Add...阅读全文

posted @ 2005-09-07 17:11 vibration 阅读(682) 评论(0) 编辑

2005年6月30日 #

关于C++模板的连接问题

摘要: 好久没写模板了,这次做一项目,发现一处非常适合使用模板应用,于是写了个模板类,大概类似于酱紫//头文件template<classTElement>classTTTextT{protected:vector<TElement>m_chVector;public:intCount();}//CPP文件#include"TTText.h"template<classTEl...阅读全文

posted @ 2005-06-30 14:27 vibration 阅读(3755) 评论(12) 编辑

2005年6月2日 #

用窗口消息解决COM接口的多线程访问问题

摘要: 上篇讲了COM接口的多线程访问问题,并用全局接口表的方法解决了。但有时候我们不能直接访问接口指针,而是通过一个封装类间接的访问。比如:classSomeClass{private:IMyInterface*m_pInt;public:voidMethod1(){//Initm_pInt}voidMethod2(){//callmethodofm_pInt}}我们只能访问SomeClass的公共方法...阅读全文

posted @ 2005-06-02 10:16 vibration 阅读(1451) 评论(2) 编辑

2005年5月30日 #

用全局接口表实现COM接口在不同线程中的传递

摘要: 在多线程程序中使用COM对象真是件令人头疼的事情。当你能够访问一个接口指针的时候,并不意味着你可以调用接口上的方法。我从来没真正搞懂过所谓COM的几种线程模式。问题是,当我试图在一个线程里调用一个COM接口的方法,而这个COM接口是在另一个线程中创建时,从来没有成功过。更糟的是,连错误提示都没有。在多个线程中传递接口需要额外的工作,各种书籍上都介绍了所谓Marshal一个接口方法,不过我从来没有使...阅读全文

posted @ 2005-05-30 10:13 vibration 阅读(1299) 评论(0) 编辑