正文

VC技术点滴-12006-10-09 11:42:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/378121200/19142.html

分享到:

VC技术-1                      

全局函数AfxGetApp()返回是一个指向应用程序对象的指针。借助于这个指针,我们可以得到CWinApp类的公共数据成员(m_wndToolBar,m_pMainWnd等)和接口

工具栏对象,状态栏对象以及菜单对象对是主框架窗口相联系的,而不是和视图窗口相联系的。所以对于视图窗口来说,要想得到工具栏对象,必须先得到主框架窗口对象,然后再根据主框架窗口对象得到工具栏对象:
CMainFrame* pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;
CToolBar* pToolBar = &pFrame->m_wndToolBar;
我们必须把m_pMainWnd从CFrameWnd*转换到CMainFrame*,因为m_wndToolBar是派生类的成员。我们还必须使 m_wndToolBar为公有成员或我们的类成为CMainFrame的友元。

我们还可以利用同样的方法来得到菜单对象、状态栏对象和对话框对象。

InvalidateRect()指定需要刷新的区域(局部刷新)

菜单和工具栏更新命令常用函数
CCmdUI* pCmdUI;
pCmdUI->SetCheck(TRUE);//勾选/复选
pCmdUI->Enable(False);//不可用(灰化)

操作状态栏常用函数

SetPaneText()

例子:
CMainFrame* pFrame = (CMainFrame*)AfxGetApp()->m_pMainWnd;
CStatusBar* pStatusBar = &pFrame->m_wndStatusBar;
pStatusBar->SetPaneText(0,"message line for first pane");


CString 与字符串数组之间的转换

char szMessageText[] = "Unknown error";
CString str("Unknown error");
CString::GetBuffer()

文档与视图
文档对象是用来保存数据的,而视图对象是用来显示数据的。
一个文档可以有多个视图,而一个视图只能对应一个文档。

CView::GetDocument()
视图对象只有一个与之相的文档对象,GetDocument()允许我们由视图得到与之相联系的文档。


CDocument::UpdateAllViews()
在文档数据发生变化时,通知所有视图更新。
如果在派生文档类的成员函数中调用的话,它的第一个参数为NULL,表示更新所有视图
UpdateAllViews(NULL);
如果在派生视图类的成员函数中调用的话,它的第一个参数为this,表示只更新本视图?
GetDocument()->UpdateAllViews(this);

CView::OnUpdate()

---------------------------

下面介绍两个指针列表类:CPrtList CObList和一个模板集合类CtypedPrtList

当应用程序框架接收到框架窗口命令时,它将接如下次序来寻找相应的消息控制函数

视图-文档-MDI子框架窗口-MDI主框架窗口-应用程序


CObList集合类
对先入先出列表使用CObList类
例子:CObList的使用
#include "stdafx.h"
#include
#include
#include


class CAction:public CObject
{
private:
 int m_nTime;
public:
 CAction(int nTime){ m_nTime = nTime;}
 void PrintTime(){printf("time = %d\n",m_nTime);}
};

int main(int argc, char* argv[])
{
 CAction* pAction;
 CObList actionList;
 
 for (int i=0; i<5; i++)
 {
  pAction = new CAction(i);
  actionList.AddTail(pAction);
 }

 while (!actionList.IsEmpty())
 {
  pAction = (CAction*)actionList.RemoveHead();
  pAction->PrintTime();
  delete pAction;
 }
 return 0;
}

