Files
envi-code/SourceCode/Code2026/EarthWormMain/ShortcutKeyMappingDlg.cpp
T
2026-09-28 15:28:50 +08:00

702 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "stdafx.h"
#include "shortcutkeymappingdlg.h"
#include "IniTools.h"
#include "CoreTools.h"
namespace
{
// 辅助函数:将 UTF-8 编码的 char* 转换为 MFC 的 CString
CString ConvertUTF8ToCString(const char *pUTF8, int nLen)
{
if (nLen == 0) return _T("");
// 1. 获取转换为 Unicode (UTF-16) 所需的缓冲区大小
int nWideLen = MultiByteToWideChar(CP_UTF8, 0, pUTF8, nLen, NULL, 0);
if (nWideLen == 0) return _T("");
std::vector<wchar_t> vecWide(nWideLen + 1);
// 2. 执行转换: UTF-8 -> UTF-16 (wchar_t)
MultiByteToWideChar(CP_UTF8, 0, pUTF8, nLen, &vecWide[0], nWideLen);
vecWide[nWideLen] = 0; // 确保 null 结尾
// 3. 将 wchar_t 赋值给 CString
// 如果工程是 Unicode (默认),CString 就是 wchar_t,直接赋值
// 如果工程是 MBCS,CString 会自动将 wchar_t 转换为 ANSI
return CString(&vecWide[0]);
}
std::string ConvertCStringToUTF8(const CString &strContent)
{
#ifdef _UNICODE
const wchar_t *pSrc = strContent.GetString();
int nLen = strContent.GetLength();
#else
CA2W wStr(strContent);
const wchar_t *pSrc = wStr;
int nLen = wcslen(pSrc);
#endif
if (nLen == 0) return "";
int nUTF8Len = WideCharToMultiByte(CP_UTF8, 0, pSrc, nLen, NULL, 0, NULL, NULL);
if (nUTF8Len == 0) return "";
std::string strUTF8(nUTF8Len, 0);
WideCharToMultiByte(CP_UTF8, 0, pSrc, nLen, &strUTF8[0], nUTF8Len, NULL, NULL);
return strUTF8;
}
}
IMPLEMENT_DYNAMIC(CShortcutKeyMappingDlg, CDialog)
CShortcutKeyMappingDlg::CShortcutKeyMappingDlg(CWnd* pParent /*=NULL*/)
: CDialog(CShortcutKeyMappingDlg::IDD, pParent) { }
CShortcutKeyMappingDlg::~CShortcutKeyMappingDlg() { }
void CShortcutKeyMappingDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_STATIC_LIST, m_wndStatic);
}
BEGIN_MESSAGE_MAP(CShortcutKeyMappingDlg, CDialog)
ON_BN_CLICKED(IDOK, &CShortcutKeyMappingDlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_EXPORT, &CShortcutKeyMappingDlg::OnBnClickedExport)
ON_BN_CLICKED(IDC_IMPORT, &CShortcutKeyMappingDlg::OnBnClickedImport)
ON_NOTIFY(TVN_ENDLABELEDIT, IDC_SHORTCUT_MAPPING_LIST, &CShortcutKeyMappingDlg::OnEditEnd)
ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_SHORTCUT_MAPPING_LIST, &CShortcutKeyMappingDlg::OnEditBegin)
END_MESSAGE_MAP()
void CShortcutKeyMappingDlg::OnEditEnd(NMHDR * pNMHDR, LRESULT * pResult)
{
*pResult = 0; // 默认成功
NMTVDISPINFO *pItem = (NMTVDISPINFO*)pNMHDR;
CString sNewText = pItem->item.pszText;
if (sNewText.IsEmpty())
{
*pResult = 1;
return;
}
CString sCmd = m_wndMappingLst.GetItemText(pItem->item.hItem, 1);
std::map<CString, CString>::iterator it(m_pgpCmdMapping.begin());
for (; it != m_pgpCmdMapping.end(); ++it)
{
if (it->first.CompareNoCase(sCmd) != 0 && it->second.CompareNoCase(sNewText) == 0)
{
CString sMsg;
sMsg.Format(_T("“%s”与命令:“%s”的快捷键相同,请重新修改!"), sNewText, sCmd);
AfxMessageBox(sMsg);
*pResult = 1; // 失败
return;
}
}
m_pgpCmdMapping[sCmd] = sNewText;
}
void CShortcutKeyMappingDlg::OnEditBegin(NMHDR *pNMHDR, LRESULT *pResult)
{
NMTVDISPINFO *pItem = (NMTVDISPINFO*)pNMHDR;
m_sItemOldText = pItem->item.pszText;
}
void CShortcutKeyMappingDlg::initListCtrl()
{
CRect rc;
m_wndStatic.GetWindowRect(rc);
ScreenToClient(rc);
m_wndStatic.ShowWindow(SW_HIDE);
unsigned uStyle = TVS_HASBUTTONS
|TVS_HASBUTTONS
|TVS_HASLINES
|TVS_LINESATROOT
|TVS_EDITLABELS
|TVS_FULLROWSELECT;
unsigned uExStyle = TVS_EX_ITEMLINES
|TVS_EX_ITEMLINES
|TVS_EX_ALTERNATECOLOR
|TVS_EX_SUBSELECT
|TVS_EX_FULLROWMARK;
m_wndMappingLst.Create(uStyle|WS_CHILD|WS_VISIBLE|WS_BORDER, CRect(0, 0, 0, 0), this, IDC_SHORTCUT_MAPPING_LIST);
if (!m_wndMappingLst.GetSafeHwnd())
return;
m_wndMappingLst.SetExtendedStyle(uExStyle);
m_wndMappingLst.MoveWindow(rc);
CBitmap bmp;
bmp.Attach(::LoadBitmap(_hdllInstance, MAKEINTRESOURCE(IDB_MENUSTATE)));
m_imageList.Create(12, 12, ILC_COLOR24|ILC_MASK, 0, 0);
m_imageList.Add(&bmp, RGB(255, 0, 255));
m_wndMappingLst.SetImageList(&m_imageList,TVSIL_NORMAL);
double dScale = CoreTools::GetDPIScale();
LV_COLUMN lvColumn;
lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
lvColumn.cx = 200 * dScale;
lvColumn.pszText = const_cast<LPTSTR>(_T("菜单名称"));
lvColumn.fmt = LVCFMT_LEFT;
m_wndMappingLst.InsertColumn(0, &lvColumn);
lvColumn.cx = 100 * dScale;
lvColumn.pszText = const_cast<LPTSTR>(_T("命令名称"));
lvColumn.fmt = LVCFMT_CENTER;
m_wndMappingLst.InsertColumn(1, &lvColumn);
lvColumn.pszText = const_cast<LPTSTR>(_T("快捷按键"));
m_wndMappingLst.InsertColumn(2, &lvColumn);
m_wndMappingLst.SetColumnAutoEdit(2, TVAE_EDIT | TVAE_DBLCLICK);
}
void CShortcutKeyMappingDlg::OnBnClickedOk()
{
UpdateData();
WriteToAcadPGPFile();
AfxMessageBox(_T("在弹出的对话框中,勾选上 “PGP 文件(F)” 后点击 “确定” 按钮。"));
acedPostCommand(_T("_REINIT "));
OnOK();
}
void CShortcutKeyMappingDlg::OnBnClickedExport()
{
#if ARX > 18
CString pgpFilePath = FilePath::GetSysDir() + _T("\\acad.pgp");
// 1. 基本校验:确保源文件存在
if (_taccess(pgpFilePath, 0) != 0)
{
AfxMessageBox(_T("源文件不存在,无法导出!"));
return;
}
// 2. 弹出文件夹选择对话框
// 参数1: 默认路径(NULL),参数2: 标志,参数3: 父窗口(NULL),参数4: 安全属性
CFolderPickerDialog dlg(NULL, OFN_FILEMUSTEXIST, NULL, 0);
dlg.m_ofn.lpstrTitle = _T("请选择导出文件夹");
if (dlg.DoModal() != IDOK)
{
return; // 用户取消
}
CString strDestFolder = dlg.GetPathName();
// 3. 获取源文件名
// 例如 C:\User\acad.pgp -> acad.pgp
CString strSrcPath(pgpFilePath);
int nPos = strSrcPath.ReverseFind('\\');
CString strFileName;
if (nPos != -1)
strFileName = strSrcPath.Mid(nPos + 1);
else
strFileName = strSrcPath; // 只有文件名的情况
// 4. 构建目标路径
// 简单的路径拼接,处理末尾斜杠问题
CString strDestPath = strDestFolder;
if (strDestPath.Right(1) != _T("\\"))
strDestPath += _T("\\");
strDestPath += strFileName;
// 检查源路径和目标路径是否完全一致(防止用户选了同一个文件夹)
if (strSrcPath.CompareNoCase(strDestPath) == 0)
{
AfxMessageBox(_T("源文件和目标文件路径相同,无需导出。"));
return;
}
// 5. 检查同名文件并询问覆盖
if (_taccess(strDestPath, 0) == 0)
{
CString strMsg;
strMsg.Format(_T("目标文件夹已存在文件:\n%s\n\n是否覆盖该文件?"), strFileName);
// IDYES, IDNO
if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONQUESTION) == IDNO)
{
return; // 用户选择不覆盖,取消操作
}
}
// 6. 执行复制
// CopyFile 参数3: FALSE 表示如果文件存在则直接覆盖(因为我们上面已经询问过用户了)
// 如果上面不询问,这里设为 TRUE 会导致复制失败
if (::CopyFile(pgpFilePath, strDestPath, FALSE))
{
AfxMessageBox(_T("导出成功!"));
}
else
{
// 获取错误信息
DWORD dwError = GetLastError();
CString strErr;
strErr.Format(_T("导出失败,错误代码:%d"), dwError);
AfxMessageBox(strErr, MB_ICONERROR);
}
#endif
}
void CShortcutKeyMappingDlg::OnBnClickedImport()
{
// 1. 获取系统目录下的目标文件路径
CString strDestPath = FilePath::GetSysDir() + _T("\\acad.pgp");
// 2. 配置文件打开对话框
// 参数1: TRUE 为 "打开" 对话框
// 参数2: 默认扩展名 "pgp"
// 参数3: 默认文件名 NULL
// 参数4: 标志位 (文件必须存在 | 隐藏只读复选框)
// 参数5: 过滤器
LPCTSTR szFilter = _T("PGP Files (*.pgp)|*.pgp|All Files (*.*)|*.*||");
CFileDialog dlg(TRUE, _T("pgp"), NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, szFilter, this);
dlg.m_ofn.lpstrTitle = _T("请选择要导入的 PGP 文件");
// 3. 显示对话框
if (dlg.DoModal() != IDOK)
{
return; // 用户取消
}
CString strSrcPath = dlg.GetPathName();
// 4. 校验:不能选择目标文件自己来覆盖自己
if (strSrcPath.CompareNoCase(strDestPath) == 0)
{
AfxMessageBox(_T("不能选择当前正在使用的文件进行导入!\n请选择其他备份文件。"), MB_ICONWARNING);
return;
}
// 5. 覆盖确认提示
CString strMsg;
strMsg.Format(_T("导入操作将使用选中的文件覆盖当前的配置:\n\n%s\n\n此操作不可撤销,是否继续?"), strDestPath);
if (AfxMessageBox(strMsg, MB_YESNO | MB_ICONQUESTION) != IDYES)
{
return;
}
// 6. 执行文件复制 (覆盖)
// 第三个参数 FALSE 表示:如果目标文件已存在,不失败(即允许覆盖)
if (::CopyFile(strSrcPath, strDestPath, FALSE))
{
// 7. 导入成功后的处理
AfxMessageBox(_T("导入成功!"));
// 【关键步骤】
// 此时磁盘上的文件已经变了,但内存里的 map 还是旧的。
// 你需要清空 map 并重新解析新文件,然后刷新列表控件。
m_wndMappingLst.DeleteAllItems();
m_pgpCmdMapping.clear(); // 清空你的 map
ParseAcadPGPFile(); // 重新解析
LoadMenus();
}
else
{
// 获取错误代码
DWORD dwError = GetLastError();
CString strErr;
strErr.Format(_T("导入失败,无法覆盖文件。\n错误代码:%d"), dwError);
AfxMessageBox(strErr, MB_ICONERROR);
}
}
BOOL CShortcutKeyMappingDlg::OnInitDialog()
{
CDialog::OnInitDialog();
initListCtrl();
ParseAcadPGPFile();
LoadMenus();
UpdateData(FALSE);
return TRUE;
}
void CShortcutKeyMappingDlg::start()
{
CAcModuleResourceOverride myResources;
CShortcutKeyMappingDlg dlg;
dlg.DoModal();
}
// 解析单个菜单文件
void CShortcutKeyMappingDlg::ParseMenuFile(const CString& strFilePath, const CString& strFileAlias, HTREEITEM hParent)
{
CStdioFile file;
// 尝试打开文件
if (!file.Open(strFilePath, CFile::modeRead | CFile::shareDenyNone | CFile::typeText))
{
return;
}
// 插入根节点(文件名中文别名)
HTREEITEM hRoot = m_wndMappingLst.InsertItem(strFileAlias, 1, 1, TVI_ROOT, TVI_LAST);
HTREEITEM hCurrentParent = hRoot;
std::map<CString, CString>::const_iterator itFind(m_pgpCmdMapping.end());
CString strLine;
while (file.ReadString(strLine))
{
strLine.Trim();
if (strLine.IsEmpty()) continue;
// 忽略分隔符
if (strLine.Find(_T("[--]")) >= 0) continue;
if (strLine.Find(_T("//")) == 0) continue;
if (strLine.Find(_T("**")) == 0) continue;
if (strLine.Find(_T("ID_SCREEN")) == 0) continue;
if (strLine.Find(_T("***RESOURCE")) == 0) break;
// 查找 [] 结构
int nStart = strLine.Find(_T('['));
int nEnd = strLine.Find(_T(']'));
// 如果没有合法的 [],则视为无效行或头部垃圾信息,跳过
if (nStart == -1 || nEnd == -1 || nEnd <= nStart)
continue;
// 1. 提取显示名称 (去掉 [])
CString strMenuText = strLine.Mid(nStart + 1, nEnd - nStart - 1);
strMenuText.Trim();
// 2. 提取命令 (] 之后的部分)
CString strCommand = strLine.Mid(nEnd + 1);
strCommand.Trim();
// 3. 判断层级标记 -> 或 <-
bool bIsSubStart = (strMenuText.Left(2) == _T("->"));
bool bIsSubEnd = (strMenuText.Left(2) == _T("<-"));
// 去掉标记符号,获取纯文本名称
if (bIsSubStart || bIsSubEnd)
{
strMenuText = strMenuText.Mid(2);
strMenuText.Trim();
}
// 4. 插入节点
HTREEITEM hNewItem = NULL;
if (!strMenuText.IsEmpty())
{
// 在当前父节点下插入
hNewItem = m_wndMappingLst.InsertItem(
strMenuText
, strCommand.IsEmpty() ? 2 : 3
, strCommand.IsEmpty() ? 2 : 3
, hCurrentParent);
// 如果有命令,设置到第1列 (索引1,对应“命令名称”)
if (!strCommand.IsEmpty())
{
m_wndMappingLst.SetItemText(hNewItem, strCommand, 1);
strCommand.MakeUpper();
itFind = m_pgpCmdMapping.find(strCommand);
if (itFind != m_pgpCmdMapping.end())
m_wndMappingLst.SetItemText(hNewItem, itFind->second, 2);
else
m_wndMappingLst.SetItemText(hNewItem, _T(""), 2);
}
}
// 5. 调整层级关系
if (bIsSubStart)
{
// 遇到 ->,说明刚才插入的项是下一级菜单的父节点
if (hNewItem)
{
hCurrentParent = hNewItem;
}
}
else if (bIsSubEnd)
{
// 遇到 <-,说明本项是当前层级的最后一项,或者本项结束后返回上一层
// 逻辑通常是:插入当前项,然后层级回退
HTREEITEM hGrandParent = m_wndMappingLst.GetParentItem(hCurrentParent);
if (hGrandParent)
{
hCurrentParent = hGrandParent;
}
else
{
// 如果已经退无可退,就回到根节点
hCurrentParent = hRoot;
}
}
}
// 展开根节点
m_wndMappingLst.Expand(hRoot, TVE_EXPAND);
file.Close();
}
// 加载菜单主逻辑
void CShortcutKeyMappingDlg::LoadMenus()
{
std::vector<std::pair<CString, CString>> mm;
IniTools::getSectionContent(FilePath::GetSysDir()+_T("\\product.ini")
, _T("MENUS")
, mm);
if (mm.empty()) return;
CString menuPath = FilePath::GetSysDir() + _T("\\");
std::vector<std::pair<CString, CString>>::const_iterator it(mm.begin());
for (; it != mm.end(); ++it)
{
ParseMenuFile(menuPath + it->first + _T(".mnu"), it->second, NULL);
}
}
void CShortcutKeyMappingDlg::ParseAcadPGPFile()
{
CFile file;
CFileException ex;
// 1. 打开文件
if (!file.Open(FilePath::GetSysDir() + _T("\\acad.pgp")
, CFile::modeRead | CFile::shareDenyNone
, &ex))
{
TRACE(_T("无法打开文件: acad.pgp\n"));
return;
}
ULONGLONG uFileSize = file.GetLength();
if (uFileSize == 0)
{
file.Close();
return;
}
// 2. 读取二进制数据
std::vector<char> vecBuffer((size_t)uFileSize + 1);
file.Read(&vecBuffer[0], (UINT)uFileSize);
vecBuffer[(size_t)uFileSize] = 0;
file.Close();
// 处理 BOM
int nOffset = 0;
if (uFileSize >= 3 &&
(unsigned char)vecBuffer[0] == 0xEF &&
(unsigned char)vecBuffer[1] == 0xBB &&
(unsigned char)vecBuffer[2] == 0xBF)
{
nOffset = 3;
}
// 3. 转换为 CString
CString strContent = ConvertUTF8ToCString(&vecBuffer[nOffset], (int)(uFileSize - nOffset));
// 4. 解析逻辑
int nCurPos = 0;
CString strLine;
bool bMappingSection = false;
// 标记字符串
const CString strBeginMarker = _T(";shanghua soft command shortcut key mapping begin");
const CString strEndMarker = _T(";shanghua soft command shortcut key mapping end");
while (!(strLine = strContent.Tokenize(_T("\r\n"), nCurPos)).IsEmpty())
{
// 先备份原始行用于判断标记(因为标记行本身以 ; 开头,不能先去注释)
CString strRawLine = strLine;
strRawLine.Trim();
// ---------------------------------------------------------
// A. 检查区块标记 (Start / End)
// ---------------------------------------------------------
if (strRawLine.CompareNoCase(strBeginMarker) == 0)
{
bMappingSection = true;
continue; // 跳过开始标记行
}
if (strRawLine.CompareNoCase(strEndMarker) == 0)
{
bMappingSection = false;
break; // 遇到结束标记,停止解析
}
// 如果还没进入有效区域,跳过
if (!bMappingSection)
continue;
// ---------------------------------------------------------
// B. 去除注释 (核心修改)
// ---------------------------------------------------------
// 查找分号位置
int nCommentPos = strLine.Find(_T(';'));
if (nCommentPos != -1)
{
// 截取分号前面的部分
// 例如 "DJ, *PL_QTDJ ;注释" -> "DJ, *PL_QTDJ "
// 例如 ";整行注释" -> "" (空字符串)
strLine = strLine.Left(nCommentPos);
}
// 去除截取后可能产生的尾部空格
strLine.Trim();
// 如果去注释后为空行,说明该行无数据
if (strLine.IsEmpty())
continue;
// ---------------------------------------------------------
// C. 解析 Key 和 Value
// ---------------------------------------------------------
// 格式: VIEWPORTS, *VPORTS
int nCommaPos = strLine.Find(_T(','));
int nStarPos = strLine.Find(_T('*'));
// 确保既有逗号又有星号,且星号在逗号后面(通常情况)
if (nCommaPos != -1 && nStarPos != -1)
{
// 提取 Value: 逗号前的部分
CString strValue = strLine.Left(nCommaPos);
// 提取 Key: 星号后的部分
CString strKey = strLine.Mid(nStarPos + 1);
// 清理空格
strValue.Trim();
strKey.Trim();
if (!strKey.IsEmpty())
{
// 存入 Map
// 注意:如果 Key 重复,这里会覆盖旧值,根据需求可调整
m_pgpCmdMapping[strKey.MakeUpper()] = strValue;
}
}
}
}
void CShortcutKeyMappingDlg::WriteToAcadPGPFile()
{
const CString strBeginMarker = _T(";shanghua soft command shortcut key mapping begin");
const CString strEndMarker = _T(";shanghua soft command shortcut key mapping end");
// 1. 读取原文件(为了保留 begin 之前和 end 之后的内容)
CString strHeader, strFooter;
bool bFoundSection = false;
CString path(FilePath::GetSysDir() + _T("\\acad.pgp"));
CFile fileRead;
CFileException ex;
// 尝试打开读取,如果文件不存在则视为新文件
if (fileRead.Open( path , CFile::modeRead | CFile::shareDenyNone , &ex))
{
ULONGLONG uLen = fileRead.GetLength();
if (uLen > 0)
{
std::vector<char> vecBuf((size_t)uLen + 1);
fileRead.Read(&vecBuf[0], (UINT)uLen);
vecBuf[(size_t)uLen] = 0;
fileRead.Close();
// 跳过 BOM
int nOffset = 0;
if (uLen >= 3 && (unsigned char)vecBuf[0] == 0xEF && (unsigned char)vecBuf[1] == 0xBB && (unsigned char)vecBuf[2] == 0xBF)
nOffset = 3;
CString strFullContent = ConvertUTF8ToCString(&vecBuf[nOffset], (int)(uLen - nOffset));
int nBeginPos = strFullContent.Find(strBeginMarker);
int nEndPos = strFullContent.Find(strEndMarker);
if (nBeginPos != -1 && nEndPos != -1 && nEndPos > nBeginPos)
{
// 标记存在,切分头部和尾部
strHeader = strFullContent.Left(nBeginPos + strBeginMarker.GetLength());
strFooter = strFullContent.Mid(nEndPos);
bFoundSection = true;
}
else
{
// 标记不完整,保留全文作为 Header
strHeader = strFullContent;
}
}
else
{
fileRead.Close();
}
}
// 2. 构建中间数据段
CString strMiddle;
strMiddle += _T("\r\n"); // Begin 标记后换行
// -----------------------------------------------------------
// 核心逻辑修正区
// Input Mapping: Key = 命令 (Command), Value = 快捷键 (Shortcut)
// Target Format: 快捷键, *命令 (Shortcut, *Command)
// -----------------------------------------------------------
std::map<CString, CString>::const_iterator it(m_pgpCmdMapping.begin());
for (; it != m_pgpCmdMapping.end(); ++it)
{
const CString &strCommand = it->first; // Key: 命令 (例如 "LINE")
const CString &strShortcut = it->second; // Value: 快捷键 (例如 "L")
CString strLine;
// 格式化:Value + "," + 制表符 + "*" + Key
// 结果:L, *LINE
strLine.Format(_T("%s,\t*%s"), strShortcut, strCommand);
strMiddle += strLine + _T("\r\n");
}
// 3. 拼接最终字符串
CString strFinal;
if (bFoundSection)
{
strFinal = strHeader + strMiddle + strEndMarker;
}
else
{
// 如果原文件没有标记,追加到末尾
if (!strHeader.IsEmpty() && strHeader.Right(1) != _T("\n"))
strHeader += _T("\r\n");
strFinal = strHeader;
strFinal += strBeginMarker;
strFinal += strMiddle;
strFinal += strEndMarker;
strFinal += _T("\r\n");
}
// 4. 写入文件 (UTF-8 with BOM)
CFile fileWrite;
if (!fileWrite.Open(path, CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone, &ex))
{
TRACE(_T("无法写入文件: %s\n"), path);
return;
}
const unsigned char bom[] = { 0xEF, 0xBB, 0xBF };
fileWrite.Write(bom, 3);
std::string strData = ConvertCStringToUTF8(strFinal);
if (!strData.empty())
{
fileWrite.Write(strData.c_str(), (UINT)strData.length());
}
fileWrite.Close();
}