バージョン情報と一口にいっても、実行ファイルのバージョン、DLLのバージョン、ClickOnceやインストーラのバージョンなど様々です。
バージョン情報ダイアログを作るために、これらのバージョンの取得方法を書いてみます。といってもコードで。
/// <summary>
/// ClickOnceで設定されたバージョンを取得
/// </summary>
public static Version ClickOnceVersion
{
if (!System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed)
{
return null;
}
return System.Deployment.Application.ApplicationDeployment
.CurrentDeployment.CurrentVersion;
}
/// <summary>
/// 実行中のexeファイルのバージョンを取得
/// </summary>
public static string AssemblyVersion
{
return System.Windows.Forms.Application.ProductVersion;
}
/// <summary>
/// 実行中のexeファイルと同一ディレクトリにあるDLLの情報を取得
/// </summary>
public static List<System.Diagnostics.FileVersionInfo> DllVersion
{
var versions = new List<System.Diagnostics.FileVersionInfo>();
//自身のファイルパスを取得し、同一ディレクトリのdllファイルを一覧する
string path = System.IO.Path.GetDirectoryName(
System.Windows.Forms.Application.ExecutablePath);
foreach (string name in System.IO.Directory.GetFiles(path, "*.dll"))
{
System.Diagnostics.FileVersionInfo info =
System.Diagnostics.FileVersionInfo.GetVersionInfo(name);
versions.Add(info);
}
return versions;
}
このような感じで、バージョン情報ダイアログが作れそうです。
※DLLのバージョン取得はもう少し工夫しないと問題がありそうですが・・・
C#等のアプリで、自身がClickOnceでインストール・実行されているかをチェックするには、
System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed
を調べます。
しかしこのプロパティ、普通に起動するとfalseが取得できますが、Visual Studioから起動するとなぜかフリーズすることがあります。ありました。
この場合は、ClickOnceアプリの実体が保存される C:/Users/ユーザ名/AppData/Local/Apps/2.0 を削除して、コンピュータを再起動すると直るようです。直りました。
起動プロセスが複雑になるとたまに面倒ですね。
PHPでmysql_queryの結果オブジェクトは、mysql_fetch_row()しても良いですが、mysql_fetch_array()した方が便利ですよね。
※mysql_fetch_row()では、結果を通常の配列で取得しますが、mysql_fetch_array()では、カラム名をキーにした連想配列で取得できます
EthnaでAppObjectを使わずにEthna_DBを直接たたく場合、結果オブジェクトはPEAR::DB_Resultオブジェクトです。
これはfetchRow()のメソッドを持ちますが、fetchArray()は存在しません。
http://pear.plus-server.net/package.database.db.db-result.fetchrow.html
カラム名をキーにした連想配列が欲しい場合、queryを投げる前にsetFetchMode(DB_FETCHMODE_ASSOC)を実行しておく必要があります。
http://pear.plus-server.net/package.database.db.db-common.setfetchmode.html
EthnaのデフォルトDBアダプタ、DB_PEARは、内部にPEAR::DBを持っているので、以下のようなコードを書けばOKです。
$db = $this->backend->getDB();
$db->db->setFetchMode(DB_FETCHMODE_ASSOC);
$res = $db->query($sql);
print_r($res->fetchRow());
※http://ethna.jp/ethna-document-dev_guide-db.htmlに
Ethnaの デフォルトの DB接続クラス(Ethna_DB_PEAR) はPEAR::DBを継承しているので
と書いてありますが、継承ではなくて委譲なので、$db->db->setFetchMode()となります。
PHPのfputcsv関数は便利です。
しかし、以下のような点が不便です。
エンコーディングを変換できない
主にCSV出力の目的になるExcelでは、Shift-JIS以外のCSVを開けません。
Excel 2007ならBOMを付けるとか、データインポートで実行するといった回避策はありますが、日本語のみならShift-JISで出力したいことが多いと思います。
このためにはあらかじめShift-JISに変換しておく必要があり、
$sjis = array_map(create_function(’$str’, ‘return mb_convert_encoding($str, “Shift-JIS”, “UTF-8″);’), $line);
といった処理をやっていましたが、正直めんどうです。
ダブルコーテーションのエスケープにバグがある
カンマが含まれる場合、ダブルコーテーションで囲ってくれます。
ダブルコーテーションが含まれる場合、ダブルコーテーションを二重にしてエスケープしてくれます。
しかし、\” (バックスラッシュ+ダブルコーテーション)があった場合、なぜかこの部分のダブルコーテーションは二重にしてくれません。
ということで、結局自前でやった方が便利です。
mb_str_replaceは、http://fetus.k-hsu.net/document/programming/php/mb_str_replace.htmlを使わせて頂きました。
function _fputcsv($fp, $data, $toEncoding='Shift-JIS', $srcEncoding='UTF-8') {
require_once 'mb_str_replace.php';
$csv = '';
foreach ($data as $col) {
if (is_numeric($col)) {
$csv .= $col;
} else {
$col = mb_convert_encoding($col, $toEncoding, $srcEncoding);
$col = mb_str_replace('"', '""', $col, $toEncoding);
$csv .= '"' . $col . '"';
}
$csv .= ',';
}
fwrite($fp, $csv);
fwrite($fp, "\r\n");
}
ちゃんとテストしていないのでミスがあるかもしれませんが、このくらいシンプルで十分そうですね。
数年前に作ったミニアプリを公開してみました。
WinBottom
ウィンドウの最小化ボタンを右クリックすると、そのウィンドウが背面に移動するだけの常駐ソフトです。
くだらないですが、大型ディスプレイでは地味に便利です。
アイコンが猫の手なので、おすすめです。
極小なのでほとんど何も書いていないですが、もしソースが欲しい等の変な方がいたらご連絡ください。
1年くらい前に社内MLに投げた、「MySQLでSET NAMESを使ってはいけない理由」をコピペしてみます。手抜きです、はい。
赤字は注釈です。
今更ながら、「MySQLで SET NAMES を使ってはいけない」の根拠のお話です。
下記のPHPスクリプトでは、入力値を元にSQL文を生成し、検索クエリを投げています。
※sqltestというDBには、カラムnameを持つuserテーブルが存在します。
GETで渡された値はきちんとmysql_real_escape_stringをかけているので、SQLインジェクションは出来ないように見えます。
しかし、
http://localhost/sqltest/index.php?name=%95%5c’%20OR%201=1%20–%20
にアクセスすると、全部のデータが見えてしまいます。
下にあるPHPスクリプトを、localhost/sqltest/index.php として配置してください。
“SET NAMES SJIS” を実行すると、MySQLのエンコードがShift-JISになりますが、mysql_real_escape_stringはUTF-8のまま動作します。
16進数で 95 5c 27 20 は、
UTF-8: (謎の文字)(バックスラッシュ)(シングルコーテーション)(スペース)
Shift-JIS: (表)(シングルコーテーション)(スペース)
になります。
mysql_real_escape_stringは、バックスラッシュとシングルコーテーションそれぞれをエスケープします。
95 5c 5c 5c 27 20
MySQLは、Shift-JISとして動作するので、 (表)(\)(\)(シングルコーテーション)(スペース) と認識します。
つまり、\が一つ余分に入ることで、入力値のシングルコーテーションがエスケープされなくなります。
マルチバイト非対応のエスケープ関数を使うのと同じ理屈で、SET NAMES は危険です。
基本的に文字コード中に5Cが入るShift-JISが危険ですが、他の文字コードでも似たようなことが起こる可能性があります。
mysql_set_charset(’SJIS’);
なら、mysql_real_escape_stringもShift-JISとして動作するようになるので、安全です。
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SQLテストページ</title>
<head>
</head>
<body>
<form action="<?php echo $_SERVER['SCRIPT_NAME'];?>">
<input type="text" name="name" />
<input type="submit" value="search" />
</form>
<?php
//DBに接続
if (! $db = mysql_connect('localhost', 'sqltest', 'sqltest'))
{
echo 'CONNECT ERROR';
exit;
}
mysql_select_db('sqltest', $db);
mysql_query('SET NAMES SJIS');
// mysql_set_charset('SJIS');
//SQLを生成
$name = mysql_real_escape_string($_REQUEST['name']);
$sql = "SELECT * FROM user WHERE name='$name'";
echo $sql . '<br />';
//実行
if (! $res = mysql_query($sql))
{
echo "QUERY ERROR <br />";
echo mysql_error();
exit;
}
echo "<pre>";
while ($row = mysql_fetch_array($res)) {
print_r($row);
}
echo "</pre>";
?>
</body>
</html>
PHPでMySQL 5をUTF8にして使う場合、文字が全部????になってしまう問題を防ぐため、
default-character-set=utf8
character-set-server=utf8
collation-server=utf8_general_ci
skip-character-set-client-handshake
を指定する、というのが定石になっています(よね?)。
しかし、共用サーバなどでmy.iniを編集できない場合、mysql_set_charset() などを使うと思います。
CakePHPでは、bootstrapでDB設定を読んで手動で設定するのも面倒だな、と思っていたら、さすがはCakePHP。便利な設定が。
app/config/database.php
var $default = array(
'driver' => 'mysql',
'persistent' => false,
'host' => 'localhost',
'login' => 'baba',
'password' => 'hoge',
'database' => 'testdb',
'prefix' => '',
'encoding' => 'utf8',
);
このようにencodingを指定するだけで、勝手にSET NAMESやmysql_set_charset()を実行してくれます。便利便利。
XAMPP はPHPやMySQLなどもいっぺんに入ってとても便利ですが、その分バージョン違いで混乱することも多いですね。
最新版1.7.3 では、PHPも5.3.1になっていて、以下の点で既存システムが動かなくなりました。php.iniのデフォルトが変わったようです。
エラー表示をE_ALLにしたら、deprecatedが大量に出る
php.ini で error_reporting = E_ALL & ~E_DEPRECATED にすればOK
cURLエクステンションが無効で、PEAR::XML_RPC2が動かない(エラーも出ない)
php.ini で extension=php_curl.dll のコメントアウトを外せばOK
特定の環境に慣れていると、開発したシステムの動作環境を正確に把握・記述することを怠ってしまうので、注意しないといけませんね。
先日のiPhone UDP受信テストの際に使った、WindowsからUDPパケットを送りつけるプログラムを載せてみます。
一切の遠慮無く、1秒に20枚のペースで、デスクトップに置いてあるimg0.png~img9.pngの画像ファイルを一方的に送りつけます。
///一定時間ごとにUDPで画像を一方的に送りつける
private void SendData()
{
//ローカルポート番号2222にバインドする
System.Net.Sockets.UdpClient udp = new System.Net.Sockets.UdpClient(2222);
int count = 0; //何番目の画像を送信するかのカウンタ
Timer timer = new Timer();
timer.Tick += new EventHandler((s, e) =>
{
if (++count > 9)
{
count = 0;
}
//デスクトップのimg0.png~img9.pngを送る
string filename = @"C:\Users\baba\desktop\img\" + count + ".png";
using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
byte[] sendBytes = new byte[fs.Length];
fs.Read(sendBytes, 0, sendBytes.Length);
//宛先IPとポート番号を指定する
udp.Send(sendBytes, sendBytes.Length, "192.168.100.50", 5555);
}
});
timer.Interval = 50;
timer.Start();
}
.NETは簡単にこういったコードが書けて便利ですね。
何の工夫も最適化も無いですが、50枚/秒くらいなら全然問題にならない程度の速度は出ていました。
CakePHPでjQueryを使ったAjaxを実装した際、IEの古いバージョンでのみエラーになる現象に見舞われました。
調べてみると、Ajaxリクエストを送った際にログインが切れているようです。
どうやら原因は、CakePHPのユーザエージェント検証機能でした。
app/config/core.phpで
Configure::write(’Session.checkAgent’, false);
を設定することで解決しました。
つまり、古いIEでは、通常時のリクエストとAjaxリクエストで、ユーザエージェントが違う、ということですね。
これはたぶん、歴史的な理由からXMLHttpRequestがActiveXオブジェクトで実装されていることに由来するのでしょう。
以下に、各ブラウザで確認したユーザエージェントを書いておきます。
PHPで、$_SERVER['HTTP_USER_AGENT'] を表示しています。また、Ajax通信は、jQueryの$.ajax()を使っています。
■Firefox 3.5.6
通常: Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)
Ajax: Mozilla/5.0 (Windows; U; Windows NT 6.1; ja; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6 (.NET CLR 3.5.30729)
■Chrome 4 beta
通常: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.64 Safari/532.5
Ajax: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.0.249.64 Safari/532.5
■IE8
通常: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
Ajax: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
■IE7
通常: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
Ajax: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
■IE6
通常: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 6.1; WOW64; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)
Ajax: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; .NET4.0C; .NET4.0E)