Posts Tagged ‘C/C++’

CRichEditCtrl使用时的一些注意事项

星期日, 5月 30th, 2010

VC6里使用CRichEditCtrl时有一些需要注意的事项:

1. 必须在InitInstance()里加上AfxInitRichEdit(),包含该控件的窗体才会显示。

2. 如果需要升级使用RICHEDIT 2.0版,只需要在InitInstance()里加上LoadLibrary("RICHED20.DLL")即可,不过记得在ExitInstance()里调用FreeLibrary()。

3. 需要响应SELCHANGE事件时需要在OnInitDialog()使用SetEventMask()注册ENM_SELCHANGE消息,其它的也类似:
m_edt.SetEventMask(m_edt.GetEventMask() | ENM_SELCHANGE);

(更多…)

定时、循环处理某项操作时让间隔时间更准确的方法

星期四, 2月 25th, 2010

我们在写C++代码时,有些情况会考虑操作超时的问题,比如循环读取某个设备的值,如果在指定的时间内读不回全部数据就认为是超时。

最直接的做法是:
for (int i = 0; i < 20; ++i)
{
...... // ①读取操作,成功则直接跳出循环
Sleep(50);  // 每次读失败后延时50毫秒,最多读20次:50ms*20=1000ms
}

这里我们忽略了①处所用的时间,假设读失败的操作用了60ms,那总超时时间应该是(60+50)*20=2200ms,本应该是1秒就超时的,结果用了2.2秒才超时,这会影响到后续的应用。

(更多…)

VC中SetIcon的应用

星期三, 7月 23rd, 2008

VC的MFC向导生成的窗体自动包含以下两句:
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon
用于设置主框架的图标,第一句是使用大图标作主框架的图标,第二名是使用小图标作主框架的图标,一般情况下主框架的图标总是小图标,所以可以把第一句注释掉。

至于如何设置在资源管理器中看到的图标呢,解决办法是把要在资源管理器显示的图标的ID值改为最小,打开resource.h文件就可以看到“#define 宏变量 ID值”,自行调整ID值即可。

这两个图标可以设成不一样的,那么在资源管理器里和程序的主框架就可以看到不同的图标了。

VC中Picture Control透明背景实现

星期四, 6月 26th, 2008

在VC中使用Picture Control来放置图片时想做成透明背景的,选择Bitmap类型时是图片原样显示出来的,想做成透明的Icon然后在Picture Control中选择Icon类型,可是控件缩成了一个图标。最后只能选择Bitmap类型,因为用这种模式可以把很多图片使用多个Picture Control组合起来。

在网上找了一些资料,经过自己多方的实验,终于可以搞定。

原理是重载Picture Control,在窗体初始化时给控件设置图片ID,并去掉控件的Bitmap类型,即设置成“无”类型,然后在OnPaint方法中重画图片。

处理步骤是:
1. Picture Control仍使用Bitmap类型,选择正确的图片并布局好。
2. 在窗体初始化时给控件设置具体的图片ID。

重载的类源码.h文件:
#pragma once

class CTransparentPic : public CStatic
{
 DECLARE_DYNAMIC(CTransparentPic)

public:
 CTransparentPic();
 virtual ~CTransparentPic();

 void SetBitmapIndex(UINT nBitmapIndex);

protected:
 DECLARE_MESSAGE_MAP()

public:
 afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 afx_msg void OnPaint();

private:
 UINT m_nBitmapIndex; // Bitmap index of resource
};

.cpp文件:
#include "stdafx.h"
#include "TransparentPic.h"

IMPLEMENT_DYNAMIC(CTransparentPic, CStatic)

CTransparentPic::CTransparentPic()
 : m_nBitmapIndex(0)
{
}

CTransparentPic::~CTransparentPic()
{
}

BEGIN_MESSAGE_MAP(CTransparentPic, CStatic)
 ON_WM_ERASEBKGND()
 ON_WM_PAINT()
END_MESSAGE_MAP()

