C#で JPEG の Exifデータ にある GPS情報(位置情報)を更新します。前回 と同じく今回も下記の4つの値を使用します。

タグ名称 タグ番号 Field Name タイプ
北緯(N)or 南緯(S)  1 GPSLatitudeRef  ASCII
緯度(数値)  2 GPSLatitude  RATIONAL
東経(E)or 西経(W)  3 GPSLongitudeRef  ASCII
経度(数値)  4 GPSLongitude  RATIONAL

※RATIONAL : 最初の数値が分子、2個目のL数値が分母を表す 32 ビット符号なし整数が2個

Exifの詳しい仕様については下記をご覧ください。

デジタルスチルカメラ用画像ファイルフォーマット規格
http://it.jeita.or.jp/document/publica/standard/exif/japanese/jeida49ja.htm

 

Bitmap クラス オブジェクトを作成し、タグ番号(Id)の配列を取得します。

string inFn = @"元画像のパス";
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(inFn);

 

緯度の南北の PropertyItem(ID:1)を取得し、SetPropertyItem を使用して値を更新します。

System.Drawing.Imaging.PropertyItem gpsLatitudeRef = 
	bmp.PropertyItems[Array.IndexOf(ids, 0x0001)];
byte[] latRef = System.Text.Encoding.Unicode.GetBytes("N");
bmp.SetPropertyItem(gpsLatitudeRef);

 

緯度の PropertyItem(ID:2)を取得し、SetPropertyItem を使用して値を更新します。
※緯度 35度39分31.05秒に更新する場合

byte[] lat = new byte[24];
Array.Copy(BitConverter.GetBytes(35), 0, lat, 0, 4);
Array.Copy(BitConverter.GetBytes(1), 0, lat, 4, 4);

Array.Copy(BitConverter.GetBytes(39), 0, lat, 8, 4);
Array.Copy(BitConverter.GetBytes(1), 0, lat, 12, 4);

Array.Copy(BitConverter.GetBytes(310500), 0, lat, 16, 4);
Array.Copy(BitConverter.GetBytes(10000), 0, lat, 20, 4);
gpsLatitude.Value = lat;
bmp.SetPropertyItem(gpsLatitude);

 

新しいファイル名を指定して保存します。

string outFn = @"更新後の画像パス";
bmp.Save(outFn, System.Drawing.Imaging.ImageFormat.Jpeg);

 

東経(E)or 西経(W), 経度も同様に更新することができます。

 

尚、PropertyItem クラスには、定義済みのパブリック コンストラクターがありません。

そのため、更新ではなく新規で位置情報などの PropertyItem を追加したい場合は、
別の画像から PropertyItem オブジェクトを取得して、SetPropertyItem で指定すれば、新に項目が追加されます。

 

サンプルコード(緯度 35度39分31.05秒, 経度 139度44分43.494秒に更新)

using System;
using System.Windows.Forms;

namespace Test
{
    public partial class Form1 : Form
    {
        private void button1_Click(object sender, EventArgs e)
        {
            string inFn = @"入力ファイル";
            string outFn = @"出力ファイル";

            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(inFn);

            int[] ids = bmp.PropertyIdList;

            // 緯度の南北(タグ番号 1.GPSLatitudeRef)を取得・設定
            System.Drawing.Imaging.PropertyItem gpsLatitudeRef = bmp.PropertyItems[Array.IndexOf(ids, 0x0001)];
            byte[] latRef = System.Text.Encoding.Unicode.GetBytes("N");
            bmp.SetPropertyItem(gpsLatitudeRef);

            // 緯度(タグ番号 2.GPSLatitude)を取得
            System.Drawing.Imaging.PropertyItem gpsLatitude = bmp.PropertyItems[Array.IndexOf(ids, 0x0002)];

            // 緯度 35度39分31.05秒 を設定
            byte[] lat = new byte[24];
            Array.Copy(BitConverter.GetBytes(35), 0, lat, 0, 4);
            Array.Copy(BitConverter.GetBytes(1), 0, lat, 4, 4);

            Array.Copy(BitConverter.GetBytes(39), 0, lat, 8, 4);
            Array.Copy(BitConverter.GetBytes(1), 0, lat, 12, 4);

            Array.Copy(BitConverter.GetBytes(310500), 0, lat, 16, 4);
            Array.Copy(BitConverter.GetBytes(10000), 0, lat, 20, 4);
            gpsLatitude.Value = lat;
            bmp.SetPropertyItem(gpsLatitude);


            // 経度の東西(タグ番号 3.GPSLongitudeRef)を取得・設定
            System.Drawing.Imaging.PropertyItem gpsLongitudeRef = bmp.PropertyItems[Array.IndexOf(ids, 0x0003)];
            byte[] lonRef = System.Text.Encoding.Unicode.GetBytes("E");

            // 経度(タグ番号 4.GPSLongitude)を取得
            System.Drawing.Imaging.PropertyItem gpsLongitude = bmp.PropertyItems[Array.IndexOf(ids, 0x0004)];

            // 経度 139度44分43.494秒 を設定
            byte[] lon = new byte[24];
            Array.Copy(BitConverter.GetBytes(139), 0, lon, 0, 4);
            Array.Copy(BitConverter.GetBytes(1), 0, lon, 4, 4);

            Array.Copy(BitConverter.GetBytes(44), 0, lon, 8, 4);
            Array.Copy(BitConverter.GetBytes(1), 0, lon, 12, 4);

            Array.Copy(BitConverter.GetBytes(434940), 0, lon, 16, 4);
            Array.Copy(BitConverter.GetBytes(10000), 0, lon, 20, 4);
            gpsLongitude.Value = lon;
            bmp.SetPropertyItem(gpsLongitude);

            // 保存
            bmp.Save(outFn, System.Drawing.Imaging.ImageFormat.Jpeg);

            bmp.Dispose();
        }

