.NETプログラミング研究 第49号 †
明けましておめでとうございます。今年も「.NETプログラミング研究」をよろしくお願いいたします。
ピンポイントリンク †
ZIP書庫の作成、閲覧、展開を行う †
ここでは、C#またはVB.NETでZip書庫を扱う(作成、閲覧、展開)方法を紹介します。
C#、VB.NETには標準ではZipを扱う方法が用意されていませんが、J#にはvjslib.dllというものがあり、この中のjava.util.zip名前空間にあるクラスを使うことにより、Zipを扱うことが出来ます。
この方法は、次のURL先で詳しく紹介されています。
この方法によりZip書庫を扱う具体的なコードを以下に示します。必ずvjslib.dllを参照に加えてください。(後ほど述べますが、現在この方法には多くの問題がありますので、ご注意ください。)
まずはZip書庫内のファイル(エントリ)の情報を列挙するコードです。
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
| | Dim zipPath As String = "C:\test.zip"
Dim fis As New java.io.FileInputStream(zipPath)
Dim zis As New java.util.zip.ZipInputStream(fis)
While True
Dim ze As java.util.zip.ZipEntry = zis.getNextEntry()
If (ze Is Nothing) Then
Exit While
End If
If Not ze.isDirectory() Then
Console.WriteLine("ファイル名 : {0}", ze.getName())
Console.WriteLine("サイズ : {0} bytes", ze.getSize())
Console.WriteLine("圧縮サイズ : {0} bytes", _
ze.getCompressedSize())
Console.WriteLine("CRC : {0:X}", ze.getCrc())
Dim [date] As New java.util.Date(ze.getTime())
Console.WriteLine("日時 : {0}", [date].ToString())
Console.WriteLine()
End If
End While
zis.close()
fis.close()
|
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
| | string zipPath = "C:\\test.zip";
java.io.FileInputStream fis =
new java.io.FileInputStream(zipPath);
java.util.zip.ZipInputStream zis =
new java.util.zip.ZipInputStream(fis);
java.util.zip.ZipEntry ze;
while ((ze = zis.getNextEntry()) != null)
{
if (!ze.isDirectory())
{
Console.WriteLine("ファイル名 : {0}", ze.getName());
Console.WriteLine("サイズ : {0} bytes", ze.getSize());
Console.WriteLine("圧縮サイズ : {0} bytes",
ze.getCompressedSize());
Console.WriteLine("CRC : {0:X}", ze.getCrc());
java.util.Date date = new java.util.Date(ze.getTime());
Console.WriteLine("日時 : {0}", date.ToString());
Console.WriteLine();
}
}
zis.close();
fis.close();
|
次はZip書庫内のファイルを展開するコードです。ここでは書庫内のすべてのファイルを展開しています。
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
| | Dim zipPath As String = "C:\test.zip"
Dim extractDir As String = "C:\temp"
Dim fis As New java.io.FileInputStream(zipPath)
Dim zis As New java.util.zip.ZipInputStream(fis)
While True
Dim ze As java.util.zip.ZipEntry = zis.getNextEntry()
If ze Is Nothing Then
Exit While
End If
If Not ze.isDirectory() Then
Dim fileName As String = _
System.IO.Path.GetFileName(ze.getName())
Dim destDir As String = System.IO.Path.Combine( _
extractDir, System.IO.Path.GetDirectoryName(ze.getName()))
System.IO.Directory.CreateDirectory(destDir)
Dim destPath As String = _
System.IO.Path.Combine(destDir, fileName)
Dim fos As New java.io.FileOutputStream(destPath)
Dim buffer(8191) As System.SByte
While True
Dim len As Integer = zis.read(buffer, 0, buffer.Length)
If len <= 0 Then
Exit While
End If
fos.write(buffer, 0, len)
End While
fos.close()
End If
End While
zis.close()
fis.close()
|
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
| | string zipPath = "C:\\test.zip";
string extractDir = "C:\\temp";
java.io.FileInputStream fis = new java.io.FileInputStream(zipPath);
java.util.zip.ZipInputStream zis =
new java.util.zip.ZipInputStream(fis);
java.util.zip.ZipEntry ze;
while ((ze = zis.getNextEntry()) != null)
{
if (!ze.isDirectory())
{
string fileName = System.IO.Path.GetFileName(ze.getName());
string destDir = System.IO.Path.Combine(
extractDir, System.IO.Path.GetDirectoryName(ze.getName()));
System.IO.Directory.CreateDirectory(destDir);
string destPath = System.IO.Path.Combine(destDir, fileName);
java.io.FileOutputStream fos =
new java.io.FileOutputStream(destPath);
sbyte[] buffer = new sbyte[8192];
int len;
while ((len = zis.read(buffer, 0, buffer.Length)) > 0)
{
fos.write(buffer, 0, len);
}
fos.close();
}
}
zis.close();
fis.close();
|
最後は指定したファイルを圧縮してZip書庫を作成するコードです。
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
| | Dim zipPath As String = "C:\test.zip"
Dim filePaths As String() = _
{ _
"C:\1.bmp", _
"C:\2.bmp" _
}
Dim fos As New java.io.FileOutputStream(zipPath)
Dim zos As New java.util.zip.ZipOutputStream(fos)
Dim file As String
For Each file In filePaths
Dim f As String = System.IO.Path.GetFileName(file)
Dim ze As New java.util.zip.ZipEntry(f)
ze.setMethod(java.util.zip.ZipEntry.DEFLATED)
zos.putNextEntry(ze)
Dim fis As New java.io.FileInputStream(file)
Dim buffer(8191) As System.SByte
While True
Dim len As Integer = fis.read(buffer, 0, buffer.Length)
If len <= 0 Then
Exit While
End If
zos.write(buffer, 0, len)
End While
fis.close()
zos.closeEntry()
Next file
zos.close()
fos.close()
|
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
| | string zipPath = "C:\\test.zip";
string[] filePaths =
{
"C:\\1.bmp",
"C:\\2.bmp"
};
java.io.FileOutputStream fos = new java.io.FileOutputStream(zipPath);
java.util.zip.ZipOutputStream zos =
new java.util.zip.ZipOutputStream(fos);
foreach (string file in filePaths)
{
string f = System.IO.Path.GetFileName(file);
java.util.zip.ZipEntry ze = new java.util.zip.ZipEntry(f);
ze.setMethod(java.util.zip.ZipEntry.DEFLATED);
zos.putNextEntry(ze);
java.io.FileInputStream fis = new java.io.FileInputStream(file);
sbyte[] buffer = new sbyte[8192];
int len;
while((len = fis.read(buffer, 0, buffer.Length)) > 0)
{
zos.write(buffer, 0, len);
}
fis.close();
zos.closeEntry();
}
zos.close();
fos.close();
|
このようにjava.util.zip内のクラスを使うと簡単にZipの扱いができるようになりますが、現在のバージョン(.NET Framework 1.1)では、このやり方は非常に多くの問題があることが分かっています(2.0では修正されるようです)。例えば、上記の方法でパス名に日本語を含むファイルを圧縮するとZip書庫は正常に作成されません。また、ZipEntry.setTimeで日時を指定できません。さらに、先に紹介した「Using the Zip Classes in the J# Class Libraries to Compress Files and Data with C#」のサンプルでは、サイズの大きいバイナリファイルを圧縮すると、エラーが発生します(new ZipFileでエラーが発生しますが、上記のコードではこれを使用していません)。これらのバグについては、次のURL先が参考になります。
さらに別の方法として、Zipを扱う有名なライブラリである#ziplibを使う方法があります。(現在のバージョンは0.81。)
#ziplibを使ってZipを扱うサンプルを以下に紹介します。まずはZip内のファイルを列挙するコードから。先ほどのjava.util.zipのサンプルと似ていますね。
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
| | Dim zipPath As String = "C:\test.zip"
Dim fs As New System.IO.FileStream( _
zipPath, System.IO.FileMode.Open, _
System.IO.FileAccess.Read, System.IO.FileShare.Read)
Dim zis As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs)
Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry
While True
ze = zis.GetNextEntry()
If ze Is Nothing Then
Exit While
End If
If Not ze.IsDirectory Then
Console.WriteLine("ファイル名 : {0}", ze.Name)
Console.WriteLine("サイズ : {0} bytes", ze.Size)
Console.WriteLine("圧縮サイズ : {0} bytes", _
ze.CompressedSize)
Console.WriteLine("CRC : {0:X}", ze.Crc)
Console.WriteLine("日時 : {0}", ze.DateTime)
Console.WriteLine()
End If
End While
zis.Close()
fs.Close()
|
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
| | string zipPath = "C:\\test.zip";
System.IO.FileStream fs = new System.IO.FileStream(
zipPath, System.IO.FileMode.Open,
System.IO.FileAccess.Read, System.IO.FileShare.Read);
ICSharpCode.SharpZipLib.Zip.ZipInputStream zis =
new ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs);
ICSharpCode.SharpZipLib.Zip.ZipEntry ze;
while ((ze = zis.GetNextEntry()) != null)
{
if (!ze.IsDirectory)
{
Console.WriteLine("ファイル名 : {0}", ze.Name);
Console.WriteLine("サイズ : {0} bytes", ze.Size);
Console.WriteLine("圧縮サイズ : {0} bytes",
ze.CompressedSize);
Console.WriteLine("CRC : {0:X}", ze.Crc);
Console.WriteLine("日時 : {0}", ze.DateTime);
Console.WriteLine();
}
}
zis.Close();
fs.Close();
|
次はZip書庫内のすべてのファイルを展開するコードです。これもjava.util.zipのサンプルと似ています。
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
| | Dim zipPath As String = "C:\test.zip"
Dim extractDir As String = "C:\temp"
Dim fs As New System.IO.FileStream( _
zipPath, System.IO.FileMode.Open, _
System.IO.FileAccess.Read, System.IO.FileShare.Read)
Dim zis As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs)
Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry
While True
ze = zis.GetNextEntry()
If ze Is Nothing Then
Exit While
End If
If Not ze.IsDirectory Then
Dim fileName As String = System.IO.Path.GetFileName(ze.Name)
Dim destDir As String = System.IO.Path.Combine( _
extractDir, System.IO.Path.GetDirectoryName(ze.Name))
System.IO.Directory.CreateDirectory(destDir)
Dim destPath As String = _
System.IO.Path.Combine(destDir, fileName)
Dim writer As New System.IO.FileStream( _
destPath, System.IO.FileMode.Create, _
System.IO.FileAccess.Write, System.IO.FileShare.Write)
Dim buffer(2047) As Byte
Dim len As Integer
While True
len = zis.Read(buffer, 0, buffer.Length)
If len <= 0 Then
Exit While
End If
writer.Write(buffer, 0, len)
End While
writer.Close()
End If
End While
zis.Close()
fs.Close()
|
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
| | string zipPath = "C:\\test.zip";
string extractDir = "C:\\temp";
System.IO.FileStream fs = new System.IO.FileStream(
zipPath, System.IO.FileMode.Open,
System.IO.FileAccess.Read, System.IO.FileShare.Read);
ICSharpCode.SharpZipLib.Zip.ZipInputStream zis =
new ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs);
ICSharpCode.SharpZipLib.Zip.ZipEntry ze;
while ((ze = zis.GetNextEntry()) != null)
{
if (!ze.IsDirectory)
{
string fileName = System.IO.Path.GetFileName(ze.Name);
string destDir = System.IO.Path.Combine(extractDir,
System.IO.Path.GetDirectoryName(ze.Name));
System.IO.Directory.CreateDirectory(destDir);
string destPath = System.IO.Path.Combine(destDir, fileName);
System.IO.FileStream writer = new System.IO.FileStream(
destPath, System.IO.FileMode.Create,
System.IO.FileAccess.Write,
System.IO.FileShare.Write);
byte[] buffer = new byte[2048];
int len;
while ((len = zis.Read(buffer, 0, buffer.Length)) > 0)
{
writer.Write(buffer, 0, len);
}
writer.Close();
}
}
zis.Close();
fs.Close();
|
最後にファイルを圧縮してZip書庫を作成するサンプルです。ここではヘッダでエントリの情報を設定していますが、これを省略すると、フッタに書き込まれます。
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
| | Dim zipPath As String = "C:\test.zip"
Dim filePaths As String() = _
{ _
"C:\1.txt", _
"D:\2.txt" _
}
Dim crc As New ICSharpCode.SharpZipLib.Checksums.Crc32
Dim writer As New System.IO.FileStream( _
zipPath, System.IO.FileMode.Create, _
System.IO.FileAccess.Write, System.IO.FileShare.Write)
Dim zos As New ICSharpCode.SharpZipLib.Zip.ZipOutputStream(writer)
zos.SetLevel(6)
Dim file As String
For Each file In filePaths
Dim f As String = System.IO.Path.GetFileName(file)
Dim ze As New ICSharpCode.SharpZipLib.Zip.ZipEntry(f)
Dim fs As New System.IO.FileStream( _
file, System.IO.FileMode.Open, _
System.IO.FileAccess.Read, System.IO.FileShare.Read)
Dim buffer(fs.Length) As Byte
fs.Read(buffer, 0, buffer.Length)
fs.Close()
crc.Reset()
crc.Update(buffer)
ze.Crc = crc.Value
ze.Size = buffer.Length
ze.DateTime = DateTime.Now
zos.PutNextEntry(ze)
zos.Write(buffer, 0, buffer.Length)
Next file
zos.Close()
writer.Close()
|
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
| | string zipPath = "C:\\test.zip";
string[] filePaths =
{
@"C:\test1.txt",
@"D:\Download\mt.txt"
};
ICSharpCode.SharpZipLib.Checksums.Crc32 crc =
new ICSharpCode.SharpZipLib.Checksums.Crc32();
System.IO.FileStream writer = new System.IO.FileStream(
zipPath, System.IO.FileMode.Create,
System.IO.FileAccess.Write,
System.IO.FileShare.Write);
ICSharpCode.SharpZipLib.Zip.ZipOutputStream zos =
new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(writer);
zos.SetLevel(6);
foreach (string file in filePaths)
{
string f = System.IO.Path.GetFileName(file);
ICSharpCode.SharpZipLib.Zip.ZipEntry ze =
new ICSharpCode.SharpZipLib.Zip.ZipEntry(f);
System.IO.FileStream fs = new System.IO.FileStream(
file,
System.IO.FileMode.Open,
System.IO.FileAccess.Read,
System.IO.FileShare.Read);
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
crc.Reset();
crc.Update(buffer);
ze.Crc = crc.Value;
ze.Size = buffer.Length;
ze.DateTime = DateTime.Now;
zos.PutNextEntry(ze);
zos.Write(buffer, 0, buffer.Length);
}
zos.Close();
writer.Close();
|
これ以外の方法では、アンマネージDLLを使う方法があります。これに関しては、次のURL先をご覧ください。
.NET質問箱 †
「.NET質問箱」では、「どぼん!のプログラミング掲示板」に書き込まれた.NETプログラミングに関する投稿を基に、さらに考察を加え、Q&A形式にまとめて紹介します。
マルチTIFFやアニメーションGIFに含まれるすべてのイメージを表示するには? †
【質問】
マルチTIFFやアニメーションGIFのように複数のイメージを含んだ画像ファイルからすべてのイメージを取り出して表示したいのですが、どのようにすればよいですか?
【回答】
これには、Image.SelectActiveFrameメソッドでアクティブなフレームを変更し、すべてのフレームのイメージを表示するようにすればよいでしょう。
まず、複数のイメージを含んだ画像ファイルをImageオブジェクトに読み込みます。そして、Image.FrameDimensionsListプロパティにより、フレームのディメンションGUIDを取得し、これからFrameDimensionオブジェクトを作成します。これを使用して、SelectActiveFrameメソッドを呼び出します。
以下にアニメーションGIFファイル(C:\test.gif)内のすべてのイメージをPictureBox(PictureBox1)に表示するサンプルを示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | Dim filePath As String = "C:\test.gif"
Dim g As Graphics = PictureBox1.CreateGraphics()
Dim img As Image = Image.FromFile(filePath)
Dim fd As New FrameDimension(img.FrameDimensionsList(0))
Dim fd_count As Integer = img.GetFrameCount(fd)
Dim y As Integer = 0
Dim i As Integer
For i = 0 To fd_count - 1
img.SelectActiveFrame(fd, i)
g.DrawImage(img, 0, y, img.Width, img.Height)
y += img.Height
Next i
img.Dispose()
g.Dispose()
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| | string filePath = "C:\\test.gif";
Graphics g = PictureBox1.CreateGraphics();
Image img = Image.FromFile(filePath);
FrameDimension fd = new FrameDimension(img.FrameDimensionsList[0]);
int fd_count = img.GetFrameCount(fd);
int y = 0;
for (int i = 0; i < fd_count; i++)
{
img.SelectActiveFrame(fd, i);
g.DrawImage(img, 0, y, img.Width, img.Height);
y += img.Height;
}
img.Dispose();
g.Dispose();
|
○この記事の基になった掲示板のスレッド
TextBoxコントロールのEnabledプロパティをFalseにしても前景色と背景色を変えないようにするには? †
【質問】
System.Windows.Forms.TextBoxコントロールのEnabledプロパティをFalseにすると、文字の色と背景が灰色になりますが、EnabledプロパティをFalseにしても文字の色と背景色が変わらないようにできませんか?
【回答】
TextBoxの背景色だけであれば、EnabledプロパティをFalseにした後、BackColorプロパティを設定するだけで、色を変更できます。ただし、ForeColorはこの方法では変更できないようです。(.NET Framework 1.1)
1
2
3
| | TextBox1.Enabled = False
TextBox1.BackColor = TextBox1.BackColor
|
1
2
3
| | TextBox1.Enabled = false;
TextBox1.BackColor = TextBox1.BackColor;
|
TextBoxの前景色を変えないようにするには、EnabledプロパティをFalseにする代わりに、ReadOnlyプロパティをTrueにするという方法があります。ReadOnlyプロパティをTrueにしても背景色は灰色となりますが、上の例と同様にBackColorプロパティを設定すれば、背景色を変えなくすることもできます。
ReadOnlyを使うのではなく、どうしてもEnabledプロパティをFalseにしても前景色と背景色が変わらないようにしたいということであれば、TextBoxクラスから派生したクラスを作成し、OnPaintメソッドをオーバーライドすることにより自分で文字列を描画するという方法があります。
この方法については、「Windows Forms FAQ」で紹介されています。
しかし残念ながらここに紹介されている方法では、TextBoxのEnabledがTrueの時に別のフォント(システムフォント?)でTextが表示されてしまいます。
これを防ぐには、TextBoxのEnabledがFalseの時だけ自分で描画するようにします。次の例ではOnEnabledChangedメソッドをオーバーライドすることにより、EnabledがTrueの時のみ独自に描画するようにしています。このクラスはTextAlignやRightToLeftなどのプロパティを一切無視していますので、あくまで参考としてご覧ください。
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
| | Class MyTextBox
Inherits TextBox
Protected Overrides Sub OnEnabledChanged(ByVal e As EventArgs)
MyBase.OnEnabledChanged(e)
If Me.Enabled Then
Me.SetStyle(ControlStyles.UserPaint, False)
Else
Me.SetStyle(ControlStyles.UserPaint, True)
End If
Me.Invalidate()
End Sub
Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
MyBase.OnPaint(e)
Dim b As New System.Drawing.SolidBrush(Me.ForeColor)
e.Graphics.DrawString(Me.Text, Me.Font, b, -1, 1)
b.Dispose()
End Sub
End Class
|
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
| | class MyTextBox : TextBox
{
protected override void OnEnabledChanged(EventArgs e)
{
base.OnEnabledChanged(e);
if (this.Enabled)
this.SetStyle(ControlStyles.UserPaint, false);
else
this.SetStyle(ControlStyles.UserPaint, true);
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
System.Drawing.Brush b =
new System.Drawing.SolidBrush(this.ForeColor);
e.Graphics.DrawString(this.Text, this.Font, b, -1, 1);
b.Dispose();
}
}
|
このような方法を無理して使うよりも、できることならReadOnlyプロパティを使うか、別の持った簡単な方法を考える方がよいと思います(下記URL参照)。
○この記事の基になった掲示板のスレッド
ページ情報 | - 作成日 : 2010-03-16 (火) 23:15:53
- 作成者 : DOBON!
- 最終編集日 : 2010-03-16 (火) 23:15:53
- 最終編集者 : DOBON!
|
|