void CTransparentPic::SetBitmapIndex(UINT nBitmapIndex)
{
 ModifyStyle(SS_BITMAP, 0);  // Remove bitmap style, use owner paint

 bool bForceRedraw = (m_nBitmapIndex != nBitmapIndex);

 m_nBitmapIndex = nBitmapIndex;

 // Force owner paint
 if (::IsWindow(m_hWnd) && bForceRedraw)
 {
  Invalidate();
 }
}

// CTransparentPic message handlers
BOOL CTransparentPic::OnEraseBkgnd(CDC* pDC)
{
 return TRUE;
}

void CTransparentPic::OnPaint()
{
 CWnd::Default(); // Calls the default window procedure

 CClientDC dc(this);
 CBitmap bmp;

 // Try to load bitmap
 if (bmp.LoadBitmap(m_nBitmapIndex))
 {
  CDC memDC;

  memDC.CreateCompatibleDC(NULL);

  CBitmap* pOldBmp = memDC.SelectObject(&bmp);
  BITMAP bitmap;

  bmp.GetBitmap(&bitmap);
  TransparentBlt(dc.m_hDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight,
   memDC.m_hDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, RGB(236, 233, 216));

  memDC.SelectObject(pOldBmp);
  memDC.DeleteDC();
 }
 else
 {
  // Draw error information
  dc.TextOut(1, 1, _T("ERROR!"));
 }
}
说明一下:上面的RGB(236, 233, 216)指的是制作图片时以该颜色值作为背景色(要改一起改),TransparentBit方法在处理时自动把这些颜色值过滤掉,看起来就是透明的。

然后给Picture Control指定一个ID,在对话框的.h文件中定义CTransparentPic对象:
 CTransparentPic m_ctrlPic1;

接着在对话框.cpp文件的DoDataExchange方法CDialog::DoDataExchange之后加一项完成关联:
 CDialog::DoDataExchange(pDX);
 DDX_Control(pDX, IDC_BITMAP1, m_ctrlPic1);

最后在对话框的OnInitDialog加一项设置图片ID项:
 m_ctrlPic1.SetBitmapIndex(IDB_BITMAP1);

CListCtrl在LVS_SMALLICON模式下显示不正常的解决办法

星期四, 5月 8th, 2008

前段时间写的贴子《CListCtrl在LVS_ICON和LVS_SMALLICON模式下的显示问题》解决了CListCtrl在LVS_ICON和LVS_SMALLICON两种模式下插入记录不正常显示的问题,但有个问题当时没有发现:在LVS_SMALLICON模式下,当数据点的标签比较长时,左边的标签就覆盖了右边的图标和标签,如下图:

试了好几种方法:
m_lvwAlarmList.Arrange(LVA_DEFAULT);
m_lvwAlarmList.RedrawItems(0, m_lvwAlarmList.GetItemCount());
m_lvwAlarmList.RedrawWindow();
m_lvwAlarmList.UpdateWindow();

m_lvwAlarmList.ModifyStyle(LVS_TYPEMASK, LVS_ICON, 0);
m_lvwAlarmList.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON, 0);
只有最后两句一起用时才有效,只用上面的4句仍然无效。

最后通过观察资源管理器的情况,使用LVS_LIST来替找LVS_SMALLICON来解决这个问题的:
把“m_lvwAlarmList.ModifyStyle(LVS_TYPEMASK, LVS_SMALLICON, 0);”改成“m_lvwAlarmList.ModifyStyle(LVS_TYPEMASK, LVS_LIST, 0);”。

发贴的链接与上回的贴子是同一个。

WTL::CListViewCtrl动态显示Tooltip的实现

星期四, 3月 13th, 2008

在WTL::CListViewCtrl控件实现Tooltip显示的实例:

1. 从CListViewCtrl类重载,创建CTooltipCtrl控件,处理鼠标事件。

重载类头文件:
class CListViewCtrlTip : public CWindowImpl<CListViewCtrlTip, CListViewCtrl>
{
public:
 CListViewCtrlTip()
 {
  m_nCurItem = -1;
  m_dwCurItemData = 0;
 }

