第227章 ディレクトリにアイコンを付ける


今回は、ローカル・FTPのリストビューでディレクトリは 一目でわかるようにディレクトリのアイコンを付けるようにします。 また、ローカルのファイルの属性やMSDOS名も表示するようにします。



左の図のようにディレクトリにはアイコンを付けてわかりやすくします。 属性については後で解説します。MSDOS名も表示するようにします。



では、プログラムを見てみましょう。

// myftp.h #define ID_LIST 100 #define ID_STATIC 101 #define ID_LISTL 102 #define ID_STATICL 103 #define ID_STATUSBAR 104 #define UP 1 //ソート;昇順 #define DOWN 2 //降順 #define NO_OF_SUBITEM 5 //サブアイテムの数 #define CONTEXT_GETFILE 100 #define CONTEXT_CONNECT 200 #define CONTEXT_PUTFILE 300 typedef struct _tagAccount{ char szUserName[64]; char szPassWord[64]; } ACCOUNT; typedef struct _tagFTPAddress{ char szHost[64]; char szBaseDir[64]; } FTPADDRESS; typedef struct _tagINETHANDLE{ HINTERNET hInternet; HINTERNET hHost; } INETHANDLE; typedef struct _tagFNAME{ char szFName[MAX_PATH]; char szLocalFileName[MAX_PATH]; } FNAME; typedef struct _tagHWNDSET{ HWND hwnd1; HWND hwnd2; HIMAGELIST hImage; } HWNDSET; typedef struct _tagSORTDATA{ HWND hwndList; //リストビューのhwnd int isortSubItem; //ソートするサブアイテムインデックス int iUPDOWN; //昇順か降順か } SORTDATA; typedef struct _tagSTATUSCALLBACK { HWND hwndStatus; DWORD dwFrom; } STATUSCALLBACK; typedef struct _tagWINPOS { int x; int y; int wx; int wy; } WINPOS; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyAccountProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyFtpAddressProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyGetFNameProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDriveProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyCreateDirProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyTextProc(HWND, UINT, WPARAM, LPARAM); int CALLBACK MyCompProc(LPARAM, LPARAM, LPARAM); void CALLBACK MyInetCallback(HINTERNET, DWORD, DWORD, LPVOID, DWORD); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MyConnect(HWND, INETHANDLE *, ACCOUNT, FTPADDRESS, HWND, HWND, HWND); void MyDown(HWND, INETHANDLE, FTPADDRESS, FNAME *, HWND, HWND, HWND, HWND, HIMAGELIST); void SetAccount(HWND, ACCOUNT *); void SetHost(HWND, FTPADDRESS *); void GetAllFiles(HWND, HWND, HWND, INETHANDLE *); void SetMyLocalDir(HWND, HWND, HWND, HIMAGELIST); void ChangeLocalDrive(HWND, HWND, HWND, HIMAGELIST); void MyUp(HWND, INETHANDLE, HWND, HWND, HWND, HWND, HWND); void MyFtpDelFile(HWND, INETHANDLE, HWND, HWND); void MyFtpCreatDir(HWND, INETHANDLE, HWND, HWND); void MyFtpRemoveDir(HWND, INETHANDLE, HWND, HWND); HWND MakeMyList(HWND); void InsertMyColumn(HWND, BOOL); void MyFtpRead(HWND, HWND, INETHANDLE, HWND); void MyGetInitialData(ACCOUNT *, FTPADDRESS *, WINPOS *); void MySetInitialData(ACCOUNT *, FTPADDRESS *, WINPOS *);

関数のプロトタイプ宣言などが収められているヘッダファイルです。 リストビューにアイコンを表示するためHIMAGELISTを渡すように いくつかの関数の引数が変更になっています。 この作業は思ったほど面倒ではありません。

