.NETプログラミング研究 第48号 †
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。
Windowsディレクトリを取得する †
【質問】
Windowsディレクトリのパスを取得するにはどうしたらよいのでしょうか?Environment.GetFolderPathメソッドでは取得できないようですが...。
【回答】
まず考えられるのは、環境変数で定義されている"windir"の値を取得する方法です。環境変数の値を取得するためには、Environment.GetEnvironmentVariableメソッドまたは、Environment.ExpandEnvironmentVariablesメソッドを使用します。
それぞれのメソッドを使ってWindowsディレクトリのパスを取得する例を示します。
1
2
3
4
5
6
7
8
9
| | Dim windir As String
windir = System.Environment.GetEnvironmentVariable("windir")
Console.WriteLine(windir)
windir = System.Environment.ExpandEnvironmentVariables("%windir%")
Console.WriteLine(windir)
|
1
2
3
4
5
6
7
8
9
10
11
| | string windir;
windir =
System.Environment.GetEnvironmentVariable("windir");
Console.WriteLine(windir);
windir =
System.Environment.ExpandEnvironmentVariables("%windir%");
Console.WriteLine(windir);
|
また、Windowsディレクトリはシステムディレクトリの親ディレクトリであると考えれば、Environment.GetFolderPathメソッドを使って次のようにできます。
1
2
3
4
5
6
7
8
| | Dim sysdir As String = _
System.Environment.GetFolderPath( _
System.Environment.SpecialFolder.System)
Dim windir As String = _
System.IO.Path.GetDirectoryName(sysdir)
Console.WriteLine(windir)
|
1
2
3
4
5
6
7
8
| | string sysdir =
System.Environment.GetFolderPath(
System.Environment.SpecialFolder.System);
string windir =
System.IO.Path.GetDirectoryName(sysdir);
Console.WriteLine(windir);
|
また、Win32 APIのGetWindowsDirectory関数を使用する方法もあります。
1
2
3
4
5
6
7
8
9
10
11
12
| | <DllImport("kernel32", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetWindowsDirectory( _
ByVal buffer As String, _
ByVal length As Integer) As Integer
End Function
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
|
1
2
3
4
5
6
7
8
9
10
11
| | [DllImport("kernel32", CharSet=CharSet.Auto, SetLastError=true)]
private static extern uint GetWindowsDirectory(
string buffer, uint length);
public string GetWindowsDirectoryPath()
{
string buf = new string(' ', 260);
uint len = GetWindowsDirectory(buf, 260);
return buf.Substring(0, (int) len);
}
|
裏技として、ニュースグループ「microsoft.public.dotnet.languages.vb」では、Environment.GetFolderPathメソッドで直接取得する方法が紹介されています。
この記事によると、Environment.SpecialFolder列挙体のメンバにWindowsディレクトリに相当するものが見つからないため、一見GetFolderPathメソッドではWindowsディレクトリのパスは取得できないように思われますが、次のような方法によりそれが可能になるということです。
1
2
3
4
5
6
| | Dim windir As String
windir = System.Environment.GetFolderPath( _
CType(&H24, System.Environment.SpecialFolder))
Console.WriteLine(windir)
|
1
2
3
4
5
6
7
| | string windir;
windir =
System.Environment.GetFolderPath(
(System.Environment.SpecialFolder) 0x24);
Console.WriteLine(windir);
|
しかしこの方法は残念ながら私の環境では(.NET Framework 1.1)成功しませんでした。現在はこの方法は使用できないようです。
○この記事の基になった掲示板のスレッド
2つのフォルダの同期を行う †
【質問】
フォルダをコピーする際に、コピー元のファイルがコピー先に存在しないか、コピー先のファイルより新しい場合に限りコピーするようにしたいのですが、どうすればよいのでしょうか?また、2つのフォルダのファイル構成が同じになるようにミラーリング(同期)したいのですが、どうすればできますか?
【回答】
まずフォルダのコピーについては、私のサイトの「フォルダをコピーする」をご覧ください。
ここではこのコードに手を加えることにします。
まず、File.ExistsメソッドとFile.GetLastWriteTimeメソッドを使い、コピー元のファイルがコピー先に存在しないか、またはコピー先のファイルより新しいか調べ、ファイルをコピーするようにします。
さらにミラーリングをするために、コピー先にあってコピー元にないファイルを探して削除するためのメソッド(ここでは"DeleteNotExistFiles")を作成します。
このようにして作成された新たなCopyDirectoryメソッドは次のようなものです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
| | 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
Private Shared Sub DeleteNotExistFiles( _
ByVal sourceDirName As String, ByVal destDirName As String)
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
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
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
| | 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);
}
private static void DeleteNotExistFiles(
string sourceDirName, string destDirName)
{
string[] files = Directory.GetFiles(destDirName);
foreach (string file in files)
{
if (!File.Exists(Path.Combine(
sourceDirName, Path.GetFileName(file))))
File.Delete(file);
}
string[] folders = Directory.GetDirectories(destDirName);
foreach (string folder in folders)
{
if (!Directory.Exists(Path.Combine(
sourceDirName, Path.GetFileName(folder))))
Directory.Delete(folder, true);
}
}
|
次に使用法を示します。フォルダ"C:\test1"内の更新されたファイルをフォルダ"C:\test2"にコピーし、ミラーリングを行うには、次のようにします。
1
| | CopyDirectory("C:\test1", "C:\test2", True, True)
|
1
| | CopyDirectory("C:\\test1", "C:\\test2", true, true);
|
○この記事の基になった掲示板のスレッド
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ではクラスライブラリを作成することができないように思われます。しかし掲示板でよねKENさんに紹介していただいたように、プロジェクトファイルを書き換えればStandard Editionでもクラスライブラリを作成できます。
具体的な方法は、まずプロジェクトのプロジェクトファイル("*.vbproj"または"*.csproj")をテキストエディタで開き、
OutputType = "???"
("???"は"WinExe"や"Exe"など)となっているところを
OutputType = "Library"
と書き換えるだけです。
また、この書き換えを行うためのマクロが「Planet Source Code」で公開されています。
このような書き換えが製品のライセンス等に違反しないかについて私は断言することはできませんが、問題があるという話を聞いたことがありませんので、大丈夫ではないかと思われます。
○この記事の基になった掲示板のスレッド
印刷プレビューダイアログの表示位置や表示倍率を指定する †
【質問】
PrintPreviewDialogクラスにより印刷プレビューダイアログを表示させるときに、ダイアログの表示位置や表示倍率を指定することはできるでしょうか?
【回答】
PrintPreviewDialogクラスはFormクラスから派生していますので、基本的にはFormクラスと同様の方法で表示位置を指定することができます。フォームの位置を指定する方法に関して詳しくは、「フォームの位置と大きさを変更する」をご覧ください。
また表示倍率は、PrintPreviewDialogで使われているPrintPreviewControlオブジェクトを取得し、そのZoomプロパティを設定することにより変更が可能です。PrintPreviewControlオブジェクトはPrintPreviewDialog.PrintPreviewControlプロパティで取得できます(このプロパティはヘルプでは「このメンバは、.NET Framework インフラストラクチャのサポートを目的としています。独自に作成したコード内で直接使用することはできません。」と書かれています)。
表示位置(及びサイズ)と表示倍率を指定して印刷プレビューダイアログを表示する例を示します。ここでは、PrintDocument.PrintPageイベントハンドラ"pd_PrintPage"は省略しています。省略しないコードに関しては、「印刷プレビューを表示する」を参考にしてください。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| | Dim pd As New System.Drawing.Printing.PrintDocument
AddHandler pd.PrintPage, AddressOf pd_PrintPage
Dim ppd As New PrintPreviewDialog
ppd.StartPosition = FormStartPosition.Manual
ppd.SetBounds(0, 0, 400, 400)
ppd.PrintPreviewControl.Zoom = 2
ppd.Document = pd
ppd.ShowDialog()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| | System.Drawing.Printing.PrintDocument pd =
new System.Drawing.Printing.PrintDocument();
pd.PrintPage +=
new System.Drawing.Printing.PrintPageEventHandler(pd_PrintPage);
PrintPreviewDialog ppd = new PrintPreviewDialog();
ppd.StartPosition = FormStartPosition.Manual;
ppd.SetBounds(0, 0, 400, 400);
ppd.PrintPreviewControl.Zoom = 2;
ppd.Document = pd;
ppd.ShowDialog();
|
○この記事の基になった掲示板のスレッド
コメント †