您的位置:首页 > 编程语言

如果不用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:并发线程怎么协作,如何在WindowsLinux系统中解决生产者/消费者问题?
答:涉及到访问共享资源的并发线程,需要做好同步与互斥。
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使用互斥。对以上容器做一定改写既可。

问题3TCP/IP协议中,TCPUDP有什么区别?TCP发送数据时,接收方收到包后是否需要自己按顺序组装?HTTP协议是什么?用get方法得到响应后,如何区分出HTTP头部与报文主体?
答: TCP:传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

UDP:用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快

用TCP发送数据时,接收方收到后不需要自己重新排序组装,因为TCP协议已经做好了这些。如果是用UDP传输,则需要。

HTTP:超文本传输协议。是客户端(如:WEB浏览器)与服务器端(Web网站)请求和应答的标准。是基于传输层(一般TCP)上的应用层协议。

请求报文格式:
请求行 - 通用信息头 - 请求头 - 实体头 -空行 - 报文主体
响应报文格式:
状态行 - 通用信息头 - 响应头 - 实体头 -空行 - 报文主体

两种报文都是以空行来区别头部与报文主体。

问题4UNICODE是什么?gb2312UNICODE码吗?
答:统一码、万国码、单一码。是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。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。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