主要界面如下:

主要代码如下:

 BOOL CPEParseDlg::OnInitDialog()
{
CDialog::OnInitDialog(); // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码
InitSectionList(); return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
} // 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。 void CPEParseDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), ); // 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + ) / ;
int y = (rect.Height() - cyIcon + ) / ; // 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
} //当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CPEParseDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
} void CPEParseDlg::OnBnClickedButtonLook()
{
// TODO: 在此添加控件通知处理程序代码
((CEdit*)GetDlgItem(IDC_EDIT_FILEPATH))->GetWindowText(m_strPathName);
m_strPathName = m_strPathName.Trim();
if (m_strPathName.IsEmpty())
{
AfxMessageBox(_T("请选择要查看的文件!"));
return;
} FileCreate();
if (FALSE == IsPeFileAndGetPePointer())
{
AfxMessageBox(_T("该文件不是PE文件!"));
return;
}
ParseBasePe();
EnumSections();
} void CPEParseDlg::OnBnClickedButtonExit()
{
// TODO: 在此添加控件通知处理程序代码
OnOK();
} void CPEParseDlg::OnBnClickedButtonBrowse()
{
// TODO: 在此添加控件通知处理程序代码
CFileDialog FileDlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, _T("Executeable Files (*.exe)|*.exe|Dynamic Linker Library Files (*.dll)|*.dll|OCX Files (*.ocx)|*.ocx|Driver Files (*.sys)|*.sys||"));
FileDlg.DoModal();
CString strPathName = FileDlg.GetPathName();
((CEdit*)GetDlgItem(IDC_EDIT_FILEPATH))->SetWindowText(strPathName);
} BOOL CPEParseDlg::FileCreate(void)
{
BOOL bRet = FALSE; m_hFile = CreateFile(m_strPathName.GetBuffer(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == m_hFile)
{
return bRet;
} m_hMap = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, , , NULL);
if (NULL == m_hMap)
{
CloseHandle(m_hFile);
return bRet;
} m_lpBase = MapViewOfFile(m_hMap, FILE_MAP_READ | FILE_MAP_WRITE, , , );
if (NULL == m_lpBase)
{
CloseHandle(m_hMap);
CloseHandle(m_hFile);
return bRet;
} bRet = TRUE;
return bRet;
} void CPEParseDlg::InitSectionList(void)
{
CRect Rect;
m_ctrlSections.GetClientRect(&Rect);
m_ctrlSections.SetExtendedStyle(m_ctrlSections.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);
m_ctrlSections.InsertColumn(, _T("节名"));
m_ctrlSections.InsertColumn(, _T("V.偏移"));
m_ctrlSections.InsertColumn(, _T("V.大小"));
m_ctrlSections.InsertColumn(, _T("R.偏移"));
m_ctrlSections.InsertColumn(, _T("R.大小"));
m_ctrlSections.InsertColumn(, _T("标志"));
m_ctrlSections.SetColumnWidth(, Rect.Width() / );
m_ctrlSections.SetColumnWidth(, Rect.Width() / );
m_ctrlSections.SetColumnWidth(, Rect.Width() / );
m_ctrlSections.SetColumnWidth(, Rect.Width() / );
m_ctrlSections.SetColumnWidth(, Rect.Width() / );
m_ctrlSections.SetColumnWidth(, Rect.Width() / );
} BOOL CPEParseDlg::IsPeFileAndGetPePointer(void)
{
BOOL bRet = FALSE; m_pDosHeader = (PIMAGE_DOS_HEADER)m_lpBase;
if (IMAGE_DOS_SIGNATURE != m_pDosHeader->e_magic)
{
return bRet;
} m_pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)m_lpBase + m_pDosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE != m_pNtHeaders->Signature)
{
return bRet;
} m_pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)&(m_pNtHeaders->OptionalHeader) + m_pNtHeaders->FileHeader.SizeOfOptionalHeader); bRet = TRUE;
return bRet;
} void CPEParseDlg::ParseBasePe(void)
{
m_strEntryPoint.Format(_T("%08X"), m_pNtHeaders->OptionalHeader.AddressOfEntryPoint);
m_strImageBase.Format(_T("%08X"), m_pNtHeaders->OptionalHeader.ImageBase);
m_strLinkerVersion.Format(_T("%d.%d"), m_pNtHeaders->OptionalHeader.MajorLinkerVersion, m_pNtHeaders->OptionalHeader.MinorLinkerVersion);
m_strSectionNum.Format(_T("%02X"), m_pNtHeaders->FileHeader.NumberOfSections);
m_strFileAlignment.Format(_T("%08X"), m_pNtHeaders->OptionalHeader.FileAlignment);
m_strSectionAlignment.Format(_T("%08X"), m_pNtHeaders->OptionalHeader.SectionAlignment);
UpdateData(FALSE);
} void CPEParseDlg::EnumSections(void)
{
m_ctrlSections.DeleteAllItems(); CString strTemp;
int iSectionNumber = m_pNtHeaders->FileHeader.NumberOfSections; for (int i = ; i < iSectionNumber; ++i)
{
strTemp = m_pSectionHeader[i].Name;
m_ctrlSections.InsertItem(i, strTemp); strTemp.Format(_T("%08X"), m_pSectionHeader[i].VirtualAddress);
m_ctrlSections.SetItemText(i, , strTemp); strTemp.Format(_T("%08X"), m_pSectionHeader[i].Misc.VirtualSize);
m_ctrlSections.SetItemText(i, , strTemp); strTemp.Format(_T("%08X"), m_pSectionHeader[i].PointerToRawData);
m_ctrlSections.SetItemText(i, , strTemp); strTemp.Format(_T("%08X"), m_pSectionHeader[i].SizeOfRawData);
m_ctrlSections.SetItemText(i, , strTemp); strTemp.Format(_T("%08X"), m_pSectionHeader[i].Characteristics);
m_ctrlSections.SetItemText(i, , strTemp);
}
} void CPEParseDlg::OnBnClickedRadioVa()
{
// TODO: 在此添加控件通知处理程序代码
m_nSelected = ;
((CEdit*)GetDlgItem(IDC_EDIT_VA))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_RVA))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_FILEOFFSET))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_VA))->SetReadOnly(FALSE);
((CEdit*)GetDlgItem(IDC_EDIT_RVA))->SetReadOnly(TRUE);
((CEdit*)GetDlgItem(IDC_EDIT_FILEOFFSET))->SetReadOnly(TRUE);
} void CPEParseDlg::OnBnClickedRadioRva()
{
// TODO: 在此添加控件通知处理程序代码
m_nSelected = ;
((CEdit*)GetDlgItem(IDC_EDIT_VA))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_RVA))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_FILEOFFSET))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_VA))->SetReadOnly(TRUE);
((CEdit*)GetDlgItem(IDC_EDIT_RVA))->SetReadOnly(FALSE);
((CEdit*)GetDlgItem(IDC_EDIT_FILEOFFSET))->SetReadOnly(TRUE);
} void CPEParseDlg::OnBnClickedRadioFileoffset()
{
// TODO: 在此添加控件通知处理程序代码
m_nSelected = ;
((CEdit*)GetDlgItem(IDC_EDIT_VA))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_RVA))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_FILEOFFSET))->SetWindowText(_T(""));
((CEdit*)GetDlgItem(IDC_EDIT_VA))->SetReadOnly(TRUE);
((CEdit*)GetDlgItem(IDC_EDIT_RVA))->SetReadOnly(TRUE);
((CEdit*)GetDlgItem(IDC_EDIT_FILEOFFSET))->SetReadOnly(FALSE);
} void CPEParseDlg::OnBnClickedButtonCalc()
{
// TODO: 在此添加控件通知处理程序代码
m_strImageBase = m_strImageBase.Trim();
if (m_strImageBase.IsEmpty())
{
AfxMessageBox(_T("请先点击查看按钮!"));
return;
} CString strPathName;
((CEdit*)GetDlgItem(IDC_EDIT_FILEPATH))->GetWindowText(strPathName);
if (strPathName != m_strPathName)
{
AfxMessageBox(_T("你已经更换了PE文件,请先点击查看按钮!"));
return;
} DWORD dwAddr = ;
dwAddr = GetAddr();
int nInNum = GetAddrInSectionNum(dwAddr);
CalcAddr(nInNum, dwAddr);
} DWORD CPEParseDlg::GetAddr(void)
{
TCHAR szAddr[] = {};
DWORD dwAddr = ;
switch (m_nSelected)
{
case :
GetDlgItemText(IDC_EDIT_VA, szAddr, );
HexStrToInt(szAddr, &dwAddr);
break;
case :
GetDlgItemText(IDC_EDIT_RVA, szAddr, );
HexStrToInt(szAddr, &dwAddr);
break;
case :
GetDlgItemText(IDC_EDIT_FILEOFFSET, szAddr, );
HexStrToInt(szAddr, &dwAddr);
break;
} return dwAddr;
} int CPEParseDlg::GetAddrInSectionNum(DWORD dwAddr)
{
int nInNum = ;
int nSectionNum = m_pNtHeaders->FileHeader.NumberOfSections;
switch (m_nSelected)
{
case :
{
DWORD dwImageBase = m_pNtHeaders->OptionalHeader.ImageBase;
for (nInNum = ; nInNum < nSectionNum; nInNum++)
{
if (dwAddr >= dwImageBase + m_pSectionHeader[nInNum].VirtualAddress
&& dwAddr <= dwImageBase + m_pSectionHeader[nInNum].VirtualAddress
+ m_pSectionHeader[nInNum].Misc.VirtualSize)
{
return nInNum;
}
}
break;
}
case :
for (nInNum = ; nInNum < nSectionNum; nInNum++)
{
if (dwAddr >= m_pSectionHeader[nInNum].VirtualAddress
&& dwAddr <= m_pSectionHeader[nInNum].VirtualAddress
+ m_pSectionHeader[nInNum].Misc.VirtualSize)
{
return nInNum;
}
}
break;
case :
for (nInNum = ; nInNum < nSectionNum; nInNum++)
{
if (dwAddr >= m_pSectionHeader[nInNum].PointerToRawData
&& dwAddr <= m_pSectionHeader[nInNum].PointerToRawData
+ m_pSectionHeader[nInNum].SizeOfRawData)
{
return nInNum;
}
}
break;
} return -;
} void CPEParseDlg::CalcAddr(int nInNum, DWORD dwAddr)
{
DWORD dwVa = ;
DWORD dwRva = ;
DWORD dwFileOffset = ; switch (m_nSelected)
{
case :
dwVa = dwAddr;
dwRva = dwVa - m_pNtHeaders->OptionalHeader.ImageBase;
dwFileOffset = m_pSectionHeader[nInNum].PointerToRawData + (dwRva - m_pSectionHeader[nInNum].VirtualAddress);
break;
case :
dwVa = dwAddr + m_pNtHeaders->OptionalHeader.ImageBase;
dwRva = dwAddr;
dwFileOffset = m_pSectionHeader[nInNum].PointerToRawData + (dwRva - m_pSectionHeader[nInNum].VirtualAddress);
break;
case :
dwFileOffset = dwAddr;
dwRva = m_pSectionHeader[nInNum].VirtualAddress + (dwFileOffset - m_pSectionHeader[nInNum].PointerToRawData);
dwVa = dwRva + m_pNtHeaders->OptionalHeader.ImageBase;
break;
} SetDlgItemText(IDC_EDIT_SECTION, CString(m_pSectionHeader[nInNum].Name)); CString str;
str.Format(_T("%08X"), dwVa);
SetDlgItemText(IDC_EDIT_VA, str); str.Format(_T("%08X"), dwRva);
SetDlgItemText(IDC_EDIT_RVA, str); str.Format(_T("%08X"), dwFileOffset);
SetDlgItemText(IDC_EDIT_FILEOFFSET, str);
} void CPEParseDlg::HexStrToInt(TCHAR* szAddr, DWORD* pdwAddr)
{
int iLen = lstrlen(szAddr);
*pdwAddr = ; for (int i = ; i < iLen; i++)
{
if (szAddr[i] >= _T('') && szAddr[i] <= _T(''))
{
*pdwAddr = ((*pdwAddr) << ) | (szAddr[i] - _T(''));
}
else if (szAddr[i] >= _T('A') && szAddr[i] <= _T('F'))
{
*pdwAddr = ((*pdwAddr) << ) | (szAddr[i] - _T('A') + 0xA);
}
else if (szAddr[i] >= _T('a') && szAddr[i] <= _T('f'))
{
*pdwAddr = ((*pdwAddr) << ) | (szAddr[i] - _T('a') + 0xA);
}
}
} void CPEParseDlg::OnDestroy()
{
CDialog::OnDestroy(); // TODO: 在此处添加消息处理程序代码
if (NULL != m_lpBase)
{
UnmapViewOfFile(m_lpBase);
} if (NULL != m_hMap)
{
CloseHandle(m_hMap);
} if (INVALID_HANDLE_VALUE != m_hFile)
{
CloseHandle(m_hFile);
}
} HBRUSH CPEParseDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: 在此更改 DC 的任何属性
//if (nCtlColor == CTLCOLOR_DLG || nCtlColor == CTLCOLOR_STATIC || nCtlColor == CTLCOLOR_EDIT)
//{
// pDC->SetBkMode(TRANSPARENT);
// return CreateSolidBrush(RGB(0x2F, 0x4F, 0x4F));
//} // TODO: 如果默认的不是所需画笔,则返回另一个画笔
return hbr;
} void CPEParseDlg::OnBnClickedButtonAddsection()
{
// TODO: 在此添加控件通知处理程序代码
m_strImageBase = m_strImageBase.Trim();
if (m_strImageBase.IsEmpty())
{
AfxMessageBox(_T("请先点击查看按钮!"));
return;
} CString strPathName;
((CEdit*)GetDlgItem(IDC_EDIT_FILEPATH))->GetWindowText(strPathName);
if (strPathName != m_strPathName)
{
AfxMessageBox(_T("你已经更换了PE文件,请先点击查看按钮!"));
return;
} TCHAR szSectionName[] = {};
int nSectionSize = ; GetDlgItemText(IDC_EDIT_SECTIONNAME, szSectionName, );
nSectionSize = GetDlgItemInt(IDC_EDIT_SECTIONSIZE, FALSE, TRUE); AddSection(szSectionName, nSectionSize);
} void CPEParseDlg::AddSection(TCHAR* szSectionName, int nSectionSize)
{
int nSectionNum = m_pNtHeaders->FileHeader.NumberOfSections;
DWORD dwFileAlignment = m_pNtHeaders->OptionalHeader.FileAlignment;
DWORD dwSectionAlignment = m_pNtHeaders->OptionalHeader.SectionAlignment; PIMAGE_SECTION_HEADER pTempSection = m_pSectionHeader + nSectionNum; int nLen = WideCharToMultiByte(CP_ACP, , (LPCWSTR)szSectionName, -, NULL, , NULL, NULL);
char *pszSectionNameA = new char[nLen];
WideCharToMultiByte(CP_ACP, , szSectionName, -, pszSectionNameA, nLen * sizeof(char), NULL, NULL);
//拷贝节名
strncpy((char*)(pTempSection->Name), pszSectionNameA, );
delete[] pszSectionNameA;
pszSectionNameA = NULL;
//节的内存大小
pTempSection->Misc.VirtualSize = AlignSize(nSectionSize, dwSectionAlignment);
//节的内存起始位置
pTempSection->VirtualAddress = m_pSectionHeader[nSectionNum - ].VirtualAddress
+ AlignSize(m_pSectionHeader[nSectionNum - ].Misc.VirtualSize, dwSectionAlignment);
//节的文件大小
pTempSection->SizeOfRawData = AlignSize(nSectionSize, dwFileAlignment);
//节的文件起始位置
pTempSection->PointerToRawData = m_pSectionHeader[nSectionNum - ].PointerToRawData
+ AlignSize(m_pSectionHeader[nSectionNum - ].SizeOfRawData, dwFileAlignment);
//修正节数量
m_pNtHeaders->FileHeader.NumberOfSections++;
//修正映像大小
m_pNtHeaders->OptionalHeader.SizeOfImage += pTempSection->Misc.VirtualSize;
//填充内存映射
FlushViewOfFile(m_lpBase, );
//添加节数据
AddSectionData(pTempSection->SizeOfRawData); CString str;
str.Format(_T("%02X"), m_pNtHeaders->FileHeader.NumberOfSections);
((CEdit*)GetDlgItem(IDC_EDIT_SECTIONNUM))->SetWindowText(str);
EnumSections();
} DWORD CPEParseDlg::AlignSize(int nSectionSize, DWORD dwAlignment)
{
int nSize = nSectionSize;
if (nSize % dwAlignment != )
{
nSectionSize = (nSize / dwAlignment + ) * dwAlignment;
} return nSectionSize;
} void CPEParseDlg::AddSectionData(int nSectionSize)
{
PBYTE pByte = NULL;
pByte = (PBYTE)malloc(nSectionSize);
ZeroMemory(pByte, nSectionSize); DWORD dwNum = ;
SetFilePointer(m_hFile, , , FILE_END);
WriteFile(m_hFile, pByte, nSectionSize, &dwNum, NULL);
FlushFileBuffers(m_hFile); free(pByte);
}