// myftp11.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END END POPUP "ftp(&T)" BEGIN MENUITEM "接続(&C)", IDM_CONNECT MENUITEM SEPARATOR MENUITEM "ファイルのダウンロード(&D)...", IDM_DOWNLOAD MENUITEM "ファイルのアップロード(&U)...", IDM_UPLOAD MENUITEM SEPARATOR MENUITEM "ディレクトリの作成...", IDM_CREATEDIR MENUITEM "ディレクトリの削除...", IDM_REMOVEDIR MENUITEM "ファイルの削除...", IDM_DELFILE MENUITEM SEPARATOR MENUITEM "ひとつ上のディレクトリへ", IDM_UPDIRFTP MENUITEM SEPARATOR MENUITEM "テキストファイルの読み出し(&R)...", IDM_READ MENUITEM SEPARATOR MENUITEM "切断(&X)...", IDM_DISCONNECT END POPUP "ローカル(&L)" BEGIN MENUITEM "ドライブの変更(&D)...", IDM_LOCALDRIVE MENUITEM SEPARATOR MENUITEM "ひとつ上のディレクトリへ", IDM_UPDIRLOCAL END POPUP "設定(&S)" BEGIN MENUITEM "アカウント設定(&A)...", IDM_ACCOUNT MENUITEM "ホスト設定(&H)...", IDM_GETHOST END END POPUPMENULOCAL MENU DISCARDABLE BEGIN POPUP "ダミーです" BEGIN MENUITEM "アップロード(&U)...", IDM_UPLOAD MENUITEM SEPARATOR MENUITEM "ドライブの変更(&D)...", IDM_LOCALDRIVE MENUITEM SEPARATOR MENUITEM "ひとつ上のディレクトリへ", IDM_UPDIRLOCAL END END POPUPMENUFTP MENU DISCARDABLE BEGIN POPUP "ダミーです" BEGIN MENUITEM "ディレクトリの作成...", IDM_CREATEDIR MENUITEM "ディレクトリの削除...", IDM_REMOVEDIR MENUITEM "ファイルの削除...", IDM_DELFILE MENUITEM SEPARATOR MENUITEM "ダウンロード(&D)...", IDM_DOWNLOAD MENUITEM SEPARATOR MENUITEM "ひとつ上のディレクトリへ", IDM_UPDIRFTP MENUITEM SEPARATOR MENUITEM "テキストファイルの読み出し(&R)...", IDM_READ END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYACCOUNT DIALOG DISCARDABLE 0, 0, 143, 67 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "アカウント" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,46,50,14 PUSHBUTTON "キャンセル",IDCANCEL,86,46,50,14 LTEXT "ID",IDC_STATIC,7,7,8,8 LTEXT "パスワード",IDC_STATIC,7,25,32,8 EDITTEXT IDC_ID,43,7,94,13,ES_AUTOHSCROLL EDITTEXT IDC_PASSWORD,43,25,94,13,ES_PASSWORD | ES_AUTOHSCROLL END MYFTPADDRESS DIALOG DISCARDABLE 0, 0, 159, 63 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ホスト" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,42,50,14 PUSHBUTTON "キャンセル",IDCANCEL,102,42,50,14 LTEXT "FTPアドレス",IDC_STATIC,7,7,36,8 LTEXT "基準となるディレクトリ",IDC_STATIC,7,25,64,8 EDITTEXT IDC_FTPADDRESS,75,7,78,13,ES_AUTOHSCROLL EDITTEXT IDC_BASEDIR,75,25,78,13,ES_AUTOHSCROLL END MYGETFNAME DIALOG DISCARDABLE 0, 0, 187, 67 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ファイル名入力" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,46,50,14 PUSHBUTTON "キャンセル",IDCANCEL,130,46,50,14 LTEXT "ダウンロードするファイル",IDC_STATIC,7,7,72,8 EDITTEXT IDC_FNAME,88,7,92,12,ES_AUTOHSCROLL LTEXT "ダウンロード先ファイル名",IDC_STATIC,7,31,74,8 EDITTEXT IDC_LOCALFILE,88,27,92,12,ES_AUTOHSCROLL END MYDRIVE DIALOG DISCARDABLE 0, 0, 43, 115 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "ドライブ" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,9,94,24,14 LISTBOX IDC_LIST1,7,7,29,83,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP END MYCREATEDIR DIALOG DISCARDABLE 0, 0, 135, 47 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "FTPディレクトリの新規作成" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,7,73,13,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,7,26,50,14 PUSHBUTTON "キャンセル",IDCANCEL,78,26,50,14 LTEXT "を作成します",IDC_STATIC,81,12,39,8 END MYTEXTDLG DIALOG DISCARDABLE 0, 0, 170, 167 STYLE DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "テキスト表示" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,7,155,133,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_OEMCONVERT | ES_READONLY | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL | NOT WS_TABSTOP PUSHBUTTON "閉じる",IDC_BUTTON1,53,144,63,16 END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. MYICON ICON DISCARDABLE "myicon.ico" MYFILE ICON DISCARDABLE "myfile.ico"

今までとほとんど同じですが、アイコンが増えました。 "MYICON"はフォルダのアイコン、"MYFILE"はダミーのアイコンです。 (普通のファイルには何もアイコンが付いていないように見えるが 空白の見えないアイコンを付けてあります)また、これらのアイコンは 16*16の小さいアイコンですが、どうせだからこのアプリケーション自体の 32*32のアイコンも作っておきます。("MYICON"に複数のアイコンを作れます。)

// myftp11.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <wininet.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #include "myftp.h" const char szWindowTitle[] = "猫でもわかるFTP"; char szClassName[] = "myftp11"; //ウィンドウクラス BOOL bConnect = FALSE;//インターネットに接続しているかどうか

ウィンドウクラス名が変わっただけです。

WinMain関数に変更はありません。

