当然ながら、言語ごとに真偽判定(true/false)の基準が違います。
特にスクリプト系の言語では、明示的に型を指定しないことが多いので、たまに問題になります。
たまたま手元にあった環境で、どんな値が真に判定されるのか、というのをまとめてみました。
新しい言語に突撃するときのメモ代わりにでもして頂ければ幸いです。
検証コードイメージ
a = 0
if a
print 'true'
else
print 'false'
end

凡例:
× は、コンパイルエラー
ー は、言語仕様にその値が存在しない
備考:
*”" というのは、ナル文字の値を指す(\0なので、実体はゼロという整数値)
array[] というのは、各言語での空の配列を指す
※1 C言語にfalseは存在しない
※2 配列のアドレスを指すため、真と評価される
※3 noticeが発生するが、設定や@で抑制可能
こうしてみると、やはりPHPは変態です。’0′ をfalseと判定する言語はあまりないですね。Webに特化しているのがよくわかります。$_GETなどは文字列で渡ってくるので、確かに手抜きに便利仕様です。
空配列をfalseと判定するのが、PythonとPHPというなんだかおもしろい組み合わせになりました。
良く忘れるけど、rubyはゼロという数字をtrueに判定するので注意が必要ですね。
結局、ミスのしようが無い Java / C# は安全で好きです。
また、ゼロは false、ほかは true と割り切ったC言語も、シンプルで好きです。
Visual Studio 2010で遊んでみたくなったので、またちょっとしたものを作ってみました。
画面上で簡易的にOCRを実行するアプリです。
http://www.bpsinc.jp/blog/mini-software/screenocr
実行するのにOffice Document Imagingのインストールが必要で、若干面倒ですが、良かったら試してみて下さい。
ちょっとしたデータサーバ障害により、バックアップをしていなかった、C#で開発していたミニプログラムのソースコードが紛失してしまいました。
そこで Reflector.NETの出番!
exeやdllを読み込ませると、リバースエンジニアリングでC#の形に復元してくれます。
マシン語にコンパイルされるC++などと違い、C#は変数名やオブジェクト構造も維持するMSILにコンパイルされるため、精度は抜群。
コメントが消えたり、プロパティが get_* というメソッドに置換されたり、ラムダ式が展開されていたりするくらいで、ほとんど元のソースコードが復元できます。
むしろ面倒なのが、XAMLの記述やリソースファイルの配置だったり・・・
とにかくReflectorは便利ですね。
その分、読まれても問題ないコードを書くように気をつけないといけませんが・・・
バージョン情報と一口にいっても、実行ファイルのバージョン、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 を削除して、コンピュータを再起動すると直るようです。直りました。
起動プロセスが複雑になるとたまに面倒ですね。
先日の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枚/秒くらいなら全然問題にならない程度の速度は出ていました。
C#でHTTPサーバを作るのはすごく簡単、
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8000/"); //ポート番号は好みで。80でもOK
listener.Start();
while (true)
{
HttpListenerResponse response = listener.GetContext().Response;
response.OutputStream.Write(data, 0, (int)data.Length); //ここで何かdataを出力する
response.Close();
}
のようなコードだけで出来てしまいます。
しかし、これだとWindows Vista / 7 のUAC有効環境で、HttpListenerExceptionが発生します。
Windowsファイアウォールを無効にしても結果は変わりません。
UAC有効の状態では、通常権限のプログラムは、localhost以外からの接続を受け付けられないようです。
そもそもテストやミニプログラム以外でHttpListenerを使うか疑問が大きいので、手抜きで安直な解決方法を探ってみます。
localhostに限定
ローカルでテストする分には、
listener.Prefixes.Add(”http://localhost:8000″);
のようにすればとりあえず動きます。
管理者権限にする
localhost以外からも接続させたいなら、管理者権限で動かしてしまうのが簡単です。
Visual Studioを起動する際に管理者権限にしておけば、デバッグで起動するプログラムも管理者権限になるので、例外は発生しなくなります。
WPFで枠を消す の続きです。
前回紹介した方法は、以下のような内容でした。
- WindowStyle=None, AllowsTransparency=True にすると枠が消える
- 移動できるように、WM_NCLBUTTONDOWN + HTCAPTION を送信する
しかし、この方法には欠点があります。
AllowsTransparencyを有効にしていると、WindowsFormsHostが表示されなくなってしまうのです。
http://msdn.microsoft.com/ja-jp/library/aa970911(VS.80).aspx
HwndHost クラスは、レイヤ表示をサポートしません。つまり、WindowsFormsHost 要素で Opacity プロパティを設定しても何の効果もなく、AllowsTransparency が true に設定されている他の WPF ウィンドウとのブレンド操作は行われません。
そこで、DirectX と連携する際などは、AllowsTransparency を False のままにしておく必要があります。
困ったときのWinAPI。直接ウィンドウスタイルを設定して、枠を消してしまいましょう。
Windows Forms Application 時代は、CreateParams で設定していたような内容ですね。
[DllImport("user32.dll")]
static extern long GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern long SetWindowLong(IntPtr hWnd, int nIndex, long dwNewLong);
[DllImport("user32.dll")]
static extern UInt32 SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
private void RemoveFrame()
{
long WS_THICKFRAME = 0x00040000L;
int GWL_STYLE = -16;
int SWP_FRAMECHANGED = 0x0020;
int SWP_NOSIZE = 0x1;
int SWP_NOMOVE = 0x2;
int SWP_NOZORDER = 0x4;
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle;
SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) ^ WS_THICKFRAME);
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
}
やっていることは以下のような感じです。
- GetWindowLongでWindowStyleを取得
- SetWindowLongで、今までのスタイルからWS_THICKFRAME(ウィンドウ枠)を除いたものを設定
- SetWindowPosで表示を更新
最後のSetWindowPostをやらないと、サイズ変更するまで描画が崩れてしまいます。
注意点としては、WS_THICKFRAME を外すと、WM_NCLBUTTONDOWN で HTBOTTOMRIGHT を送信しても、サイズ変更できなくなります。
諦めて、自分でサイズ変更処理を書くしかないみたいですね。
Windowsのデフォルトでは、省エネのためしばらく操作しないとディスプレイ出力が停止します。
マウス操作などすれば再度ディスプレイが付きますが、常駐アプリなどを作成する場合、プログラムからディスプレイをONにしたい場合があります。
そんなときは、PostMessageでWM_SYSCOMMANDメッセージを送り、wParamにSC_MONITORPOWER、lParamに-1をセットすればOKです【方法A】。
http://msdn.microsoft.com/en-us/library/ms646360(VS.85).aspx
しかしこの方法では、WindowsXPなどの環境で、一瞬ディスプレイが付いたあと、すぐに再度OFFになってしまう場合があります。
これについて解析している方もいらっしゃいましたが、ここでは別の方法として、「マウスを動かす」ことによって省電力モードを解除してみましょう【方法B】。
方法AとBの両方のソースをご紹介します。C#でWPFな環境を想定しています。
なお、方法Aでは引数を変えるだけでディスプレイ電源OFFも可能です。
const int HWND_BROADCAST = 0xffff;
const int WM_SYSCOMMAND = 0x0112;
const int SC_MONITORPOWER = 0xF170;
const int INPUT_MOUSE = 0;
const int INPUT_KEYBOARD = 1;
const int MOUSEEVENTF_MOVED = 0x0001;
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public int mouseData;
public int dwFlags;
public int time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct INPUT
{
public int type;
public MOUSEINPUT mi;
}
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
//【方法A】
void WakeupDisplayA()
{
//最後の引数は、1=省電力モード、2=ディスプレイ停止、-1=省電力モードから復帰
PostMessage((IntPtr)HWND_BROADCAST, (IntPtr)WM_SYSCOMMAND, (IntPtr)SC_MONITORPOWER, (IntPtr)(-1));
}
//【方法B】
void WakeupDisplayB()
{
//適当に動かす
INPUT[] inputs = new INPUT[2];
inputs[0].mi.dx = 10;
inputs[0].mi.dwFlags = MOUSEEVENTF_MOVED;
inputs[1].mi.dx = -10;
inputs[1].mi.dwFlags = MOUSEEVENTF_MOVED;
SendInput(2, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0]));
}
最近はWPFに触れる機会が増えてきたので、小ネタを少しずつ書いていきます。
今日は、ウィンドウ枠を消す方法です。デザインを格好良くするために、ウィンドウ枠を消したいことも多々ありますよね。
Window.xaml で、
- WindowStyle を None にする
- AllowTransparency を true にする
以上で枠が消えます。
・・・これだけだとつまらないので、枠が消えても移動・リサイズが正しくできるようにしてみます。
なお、システムメニューを表示する方法は先日の記事をご覧下さい。
まず、右下の方に適当なリサイズハンドルを用意します。ButtonやImageなどで良いでしょう。
そして、MouseLeftButtonDown イベントに以下のイベントハンドラを追加します。
[DllImport("user32.dll")]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
private void Hoge_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//WM_NCLBUTTONDOWN=0xA1, HTBOTTOMRIGHT=17
IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
PostMessage(hwnd, 0xA1, (IntPtr)17, IntPtr.Zero);
}
これで、そのハンドルをドラッグするとリサイズできるようになります。
また、17(HTBOTTOMRIGHT)のところを 2(HTCAPTION)に変えると、ウィンドウを移動できるようになります。
WPFになっても、WinAPIの知識は役立つものです。