概要

コマンドライン版のフォルダー内のフォルダ名に含まれる全角文字列を半角に変換するプログラムフォルダー内のファイル名の全角英数を半角に変換するプログラムのウィンドウ版(GUI)です。
フォルダー内に含まれるファイル名のうちフォルダ・ファイル名に全角英数があった場合、半角に変換するプログラムです。サブフォルダー内のフォルダ名も変換対象になります。
フォルダー名の指定は、参照ボタンをクリックして選択するか、エディットボックスにフォルダー名を入力します。
その後変換ボタンをクリックすると変換処理されます。変換されたフォルダ・ファイル名はリストボックスに一覧表示されます。

変更履歴

2014.04.24 初版
2014.05.07 64bitでコンパイルした場合の警告をキャストにより解消

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Virtual Box上の仮想マシーン)
Windows 7 Enterprise Service Pack 1 64bit

動作例

本プログラムを起動した状態

プログラムソースの概要

gzen2han.cpp

_tWinMain

GdiplusStartupによりGDI+の初期化をします。
_tWinMain関数よりダイアログボックスを表示します。

DlgProc1

ダイアログの初期化時にDlgProc1関数にWM_INITDIALOGメッセージが発生するのでダイアログのハンドルをグローバル変数に保存します。
フォルダー名の参照
ボタンがクリックされると、WM_COMMANDメッセージが発生するので参照ボタンに対するもである場合GetDir関数を呼び出します。GetDir関数からSHBrowseForFolder APIを呼び出し、フォルダーを選択するダイアログが初期化されます。初期化時にBrowseCallbackProc関数が呼び出されます。BFFM_INITIALIZEDメッセージの場合フォルダー選択の初期時のフォルダーを設定します。
変換ボタン
ボタンがクリックされると、DlgProc1関数にWM_COMMANDメッセージが発生するので、変換ボタンのメッセージの場合、エディットボックスの内容を取得しフォルダー名とします。
findメンバー関数
findメンバー関数でフォルダー名およびファイル名の検索をします。サブフォルダーも検索します。
full_renameメンバー関数
full_renameメンバー関数の中でファイル名に全角が含まれている場合、MoveFile APIにより全角から半角に変換します。フォルダー内の変換が終了した後、フォルダー名の変更が行われます。変換が行われた場合、リストボックスにフォルダー名等を追加します。

ソースコード

gzen2han.cpp

// 指定したフォルダーのフォルダー名とファイル名を全角から半角に変換する。

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <direct.h>
#include <shlobj.h>
#include "list.hpp"
#include "resource.h"

//	ファイルの情報を保存する構造体

struct FILE_LINK{
	friend struct FILE_LIST;
	TCHAR* name;			//	ファイル名
	FILE_LINK* next;	//	次のファイル名へのポインタ
	FILE_LIST* child;	//	チャイルドフォルダー
	FILE_LINK(){
		name = 0;
		next = 0;
		child = 0;
	}
};

//	ファイル名を管理する構造体

struct FILE_LIST : public LIST{
	TCHAR* name;	//	親フォルダー名
	FILE_LIST* parent;	//	親フォルダーのFILE_LIST
	FILE_LIST() : LIST() {
		name = 0;
		parent = 0;
	}
	void append(FILE_LINK* p){	//	ファイルを追加
		LIST::append((void*)p);
	}
	FILE_LINK* next(void){	//	次のファイルを検索	
		return (FILE_LINK*)LIST::next();
	}
	FILE_LINK* first(void){	//	最初のファイルを検索
		return (FILE_LINK*)LIST::first();
	}
	int find(TCHAR* pass, int sz, TCHAR* card);	// passで示されるフォルダーのcardで示されるワイルカードに合致するファイルを検索しFILE_LISTに登録する
	FILE_LINK* dupchk(TCHAR* fullpass);	// fullpassがFILE_LISTに登録されているか検索
	void short_puts(TCHAR* pass, int num);	// ファイルの一覧を標準出力に出力する。フォルダー階層ごとにインデントされる。
	void full_rename(TCHAR* pass, int num); // フォルダー・ファイル名を全角から半角に変換する
	FILE_LINK* find_name(TCHAR* name){	//	チャイルドフォルダーを含まず一致する名前のFILE_LINK*を返す。
		FILE_LINK* p = first();
		while (p){
			if (_tcscmp(name, p->name) == 0){
				return p;
			}
			p = next();
		}
		return 0;
	}
	void clear(void){	//	FILE_LISTに登録されているフォルダー・ファイルをクリアする。
		FILE_LINK* p = first();
		FILE_LINK* np;
		while (p){
			np = next();
			if (p->child){
				p->child->clear();
			}
			delete p->name;
			delete p;
			p = np;
		}
	}
};

LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); // ダイアログボックスプロシージャー
bool zen2han(TCHAR* s);	//	全角文字を半角に変換する 変換された文字があった場合はtrueを返す
int GetDir(HWND hWnd, TCHAR* path, TCHAR* root);	//	フォルダー名を取得するコモンコントロール

TCHAR def_dir[MAX_PATH];
HWND hDlg;

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst, TCHAR* lpszCmdLine, int nCmdShow){
	_tcscpy_s(def_dir,sizeof(def_dir)/sizeof(TCHAR), _TEXT("C:\\"));
	if (DialogBox(hInstance, TEXT("DLG1"), 0, (DLGPROC)DlgProc1)){

	}
	return 0;
}

 // ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc1(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	TCHAR buf[MAX_PATH];
	FILE_LIST file_list;

	switch (msg) {
	case WM_INITDIALOG:
		::hDlg = hDlg;
		SetWindowText(GetDlgItem(hDlg, IDC_EDIT_DIR), def_dir);
		return TRUE;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_BUTTON_DIR:
			_tcscpy_s(def_dir, sizeof(def_dir) / sizeof(TCHAR), _TEXT("C:\\"));
			GetDir(hDlg, buf, 0);
			SetWindowText(GetDlgItem(hDlg, IDC_EDIT_DIR), buf);
			return TRUE;
		case IDOK:
			GetWindowText(GetDlgItem(hDlg, IDC_EDIT_DIR), buf,sizeof(buf)/sizeof(TCHAR));
			file_list.find(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("*.*"));
			file_list.full_rename(buf, sizeof(buf) / sizeof(TCHAR));
			return TRUE;
		case IDCANCEL:
			EndDialog(hDlg, FALSE);
			return FALSE;
		default:
			return FALSE;
		}
	default:
		return FALSE;
	}
	return TRUE;
}

//	相対パスが登録されているか確認し、登録されていればFILE_LINK*を返す。登録されていなければ0を返す。

FILE_LINK* FILE_LIST::dupchk(TCHAR* fullpass){
	FILE_LIST* flp = this;
	FILE_LINK* p;
	TCHAR* sp = fullpass;
	TCHAR* tp = fullpass;
	FILE_LINK* np;
	while (*sp){
		if (*sp == _T('\\')){
			sp[0] = _T('\0');
			p = flp->first();
			np = 0;
			while (p){
				if (_tcscmp(tp, p->name) == 0){
					np = p;
					flp = p->child;
					break;
				}
				p = flp->next();
			}
			if (np == 0)
				return 0;
			tp = sp + 1;
		}
		++sp;
	}

	p = flp->first();
	while (p){
		if (_tcscmp(tp, p->name) == 0){
			return p;
		}
		p = flp->next();
	}
	return 0;
}


void FILE_LIST::short_puts(TCHAR* pass, int num){
	FILE_LINK* p = first();
	while (p){
		for (int i = 0; i<num; i++)
			_tprintf(_TEXT("\t"));
		if (p->child){
			_tprintf(_TEXT("[%s]\n"), p->name);
			p->child->short_puts(pass, num + 1);
		}
		else
			_tprintf(_TEXT("%s\n"), p->name);
		p = next();
	}

}

