Category: C / C++

Windows 7, 8 での3本スワイプによるデスクトップ切り替えの発表資料 / バイナリを公開しました。


どうもみむらです。

もう12月ですか・・。月日の過ぎるのは本当に早いですね。
私もいろいろなことがありすぎて、もう12月かと。そんな感じです。

いろいろと、今年は本当に反省に次ぐ反省の年だったように思います。
残った一ヶ月で、可能な限りリカバリーをしていきたいです。


2016/02/20 Update:

本記事の内容は Windows 10 環境では正常に動作いたしません。

Windows 10 環境をお使いの場合は、
Windows 10 で 3本指ジェスチャで仮想デスクトップを制御出来るようにしてみた。 – みむらの手記手帳
より、Windows 10 版をご利用ください。


 

さて、表題の件ですが、
これは各所の勉強会で2回ほど使ったネタで、
Mac の「3本指でタッチパッドをスワイプすると仮想デスクトップが切り替わる」
Mission Control のあれを Windows で実装してみた、という話です。

 

またこれは Windows 7 や 8 で動作するもので、Windows 10 のそれとはまた別ものです。

 

概要については次のスライドをご参照ください。

— 余談 —

同じネタを福岡と東京でやったのですが、
東京はかなり受けたのですが福岡の反応はイマイチでした・・。

んー。福岡受けするにはどうすれば良かったんだろう・・

— 余談ここまで —

 

 

また実際の動く雰囲気としてはこのような感じとなります