        public Form1()
        {
            InitializeComponent();
        }
    }
}

C#で JPEG の Exifデータ にある GPS情報(位置情報)を取得します。今回は、GPS情報のうち下記の4つを取得しています。

タグ名称 タグ番号 Field Name タイプ
北緯(N)or 南緯(S)  1 GPSLatitudeRef  ASCII
緯度(数値)  2 GPSLatitude  RATIONAL
東経(E)or 西経(W)  3 GPSLongitudeRef  ASCII
経度(数値)  4 GPSLongitude  RATIONAL

※RATIONAL : 最初の数値が分子、2個目の数値が分母を表す 32 ビット符号なし整数が2個

Exifの詳しい仕様についてはJEITAをご覧ください。

http://www.jeita.or.jp/japanese/index.cgi

 

Bitmap クラス オブジェクトを作成し、タグ番号(Id)の配列を取得します。

System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(fn);
int[] ids = bmp.PropertyIdList;

 

緯度の南北を文字列として取得します。

int index = Array.IndexOf(ids, 0x0001);
System.Drawing.Imaging.PropertyItem item = bmp.PropertyItems[index];
string s = BitConverter.ToChar(item.Value, 0).ToString();

 

緯度を取得します。緯度は、度、分、秒3つの RATIONALとなっています。ただし、DMM形式又はDMS形式となっていますので、今回は形式を統一するためDMM形式の場合は、DMS形式に変換します。

DMS形式の場合は、そのまま割り算すれば取得できます。

int index = Array.IndexOf(ids, 0x0002);
System.Drawing.Imaging.PropertyItem item = bmp.PropertyItems[index];
double dd = BitConverter.ToUInt32(item.Value, 0) / BitConverter.ToUInt32(item.Value, 4);
double mm = BitConverter.ToUInt32(item.Value, 8) / BitConverter.ToUInt32(item.Value, 12);
double ss = (double)BitConverter.ToUInt32(item.Value, 16) / (double)BitConverter.ToUInt32(item.Value, 20);

 

DMM形式の場合は、度はそのまま割り算し、分は割り算の結果の整数部分のみ、秒は分の小数点以下に60を掛けて計算します。

int index = Array.IndexOf(ids, 0x0002);
System.Drawing.Imaging.PropertyItem item = bmp.PropertyItems[index];
double dd = BitConverter.ToUInt32(item.Value, 0) / BitConverter.ToUInt32(item.Value, 4);
double mmmm = (double)BitConverter.ToUInt32(item.Value, 8) / (double)BitConverter.ToUInt32(item.Value, 12);
double ss = (mmmm % 1) * 60;
double mm = (UInt32)mmmm;

 

DMS形式とDMM形式の判断は、分の分母で判断できます(DMM形式の場合は「100」)。

if (BitConverter.ToUInt32(item.Value, 12) == 100) {
	・・・・
}

 

東経(E)or 西経(W), 経度も同様に取得することができます。

また、緯度・経度共に百分率で表示する場合は、「度 + (分 / 60) + (秒 / 3600)」で計算します。

 

サンプルコード

using System;
using System.Windows.Forms;

namespace Test
{

    public partial class Form1 : Form
    {
        public class Point
        {
            public string r { get; set; }
            public double dd { get; set; }
            public double mm { get; set; }
            public double ss { get; set; }

