┏第48号━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃         .NETプログラミング研究         ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ──<メニュー>─────────────────────── ■.NET質問箱 ・Windowsディレクトリを取得する ・2つのフォルダの同期を行う ・Visual Basic .NET, Visual C# Standard Editionでクラスライブ  ラリを作成する ・印刷プレビューダイアログの表示位置や表示倍率を指定する ─────────────────────────────── ─────────────────────────────── ■.NET質問箱 ─────────────────────────────── 「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込 まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、 Q&A形式にまとめて紹介します。 [URL]どぼん!のプログラミング掲示板 http://dobon.net/vb/bbs.html ─────────────────────────────── ●Windowsディレクトリを取得する 【質問】 Windowsディレクトリのパスを取得するにはどうしたらよいのでしょ うか?Environment.GetFolderPathメソッドでは取得できないようで すが...。 【回答】 まず考えられるのは、環境変数で定義されている"windir"の値を取得 する方法です。環境変数の値を取得するためには、Environment. GetEnvironmentVariableメソッドまたは、Environment. ExpandEnvironmentVariablesメソッドを使用します。 それぞれのメソッドを使ってWindowsディレクトリのパスを取得する 例を示します。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Dim windir As String 'GetEnvironmentVariableメソッドによりWindowsディレクトリを取得 windir = System.Environment.GetEnvironmentVariable("windir") Console.WriteLine(windir) 'ExpandEnvironmentVariablesメソッドによりWindowsディレクトリを取得 windir = System.Environment.ExpandEnvironmentVariables("%windir%") Console.WriteLine(windir) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ string windir; //GetEnvironmentVariableメソッドによりWindowsディレクトリを取得 windir = System.Environment.GetEnvironmentVariable("windir"); Console.WriteLine(windir); //ExpandEnvironmentVariablesメソッドによりWindowsディレクトリを取得 windir = System.Environment.ExpandEnvironmentVariables("%windir%"); Console.WriteLine(windir); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ また、Windowsディレクトリはシステムディレクトリの親ディレクト リであると考えれば、Environment.GetFolderPathメソッドを使って 次のようにできます。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'システムディレクトリを取得 Dim sysdir As String = _ System.Environment.GetFolderPath( _ System.Environment.SpecialFolder.System) 'Windowsディレクトリを取得 Dim windir As String = _ System.IO.Path.GetDirectoryName(sysdir) Console.WriteLine(windir) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //システムディレクトリを取得 string sysdir = System.Environment.GetFolderPath( System.Environment.SpecialFolder.System); //Windowsディレクトリを取得 string windir = System.IO.Path.GetDirectoryName(sysdir); Console.WriteLine(windir); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ また、Win32 APIのGetWindowsDirectory関数を使用する方法もありま す。 [URL]GetWindowsDirectory http://www.microsoft.com/japan/msdn/library/ja/jpsysinf/html/_win32_getwindowsdirectory.asp ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ _ Private Shared Function GetWindowsDirectory( _ ByVal buffer As String, _ ByVal length As Integer) As Integer End Function 'Windowsディレクトリを取得する Public Shared Function GetWindowsDirectoryPath() As String Dim buf As New String(" "c, 260) Dim len As Integer = GetWindowsDirectory(buf, 260) Return buf.Substring(0, len) End Function ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ [DllImport("kernel32", CharSet=CharSet.Auto, SetLastError=true)] private static extern uint GetWindowsDirectory( string buffer, uint length); //Windowsディレクトリを取得する public string GetWindowsDirectoryPath() { string buf = new string(' ', 260); uint len = GetWindowsDirectory(buf, 260); return buf.Substring(0, (int) len); } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 裏技として、ニュースグループ「microsoft.public.dotnet. languages.vb」では、Environment.GetFolderPathメソッドで直接取 得する方法が紹介されています。 [URL]Newsgroups:microsoft.public.dotnet.languages.vb Subject:Re: Get Windows Directory http://groups.google.de/groups?hl=ja&lr=&selm=eLs4xr7CBHA.1448%40tkmsftngp05 この記事によると、Environment.SpecialFolder列挙体のメンバに Windowsディレクトリに相当するものが見つからないため、一見 GetFolderPathメソッドではWindowsディレクトリのパスは取得できな いように思われますが、次のような方法によりそれが可能になるとい うことです。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ Dim windir As String 'Windowsディレクトリを取得 windir = System.Environment.GetFolderPath( _ CType(&H24, System.Environment.SpecialFolder)) Console.WriteLine(windir) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ string windir; //Windowsディレクトリを取得 windir = System.Environment.GetFolderPath( (System.Environment.SpecialFolder) 0x24); Console.WriteLine(windir); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ しかしこの方法は残念ながら私の環境(.NET Framework 1.1)では成功 しませんでした。現在はこの方法は使用できないようです。 ○この記事の基になった掲示板のスレッド [題名] システムディレクトリーの取得 [投稿者(敬称略)] よしね, comaru [URL] http://dobon.net/vb/bbs/log3-4/2158.html ─────────────────────────────── ●2つのフォルダの同期を行う 【質問】 フォルダをコピーする際に、コピー元のファイルがコピー先に存在し ないか、コピー先のファイルより新しい場合に限りコピーするように したいのですが、どうすればよいのでしょうか?また、2つのフォル ダのファイル構成が同じになるようにミラーリング(同期)したいの ですが、どうすればできますか? 【回答】 まずフォルダのコピーについては、私のサイトの「フォルダをコピー する」をご覧ください。 [URL]DOBON.NET .NET Tips - フォルダをコピーする http://dobon.net/vb/dotnet/file/copyfolder.html ここではこのコードに手を加えることにします。 まず、File.ExistsメソッドとFile.GetLastWriteTimeメソッドを使い、 コピー元のファイルがコピー先に存在しないか、またはコピー先のフ ァイルより新しいか調べ、ファイルをコピーするようにします。 さらにミラーリングをするために、コピー先にあってコピー元にない ファイルを探して削除するためのメソッド(ここでは "DeleteNotExistFiles")を作成します。 このようにして作成された新たなCopyDirectoryメソッドは次のよう なものです。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ''' ''' ディレクトリをコピーする ''' ''' コピーするディレクトリ ''' コピー先のディレクトリ ''' 新しいファイルのみコピーする ''' sourceDirNameにないファイルを削除する Public Shared Sub CopyDirectory(ByVal sourceDirName As String, _ ByVal destDirName As String, ByVal newerOnly As Boolean, _ ByVal sync As Boolean) 'コピー先のディレクトリがないときは作る If Not Directory.Exists(destDirName) Then Directory.CreateDirectory(destDirName) '属性もコピー File.SetAttributes(destDirName, File.GetAttributes(sourceDirName)) End If 'コピー先のディレクトリ名の末尾に"\"をつける If destDirName.Chars((destDirName.Length - 1)) <> _ Path.DirectorySeparatorChar Then destDirName = destDirName + Path.DirectorySeparatorChar End If 'コピー元のディレクトリにあるファイルをコピー Dim files As String() = Directory.GetFiles(sourceDirName) Dim f As String For Each f In files Dim destFileName As String = destDirName + Path.GetFileName(f) 'コピー先にファイルが存在し、 'コピー元より更新日時が古い時はコピーする If Not newerOnly Or _ Not File.Exists(destFileName) Or _ File.GetLastWriteTime(destFileName) < _ File.GetLastWriteTime(f) Then File.Copy(f, destFileName, True) End If Next f 'コピー先にあってコピー元にないファイルを削除 If sync Then DeleteNotExistFiles(sourceDirName, destDirName) End If 'コピー元のディレクトリにあるディレクトリについて、 '再帰的に呼び出す Dim dirs As String() = Directory.GetDirectories(sourceDirName) Dim dir As String For Each dir In dirs CopyDirectory(dir, destDirName + Path.GetFileName(dir), _ newerOnly, sync) Next dir End Sub ''' ''' destDirNameにありsourceDirNameにないファイルを削除する ''' ''' 比較先のフォルダ ''' 比較もとのフォルダ Private Shared Sub DeleteNotExistFiles( _ ByVal sourceDirName As String, ByVal destDirName As String) 'destDirNameにありsourceDirNameにないファイルを削除する Dim files As String() = Directory.GetFiles(destDirName) Dim f As String For Each f In files If Not File.Exists(Path.Combine( _ sourceDirName, Path.GetFileName(f))) Then File.Delete(f) End If Next f 'destDirNameにありsourceDirNameにないフォルダを削除する Dim folders As String() = Directory.GetDirectories(destDirName) Dim folder As String For Each folder In folders If Not Directory.Exists(Path.Combine( _ sourceDirName, Path.GetFileName(folder))) Then Directory.Delete(folder, True) End If Next folder End Sub ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ /// /// ディレクトリをコピーする /// /// コピーするディレクトリ /// コピー先のディレクトリ /// 新しいファイルのみコピーする /// sourceDirNameにないファイルを削除する public static void CopyDirectory( string sourceDirName, string destDirName, bool newerOnly, bool sync) { //コピー先のディレクトリがないときは作る if (!Directory.Exists(destDirName)) { Directory.CreateDirectory(destDirName); //属性もコピー File.SetAttributes(destDirName, File.GetAttributes(sourceDirName)); } //コピー先のディレクトリ名の末尾に"\"をつける if (destDirName[destDirName.Length - 1] != Path.DirectorySeparatorChar) destDirName = destDirName + Path.DirectorySeparatorChar; //コピー元のディレクトリにあるファイルをコピー string[] files = Directory.GetFiles(sourceDirName); foreach (string file in files) { string destFileName = destDirName + Path.GetFileName(file); //コピー先にファイルが存在し、 //コピー元より更新日時が古い時はコピーする if (!newerOnly || !File.Exists(destFileName) || File.GetLastWriteTime(destFileName) < File.GetLastWriteTime(file)) { File.Copy(file, destFileName, true); } } //コピー先にあってコピー元にないファイルを削除 if (sync) DeleteNotExistFiles( sourceDirName, destDirName); //コピー元のディレクトリにあるディレクトリについて、 //再帰的に呼び出す string[] dirs = Directory.GetDirectories(sourceDirName); foreach (string dir in dirs) CopyDirectory(dir, destDirName + Path.GetFileName(dir), newerOnly, sync); } /// /// destDirNameにありsourceDirNameにないファイルを削除する /// /// 比較先のフォルダ /// 比較もとのフォルダ private static void DeleteNotExistFiles( string sourceDirName, string destDirName) { //destDirNameにありsourceDirNameにないファイルを削除する string[] files = Directory.GetFiles(destDirName); foreach (string file in files) { if (!File.Exists(Path.Combine( sourceDirName, Path.GetFileName(file)))) File.Delete(file); } //destDirNameにありsourceDirNameにないフォルダを削除する string[] folders = Directory.GetDirectories(destDirName); foreach (string folder in folders) { if (!Directory.Exists(Path.Combine( sourceDirName, Path.GetFileName(folder)))) Directory.Delete(folder, true); } } ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 次に使用法を示します。フォルダ"C:\test1"内の更新されたファイル をフォルダ"C:\test2"にコピーし、ミラーリングを行うには、次のよ うにします。 ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ CopyDirectory("C:\test1", "C:\test2", True, True) ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ CopyDirectory("C:\\test1", "C:\\test2", true, true); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ○この記事の基になった掲示板のスレッド [題名] 更新されたファイルのみコピーするには? [投稿者(敬称略)] BELL, 琴 [URL] http://dobon.net/vb/bbs/log3-4/2164.html ─────────────────────────────── ●Visual Basic .NET, Visual C# Standard Editionでクラスライブラリ  を作成する 【質問】 Visual Basic .NET Standard Edition(または、Visual C# Standard Edition)を使っているのですが、クラスライブラリを作成できませ ん。Standard Editionではクラスライブラリの作成ができないのでし ょうか? 【回答】 (注:私はStandard Editionを使用したことがありませんので、ここ で紹介する事柄は私が直接確認を取ったわけではないことをご了承く ださい。) Visual Basic .NET Standard Edition(または、Visual C# Standard Edition)では「新しいプロジェクト」ダイアログに「クラスライブ ラリ」が表示されないため、Standard Editionではクラスライブラリ を作成することができないように思われます。しかし掲示板でよねK ENさんに紹介していただいたように、プロジェクトファイルを書き 換えればStandard Editionでもクラスライブラリを作成できます。 具体的な方法は、まずプロジェクトのプロジェクトファイル ("*.vbproj"または"*.csproj")をテキストエディタで開き、 OutputType = "???" ("???"は"WinExe"や"Exe"など)となっているところを OutputType = "Library" と書き換えるだけです。 また、この書き換えを行うためのマクロが「Planet Source Code」で 公開されています。 [URL]Create libraries (.dll) with VB.NET - Standard Edition http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=517&lngWId=10 このような書き換えが製品のライセンス等に違反しないかについて私 は断言することはできませんが、問題があるという話を聞いたことが ありませんので、大丈夫ではないかと思われます。 ○この記事の基になった掲示板のスレッド [題名] 自作のコントロールを別のプロジェクトで使用するには? [投稿者(敬称略)] yuyu, dt, よねKEN [URL] http://dobon.net/vb/bbs/log3-4/2177.html ─────────────────────────────── ●印刷プレビューダイアログの表示位置や表示倍率を指定する 【質問】 PrintPreviewDialogクラスにより印刷プレビューダイアログを表示さ せるときに、ダイアログの表示位置や表示倍率を指定することはでき るでしょうか? 【回答】 PrintPreviewDialogクラスはFormクラスから派生していますので、基 本的にはFormクラスと同様の方法で表示位置を指定することができま す。フォームの位置を指定する方法に関して詳しくは、「フォームの 位置と大きさを変更する」をご覧ください。 [URL]フォームの位置と大きさを変更する http://dobon.net/vb/dotnet/form/setdesktoplocation.html また表示倍率は、PrintPreviewDialogで使われている PrintPreviewControlオブジェクトを取得し、そのZoomプロパティを 設定することにより変更が可能です。PrintPreviewControlオブジェ クトはPrintPreviewDialog.PrintPreviewControlプロパティで取得で きます(このプロパティはヘルプでは「このメンバは、.NET Framework インフラストラクチャのサポートを目的としています。独 自に作成したコード内で直接使用することはできません。」と書かれ ています)。 表示位置(及びサイズ)と表示倍率を指定して印刷プレビューダイア ログを表示する例を示します。ここでは、PrintDocument.PrintPage イベントハンドラ"pd_PrintPage"は省略しています。省略しないコー ドに関しては、「印刷プレビューを表示する」を参考にしてください。 [URL]印刷プレビューを表示する http://dobon.net/vb/dotnet/graphics/printpreviewdialog.html ‥‥▽VB.NET ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ 'PrintDocumentオブジェクトの作成 Dim pd As New System.Drawing.Printing.PrintDocument 'PrintPageイベントハンドラの追加 AddHandler pd.PrintPage, AddressOf pd_PrintPage 'PrintPreviewDialogオブジェクトの作成 Dim ppd As New PrintPreviewDialog 'はじめの表示位置を指定する ppd.StartPosition = FormStartPosition.Manual ppd.SetBounds(0, 0, 400, 400) '表示倍率を2倍にする ppd.PrintPreviewControl.Zoom = 2 'プレビューするPrintDocumentを設定 ppd.Document = pd '印刷プレビューダイアログを表示する ppd.ShowDialog() ‥‥△VB.NET ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ‥‥▽C# ここから▽‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ //PrintDocumentオブジェクトの作成 System.Drawing.Printing.PrintDocument pd = new System.Drawing.Printing.PrintDocument(); //PrintPageイベントハンドラの追加 pd.PrintPage += new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage); //PrintPreviewDialogオブジェクトの作成 PrintPreviewDialog ppd = new PrintPreviewDialog(); //はじめの表示位置を指定する ppd.StartPosition = FormStartPosition.Manual; ppd.SetBounds(0, 0, 400, 400); //表示倍率を2倍にする ppd.PrintPreviewControl.Zoom = 2; //プレビューするPrintDocumentを設定 ppd.Document = pd; //印刷プレビューダイアログを表示する ppd.ShowDialog(); ‥‥△C# ここまで△‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥‥ ○この記事の基になった掲示板のスレッド [題名] 印刷プレビュー画面のサイズについて [投稿者(敬称略)] りょう, ピラルク [URL] http://dobon.net/vb/bbs/log3-4/2259.html [題名] 標準の印刷プレビューダイアログについて [投稿者(敬称略)] りょう, 深山 [URL] http://dobon.net/vb/bbs/log3-4/2312.html =============================== ■ここで示したコードの多くはまずC#で書き、それを「C# to VB.NET Translator」でVB.NETのコードに変換し、修正を加えたものです。 [URL]C# to VB.NET Translator http://authors.aspalliance.com/aldotnet/examples/translate.aspx ■このマガジンの購読、購読中止、バックナンバー、説明に関しては  次のページをご覧ください。  http://www.mag2.com/m/0000104516.htm ■発行人・編集人:どぼん!  (Microsoft MVP for Visual Basic, Oct 2004-Oct 2005)  http://dobon.net  dobon_info@yahoo.co.jp ■ご質問等はメールではなく、掲示板へお願いいたします。  http://dobon.net/vb/bbs.html ■上記メールアドレスへのメールは確実に読まれる保障はありません  (スパム、ウィルス対策です)。メールは下記URLのフォームメール  から送信してください。  http://dobon.net/mail.html Copyright (c) 2003 - 2004 DOBON! All rights reserved. ===============================