// FontSub by Katayama Hirofumi MZ // // To the extent possible under law, the person who associated CC0 with // FontSub has waived all copyright and related or neighboring rights // to FontSub. // // You should have received a copy of the CC0 legalcode along with this // work. If not, see . #include #include #include #include #include // for std::vector #include // for std::set #include // for std::basic_string #include // for std::sort #include #include #include #include "resource.h" #define NAME_COLUMN_WIDTH 250 #define SUB_COLUMN_WIDTH 250 #define MAX_STRING 120 #ifndef _countof #define _countof(array) (sizeof(array) / sizeof(array[0])) #endif typedef std::wstring STRING; struct ITEM { STRING m_Name, m_Substitute; BYTE m_CharSet1, m_CharSet2; ITEM(const STRING& Name, const STRING& Substitute, BYTE CharSet1, BYTE CharSet2) : m_Name(Name), m_Substitute(Substitute), m_CharSet1(CharSet1), m_CharSet2(CharSet2) { } }; /* global variables */ HINSTANCE g_hInstance = NULL; HWND g_hMainWnd = NULL; HICON g_hIcon = NULL; HWND g_hListView = NULL; BOOL g_bModified = FALSE; BOOL g_bNeedsReboot = FALSE; INT g_iItem = 0; LPCWSTR g_pszClassName = L"ReactOS Font Substitutes Editor"; LPCWSTR g_pszFileHeader = L"Windows Registry Editor Version 5.00"; WCHAR g_szTitle[MAX_STRING]; WCHAR g_szNameHead[MAX_STRING]; WCHAR g_szSubstituteHead[MAX_STRING]; INT g_iSortColumn = 0; BOOL g_bSortAscendant = TRUE; LPCWSTR g_pszKey = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"; typedef std::set FONTNAMESET; typedef std::vector ITEMVECTOR; FONTNAMESET g_Names; ITEMVECTOR g_Items; STRING g_strFontName; STRING g_strSubstitute; BYTE g_CharSet1 = DEFAULT_CHARSET; BYTE g_CharSet2 = DEFAULT_CHARSET; typedef struct CHARSET_ENTRY { BYTE CharSet; LPCWSTR DisplayName; } CHARSET_ENTRY; CHARSET_ENTRY g_CharSetList[] = { { DEFAULT_CHARSET, L"DEFAULT_CHARSET (1)" }, { ANSI_CHARSET, L"ANSI_CHARSET (0)" }, { SYMBOL_CHARSET, L"SYMBOL_CHARSET (2)" }, { SHIFTJIS_CHARSET, L"SHIFTJIS_CHARSET (128)" }, { HANGUL_CHARSET, L"HANGUL_CHARSET (129)" }, { GB2312_CHARSET, L"GB2312_CHARSET (134)" }, { CHINESEBIG5_CHARSET, L"CHINESEBIG5_CHARSET (136)" }, { OEM_CHARSET, L"OEM_CHARSET (255)" }, { JOHAB_CHARSET, L"JOHAB_CHARSET (130)" }, { HEBREW_CHARSET, L"HEBREW_CHARSET (177)" }, { ARABIC_CHARSET, L"ARABIC_CHARSET (178)" }, { GREEK_CHARSET, L"GREEK_CHARSET (161)" }, { TURKISH_CHARSET, L"TURKISH_CHARSET (162)" }, { VIETNAMESE_CHARSET, L"VIETNAMESE_CHARSET (163)" }, { THAI_CHARSET, L"THAI_CHARSET (222)" }, { EASTEUROPE_CHARSET, L"EASTEUROPE_CHARSET (238)" }, { RUSSIAN_CHARSET, L"RUSSIAN_CHARSET (204)" }, { MAC_CHARSET, L"MAC_CHARSET (77)" }, { BALTIC_CHARSET, L"BALTIC_CHARSET (186)" } }; const WCHAR g_LongestName[] = L"CHINESEBIG5_CHARSET (136)"; static void trim(STRING& str) { static const WCHAR Spaces[] = L" \t\r\n"; size_t i = str.find_first_not_of(Spaces); size_t j = str.find_last_not_of(Spaces); if (i == STRING::npos || j == STRING::npos) { str.clear(); } else { str = str.substr(i, j - i + 1); } } static int CALLBACK EnumFontFamExProc(const ENUMLOGFONTW *pelf, const NEWTEXTMETRICW *pntm, int FontType, LPARAM lParam) { switch (pelf->elfFullName[0]) { case UNICODE_NULL: case L'@': break; default: g_Names.insert((const WCHAR *)pelf->elfFullName); } switch (pelf->elfLogFont.lfFaceName[0]) { case UNICODE_NULL: case L'@': break; default: g_Names.insert(pelf->elfLogFont.lfFaceName); } return 1; } BOOL DoLoadNames(void) { g_Names.clear(); LOGFONTW lf; ZeroMemory(&lf, sizeof(lf)); lf.lfCharSet = DEFAULT_CHARSET; HDC hDC = CreateCompatibleDC(NULL); EnumFontFamiliesExW(hDC, &lf, (FONTENUMPROCW)EnumFontFamExProc, 0, 0); DeleteDC(hDC); return !g_Names.empty(); } inline bool ItemCompareByNameAscend(const ITEM& Item1, const ITEM& Item2) { return Item1.m_Name < Item2.m_Name; } inline bool ItemCompareByNameDescend(const ITEM& Item1, const ITEM& Item2) { return Item1.m_Name > Item2.m_Name; } inline bool ItemCompareBySubAscend(const ITEM& Item1, const ITEM& Item2) { return Item1.m_Substitute < Item2.m_Substitute; } inline bool ItemCompareBySubDescend(const ITEM& Item1, const ITEM& Item2) { return Item1.m_Substitute > Item2.m_Substitute; } void DoSort(INT iColumn, BOOL bAscendant = TRUE) { LV_COLUMN Column; ZeroMemory(&Column, sizeof(Column)); Column.mask = LVCF_IMAGE | LVCF_SUBITEM; Column.iImage = 2; Column.iSubItem = 0; ListView_SetColumn(g_hListView, 0, &Column); Column.iSubItem = 1; ListView_SetColumn(g_hListView, 1, &Column); switch (iColumn) { case 0: Column.iSubItem = 0; if (bAscendant) { std::sort(g_Items.begin(), g_Items.end(), ItemCompareByNameAscend); Column.iImage = 0; ListView_SetColumn(g_hListView, 0, &Column); } else { std::sort(g_Items.begin(), g_Items.end(), ItemCompareByNameDescend); Column.iImage = 1; ListView_SetColumn(g_hListView, 0, &Column); } break; case 1: Column.iSubItem = 1; if (bAscendant) { std::sort(g_Items.begin(), g_Items.end(), ItemCompareBySubAscend); Column.iImage = 0; ListView_SetColumn(g_hListView, 1, &Column); } else { std::sort(g_Items.begin(), g_Items.end(), ItemCompareBySubDescend); Column.iImage = 1; ListView_SetColumn(g_hListView, 1, &Column); } break; } g_iSortColumn = iColumn; g_bSortAscendant = bAscendant; InvalidateRect(g_hListView, NULL, TRUE); } void LV_AddItems(HWND hwnd) { ListView_DeleteAllItems(hwnd); LV_ITEM Item; ZeroMemory(&Item, sizeof(Item)); Item.mask = LVIF_PARAM; const INT Count = INT(g_Items.size()); for (INT i = 0; i < Count; ++i) { Item.iItem = i; Item.iSubItem = 0; Item.lParam = i; ListView_InsertItem(hwnd, &Item); Item.iItem = i; Item.iSubItem = 1; Item.lParam = i; ListView_InsertItem(hwnd, &Item); } } BOOL DoLoadItems(void) { ITEMVECTOR Items; HKEY hKey = NULL; RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_pszKey, 0, KEY_READ, &hKey); if (hKey == NULL) return FALSE; WCHAR szName[MAX_STRING], szValue[MAX_STRING]; DWORD cbName, cbValue; for (DWORD dwIndex = 0; ; ++dwIndex) { cbName = sizeof(szName); cbValue = sizeof(szValue); LONG Error = RegEnumValueW(hKey, dwIndex, szName, &cbName, NULL, NULL, (LPBYTE)szValue, &cbValue); if (Error != ERROR_SUCCESS) break; BYTE CharSet1 = DEFAULT_CHARSET, CharSet2 = DEFAULT_CHARSET; LPWSTR pch; pch = wcsrchr(szName, L','); if (pch) { *pch = 0; CharSet1 = (BYTE)_wtoi(pch + 1); } pch = wcsrchr(szValue, L','); if (pch) { *pch = 0; CharSet2 = (BYTE)_wtoi(pch + 1); } ITEM Item(szName, szValue, CharSet1, CharSet2); trim(Item.m_Name); trim(Item.m_Substitute); Items.push_back(Item); } RegCloseKey(hKey); g_Items = Items; LV_AddItems(g_hListView); DoSort(0, TRUE); g_bModified = FALSE; g_bNeedsReboot = FALSE; return !g_Items.empty(); } BOOL DoLoad(void) { return DoLoadNames() && DoLoadItems(); } void LV_InvalidateRow(HWND hwnd, INT iRow = -1) { if (iRow == -1) iRow = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED); if (iRow == -1) return; RECT Rect; LPRECT GccIsWhining = &Rect; ListView_GetItemRect(hwnd, iRow, GccIsWhining, LVIR_BOUNDS); InvalidateRect(hwnd, &Rect, FALSE); } BOOL LV_Init(HWND hwnd) { ListView_SetExtendedListViewStyle(hwnd, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES); HIMAGELIST hImageList; hImageList = ImageList_Create(12, 12, ILC_COLOR8 | ILC_MASK, 2, 2); HBITMAP hbm; hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(2), IMAGE_BITMAP, 12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS); assert(hbm); ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192)); DeleteObject(hbm); hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(3), IMAGE_BITMAP, 12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS); assert(hbm); ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192)); DeleteObject(hbm); hbm = (HBITMAP)LoadImage(g_hInstance, MAKEINTRESOURCE(4), IMAGE_BITMAP, 12, 12, LR_CREATEDIBSECTION | LR_LOADMAP3DCOLORS); assert(hbm); ImageList_AddMasked(hImageList, hbm, RGB(192, 192, 192)); DeleteObject(hbm); ListView_SetImageList(hwnd, hImageList, LVSIL_SMALL); LV_COLUMNW Column; ZeroMemory(&Column, sizeof(Column)); Column.mask = LVCF_FMT | LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_IMAGE; Column.fmt = LVCFMT_LEFT; Column.cx = NAME_COLUMN_WIDTH; Column.pszText = g_szNameHead; Column.iSubItem = 0; Column.iImage = 0; ListView_InsertColumn(hwnd, 0, &Column); Column.cx = SUB_COLUMN_WIDTH; Column.pszText = g_szSubstituteHead; Column.iSubItem = 1; Column.iImage = 2; ListView_InsertColumn(hwnd, 1, &Column); UINT State = LVIS_SELECTED | LVIS_FOCUSED; ListView_SetItemState(hwnd, 0, State, State); return TRUE; } BOOL EditDlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { COMBOBOXEXITEMW Item; ZeroMemory(&Item, sizeof(Item)); Item.mask = CBEIF_TEXT; FONTNAMESET::iterator it, end = g_Names.end(); for (it = g_Names.begin(); it != end; ++it) { Item.pszText = const_cast(it->c_str()); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb2)); SendDlgItemMessageW(hwnd, cmb2, CBEM_INSERTITEM, 0, (LPARAM)&Item); } SetDlgItemTextW(hwnd, edt1, g_strFontName.c_str()); SetDlgItemTextW(hwnd, cmb2, g_strSubstitute.c_str()); const INT Count = _countof(g_CharSetList); for (INT i = 0; i < Count; ++i) { Item.pszText = const_cast(g_CharSetList[i].DisplayName); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb3)); SendDlgItemMessageW(hwnd, cmb3, CBEM_INSERTITEM, 0, (LPARAM)&Item); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb4)); SendDlgItemMessageW(hwnd, cmb4, CBEM_INSERTITEM, 0, (LPARAM)&Item); } SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, 0, 0); SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, 0, 0); for (INT i = 0; i < Count; ++i) { if (g_CharSet1 == g_CharSetList[i].CharSet) { SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, i, 0); } if (g_CharSet2 == g_CharSetList[i].CharSet) { SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, i, 0); } } SIZE siz; HDC hDC = CreateCompatibleDC(NULL); SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT)); GetTextExtentPoint32W(hDC, g_LongestName, lstrlenW(g_LongestName), &siz); DeleteDC(hDC); SendDlgItemMessageW(hwnd, cmb3, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0); SendDlgItemMessageW(hwnd, cmb4, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0); EnableWindow(GetDlgItem(hwnd, cmb3), FALSE); return TRUE; } void LV_OnDelete(HWND hwnd, INT iRow = -1) { if (iRow == -1) iRow = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED); if (iRow == -1) return; UINT State = LVIS_SELECTED | LVIS_FOCUSED; ListView_SetItemState(g_hListView, iRow, State, State); WCHAR sz[MAX_STRING]; LoadStringW(g_hInstance, IDS_QUERYDELETE, sz, _countof(sz)); if (IDYES != MessageBoxW(g_hMainWnd, sz, g_szTitle, MB_ICONINFORMATION | MB_YESNO)) { return; } ListView_DeleteItem(hwnd, iRow); g_Items.erase(g_Items.begin() + iRow); g_bModified = TRUE; ListView_SetItemState(g_hListView, iRow, State, State); InvalidateRect(hwnd, NULL, TRUE); } void EditDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { WCHAR szValue[MAX_STRING]; STRING str; INT i; switch (id) { case IDOK: GetDlgItemTextW(hwnd, cmb2, szValue, _countof(szValue)); str = szValue; trim(str); if (str.empty()) { WCHAR sz[MAX_STRING]; SendDlgItemMessageW(hwnd, cmb2, CB_SETEDITSEL, 0, MAKELPARAM(0, -1)); SetFocus(GetDlgItem(hwnd, cmb2)); LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz)); MessageBoxW(hwnd, sz, NULL, MB_ICONERROR); return; } g_Items[g_iItem].m_CharSet2 = DEFAULT_CHARSET; i = SendDlgItemMessageW(hwnd, cmb4, CB_GETCURSEL, 0, 0); if (i != CB_ERR) { g_Items[g_iItem].m_CharSet2 = g_CharSetList[i].CharSet; } g_Items[g_iItem].m_Substitute = str; g_bModified = TRUE; EndDialog(hwnd, IDOK); break; case IDCANCEL: EndDialog(hwnd, IDCANCEL); break; case psh1: LV_OnDelete(g_hListView, g_iItem); EndDialog(hwnd, psh1); break; } } INT_PTR CALLBACK EditDlg_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_INITDIALOG, EditDlg_OnInitDialog); HANDLE_MSG(hwnd, WM_COMMAND, EditDlg_OnCommand); } return 0; } void LV_OnDblClk(HWND hwnd) { g_iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED); if (g_iItem == -1) return; g_strFontName = g_Items[g_iItem].m_Name; g_strSubstitute = g_Items[g_iItem].m_Substitute; g_CharSet1 = g_Items[g_iItem].m_CharSet1; g_CharSet2 = g_Items[g_iItem].m_CharSet2; DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_EDIT), g_hMainWnd, EditDlg_DlgProc); InvalidateRect(g_hListView, NULL, TRUE); } BOOL MainWnd_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) { DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_VSCROLL | LVS_SINGLESEL | LVS_REPORT | LVS_OWNERDRAWFIXED; DWORD dwExStyle = WS_EX_CLIENTEDGE; g_hListView = CreateWindowEx(dwExStyle, WC_LISTVIEW, NULL, dwStyle, 0, 0, 0, 0, hwnd, (HMENU)1, g_hInstance, NULL); if (g_hListView == NULL) return FALSE; if (!LV_Init(g_hListView)) return FALSE; if (!DoLoad()) return FALSE; UINT State = LVIS_SELECTED | LVIS_FOCUSED; ListView_SetItemState(g_hListView, 0, State, State); SetFocus(g_hListView); return TRUE; } BOOL AddDlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { COMBOBOXEXITEMW Item; ZeroMemory(&Item, sizeof(Item)); Item.iItem = -1; Item.mask = CBEIF_TEXT; FONTNAMESET::iterator it, end = g_Names.end(); for (it = g_Names.begin(); it != end; ++it) { Item.pszText = const_cast(it->c_str()); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb1)); SendDlgItemMessageW(hwnd, cmb1, CBEM_INSERTITEM, 0, (LPARAM)&Item); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb2)); SendDlgItemMessageW(hwnd, cmb2, CBEM_INSERTITEM, 0, (LPARAM)&Item); } WCHAR szEnterName[MAX_STRING]; LoadStringW(g_hInstance, IDS_ENTERNAME, szEnterName, _countof(szEnterName)); SetDlgItemTextW(hwnd, cmb1, szEnterName); SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1)); const INT Count = _countof(g_CharSetList); for (INT i = 0; i < Count; ++i) { Item.pszText = const_cast(g_CharSetList[i].DisplayName); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb3)); SendDlgItemMessageW(hwnd, cmb3, CBEM_INSERTITEM, 0, (LPARAM)&Item); Item.iItem = ComboBox_GetCount(GetDlgItem(hwnd, cmb4)); SendDlgItemMessageW(hwnd, cmb4, CBEM_INSERTITEM, 0, (LPARAM)&Item); } SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, 0, 0); SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, 0, 0); for (INT i = 0; i < Count; ++i) { if (g_CharSet1 == g_CharSetList[i].CharSet) { SendDlgItemMessageW(hwnd, cmb3, CB_SETCURSEL, i, 0); } if (g_CharSet2 == g_CharSetList[i].CharSet) { SendDlgItemMessageW(hwnd, cmb4, CB_SETCURSEL, i, 0); } } SIZE siz; HDC hDC = CreateCompatibleDC(NULL); SelectObject(hDC, GetStockObject(DEFAULT_GUI_FONT)); GetTextExtentPoint32W(hDC, g_LongestName, lstrlenW(g_LongestName), &siz); DeleteDC(hDC); SendDlgItemMessageW(hwnd, cmb3, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0); SendDlgItemMessageW(hwnd, cmb4, CB_SETHORIZONTALEXTENT, siz.cx + 16, 0); return TRUE; } void AddDlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { WCHAR szKey[MAX_STRING], szValue[MAX_STRING], sz[MAX_STRING]; INT i, iCharSet1, iCharSet2; BYTE CharSet1, CharSet2; STRING key, value; switch (id) { case IDOK: GetDlgItemTextW(hwnd, cmb1, szKey, _countof(szKey)); key = szKey; trim(key); LoadStringW(g_hInstance, IDS_ENTERNAME, sz, _countof(sz)); if (key.empty() || key == sz) { SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1)); SetFocus(GetDlgItem(hwnd, cmb1)); LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz)); MessageBoxW(hwnd, sz, NULL, MB_ICONERROR); return; } GetDlgItemTextW(hwnd, cmb2, szValue, _countof(szValue)); value = szValue; trim(value); if (value.empty()) { SendDlgItemMessageW(hwnd, cmb2, CB_SETEDITSEL, 0, MAKELPARAM(0, -1)); SetFocus(GetDlgItem(hwnd, cmb2)); LoadStringW(g_hInstance, IDS_ENTERNAME2, sz, _countof(sz)); MessageBoxW(hwnd, sz, NULL, MB_ICONERROR); return; } iCharSet1 = SendDlgItemMessageW(hwnd, cmb3, CB_GETCURSEL, 0, 0); if (iCharSet1 == CB_ERR) iCharSet1 = 0; iCharSet2 = SendDlgItemMessageW(hwnd, cmb4, CB_GETCURSEL, 0, 0); if (iCharSet2 == CB_ERR) iCharSet2 = 0; CharSet1 = g_CharSetList[iCharSet1].CharSet; CharSet2 = g_CharSetList[iCharSet2].CharSet; for (i = 0; i < (INT)g_Items.size(); ++i) { if (g_Items[i].m_Name == key && g_Items[i].m_CharSet1 == CharSet1) { WCHAR sz[MAX_STRING]; SendDlgItemMessageW(hwnd, cmb1, CB_SETEDITSEL, 0, MAKELPARAM(0, -1)); SetFocus(GetDlgItem(hwnd, cmb1)); LoadStringW(g_hInstance, IDS_ALREADYEXISTS, sz, _countof(sz)); MessageBoxW(hwnd, sz, NULL, MB_ICONERROR); return; } } { ITEM Item(key, value, CharSet1, CharSet2); g_Items.push_back(Item); g_bModified = TRUE; i = (INT)g_Items.size(); LV_ITEM LvItem; ZeroMemory(&LvItem, sizeof(LvItem)); LvItem.mask = LVIF_PARAM; LvItem.iItem = i; LvItem.lParam = i; LvItem.iSubItem = 0; ListView_InsertItem(g_hListView, &LvItem); LvItem.iSubItem = 1; ListView_InsertItem(g_hListView, &LvItem); } g_bModified = TRUE; EndDialog(hwnd, IDOK); break; case IDCANCEL: EndDialog(hwnd, IDCANCEL); break; } } INT_PTR CALLBACK AddDlg_DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_INITDIALOG, AddDlg_OnInitDialog); HANDLE_MSG(hwnd, WM_COMMAND, AddDlg_OnCommand); } return 0; } void MainWnd_OnNew(HWND hwnd) { g_iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED); if (g_iItem == -1) return; g_strFontName = g_Items[g_iItem].m_Name; g_strSubstitute = g_Items[g_iItem].m_Substitute; g_CharSet1 = g_Items[g_iItem].m_CharSet1; g_CharSet2 = g_Items[g_iItem].m_CharSet2; if (IDOK == DialogBox(g_hInstance, MAKEINTRESOURCE(IDD_ADD), g_hMainWnd, AddDlg_DlgProc)) { INT i = ListView_GetItemCount(g_hListView) - 1; UINT State = LVIS_SELECTED | LVIS_FOCUSED; ListView_SetItemState(g_hListView, i, State, State); ListView_EnsureVisible(g_hListView, i, FALSE); } } BOOL MainWnd_OnUpdateRegistry(HWND hwnd) { // open the key HKEY hKey = NULL; RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_pszKey, 0, KEY_ALL_ACCESS, &hKey); if (hKey == NULL) return FALSE; // clear all values WCHAR szName[MAX_STRING], szValue[MAX_STRING]; DWORD cbName, cbValue; for (;;) { cbName = sizeof(szName); cbValue = sizeof(szValue); LONG Error = RegEnumValueW(hKey, 0, szName, &cbName, NULL, NULL, (LPBYTE)szValue, &cbValue); if (Error != ERROR_SUCCESS) break; RegDeleteValueW(hKey, szName); } // set values size_t Count = g_Items.size(); for (size_t i = 0; i < Count; ++i) { DWORD cbData = (g_Items[i].m_Substitute.size() + 1) * sizeof(WCHAR); RegSetValueExW(hKey, g_Items[i].m_Name.c_str(), 0, REG_SZ, (LPBYTE)g_Items[i].m_Substitute.c_str(), cbData); } // close now RegCloseKey(hKey); g_bModified = FALSE; g_bNeedsReboot = TRUE; return TRUE; } LPWSTR SkipSpace(LPCWSTR pch) { while (*pch && wcschr(L" \t\r\n", *pch) != NULL) { ++pch; } return const_cast(pch); } LPWSTR SkipQuoted(LPWSTR pch) { ++pch; // L'"' while (*pch) { if (*pch == L'"') { ++pch; break; } if (*pch == L'\\') { ++pch; } ++pch; } return pch; } void UnescapeHex(const STRING& str, size_t& i, STRING& Ret, BOOL Unicode) { STRING Num; // hexadecimal if (iswxdigit(str[i])) { Num += str[i]; ++i; if (iswxdigit(str[i])) { Num += str[i]; ++i; if (Unicode) { if (iswxdigit(str[i])) { Num += str[i]; ++i; if (iswxdigit(str[i])) { Num += str[i]; ++i; } } } } } if (!Num.empty()) { Ret += (WCHAR)wcstoul(&Num[0], NULL, 16); } } void UnescapeOther(const STRING& str, size_t& i, STRING& Ret) { STRING Num; // check octal if (L'0' <= str[i] && str[i] < L'8') { Num += str[i]; ++i; if (L'0' <= str[i] && str[i] < L'8') { Num += str[i]; ++i; if (L'0' <= str[i] && str[i] < L'8') { Num += str[i]; ++i; } } } if (Num.empty()) { Ret += str[i]; ++i; } else { // octal Ret += (WCHAR)wcstoul(&Num[0], NULL, 8); } } // process escape sequence void UnescapeChar(const STRING& str, size_t& i, STRING& Ret) { if (str[i] != L'\\') { Ret += str[i]; ++i; return; } ++i; switch (str[i]) { case L'a': Ret += L'\a'; ++i; break; case L'b': Ret += L'\b'; ++i; break; case L'f': Ret += L'\f'; ++i; break; case L'n': Ret += L'\n'; ++i; break; case L'r': Ret += L'\r'; ++i; break; case L't': Ret += L'\t'; ++i; break; case L'v': Ret += L'\v'; ++i; break; case L'x': // hexidemical ++i; UnescapeHex(str, i, Ret, FALSE); break; case L'u': // Unicode hexidemical ++i; UnescapeHex(str, i, Ret, TRUE); break; default: // other case UnescapeOther(str, i, Ret); break; } } STRING Unquote(const STRING& str) { if (str[0] != L'"') return str; STRING Ret; size_t i = 1; while (i < str.size()) { if (str[i] == L'"' || str[i] == UNICODE_NULL) break; UnescapeChar(str, i, Ret); } return Ret; } BOOL DoParseFile(LPVOID pvContents, DWORD dwSize) { ITEMVECTOR Items; LPWSTR pch, pchSep, pchStart = (LPWSTR)pvContents; pchStart[dwSize / sizeof(WCHAR)] = UNICODE_NULL; // check header const DWORD cbHeader = lstrlenW(g_pszFileHeader) * sizeof(WCHAR); if (memcmp(pchStart, g_pszFileHeader, cbHeader) != 0) return FALSE; pchStart += cbHeader / sizeof(WCHAR); // find the key WCHAR szKey[MAX_STRING]; wsprintfW(szKey, L"[HKEY_LOCAL_MACHINE\\%s]", g_pszKey); pch = wcsstr(pchStart, szKey); if (pch == NULL) return FALSE; pchStart = pch + lstrlenW(szKey); for (;;) { pchStart = SkipSpace(pchStart); if (*pchStart == UNICODE_NULL || *pchStart == L'[') break; pch = wcschr(pchStart, L'\n'); if (pch) *pch = UNICODE_NULL; pchSep = SkipQuoted(pchStart); if (*pchSep == L'=') { *pchSep = UNICODE_NULL; STRING key = pchStart; trim(key); key = Unquote(key); STRING value = pchSep + 1; trim(value); value = Unquote(value); BYTE CharSet1 = DEFAULT_CHARSET, CharSet2 = DEFAULT_CHARSET; size_t pos; pos = key.find(L','); if (pos != STRING::npos) { CharSet1 = (BYTE)_wtoi(&key[pos + 1]); key.resize(pos); trim(key); } pos = value.find(L','); if (pos != STRING::npos) { CharSet2 = (BYTE)_wtoi(&value[pos + 1]); value.resize(pos); trim(value); } ITEM Item(key, value, CharSet1, CharSet2); Items.push_back(Item); } if (pch == NULL) break; pchStart = pch + 1; } g_Items = Items; g_bModified = TRUE; LV_AddItems(g_hListView); return TRUE; } BOOL DoImport(HWND hwnd, LPCWSTR pszFile) { HANDLE hFile = CreateFileW(pszFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; BOOL bSuccess = FALSE; DWORD dwSize = GetFileSize(hFile, NULL); if (dwSize != 0xFFFFFFFF) { std::vector Contents(dwSize + 2); DWORD cbRead; if (ReadFile(hFile, &Contents[0], dwSize, &cbRead, NULL) && cbRead == dwSize) { /* check BOM */ if (memcmp(&Contents[0], "\xFF\xFE", 2) == 0) { bSuccess = DoParseFile(&Contents[2], dwSize - 2); } else { bSuccess = DoParseFile(&Contents[0], dwSize); } } } CloseHandle(hFile); return bSuccess; } STRING Escape(const STRING& str) { STRING Ret; for (size_t i = 0; i < str.size(); ++i) { switch (str[i]) { case L'"': case L'\\': Ret += L'\\'; Ret += str[i]; break; default: Ret += str[i]; } } return Ret; } BOOL DoExport(HWND hwnd, LPCWSTR pszFile) { HANDLE hFile = CreateFileW(pszFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); if (hFile == INVALID_HANDLE_VALUE) return FALSE; BOOL bSuccess; DWORD dwSize, cbWritten; WCHAR szCharSet1[MAX_STRING], szCharSet2[MAX_STRING]; WCHAR szLine[MAX_STRING * 2 + 4]; /* write header */ dwSize = lstrlenW(g_pszFileHeader) * sizeof(WCHAR); bSuccess = WriteFile(hFile, "\xFF\xFE", 2, &cbWritten, NULL) && WriteFile(hFile, g_pszFileHeader, dwSize, &cbWritten, NULL); if (bSuccess) { wsprintfW(szLine, L"\r\n\r\n[HKEY_LOCAL_MACHINE\\%s]\r\n", g_pszKey); dwSize = lstrlenW(szLine) * sizeof(WCHAR); bSuccess = WriteFile(hFile, szLine, dwSize, &cbWritten, NULL); } if (bSuccess) { size_t i, Count = g_Items.size(); for (i = 0; i < Count; ++i) { if (g_Items[i].m_CharSet1 != DEFAULT_CHARSET) wsprintfW(szCharSet1, L",%u", g_Items[i].m_CharSet1); else szCharSet1[0] = UNICODE_NULL; if (g_Items[i].m_CharSet2 != DEFAULT_CHARSET) wsprintfW(szCharSet2, L",%u", g_Items[i].m_CharSet2); else szCharSet2[0] = UNICODE_NULL; STRING Name = Escape(g_Items[i].m_Name); STRING Substitute = Escape(g_Items[i].m_Substitute); wsprintfW(szLine, L"\"%s%s\"=\"%s%s\"\r\n", Name.c_str(), szCharSet1, Substitute.c_str(), szCharSet2); dwSize = lstrlenW(szLine) * sizeof(WCHAR); if (!WriteFile(hFile, szLine, dwSize, &cbWritten, NULL)) { bSuccess = FALSE; break; } } WriteFile(hFile, L"\r\n", 2 * sizeof(WCHAR), &cbWritten, NULL); } CloseHandle(hFile); if (!bSuccess) { DeleteFileW(pszFile); } return bSuccess; } void MakeFilter(LPWSTR pszFilter) { while (*pszFilter) { if (*pszFilter == L'|') *pszFilter = 0; ++pszFilter; } } void MainWnd_OnImport(HWND hwnd) { OPENFILENAMEW ofn = {0}; WCHAR szFile[MAX_PATH] = L""; WCHAR szImportTitle[MAX_STRING]; WCHAR szCannotImport[MAX_STRING]; WCHAR szImportFilter[MAX_STRING]; LoadStringW(g_hInstance, IDS_IMPORT, szImportTitle, _countof(szImportTitle)); LoadStringW(g_hInstance, IDS_CANTIMPORT, szCannotImport, _countof(szCannotImport)); LoadStringW(g_hInstance, IDS_INPFILTER, szImportFilter, _countof(szImportFilter)); MakeFilter(szImportFilter); ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofn.hwndOwner = hwnd; ofn.lpstrFilter = szImportFilter; ofn.lpstrFile = szFile; ofn.nMaxFile = _countof(szFile); ofn.lpstrTitle = szImportTitle; ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST; ofn.lpstrDefExt = L"reg"; if (GetOpenFileNameW(&ofn)) { if (!DoImport(hwnd, szFile)) { MessageBoxW(hwnd, szCannotImport, g_szTitle, MB_ICONERROR); } } } void MainWnd_OnExport(HWND hwnd) { OPENFILENAMEW ofn = {0}; WCHAR szFile[MAX_PATH] = L""; WCHAR szExportTitle[MAX_STRING]; WCHAR szCannotExport[MAX_STRING]; WCHAR szExportFilter[MAX_STRING]; LoadStringW(g_hInstance, IDS_EXPORT, szExportTitle, _countof(szExportTitle)); LoadStringW(g_hInstance, IDS_CANTEXPORT, szCannotExport, _countof(szCannotExport)); LoadStringW(g_hInstance, IDS_OUTFILTER, szExportFilter, _countof(szExportFilter)); MakeFilter(szExportFilter); ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofn.hwndOwner = hwnd; ofn.lpstrFilter = szExportFilter; ofn.lpstrFile = szFile; ofn.nMaxFile = _countof(szFile); ofn.lpstrTitle = szExportTitle; ofn.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; ofn.lpstrDefExt = L"reg"; if (GetSaveFileNameW(&ofn)) { if (!DoExport(hwnd, szFile)) { MessageBoxW(hwnd, szCannotExport, g_szTitle, MB_ICONERROR); } } } void MainWnd_OnReload(HWND hwnd) { DoLoad(); } void MainWnd_OnEdit(HWND hwnd) { LV_OnDblClk(g_hListView); } void MainWnd_OnDelete(HWND hwnd) { LV_OnDelete(g_hListView); } void MainWnd_OnOpenRegKey(HWND hwnd) { static const WCHAR s_szRegeditKey[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit"; WCHAR sz[MAX_STRING]; // open regedit key HKEY hKey = NULL; LSTATUS Result = RegCreateKeyExW(HKEY_CURRENT_USER, s_szRegeditKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL); if (Result != ERROR_SUCCESS) { LoadStringW(g_hInstance, IDS_CANTOPENKEY, sz, _countof(sz)); MessageBoxW(hwnd, sz, NULL, MB_ICONERROR); return; } // set LastKey value wsprintfW(sz, L"HKEY_LOCAL_MACHINE\\%s", g_pszKey); DWORD dwSize = sizeof(sz); Result = RegSetValueExW(hKey, L"LastKey", 0, REG_SZ, (LPBYTE)sz, dwSize); // close now RegCloseKey(hKey); if (Result != ERROR_SUCCESS) { LoadStringW(g_hInstance, IDS_CANTOPENKEY, sz, _countof(sz)); MessageBoxW(hwnd, sz, NULL, MB_ICONERROR); return; } // open by regedit ShellExecuteW(hwnd, NULL, L"regedit.exe", NULL, NULL, SW_SHOWNORMAL); } void MainWnd_OnAbout(HWND hwnd) { WCHAR szAbout[MAX_PATH]; LoadStringW(g_hInstance, IDS_ABOUT, szAbout, _countof(szAbout)); MSGBOXPARAMS Params; ZeroMemory(&Params, sizeof(Params)); Params.cbSize = sizeof(Params); Params.hwndOwner = hwnd; Params.hInstance = g_hInstance; Params.lpszText = szAbout; Params.lpszCaption = g_szTitle; Params.dwStyle = MB_OK | MB_USERICON; Params.lpszIcon = MAKEINTRESOURCEW(1); Params.dwLanguageId = LANG_USER_DEFAULT; MessageBoxIndirectW(&Params); } void MainWnd_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case ID_NEW: MainWnd_OnNew(hwnd); break; case ID_EDIT: MainWnd_OnEdit(hwnd); break; case ID_EXIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; case ID_RELOAD: MainWnd_OnReload(hwnd); break; case ID_UPDATE_REGISTRY: MainWnd_OnUpdateRegistry(hwnd); break; case ID_DELETE: MainWnd_OnDelete(hwnd); break; case ID_IMPORT: MainWnd_OnImport(hwnd); break; case ID_EXPORT: MainWnd_OnExport(hwnd); break; case ID_OPEN_REGKEY: MainWnd_OnOpenRegKey(hwnd); break; case ID_ABOUT: MainWnd_OnAbout(hwnd); break; } } void MainWnd_OnDestroy(HWND hwnd) { PostQuitMessage(0); } void MainWnd_OnSize(HWND hwnd, UINT state, int cx, int cy) { MoveWindow(g_hListView, 0, 0, cx, cy, TRUE); } void MainWnd_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT *lpDrawItem) { if (lpDrawItem->CtlType != ODT_LISTVIEW) return; HDC hDC = lpDrawItem->hDC; SetBkMode(hDC, TRANSPARENT); INT iColumn = 0, x, cx; RECT rcItem, rcSubItem, rcText; STRING Str; x = -GetScrollPos(g_hListView, SB_HORZ); rcItem = lpDrawItem->rcItem; if (lpDrawItem->itemState & ODS_SELECTED) { FillRect(hDC, &rcItem, (HBRUSH)(COLOR_HIGHLIGHT + 1)); SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT)); } else { FillRect(hDC, &rcItem, (HBRUSH)(COLOR_WINDOW + 1)); SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT)); } cx = ListView_GetColumnWidth(g_hListView, iColumn); rcSubItem = rcItem; rcSubItem.left = x; rcSubItem.right = x + cx; WCHAR sz[MAX_STRING]; rcText = rcSubItem; InflateRect(&rcText, -1, -1); Str = g_Items[lpDrawItem->itemID].m_Name; BYTE CharSet1 = g_Items[lpDrawItem->itemID].m_CharSet1; if (CharSet1 != DEFAULT_CHARSET) wsprintfW(sz, L"%s,%u", Str.c_str(), CharSet1); else wsprintfW(sz, L"%s", Str.c_str()); DrawTextW(hDC, sz, lstrlenW(sz), &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX); x += cx; ++iColumn; cx = ListView_GetColumnWidth(g_hListView, iColumn); rcSubItem = rcItem; rcSubItem.left = x; rcSubItem.right = x + cx; rcText = rcSubItem; InflateRect(&rcText, -1, -1); Str = g_Items[lpDrawItem->itemID].m_Substitute; BYTE CharSet2 = g_Items[lpDrawItem->itemID].m_CharSet2; if (CharSet2 != DEFAULT_CHARSET) wsprintfW(sz, L"%s,%u", Str.c_str(), CharSet2); else wsprintfW(sz, L"%s", Str.c_str()); DrawTextW(hDC, sz, lstrlenW(sz), &rcText, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_NOPREFIX); } void MainWnd_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT *lpMeasureItem) { if (lpMeasureItem->CtlType != ODT_LISTVIEW) return; TEXTMETRIC tm; HDC hDC = GetDC(hwnd); GetTextMetrics(hDC, &tm); ReleaseDC(hwnd, hDC); lpMeasureItem->itemHeight = tm.tmHeight * 4 / 3; } LRESULT MainWnd_OnNotify(HWND hwnd, int idFrom, NMHDR *pnmhdr) { NM_LISTVIEW *pNMLV = (NM_LISTVIEW *)pnmhdr; LV_KEYDOWN *pLVKD = (LV_KEYDOWN *)pnmhdr; switch (pnmhdr->code) { case LVN_COLUMNCLICK: if (pNMLV->iSubItem == g_iSortColumn) DoSort(pNMLV->iSubItem, !g_bSortAscendant); else DoSort(pNMLV->iSubItem, TRUE); break; case NM_DBLCLK: LV_OnDblClk(g_hListView); break; case LVN_KEYDOWN: if (pLVKD->wVKey == VK_RETURN) // [Enter] key { LV_OnDblClk(g_hListView); } if (pLVKD->wVKey == VK_DELETE) // [Del] key { LV_OnDelete(g_hListView); } break; } return 0; } LRESULT MainWnd_OnContextMenu(HWND hwnd, HWND hwndContext, UINT xPos, UINT yPos) { POINT pt = {(INT)xPos, (INT)yPos}; ScreenToClient(g_hListView, &pt); SendMessageW(g_hListView, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y)); HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(2)); if (hMenu == NULL) return 0; HMENU hSubMenu = GetSubMenu(hMenu, 0); if (hSubMenu == NULL) return 0; SetForegroundWindow(hwnd); TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, xPos, yPos, 0, g_hMainWnd, NULL); PostMessage(g_hMainWnd, WM_NULL, 0, 0); return 0; } void MainWnd_OnActivate(HWND hwnd, UINT state, HWND hwndActDeact, BOOL fMinimized) { if (state != WA_INACTIVE) { SetFocus(g_hListView); } } BOOL EnableProcessPrivileges(LPCWSTR lpPrivilegeName, BOOL bEnable = TRUE) { HANDLE hToken; LUID luid; TOKEN_PRIVILEGES tokenPrivileges; BOOL Ret; Ret = ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); if (!Ret) return Ret; // failure Ret = ::LookupPrivilegeValueW(NULL, lpPrivilegeName, &luid); if (Ret) { tokenPrivileges.PrivilegeCount = 1; tokenPrivileges.Privileges[0].Luid = luid; tokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0; Ret = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, 0, 0); } ::CloseHandle(hToken); return Ret; } void MainWnd_OnClose(HWND hwnd) { if (!g_bNeedsReboot && !g_bModified) { DestroyWindow(hwnd); return; } if (g_bModified) { WCHAR szUpdateNow[MAX_STRING]; LoadStringW(g_hInstance, IDS_QUERYUPDATE, szUpdateNow, _countof(szUpdateNow)); INT id = MessageBoxW(hwnd, szUpdateNow, g_szTitle, MB_ICONINFORMATION | MB_YESNOCANCEL); switch (id) { case IDYES: MainWnd_OnUpdateRegistry(hwnd); break; case IDNO: break; case IDCANCEL: return; } } if (g_bNeedsReboot) { WCHAR szRebootNow[MAX_STRING]; LoadStringW(g_hInstance, IDS_REBOOTNOW, szRebootNow, _countof(szRebootNow)); INT id = MessageBoxW(hwnd, szRebootNow, g_szTitle, MB_ICONINFORMATION | MB_YESNOCANCEL); switch (id) { case IDYES: EnableProcessPrivileges(SE_SHUTDOWN_NAME, TRUE); ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0); break; case IDNO: break; case IDCANCEL: return; } } ::DestroyWindow(hwnd); } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_CREATE, MainWnd_OnCreate); HANDLE_MSG(hwnd, WM_COMMAND, MainWnd_OnCommand); HANDLE_MSG(hwnd, WM_DESTROY, MainWnd_OnDestroy); HANDLE_MSG(hwnd, WM_SIZE, MainWnd_OnSize); HANDLE_MSG(hwnd, WM_DRAWITEM, MainWnd_OnDrawItem); HANDLE_MSG(hwnd, WM_MEASUREITEM, MainWnd_OnMeasureItem); HANDLE_MSG(hwnd, WM_NOTIFY, MainWnd_OnNotify); HANDLE_MSG(hwnd, WM_CONTEXTMENU, MainWnd_OnContextMenu); HANDLE_MSG(hwnd, WM_ACTIVATE, MainWnd_OnActivate); HANDLE_MSG(hwnd, WM_CLOSE, MainWnd_OnClose); default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } } INT WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, INT nCmdShow) { g_hInstance = hInstance; InitCommonControls(); HACCEL hAccel = LoadAcceleratorsW(hInstance, MAKEINTRESOURCEW(1)); LoadStringW(hInstance, IDS_TITLE, g_szTitle, _countof(g_szTitle)); LoadStringW(hInstance, IDS_FONTNAME, g_szNameHead, _countof(g_szNameHead)); LoadStringW(hInstance, IDS_SUBSTITUTE, g_szSubstituteHead, _countof(g_szSubstituteHead)); WNDCLASSW wc = {0}; wc.style = 0; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; g_hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(1)); wc.hIcon = g_hIcon; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); wc.lpszMenuName = MAKEINTRESOURCEW(1); wc.lpszClassName = g_pszClassName; if (!RegisterClassW(&wc)) { MessageBoxA(NULL, "ERROR: RegisterClass failed.", NULL, MB_ICONERROR); return 1; } const DWORD dwStyle = WS_OVERLAPPEDWINDOW; INT Width = NAME_COLUMN_WIDTH + SUB_COLUMN_WIDTH + GetSystemMetrics(SM_CXVSCROLL) + GetSystemMetrics(SM_CXSIZEFRAME); INT Height = 320; RECT Rect = { 0, 0, Width, Height }; AdjustWindowRect(&Rect, dwStyle, TRUE); Width = Rect.right - Rect.left; Height = Rect.bottom - Rect.top; g_hMainWnd = CreateWindowW(g_pszClassName, g_szTitle, dwStyle, CW_USEDEFAULT, CW_USEDEFAULT, Width, Height, NULL, NULL, hInstance, NULL); if (g_hMainWnd == NULL) { MessageBoxA(NULL, "ERROR: CreateWindow failed.", NULL, MB_ICONERROR); return 2; } ShowWindow(g_hMainWnd, nCmdShow); UpdateWindow(g_hMainWnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (TranslateAccelerator(g_hMainWnd, hAccel, &msg)) continue; TranslateMessage(&msg); DispatchMessage(&msg); } return (INT)msg.wParam; }