驱动开发系列(第一个非WDM实例)

5 7 月, 2007

一般来说,不管学习啥第一个实例总是不那么容易跑起来。为了对相关的类型和函数有更深的印象,第一个实例应该全部手工输入,可以参照相关的实例或文档说明。只有通过手工输入所有的代码,在编译不过时不断的比照实例或文档找出问题的产生的原因才能很快的入门。

在看技术文档的过程中最后能马上实践,而且前面的例子最好能跑起来,这样继续后面的阅读才有意义,不然总有那种想实践又不知如何下手的感觉,还有就是看到后面前面的就忘掉了。只有不断的实践才能很快的掌握所学的知识。(对E文也应该如此,只不过本人总是找借口偷懒,所以至今还是马马虎虎)

第一个非WDM实例

 所谓第一个实例,当然是越简单也越好,就象各种开发工具文档中的Hello World工程一样。为了简单而且让驱动能跑起来,我们先实践非WDM的实例,只实践了两个必须的函数:驱动入口函数DriverEntry和驱动卸载函数DeviceUnload。

为了能用VC IDE来开发驱动(以VC6为例,配上Visual Assist X),我们新建一个空的Win32 Console Application,实例名为DrvSample。添加空的DrvSample.h和DrvSample.c文件到工程中,然后我们给Additional include dictionaries添加C:\WINDDK\inc\ddk\w2k(Setting->C/C++->Category<Preprocessor>),在XP系统下或开发WDM项目要修改成对就的目录。

1. DrvSample.h的内容:
#ifndef _DRV_SAMPLE_H_
#define _DRV_SAMPLE_H_

#include <ntddk.h>

#if DBG
#define DRVSAMPLE_KDPRINT(_x_) \
 DbgPrint("DRVSAMPLE.SYS: "); \
 DbgPrint _x_
#else
#define DRVSAMPLE_KDPRINT(_x_)
#endif

#define DRVSAMPLE_DEVICE_NAME_U   L"\\Device\\DRVSAMPLE"
#define DRVSAMPLE_DOS_DEVICE_NAME_U  L"\\DosDevices\\DrvSample"

NTSTATUS
DriverEntry(
   IN PDRIVER_OBJECT pDriverObject,
   IN PUNICODE_STRING pRegistryPath
   );

VOID
DeviceUnload(
    IN PDRIVER_OBJECT pDriverObject
    );

#endif // _DRV_SAMPLE_H_

2. DrvSample.c的内容:
#include "DrvSample.h"

NTSTATUS
DriverEntry(
   IN PDRIVER_OBJECT pDriverObject,
   IN PUNICODE_STRING pRegistryPath
   )
{
 NTSTATUS status;
 PDEVICE_OBJECT pDevObj;
 UNICODE_STRING uniDeviceName;
 UNICODE_STRING uniDosDeviceName;

 DRVSAMPLE_KDPRINT(("DriverEntry Enter\n"));

 RtlInitUnicodeString(&uniDeviceName, DRVSAMPLE_DEVICE_NAME_U);
 status = IoCreateDevice(
  pDriverObject,
  0,
  &uniDeviceName,
  FILE_DEVICE_UNKNOWN,
  0,
  (BOOLEAN)FALSE,
  &pDevObj
  );
 if (!NT_SUCCESS(status))
 {
  return status;
 }

 DbgPrint("DeviceObject %p\n", pDevObj);

 RtlInitUnicodeString(&uniDosDeviceName, DRVSAMPLE_DOS_DEVICE_NAME_U);
 status = IoCreateSymbolicLink(&uniDosDeviceName, &uniDeviceName);
 if (!NT_SUCCESS(status))
 {
  IoDeleteDevice(pDevObj);
  return status;
 }

 pDriverObject->DriverUnload = DeviceUnload;

 DRVSAMPLE_KDPRINT(("DriverEntry Leave\n"));

 return status;
}

