Win32/64ネットワーク開発 003 wininetを使ったftp通信
前の記事:Win32/64ネットワーク開発 002 wininetを使ったhttp通信
次の記事:[[Win32/64ネットワーク開発 004]]
概要
wininet.Libやwininet.hのインクルードについては前の記事の通り、同じように設定する必要があります。
まずは、ホスト名やユーザID、パスワード、接続時に最初に表示するディレクトリ名を聞いてくるようなインタフェースを持たない固定値での接続を試してみましょう。接続して、FTPでカレントディレクトリを設定してカレントディレクトリが今何かを返してくれるとこまでのプログラムを書いてみます。以下のとおりです。
HINTERNET HINTERNETinet = InternetOpen(L"yo-net.jp ftp", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
HINTERNET HINTERNEThost = InternetConnect(
HINTERNETinet,
L"ftp.xxxx.com", INTERNET_DEFAULT_FTP_PORT,
L"ftp_user_name", L"ftp_user_password",
INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
FtpSetCurrentDirectory(HINTERNEThost, L"/");
DWORD DWORDsize = 0;
FtpGetCurrentDirectory(HINTERNEThost, NULL, &DWORDsize);
wchar_t* pwchCurrentDir = new wchar_t[DWORDsize/sizeof(wchar_t)];
FtpGetCurrentDirectory(HINTERNEThost, pwchCurrentDir, &DWORDsize);
上記のプログラムにより、pwchCurrentDirにFTP接続したときの現在のディレクトリを取得できます。
InternetOpen関数についての説明はコチラのリンクから確認して下さい。FTP通信をするときはホスト名やログイン名を使うので第2引数のフラグはINTERNET_OPEN_TYPE_DIRECTを指定します。
URLを指定してhttp通信をするときは、InternetOpenUrl関数を使いましたが、FTP通信をするときはInternetConnect関数を使います。
■InternetConnect(HINTERNET, LPCWSTR, INTERNET_PORT, LPCWSTR, LPCWSTR, DWORD, DWORD, DWORD_PTR)
第1引数:HINTERNET hInternet
InternetOpen関数によって得られる、HINTERNETハンドルです。
第2引数:LPCWSTR lpszServerName
FTPサーバへのアドレスまたはホスト名を文字列で指定します。
第3引数:INTERNET_PORT nServerPort
wininet.hで INTERNET_DEFAULT_FTP_PORT には通常FTPで使われるポート番号21が設定されていますので、このキーワードを使うのが普通ですが、違うPORT番号でサーバ側がFTP通信をリスニング、受け入れ待ちをしている場合は、特定の番号を指定する必要があります。DWORD型の別名がINTERNET_PROTとなっています。0~65535の間の16bitの整数値です。
第4引数:LPCWSTR lpszUserName
FTPログインをするときのID名です。
第5引数:LPCWSTR lpszPassword
FTPログインをするときのID名に対して関連付けられたパスワード文字列です。
第6引数:DWORD dwService
アクセスするサービスの種類です。FTP(INTERNET_SERVICE_FTP)とGOPHER(INTERNET_SERVICE_GOPHER)とHTTP(INTERNET_SERVICE_HTTP)があります。FTP通信時は通常はFTPを指定します。
第7引数:DWORD dwFlags
INTERNET_FLAG_PASSIVE(0x0800 0x0000)を指定します。
第8引数:DWORD_PTR dwContext
返されるハンドルと共にコールバック関数に渡されるアプリケーション定義値を指定する変数へのポインター。一瞬なんのことをいっているかわからない説明だったと思いますが、DWORD dwContext=123(関数はDWORD_PTRの形式を必要としているので、&dwContextのように設定します)のように特定の識別値を与えることで、このあと使う関数で、この値によって複数の通信を切り分けて使うのに利用する目的で準備されている仕組みです。wininetを使うときにそこまで複雑な通信をするとは思えませんが、winsockなどで通信するときも、こういった複数のハンドル値を管理するうまい仕組みが必要だという予備知識にもなるかと思います。切り分けをするためにもこういった使い方をする手法があるということくらいは頭の隅においておいたほうがいいかもしれません。この仕組みを使わないで、適当に0と設定しておいてもいいわけです。
■FtpSetCurrentDirectory(HINTERNET, LPCWSTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszDirectory
接続しているFTP通信で、現在のディレクトリとして設定したい文字列を設定します。FTPルートディレクトリからの絶対パスである必要があります。
■FtpGetCurrentDirectory(HINTERNET, LPCWSTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPWSTR lpszCurrentDirectory
接続しているFTP通信で、現在のディレクトリがどういう名前なのか取得したい文字列取得領域を設定します。FTPルートディレクトリからの絶対パスが取得できます。文字列の確保領域が取得できる文字列より小さいあるいはNULLである場合は、第3引数に必要な文字列バイト長が返却されます。UTF-16で受け取ろうとする場合は、文字列の長さの2倍となっています。
第3引数:LPDWORD lpdwCurrentDirectory
取得した文字列の長さを受け取るWORD型の変数へのアドレスを指定します。
DWORD DWORDcontext0 = 0;
DWORD DWORDcontext1 = 1;
DWORD DWORDcontext2 = 2;
DWORD DWORDcontext3 = 3;
DWORD DWORDcontext4 = 4;
DWORD DWORDsizeHigh = 0;
DWORD DWORDsizeLow = 0;
FtpCreateDirectory(HINTERNEThost, L"NewFolder230101011111");
FtpRemoveDirectory(HINTERNEThost, L"NewFolder230101011111");
FtpPutFile(HINTERNEThost, //FTPのインターネットハンドル
L"File230101011111.lzh", //アップロードするファイル
L"/public_html/www.yo-net.jp/File230101011111.lzh", //アップロード先のパス付ファイル名
FTP_TRANSFER_TYPE_BINARY, //バイナリファイルとしてダウンロード
(DWORD_PTR)&DWORDcontext0);
FtpRenameFile(HINTERNEThost, L"File230101011111.lzh", L"File230101011112.lzh");
FtpRenameFile(HINTERNEThost, L"File230101011112.lzh", L"File230101011111.lzh");
WIN32_FIND_DATA WIN32_FIND_DATAfind;
FtpFindFirstFile(HINTERNEThost, L"File230101011111.lzh", &WIN32_FIND_DATAfind, 0, 0);
FtpDeleteFile(HINTERNEThost, L"File230101011111.lzh");
HINTERNET HINTERNETopen = FtpOpenFile(HINTERNEThost, L"Test_SJIS.css", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, (DWORD_PTR)&DWORDcontext1);
DWORDsizeLow = FtpGetFileSize(HINTERNETopen, (LPDWORD)&DWORDsizeHigh);
DWORD DWORDBufSize = 1024;
DWORD DWORDTotal = 0;
HGLOBAL HGLOBALMem;
HGLOBALMem = GlobalAlloc(GHND, 1);
char* pchOpenFileStrTotal = nullptr;
char* pchOpenFileStr = new char[DWORDBufSize + 1];
while(1){
BOOL bResult = InternetReadFile(HINTERNETopen, pchOpenFileStr, DWORDBufSize, &DWORDcontext2);
pchOpenFileStr[DWORDcontext2] = '\0';
if (DWORDcontext4 == 0) {
break;
}
DWORDTotal += DWORDcontext4;
HGLOBALMem = GlobalReAlloc(HGLOBALMem, DWORDTotal + 1, GMEM_MOVEABLE);
pchOpenFileStrTotal = (char*)GlobalLock(HGLOBALMem);
strcat_s(pchOpenFileStrTotal, DWORDTotal + 1, pchOpenFileStr);
}
int iSize = MultiByteToWideChar(932, 0U, pchOpenFileStrTotal, -1, nullptr, 0);
wchar_t* pwchOpenFileStr = new wchar_t[iSize];
MultiByteToWideChar(932, 0U, pchOpenFileStrTotal, -1, pwchOpenFileStr, iSize);
InternetCloseHandle(HINTERNEThost);
InternetCloseHandle(HINTERNETinet);
HINTERNETinet = InternetOpen(L"yo-net.jp ftp", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
HINTERNEThost = InternetConnect(
HINTERNETinet,
L"ftp.xxxx.com", INTERNET_DEFAULT_FTP_PORT,
L"ftp_user_name", L"ftp_user_password",
INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0);
FtpSetCurrentDirectory(HINTERNEThost, L"/public_html/www.yo-net.jp/");
HINTERNET HINTERNETopen = FtpOpenFile(HINTERNEThost, L"Test_SJIS.css", GENERIC_READ, FTP_TRANSFER_TYPE_ASCII, (DWORD_PTR)&DWORDcontext3);
//sjisのcssファイルをUTF-8に変換する処理。
wchar_t* pwchutf16String = nullptr;
int utf16Length = MultiByteToWideChar(932, 0, pchOpenFileStrTotal, -1, NULL, 0);
if (utf16Length > 0) {
pwchutf16String = new wchar_t[utf16Length];
MultiByteToWideChar(932, 0, pchOpenFileStrTotal, -1, pwchutf16String, utf16Length);
}
char* pchutf8String = nullptr;
int iutf8Length = WideCharToMultiByte(CP_UTF8, 0, pwchutf16String, -1, nullptr, 0, nullptr, nullptr);
pchutf8String = new char[iutf8Length]; // NULL 終端文字を含めないサイズに修正
std::string stringutf8String(iutf8Length, 0);
if (iutf8Length > 0) {
WideCharToMultiByte(CP_UTF8, 0, pwchutf16String, -1, &stringutf8String[0], iutf8Length, nullptr, nullptr);
stringutf8String[iutf8Length - 1] = '\0';
}
InternetWriteFile(HINTERNETopen, stringutf8String.c_str(), stringutf8String.size() - 1, &DWORDcontext4);
delete[] pwchutf16String;
delete[] pchutf8String;
■FtpCreateDirectory(HINTERNET, LPCWSTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszDirectory
作成するディレクトリの名前の文字列を保持した配列変数の先頭を差す文字型のポインタです。
■FtpRemoveDirectory(HINTERNET, LPCWSTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszDirectory
削除するディレクトリの名前の文字列を保持した配列変数の先頭を差す文字型のポインタです。
■FtpPutFile(HINTERNET, LPCWSTR, LPCWSTR, DWORD, DWORD_PTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszLocalFile
アップロードするファイル名の文字列を保持した配列変数の先頭を差す文字型のポインタです。
第3引数:LPCWSTR lpszNewRemoteFile
アップロードしたファイルをアップロード先でのファイル名を指定したルートディレクトリからのファイルパスを含めた文字列を保持した配列変数の先頭を差す文字型のポインタです。
第4引数:DWORD dwFlags
アップロードするファイルの通信方式を定義するフラグです。FTP_TRANSFER_TYPE_BINARY(2)とFTP_TRANSFER_TYPE_ASCII(1)を主に指定します。FTP_TRANSFER_TYPE_BINARYの場合はファイルはそのまま送られ、FTP_TRANSFER_TYPE_ASCIIの場合は改行コードを変換する処理が入ります。CR+LFをLFだけにする変換を行います。現代においては、サーバー側に配置されるテキストがCR+LFの形式であっても問題なく表示・プログラム実行処理されるので、変換する必要はなくなってきています。ただし、変換してアップロードしたファイルがあって、取得する場合にまた変換する必要があったりはしますので、ファイルの扱い方の状況次第では必要になってきます。正しくない通信によって、改行コードがごちゃごちゃしてきている可能性があります。WindowsではCR+LFにUnix系ではLFにという変換を管理する必要があります。作るならば、そういったことを手助けするようなFTPツールであるべきですね。
第5引数:DWORD_PTR dwContext
返されるハンドルと共にコールバック関数に渡されるアプリケーション定義値を指定する変数へのポインター。一瞬なんのことをいっているかわからない説明だったと思いますが、DWORD dwContext=123(関数はDWORD_PTRの形式を必要としているので、&dwContextのように設定します)のように特定の識別値を与えることで、このあと使う関数で、この値によって複数の通信を切り分けて使うのに利用する目的で準備されている仕組みです。wininetを使うときにそこまで複雑な通信をするとは思えませんが、winsockなどで通信するときも、こういった複数のハンドル値を管理するうまい仕組みが必要だという予備知識にもなるかと思います。切り分けをするためにもこういった使い方をする手法があるということくらいは頭の隅においておいたほうがいいかもしれません。この仕組みを使わないで、適当に0と設定しておいてもいいわけです。毎回同じことを書いてますので、以降では説明は省略しましょうかね。
■FtpRenameFile(HINTERNET, LPCWSTR, LPCWSTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszExisting
変更するファイルの元の名前の文字列を保持した配列変数の先頭を差す文字型のポインタです。
第3引数:LPCWSTR lpszNew
変更するファイルの変更後の名前の文字列を保持した配列変数の先頭を差す文字型のポインタです。
■FtpFindFirstFile(HINTERNET, LPCWSTR, LPWIN32_FIND_DATAW, DWORD, DWORD_PTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszSearchFile
検索するファイル名の文字列を保持した配列変数の先頭を差す文字型のポインタです。
第3引数:LPWIN32_FIND_DATAW lpFindFileData
検索結果を返すLPWIN32_FIND_DATAW型構造体の変数。参照型として渡します。
第4引数:DWORD dwFlags
INTERNET_FLAG_HYPERLINK, INTERNET_FLAG_NEED_FILE, INTERNET_FLAG_NO_CACHE_WRITE, INTERNET_FLAG_RELOAD, INTERNET_FLAG_RESYNCHRONIZEといった検索処理についてのフラグを設定します。
- INTERNET_FLAG_HYPERLINK
- ネットワークからアイテムを再読み込みするかどうかを決定するときに、有効期限時間がなく、サーバーから LastModified 時刻が返されない場合は、強制的に再読み込みを行う。
- INTERNET_FLAG_NEED_FILE
- ファイルをキャッシュできない場合は、一時ファイルを作成します。
- INTERNET_FLAG_NO_CACHE_WRITE
- 返されたエンティティをキャッシュに追加しません。
- INTERNET_FLAG_RELOAD
- 要求されたファイル、オブジェクト、またはディレクトリ リストをキャッシュからではなく元のサーバーからダウンロードします。
- INTERNET_FLAG_RESYNCHRONIZE
- 最後にダウンロードされてからリソースが変更された場合は、HTTP リソースを再読み込みします。 すべての FTP リソースが再読み込みされます。
第5引数:DWORD_PTR dwContext
返されるハンドルと共にコールバック関数に渡されるアプリケーション定義値を指定する変数へのポインター。
■FtpDeleteFile(HINTERNET, LPCWSTR)
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszFileName
削除するファイルの名前の文字列を保持した配列変数の先頭を差す文字型のポインタです。
■FtpOpenFile(HINTERNET, LPCWSTR, DWORD, DWORD, DWORD_PTR)
オープンした状態でFtpGetFileSizeとInternetReadFileとInternetWriteFile関数が使えます。戻り値にはHINTERNET型のインスタンスハンドルを取得できます。
第1引数:HINTERNET hConnect
InternetConnect関数で得られるHINTERNET型ハンドルです。
第2引数:LPCWSTR lpszFileName
ファイルオープンするファイルのファイル名を保持した配列変数の先頭を差す文字型のポインタです。
第3引数:DWORD dwAccess
ファイルアクセスについての指定。このパラメーターはGENERIC_READまたはGENERIC_WRITEを指定できます。両方を指定することはできません。
第4引数:DWORD dwFlags
ファイルの通信方式を定義するフラグです。FTP_TRANSFER_TYPE_BINARY(2)とFTP_TRANSFER_TYPE_ASCII(1)を主に指定します。
第5引数:DWORD_PTR dwContext
返されるハンドルと共にコールバック関数に渡されるアプリケーション定義値を指定する変数へのポインター。
■FtpGetFileSize(HINTERNET, LPCWSTR)
第1引数:HINTERNET hConnect
FtpOpenFile関数で得られるHINTERNET型ハンドルです。
第2引数:LPDWORD lpdwFileSizeHigh
要求されたFTPリソースのファイルサイズの上位符号なしlong整数へのポインター。参照で渡します。ファイルサイズが4GByteを超えるような場合だけ第2引数で0以外の値を得ることができます。
■InternetWriteFile(HINTERNET, LPCVOID, DWORD, LPDWORD)
第1引数:HINTERNET hConnect
FtpOpenFile関数で得られるHINTERNET型ハンドルです。
第2引数:LPCVOID lpBuffer
ファイルに書き込まれる、文字列の先頭アドレスです。
第3引数:DWORD dwNumberOfBytesToWrite
ファイルに書き込まれるバイト数。
第4引数:LPDWORD lpdwNumberOfBytesWritten
ファイルに書き込まれたバイト数を受け取る変数です。参照で渡します。
前の記事:Win32/64ネットワーク開発 002 wininetを使ったhttp通信
次の記事:[[Win32/64ネットワーク開発 004]]