第219章 ローカルのカレントディレクトリ表示


さて、今回は今まで作ってきたFTPをもう少し使いやすく 改良します。ローカルのディレクトリを表示してダウンロードする ディレクトリを選択します。また、FTPのディレクトリ表示より ダウンロードするファイルを選択します。 その後メニューの「ftp」「ファイルのダウンロード」を選択すると ダイアログボックスのエディットコントロールにはファイル名が 設定されて表示されるのであとは、「OK」ボタンを押すだけです。



左側にローカルのディレクトリ、右側にFTPサーバのディレクトリが 表示されます。左の図ではFTPのpublic_html/index.htmlがローカルの デスクトップにダウンロードするよう選択されています。 このあと、メニューから「ファイルのダウンロード」を選択するだけです。



さて、上のプログラムではローカルのディレクトリを移動するには ディレクトリ名をダブルクリックします。しかし、ドライブを 変更することはできません。変更するにはメニューの「ローカル」 「ドライブの変更」を選択します。

すると、左の図のようなダイアログボックスが出てきますので これで、ドライブを変更します。



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

// myftp03.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "ftp(&T)" BEGIN MENUITEM "接続(&C)", IDM_CONNECT MENUITEM "ファイルのダウンロード(&D)", IDM_DOWNLOAD MENUITEM "切断(&X)", IDM_DISCONNECT END POPUP "ローカル(&L)" BEGIN MENUITEM "ドライブの変更(&D)", IDM_LOCALDRIVE END POPUP "設定(&S)" BEGIN MENUITEM "アカウント設定(&A)", IDM_ACCOUNT MENUITEM "ホスト設定(&H)", IDM_GETHOST 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

メニューに「ローカル」「ドライブの変更」が追加されました。
"MYDRIVE"ダイアログボックスが追加されました。

// myftp03.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <wininet.h> #include <windowsx.h> #include "resource.h" #define ID_LIST 100 #define ID_STATIC 101 #define ID_LISTL 102 #define ID_STATICL 103 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; } HWNDSET; 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); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MyConnect(HWND, INETHANDLE *, ACCOUNT, FTPADDRESS, HWND, HWND); void MyDown(HWND, INETHANDLE, FTPADDRESS, FNAME *, HWND, HWND); void SetAccount(HWND, ACCOUNT *); void SetHost(HWND, FTPADDRESS *); void GetAllFiles(HWND, HWND, HWND, INETHANDLE *); void SetMyLocalDir(HWND, HWND, HWND); void ChangeLocalDrive(HWND, HWND, HWND); const char szWindowTitle[] = "猫でもわかるFTP"; char szClassName[] = "myftp03"; //ウィンドウクラス BOOL bConnect = FALSE;//インターネットに接続しているかどうか

今回新たに、ローカルのディレクトリを表示する子ウィンドウを 2つ作ったのでそれぞれのIDを新たに定義しました(ID_LIDTL, ID_STATICL)。

また、ダイアログボックスに2つのHWNDを渡したい関係からHWNDSETという 構造体を定義してみました(後出)。

ドライブ変更のためのダイアログボックスのプロシージャ(MyDriveProc) を新しく作ります。

ファイルをダウンロードするための関数MyDownの引数が増えています。 リストボックスにローカルのディレクトリを表示するSetMyLocalDir関数、 ローカルのドライブを変更するChangeLocalDrive関数が追加になりました。

WinMain, InitApp, InitInstanceの各関数に変更はありません。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id, i, iIndex; static ACCOUNT myaccount; static FNAME myfname; static FTPADDRESS myftpaddress; static INETHANDLE inet; static HWND hList, hStatic, hListL, hStaticL; static HINSTANCE hInst; RECT rc; HDWP hDwp; char szDir[MAX_PATH], szBuf[MAX_PATH]; switch (msg) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lp)->hInstance; GetClientRect(hWnd, &rc); hList = CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_SORT | LBS_NOTIFY, 0, 0, 0, 0, hWnd, (HMENU)ID_LIST, hInst,NULL); hListL = CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_SORT | LBS_NOTIFY, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTL, hInst, NULL); 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); 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, SWP_SHOWWINDOW); hDwp = DeferWindowPos(hDwp, hList, HWND_TOP, LOWORD(lp) / 2 + 5, 40, LOWORD(lp) / 2 - 15, HIWORD(lp) - 50, SWP_SHOWWINDOW); EndDeferWindowPos(hDwp); break; case WM_COMMAND: switch (LOWORD(wp)) { case ID_LIST: if (HIWORD(wp) == LBN_DBLCLK) { iIndex = ListBox_GetCurSel(hList); ListBox_GetText(hList, iIndex, szDir); for (i = 0; i <= lstrlen(szDir) -5 ; i++) { szBuf[i] = szDir[i + 6]; } FtpSetCurrentDirectory(inet.hHost, szBuf); GetAllFiles(hWnd, hList, hStatic, &inet); } break; case ID_LISTL: if (HIWORD(wp) == LBN_DBLCLK) { iIndex = ListBox_GetCurSel(hListL); ListBox_GetText(hListL, iIndex, szDir); for (i = 0; i <= lstrlen(szDir) - 5; i++) { szBuf[i] = szDir[i + 6]; } SetCurrentDirectory(szBuf); SetMyLocalDir(hWnd, hListL, hStaticL); } break; case IDM_LOCALDRIVE: ChangeLocalDrive(hWnd, hListL, hStaticL); break; case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_CONNECT: bConnect = MyConnect(hWnd, &inet, myaccount, myftpaddress, hList, hStatic); break; case IDM_DOWNLOAD: MyDown(hWnd, inet, myftpaddress, &myfname, hList, hStaticL); 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) { 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; }