VOID
DeviceUnload(
    IN PDRIVER_OBJECT pDriverObject
    )
{
 UNICODE_STRING uniDosDeviceName;
 
 DRVSAMPLE_KDPRINT(("DeviceUnload Leave\n"));
 RtlInitUnicodeString(&uniDosDeviceName, DRVSAMPLE_DOS_DEVICE_NAME_U);
 IoDeleteSymbolicLink(&uniDosDeviceName);
 IoDeleteDevice(pDriverObject->DeviceObject);
 DRVSAMPLE_KDPRINT(("DeviceUnload Leave\n"));
}

3. 在DrvSample.c的同一目录下添加Makefile文件,内容总是:
#
# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
# file to this component. This file merely indirects to the real make file
# that is shared by all the driver components of the Windows NT DDK
#

!INCLUDE $(NTMAKEENV)\makefile.def

4. 在DrvSample.c的同一目录下添加Sources文件,内容是:
TARGETNAME=DrvSample
TARGETPATH=obj
TARGETTYPE=DRIVER
SOURCES=DrvSample.c
其中TARGETNAME是要生成的SYS文件名,不包括.sys

5. 用命令行方式编译:
参见驱动开发系列(配置篇)的第5点。

6. 安装:
1) 将编译出得到的DrvSample.sys拷到%windir%\system32\drivers中
2) 往注册表中添加注册项:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DrvSample]
"ErrorControl"=dword:00000001
"Start"=dword:00000001
"Type"=dword:00000001
将上面的代码保存成.reg文件,DrvSample必须与驱动文件名一致。
注意:文档中Start的默认设置是3(Demand),搞得我总是在设备管理器中找不到驱动,试下来才发现设置成1(System)才可以成功安装。需要重新启动机器才能看得到。
Start: 0 – Boot, 1 – System, 2 – Automation, 3 – Demand, 4 – Disabled

7. 测试:
重新启动之后打开设备管理器,在View菜单中选择“显示隐藏设备”,然后在“非即插即用设备”列表中可以看到DrvSample项,双击可以打开属性窗体,切换到Driver而可以启动/停止驱动。在启动/停止驱动时如果运行Dbgview.exe程序就可以看到输出的信息。

万事开头难,完成了第一个可以跑的驱动后,下一步再测试代码就简单多了。

驱动开发系列(配置篇)

5 7 月, 2007

因公司项目需要,开始了新的学习历程:使用DDK开发硬件驱动。

虽然在原来公司接触过驱动开发(仅项目维护),但没有亲自动手开发过驱动,所以这次只能通过论坛和资料开始这个历程了。

DDK安装盘和相关资料可以通过电驴客户端搜索得到,驱动开发论坛主要是驱动开发网

首先从《Windows2000设备驱动程序设计指南.pdf》电子影印版着手,由于没有配套光盘,只能自己分析和摸索。通过在虚拟机上不断的安装测试,终于搞定了最简单的非WDM驱动,在此记录一下。

从配置篇开始一系列的驱动开发实践……

配置编

安装完DDK后就开做一些方便开发的环境配置了,假设DDK安装的位置是“C:\WINDDK”。

1. 将DDK的安装位置添加到系统环境变量:DDKROOT=C:\WINDDK,下一步可以使用%DDKROOT%得到DDK的安装位置。

2. 分别给w2k和wxp配置环境设置批处理文件(视环境情况选择配置):
%DDKROOT%\bin\startddk_w2k.bat文件的内容(安装时要选择w2k的头文件部分):
@echo off
call %DDKROOT%\bin\w2k\set2k.bat %DDKROOT% checked
cd /d %1
cmd.exe

%DDKROOT%\bin\startddk_wxp.bat文件的内容(安装时要选择wxp的头文件部分):
@echo off
call %DDKROOT%\bin\setenv.bat %DDKROOT% checked
cd /d %1
cmd.exe

如果想编译free版本可以把checked修改成free,也可以自由发挥改成参数形式。

3. 分别写注册表项添加文件
w2k.reg内容:
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\shell\DDK Command\command]
@="\"C:\\WINDDK\\bin\\startddk_w2k.bat\" \"%1\""

wxp.reg的内容:
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\shell\DDK Command\command]
@="\"C:\\WINDDK\\bin\\startddk_wxp.bat\" \"%1\""