//ウィンドウ・クラスの登録 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 = LoadIcon(hInst, "MYFILE"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(hInst, "MYFILE"); return (RegisterClassEx(&wc)); }

アイコンの登録部分が変更になっています。

InitInstance関数に変更はありません。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, iIndex; static ACCOUNT myaccount; static FNAME myfname; static FTPADDRESS myftpaddress; static INETHANDLE inet; static HWND hList, hStatic, hListL, hStaticL, hStatus; static HINSTANCE hInst; RECT rc; HDWP hDwp; char szDir[MAX_PATH], szBuf[MAX_PATH]; INITCOMMONCONTROLSEX icc; LPNMHDR lpnmhdr; static int sortsubno[NO_OF_SUBITEM] = {UP}; NMLISTVIEW *pNMLV; SORTDATA SortData; static int isortsubno;//どのサブアイテムでソートされているか int sb_size[] = {100, 200, -1}; static int nStatus_h;//ステータスバーの高さ POINT pt; HMENU hMenu, hSubMenu; WINPOS winpos = {20, 20, 600, 450};//親ウィンドウの位置、大きさ static HIMAGELIST hImage; switch (msg) { case WM_CREATE: MyGetInitialData(&myaccount, &myftpaddress, &winpos); MoveWindow(hWnd, winpos.x, winpos.y, winpos.wx, winpos.wy, TRUE); icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_LISTVIEW_CLASSES | ICC_BAR_CLASSES; InitCommonControlsEx(&icc); hInst = ((LPCREATESTRUCT)lp)->hInstance; GetClientRect(hWnd, &rc); hList = MakeMyList(hWnd); InsertMyColumn(hList, TRUE); hListL = MakeMyList(hWnd); InsertMyColumn(hListL, FALSE); hStatic = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 0, 0, 0, hWnd, (HMENU)ID_STATIC, hInst, NULL); hStaticL = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 0, 0, 0, 0, hWnd, (HMENU)ID_STATICL, hInst, NULL); SetMyLocalDir(hWnd, hListL, hStaticL, hImage); SortData.hwndList = hListL; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hListL, MyCompProc, &SortData); hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE, "", hWnd, ID_STATUSBAR); SendMessage(hStatus, SB_SETPARTS, (WPARAM)3, (LPARAM)sb_size); GetWindowRect(hStatus, &rc); nStatus_h = rc.bottom - rc.top; hImage = ImageList_Create(16, 16, ILC_COLOR4, 2, 0); ListView_SetImageList(hListL, hImage, LVSIL_SMALL); ListView_SetImageList(hList, hImage, LVSIL_SMALL); ImageList_AddIcon(hImage, LoadIcon(hInst, "MYICON")); ImageList_AddIcon(hImage, LoadIcon(hInst, "MYFILE")); break; case WM_SIZE: hDwp = BeginDeferWindowPos(4); hDwp = DeferWindowPos(hDwp, hStaticL, HWND_TOP, 10, 5, LOWORD(lp) / 2 - 15, 30, SWP_SHOWWINDOW); hDwp = DeferWindowPos(hDwp, hStatic, HWND_TOP, LOWORD(lp) / 2 + 5, 5, LOWORD(lp) / 2 - 15, 30, SWP_SHOWWINDOW); hDwp = DeferWindowPos(hDwp, hListL, HWND_TOP, 10, 40, LOWORD(lp) / 2 - 15, HIWORD(lp) - 50 - nStatus_h, SWP_SHOWWINDOW); hDwp = DeferWindowPos(hDwp, hList, HWND_TOP, LOWORD(lp) / 2 + 5, 40, LOWORD(lp) / 2 - 15, HIWORD(lp) - 50 - nStatus_h, SWP_SHOWWINDOW); EndDeferWindowPos(hDwp); SendMessage(hStatus, WM_SIZE, wp, lp); break; case WM_CONTEXTMENU: pt.x = LOWORD(lp); pt.y = HIWORD(lp); if (wp == (WPARAM)hListL) { hMenu = LoadMenu(hInst, "POPUPMENULOCAL"); hSubMenu = GetSubMenu(hMenu, 0); TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hSubMenu); DestroyMenu(hMenu); } if (wp == (WPARAM)hList) { hMenu = LoadMenu(hInst, "POPUPMENUFTP"); hSubMenu = GetSubMenu(hMenu, 0); TrackPopupMenu(hSubMenu, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hSubMenu); DestroyMenu(hMenu); } if (wp != (WPARAM)hListL && wp != (WPARAM)hList) { MessageBox(hWnd, "範囲外です", "OK", MB_OK); } break; case WM_NOTIFY: lpnmhdr = (LPNMHDR)lp; if (lpnmhdr->hwndFrom == hListL) { switch (lpnmhdr->code) { case NM_DBLCLK: iIndex = ListView_GetNextItem(hListL, -1, LVNI_SELECTED); ListView_GetItemText(hListL, iIndex, 0, szDir, sizeof(szDir)); ListView_GetItemText(hListL, iIndex, 1, szBuf, sizeof(szBuf)); if (strcmp(szBuf, "[dir]") != 0) { MessageBox(hWnd, "ディレクトリではありません", "注意", MB_OK); break; } SetCurrentDirectory(szDir); SetMyLocalDir(hWnd, hListL, hStaticL, hImage); SortData.hwndList = hListL; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hListL, MyCompProc, &SortData); break; case LVN_COLUMNCLICK: pNMLV = (NMLISTVIEW *)lp; isortsubno = pNMLV->iSubItem; if (sortsubno[isortsubno] == UP) sortsubno[isortsubno] = DOWN; else sortsubno[isortsubno] = UP; SortData.hwndList = hListL; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; if(ListView_SortItems(hListL, MyCompProc, &SortData) != TRUE) MessageBox(hWnd, "ソート失敗", "Error", MB_OK); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } } if (lpnmhdr->hwndFrom == hList) { switch (lpnmhdr->code) { case NM_DBLCLK: iIndex = ListView_GetNextItem(hList, -1, LVNI_SELECTED); ListView_GetItemText(hList, iIndex, 0, szDir, sizeof(szDir)); ListView_GetItemText(hList, iIndex, 1, szBuf, sizeof(szBuf)); if (strcmp(szBuf, "[dir]") != 0) { MessageBox(hWnd, "ディレクトリではありません", "注意", MB_OK); break; } FtpSetCurrentDirectory(inet.hHost, szDir); GetAllFiles(hWnd, hList, hStatic, &inet); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); break; case LVN_COLUMNCLICK: pNMLV = (NMLISTVIEW *)lp; isortsubno = pNMLV->iSubItem; if (sortsubno[isortsubno] == UP) sortsubno[isortsubno] = DOWN; else sortsubno[isortsubno] = UP; SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; if(ListView_SortItems(hList, MyCompProc, &SortData) != TRUE) MessageBox(hWnd, "ソート失敗", "Error", MB_OK); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_LOCALDRIVE: ChangeLocalDrive(hWnd, hListL, hStaticL, hImage); SortData.hwndList = hListL; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hListL, MyCompProc, &SortData); break; case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_CONNECT: bConnect = MyConnect(hWnd, &inet, myaccount, myftpaddress, hList, hStatic, hStatus); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); SendMessage(hStatus, SB_SETTEXT, (WPARAM)2 | 0, (LPARAM)""); break; case IDM_READ: MyFtpRead(hWnd, hList, inet, hStatus); break; case IDM_DOWNLOAD: MyDown(hWnd, inet, myftpaddress, &myfname, hList, hListL, hStaticL, hStatus, hImage); SortData.hwndList = hListL; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hListL, MyCompProc, &SortData); SendMessage(hStatus, SB_SETTEXT, (WPARAM)2 | 0, (LPARAM)""); break; case IDM_UPLOAD: MyUp(hWnd, inet, hList, hStatic, hListL, hStaticL, hStatus); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); SendMessage(hStatus, SB_SETTEXT, (WPARAM)2 | 0, (LPARAM)""); break; case IDM_DELFILE: MyFtpDelFile(hWnd, inet, hList, hStatic); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); break; case IDM_CREATEDIR: MyFtpCreatDir(hWnd, inet, hList, hStatic); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); break; case IDM_REMOVEDIR: MyFtpRemoveDir(hWnd, inet, hList, hStatic); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); break; case IDM_UPDIRFTP: if (!bConnect) { MessageBox(hWnd, "インターネットに接続されていません", "失敗", MB_OK); break; } FtpSetCurrentDirectory(inet.hHost, ".."); GetAllFiles(hWnd, hList, hStatic, &inet); SortData.hwndList = hList; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hList, MyCompProc, &SortData); break; case IDM_UPDIRLOCAL: SetCurrentDirectory(".."); SetMyLocalDir(hWnd, hListL, hStaticL, hImage); SortData.hwndList = hListL; SortData.isortSubItem = isortsubno; SortData.iUPDOWN = sortsubno[isortsubno]; ListView_SortItems(hListL, MyCompProc, &SortData); break; case IDM_ACCOUNT: SetAccount(hWnd, &myaccount); break; case IDM_GETHOST: SetHost(hWnd, &myftpaddress); break; case IDM_DISCONNECT: if (!bConnect) { MessageBox(hWnd, "インターネットに接続されていません", "失敗", MB_OK); break; } if (InternetCloseHandle(inet.hHost)) MessageBox(hWnd, "FTPをクローズしました", "OK", MB_OK); if (InternetCloseHandle(inet.hInternet)) MessageBox(hWnd, "インターネットから切り離しました", "OK", MB_OK); bConnect = FALSE; SetWindowText(hWnd, szWindowTitle); break; } break; case WM_CLOSE: if (bConnect) { MessageBox(hWnd, "インターネットの接続を切ってから終了してください", "注意", MB_OK); break; } id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { GetWindowRect(hWnd, &rc); winpos.x = rc.left; winpos.y = rc.top; winpos.wx = rc.right - rc.left; winpos.wy = rc.bottom -rc.top; MySetInitialData(&myaccount, &myftpaddress, &winpos); ImageList_Destroy(hImage); DestroyWindow(hList); DestroyWindow(hStatic); DestroyWindow(hListL); DestroyWindow(hStaticL); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