void FILE_LIST::full_rename(TCHAR* pass, int sz){
	TCHAR dtc_file[MAX_PATH * 2];
	TCHAR* pass_end = pass + _tcslen(pass);
	TCHAR* dtc_end = dtc_file + (pass_end - pass);
	pass_end[0] = _T('\\');
	_tcscpy_s(dtc_file, sizeof(dtc_file)/sizeof(TCHAR), pass);
	dtc_end[0] = _T('\\');

	FILE_LINK* p = first();
	while (p){
		_tcscpy_s(pass_end + 1, sz - (pass_end - pass + 1), p->name);
		_tcscpy_s(dtc_end + 1, sz - (pass_end - pass + 1), p->name);
		if (p->child){
			p->child->full_rename(pass, int(sz - (pass_end - pass + 1)));
			if (zen2han(dtc_end + 1)){
				if (MoveFile(pass, dtc_file) == 0){
					MessageBox(0, dtc_file, pass, MB_OK);
				}
				SendMessage(GetDlgItem(::hDlg, IDC_LISTBOX1), LB_ADDSTRING, 0, (LPARAM)dtc_file);
			}
		}else{
			if (zen2han(dtc_end+1)){
				MoveFile(pass,dtc_file);
				SendMessage(GetDlgItem(::hDlg, IDC_LISTBOX1), LB_ADDSTRING, 0, (LPARAM)dtc_file);
			}
		}
		p = next();
	}
	pass_end[0] = _T('\0');
}

//	再帰呼び出しによりサブディレクトリ内の指定ファイルを検索する

int FILE_LIST::find(TCHAR* pass, int sz, TCHAR* card){
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	FILE_LIST* child;
	int num = 0;	//	見つかったファイル数

	TCHAR* pass_end = pass + _tcslen(pass);	//	パス名の最後の位置
	TCHAR* pass_add;	//	パス名の最後の位置(\を含む)

	if (*(pass_end - 1) == _T(':')){	//	ドライブ名のみ指定された場合
		pass_add = pass_end;
	}
	else{	//	フォルダー名が含まれている場合
		pass_add = pass_end + 1;
		pass_end[0] = _T('\\');
	}
	_tcscpy_s(pass_add, sz - (pass_add - pass + 1), _TEXT("*.*"));	//	サブディレクトリの検索


	hFind = FindFirstFile(pass, &FindFileData);
	if (hFind != INVALID_HANDLE_VALUE){
		do{
			if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			if (_tcscmp(FindFileData.cFileName, _TEXT("..")) != 0 && _tcscmp(FindFileData.cFileName, _TEXT(".")) != 0){	//	親ディレクトリ及び現在のディレクトリは無視する
				_tcscpy_s(pass_add, sz - (pass_add - pass + 1), FindFileData.cFileName);
				size_t len = _tcslen(FindFileData.cFileName);
				FILE_LINK* tp = find_name(FindFileData.cFileName);
				if (tp == 0){	//	フォルダが登録されていない場合
					TCHAR* n = new TCHAR[len + 1];
					_tcscpy_s(n, len + 1, FindFileData.cFileName);
					FILE_LINK* p = new FILE_LINK;
					p->name = n;
					p->next = 0;
					append(p);
					p->child = new FILE_LIST;
					child = p->child;
					child->parent = this;	//	親フォルダーを登録
				}
				else{	//	フォルダがすでに登録されている場合
					child = tp->child;
				}
				num += child->find(pass, int(sz - (pass_add - pass + 1)), card);	//	サブディレクトリの中を検索
			}
		} while (FindNextFile(hFind, &FindFileData));
	}
	FindClose(hFind);

	_tcscpy_s(pass_add, sz - (pass_add - pass + 1), card);
	hFind = FindFirstFile(pass, &FindFileData);
	if (hFind != INVALID_HANDLE_VALUE){
		do{
			if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
				FILE_LINK* tp = find_name(FindFileData.cFileName);
				if (tp == 0){	//ファイルが登録されていないので登録する
					++num;
					size_t len = _tcslen(FindFileData.cFileName);
					TCHAR* n = new TCHAR[len + 1];
					_tcscpy_s(n, len + 1, FindFileData.cFileName);
					FILE_LINK* p = new FILE_LINK;
					p->name = n;
					p->next = 0;
					p->child = 0;
					append(p);
				}
			}
		} while (FindNextFile(hFind, &FindFileData));
		pass_end[0] = _T('\0');
		FindClose(hFind);
		return num;
	}
	else{
		pass_end[0] = _T('\0');
		FindClose(hFind);
		return num;
	}
}