4. 根据操作系统版本加载不同的reg文件。加载后在资源管理器中右击驱动源码所在的目录选择“DDK_Command”菜单即可进入已经配置好DDK环境的命令行模式。

5. 常用编译驱动的命令是“build -b -w -c -e -f”,用“build -?”可以看到相关选项说明。

现在可以试试去编译%DDKROOT%\src下的各个例子程序了。

好的背景色可以保护眼睛

3 7 月, 2007

Windows系统白底黑字看的时间长了眼睛就受不了,尤其是在看电子书或做软件开发时。所以设置合适的背景色不但能保护眼睛,还可以缓解人的精神压力。昨天在看一本PDF格式的电子书时,由于该电子书每一页都是扫描成图片,在选中一页时感觉其选中的背景色很适合眼睛,因此特使用Photoshop找到其RGB值,在此记录一下。

RGB值是153, 193, 218,转换成GIF可以看到其索引值是6:

效果图:

修改背景图方法:
① 右击桌面选择“属性”
② 切换到“外观”页
③ 点“高级”按钮
④ 在“选项”列表中选择“Window”
⑤ 点击右边的“颜色1”按钮
⑥ 在弹出的颜色选择框中选择“其它”按钮
⑦ 在右下角的“红、绿、蓝”分别输入“153, 193, 218”
⑧ 然后一路“确定”回去即可,如下图:

Update:
有朋友推荐新的背景色,据说是眼科专家给出的调节色,分别是:色调85、饱和度90、亮度205,对应的RGB值是:204, 232, 207,如下图:

AJAX中Get返回的ResponseText中文乱码的最简解决办法

23 6 月, 2007

用AJAX来GET回一个页面时,RESPONSETEXT里面的中文多半会出现乱码,这是因为xmlhttp在处理返回的responseText的时候,是把resposeBody按UTF-8编码进解码而形成的,如果服务器送出的确实是UTF-8的数据流的时候汉字会正确显示,而送出了GBK编码流的时候就乱了。解决的办法就是在送出的流里面加一个HEADER,指明送出的是什么编码流,这样XMLHTTP就不会乱搞了。

PHP:header(‘Content-Type:text/html;charset=GB2312’);
ASP:Response.Charset="GB2312"
JSP:response.setHeader("Charset","GB2312");

摘自:http://www.wujianrong.com/archives/ajax/8.html

prototype.js 1.4版开发者手册:http://www.cnlei.org/blog/article.asp?id=253

生活尝试及小窍门(精华汇总)

22 6 月, 2007

1.使音质变坏的cd变好的技巧

盘面上被划了斜纹,音质变的不好了。解决的办法其实操作很简单。将音质变坏
的cd放入电冰箱2个小时。之后,蒸发掉水分。就好了。当然,划痕很深的话是不行
的。

2.即使没有定发胶也能固定头发

早上起来,头发乱糟糟的。但是,定发胶又用完了。用碳酸饮料来代替吧。涂上
之后,在用吹风机吹,就能定型的很漂亮。用碳酸饮料定型,为什么可以代替发胶
呢?实际上,起作用的是碳酸饮料的糖分。酸饮料含有13%左右的糖。糖分干后凝
结没,被吹风机一吹,就固定了。当然,它是对头发无害的,还可以持续一天呢。
 
 3.不疼的揭除膏药法

贴在腿上的膏药,剥落的时候是很疼的。尤其对于体毛较重的男性来说,是最恐
怖的。怎样不疼就能把膏药剥除呢?其实也很简单,只要用棉棒沾上“婴儿油”
轻轻探到膏药与皮肤接触的一面。这样,膏药就会自然剥落了。而且,还会很干
净的不留痕迹哦。用棉棒剥除膏药为什么那么有效呢?
因为膏药有很多小空,“婴儿油”可以使膏药与肌肤之间产生空间,黏着力就小
了但是您一定要记住,应用这个窍门的时候千万不能用色拉油,因为色拉油对身
体是有害的。

 4.酸橘子变甜橘子的小技巧