 BEGIN_MSG_MAP(CListViewCtrlTip)
  MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
  MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
 END_MSG_MAP()

 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

 void Attach(HWND hWnd);
 void Detach();
 void DeactivateTip();
 int GetCurItem()
 {
  return m_nCurItem;
 }

 DWORD GetCurItemData()
 {
  return m_dwCurItemData;
 }

private:
 CToolTipCtrl m_ctrlTip;
 int m_nCurItem;
 DWORD m_dwCurItemData;
};

重载类实现:
void CListViewCtrlTip::Attach(HWND hWnd)
{
 // Use subclass window to support message event instead of attach
 if (CWindowImpl<CListViewCtrlTip, CListViewCtrl>::SubclassWindow(hWnd))
 {
  m_ctrlTip.Create(m_hWnd);
  m_ctrlTip.Activate(FALSE);
  m_ctrlTip.AddTool(m_hWnd);
  m_ctrlTip.SetMaxTipWidth(260); // Set the max tip width to 260 characters
  m_ctrlTip.SetDelayTime(TTDT_AUTOPOP, 10000); // Set auto pop delay time to 10s
 }
}

void CListViewCtrlTip::Detach()
{
 if (::IsWindow(m_hWnd))
 {
  m_ctrlTip.Activate(FALSE);
  m_ctrlTip.DelTool(m_hWnd);
  CWindowImpl<CListViewCtrlTip, CListViewCtrl>::UnsubclassWindow(TRUE);
 }
}

void CListViewCtrlTip::DeactivateTip()
{
 m_nCurItem = -1;
 m_dwCurItemData = 0;
 m_ctrlTip.Activate(FALSE);
}

LRESULT CListViewCtrlTip::OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
 MSG msg = { m_hWnd, uMsg, wParam, lParam };

 if (m_ctrlTip.IsWindow())
 {
  m_ctrlTip.RelayEvent(&msg);
 }
 bHandled = FALSE; // Leave the message to next case

 return 0;
}

LRESULT CListViewCtrlTip::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
 int xPos = GET_X_LPARAM(lParam);
 int yPos = GET_Y_LPARAM(lParam);
 POINT pt = { xPos, yPos };
 int nCurItem = HitTest(pt, NULL);
 
 // Check and active the tool tip control
 if (nCurItem >= 0)
 {
  if (m_nCurItem != nCurItem)
  {
   m_ctrlTip.Activate(FALSE); // Deactivate first
   m_nCurItem = nCurItem;
   m_dwCurItemData = (ULONG)GetItemData(m_nCurItem);
   m_ctrlTip.Activate(TRUE);
  }
 }
 else
 {
  DeactivateTip();
 }
 bHandled = FALSE; // Leave the message to next case

 return 0;
}

2. 修改调用类中CListViewCtrl类型为重载的类型,在初始化函数中调用Attach方法,在OnDestroy调用Detach方法。

CListViewCtrlTip m_lvw;

m_lvw.Attach(GetDlgItem(IDC_IST));

m_lvw.Detach();

3. 调用类处理TTN_GETDISPINFO通知,更新Tooltip的数据。

char m_szCurToolTip[MAX_TOOLTIP_LEN];

LRESULT CTestDlg::OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam,
          BOOL& /*bHandled*/)
{
 switch (((LPNMHDR)lParam)->code)
 {
 case TTN_GETDISPINFO: // Notify event from list view control to get the tool tip string
  LPNMTTDISPINFO pInfo = (LPNMTTDISPINFO)lParam;
  // Make dynamic tool tip string here
  pInfo->lpszText = m_szCurToolTip;

  break;
 }

 return 0;
}

CListCtrl在LVS_ICON和LVS_SMALLICON模式下的显示问题

星期三, 2月 27th, 2008

我在使用CListCtrl控件时只使用LVS_ICON和LVS_SMALLICON两种模式,现在有个需求就是想动态的在数据中插入新记录,比如InsertItem(0,xxx)插一条记录到最前面。调用是成功的,但新插入的记录只在显示在最后,此时如果切换成LVS_REPORT模式时,新插入的记录显示在最前。后面这种情况算是正确的,那不使用这种模式时如何解决乱序的问题呢?

