MFC第五天 Unicode软件开发 MFC框架构成与封装类原理

Unicode软件开发

Unicode软件开发时需要遵循以下规则:使用中可尽量使用自适应版本。

 Unicode软件开发:
a)微软的软件工程现在默认使用Unicode(UTF16位小端),尤其是Windows图形化软件工程。
b)Unicode软件工程下,常量的定义使用L"xxxx",ANSI使用“XXX”;
c)微软的跟字符串打交道的API都会定义两套:例如:
#ifdef UNICODE
#define SetDlgItemText  SetDlgItemTextW
#else
#define SetDlgItemText  SetDlgItemTextA
#endif // !UNICODE
d)C语言库函数跟字符串打交道的,全部重写Unicode版本。
比如:
char *strcat(
   char *strDestination,
   const char *strSource 
);
wchar_t *wcscat(
   wchar_t *strDestination,
   const wchar_t *strSource 
);

TCHAR类型:

a)窄字符串:typedef const char* LPCSTR; 
b)宽字符串:typedef const wchar_t* LPCWSTR; 
c)自适应字符串:typedef TCHAR* LPCTSTR;

#ifdef  UNICODE                     // r_winnt
typedef wchar_t TCHAR;
typedef const wchar_t* LPCTSTR;
#else
typedef char TCHAR;
typedef const char* LPCTSTR;
#endif

以Unicode为字符集的记事本软件开发

主函数Main.cpp 主要是把ANSI和UTF8编码转换成为Unicode16

#define  _CRT_SECURE_NO_WARNINGS
#include<windows.h>
#include"resource.h"
#include<stdio.h>

void ConvertBig(LPSTR p);
bool CheckUtf8(LPCSTR p);
wchar_t* ANSIToUnicode(const char* str);
wchar_t* UTF8ToUnicode(const char* str);

void ParseText(HWND hwndDlg, LPSTR p)
{
	wchar_t* q = nullptr;
	switch (*(WORD*)p)
	{
	case 0xFFFE:  //BE 大端   大端与小端之间完全颠倒 每个字节之间完全的进行反转 翻转以后就说小端的代码 
		ConvertBig(p);
	case 0xFEFF:	//LE 小端
		SetDlgItemText(hwndDlg, IDC_TEXT, (LPCTSTR)p+2);
		delete[] q;
		return;
	case 0xBBEF: //UTF8 BOM
		if (p[2] == (char)0xBF)
		{
			q = UTF8ToUnicode(p + 3);
			SetDlgItemText(hwndDlg, IDC_TEXT, q);
			delete[] q;
			return;
		}
		return;
	}
	if (CheckUtf8(p))
	{
		q = UTF8ToUnicode(p);
		SetDlgItemText(hwndDlg, IDC_TEXT, q);
		delete[] q;
	}
	else
	{
		q = ANSIToUnicode(p);
		SetDlgItemText(hwndDlg, IDC_TEXT, q);
		delete[] q;
	}
		
}
int GetFileSize(FILE* pf)
{
	long m = ftell(pf);
	fseek(pf, 0, SEEK_END);
	long n = ftell(pf);
	fseek(pf, m, SEEK_SET);
	return n;
}
void ReadTextFile(HWND hwndDlg, LPCTSTR sFile)
{
	FILE* pf = _wfopen(sFile, L"rb");
	if (!pf)
		return;
	int nSize = GetFileSize(pf);
	if (nSize > 0)
	{
		char* p = new char[nSize + 2];
		auto n = fread(p, 1, nSize, pf);
		p[n] = 0;
		p[n + 1] = 0;
		ParseText(hwndDlg, p);
		//	SetDlgItemText(hwndDlg, IDC_TEXT, p);
		delete[] p;
	}
	fclose(pf);
}