冬天是橘子味道最好的季节。但是,有时会碰到酸酸的橘子。这种时候,有一种
方法可以让酸橘子变成甜橘子。方法超级简单,将橘子放在自行车的篮子里,在附
近转一圈。回来后,你再尝尝篮子里的橘子,啊,真是不可思议啊。酸橘子不知怎
么回事变成了甜甜的橘子了。冬天,人们一般喜欢闷在家里,推着自行车转一圈,
正好可以缓解运动不足,而且吃橘子可以获取维生素c,真是美容与健康一举两得
啊。一定要试试啊。
将橘子放在自行车的篮子里,绕附近转一圈。为什么橘子会变甜呢?用这个方法
之后,橘子真的会变甜吗?检测一下橘子的成分,可以发现含糖量11.6%的酸橘
子,在用过这个方法后含糖量仍然是11.6%。但是吃的时候会明显感觉到甜了。实际
上,甜味的变化与橘子当中含的酸度的不同有关系。橘子里既含有产生甜味的糖,
也有产生酸味的酸,酸是很容易受到冲击的,所以它在受到冲击后,就会减少。也就是
说,因为酸减少了,所以才会感觉到甜。检测一下酸度,在使用该方法之前,是
6.6%,而使用后变成了2.1%。由于自行车篮子的晃动,酸受到了冲击,所以减少了。这
样给橘子以冲击,橘子会变得很甜,但是我不赞成给它太大的冲击。像自行车篮子
那样程度的冲击是最合适的。

 5.驱除水壶里水垢的方法

削土豆剩下的土豆皮放入壶里,倒入水没过土豆皮,放在火上加热(就像平常烧
水一样),10多分钟后,把壶里的东东倒出,里面的水垢混杂在土豆皮里一起出来
了,水壶干净了。呵呵!

6.去除米饭烧糊意味的方法

如果不小心将米饭烧糊了,可迅速把火关掉,在米饭上边放一块面包皮,盖上
锅,5分钟后,面包皮即可将糊味吸尽。

7.饼干受潮不好吃了怎么办

饼干受潮变软,用电吹风吹几分钟,冷却后即变得酥脆可口。

8.真丝衣服起皱怎么办

真丝衣服喷湿后,装入塑料袋放入冰箱,10分钟后取出就易烫平。

9.家具用久了失去光泽如何是好

 用干净软布蘸浓茶擦拭失去光泽的油漆家俱,连续擦2~3遍,光亮如新。
 
10.煎炸食物时油总是外溅怎么处理

用油炸食物时,油锅里放少许食盐,油就不会外溅。

11.常自己煮面的同志有福了

煮面条时,如果在水中加一汤匙食油,面条就不会粘连而且还可防止面汤起泡
沫溢出锅外。

12.巧去土豆皮

将土豆放入热水浸泡一下,再放入冷水中,易去皮。

13.桃子毛好讨厌

将桃子放入淡盐水中轻搓,桃毛可很快脱落。

14.在mm面前做蛋汤露一手

做蛋汤时,先往沸汤里滴点醋,蛋汁下锅后形成漂亮的蛋花。

15.切蛋刀法(武侠小说看多了)

切白煮蛋时先将蛋完全冷却,刀上蘸点水,蛋黄不会粘刀。

16.煮饺子后的粘连问题

饺子出锅前先放温水浸泡一下,表面的面糊用冷开水漂清,就不会相互粘边
了。

17.大家都有对葱流泪的经历吧

切葱时,先把刀沾点凉水再切,眼睛就不会被辣得流泪了。

18.光盘被划不好读盘

使用油性笔画在划痕上即可。

19.吃了葱蒜如何去异味

吃了大蒜、葱等食物后,口中易产生异味,可饮半杯牛奶,异味即除。

20.省力的拖地

用拖把拖厨房地面时,在拖把上倒少许醋,可迅速去掉地面上的油垢。

21.大蒜去皮
> 将大蒜掰为小瓣后放在热水中浸泡数分钟,然后捞出,其皮便可很容易剥下。亦
可将蒜瓣放在案板桌上,用菜刀轻拍几下,皮即极易剥掉。

