如果不用MFC,直接使用API写一个Windows程序,需哪些步骤?MFC是怎么做到菜单点击后,响应对应的代码的?
2010-02-08 18:35
1376 查看
答:如果直接使用Windows API写一个窗体程序,如下:
int WinMain(...)
{
MSG msg;
RegisterClass(...); //注册窗口类
CreateWindow(...); //创建窗口
ShowWindow(...); //显示窗口
UpdateWindow(...);
While(GetMessage(&msg, NULL...))
{ //消息循环, 取得跟主线程相关的消息。如果取得WM_QUIT则返回0,退出循环。
TranslateMessage(...);
DispatchMessage(...); //分发消息给对应的窗体。
}
return msg.wParam;
}
需定义一个主窗体消息处理函数(窗体注册时需指定函数名),如下:
LRESULT CALLBACK WndProc(...)
{
switch(message)
{
case WM_COMMAND: //命令处理,如菜单命令
}
}
在此函数里处理跟主窗体有关的消息。一个程序可能有多个窗体,每个窗体都有消息处理函数。消息先由WinMain函数的消息循环接收(来自OS),通过DispatchMessage派发给对应的窗体。
MFC封装了以上消息处理过程,如点击菜单项draw,其做法是:
窗体类.h文件
class CMainFrame : public CFrameWnd
{
… afx_msg void OnEditDraw();
DECLARE_MESSAGE_MAP()
}
窗体类.cpp文件
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_COMMAND(IDC_EDIT_DRAW, OnEditDraw)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
.h文件中包含了DECLARE_MESSAGE_MAP, 其声明了一个消息映射结构体,在.cpp中通过BEGIN_MESSAGE_MAP、END_MESSAGE_MAP代码块实现了这个消息映射结构体(消息和处理函数的映射)。在窗体收到消息时(来自WinMain),查找消息映射表执行相应函数。
问题2:并发线程怎么协作,如何在Windows与Linux系统中解决生产者/消费者问题?
答:涉及到访问共享资源的并发线程,需要做好同步与互斥。
Windows系统下的线程的互斥与同步有四种:临界区、事件、信号量、互斥。其中临界区是用户模式,其他为内核模式。用户模式会占用资源少(CPU时钟),但只适合同一进程里线程同步或互斥。内核模式的会占用资源较多,但可以做到跨进程的线程同步与互斥。
临界区:是一段独占对某些共享资源访问的代码,保证多个线程对同一共享资源的互斥访问。
事件:通过通知操作的方式来保持线程的同步。
信号量:信号量是通过计数来对线程访问资源进行控制
互斥:同临界区有些类似,保证多个线程对同一共享资源的互斥访问。
MFC对以上分别做了封装类:CCriticalSection、CEvent、CSemaphore、CMutex
Linux系统上线程的同步与互斥有3种:互斥体(Mutex)、信号灯(Semophore)、条件变量(Conditions)。一般用pthread库来实现。
互斥体(Mutex):能够保证多个线程对同一共享资源的互斥访问,同Windows上的互斥。
信号灯(Semophore):信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问号。
条件变量(Conditions):使用条件变量,我们就可以将等待信号的线程阻塞,直到有信号的时候再去唤醒它。
生产者/消费者问题解决方案:
根据生产者与消费者数量为2种情况
1)一个生产者和一个消费者,共用一个缓冲区,缓冲区里能存放n件物品。
可以定义两个信号量: SP(表示可以放物品入缓冲区),初值为n
SG(表示可以从缓冲区取物品),初值为0
缓冲区用环形队列表示,大小为n。
2)m个生产者和r个消费者,共用一个缓冲区,缓冲区里可存放n件物品
可以定义四个信号量:SP(表示可以放物品入缓冲区),初值为n
SG(表示可以从缓冲区取物品),初值为0
S1 (用于生产者间互斥),初值为0
S2(用于消费者间互斥),初值为0
缓冲区用环形队列表示,大小为n。
Windows(API)上解决生产者/消费者方案(第2种情况)
SP, 使用信号; SG使用事件; S1, S2使用临界区。容器使用自定义环形队列:CSyncCache,生产者和消费者之间的协调就是对容器的访问控制,所以重点在于这个容器类编写。
template <class Type>
class CProductCache
{
private:
const int m_size;
Type* m_productArray;
int m_writePos;
int m_readPos;
CRITICAL_SECTION m_writerCS; //生产者之间的互斥
CRITICAL_SECTION m_readerCS; //消费者之间的互斥
HANDLE m_hHasProductEvent; //容器中有产品事件
HANDLE m_hProductCountSema; //窗口中还可放入产品数(信号量)
public:
void Put(const Type& product)
{
WaitForSingleObject(m_hProductCountSema, INFINITE);
EnterCriticalSection(&m_writerCS);
m_productArray[m_writePos] = product;
m_writePos = (m_writePos + 1) % m_size;
LeaveCriticalSection(&m_writerCS);
SetEvent(m_hHasProductEvent);
}
Type Get()
{
WaitForSingleObject(m_hHasProductEvent, INFINITE);
EnterCriticalSection(&m_readerCS);
Type product = m_productArray[m_readPos];
m_readPos = (m_readPos + 1) % m_size;
LeaveCriticalSection(&m_readerCS);
ReleaseSemaphore(m_hProductCountSema, 1, NULL);
return product;
}
public:
CProductCache(int size):m_size(size)
{
m_productArray = new Type[m_size];
m_writePos = 0;
m_readPos = 0;
InitializeCriticalSection(&m_writerCS);
InitializeCriticalSection(&m_readerCS);
m_hHasProductEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hProductCountSema = CreateSemaphore(NULL, m_size, m_size, NULL);
}
virtual ~CProductCache()
{
delete[] m_productArray;
}
};
Linux 上解决生产者/消费者方案(第2种情况)
使用pthread库。SP, 使用信号; SG使用信号; S1, S2使用互斥。对以上容器做一定改写既可。
问题3:TCP/IP协议中,TCP、UDP有什么区别?TCP发送数据时,接收方收到包后是否需要自己按顺序组装?HTTP协议是什么?用get方法得到响应后,如何区分出HTTP头部与报文主体?
答: TCP:传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP:用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
用TCP发送数据时,接收方收到后不需要自己重新排序组装,因为TCP协议已经做好了这些。如果是用UDP传输,则需要。
HTTP:超文本传输协议。是客户端(如:WEB浏览器)与服务器端(Web网站)请求和应答的标准。是基于传输层(一般TCP)上的应用层协议。
请求报文格式:
请求行 - 通用信息头 - 请求头 - 实体头 -空行 - 报文主体
响应报文格式:
状态行 - 通用信息头 - 响应头 - 实体头 -空行 - 报文主体
两种报文都是以空行来区别头部与报文主体。
问题4:UNICODE是什么?gb2312是UNICODE码吗?
答:统一码、万国码、单一码。是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。UNICODE的编码方式有UCS-2,UCS-4。UCS-2用2个字节编码,UCS-4用4个字节编码,UCS-2是UCS-4的一个子集(BMP部分)
UNICODE的实现方式有:UTF-8,UTF-16,UTF-32。UTF8变长(1-4个字节),与ASCII兼容。UTF16, 对于BMP部分采用16bit编码,超出BMP部分,用32bit编码。UTF32实现方式基本等同于UCS32编码方式。
C程序里使用UNICODE, _UNICODE预定义宏编译,将生成支持UNICODE的程序。要求:
字符串:TCHAR, LPCTSTR, LPTSTR
字符串处理函数:_tcslen, _tcscpy… Windows函数:lstrlen, lstrcpy
Gb2312是MBCS编码,代码页为936。
MBCS转UNICODE : MultiByteToWideChar UNICODE转MBCS: WideCharToMultiByte
问题5:COM是什么?COM是怎么做到跟语言无关,可以跨进程,甚至可以跨机器的? COM最基本接口是什么?
COM是与语言平台无关、以二进制形式发布的(DLL或EXE)组件。它可以在不妨碍已有用户的情况下被升级。COM为了支持语言无关,创建一种所有语言都识别的语言:IDL。每种语言都按照自己的需求翻译IDL文件,来调用相关接口。COM跨进程跨机器特性:COM通过一种被称为列集/散集的技术,允许接口指针可被跨套间边界传递出去。
COM对象的基本接口是IUnknown,其有三个方法:
1) HRESULT _stdcall QueryInterface( [in] GUID* riid, [out] void** ppvObj);
用于查询以象是否支持某个接口(riid), 如果支持,传递出去(ppvObj)。
2) unsigned long _stdcall AddRef();
对象引用数加1
3) unsigned long _stdcall Release();
对象引用数减1,如果减完之后为0,则删除对象。
问题6:使用过哪些源码管理工具?使用过哪些开发工具?哪些测试工具?
答:源码管理器方面,使用过VSS6.0, CVS, SVN。
开发工具方面:用过VC6.0, .Net 2003, GCC(Linux),acc(HPUX), gdb
开发库方面:MFC, STL, BOOST, QT
开发语言方面:C/C++, C#
测试工具方面:cxxtest, purify。
int WinMain(...)
{
MSG msg;
RegisterClass(...); //注册窗口类
CreateWindow(...); //创建窗口
ShowWindow(...); //显示窗口
UpdateWindow(...);
While(GetMessage(&msg, NULL...))
{ //消息循环, 取得跟主线程相关的消息。如果取得WM_QUIT则返回0,退出循环。
TranslateMessage(...);
DispatchMessage(...); //分发消息给对应的窗体。
}
return msg.wParam;
}
需定义一个主窗体消息处理函数(窗体注册时需指定函数名),如下:
LRESULT CALLBACK WndProc(...)
{
switch(message)
{
case WM_COMMAND: //命令处理,如菜单命令
}
}
在此函数里处理跟主窗体有关的消息。一个程序可能有多个窗体,每个窗体都有消息处理函数。消息先由WinMain函数的消息循环接收(来自OS),通过DispatchMessage派发给对应的窗体。
MFC封装了以上消息处理过程,如点击菜单项draw,其做法是:
窗体类.h文件
class CMainFrame : public CFrameWnd
{
… afx_msg void OnEditDraw();
DECLARE_MESSAGE_MAP()
}
窗体类.cpp文件
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
//{{AFX_MSG_MAP(CMainFrame)
ON_WM_CREATE()
ON_COMMAND(IDC_EDIT_DRAW, OnEditDraw)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
.h文件中包含了DECLARE_MESSAGE_MAP, 其声明了一个消息映射结构体,在.cpp中通过BEGIN_MESSAGE_MAP、END_MESSAGE_MAP代码块实现了这个消息映射结构体(消息和处理函数的映射)。在窗体收到消息时(来自WinMain),查找消息映射表执行相应函数。
问题2:并发线程怎么协作,如何在Windows与Linux系统中解决生产者/消费者问题?
答:涉及到访问共享资源的并发线程,需要做好同步与互斥。
Windows系统下的线程的互斥与同步有四种:临界区、事件、信号量、互斥。其中临界区是用户模式,其他为内核模式。用户模式会占用资源少(CPU时钟),但只适合同一进程里线程同步或互斥。内核模式的会占用资源较多,但可以做到跨进程的线程同步与互斥。
临界区:是一段独占对某些共享资源访问的代码,保证多个线程对同一共享资源的互斥访问。
事件:通过通知操作的方式来保持线程的同步。
信号量:信号量是通过计数来对线程访问资源进行控制
互斥:同临界区有些类似,保证多个线程对同一共享资源的互斥访问。
MFC对以上分别做了封装类:CCriticalSection、CEvent、CSemaphore、CMutex
Linux系统上线程的同步与互斥有3种:互斥体(Mutex)、信号灯(Semophore)、条件变量(Conditions)。一般用pthread库来实现。
互斥体(Mutex):能够保证多个线程对同一共享资源的互斥访问,同Windows上的互斥。
信号灯(Semophore):信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问号。
条件变量(Conditions):使用条件变量,我们就可以将等待信号的线程阻塞,直到有信号的时候再去唤醒它。
生产者/消费者问题解决方案:
根据生产者与消费者数量为2种情况
1)一个生产者和一个消费者,共用一个缓冲区,缓冲区里能存放n件物品。
可以定义两个信号量: SP(表示可以放物品入缓冲区),初值为n
SG(表示可以从缓冲区取物品),初值为0
缓冲区用环形队列表示,大小为n。
2)m个生产者和r个消费者,共用一个缓冲区,缓冲区里可存放n件物品
可以定义四个信号量:SP(表示可以放物品入缓冲区),初值为n
SG(表示可以从缓冲区取物品),初值为0
S1 (用于生产者间互斥),初值为0
S2(用于消费者间互斥),初值为0
缓冲区用环形队列表示,大小为n。
Windows(API)上解决生产者/消费者方案(第2种情况)
SP, 使用信号; SG使用事件; S1, S2使用临界区。容器使用自定义环形队列:CSyncCache,生产者和消费者之间的协调就是对容器的访问控制,所以重点在于这个容器类编写。
template <class Type>
class CProductCache
{
private:
const int m_size;
Type* m_productArray;
int m_writePos;
int m_readPos;
CRITICAL_SECTION m_writerCS; //生产者之间的互斥
CRITICAL_SECTION m_readerCS; //消费者之间的互斥
HANDLE m_hHasProductEvent; //容器中有产品事件
HANDLE m_hProductCountSema; //窗口中还可放入产品数(信号量)
public:
void Put(const Type& product)
{
WaitForSingleObject(m_hProductCountSema, INFINITE);
EnterCriticalSection(&m_writerCS);
m_productArray[m_writePos] = product;
m_writePos = (m_writePos + 1) % m_size;
LeaveCriticalSection(&m_writerCS);
SetEvent(m_hHasProductEvent);
}
Type Get()
{
WaitForSingleObject(m_hHasProductEvent, INFINITE);
EnterCriticalSection(&m_readerCS);
Type product = m_productArray[m_readPos];
m_readPos = (m_readPos + 1) % m_size;
LeaveCriticalSection(&m_readerCS);
ReleaseSemaphore(m_hProductCountSema, 1, NULL);
return product;
}
public:
CProductCache(int size):m_size(size)
{
m_productArray = new Type[m_size];
m_writePos = 0;
m_readPos = 0;
InitializeCriticalSection(&m_writerCS);
InitializeCriticalSection(&m_readerCS);
m_hHasProductEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
m_hProductCountSema = CreateSemaphore(NULL, m_size, m_size, NULL);
}
virtual ~CProductCache()
{
delete[] m_productArray;
}
};
Linux 上解决生产者/消费者方案(第2种情况)
使用pthread库。SP, 使用信号; SG使用信号; S1, S2使用互斥。对以上容器做一定改写既可。
问题3:TCP/IP协议中,TCP、UDP有什么区别?TCP发送数据时,接收方收到包后是否需要自己按顺序组装?HTTP协议是什么?用get方法得到响应后,如何区分出HTTP头部与报文主体?
答: TCP:传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。
UDP:用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快
用TCP发送数据时,接收方收到后不需要自己重新排序组装,因为TCP协议已经做好了这些。如果是用UDP传输,则需要。
HTTP:超文本传输协议。是客户端(如:WEB浏览器)与服务器端(Web网站)请求和应答的标准。是基于传输层(一般TCP)上的应用层协议。
请求报文格式:
请求行 - 通用信息头 - 请求头 - 实体头 -空行 - 报文主体
响应报文格式:
状态行 - 通用信息头 - 响应头 - 实体头 -空行 - 报文主体
两种报文都是以空行来区别头部与报文主体。
问题4:UNICODE是什么?gb2312是UNICODE码吗?
答:统一码、万国码、单一码。是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。UNICODE的编码方式有UCS-2,UCS-4。UCS-2用2个字节编码,UCS-4用4个字节编码,UCS-2是UCS-4的一个子集(BMP部分)
UNICODE的实现方式有:UTF-8,UTF-16,UTF-32。UTF8变长(1-4个字节),与ASCII兼容。UTF16, 对于BMP部分采用16bit编码,超出BMP部分,用32bit编码。UTF32实现方式基本等同于UCS32编码方式。
C程序里使用UNICODE, _UNICODE预定义宏编译,将生成支持UNICODE的程序。要求:
字符串:TCHAR, LPCTSTR, LPTSTR
字符串处理函数:_tcslen, _tcscpy… Windows函数:lstrlen, lstrcpy
Gb2312是MBCS编码,代码页为936。
MBCS转UNICODE : MultiByteToWideChar UNICODE转MBCS: WideCharToMultiByte
问题5:COM是什么?COM是怎么做到跟语言无关,可以跨进程,甚至可以跨机器的? COM最基本接口是什么?
COM是与语言平台无关、以二进制形式发布的(DLL或EXE)组件。它可以在不妨碍已有用户的情况下被升级。COM为了支持语言无关,创建一种所有语言都识别的语言:IDL。每种语言都按照自己的需求翻译IDL文件,来调用相关接口。COM跨进程跨机器特性:COM通过一种被称为列集/散集的技术,允许接口指针可被跨套间边界传递出去。
COM对象的基本接口是IUnknown,其有三个方法:
1) HRESULT _stdcall QueryInterface( [in] GUID* riid, [out] void** ppvObj);
用于查询以象是否支持某个接口(riid), 如果支持,传递出去(ppvObj)。
2) unsigned long _stdcall AddRef();
对象引用数加1
3) unsigned long _stdcall Release();
对象引用数减1,如果减完之后为0,则删除对象。
问题6:使用过哪些源码管理工具?使用过哪些开发工具?哪些测试工具?
答:源码管理器方面,使用过VSS6.0, CVS, SVN。
开发工具方面:用过VC6.0, .Net 2003, GCC(Linux),acc(HPUX), gdb
开发库方面:MFC, STL, BOOST, QT
开发语言方面:C/C++, C#
测试工具方面:cxxtest, purify。
相关文章推荐
- 在Windows操作系统下使用Visual C++ 6.0编程时,如果点击菜单中的【打开】或者【添加】,或者按快捷键,都会弹出一个对话框,怎么解决?
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- //1.编写代码模拟三次密码输入的场景。//2.编写一个程序,可以一直接收键盘字符,如果是小写字符就输出对应的大写字符,如果接收的是大写字符,就输出对应的小写字符,如果是数字不输出。
- 激活前一个程序(注册全局消息,使用Mutex探测,如果已经占用就广播消息通知第一个程序,然后第一个程序做出响应)
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- CoInitialize ( NULL )或其它启动代码。MFC程序使用AfxOleInit() 收回COM库。MFC 程序不用这一步,它自动完成。 CoUninitialize();
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 【MFC】Windows 用Visual studio 2010 编译出来的MFC 程序,在平板电脑上无法使用长按来弹出右击菜单
- MFC中多个子菜单对应同一个消息响应函数
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- 我创建了一个托盘图标,可以正常使用,点击右键打开菜单。问题是如果点击右键后不选择其中一个菜单项进行操作的话,它就总不消失。
- MFC小程序003------MFC使用WebBrowser组件,在对话框中创建滚动视图,动态创建一个静态文本控件并设置鼠标单击的消息响应
- MFC基于对话框的程序添加菜单&点击菜单弹出一个新的对话框
- 如果你参与到一个项目中,发现他们使用 Tab 来缩进代码,但是你喜欢空格,你会怎么做?
- MFC小程序003------MFC使用WebBrowser组件,在对话框中创建滚动视图,动态创建一个静态文本控件并设置鼠标单击的消息响应