前章では自作構造体FNAME(ダウンロードする ファイル名とローカルのファイル名を格納する)はWndProcプロシージャ内には 登場してきませんでした。しかし、後のプログラムの関係上ここで 登場させます。あちこちで使うのでここの関数内にstaticな変数として 宣言しておくと便利です。

WM_CREATEのところで今回新たに増えた子供ウィンドウ(ローカルのディレクトリ表示用) を作ります。子供を作ったらSetMyLocalDir関数(後出)を呼んでローカルのディレクトリを 表示させます。

WM_SIZEメッセージが来たら4つの子供ウィンドウの位置、大きさを 調整します。ローカルとFTPの表示用ウィンドウは同じ大きさになるように してあります。

ローカルディレクトリ表示用のリストボックスがダブルクリックされたら そこの文字列を取得してここから「<dir> 」を取り除きます。 そして、SetCurrentDirectory関数でカレントディレクトリを変更します。 そして、SetMyLocalDir関数を呼んで新しいディレクトリ表示をさせます。 ftpのディレクトリ変更とほとんど同じです。

BOOL SetCurrentDirectory( LPCTSTR lpPathName );

カレントディレクトリを変更します。
lpPathNameに新しいディレクトリを指定します。絶対パスでも 相対パスでもOKです。

メニューからIDM_LOCALDRIVE(「ドライブの変更」)が選択されたら ChangeLocalDrive関数(後出)を呼び出してドライブの変更を行います。

メニューからIDM_DOWNLOAD(「ファイルのダウンロード」)が選択されたら MyDown関数を呼びますが、引数が変更になっているので注意してください。

プログラム終了時、今回新しく作った2つの子供ウィンドウを破棄します。 この操作は省略しても差し支えありません。(親が死んだら子供も自動的に死ぬ)

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

