Archive for the ‘东东博客’ Category

从博弈论看“抵制日货”

星期二, 18 9 月, 2007

最近在看谢洪波写的《生活中的博弈论》,关于日本问题摘录两段如下,让我们一起来抵制日货

对于日本这样一个只崇拜强者的近邻,我们只能以实力来求得与对方的均衡,赢得对方的尊重。中日双方政治冷淡,这是事实,也是策略,但对于中国来说,政治寒冷,贸易额也屡创新高的“经热”对自己极为不利。日本从中国得到了巨额贸易顺差,实力更加强劲,这无异于资敌养虎;而与日本经济越密切,给欧美进入中国市场从中分食蛋糕的机会就少,反而加剧中国与欧美的贸易摩擦,使中国没法玩转“经济牌”。

日本与中国和韩国都存在岛屿与主权冲突,但日本在与中国钓鱼岛的冲突中采取强硬态度,而在与韩国的“独岛”冲突中采取软弱态度,方式截然不同,根源何在?日本人自己说过:“对韩国人再好,他们不会买日本的产品;对中国人再坏,他们还是愿意买日本产品。”这就是经济上的“投降”付出的政治和国土代价。因此,对日本只有减少经济上对它的“资助”,让它看到一个国家、一个民族的自强、自信,它才会低下高傲的头颅,主动修好双边关系,减少近邻“近攻”的威胁,进而削弱超级大国在东亚的支柱。

C# PropertyGrid自定义UserControl项不可编辑实现

星期一, 10 9 月, 2007

C#的PropertyGrid控件支持给属性项自定义Converter和Editor,当指定从TypeConverter派生的Converter时可以只能从下拉列表中选择,即属性值不可手工编辑,但不支持UserControl;而指定从UITypeEditor派生的Editor可以支持UserControl但又没办法限制用户直接编辑属性值。

有时我们需要实现既能从UserControl中设定值又不想让用户直接编辑,就象一些Collection一样,只显示"(Collection)"串不可编辑又可以通过下拉或弹出窗体让用户选择。经过多次实现,发现属性项同时指定自定义Converter和Editor可以解决这个问题,后面附一些实现代码。

定义属性项:
        private ItemContent _itemContent = new ItemContent();
        [EditorAttribute(typeof(ContentEditor), typeof(UITypeEditor)),
        TypeConverterAttribute(typeof(ConentConverter)),
        DescriptionAttribute("Select item content")]
        public ItemContent Content
        {
            get { return _itemContent; }
            set { _itemContent = value; }
        }

定义属性内容类:
    public class ItemContent
    {
        private string _type = "Type";
        private string _content = "Content";

        public string Type
        {
            get { return _type; }
        }

        public string Content
        {
            get { return _content; }
            set { _content = value; }
        }
    }

定义Converter:
    public class ConentConverter : ExpandableObjectConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }

        public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
        {
            return true;
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(ItemContent))
            {
                return true;
            }

            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(System.String) &&
                value is ItemContent)
            {
                return "(Collection)";
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

定义Editor:
    public class ContentEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }

        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            IWindowsFormsEditorService service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            SelectControl ctrl = new SelectControl();

            if (value is ItemContent)
            {
                ctrl.Selected = (value as ItemContent).Content;
            }
            service.DropDownControl(ctrl);

            (value as ItemContent).Content = ctrl.Selected;

            return value;
        }
    }

实现SelectControl的UserControl,提供Selected属性,做一些操作。

最后将包含有ItemContent属性的类对象赋给PropertyGrid的SelectedObject即可。

WTL应用程序处理ActiveX事件更好的方法

星期一, 6 8 月, 2007

之前写的“WTL应用程序接收处理ActiveX控件事件的实现”文章已经描述了如何处理ActiveX的事件,但那个方法实施起来比较繁锁,也不易读解。这里介绍更简便的方法,甚少不需要一个一个的将UUID拷到WTL工程来用,而是直到使用IDL文件编译出来的结果,这样的好处是当这些UUID更改后重新编译就可以了,不需要修改源码。

首先我们写的ActiveX控件可以将IDL公开给使用者(仅项目内或公司内),如果直接把IDL文件给使用者,后者使用#include方法把该IDL包含到其IDL中,编译时可能会有问题,毕竟ActiveX控件的library对使用者是没有意义的反而会导致错误。