            public double per
            {
                get
                {
                    return dd + (mm / 60) + (ss / 3600);
                }
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Point lat = new Point();
            Point lon = new Point();

            string fn = "ファイル名";

            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(fn);
            int[] ids = bmp.PropertyIdList;

            // 緯度の南北(タグ番号 1.GPSLatitudeRef)を取得
            System.Drawing.Imaging.PropertyItem gpsLatitudeRef = 
                bmp.PropertyItems[Array.IndexOf(ids, 0x0001)];
            lat.r = BitConverter.ToChar(gpsLatitudeRef.Value, 0).ToString();

            // 緯度(タグ番号 2.GPSLatitude)を取得
            System.Drawing.Imaging.PropertyItem gpsLatitude = 
                bmp.PropertyItems[Array.IndexOf(ids, 0x0002)];
            lat.dd = BitConverter.ToUInt32(gpsLatitude.Value, 0) / BitConverter.ToUInt32(gpsLatitude.Value, 4);
            if (BitConverter.ToUInt32(gpsLatitude.Value, 12) == 100)
            {
                // DMM形式 → DMS形式
                double mmmmm = (double)BitConverter.ToUInt32(gpsLatitude.Value, 8) / (double)BitConverter.ToUInt32(gpsLatitude.Value, 12);
                lat.ss = (mmmmm % 1) * 60;
                lat.mm = (UInt32)mmmmm;
            }
            else
            {
                // DMS形式
                lat.mm = BitConverter.ToUInt32(gpsLatitude.Value, 8) / BitConverter.ToUInt32(gpsLatitude.Value, 12);
                lat.ss = (double)BitConverter.ToUInt32(gpsLatitude.Value, 16) / (double)BitConverter.ToUInt32(gpsLatitude.Value, 20);
            }


            // 経度の東西(タグ番号 3.GPSLongitudeRef)を取得
            System.Drawing.Imaging.PropertyItem gpsLongitudeRef = bmp.PropertyItems[Array.IndexOf(ids, 0x0003)];
            lon.r = BitConverter.ToChar(gpsLongitudeRef.Value, 0).ToString();

            // 経度(タグ番号 4.GPSLongitude)を取得
            System.Drawing.Imaging.PropertyItem gpsLongitude = bmp.PropertyItems[Array.IndexOf(ids, 0x0004)];
            lon.dd = BitConverter.ToUInt32(gpsLongitude.Value, 0) / BitConverter.ToUInt32(gpsLongitude.Value, 4);

            if (BitConverter.ToUInt32(gpsLongitude.Value, 12) == 100)
            {
                // DMM形式 → DMS形式
                double mmmmm = (double)BitConverter.ToUInt32(gpsLongitude.Value, 8) / (double)BitConverter.ToUInt32(gpsLongitude.Value, 12);
                lon.ss = (mmmmm % 1) * 60;
                lon.mm = (UInt32)mmmmm;
            }
            else
            {
                // DMS形式
                lon.mm = BitConverter.ToUInt32(gpsLongitude.Value, 8) / BitConverter.ToUInt32(gpsLongitude.Value, 12);
                lon.ss = (double)BitConverter.ToUInt32(gpsLongitude.Value, 16) / (double)BitConverter.ToUInt32(gpsLongitude.Value, 20);
            }

            textBox1.Text += string.Format("{0}{1}°{2} {3}\r\n", lat.r, lat.dd, lat.mm, lat.ss);
            textBox1.Text += string.Format("{0}{1}°{2} {3}\r\n", lon.r, lon.dd, lon.mm, lon.ss);

            // 百分率表記
            textBox1.Text += string.Format("{0}\r\n", lat.per);
            textBox1.Text += string.Format("{0}\r\n", lon.per);

            bmp.Dispose();
        }

        public Form1()
        {
            InitializeComponent();
        }
    }
}

C#でイベントログの書き込みを監視します。

環境:Vista, .NET Framework 3.5, Visual Studio 2008

監視するログの名前を指定し、監視を有効にします。

今回は textbox に書き込まれたイベントの内容を表示しますので、イベントの結果で呼ばれるメゾットが同じスレッドで呼び出されるように設定を行います。

System.Diagnostics.EventLog EvtLog = new System.Diagnostics.EventLog();

EvtLog.Log = "Application";
EvtLog.SynchronizingObject = this;
EvtLog.EntryWritten += new System.Diagnostics.EntryWrittenEventHandler(EventLog_EntryWritten);

EvtLog.EnableRaisingEvents = true;

 

イベントが書き込まれたときに呼ばれるメゾットで、textbox にイベントの内容を追加します。

textBox1.Text += "Source: " + e.Entry.Source + "\r\n";
textBox1.Text += "Entry : " + e.Entry.Message + "\r\n";

 

最後に、リソースを解放しイベントの監視を終了します。

EvtLog.Dispose();

 

サンプルコード

using System;
using System.Windows.Forms;

namespace Test
{
	public partial class Form1 : Form
	{
		System.Diagnostics.EventLog EvtLog;