22.洗菜去小虫

从市场上买到的叶类蔬菜上面往往有些蜜虫等小虫子,这些小虫不容易洗下来。
如果先用盐水(一盆水中放半小匙盐即可)浸泡一下,小虫受到盐的刺激,便很快
和菜叶分开。另外,盐水的比重较大,小虫和菜叶脱离开以后,便漂浮在水面上,
很容易从盆中倒出。

23.巧剥橙子皮

人们吃橙子大都用刀切,汁液流溢,既浪费又不卫生。其实,橙子剥皮并不难,
下面介绍一种比较简便的方法。可把橙子放在桌面上,用手掌压住慢慢地来回揉
搓,只要用力均匀地揉搓一会儿再剥,橙子就会像橘子一样容易剥皮,吃起来既干净
又方便。

24.菜刀生锈如何去

把菜刀浸泡在淘米省下的水里,1个小时后用布擦拭即可。

25.使剪刀恢复锋利的小窍门

很多人为了使用钝了的剪刀恢复锋利,往往用磨刀石来磨。其实还有比这更方
便的办法,那就是利用锡箔纸。锡箔纸不一定要新的,用过的旧的就可以了。用
法为:把锡箔纸折成2-3层,然后拿剪刀剪锡箔纸,就这么简单,剪刀或菜刀就
可以 迅速恢复锋利了。

26.治牙疼

干爵绿茶,可以立即缓解牙疼症状,还可治牙龈出血。

27.擦玻璃

擦玻璃时,可用一半醋加一半水调匀,放在喷壶内,喷在玻璃上随喷随用旧而或
旧报纸团擦亮。或先用湿布擦一擦玻璃,然后用干净的湿布沾一点白酒,稍用力在玻璃上擦,擦过后玻璃不仅干净而且明亮。
  
28.居室忌放的花卉

月季花??它所散发出来的香味,会使个别人闻后突然感到胸闷不适、憋气与呼吸
困难。
兰花??它所散出的香气,久闻之会令人过度兴奋而引起失眠。

紫荆花??它所散发出来的花粉如与人接触过久,会诱发哮喘症或使咳嗽症状加
重。

夜来香??它在晚上能大量散发出强烈刺激嗅觉的微粒,高血压和心脏病患者容易
感到头晕目眩,郁闷不适,甚至会使病情加

WTL应用程序接收处理ActiveX控件事件的实现

20 6 月, 2007

使用ATL编写一个带有事件的ActiveX控件后,在VB中测试事件是很方便的,在MFC Application中可以通过“从ActiveX控件导入MFC类”办法来得到控件的包装类,然后插入该控件到窗体上,就可以很方便的处理控件的事件。

使用WTL的BEGIN_SINK_MAP…END_SINK_MAP也可以较方便的处理控件的事件(在MFC中试过此种方法,目前还无法实现),而不需要生成控件类和插入控件,移植性比较好,在此写些实现的步骤和方法。(以VS2003中的VC7.1为例)

1. 生成ActiveX控件

首先创建一个ATL Project项目,如果不需要Attibuted可以把默认钩选去掉(我还没用过这个功能,暂不时其用意)。生成的“工程名+PS”项目好象没有多大意义,我一般都直接删除。

添加一个ATL Control控件(在Class View中右击项目选Add Class,选中ATL类然后在右边找到ATL Control,这跟VC6不太一样),在Options中要选中Connection points以支持事件。

添加接口属性的方法(在Class View中右击接口,弹出菜单可以打开向导),添加事件(在Class View中右击“接口+Events”接口,需要展开“项目名+Lib”才能看到),事件一般都是方法。

添加连接点(在Class View中右击生成的控件类,选择“Add Connection Point”)。

在需要发事件的地方调用“Fire_+事件方法名”来发出事件。

2. WTL中接收事件

创建一个基于对话框的WTL工程,如果没有把WTL的include添加到VC7.1的include路径中,在打资源时会提示找不到atlres.h,解决方法可以是把WTL的include目录添加到VC7.1的include路径中,也可以分别添加到工程中C/C++、Resources、MIDL的Addional Include Directories中。