下载地址如下:

http://pan.baidu.com/s/1hqBR06S

PE查看器的更多相关文章

  1. 用Qt写软件系列一:QCacheViewer(浏览器缓存查看器)

    介绍 Cache技术广泛应用于计算机行业的软硬件领域.该技术既是人们对新技术探讨的结果,也是对当前软硬件计算能力的一种妥协.在浏览器中使用cache技术,可以大幅度提高web页面的响应速度,降低数据传 ...

  2. PE解析器的编写(四)——数据目录表的解析

    在PE结构中最重要的就是区块表和数据目录表,上节已经说明了如何解析区块表,下面就是数据目录表,在数据目录表中一般只关心导入表,导出表和资源这几个部分,但是资源实在是太复杂了,而且在一般的病毒木马中也不 ...

  3. Map工具系列-08-map控件查看器

    所有cs端工具集成了一个工具面板 -打开(IE) Map工具系列-01-Map代码生成工具说明 Map工具系列-02-数据迁移工具使用说明 Map工具系列-03-代码生成BySQl工具使用说明 Map ...

  4. wpf 仿QQ图片查看器

    参考博客 WPF下的仿QQ图片查看器 wpf图片查看器,支持鼠标滚动缩放拖拽 实现效果 主要参考的WPF下的仿QQ图片查看器,原博主只给出了部分代码. 没有完成的部分 1.右下角缩略图是原图不是缩略图 ...

  5. Win10系统怎样让打开图片方式为照片查看器

    转载自:百度经验 http://jingyan.baidu.com/article/5d368d1ef0cad13f60c057e3.html 1.首先,我们需要使用注册表编辑器来开启Win10系统照 ...

  6. 发布两款JQ小插件(图片查看器 + 分类选择器),开源

    图片查看器,github地址:https://github.com/VaJoy/imgViewer 效果如下: 这款当初大概写了2小时,有点匆忙地赶出来的,使用的接口很简单: $.bindViewer ...

  7. IIS事件查看器_WebServer事件查看器_帮助查看IIS-Web服务器事件执行日志

    IIS服务器是我们常用的Web站点部署工具,而我们有时可能遇到IIS服务器的应用程序池莫名其妙的关闭了,或者是其他未知原因等等,我们这是可以通过微软提供的WebServer(Web服务事件查看器),来 ...

  8. wpf图片查看器,支持鼠标滚动缩放拖拽

    最近项目需要,要用到一个图片查看器,类似于windows自带的图片查看器那样,鼠标滚动可以缩放,可以拖拽图片,于是就写了这个简单的图片查看器. 前台代码: <Window x:Class=&qu ...

  9. 在Windows 10下启用旧的照片查看器

    从Windows 10开始,默认只能通过“照片”来查看图片了,非常不方便!通过将下列文本保存在.reg文件后导入,即可找回Windows XP时代的“照片查看器”. Windows Registry ...