//	ディレクトリ名を取得するコモンダイアログを表示する

int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);


int GetDir(HWND hWnd, TCHAR* path, TCHAR* root){
	BROWSEINFO bInfo;
	LPITEMIDLIST pIDList;
	bInfo.hwndOwner = hWnd; // ダイアログの親ウインドウのハンドル 
	bInfo.pidlRoot = NULL; // ルートフォルダをデスクトップフォルダとする 
	bInfo.pszDisplayName = path; //szDisplayName; // フォルダ名を受け取るバッファへのポインタ 
	bInfo.lpszTitle = TEXT("保存するフォルダの選択"); // ツリービューの上部に表示される文字列 
	bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE; // 表示されるフォルダの種類を示すフラグ 
	bInfo.lpfn = BrowseCallbackProc; // BrowseCallbackProc関数のポインタ 
	bInfo.lParam = NULL;
	pIDList = SHBrowseForFolder(&bInfo);
	if (pIDList == NULL){
		path[0] = _TEXT('\0');
		return FALSE; //何も選択されなかった場合 
	}
	else{
		if (!SHGetPathFromIDList(pIDList, path))
			return FALSE;//変換に失敗 
		CoTaskMemFree(pIDList);// pIDListのメモリを開放 
		return TRUE;
	}
}
//	上記ダイアログのコールバック関数

int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData){
	if (uMsg == BFFM_INITIALIZED){
		SendMessage(hwnd, BFFM_SETSELECTION, (WPARAM)TRUE, (LPARAM)def_dir);	//	コモンダイアログの初期ディレクトリ
	}
	return 0;
}

//	全角文字を半角に変換する 変換された文字があった場合はtrueを返す

bool zen2han(TCHAR* s){
	bool f = false;
	TCHAR* p = s;

	while (*p){
		TCHAR* ep = _TEXT("¥/:*?<>|{}%()&”~");	//	フォルダーに使用できない文字
		while (*ep){
			if (*ep == *p)
				break;
			++ep;
		}
		if (*ep == _T('\0')){
			if (0xff01 <= *p  && *p <= 0xff5d){	//	全角数字英字記号の場合
				*p -= 0xff00 - 0x20;
				f = true;
			}
			else if (*p == _T(' ')){
				*p = _T(' ');
				f = true;
			}
		}
		++p;
	}
	return f;
}

resource.h

#define IDC_LISTBOX1 2001
#define IDC_EDIT_DIR 2002
#define IDC_BUTTON_DIR 2003

resource.rc

#include 
#include "resource.h"


DLG1 DIALOG DISCARDABLE  0, 0, 380, 309
STYLE DS_MODALFRAME | WS_POPUP
CAPTION "フォルダー・ファイル名変換変換(全角→半角)"
FONT 9, "MS Pゴシック"
BEGIN
	
	CONTROL "変換フォルダー・ファイル名", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 12 , 96, 18
	CONTROL "参照(&B)", IDC_BUTTON_DIR, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 110,7 , 50, 14
	CONTROL "",IDC_EDIT_DIR, "EDIT", WS_CHILD | WS_DLGFRAME | WS_VISIBLE | ES_AUTOHSCROLL, 14, 30 , 353, 15

	CONTROL "変換されたフォルダー・ファイル", -1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 59, 96, 18
	CONTROL "ListBox", IDC_LISTBOX1, "LISTBOX", WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_DISABLENOSCROLL, 7, 84, 366, 211


	DEFPUSHBUTTON	"変換(&C)",IDOK,115,288,50,14
	PUSHBUTTON	"終了(&X)",IDCANCEL,225,288,50,14
