はじめに
僕は以前 Delphi でファイルの日時を変更する Windows アプリを作りました。
[簡単エクスプローラ拡張 EzExpEx]
(http://hp.vector.co.jp/authors/VA029585/EzExpEx/)
これは残念なことにユニコードの名称のファイルを扱えないんですよね。
最新の Delphi でビルドし直せばいいのですが、僕が持っている Delphi ではだめなんです。
これを VB.NET で作り直したいとずっと思っていました。
僕のアプリが扱えるファイルの日時は
・ファイル自体の作成日時、更新日時
・Exif 情報の撮影日時、更新日時
・PDF ファイルの作成日時、更新日時
・概要情報の作成日時、更新日時
ファイル自体の日時を変更するのは .NET の標準機能で対応できますが、PDF ファイルの日時などはどうでしょう。
PDF ファイルの日時を変更する
PDF ファイルは、Windows が管理するファイルそのものの日時と別に、ファイルの内容に作成日時、更新日時を持っています。
PDF ファイルが持っている日時は、.NET の標準機能で変更できません。
PDF ファイルを扱うライブラリが多く作られています。例えば
・[PDFsharp - A .NET library for processing PDF]
(http://www.pdfsharp.com/PDFsharp/)
・[iTextSharp]
(https://github.com/itext/itextsharp)
・[iText for .NET]
(https://github.com/itext/itext7-dotnet)
これは楽でいいのですが、この方法で PDF ファイルを変更すると、読込時点でデコードされ保存時点でエンコードし直されるようです。
僕が Delphi で作ったアプリは、メタデータだけ読取して変更するようにしていました。
これを VB.NET で書き直しました。C# のコードも用意しました。
PDF ファイルの日時を取得する
Dim Path As String = "●●●●.jpg" '対象とするファイルのパス
Dim CreationTime As DateTime '作成日時を返す
Dim CreationValid As Boolenan = False '作成日時を取得できたか返す
Dim ModTime As DateTime '更新日時を返す
Dim ModValid As Boolean = False '更新日時を取得できたか返す
If Not System.IO.File.Exists(Path) Then
Exit Function
End If
Try
'ファイルを読取
Using fs As New System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read)
'PDF ファイルか確認
Dim PdfHeaderBuff As Byte() = {0, 0, 0, 0, 0}
If fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) <> PdfHeaderBuff.Length Then Exit Function
If System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper() <> "%PDF-" Then
Exit Function
End If
End Using
Dim buff As Byte() = System.IO.File.ReadAllBytes(Path)
With Me
'作成日時を取得
Dim p As Long = 0
Do While p <= buff.Length - 13
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12)}) = "/CreationDate" Then Exit Do
p += 1
Loop
Do While p <= buff.Length - 3
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
p += 1
Loop
p += 3
If p <= buff.Length - 14 Then
Dim yy As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 0), buff(p + 1), buff(p + 2), buff(p + 3)}))
Dim mm As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 4), buff(p + 5)}))
Dim dd As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 6), buff(p + 7)}))
Dim hh As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 8), buff(p + 9)}))
Dim nn As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 10), buff(p + 11)}))
Dim ss As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 12), buff(p + 13)}))
CreationTime = New DateTime(yy, mm, dd, hh, nn, ss)
CreationValid = True
End If
'更新日時を取得
p = 0
Do While p <= buff.Length - 8
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7)}) = "/ModDate" Then Exit Do
p += 1
Loop
Do While p <= buff.Length - 3
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
p += 1
Loop
p += 3
If p <= buff.Length - 14 Then
Dim yy As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 0), buff(p + 1), buff(p + 2), buff(p + 3)}))
Dim mm As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 4), buff(p + 5)}))
Dim dd As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 6), buff(p + 7)}))
Dim hh As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 8), buff(p + 9)}))
Dim nn As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 10), buff(p + 11)}))
Dim ss As Integer = CInt(System.Text.Encoding.ASCII.GetString({buff(p + 12), buff(p + 13)}))
ModTime = New DateTime(yy, mm, dd, hh, nn, ss)
ModValid = True
End If
End With
Catch ex As Exception
Exit Function
End Try
string Path = "●●●●.jpg"; // 対象とするファイルのパス
DateTime CreationTime; // 作成日時を返す
bool CreationValid = false; // 作成日時を取得できたか返す
DateTime ModTime; // 更新日時を返す
bool ModValid = false; // 更新日時を取得できたか返す
if (!System.IO.File.Exists(Path)) {
return false;
}
try {
// ファイルを読取
using (System.IO.FileStream fs = new System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) {
// PDF ファイルか確認
byte[] PdfHeaderBuff = new byte[5];
if (fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) != PdfHeaderBuff.Length) return false;
if (System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper() != "%PDF-") {
return false;
}
}
byte[] buff = System.IO.File.ReadAllBytes(Path);
{
// 作成日時を取得
long p = 0;
while(p <= buff.Length - 13) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12]}) == "/CreationDate") break;
p++;
}
while (p <= buff.Length - 3) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2]}) == "(D:") break;
p++;
}
p += 3;
if (p <= buff.Length - 14) {
int yy = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 0], buff[p + 1], buff[p + 2], buff[p + 3]}));
int mm = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 4], buff[p + 5]}));
int dd = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 6], buff[p + 7]}));
int hh = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 8], buff[p + 9]}));
int nn = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 10], buff[p + 11]}));
int ss = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 12], buff[p + 13]}));
CreationTime = new DateTime(yy, mm, dd, hh, nn, ss);
CreationValid = true;
}
// 更新日時を取得
p = 0;
while (p <= buff.Length - 13) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7]}) == "/ModDate") break;
p++;
}
while (p <= buff.Length - 3) {
if (System.Text.Encoding.ASCII.GetString(new byte[] { buff[p], buff[p + 1], buff[p + 2] }) == "(D:") break;
p++;
}
p += 3;
if (p <= buff.Length - 14) {
int yy = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 0], buff[p + 1], buff[p + 2], buff[p + 3]}));
int mm = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 4], buff[p + 5]}));
int dd = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 6], buff[p + 7]}));
int hh = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 8], buff[p + 9]}));
int nn = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 10], buff[p + 11]}));
int ss = int.Parse(System.Text.Encoding.ASCII.GetString(new byte[] {buff[p + 12], buff[p + 13]}));
ModTime = new DateTime(yy, mm, dd, hh, nn, ss);
ModValid = true;
}
}
}
catch (Exception) {
return false;
}
PDF ファイルの日時を変更する
Dim Path As String = "●●●●.jpg" '対象とするファイルのパス
Dim CreationTime As DateTime '作成日時を指定する
Dim CreationValid As Boolean = True '作成日時を変更するか指定する
Dim ModTime As DateTime '更新日時を指定する
Dim ModValid As Boolean = True '更新日時を変更するか指定する
If Not System.IO.File.Exists(Path) Then
Exit Function
End If
Try
'ファイルを読取
Using fs As New System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.Read)
'PDF ファイルか確認
Dim PdfHeaderBuff As Byte() = {0, 0, 0, 0, 0}
If fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) <> PdfHeaderBuff.Length Then Exit Function
If System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper().TrimEnd(vbNullChar) <> "%PDF-" Then
Exit Function
End If
End Using
Dim buff As Byte() = System.IO.File.ReadAllBytes(Path)
With Me
'作成日時を更新
If CreationValid Then
Dim p As Long = 0
Do While p <= buff.Length - 13
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12)}) = "/CreationDate" Then Exit Do
p += 1
Loop
Do While p <= buff.Length - 3
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
p += 1
Loop
p += 3
If p <= buff.Length - 14 Then
Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(CreationTime, "yyyyMMddHHmmss"))
Buffer.BlockCopy(s, 0, buff, p, s.Length)
End If
'作成日時を更新
p = 0
Do While p <= buff.Length - 16
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12), buff(p + 13), buff(p + 14), buff(p + 15)}) = "<xmp:CreateDate>" Then Exit Do
p += 1
Loop
p += 16
If p <= buff.Length - 19 Then
Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(CreationTime, "yyyy-MM-ddTHH:mm:ss"))
Buffer.BlockCopy(s, 0, buff, p, s.Length)
End If
End If
'更新日時を更新
If CreationValid Then
Dim p As Long = 0
Do While p <= buff.Length - 8
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7)}) = "/ModDate" Then Exit Do
p += 1
Loop
Do While p <= buff.Length - 3
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2)}) = "(D:" Then Exit Do
p += 1
Loop
p += 3
If p <= buff.Length - 14 Then
Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(ModTime, "yyyyMMddHHmmss"))
Buffer.BlockCopy(s, 0, buff, p, s.Length)
End If
'更新日時を更新
p = 0
Do While p <= buff.Length - 16
If System.Text.Encoding.ASCII.GetString({buff(p), buff(p + 1), buff(p + 2), buff(p + 3), buff(p + 4), buff(p + 5), buff(p + 6), buff(p + 7), buff(p + 8), buff(p + 9), buff(p + 10), buff(p + 11), buff(p + 12), buff(p + 13), buff(p + 14), buff(p + 15)}) = "<xmp:ModifyDate>" Then Exit Do
p += 1
Loop
p += 16
If p <= buff.Length - 19 Then
Dim s As Byte() = System.Text.Encoding.ASCII.GetBytes(Format(ModTime, "yyyy-MM-ddTHH:mm:ss"))
Buffer.BlockCopy(s, 0, buff, p, s.Length)
End If
End If
End With
'ファイルに書込
System.IO.File.WriteAllBytes(Path, buff)
Catch ex As Exception
Exit Function
End Try
string Path = "●●●●.jpg"; // 対象とするファイルのパス
DateTime CreationTime; // 作成日時を指定する
bool CreationValid; // 作成日時を変更するか指定する
DateTime ModTime; // 更新日時を指定する
bool ModValid = True; // 更新日時を変更するか指定する
if (!System.IO.File.Exists(Path)) {
return false;
}
try {
// ファイルを読取
using (System.IO.FileStream fs = new System.IO.FileStream(Path, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite)) {
// PDF ファイルか確認
byte[] PdfHeaderBuff = new byte[5];
if (fs.Read(PdfHeaderBuff, 0, PdfHeaderBuff.Length) != PdfHeaderBuff.Length) return false;
if (System.Text.Encoding.ASCII.GetString(PdfHeaderBuff).ToUpper() != "%PDF-") {
return false;
}
}
byte[] buff = System.IO.File.ReadAllBytes(Path);
{
// 作成日時を更新
if (CreationValid) {
int p = 0;
while (p <= buff.Length - 13) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12]}) == "/CreationDate") break;
p++;
}
while (p <= buff.Length - 3) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2]}) == "(D:") break;
p++;
}
p += 3;
if (p <= buff.Length - 14) {
byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyyMMddHHmmss"));
Buffer.BlockCopy(s, 0, buff, p, s.Length);
}
// 作成日時を更新
p = 0;
while (p <= buff.Length - 16) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12], buff[p + 13], buff[p + 14], buff[p + 15]}) == "<xmp:CreateDate>") break;
p++;
}
p += 16;
if (p <= buff.Length - 19) {
byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyy-MM-ddTHH:mm:ss"));
Buffer.BlockCopy(s, 0, buff, p, s.Length);
}
}
// 更新日時を更新
if (CreationValid) {
int p = 0;
while (p <= buff.Length - 8) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7]}) == "/ModDate") break;
p++;
}
while (p <= buff.Length - 3) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2]}) == "(D:") break;
p++;
}
p += 3;
if (p <= buff.Length - 14) {
byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyyMMddHHmmss"));
Buffer.BlockCopy(s, 0, buff, p, s.Length);
}
// 更新日時を更新
p = 0;
while (p <= buff.Length - 16) {
if (System.Text.Encoding.ASCII.GetString(new byte[] {buff[p], buff[p + 1], buff[p + 2], buff[p + 3], buff[p + 4], buff[p + 5], buff[p + 6], buff[p + 7], buff[p + 8], buff[p + 9], buff[p + 10], buff[p + 11], buff[p + 12], buff[p + 13], buff[p + 14], buff[p + 15] }) == "<xmp:ModifyDate>") break;
p++;
}
p += 16;
if (p <= buff.Length - 19) {
byte[] s = System.Text.Encoding.ASCII.GetBytes(CreationTime.ToString("yyyy-MM-ddTHH:mm:ss"));
Buffer.BlockCopy(s, 0, buff, p, s.Length);
}
}
}
// ファイルに書込
System.IO.File.WriteAllBytes(Path, buff);
}
catch (Exception) {
return false;
}