void onDropFile(HWND hwndDlg, HDROP hDrop)
{
	TCHAR s[MAX_PATH];
	int nCount = DragQueryFile(hDrop, 0, s, _countof(s));  //取第一个文件名 如果要去最后一个,先求出总数再ncount-1

	//SetDlgItemText(hwndDlg, IDC_TEXT, s);
	ReadTextFile(hwndDlg, s);
	DragFinish(hDrop);
} 

INT_PTR CALLBACK theProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_DROPFILES:
		onDropFile(hwndDlg, (HDROP)wParam);
		break;
	case WM_COMMAND:
	{
		switch (LOWORD(wParam))
		{
		case IDCANCEL:
			EndDialog(hwndDlg, 88);
			break;
		}
	}
	}
	return 0;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
	UINT_PTR n = DialogBox(hInstance, (LPCWSTR)IDD_MAIN_DLG, NULL, theProc);
	return 0;
}

codec.cpp编码格式转换
需要了解utf8编码规则

UTF-8编码规则如下:
如果字符的Unicode码值范围是U+0000至U+007F(即0~127),则使用一个字节表示,最高位为0。
如果字符的Unicode码值范围是U+0080至U+07FF(即128~2047),则使用两个字节表示,最高位为110,第二高位为10。
如果字符的Unicode码值范围是U+0800至U+FFFF(即2048~65535),则使用三个字节表示,最高位为1110,第二高位为10,第三高位为10。
如果字符的Unicode码值范围是U+10000至U+10FFFF(即65536~1114111),则使用四个字节表示,最高位为11110,第二高位为10,第三高位为10,第四高位为10

#include<windows.h>
bool CheckUtf8(LPCSTR p) //检查是否为utf8不带bom头格式
{
    auto q = p;
    while (*p)
    {
        BYTE c = *p;
        BYTE x = 0x80;
        int n = 0;
        while ((c & x) == x)
            ++n, x >>= 1;
        if (n == 1 || n > 4)
            return false;
        ++p;
        while (--n>0)
        {
            c = *p++;
            if (c >> 6 != 2) //00000010 把这个数直接左移6位 看他的高位是不是10
                return false;
        }
    }
    return true;
}
void ConvertBig(LPSTR p)
{
    while (*(WORD*)p) //双字节的0结尾结束
    {
      /*  CHAR c = *p;
        *p = p[1];
        p[1] = c;*/

        *p = *p ^ p[1];     //没有中间变量的   反转
        p[1] = *p ^ p[1];
        *p = *p ^ p[1];
        p += 2;
    }
}

char* UnicodeToANSI(const wchar_t* str)
{
    int n = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); //第一次求长度   带L是Unicode的格式
    auto p = new char[n + 1];
    n =  WideCharToMultiByte(CP_ACP, 0, str, -1, p, n, NULL, NULL); //第二次填充 p, n 你申请的空间,边界限制
    p[n] = 0; //结尾
    return p;
} //算出长度 申请空间 填充长度

wchar_t* ANSIToUnicode(const char* str)
{
    wchar_t n = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
    auto p = new wchar_t[n + 1];
    MultiByteToWideChar(CP_ACP, 0, str, -1, p, n);
    return p;
}

wchar_t* UTF8ToUnicode(const char* str)
{
    int n = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
    auto p = new wchar_t[n + 1];
    n = MultiByteToWideChar(CP_UTF8, 0, str, -1,p, n);
    p[n] = 0;
    return p;
}
char* UTF8ToANSI(const char* str)
{
    auto p = UTF8ToUnicode(str);
    auto q= UnicodeToANSI(p);
    delete[] p;
    return q;
}

针对void ConvertBig(LPSTR p)函数相应释义
在这里插入图片描述


MFC框架构成与封装类原理

1、MFC框架构建:
要使用MFC静态链接库,链接器中要选择 窗口 (/SUBSYSTEM:WINDOWS)
a)必须派生CWinApp类
b)并且重写虚函数InitInstance,作为MFC的框架入口函数。
c)必须在全局区申请一个派生类对象。
在这里插入图片描述
在这里插入图片描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4fVZs6xv-1687351587792)(null)]