解决办法是:(假设工程名是Test,接口是TestCtrl)
1. 在完成接口和事件定义后将library TestLib部分移出到TestLib.idl文件中,原来的idl文件删除该library项只保留interface ITestCtrl和dispinterface _ITestCtrlEvents,在TestLib.idl中使用#include "Test.idl"包含接口。
2. 然后将TestLib.idl添加到工程并删除原来的IDL文件,将Test.idl移到公用目录下(别人可以直接使用),并将该公用目录添加到MIDL的Addional Inlcude Directories中。
3. 将interface和dispinterface中的所有id值定义成宏,文件名格式可以是工程名+DISPIDs.h(与Test.idl放在同一目录下),比如TestDISPIDs.h,在该宏定义文件加上事件的扩展声明,以后用到__uuidof(_ITestCtrlEvents)的地方全用DIID_ITestCtrlEvents代替:
#ifdef __cplusplus
extern "C" const IID DIID__ITestCtrlEvents;
#else
extern const IID DIID__ITestCtrlEvents;
#endif
4. 生成连接点后在_ITestCtrlEvents_CP.h包含TestDISPIDs.h文件。添加实现代码,编译生成DLL并注册,下一步使用。

在WTL工程中,使用IDispEventSimpleImpl代替IDispEventImpl,前者不再需要Lib的UUID,处理方法是:
1. 在MainDlg.h中包含TestDISPIDs.h,定义一个控件ID比如“#define CONTROL_ID 1001”,修改CMainDlg类从IDispEventSimpleImpl中重载:
public IDispEventSimpleImpl<CONTROL_ID, CMainDlg, &DIID__ITestCtrlEvents>
2. 在CMainDlg中添加SINK_MAP:
 BEGIN_SINK_MAP(CMainDlg)
  SINK_ENTRY_INFO(CONTROL_ID, DIID__ITestCtrlEvents, DISPID_CLICK, OnItemClick, &s_infoOnItemClick)
  SINK_ENTRY_INFO(CONTROL_ID, DIID__ITestCtrlEvents, DISPID_DBLCLK, OnItemDblClk, &s_infoOnItemDblClk)
  SINK_ENTRY_INFO(CONTROL_ID, DIID__ITestCtrlEvents, DISPID_CHANGED, OnItemChanged, &s_infoOnItemChanged)
 END_SINK_MAP()
3. 添加事件处理函数:
 STDMETHOD(OnItemClick)(IBaseNode* pItem);
 STDMETHOD(OnItemDblClk)(IBaseNode* pItem);
 STDMETHOD(OnItemChanged)(IBaseNode* pNewItem, IBaseNode* pOldItem);
4. 定义Entry Info为static:
 static _ATL_FUNC_INFO s_infoOnItemClick;
 static _ATL_FUNC_INFO s_infoOnItemDblClk;
 static _ATL_FUNC_INFO s_infoOnItemChanged;
5. 在cpp文件中定义Entry Info实体:
_ATL_FUNC_INFO CMainDlg::s_infoOnItemClick =
{
 CC_STDCALL,   // Calling convention.
  VT_I4,    // Return type.
  1,        // Number of arguments.
 {VT_DISPATCH} // Argument types.
};
_ATL_FUNC_INFO CMainDlg::s_infoOnItemDblClk =
{
 CC_STDCALL,   // Calling convention.
  VT_I4,    // Return type.
  1,        // Number of arguments.
 {VT_DISPATCH} // Argument types.
};
_ATL_FUNC_INFO CMainDlg::s_infoOnItemChanged =
{
 CC_STDCALL,   // Calling convention.
  VT_I4,    // Return type.
  2,        // Number of arguments.
 {VT_DISPATCH, VT_DISPATCH} // Argument types.
};
6. 定义事件函数实体:
STDMETHODIMP CMainDlg::OnItemClick(IBaseNode* pItem){…}
STDMETHODIMP CMainDlg::OnItemDblClk(IBaseNode* pItem){…}
STDMETHODIMP CMainDlg::OnItemChanged(IBaseNode* pNewItem, IBaseNode* pOldItem){…}
7. 将Test.idl包含到WTL工程的IDL文件中(没有则创建一个与工程同名的IDL),编译IDL后将xxx_i.c添加到工程中,编译整个WTL工程,测试。

货币基金可转换图例

星期二, 31 7 月, 2007

使用招行的银基通转换货币基金成其它基金时,不知道哪些基金之间可以转换。而专业版中的基金功能在转换时只列出可以转换成的基金供选择,很方便,因此为了方便使用银基通转换基金,特将偶常用的可转换基金列表截成图贴在这供参考。

 

驱动开发系列(第一个非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

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()