まずは、ベクター等よりNKF32を入手し適当なディレクトリにインストールします。ここでは、C:\nkfにインストールしてたものとします。
この中に、nkf32.h, hkf32.dll, nkf32.lib, nkf32b.lib, nkf32.docが入っています。
dllは、システムのフォルダなどパスの通っているディレクトリにコピーしておくと便利です。
ここにある、ヘッダファイルと、ライブラリファイルをそのまま使うためには、C++ではなく、Cでプログラムを書く必要があります。
さて、VC++では次のような設定をしておきます。
プロジェクトにnkf32.libを参加させます。(注:nkf32b.libはC++Builder用です) ヘッダファイルをプロジェクトのフォルダにコピーしてもいいのですが、ここでは、 ソリューション・エクスプローラを右クリックしてプロパティを選択し、C/C++ の「全般」「追加のインクルードディレクトリ」にc:\nkfを追加しておきます。
今回は、簡単のためS-JISで入力された、テキストをEUCに変換して出力する簡単な
エディタを作ってみます。
// nkf01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END MENUITEM "名前を付けてEUCに変換して保存(&A)...", IDM_SAVEAS END END普通のメニューのリソース・スクリプトです。
// nkf01.c #define ID_EDIT 100 #include <windows.h> #include "resource.h" #include "nkf32.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MySaveAs(HWND, HWND); char szClassName[] = "nkf01"; //ウィンドウクラス HINSTANCE hInst;ソース・ファイルの拡張子は「C」にしておきます。nkf32.hをインクルードしておきます。
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; BOOL bRet; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるnkf", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }いつもと同じです。現在のインスタンスハンドルをグローバル変数にコピーしておきました。
//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HWND hEdit; switch (msg) { case WM_CREATE: hEdit = CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN, 0, 0, 0, 0, hWnd, (HMENU)ID_EDIT, hInst, NULL); break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_SETFOCUS: SetFocus(hEdit); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_SAVEAS: MySaveAs(hWnd, hEdit); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよろしいですか", "終了確認", MB_OKCANCEL); if (id == IDOK) { DestroyWindow(hEdit); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }メイン・ウィンドウのプロシージャです。
WM_CREATEメッセージが来たら、エディットコントロールを作っておきます。
WM_SIZEメッセージが来たら、エディットコントロールの大きさを調整して、クライアント 領域と同じ大きさにしておきます。
WM_SETFOCUSメッセージが来たら、SetFocus関数でエディットコントロールに、キーボード・フォーカスを設定します。このメッセージは、ウィンドウがキーボード・フォーカス を取得したら送られてきます。
LRESULT CALLBACK WindowProc( HWND hwnd, // ウィンドウハンドル UINT uMsg, // WM_SETFOCUS WPARAM wParam, // ウィンドウハンドル LPARAM lParam // 使用しない );メニューから、IDM_SAVEASが選択されたら、自作関数MySaveAsを呼びます。
BOOL MySaveAs(HWND hWnd, HWND hEdit) { HLOCAL hMem; HANDLE hHeap, hFile; char *lpszBuf, *lpszOut; static char szFile[MAX_PATH] = "", szFileTitle[MAX_PATH] = ""; OPENFILENAME ofn; int nLen, nLenE, i; DWORD dwWritten; hMem = (HLOCAL)SendMessage(hEdit, EM_GETHANDLE, 0, 0); lpszBuf = (char *)LocalLock(hMem); LocalUnlock(hMem); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hWnd; ofn.lpstrFile = szFile; ofn.nMaxFile = MAX_PATH; ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = MAX_PATH; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY; ofn.lpstrDefExt = "txt"; ofn.lpstrFilter = "text(*.txt)\0*.txt\0All files(*.*)\0*.*\0\0"; if (GetSaveFileName(&ofn) == 0) return FALSE; nLen = (int)strlen(lpszBuf); hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, (nLen + 256) * 3, 0); lpszOut = (char *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, (nLen + 256) * 3); SetNkfOption("-Se"); NkfConvert(lpszOut, lpszBuf); hFile = CreateFile(szFile, GENERIC_WRITE, 0, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "CreateFile関数失敗", "Error", MB_OK); HeapDestroy(hHeap); return FALSE; } nLenE = (int)strlen(lpszOut); for (i = 0; i < nLenE; i++) { if (lpszOut[i] == 0x0d) { continue; } else { WriteFile(hFile, &lpszOut[i], 1, &dwWritten, NULL); } if (dwWritten == 0) { MessageBox(hWnd, "書き込みエラーです", "Error", MB_OK); } } SetEndOfFile(hFile); CloseHandle(hFile); HeapDestroy(hHeap); return TRUE; }エディットコントロールの文字列を取得して、これをECUに変換して、ファイルに保存する 関数です。
エディットコントロールから文字列を取得するにはGetWindowText関数でもよいのですが、 ここでは、EM_GETHANDLEメッセージを送って、エディットコントロールのメモリハンドルを取得しています。
SendMessage( (HWND) hWnd, // ウィンドウハンドル EM_GETHANDLE, (WPARAM) wParam, // 使用しない(0にしておく) (LPARAM) lParam // 使用しない(0にしておく) );成功すれば、複数行エディットコントロールが内容を保持しているメモリのハンドルが 返されます。失敗した時や、単一行エディットコントロールに送った場合は0が返されます。
メモリオブジェクトのハンドルが返されたら、LocalLock関数で、ポインタを取得できます。
LPVOID LocalLock( HLOCAL hMem // ローカルメモリオブジェクトのハンドル );関数が失敗した時はNULLが返されます。
LocalLockしたものはLocalUnlockしなくてはいけません。
BOOL LocalUnlock( HLOCAL hMem // ローカルメモリオブジェクトのハンドル );さて、次にGetSaveFileName関数で、保存するファイル名をユーザーに 入力させます。
次に、EUC変換のための前準備をしておきます。 まずは、strlen関数でエディットコントロールに入力されている、バイト数を求めておきます。SJISからEUCに変換した場合、サイズはほとんど変わりません。改行があると、そこで 1バイト少なくなります。
ここでは、(nLen + 1) バイト程度のメモリを出力用に確保しておけば充分でしょう。 今回は、HeapAlloc関数で動的にメモリを確保してみます。この関数を使う前に、HeapCreate関数で、使用可能なヒープオブジェクトを作成しておくことが必要です。 ヒープオブジェクトを作成した後に、HeapAlloc関数でメモリを割り当てます。
メモリを解放するにはHeapFree関数を使います。また、HeapDestroy関数で、ヒープオブジェクトを破棄することもできます。この場合、HeapFree関数を使う必要はありません。
HANDLE HeapCreate( DWORD flOptions, // ヒープ割り当て方法 SIZE_T dwInitialSize, // 初期のヒープサイズ SIZE_T dwMaximumSize // 最大ヒープサイズ );flOptionsには、作成したいヒープオブジェクトの属性を指定します。
HEAP_GENERATE_EXCEPTIONSは、関数が失敗した時に例外を発生させます。
HEAP_NO_SERIALIZEは、メモリの割り当てや解放を行う時、相互排他を行いません。
(普通は、指定しません)
dwInitialSizeには、ヒープの初期サイズを指定します。
dwMaximumSizeには、ヒープの最大サイズを指定します。0を指定すると拡張可能となります。
関数が成功するとヒープハンドルが返されます。
LPVOID HeapAlloc( HANDLE hHeap, // ヒープブロックのハンドル DWORD dwFlags, // ヒープの割り当て方法 SIZE_T dwBytes // 割り当てたいバイト数 );hHeapには、ヒープハンドルを指定します。
dwFlagsには、割り当て方法のオプションを指定します。
HEAP_GENERATE_EXCEPTIONSは、関数が失敗した時に例外を発生させます。
HEAP_NO_SERIALIZEは、この関数がヒープにアクセスしている時相互排他を行いません。
HEAP_ZERO_MEMORYは、割り当てたメモリを0で初期化します。
dwBytesには、確保したいバイト数を指定します。HeapCreate関数で指定したヒープが 拡張不能の時は、0x7FFF8バイト未満の数値を指定しなくはなりません。
関数が成功すると、メモリブロックへのポインタが返されます。
メモリの準備ができたら、書き込まれた内容をEUCに変換します。
これは、至って簡単です。
SetNkfOption関数で変換オプションを指定します。
int CALLBACK SetNkfOption(LPCSTR optStr);optStrに変換オプション文字列を指定します。詳しくは添付のドキュメントを読んでください。簡単に覚えるには、入力側は大文字、出力側は小文字を指定します。
たとえば、入力側がS-JISの場合は、"-S"、出力側がEUCの場合は"-e"となります。 これをまとめて"-Se"とすることもできます。(「小さくなって出ていく」と覚えると忘れません。)また、入力側を指定しないと自動的に 判断してくれますが、100%正しいとは言い切れないので、入力側も指定するのが 安全だと思います。
さて、オプションを設定したら、実際に変換をします。
void CALLBACK NkfConvert(LPSTR outStr, LPCSTR inStr);outStrには、出力を受けるバッファを指定します。
ここでは、ファイルに1バイトずつ書き込み、"\r"が出てきたら、書き込まない方式を とっています。
nkfの使い方は、非常に簡単です。これを自力でやるのはかなり、面倒くさいので 非常に重宝します。この他に、BASE64や、半角全角の変換などもできます。研究して みてください。
Update 05/Oct/2004 By Y.Kumei