2、如何在MFC窗口中接收消息呢?
a)所有MFC的窗口都对应一个系统的派生类,包括:对话框类、控件类,框架和视图等等;
b)消息的接收必须在派生类内,使用消息映射机制实现。
c)消息机制就是MFC内部管理消息循环,在窗口派生类中建立消息与成员函数的关联;
d)MFC类向导第一页是控件编号,选择不同控件对应不同的消息;
e)MFC类向导第二页是主窗口消息,包括WM_MOUSEMOVE,WM_DROPFILES等。
在这里插入图片描述
消息映射机制
在这里插入图片描述

3、MFC的窗口类库封装:
a)所有的窗口类(包括:对话框类、控件类,框架和视图等等)都由CWnd类同一的窗口基类派生。
b)CWnd类内部有核心成员变量HWND m_hWnd,就如同CSocket类内部有核心成员SOCKET m_hSocket;
c)CWnd类几乎封装所有跟HWND打交道的API,

例如:
void CWnd::SetWindowText(LPCTSTR lpszString)
{
   SetWindowText(m_hWnd, lpszString);
}

void CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint)
{
	ASSERT(::IsWindow(m_hWnd) );
	MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint);
}

示例代码如下:

CApp.h

#pragma once
#include<afxwin.h>
#include "CMainDlg.h"
class CApp :public CWinApp
{
	BOOL InitInstance()
	{
		CMainDlg dlg;
		dlg.DoModal();
		return 0;
	}
};

CApp.cpp

#include "CApp.h"
CApp theApp;

CMainDlg.h

#pragma once
#include "afxdialogex.h"


// CMainDlg 对话框

class CMainDlg : public CDialogEx
{
	DECLARE_DYNAMIC(CMainDlg)
public:
	CMainDlg(CWnd* pParent = nullptr);   // 标准构造函数
	virtual ~CMainDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MAIN_DLG };
#endif

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnClickedAdd();
};

CMainDlg.cpp

// CMainDlg.cpp: 实现文件
#include "afxdialogex.h"
#include "CMainDlg.h"
#include "resource.h"
// CMainDlg 对话框
IMPLEMENT_DYNAMIC(CMainDlg, CDialogEx)

CMainDlg::CMainDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MAIN_DLG, pParent)
{

}

CMainDlg::~CMainDlg()
{
}

void CMainDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}


BEGIN_MESSAGE_MAP(CMainDlg, CDialogEx)  //消息映射机制 管理所有消息的连接桥
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_BN_CLICKED(IDC_ADD, &CMainDlg::OnClickedAdd)
END_MESSAGE_MAP()

// CMainDlg 消息处理程序

void CMainDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	CString str;
	str.Format(L"x=%d,y=%d", point.x, point.y);
	SetWindowText(str);
	if (MK_LBUTTON &nFlags)
	{
		str += "左键按下";
	}
	if (MK_RBUTTON & nFlags)
	{
		str += "右键按下";
	}
	if (MK_SHIFT & nFlags)
	{
		str += "SHIFT键按下";
	}
	SetWindowText(str);
	CDialogEx::OnMouseMove(nFlags, point);
}

void CMainDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	CString str;
	str.Format(L"x=%d,y=%d", point.x, point.y);
//	MessageBox(str, L"点击");
	CDialogEx::OnLButtonDown(nFlags, point);
}

void CMainDlg::OnClickedAdd()
{
	CString str;
	GetDlgItemText(IDC_LEFT, str);
	double fLeft=_tstof(str);

	GetDlgItemText(IDC_RIGHT, str);
	double fRight = _tstof(str);

	double fResult = fLeft + fRight;
	str.Format(L"%g", fResult);
	SetDlgItemText(IDC_RESULT, str);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jcrry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值