今までは、ディレクトリはファイル名の前に<dir>をつけていましたが 今回は「サイズ」の列に[dir]と表示するので、ファイル名から<dir>の文字列を 取り除く作業が不要になりました。したがって変数iは廃止しました。

また、HIMAGELIST変数が増えました。

今までは列見出しはローカルもFTPも同じだったのでInsertMyColumn関数で 同じに作っていました。今回は引数を増やして、2番目の引数がTRUEならFTPの 列を挿入し、FALSEならローカルの列を挿入するようにしました。

また、アイコンを入れる関係上SetMyLocalDir関数にHIMAGELISTの引数を増やしました。 そして型どおりImageList_Create, ListView_SetImageList, ImageList_AddIconで リストビューにアイコンをセットします。

リストビューがダブルクリックされたとき(WM_NOTIFY, NM_DBLCLK)それが ディレクトリかどうかは今まではファイル名の先頭に<dir>があるかどうかで 判断していましたが、今回は「サイズ」項目が[dir]になっているかどうかで判断します。 そして、ディレクトリであればSetCurrentDirectory(ファイル名);または FtpSetCurrentDirectory(ファイル名);でカレントディレクトリを変えます。 ファイル名から<dir>を取り除く作業は不要になりました。

メニューからIDM_LOCALDRIVEが選択された時、ChangeLoacalDrive関数を 呼び出しますが、これにもHIMAGELIST引数を増やしました。この関数の先で SetMyLocalDir関数が呼ばれますが、これがHIMAGELISTを必要とするからです。 メニューからIDM_DOWNLOADが選択されたときMyDown関数を呼びますが これも、HIMAGELIST引数が増えています。