随机推荐

  1. Laravel中URL,ACTION,ROUTE区别

    创建路由如下所示: Route::get('articles',['uses'=>'ArticlesController@index','as'=>'articles.index']); ...

  2. Python标准库04 文件管理 (部分os包,shutil包)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 在操作系统下,用户可以通过操作系统的命令来管理文件,参考linux文件管理相关命令 ...

  3. mysql 增加用户

    mysql 增加用户 3.增加用户: (注意:和上面不同,下面的因为是MYSQL环境中的命令,所以后面都带一个分号作为命令结束符) 格式:grant select on 数据库.* to 用户名@登录 ...

  4. java获取数据库的列名,类型等信息

    当你使用和学习JDK的时候,可以查看并学习它所提供给你的两个ResultSetMetaData 和DataBaseMetaData类的源码并很好的了解它们的实现原理和思路,JDBC中提供有两种源数据, ...

  5. web性能测试的新利器 - Gatling 介绍

    转载:http://www.51testing.com/html/10/26810-852956.html 最近发现了一个新的性能测试工具Gatling,貌似比Jmeter还好玩.这几天就先简单介绍一 ...

  6. Java基础-绘图技术

  7. Java中的Comparable<T>和Comparator<T>接口

    有的时候在面试时会被问到Comparable<T>和Comparator<T>的区别(或者Java中两种排序功能的实现区别). 1) 在使用普通数组的时候,如果想对数据进行排序 ...

  8. java记录在线人数小案例

    文件目录: web.xml: <?xml version="1.0" encoding="UTF-8"?> <web-app version= ...

  9. 从linux内核代码分析操作系统启动过程

    朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 在本次的实验中, ...

  10. django 获取系统当前时间 和linux 系统当前时间不一致 问题处理。

    问题场景: 在django admin models 实体对象添加一个属性最后修改时间,用户在添加.修改是系统自动修改操作时间. UpdateTime自动获取系统时间.并且自动修改. 代码设置如下. ...