void MyDown(HWND hWnd, INETHANDLE inet, FTPADDRESS ftpadr, FNAME *lpfname, HWND hList, HWND hStaticL) { HINSTANCE hInst; int iIndex; if (!bConnect) { MessageBox(hWnd, "インターネットに接続されていません", "失敗", MB_OK); return; } iIndex = ListBox_GetCurSel(hList); ListBox_GetText(hList, iIndex, lpfname->szFName); 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, //バイナリファイルとしてダウンロード 0 )) { MessageBox(hWnd, "ダウンロードに失敗しました。", "失敗", MB_OK); return; } MessageBox(hWnd, "無事ダウンロードできました", "OK", MB_OK); return; }

今までは、ファイルのダウンロードは自分で「ダウンロードするファイル名」と 「ダウンロード先」を入力しなくてはいけませんでした。

リストボックスで指定したファイルとか、パスを自動的に入力してくれると 大変助かります。この関数が呼ばれたらダウンロードするファイルは hListで選択されているファイル、ダウンロード先のパスはhStaticLの 文字列(ウィンドウタイトル)にhListのファイル名をくっつけたものです。

さて、これでこの関数の引数を変更した理由がわかったでしょうか。 FNAME構造体のアドレスを引数でもらってきたので、この中身をを変更すると 呼び出し元のWndProc内のFNAME構造体の内容も変更されます。 WndProc内ではstaticで宣言されているので、変更された内容は ずーっと保持されます。それで、いろいろなところで利用できるようになります。

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

void SetMyLocalDir(HWND hWnd, HWND hListL, HWND hStaticL) { char szfname[MAX_PATH + 5]; WIN32_FIND_DATA wfd; HANDLE hFind; ListBox_ResetContent(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 { if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { strcpy(szfname, "<dir> "); strcat(szfname, wfd.cFileName); } else { strcpy(szfname, wfd.cFileName); } ListBox_AddString(hListL, szfname); } while (FindNextFile(hFind, &wfd)) { if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { strcpy(szfname, "<dir> "); strcat(szfname, wfd.cFileName); } else { strcpy(szfname, wfd.cFileName); } ListBox_AddString(hListL, szfname); } FindClose(hFind); return; }

リストボックスにローカルのディレクトリを表示する関数です。 FTPのディレクトリを表示するのとほとんど同じ方法です。 まず現在のディレクトリ名を取得してhStaticLに表示します。

DWORD GetCurrentDirectory( DWORD nBufferLength, LPTSTR lpBuffer );

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

lpBufferカレントディレクトリの絶対パスが格納されるバッファです。

成功するとバッファに書き込まれた文字数、失敗すると0が返されます。

HANDLE FindFirstFile( LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData );

lpFileNameには、パス名とファイル名が入った文字列へのポインタを指定します。

lpFindFileDataにはWIN32_FIND_DATA構造体へのポインタを指定します。 この構造体については前章を参照してください。

関数が成功すると検索ハンドルが返され、失敗するとINVALID_HANDLE_VALUEが 返されます。

BOOL FindNextFile( HANDLE hFindFile, LPWIN32_FIND_DATA lpFindFileData );

以前のFindFirstFile関数に続けて、次のファイルを探します。

hFindFileにはFindFirstFile関数で返された検索ハンドルを指定します。

lpFindFileDataはWIN32_FIND_DATA構造体へのポインタです。

BOOL FindClose( HANDLE hFindFile );

検索ハンドルをクローズします。

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

ローカルのディレクトリを変更する関数です。 WndProcからもらってきたローカルディレクトリを表示する スタティックウィンドウとリストボックスのハンドルを プロシージャに渡すためにHWNDSET構造体のメンバにセットします。 そして、このアドレスをDialogBoxPalam関数の最後の引数に渡します。

LRESULT CALLBACK MyDriveProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hList; static HWND hListL, hStaticL, hParent; char szDrv[4]; HWNDSET *lphset; switch (msg) { case WM_INITDIALOG: lphset = (HWNDSET *)lp; hListL = lphset->hwnd1; hStaticL = lphset->hwnd2; 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); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

ドライブ変更ダイアログボックスのプロシージャです。

WM_INITDIALOGがきたらChangeLocalDrive関数からもらってきた HWNDSET構造体を調べてstaticな変数(hListL, hStaticL)に格納します。 また親ウィンドウ(メインウィンドウ)のハンドルも取得しておきます。 ここで、出てくるhListはこのダイアログのドライブ名を表示する リストボックスのハンドルです。
さて、次にDlgDirList関数でドライブを調べます。

int DlgDirList( HWND hDlg, LPTSTR lpPathSpec, int nIDListBox, int nIDStaticPath, UINT uFileType );

リストボックスにファイル名、ディレクトリ名、ドライブ名を表示します。

hDlgには、リストボックスを持つダイアログボックスのハンドルを指定します。

lpPathSpecには、パス名等を指定します。

nIDListBoxには、リストボックスのIDを指定します。

nIDStaticPathにはスタティックコントロールのIDを指定します。

uFileTypeには表示するファイルのタイプを指定します。 次の値の組み合わせで指定します。
DDL_ARCHIVEアーカイブファイルを表示します。
DDL_DIRECTORYサブディレクトリを表示します。
DDL_DRIVESドライブを表示します。
DDL_HIDDEN隠しファイルを表示します。
DDL_EXCLUSIVE指定された属性を持つファイルのみを表示します。
DDL_READONLY読取専用ファイルを表示します。
DDL_READWRITE読み書き可能ファイルを表示します。
DDL_SYSTEMシステムファイルを表示します。
DDL_POSTMSGSメッセージをアプリケーションメッセージキューにポストします。
通常は直接ダイアログボックスプロシージャに送られます。

この関数を用いると普通のリストボックスに簡単にファイル名等を表示できます。 ローカルのディレクトリ表示にこれを使えばよいではないか、と思われるかもしれませんが この関数は古いタイプのファイル名しかサポートしていません。また、 サブディレクトリ名に日本語が使われていると文字化けします。それで 今回はドライブ名の取得のみに使っています。

OKボタンが押されたらDlgDirSelectEx関数で選択されているドライブ名を取得します。

BOOL DlgDirSelectEx( HWND hDlg, LPTSTR lpString, int nCount, int nIDListBox );

hDlgには、リストボックスを持つダイアログボックスのハンドルを指定します。

lpStringには、選択項目が格納されるバッファへのポインタを指定します。

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

nIDListBoxには、リストボックスのIDを指定します。

これで、簡単に選択された項目が取得できます。(ドライブとかサブディレクトリに ついてくるかっこなどを取り外してくれる)

さて、選択されたドライブがわかったらSetCurrentDirectory関数で カレントディレクトリを変更し、SetMyLocalDir関数で ディレクトリ表示を行います。

今回の改良で少し使いやすくなりましたが、まだまだ不満はあります。 少しずつ改善していきましょう。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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