メニューからIDM_UPDIRLOCALが選択されたときにSetMyLocalDir関数が呼ばれますが これもHIMAGELIST引数が増えています。

プログラム終了時にImageList_Destroyでイメージリストハンドルを破棄します。

MyConnect関数に変更はありません。

void MyDown(HWND hWnd, INETHANDLE inet, FTPADDRESS ftpadr, FNAME *lpfname, HWND hList, HWND hListL, HWND hStaticL, HWND hStatus, HIMAGELIST hImage) { HINSTANCE hInst; int iIndex; static STATUSCALLBACK sc; if (!bConnect) { MessageBox(hWnd, "インターネットに接続されていません", "失敗", MB_OK); return; } sc.hwndStatus = hStatus; sc.dwFrom = CONTEXT_GETFILE; iIndex = ListView_GetNextItem(hList, -1, LVNI_SELECTED); ListView_GetItemText(hList, iIndex, 0, lpfname->szFName, MAX_PATH); GetWindowText(hStaticL, lpfname->szLocalFileName, sizeof(lpfname->szLocalFileName)); strcat(lpfname->szLocalFileName, "\\"); strcat(lpfname->szLocalFileName, lpfname->szFName); hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); if (DialogBoxParam(hInst, "MYGETFNAME", hWnd, (DLGPROC)MyGetFNameProc, (LPARAM)lpfname) == IDCANCEL) return; if (!FtpGetFile(inet.hHost, //FTPのインターネットハンドル lpfname->szFName, //ダウンロードするファイル lpfname->szLocalFileName, //ダウンロード先のパス付ファイル名 TRUE, //ダウンロード先に同名のファイルがあるときエラーにする(上書き防止) FILE_ATTRIBUTE_NORMAL, //ダウンロード先に作られるファイルのアトリビュート FTP_TRANSFER_TYPE_BINARY, //バイナリファイルとしてダウンロード (DWORD)&sc)) { MessageBox(hWnd, "ダウンロードに失敗しました。", "失敗", MB_OK); return; } SetMyLocalDir(hWnd, hListL, hStaticL, hImage); MessageBox(hWnd, "無事ダウンロードできました", "OK", MB_OK); return; }

最後の引数が増えています。これは、SetMyLocalDir関数にHIMAGELISTを渡す必要が あるからです。

MyGetFNameProc, SetAccount, MyAccountProc, SetHost, MyFtpAddressProcの各関数に変更はありません。

void GetAllFiles(HWND hWnd, HWND hList, HWND hStatic, INETHANDLE *lpinet) { WIN32_FIND_DATA FindFileData; HINTERNET hFindHandle; char fname[MAX_PATH + 5]; char szStr[MAX_PATH]; DWORD dwCD = MAX_PATH; LVITEM li; int iItemNo = 0; SYSTEMTIME st; if (FtpGetCurrentDirectory(lpinet->hHost, szStr, &dwCD) == TRUE) SetWindowText(hStatic, szStr); else { MessageBox(hWnd, "現在のディレクトリを取得できません", "失敗", MB_OK); return; } ListView_DeleteAllItems(hList); hFindHandle = FtpFindFirstFile(lpinet->hHost, "*.*", &FindFileData, INTERNET_FLAG_RELOAD, 0); if (hFindHandle == NULL) { if (GetLastError() == ERROR_NO_MORE_FILES) { MessageBox(hWnd, "ファイルはありません", "Error", MB_OK); return; } else { MessageBox(hWnd, "深刻なエラーが発生しました", "Error", MB_OK); InternetCloseHandle(lpinet->hHost); InternetCloseHandle(lpinet->hInternet); bConnect = FALSE; SetWindowText(hWnd, szWindowTitle); return; } } else { strcpy(fname, FindFileData.cFileName); li.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; li.pszText = fname; li.iItem = iItemNo; li.iSubItem = 0; li.lParam = iItemNo; if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { li.iImage = 0; } else { li.iImage = 1; } ListView_InsertItem(hList, &li); li.mask = LVIF_TEXT; li.iSubItem = 1; if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { itoa(FindFileData.nFileSizeLow, li.pszText, 10); if (FindFileData.nFileSizeHigh != 0) MessageBox(NULL, "ファイルサイズが大きすぎます", "Error", MB_OK); } else { strcpy(li.pszText, "[dir]"); ListView_SetItem(hList, &li); } ListView_SetItem(hList, &li); FileTimeToSystemTime(&FindFileData.ftLastWriteTime, &st); wsprintf(li.pszText, "%02d/%02d/%02d %02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute); li.iSubItem = 2; ListView_SetItem(hList, &li); iItemNo++; } while (InternetFindNextFile(hFindHandle, &FindFileData)) { strcpy(fname, FindFileData.cFileName); li.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; li.pszText = fname; li.iItem = iItemNo; li.iSubItem = 0; li.lParam = iItemNo; if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { li.iImage = 0; } else { li.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; li.iImage = 1; } ListView_InsertItem(hList, &li); li.mask = LVIF_TEXT; li.iSubItem = 1; if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { itoa(FindFileData.nFileSizeLow, li.pszText, 10); } else { strcpy(li.pszText, "[dir]"); } ListView_SetItem(hList, &li); FileTimeToSystemTime(&FindFileData.ftLastWriteTime, &st); wsprintf(li.pszText, "%02d/%02d/%02d %02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute); li.iSubItem = 2; ListView_SetItem(hList, &li); iItemNo++; } InternetCloseHandle(hFindHandle); return; }

FtpFindFirstFile関数やら、InternetFindNextFile関数でカレントディレクトリの ファイルを調べます。見つかったファイルはWIN32_FIND_DATA構造体の cFileNameメンバを調べてリストビューの項目とします。この時、これが ディレクトリであればディレクトリのアイコンを付け、そうでないときは ダミーのアイコンを付けます。また、ディレクトリであるときは「サイズ」 のところに[dir]を表示します。

void SetMyLocalDir(HWND hWnd, HWND hListL, HWND hStaticL, HIMAGELIST hImage) { char szfname[MAX_PATH + 5]; WIN32_FIND_DATA wfd; HANDLE hFind; int iItemNo = 0; LVITEM li; SYSTEMTIME localtm; FILETIME localft; char szBuf[MAX_PATH]; ListView_DeleteAllItems(hListL); GetCurrentDirectory(MAX_PATH, szfname); SetWindowText(hStaticL, szfname); hFind = FindFirstFile("*.*", &wfd); if (hFind == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "エラーです", "Error", MB_OK); FindClose(hFind); return; } else { strcpy(szfname, wfd.cFileName); li.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; li.pszText = szfname; li.iItem = iItemNo; li.iSubItem = 0; li.lParam = iItemNo; if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { li.iImage = 0; } else { li.iImage = 1; } ListView_InsertItem(hListL, &li); li.mask = LVIF_TEXT; li.iSubItem = 1; if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { itoa(wfd.nFileSizeLow, li.pszText, 10); if (wfd.nFileSizeHigh != 0) MessageBox(hWnd, "ファイルサイズが大きすぎます", "Error", MB_OK); } else { strcpy(li.pszText, "[dir]"); } ListView_SetItem(hListL, &li); FileTimeToLocalFileTime(&wfd.ftLastWriteTime, &localft); FileTimeToSystemTime(&localft, &localtm); wsprintf(li.pszText, "%02d/%02d/%02d %02d:%02d", localtm.wYear, localtm.wMonth, localtm.wDay, localtm.wHour, localtm.wMinute); li.iSubItem = 2; ListView_SetItem(hListL, &li); wsprintf(li.pszText, "%d", wfd.dwFileAttributes); li.iSubItem = 3; ListView_SetItem(hListL, &li); GetShortPathName(wfd.cFileName, szBuf, sizeof(szBuf)); strcpy(li.pszText, szBuf); li.iSubItem = 4; ListView_SetItem(hListL, &li); iItemNo++; } while (FindNextFile(hFind, &wfd)) { strcpy(szfname, wfd.cFileName); li.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; li.pszText = szfname; li.iItem = iItemNo; li.iSubItem = 0; li.lParam = iItemNo; if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { li.iImage = 0; } else { li.iImage = 1; } ListView_InsertItem(hListL, &li); li.mask = LVIF_TEXT; li.iSubItem = 1; if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { itoa(wfd.nFileSizeLow, li.pszText, 10); } else { strcpy(li.pszText, "[dir]"); } ListView_SetItem(hListL, &li); FileTimeToLocalFileTime(&wfd.ftLastWriteTime, &localft); FileTimeToSystemTime(&localft, &localtm); wsprintf(li.pszText, "%02d/%02d/%02d %02d:%02d", localtm.wYear, localtm.wMonth, localtm.wDay, localtm.wHour, localtm.wMinute); li.iSubItem = 2; ListView_SetItem(hListL, &li); wsprintf(li.pszText, "%d", wfd.dwFileAttributes); li.iSubItem = 3; ListView_SetItem(hListL, &li); GetShortPathName(wfd.cFileName, szBuf, sizeof(szBuf)); strcpy(li.pszText, szBuf); li.iSubItem = 4; ListView_SetItem(hListL, &li); iItemNo++; } FindClose(hFind); return; }

ローカルのディレクトリ表示もほぼ同じ要領で行います。 また、今回新たに属性とMSDOS名の項目が増えました。

ここで属性について少し解説すると次のように定義されています。 (2進はスペースの関係上下位13ビットしか表示していません。 これより上位のビットはすべて0で埋められています。)

FILE_ATTRIBUTE_*16進10進2進
READONLY0x0000000110000000000001
HIDDEN0x0000000220000000000010
SYSTEM0x0000000440000000000100
DIRECTORY0x00000010160000000010000
ARCHIVE0x00000020320000000100000
ENCRYPTED0x00000040640000001000000
NORMAL0x000000801280000010000000
TEMPORARY0x000001002560000100000000
REPARSE_POINT0x0000040010240010000000000
COMPRESSED0x0000080020480100000000000
OFFLINE0x0000100040961000000000000

さて、属性が(10進の)3であれば、足して3になるのは 1+2のみです。したがってREADONLYとHIDDENの属性を有している 事がわかります。3を2進であらわすと下位2ビットは11となります。 最下位とその次にビットがたっているのでやはりこれからも READONLYとHIDDENの属性を有していることがわかります。

WIN386.SWPというファイルは属性が(10進で)34です。 これを2進に直すと100010となります。表からビットのたっている 属性を調べるとARCHIVEとHIDDENの属性を有していることがわかります。 10進で考えると加えて34となるのは32+2の組み合わせしかありません。 これからもARCHIVEとHIDDENであることがわかります。

Windowsのプログラミングではこのようなことは日常茶飯事ですね。 C言語編第49章でも少しだけ触れています。

DWORD GetShortPathName( LPCTSTR lpszLongPath, LPTSTR lpszShortPath, DWORD cchBuffer );

指定された長いパス名から短いパス名を取得します。

lpszLongPathには、長いパス名を指定します。

lpszShortPathには、取得するパス名のバッファを指定します。

cchBufferには、バッファサイズを指定します。

さて、MS-DOSのファイル名を取得するのにWIN32_FIND_DATA構造体の cAlternateFileNameメンバを使えばよいではないか、と思われる人もいると思います。 しかし、ファイルによってはこれが空白になってしまうこともあります。 (実験するとすぐにわかります)したがって、GetShortPathNameで取得するのが 確実です。

void ChangeLocalDrive(HWND hWnd, HWND hListL, HWND hStaticL, HIMAGELIST hImage) { HINSTANCE hInst; HWNDSET hwndset; hwndset.hwnd1 = hListL; hwndset.hwnd2 = hStaticL; hwndset.hImage = hImage; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); DialogBoxParam(hInst, "MYDRIVE", hWnd, (DLGPROC)MyDriveProc, (LPARAM)&hwndset); return; }