得到当天星期几的函数
//得到当前星期
void GetCurrentWeek(char* &strWeek)
{
 CTime m_time = CTime::GetCurrentTime();
 int number = m_time.GetDayOfWeek();
 switch(number){
  case 1:
   strWeek = "星期日";
   break;
  case 2:
   strWeek = "星期一";
   break;
  case 3:
   strWeek = "星期二";
   break;
  case 4:
   strWeek = "星期三";
   break;
  case 5:
   strWeek = "星期四";
   break;
  case 6:
   strWeek = "星期五";
   break;
  case 7:
   strWeek = "星期六";
   break;
}

 

多线程编程
进程是拥有自己的内存,文件句柄和其他系统资源的运行程序, 单个进程可以包含独立的执行,叫线程.
Windows提供了两种线程, 工作者(worker)线程和用户界面线程, 用户界面线程通常有窗口,且具有自己的消息循环.工作者线程没有窗口,因此它不需要处理消息.
编写工作者线程函数并启动线程
线程体一般是如何形式:
UINT ThreadProc(LPVOID pParam)
{
return 0;
}
启动线程使用:
CwinThread* pThread =
AfxBeginThread(ThreadProc,GetSafeHwnd(),THREAD_PRIORITY_NORMAL);
主线程如何与工作线程使用全局变量通讯
全局变量通讯是最简单而有效的办法.
例如下面的代码:
UINT ThreadProc(LPVOID pParam)
{
 g_nCount = 0;
while(g_nCount<100)
 ::InterlockedIncrement((long*)&g_nCount);
return 0;
}
InterLockIncrement函数在变量加1期间阻塞其他线程访问该变量. 如果不使用此函数而直接使用:g_nCount++的话, 可能会出现错误.
工作者线程与主线程通讯发送消息进行联络
 下面的代码: 当线程完成后发送给父进程消息
 UINT ThreadProc(LPVOID pParam)
{
 .........
 ::PostMessage((HWND)pParam,WM_THREADFINISHED,0,0);
 return 0;
}
使用事件进行线程同步
Cevent类是一个事件类,刚定义时处于"非信号"状态, 可以调用SetEvent()成员函数置它为"信号"状态.
下面的代码: 线程首先等待开始信号, 如果没有置开始信号会一直挂起等待, 同时在运行的过程中等待结束信号, 如果结束信号发生就终止线程.
Cevent g_eventStart,g_eventKill;
UINT ThreadProc(LPVOID pParam)
{
//INFINITE表示等待无限时间
 ::WaitForSingleObject(g_eventtart,INFINITE);
 for (.........) 
{
..........
If(::WaitForSingleObject(g_eventKill,0)==WAIT_OBJECT_0))
Break;
}
return 0;
}
当启动线程时: g_eventStart.SetEvent();
当终止线程时: g_eventKill.SetEvent();
但在终止线程时如果还没有启动线程,则应该先启动线程再终止它.
注意: 线程如果不正常终止会引起内存泄漏, 例如用关闭进程的方法来强制终止线程,或使用Win32 TerminateThread函数.
临界段
使用CcriticalSection类可以将临界段句柄包装起来, 例如:
 CcriticalSection g_cs;
 G_cs.Lock();
 G_nCount++;
 G_cs.Unlock();
用户接口线程
 一般使用用户接口线程是想要得到多个顶级窗口,例如你想允许用户运行程序的多个实例,但是你想让所有的实例都共享内存,IE就是这样的. 你可以从CwinThread来派生一个类,使用AfxBeginThread的重载版本来启动该线程,派生的这个类具有它自己的InitInstance函数,并且具有自己的消息循环.

Win32 SDK篇
事件的使用方法
HANDLE g_hCloseEvent = NULL;
g_hCloseEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (g_hCloseEvent == NULL)
        return FALSE;
 设置信号: SetEvent(g_hCloseEvent);
线程的创建方法
 线程体的一般形式:
 DWORD WINAPI ThreadProc(LPVOID pParam)
 {
  return 0;
}
创建时:
HANDLE hReceiveThread = NULL;
UINT ThreadID;
hReceiveThread =
CreateThread(NULL,0,ThreadProc,hWnd,0, &ThreadID);
if ( hReceiveThread == NULL )
  return FALSE;