		public Form1()
		{
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e)
		{
			EvtLog = new System.Diagnostics.EventLog();

			EvtLog.Log = "Application"
			EvtLog.SynchronizingObject = this;
			EvtLog.EntryWritten += new System.Diagnostics.EntryWrittenEventHandler(EventLog_EntryWritten);
			EvtLog.EnableRaisingEvents = true;
		}

		private void button2_Click(object sender, EventArgs e)
		{
			EvtLog.Dispose();
		}

		private void EventLog_EntryWritten( object sender, System.Diagnostics.EntryWrittenEventArgs e)
		{
			textBox1.Text += "Source: " + e.Entry.Source + "\r\n";
			textBox1.Text += "Entry : " + e.Entry.Message + "\r\n";
		}
	}
}

環境:Windows Server 2008 R2(RC版)
※製品版のWindows Server 2008R2では、PowerShellがV2になるはずなので、多少異なる可能性があります。

 

Concrete5で「検索用索引の作成」や「サイトマップファイルを作成」などのジョブを自動的に実行する場合、Cronなどを使って特定のURLにアクセスする必要があります。

WindowsにはCronがありませんので、今回はタスク スケジューラとPowerShell(V1)を使用します。

 

Windows Server 2008以外でPowerShell がインストールされていない場合は、下記からダウンロードしてください。

PowerShell 
http://www.microsoft.com/japan/technet/scriptcenter/hubs/msh.mspx

PowerShell Vista
http://support.microsoft.com/?kbid=928439

 

1.PowerShellスクリプトの作成

URLにアクセスするPowerShellスクリプトを作成し、適当な場所に適当な名前(例:c:\task\concrete5.ps1)で保存します。

$url = "http://localhost/Concrete5/index.php/tools/required/jobs?auth=00000000000000000000000000000000"
[void]([Reflection.Assembly]::LoadWithPartialName("System.Web"))
$req = [Net.HttpWebRequest]::Create($url)
$res = $req.GetResponse()
$res.Close()

※URL部分は、「管理画面」の「メンテナンス」ページに記載されています。

 

2.PowerShellの環境設定

実行ポリシーが規定だと「Restricted」になっていて、PowerShellは スクリプトが実行できないので、下記の手順でスクリプトを実行できるように設定します。

 

2.1.PowerShell(powershell.exe)を管理者として実行します。

powershell.exe は、デフォルトだと C:\Windows\System32\WindowsPowerShell\v1.0 にあります。

 

2.2.下記のコマンドを実行し、スクリプトを実行できるようにポリシーを変更します。

Set-ExecutionPolicy RemoteSigned

 

2.3.「Exit」で PowerShell を終了します。

 

3.タスクの登録

3.1.コマンドプロンプトを実行します。

 

3.2.下記のコマンドを実行し、定期的に 「 1 」で作成したPowerShellスクリプトを実行するタスクを登録します。

SCHTASKS /CREATE /TN "Concrete5" /TR "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe c:\task\concrete5.ps1" /SC DAILY /ST 08:00

パラメータ説明

TN タスク名
TR 実行タスク(プログラムのパス)
SC スケジュールの頻度
ST タスク実行開始時間

詳しくは、SCHTASKS /CREATE /? でご覧ください。

 

以上で完了です。毎日午前8時にタスクが実行され、ジョブが実行されるようになります。

C#でイベントログの内容を削除します。

環境:Vista, .NET Framework 3.5, Visual Studio 2008

イベントログの内容を削除する場合は、EventLog オブジェクトの Clear メゾットを使います。

System.Diagnostics.EventLog evtlog = 
	new System.Diagnostics.EventLog(logName);
evtlog.Clear();

 

リモートコンピュータにあるイベントログの内容を削除する場合は、コンピュータ名を追加で指定してください。

System.Diagnostics.EventLog evtlog = 
	new System.Diagnostics.EventLog(logName, machineName);

 

サンプルコード

using System;
using System.Windows.Forms;

namespace Test
{
	public partial class Form1 : Form
	{
		public Form1()
		{
			InitializeComponent();
		}

		private void button1_Click(object sender, EventArgs e)
		{
			string logName = "Application";

			System.Diagnostics.EventLog evtlog = 
				new System.Diagnostics.EventLog(logName);
			evtlog.Clear();
		}
	}
}

 

Vistaでユーザーアカウント制御 (UAC)が有効の場合、Windows フォームアプリケーションは「管理者として実行」しないと、例外(「System.ComponentModel.Win32Exception: アクセスが拒否されました。」)    が発生しますので、可能ならばVSを管理者として実行してください。