この関数の引数も最後のHIMAGELISTが増えています。 また、MyDriveProcにイメージリストハンドルを渡すために、HWNDSET構造体の 定義も少し変えています。(メンバにhImageが増えた)

LRESULT CALLBACK MyDriveProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hList; static HWND hListL, hStaticL, hParent; char szDrv[4]; HWNDSET *lphset; static HIMAGELIST hImage; switch (msg) { case WM_INITDIALOG: lphset = (HWNDSET *)lp; hListL = lphset->hwnd1; hStaticL = lphset->hwnd2; hImage = lphset->hImage; hParent = GetParent(hDlg); hList = GetDlgItem(hDlg, IDC_LIST1); DlgDirList(hDlg, "*.*", IDC_LIST1, 0, DDL_DRIVES); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: DlgDirSelectEx(hDlg, szDrv, sizeof(szDrv), IDC_LIST1); strcat(szDrv, "\\"); SetCurrentDirectory(szDrv); SetMyLocalDir(hParent, hListL, hStaticL, hImage); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

ダイアログボックスのプロシージャですがChangeLocalDrive関数より HWNDSET構造体のアドレスをもらっています。これから、ローカル・FTP・ のリストビューのハンドル、イメージリストハンドルを知ることができます。 これらはSetMyLocalDir関数を呼び出すのに必要です。

MyUp, MyFtpDelFile, MyFtpCreatDir, MyCreateDirProc, MyFtpRemoveDir, MakeMyListの各関数に変更はありません。

void InsertMyColumn(HWND hList, BOOL bFTP) { LVCOLUMN lc; lc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; lc.fmt = LVCFMT_LEFT; lc.cx = 100; lc.pszText = "ファイル名"; lc.iSubItem = 0; ListView_InsertColumn(hList, 0, &lc); lc.fmt = LVCFMT_RIGHT; lc.cx = 75; lc.pszText = "サイズ"; lc.iSubItem = 1; ListView_InsertColumn(hList, 1, &lc); lc.fmt = LVCFMT_LEFT; lc.cx = 110; lc.pszText = "更新日時"; lc.iSubItem = 2; ListView_InsertColumn(hList, 2, &lc); if (bFTP == TRUE) return; lc.fmt = LVCFMT_RIGHT; lc.cx = 50; lc.pszText = "属性"; lc.iSubItem = 3; ListView_InsertColumn(hList, 3, &lc); lc.fmt = LVCFMT_LEFT; lc.cx = 100; lc.pszText = "MS-DOS名"; lc.iSubItem = 4; ListView_InsertColumn(hList, 4, &lc); return; }

2番目の引数で、ローカルかFTPのリストビューの列を挿入するかを 判断しています。(ローカルとFTPで列が異なるため)

int CALLBACK MyCompProc(LPARAM lp1, LPARAM lp2, LPARAM lp3) { static LVFINDINFO lvf; static int nItem1, nItem2; static char buf1[MAX_PATH], buf2[MAX_PATH]; SORTDATA *lpsd; lpsd = (SORTDATA *)lp3; lvf.flags = LVFI_PARAM; lvf.lParam = lp1; nItem1 = ListView_FindItem(lpsd->hwndList, -1, &lvf); lvf.lParam = lp2; nItem2 = ListView_FindItem(lpsd->hwndList, -1, &lvf); ListView_GetItemText(lpsd->hwndList, nItem1, lpsd->isortSubItem, buf1, sizeof(buf1)); ListView_GetItemText(lpsd->hwndList, nItem2, lpsd->isortSubItem, buf2, sizeof(buf2)); if (lpsd->isortSubItem != 1 && lpsd->isortSubItem != 3) { if (lpsd->iUPDOWN == UP) { return(stricmp(buf1, buf2)); } else { return(stricmp(buf1, buf2) * -1); } } else { if (lpsd->iUPDOWN == UP) { if (atoi(buf1) > atoi(buf2)) return 1; else if (atoi(buf1) == atoi(buf2)) return 0; else return -1; } else { if (atoi(buf1) > atoi(buf2)) return -1; else if (atoi(buf1) == atoi(buf2)) return 0; else return 1; } } }

今までとほとんど同じですが、属性で並べ替えをすることも考慮して サブアイテムが1または3の時は数値で並べ替えをします。

MyInetCallback, MyFtpRead, MyTextProc, MyGetInitialData, MySetInitialData の各関数に変更はありません。

アイコン表示で少し見やすくなりましたが、まだまだ改良の余地があります。 複数のファイルを一度にアップロード、ダウンロードできるように改良してみてください。 あと少し手を加えると、ホーム・ページのファイルをアップするFTPも 作れそうですね。(ローカルとFTPのディレクトリのファイルを比較して、ローカルに 新規または日付の新しいファイルがあればアップロードする)
[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 12/Sep/1999 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。