实例:
CListCtrl   m_lvw;

m_lvw.ModifyStyle(LVS_ALIGNMASK,   LVS_AUTOARRANGE);
m_lvw.InsertColumn(0,   "Header",   LVCFMT_CENTER,   100);

void   CTestLvwDlg::OnBnClickedInsert()
{
static   int   index   =   1;

CString   strIndex;

strIndex.Format("%d",   index++);
m_lvw.InsertItem(0,   strIndex);
}

void   CTestLvwDlg::OnBnClickedSmallIcon()
{
m_lvw.ModifyStyle(LVS_TYPEMASK,   LVS_SMALLICON,   0);
}

void   CTestLvwDlg::OnBnClickedLargeIcon()
{
m_lvw.ModifyStyle(LVS_TYPEMASK,   LVS_ICON,   0);
}

void   CTestLvwDlg::OnBnClickedReport()
{
m_lvw.ModifyStyle(LVS_TYPEMASK,   LVS_REPORT,   0);
}
不断的调用OnBnClickedInsert()插入新记录时,LVS_REPORT显示正确而另外两种模式显示的是乱的。

 

经过多方查找和实验,总算把问题解决了:

每次增加一条记录时将ItemData设置成index的值,然后调用一下SortItems排序。
//   Sort   items   by   associated   lParam
static   int   CALLBACK   MyCompareProc(LPARAM   lParam1,   LPARAM   lParam2,   LPARAM   lParamSort)
{
return   (int)(lParam2   -   lParam1);
}

void   CTestLvwDlg::OnBnClickedInsert()
{
static   int   index   =   1;

CString   strIndex;

strIndex.Format("%d",   index);
m_lvw.InsertItem(0,   strIndex);
m_lvw.SetItemData(0,   index);
m_lvw.SortItems(MyCompareProc,   0);

index++;
}

void   CTestLvwDlg::OnBnClickedSmallIcon()
{
m_lvw.ModifyStyle(LVS_TYPEMASK,   LVS_SMALLICON,   0);
m_lvw.SortItems(MyCompareProc,   0);
}

void   CTestLvwDlg::OnBnClickedLargeIcon()
{
m_lvw.ModifyStyle(LVS_TYPEMASK,   LVS_ICON,   0);
m_lvw.SortItems(MyCompareProc,   0);
}

void   CTestLvwDlg::OnBnClickedReport()
{
m_lvw.ModifyStyle(LVS_TYPEMASK,   LVS_REPORT,   0);
m_lvw.SortItems(MyCompareProc,   0);
}

void   CTestLvwDlg::OnBnClickedSort()
{
m_lvw.SortItems(MyCompareProc,   0);
}

发贴的链接:http://topic.csdn.net/u/20080227/17/636bade4-76b6-4d7a-a0af-9308a143fbb4.html

得到提示的链接:http://www.codeguru.com/forum/archive/index.php/t-139506.html

自已写的操作应用程序的工具

星期六, 2月 2nd, 2008

在网吧上网的时候可能会碰到那种允许下载并安装程序但不允许手动删除任何文件的情况,但是却允许执行已安装目录下的反安装程序进行卸载。因此我得出一个结论:一些网吧客户端软件是通过钩子找到删除确认提示框并禁用“是”按钮而不是禁用操作系统级的功能。从允许安装程序的角度来看,登录用户至少有User级的权限,也可能是Power User,但没有认证。

因此我自己用C++写了一个应用程序,通过API直接管理文件,包括执行常用程序、自动获取系统目录、执行指定的程序、删除指定目录或文件(删除文件之前先把其头1024字节的内容置成0,以防止二进行文件的恢复)、杀掉指定PID的进程(在任务管理器中可以找到正在运行程序的PID值,此方法应该可以终止网吧客户端进程,没有认证,推荐使用)等。

(更多…)

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

星期一, 8月 6th, 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工程,测试。

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

星期三, 6月 20th, 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