END

list.hpp

#ifndef LIST_HPP
#define LIST_HPP 1

class LINK{
	LINK* next;
	LINK* back;
	void* data;
protected:
	LINK(){
		next=0;
		back=0;
		data=0;
	}
	void set(void* d){
		data=d;
	}
	friend class LIST;
	friend int cmp(const void* d,const void* s){
		if((char*)d>(char*)s)
			return 1;
		if((char*)d<(char*)s)
			return -1;
		return 0;
		
	}
};

typedef void* LPVOID;

class LIST{
	LINK* top;	//	リストの先頭
	LINK* end;	//	リストの最後尾
	LINK* pos;	//	リストのカレントポジション
	int max;	//	リストの要素数
protected:
	LIST(){
		pos=top=end=0;
		max=0;
	}
	void append(void* d){	//	リストの最後尾にデータを追加
		LINK* p=new LINK;
		++max;
		p->data=d;
		if(top==0){
			pos=top=end=p;
		}else{
			end->next=p;
			p->back=end;
			end=p;
		}
	}
	void* next(void){	//	カレントポジジョンからデータを得た後、カレントポジションを1個進める。
		LINK* t=pos;
		if(pos==0){
			return 0;
		}else{
			pos=pos->next;
			return t->data;
		}
	}
	void* first(void){	//	カレントポジションを先頭に移動させ先頭データを得た後、カレントポジションを1個進める。
		pos=top;
		return next();
	}
	void all_del(void){	//	リスト全部を削除する
		while(top){
			LINK* t=top->next;
			delete top;
			top=t;
		}
		top=end=pos=0;
		max=0;
	}
	void repeat_del(int(*cmp )(const void* d,const void* s)){	//	ソートされたリストから重複する要素を削除
		LINK* lp=top;
		if(lp){
			LINK* bp=0;
			while(lp){
				if(bp){
					if( (*cmp)((void**)&lp->data,(void**)&bp->data) ){	//	前の要素と異なる場合
						bp=lp;
					}else{	//	前の要素と同じ場合
						LINK* t=lp->next;
						del(lp);
						--max;
						lp=t;
						continue;
					}
				}else
					bp=lp;
				lp=lp->next;
			}
		}
	}	
	void qsort(int(*cmp )(const void* d,const void* s)){
		if(max){
			int n=0;
			LPVOID* vec=new LPVOID[max];	//	リストの要素を配列にコピーする
			if(vec==0)
				return;
			void* p;
			p=first();
			while(p){
				vec[n++]=p;
				p=next();
			}
			::qsort((void*)vec,max,sizeof(LPVOID),cmp);
			LINK* lp=top;						//	配列をリストにコピー
			n=0;
			while(lp){
				lp->set(vec[n++]);
				lp=lp->next;
			}
			delete vec;
		}
	}
	void del(LINK*	p){	// リストからpを削除する
		if(p){
			if(p==top){
				LINK* t=top;
				top=top->next;
				top->back=0;
				if(pos==p){
					pos=p->next;
				}
				delete p;
				return;
			}
			if(p==end){
				LINK* t=end;
				end=end->back;
				end->next=0;
				delete p;
				return;
			}
			LINK* t=p;
			p->back->next=p->next;
			p->next->back=p->back;
			if(pos==p){
				pos=p->next;
			}
			delete t;
		}

	}
public:
	int get_max(void){
		return max;
	}
};

#endif

実行ファイルとソースファイルのダウンロード