ディスプレイに対して直接描画命令を送って、スワイプしてる雰囲気を出したいなぁ・・(苦笑

 

実際の仕掛けとしては、CreateDesktop と SwitchDesktop を使って
ジェスチャを認識したらデスクトップを切り替えている流れになります。

 

CreateDesktop
http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms682124(v=vs.85).aspx

SwitchDesktop
http://msdn.microsoft.com/ja-jp/library/windows/desktop/ms686347(v=vs.85).aspx

 

またジェスチャの認識には、
Synaptics 社の開発キットを用いて、タッチパッドの入力を直接取得した上で、
指の本数とその移動距離を取得して判定しています。

直接取得していますので、
Windows のウィンドウフォーカスの問題や、
Windows API レベルでのマウス操作の制約を受けません。

具体的には、仮想マシンをフル画面で表示して、
当然マウスは仮想マシンにキャプチャされている状況においても動作します。

 

その一方で機種依存がありまして
”Synaptics 社製タッチパッド搭載機” と “3本指を認識できるタッチパッド” が必要になります。

 

見分け方としましては、マウスの設定画面から “Synaptics” のデバイス設定に入り、

image

こんな感じで「3本指で・・」という項目があるコンピュータであれば利用可能です。

ただ、古い機種において、ドライバのバージョンの関係から、3本指ジェスチャを認識できるのにもかかわらず「3本指で・・」という項目がない機種も存在しますので、
使用するためにはこの項目があるマシンが必須というわけではありません。

 

また、使用の際はもちろん、この3本指ジェスチャのオプションは OFF でお願いします。
(ON のまま使用されますと、両方のジェスチャが実行され、いろいろと意図しない動作が発生する恐れがあります。)

 


限りなくベータ(もしかするとアルファかもしれない)版ですが、
バイナリデータを公開します。

ソースコードに関しましては、思いつきで書いた関係で私自身にも難読な状態になっていますので
リファクタリングを実行した上で公開致します。

 

検証環境:
マシン:Sony VAIO Pro 13, Sony VAIO Z VPCZ21AJ
OS:Windows 7 x64, Windows 8.1 x64
アプリケーション: VMware Workstation 10

ダウンロードリンク:
http://mimumimu.net/beta/programs/synaptics_switchdesktop.zip

注意事項:
・Windows 10 の仮想デスクトップ機能とは互換性がありません。
(これについてはまた別途、Windows 10 対応版を出したいと思っています。)
・Windows 8.1 において、切り替え先のデスクトップにてストアアプリおよびそれに付随する機能が使用できません。
・IE 11 やその他一部のソフトウェアにおいて、切り替え先のデスクトップで正常に動作しないソフトウェアが存在します。ご了承ください。

 

要望等ありましたら、可能な範囲で対応します・・
https://mimumimu.net/community/
https://mimumimu.net/bbs/

ExQueueWorkItem を使って PASSIVE_LEVEL で呼び出せる命令を他の IRQL から実行させる。


どうもみむらです。

 

最近とある用があってカーネルモードドライバを書くことが出てきました。
なんだかんだ、Linux 用のドライバも Windows 用のドライバも慣れると楽しいですね。

(ブルースクリーンに行かないように考えながらコードを書くのが良い感じに脳みその体操になってとっても楽しいです。 気を抜くとすぐにブルースクリーンになりますし。)

 

さてさて。

Windows なカーネルモードドライバを書いている上でなかなかに問題になることの一つに
IRQL の話があると思います。

詳しくはこちらのウェブサイトに詳しくまとめられていますのでそちらにお任せするとして。
http://d241445.hosting-sv.jp/community/report/report11.html

要は各割り込みに優先度を付けて、すぐに行わなければならないやつをすぐに行えるようにする・・と
おおざっぱですがそのような感じです。

 

また各 IRQL によって実行可能な API が限られており、
もしそれを無視して実行すると

image

こんな感じで、 “IRQL_NOT_LESS_OR_EQUAL” な BSoD が発生します。
(ちなみにこれは KeBugCheckEx に 0xA と適当(?)な数字を設定して呼び出しました。)

 

各種コールバックでは DISPATCH_LEVEL でやってくるケースもあり、
PASSIVE_LEVEL でのみ呼び出せる関数をどうやって呼び出すか。


今回の一件は、
フィルタを挟み込んでデータをファイルに書き出すというようなドライバを書いていて、
どこかのタイミングで書き出せればいいと考えて、

ExQueueWorkItem を DelayedWorkQueue で呼び出して、
呼び出し先にファイル書き込みを書く・・という方法で対処しました。

 

コードとしてはこんな感じ

#include <Ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>

#define STR_LENGTH 0x1000

typedef struct _DATA
{
	LARGE_INTEGER time;
	wchar_t str[STR_LENGTH];
} DATA, *PDATA;

void _PassiveWrite(PDATA);

/* 外部から呼び出される関数。ここは IRQL <= DISPATCH_LEVEL であれば実行可能 */
void Write(PDATA d)
{
	WORK_QUEUE_ITEM workitem;
	PDATA data;

	/* 非ページプール上に領域を確保する */
	data = (PDATA)ExAllocatePoolWithTag(NonPagedPool, sizeof(DATA), 0x0616);
	if(data == NULL)
		return;

	/* データを準備する。 */
	ExInitalizeWorkItem(&workitem,_PassiveWrite,data);
	memcpy_s(data,sizeof(DATA),d,sizeof(DATA));

	/* Queue に登録する */
	ExQueueWorkItem(&workitem, DelayedWorkQueue);
}

/*
ExQueueWorkItem によって登録され、実行可能になった時に呼び出される。 
ここの IRQL は PASSIVE_LEVEL になる
*/
void _PassiveWrite(PDATA data)
{
	HANDLE handle;
	NTSTATUS status;
	UNICODE_STRING str;
	IO_STATUS_BLOCK statusblock = {0};

	/* ファイルオープン */
	{
		UNICODE_STRING path;
		OBJECT_ATTRIBUTES attr = {0};

		RtlInitUnicodeString(&path, L"\\??\\C:\\Logs\\nonohara.log");
		InitializeObjectAttributes(&attr,&path,
			OBJ_CASE_INSENSITIVE | 
			OBJ_KERNEL_HANDLE |
			OBJ_FORCE_ACCESS_CHECK,
			NULL, NULL);

		status = ZwCreateFile(
			&handle,FILE_APPEND_DATA,
			&attr,&statusblock,NULL,
			FILE_ATTRIBUTE_NORMAL,
			0,FILE_OPEN_IF,
			FILE_SYNCHRONOUS_IO_NONALERT,
			NULL,0
		);
		
	}

	if(!NT_SUCCESS(status))
	{
		ExFreePoolWithTag(data,0x0616);
		return;
	}

	/* 書き込む文字列を作る */
	str.Buffer = ExAllocatePoolWithTag(NonPagedPool,STR_LENGTH*2,0x1133);
	str.MaximumLength = STR_LENGTH*2;
	str.Length = 0;

	RtlUnicodeStringPrintf(&str,L"%I64u,%s\r\n",
		data->time,
		data->str
	);

	/* 書き込む */
	ZwWriteFile(handle, NULL, NULL, NULL, 
		&statusblock, str.Buffer, str.Length, NULL, NULL);

	/* バッファーをクリアする */
	ZwFlushBuffersFile(handle,&statusblock);
	ZwClose(handle);

	/* メモリを解放する */
	ExFreePoolWithTag(str.Buffer, 0x1133);
	ExFreePoolWithTag(data, 0x0616);
}

 

たとえばこれを応用して、
ドライバを開始したタイミングでファイルを開いてハンドルを握っておき
(その間、共有状態で CreateFile をしていないので他からのアクセスがロックされる。)
ドライバが停止するタイミングでバッファをフラッシュしてクローズする・・とか。
(その間バッファリングが効くので、たぶん良い感じになる・・はず)

 

後は何か、特定の動作があった場合に何かを呼び出すとかそういうのが書けます。

 

・・・とりあえず、個人的なメモ。

誰かドライバを書いていてこの記事で解決することがあれば・・。

Visual Studio の C++ プロジェクトでフィルタではなくフォルダを使う


お久しぶりです。みむらです。

なんだかんだ書きたいことは多々あるのですが、
書く気力と時間がどうしても・・。ごめんなさい。

 

さてさて。

image

なんだかんだで Visual Studio で C++ を用いたプログラムを書くことが多々ありまして、
その関係で一つ。

右側(Express 版だと左側の方が多いかも・・。)
にあるソリューションエクスプローラにおいて、
フォルダを追加しようと右クリックメニューの追加を見に行っても、

image

「新しいフィルター」となっており、フォルダの作成メニューではありません。
このときに、フィルタではなくフォルダを作って管理したい時にどうすればいいかという話です。

 


1.「すべてのファイルを表示」ボタンをクリックします。

image

なお、クリックすると下に示すように、
フィルタによる表示ではなくなり、ファイルツリーでの表示になります。

image

 

2.フォルダを作ります。

image

先ほどの「新しいフィルター」のメニューが、「新しいフォルダー」に変わります。


私自身は普段フィルタを使って作業していますが、
何となくフォルダでやってみたくなりまして調べてみて、
なかなか見つからなかったのでメモしました。

Visual Studio 2010 から 2012 のヘルプを見に行くようにする。


新年明けましておめでとうございます。みむらです。

・・あけおめ関連の記事は後で書くとしまして、
大掃除中に見つけたことで一つ。

 

現在私の環境には、Visual Studio 2010 と 2012 がインストールされており、
それぞれを使い分けています。

ただヘルプに関しては、両方インストールしておいても特に幸せなことはありませんので
ディスクを圧迫する関係から、いつか一つにまとめたいと思っていました。

今回、
Visual Studio 2010 から Visual Studio 2012 のヘルプを参照するようにして、
問題解決(?)しましたので紹介します。

 

環境:

Windows 8 Pro
Visual Studio 2010 Ultimate
Visual Studio 2012 Ultimate


1. Visual Studio 2010 をインストール。

2. Visual Studio 2012 をインストール。

3. Visual Studio 2012 のヘルプビューアにコンテンツをインストールする。

image

 

4. Microsoft Help Viewer 1.1 とその Language Pack をアンインストール

image

消す際には、 Language Pack から消すとよさそうです。

 


5.  ms-xhelp:// を Help Viewer 2.0 で処理するように関連づける。

http://mimumimu.net/software/blogup/vs2010_hv2.zip

ftype や assoc でできないかとやってみたのですがどうもうまくいきませんでしたので、
レジストリファイルを用意しました。

このファイルを展開して結合します。


手順は以上のようになります。

当方の環境で試しましたところ、ソースコード上の命令に対する F1 ヘルプも動作しました。
(挙動がわからない命令にキャレットを合わせて F1 を押す)

 

複数環境を入れてある方は、このような方法で統合して、
リアル空間だけではなく、バーチャル空間の掃除も済ませてみてはいかがでしょうか。

ビット演算と論理演算と高速化と。


みむらです。

「私と小鳥と鈴と」って中学か小学校かでやりませんでしたかね。
どうでもいいですね。ごめんなさい。

C でコードを書いていると、AND 演算や OR 演算をよく使うんですが、
よく “&” と “&&” だったり、 “|” と “||” を間違えていたりします。

や、眠たいときや全力で \バリバリバリバリ/ みたいなことしているときによくやるんですが、
自戒を込めてメモ。 たまにテストで填まった時によく確認するとこれ、ってこともあるので。

 

サンプル

#include <stdio.h>

int main(void){
	int a = 0xFF;
	int b = 0x64;

	printf("BITWISE = 0x%x, LOGICAL = 0x%x",a&b,a&&b);
	return 0;
}

実行するとわかりますが、結果として、

BITWISE = 0x64, LOGICAL = 0x1

こうなるかと思います。

 

つまり、

演算子 意味
& ビットごとの論理AND
&& 論理AND

となるわけで、意味が異なります。
時に、C ですと、結果が 0 以外であれば TRUE として処理するように考えていますと、
演算子を書き間違えていたときに問題が起きるので、

該当箇所は func() != 0 というような感じで書いておくといいのかも、とふと。

 

OR 演算も同じような感じで。

#include <stdio.h>

int main(void){
	int a = 0x100;
	int b = 0x64;

	printf("BITWISE = 0x%x, LOGICAL = 0x%x",a|b,a||b);
	return 0;
}

もちろん結果も、

BITWISE = 0x164, LOGICAL = 0x1

こんな感じで変わってきます。

 

 

最後に高速化について。

C や C++ ではコンパイラによると思うので、私はよくわかりませんが、
C# においては、演算子の使い分けで高速化ができるようです。

& 演算子 (C# リファレンス)
http://msdn.microsoft.com/ja-jp/library/vstudio/sbf85k1c.aspx

&& 演算子 (C# リファレンス)
http://msdn.microsoft.com/ja-jp/library/vstudio/2a723cdk.aspx

こちらのウェブサイトを見比べるとわかるのですが、

& 演算子の場合は双方を評価して結果を出しますが、
&& 演算子の場合は単なる論理 AND のため、最初の演算で結果が確定する場合は2個目以降は演算しません。

要は、
funcA() & funcb()
とあった場合は、結果にかかわらず両方を演算し、

funcA() && funcb()
とあった場合、 funcA() が False を返す場合、 funcb() は実行されません。

これを応用すれば演算が最小限で済み、演算が高速になることが容易に考えられます。
(ただ、評価順を最適化する必要があるかとは思います。)

 

またやろうと思えば、

if(hoge.IsEnable() && hoge.type == Hoge.HOGEHOGE) { }

のような、有効なら細かく調査していく みたいなことも書けるのだとは思いますが、
私のような人には危険な気がしますが。

 

 

そんなこんなで特に私の場合、ビットごとの論理演算をしているかどうか。
これを間違えないようにコードを書きたいですね。

ってことで、よく間違いをやらかすのでメモ。

 


追記(2012/11/04 7:07):

記事についてコメントをいただきました。

最後の部分について、
世間では「ショートサーキット評価」と言うそうで、言語仕様で明確に定義されているとのこと。

http://ja.wikipedia.org/wiki/%E7%9F%AD%E7%B5%A1%E8%A9%95%E4%BE%A1

また、返値もブーリアンではなく最後に評価した値となる言語もあるようで、いろいろとおもしろそうです。