取りあえず動いたので、おしまい。
やらなきゃいけないことが沢山残っている。スグに思いつくだけでも
- SHFileOperationを1ファイル/フォルダ毎に呼び出しているが、パスを繋げて呼ぶようにするべき。
- ファイルの移動はMoveFileExを使った方がこのツールの目的にあう。
- 環境設定フォームで、設定パスの変更は「ユーザ設定」のradioButtonが選択されている時にするべき。
- %Y %M %Dなどで実行時の日付でフォルダを生成できるようにするべき。
など、色々ある。感じとしてはプロトタイプを作ったレベルでしかない。
当初、プロジェクトを丸ごと公開しようと思っていたけど、VC++が自動生成したコードなどを公開しても良いのかわからなかったので、自分が書いた部分のコードを簡単な説明を添えて貼ることにする。コードを貼ってみると長かったです。検索で来てしまった人ごめんなさい。
Form1.h
起動して表示されるメイン画面。
今にして思えば、設定情報を保持する_confはスグにgcnewしてしまえば良かった。アプリの動作中 = Form1のインスタンスが存在しているのだし。
後やっぱり、VC++のクラスエディタがヘッダファイルにソースコードを書かせるのは、初心者の教育に悪いので止めて欲しい。ファイルが散らかると、エディタの実装が複雑になるのはわかるけど。
public ref class Form1 : public System::Windows::Forms::Form { (略) private: gdcConf^ _conf; //設定情報 private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { FileTool^ ftool = gcnew FileTool(); switch (_conf->Radiobutton_number) { case TRASH_BOX: ftool->DeleteFoldersAndFiles(); break; case USER_CONF: ftool->MoveFoldersAndFiles(_conf->UsrSavePath); break; default: break; } } private: System::Void MainMenuItem_SavePlace_Click(System::Object^ sender, System::EventArgs^ e) { Form_SavePlace^ fsp = gcnew Form_SavePlace(_conf); fsp->ShowDialog(); } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { this->Close(); } private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) { _conf = gcnew gdcConf(); } };
gdcConf.h
設定情報を保持するために作ったクラスです。
定数を作るためにconst intを使っています。C++/CLIの作法的に正しいのかわかりません。
ヘッダファイルに、コーディング…。
const int TRASH_BOX = 1; // ゴミ箱 const int USER_CONF = 2; // ユーザー設定 public ref class gdcConf { private: int _radiobutton_number; // radioボタンの設定値 1:ゴミ箱 2:ユーザ設定 String^ _UsrSavePath; // ユーザー設定の保存場所のパス literal String^ CONF_FILE_PATH = "./gdcConf.ini"; // 設定ファイル名 literal String^ DEFAULT_CONF = "1\n\n"; // デフォルト設定 public: gdcConf(void); gdcConf(String^ newSavePath); void WriteFile(void); // 設定ファイルへの保存 (Readはインスタンス生成時に実行してしまう) property String^ UsrSavePath { String^ get() { return _UsrSavePath; } void set(String^ newPath) { if (newPath->EndsWith((Path::DirectorySeparatorChar).ToString()) != true) { // 最後が"\"でない newPath = String::Concat(newPath, (Path::DirectorySeparatorChar).ToString()); } _UsrSavePath = newPath; } } property int Radiobutton_number { int get() { return _radiobutton_number; } void set(int newNumber) { if (newNumber > 0) { _radiobutton_number = newNumber; } } } };
gdcConf.cpp
設定クラスのメンバ関数定義です。ファイルから設定情報を読むようにしています。
見ればわかるように、適当な実装です。
#include "StdAfx.h" #include "gdcConf.h" gdcConf::gdcConf(void) { // 設定ファイルから読み込む try { System::IO::StreamReader^ reader = gcnew System::IO::StreamReader(CONF_FILE_PATH); if (reader->EndOfStream == false) { // 中身がある this->Radiobutton_number = int::Parse(reader->ReadLine()); this->UsrSavePath = reader->ReadLine(); System::Diagnostics::Debug::WriteLine(this->UsrSavePath); } else { // 中身がない reader->Close(); System::Diagnostics::Debug::WriteLine("ファイルの中身がないよー"); throw gcnew System::IO::FileNotFoundException("ファイルの中身がないよ"); } reader->Close(); } catch(System::IO::FileNotFoundException^ ex) { // 新規作成 System::IO::File::AppendAllText(CONF_FILE_PATH, DEFAULT_CONF); } catch(Exception ^ex) { // 本当は必要ない。 System::Diagnostics::Debug::WriteLine("全ての例外"); } } gdcConf::gdcConf(String^ newSavePath) { // (Path::DirectorySeparatorChar).ToString() // 渡された文字列の最後が"\"で終わっていなければ付加する if (newSavePath->EndsWith((Path::DirectorySeparatorChar).ToString()) != true) { // 最後が"\"でない newSavePath = String::Concat(newSavePath, (Path::DirectorySeparatorChar).ToString()); } this->_UsrSavePath = newSavePath; } // 設定ファイルへ保存する void gdcConf::WriteFile(void) { // String^ data = this->Radiobutton_number + "\n" + this->UsrSavePath + "\n"; System::IO::StreamWriter^ writer = gcnew System::IO::StreamWriter(CONF_FILE_PATH, false); // writer->Write(data); writer->WriteLine(this->Radiobutton_number); writer->WriteLine(this->UsrSavePath); writer->Close(); }
Form_SavePlace.h
設定フォーム。役割と名前があっていない悪い例だ。
変更する設定変数は参照渡しを使うので、オーバーロードさせるコンストラクタを追加している。
フォームLOAD時に設定をフォームのデータへ反映させています。
public ref class Form_SavePlace : public System::Windows::Forms::Form { public: Form_SavePlace(gdcConf^% conf) // 設定は呼び元から入手する { _conf = conf; InitializeComponent(); }; (略) private: gdcConf^ _conf; private: System::Void FolderSelect_Click(System::Object^ sender, System::EventArgs^ e) { // Show the FolderBrowserDialog. System::Windows::Forms::DialogResult result = folderBrowserDialog1->ShowDialog(); if ( result == ::DialogResult::OK ) { this->TextBox_SavePath->Text = this->folderBrowserDialog1->SelectedPath; // 復帰と同時にTextBoxの表示は更新される(どこの処理を通っているんだ?) } } private: System::Void Form_SavePlace_Load(System::Object^ sender, System::EventArgs^ e) { // 設定ファイルから有効なチェックボックスを有効にする switch (_conf->Radiobutton_number) { case TRASH_BOX: this->radioButton_UserConf->Checked = false; this->radioButton_TrashBox->Checked = true; break; case USER_CONF: this->radioButton_UserConf->Checked = true; this->radioButton_TrashBox->Checked = false; break; default: break; } // パス設定で更新する this->TextBox_SavePath->Text = _conf->UsrSavePath; } private: System::Void Button_SavePlaceOk_Click(System::Object^ sender, System::EventArgs^ e) { // 設定ファイルの更新 if (this->radioButton_TrashBox->Checked == true) { //ゴミ箱 _conf->Radiobutton_number = TRASH_BOX; } else if (this->radioButton_UserConf->Checked == true) { //ユーザー設定 _conf->Radiobutton_number = USER_CONF; _conf->UsrSavePath = this->TextBox_SavePath->Text; } _conf->WriteFile(); this->Close(); } private: System::Void Button_SavePlaceCancel_Click(System::Object^ sender, System::EventArgs^ e) { this->Close(); } };
FileTool.h
ファイルを操作をするために作ったクラス。
やっぱ、ヘッダは宣言だけにすべきだ。
public ref class FileTool { public: FileTool(void); void DeleteFoldersAndFiles(void); void MoveFoldersAndFiles(String^ targetpath); private: int MoveToTrash(String^ pathname); int MoveToFolder(String^srcpath, String^dstpath); };
FileTool.c
Windows APIを呼び出す実装たち。TCHAR, LPCTSTRとか嫌い。
クラスで作る必要が全くなかった。
#include <windows.h> #include <string.h> #include <locale.h> FileTool::FileTool(void) { } #pragma comment(lib, "shell32.lib") extern "C" { int MyDelete(LPCTSTR files2) { SHFILEOPSTRUCT sh_file_operation_struct; FILEOP_FLAGS flags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION; int result = 1; // 文字列終端をNULLを2つにする LPTSTR files_null = (LPTSTR)calloc(lstrlen(files2) + 2, sizeof(TCHAR)); lstrcpy(files_null, files2); ZeroMemory( &sh_file_operation_struct, sizeof(sh_file_operation_struct) ); sh_file_operation_struct.wFunc = FO_DELETE; sh_file_operation_struct.fFlags = flags; sh_file_operation_struct.pFrom = files_null; result = SHFileOperation(&sh_file_operation_struct); free(files_null); return result; } int MyMoveToFolder(LPCTSTR file, LPCTSTR folder_path) { SHFILEOPSTRUCT sh_file_operation_struct; int result = 1; LPTSTR file_copied = (LPTSTR)calloc(lstrlen(file) + 2, sizeof(TCHAR)); /* files's \0 and \0 */ LPTSTR file_to = (LPTSTR)calloc(lstrlen(folder_path) + 2, sizeof(TCHAR)); FILEOP_FLAGS flags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; lstrcpy(file_copied, file); lstrcpy(file_to, folder_path); ZeroMemory( &sh_file_operation_struct, sizeof(sh_file_operation_struct) ); sh_file_operation_struct.wFunc = FO_MOVE; sh_file_operation_struct.fFlags = flags; sh_file_operation_struct.pFrom = file_copied; sh_file_operation_struct.pTo = file_to; result = SHFileOperation(&sh_file_operation_struct); free(file_copied); free(file_to); return result; } }; int FileTool::MoveToTrash(String^ pathname) { int result = 1; LPCTSTR files; //StringをLPCTSTRに変換する IntPtr p = System::Runtime::InteropServices::Marshal::StringToHGlobalAuto(pathname); files = static_cast<LPCTSTR>(p.ToPointer()); try { result = MyDelete(files); } finally { System::Runtime::InteropServices::Marshal::FreeHGlobal(p); } return result; } int FileTool::MoveToFolder(String ^srcpath, String ^dstpath) { int result = 1; LPCTSTR src, dst; //dstpathについて ¥を¥¥に置換する String^ folderPath = dstpath->Replace("\\", "\\\\"); Diagnostics::Trace::WriteLine(folderPath); IntPtr p = System::Runtime::InteropServices::Marshal::StringToHGlobalAuto(srcpath); src = static_cast<LPCTSTR>(p.ToPointer()); IntPtr q = System::Runtime::InteropServices::Marshal::StringToHGlobalAuto(folderPath); dst = static_cast<LPCTSTR>(q.ToPointer()); try { result = MyMoveToFolder(src, dst); } finally { System::Runtime::InteropServices::Marshal::FreeHGlobal(p); System::Runtime::InteropServices::Marshal::FreeHGlobal(q); } return result; } void FileTool::DeleteFoldersAndFiles(void) { String^ strCategoryFolder = Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory); array<String^>^ aryFileNames = IO::Directory::GetFiles(strCategoryFolder); for each (String^ strFileName in aryFileNames) { Diagnostics::Trace::WriteLine(strFileName); // IO::File::Delete(strFileName); MoveToTrash(strFileName); } array<String^>^ aryFolderNames = System::IO::Directory::GetDirectories(strCategoryFolder);// for each (String^ strSubFolderName in aryFolderNames) { Diagnostics::Trace::WriteLine(strSubFolderName); MoveToTrash(strSubFolderName); // IO::Directory::Delete(strSubFolderName, true); } } void FileTool::MoveFoldersAndFiles(String^ targetpath) { // targetpathの最後に ¥ がついている必要がある String^ strCategoryFolder = Environment::GetFolderPath(Environment::SpecialFolder::DesktopDirectory); array<String^>^ aryFileNames = IO::Directory::GetFiles(strCategoryFolder); for each (String^ strFileName in aryFileNames) { MoveToFolder(strFileName, targetpath); } array<String^>^ aryFolderNames = System::IO::Directory::GetDirectories(strCategoryFolder); for each (String^ strSubFolderName in aryFolderNames) { MoveToFolder(strSubFolderName, targetpath); } }