如果不用MFC,直接使用API写一个Windows程序,需哪些步骤?MFC是怎么做到菜单点击后,响应对应的代码的?

来源:互联网 发布:算法时代 编辑:程序博客网 时间:2024/05/14 22:40

答:如果直接使用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_MAPEND_MESSAGE_MAP代码块实现了这个消息映射结构体(消息和处理函数的映射)。在窗体收到消息时(来自WinMain)查找消息映射表执行相应函数。

 

问题2:并发线程怎么协作,如何在WindowsLinux系统中解决生产者/消费者问题?

答:涉及到访问共享资源的并发线程,需要做好同步与互斥。

Windows系统下的线程的互斥与同步有四种: 临界区、事件、信号量、互斥。其中临界区是用户模式,其他为内核模式。用户模式会占用资源少(CPU时钟),但只适合同一进程里线程同步或互斥。内核模式的会占用资源较多,但可以做到跨进程的线程同步与互斥。

 

临界区:是一段独占对某些共享资源访问的代码,保证多个线程对同一共享资源的互斥访问。

事件:通过通知操作的方式来保持线程的同步。

信号量:信号量是通过计数来对线程访问资源进行控制

互斥:同临界区有些类似,保证多个线程对同一共享资源的互斥访问。

MFC对以上分别做了封装类:CCriticalSectionCEventCSemaphoreCMutex

 

Linux系统上线程的同步与互斥有3:互斥体(Mutex)、信号灯(Semophore)、条件变量(Conditions)。一般用pthread库来实现。

互斥体(Mutex):能够保证多个线程对同一共享资源的互斥访问,同Windows上的互斥。

信号灯(Semophore)信号量本质上是一个非负的整数计数器,它被用来控制对公共资源的访问号。

条件变量(Conditions):使用条件变量,我们就可以将等待信号的线程阻塞,直到有信号的时候再去唤醒它。

 

生产者/消费者问题解决方案:

根据生产者与消费者数量为2种情况

1)一个生产者和一个消费者,共用一个缓冲区,缓冲区里能存放n件物品。

可以定义两个信号量: SP(表示可以放物品入缓冲区),初值为n

                  SG(表示可以从缓冲区取物品),初值为0

缓冲区用环形队列表示,大小为n

2m个生产者和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-2UCS-4UCS-22个字节编码,UCS-44个字节编码,UCS-2UCS-4的一个子集(BMP部分)

 

UNICODE的实现方式有:UTF-8,UTF-16,UTF-32UTF8变长(1-4个字节),与ASCII兼容。UTF16, 对于BMP部分采用16bit编码,超出BMP部分,用32bit编码。UTF32实现方式基本等同于UCS32编码方式。

 

C程序里使用UNICODE, _UNICODE预定义宏编译,将生成支持UNICODE的程序。要求:

字符串:TCHAR, LPCTSTR, LPTSTR

字符串处理函数:_tcslen, _tcscpy… Windows函数:lstrlen, lstrcpy

 

Gb2312MBCS编码,代码页为936

MBCSUNICODE : MultiByteToWideChar  UNICODEMBCS: WideCharToMultiByte

 

问题5COM是什么?COM是怎么做到跟语言无关,可以跨进程,甚至可以跨机器的? COM最基本接口是什么?

 

COM是与语言平台无关、以二进制形式发布的(DLLEXE)组件。它可以在不妨碍已有用户的情况下被升级。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