把控件的IDL文件添加到工程中,编译生成“项目名.h”文件,将其包含到MainDlg.h中,同时也应包含resource.h,向导没有自己包含。

定义控件的事件相关信息:(XXX是唯一标识符)
namespace XXX
{
 enum{
  ID = 给控件指定的ID值, 
  事件枚举1 = IDL中定义的对应事件的ID值,
  事件枚举2 = IDL中定义的对应事件的ID值,
 };

 struct __declspec(uuid("IDL中定义的Events的UUID")) Events;
 struct __declspec(uuid("IDL中定义的Lib的UUID")) Library;
};

CMainDlg类增加从IDispEventImpl派生:
public IDispEventImpl<XXX::ID, CMainDlg, &__uuidof(XXX::Events), &__uuidof(XXX::Library), 1, 0>

定义事件映射和处理事件的方法并实现该方法:
 BEGIN_SINK_MAP(CMainDlg)
  SINK_ENTRY_EX(XXX::ID, __uuidof(XXX::Events), XXX::事件枚举1, 事件处理函数1)
  SINK_ENTRY_EX(XXX::ID, __uuidof(XXX::Events), XXX::事件枚举2, 事件处理函数2)
 END_SINK_MAP()
 HRESULT __stdcall 事件处理函数1([参数]);
 HRESULT __stdcall 事件处理函数2([参数]);

定义一个控件的对象并在OnInitDialog中创建它:
  CAxWindow m_wndCtrl;

  CRect rcDef(10, 10, 240, 120);
  DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
  m_wndCtrl.Create(m_hWnd, rcDef, NULL, dwStyle);
  CComPtr<控件接口> pCtrl;
  HRESULT hr = m_wndCtrl.CreateControl(L"控件的ProgID");
  m_wndCtrl.QueryControl(&pCtrl);
  DispEventAdvise(pCtrl);

调用控件方法的实现:
 CComPtr<控件接口> pCtrl;
 m_wndCtrl.QueryControl(&pCtrl);
 pCtrl->控件方法([参数]);

修改_tWinMain中的“hRes = _Module.Init(NULL, hInstance);”为“hRes = _Module.Init(NULL, hInstance, &LIBID_ATLLib);”。

在stdafx.h中包含WTL的相关头文件,比如:
#include <atlframe.h>
#include <atlctrls.h>
#include <atldlgs.h>
#include <atlctrlw.h>
#include <atlsplit.h>
#include <atlmisc.h>
#include <atlcoll.h>
#include <atlddx.h>

如果需要使用GetDlgItemText和CString等,需要声明_WTL_NO_CSTRING宏禁掉WTL的CString类,并在stdafx.h中atlapp.h之前包含atlstr.h:
#include <atlstr.h>
#include <atlapp.h>

编译Release版本时需要忽略atlmincrt.lib,否则会提示以下错误:
Linking…
LIBCMT.lib(crt0dat.obj) : error LNK2005: __osplatform already defined in atlmincrt.lib(atlinit.obj)
LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function _mainCRTStartup

继承自定义的接口实现

11 6 月, 2007

目的:

首先从IDispatch派生一个接口IBaseNode:
interface IBaseNode : IDispatch{
[propget, id(1), helpstring("property ID")] HRESULT ID([out, retval] BSTR* pVal);
[propput, id(1), helpstring("property ID")] HRESULT ID([in] BSTR newVal);
[propget, id(2), helpstring("property Name")] HRESULT Name([out, retval] BSTR* pVal);
[propput, id(2), helpstring("property Name")] HRESULT Name([in] BSTR newVal);
};

然后想从IBaseNode派生出其它接口比如IDeriveNode:
nterface IDeriveNode : IBaseNode{
[propget, id(3), helpstring("property Title")] HRESULT Title([out, retval] BSTR* pVal);
[propput, id(3), helpstring("property Title")] HRESULT Title([in] BSTR newVal);
};