// 优先级为普通
SetThreadPriority(hReceiveThread,THREAD_PRIORITY_NORMAL);
临界区的使用方法
 CRITICAL_SECTION csRecvRead  = {0}; 
 InitializeCriticalSection(&csRecvRead); // 临界区初始化
 EnterCriticalSection(&csRecvRead); // 使用临界区变量
 pRightBuffer = pRightBuffer + len;
 LeaveCriticalSection(&csRecvRead);

 

理解句柄

什么是句柄?

功能上的理解:
什么是"句柄"(handle),handle的本意是把柄,把手的意思。是你与操作系统打交道的东东。
举个通俗的例子,比如你考上了大学,入学后,学校(操作系统)会给你一个学生证号。
注意,这个号码是学校指定的,你无法自选。
有了这个号码(学生证,假设一证多用)享受学校提供的服务:
如你就可以去图书馆借书,去食堂吃饭,去教室上课等等。

但你不能到食堂里买啤酒,因为学校不允许这种服务。
而在计算机中系统提供的服务就是API调用,你有了HANDLE,就可以理直气壮地向系统提出调用API的服务。
而指针的权力就大多了,有了指针你可以到处去喝酒,打架,学校(操作系统)管不着,
所以句柄和指针的区别在于句柄指针调用系统提供的服务。
而句柄虽然是一个能相互区别的号码,但与我们普通的ID号又有区别,
普通的ID号是可以由程序员自己定义的,而句柄不行,它是对象生成是系统指定的,
是为了区别系统中存在的各个对象,这个句柄不是由程序员符给的。

概念上的理解
1。句柄,是整个windows编程的基础,一个句柄是指使用的一个唯一的整数值,
是指一个四字节长的数值,用于标志应用程序中的不同对象和同类对象中的不同的实例,
诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。
应用程序能够通过句柄访问相应的对象的信息。
2。句柄不是一个指针,程序不能利用它句柄来直接阅读文件中的信息。
如果句柄不用在I/O文件中,它是毫无用处的。
3。句柄是windows用来标志应用程序中建立的或是使用的唯一整数,
windows使用了大量的句柄来来标志很多对象。

机制上的理解
前面的分析很经典,但我认为有一点必须指出的。如果不对,请各位指证。
句柄是指针,一点不假,但是这个指针又与C中的指针有不同之处。
因为Windows是一个多任务的系统,其内存是可以移动的,
这样的话如果某一时刻有一个指针指向一块内存,之后的某个时刻却被系统移走了,
如果你再用这个指针的话就会出错。
为了解决这一问题,windows在系统专区开一块内存用于存放句柄,这个句柄的值就是一个地址,
当这一块内存被移走后,windows就修改这个句柄的值,再访问这块内存时,句柄的值总是有效的。
正因为这样当你使用GlobalAlloc分配的内存时,如果你指定这块内存的属性是固定的,
那么它的返回值可以直接给一个指针,如果是可以移动的,
返回值就必须给一个句柄,你就必须先GlobalLock后才能使用。
这是我对句柄理解,不知道对不对?

我的理解
其实,句柄是一个指向指针的指针。即:
在windows程序设计中,句柄仅是一个应用程序用来识别某些事情的数字

如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。
我们知 道,所谓指针是一种内存地址。
应用程序启动后,组成这个程序的各对象是住留在内的 。
如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。
但是,如果您真的这样认为,那么您就大错特错了。
我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,
Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。
对象被移动意味着它的地址变化 了。如果地址总是如此变化,我们该到哪里去找该对象呢?
为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,
用来专门 登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。
Wi ndows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。
这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。
这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统 。

句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定) →实际对象
    但是,必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是
原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成
是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电
影院售给我们的门票总是不同的一个座位是一样的道理。

bool与BOOL的区别联系?
bool是C++中的一种变量类型(布尔类型),只可以写
bool   x;
x   = true;
x   = false;
在VC++中BOOL是这样说明的:
typedef int   BOOL;
也就是说BOOL是当int用的。
BOOL   x;
x   = 1;
x   = 0;
当然为了方便,VC++已经定义了
#define    TRUE   0
#define    FALSE  1

阅读(3738) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册