希望IBaseNode的实例类CBaseNode中实例公用的属性比如ID,Name等,然后其派生接口的实现类CDeriveNode再去实现私有属性。这样不同的派生类可以添加不同的属性,但不用重新实现基接口的方法。

试了好几种办法,CDeriveNode在编译时都报IBaseNode中定义的属法方法没有实现,属于抽象类,不能创建实例。

 

实现:(CSDN连接http://community.csdn.net/Expert/topic/5500/5500513.xml?temp=.9658319)

把IBaseNode的接口实例抽出来写成模板类:
template<class T>
class ATL_NO_VTABLE CBaseImpl :
public CComObjectRootEx<CComSingleThreadModel>,
public IDispatchImpl<T>
{
public:
CBaseImpl(void)
{
}

public:
STDMETHOD(get_ID)(BSTR* pVal);
STDMETHOD(put_ID)(BSTR newVal);
STDMETHOD(get_Name)(BSTR* pVal);
STDMETHOD(put_Name)(BSTR newVal);

protected:
CComBSTR m_bstrID;
CComBSTR m_bstrName;
};

template<class T>
STDMETHODIMP CBaseImpl<T>::get_ID(BSTR* pVal)
{
m_bstrID.CopyTo(pVal);

return S_OK;
}

template<class T>
STDMETHODIMP CBaseImpl<T>::put_ID(BSTR newVal)
{
m_bstrID = newVal;

return S_OK;
}

template<class T>
STDMETHODIMP CBaseImpl<T>::get_Name(BSTR* pVal)
{
m_bstrName.CopyTo(pVal);

return S_OK;
}

template<class T>
STDMETHODIMP CBaseImpl<T>::put_Name(BSTR newVal)
{
m_bstrName = newVal;

return S_OK;
}

然后CBaseNode定义改成:
class ATL_NO_VTABLE CBaseNode :
public CBaseImpl<IBaseNode>,
public CComCoClass<CBaseNode, &CLSID_BaseNode>
{
public:
CBaseNode()
{
}

DECLARE_REGISTRY_RESOURCEID(IDR_BASENODE)

BEGIN_COM_MAP(CBaseNode)
COM_INTERFACE_ENTRY(IBaseNode)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

CDeriveNode定义改成:
class ATL_NO_VTABLE CDeriveNode :
public CBaseImpl<IDeriveNode>,
public CComCoClass<CDeriveNode, &CLSID_DeriveNode>
{
public:
CDeriveNode()
{
}

DECLARE_REGISTRY_RESOURCEID(IDR_DERIVENODE)

BEGIN_COM_MAP(CDeriveNode)
COM_INTERFACE_ENTRY(IDeriveNode)
COM_INTERFACE_ENTRY(IBaseNode)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()

《在别康桥》

7 6 月, 2007

《在别康桥》
徐志摩

轻轻的我走了,正如我轻轻的来;
我轻轻的招手,作别西天的云彩。

那河畔的金柳,是夕阳中的新娘
波光里的艳影,在我的心头荡漾。

软泥上的青荇,油油的在水底招摇;
在康河的柔波里,我甘心做一条水草

那树荫下的一潭,不是清泉,是天上虹
揉碎在浮藻间,沉淀着彩虹似的梦。

寻梦?撑一支长篙,向青草更青处漫溯,
满载一船星辉,在星辉斑斓里放歌

但我不能放歌,悄悄是别离的笙箫;
夏虫也为我沉默,沉默是今晚的康桥!

悄悄的我走了,正如我悄悄的来;
我挥一挥衣袖,不带走一片云彩

终于花了2K大洋参与基金的博弈

3 6 月, 2007

从去年这个时候开始了解基金以来一直没敢出手去买那些需要申购手续费的开放式基金,只是陆陆续续的买了一些货币基金。考虑的主要因素是自己对这基金这方面的运作还不算了解,怕这钱投下去了就没有个回的。

货币基金申购和赎回不需要手续费,在不考虑CPI因素的情况下基本上每天都是有收入的,这样中间即使有某一天收入为负值,也不会出现把本金搭进去的情况。货币基金平均年收益是2.2%左右,与一年定期除去利息税后的收益相当,但它可以跟活期一样的灵活,能在2~3个工作日里变现。定期如果中间变现就会损失利息收益,所以货币基金是代替1~2年定期的理财品种。

在货币基金不能变现的节假日或变现中的日子里,可以合理使用信用卡应急。但不在万不得已的情况下不要使用信用卡取现,因为没有利息优惠,有的卡甚至还要扣手续费(目前已知的工行信用卡不收取现手续费)。平日里也可以将现金拿去购买货币基金,使用信用卡消费,在还信用卡时再赎回来还帐。结果当然是本月要消费的钱赚了1年定期的利息收益率,长期这样作也赚的不少的钱哟。

从开始购买货币基金以来,由于余钱不多,而且短时间内需要使用,所以始终没敢购买那些需要申购手续费的基金。5月份花掉3万把父母接到上海和北京去玩了一圈,之后短时间内急用的钱也不多了,所以经过1周的考虑后决定出手2千大洋探探路了。

前段时间从大学QQ群中了解到目前的基金“老鼠仓”太多,他们推荐购买指数基金比如嘉实300、大成300等。我5月27日在招行的银基通上了解嘉实300时看到了收益率相当的鹏华中国50,自我感觉该基金也是指数型(到现在还不知道判断对不对),而且他它们的申购是1千起,所以决定周一各购买1千试试,想看看手续费之类的是如何扣的。虽然沪指已经逛飘好长时间,相当多的人已经赚到钱,还有相当多人还在涌入,相关新闻报道股市的泡沫离暴掉的已经不远,但考虑到我这2千大洋可以放很长时间,所以就不在乎什么,亏了也亏不了多少。

5月29日收到短信,嘉实300以1.581净值成交623.16份,鹏华中国50以1.689净值成交583.32,手续费都是14.78。周二每净值分别升了3.5、3.6分,算下来已经收回成本。

周三国家开始提升印花税从1‰到3‰,结果是两市暴跌6%左右,呵呵结果当然是亏掉了。上周五的净值,嘉实300是1.48,鹏华中国50是1.592,基本上都跌1角左右。但只要我现在不赎回,再什么跌也不会有影响,过一陈子还是会升上去的,放着吧。

lower_bound在多线程下容易出问题

30 5 月, 2007

lower_bound方法一般应用于排序的系列,比如vector、list等,在插入新的记录前通过这个方法找到插入点,当找不到插入点时追加到最后即可,由此可以生成排序的系列。同样使用这个方法可以找到系列中的某一对象,binary_search方法就是使用的这个方法。

今天我在测试一个项目时发现一个奇怪的现象,就是在多线程情况下通过这个方法查找某一对象时,系列中的某一个对象的值会发生变化,而变化的值有可能还会变成其它值,结果将会导致已排序的系列变成无序,后果可想而知。

假设系列中的每个元素是下面的结构:
typedef struct tagMbParam
{
 std::string strParamName;
 unsigned char nType;
 unsigned short nPduAddr;
} MB_PARAM, *PMB_PARAM;

我们给lower_bound指定一个排序函数,按strParamName从小到大排序:
bool MBPSortFun(MB_PARAM first, MB_PARAM second)
{
 if (first.strParamName < second.strParamName)
 {
  return true;
 }
 else
 {
  return false;
 }
}

我们用下面的方法查找某一对象:
 MB_PARAM mb;
 mb.strParamName = "要查找对象对应的值";
 系列类型::iterator it = lower_bound(系列.begin(), 系列.end(), mb, MBPSortFun);

在单线程时没有问题,在多线程情况下,系统中某一个元素中的strParamName会发生变化,导致问题出现。

我怀疑是不是lower_bound不支持多线程或支持得不够好,因此在该函数的调用前后加上临界区,问题不再出现:
 EnterCriticalSection(&s_csParam);
 系列类型::iterator it = lower_bound(系列.begin(), 系列.end(), mb, MBPSortFun);
 LeaveCriticalSection(&s_csParam);