Windows」カテゴリーアーカイブ

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/

どうも IPv6 の RA (Router Advertisement) は /64 限定らしい。

どうもみむらです。
なんだかんだ、私の家では NVR500 を使って良い感じでインターネットをしていたりします。

YAMAHA NVR500 と OCN でひかり電話と IPv6

ヤマハルータと Hyper-V でネットワーク的な隔離環境を作ってみる

過去に書いたこの2つの記事を見れば、どういう環境かは簡単に想像出来るかと思います(

 

なんだかんだで、 Hyper-V 側に ipv6 環境を持って行かないとなーと思いまして
セグメントを変えて RA (Router Advertisement) を使って配信しようと思ってハマりましたのでメモ。

(どうでも良い雑談をすっ飛ばしたい方は、下の方まで読み飛ばしてください・・w)

 


プロローグ:

まず、何となく適当に、

ipv6 prefix 2 dhcp-prefix@pp2:cafe::/80

ipv6 lan1/1 address dhcp-prefix@pp2:cafe::1/80
ipv6 lan1/1 rtadv send 1 o_flag=on

こんな感じで書いてみました。

設定としては、

プロバイダから指定されたアドレスのプレフィックス (dhco-prefix@pp2) に

”cafe” というアドレスをくっつけたものをサブネットプレフィックスとして配信する設定。

(o_flag = ON : Other Flag,  これが設定されていると、DHCPv6 等を使ってアドレス以外の情報を撮ってくるようになります。)

 

image

一見、なんとなく良さそうな感じで RA が配信されるのですが、

どうもシステムに IP アドレスが設定される気配が無く。

 

どうしようか・・と思っていた所で、ルータの設定画面のトップページを見てみますと

image

・・・!  “/56 じゃん!” (OCN IPv6)

なら、 /64 としていじればいいんじゃね! ということで、

ipv6 prefix 2 dhcp-prefix@pp2:1100::/64

ipv6 lan1/1 address dhcp-prefix@pp2:1100::1/64
ipv6 lan1/1 rtadv send 1 o_flag=on

こんな感じで書いてみたところ、(これの書き方については後述)

image

なんかとっても良い感じで出てきたのでにっこり。

もちろん、カメさん、歩きます。

 


原因調査:

・・とはいっても気持ち悪いので、原因を調べることに。

友人に聞くのが早いかな・・と思ったので

いつもいろいろとお世話になっている、あたがわさんに聞いてみることに。

そうしましたらさくっと、

「RA は /64 限定ですよ!」 と。

まじすか・・! ということで RFC を当たってみることに。

 

参考にしましたのは下記の2つ。

RFC 4862 : IPv6 Stateless Address Autoconfiguration

http://tools.ietf.org/rfc/rfc4862.txt

RFC 4291 : IP Version 6 Addressing Architecture

http://tools.ietf.org/rfc/rfc4291.txt

 

まず、RFC 4862 の 5.5.3 “Router Advertisement Processing” を見てみますと、

image

こんな感じでアドレスが設定されますよ、と書いてあります。

でもって、次に RFC 4291 の 2.5.1 “Interface Identifiers” を見てみますと、

For all unicast addresses, except those that start with the binary value 000, Interface IDs are required to be 64 bits long and to be constructed in Modified EUI-64 format.

すべてのユニキャストアドレス(ネットワーク内においてただ一つの特定ノードを指すアドレスのこと)は、b000 (2進数表記) で始まるもの以外のアドレスの Interface ID は、すべて 64bit 長の “Modified EUI-64” 形式で表現される必要がある。

ということで。(訳はものすごく適当です。)

 

ここから考えるに、まず RFC 4862 5.5.3 の図に照らし合わせると、

N は N=64 である必要があって、 “Link prefix” は残った分 (64bit) になるということが読めます。

(※ RA の場合のみ。 DHCPv6 で割り当てる場合は、もちろん自由に設定できます。。)

 

このことから、先ほど上の方に書いたような、 /64 ではないプレフィックスを持った

RA が飛んできても、今のところ RFC と比較した際に誤ったデータであると見ることが出来て、

弾いた、というところが読み取れます。

 

ただ、上の図に挙げたように、

/64 でないプレフィックスの RA を飛ばすことが出来ているところから、

もしかすると /64 でないアドレスの配信も行えるようになるんですかね・・んー。

 


そういえば:

最初の部分のラストで、

ipv6 prefix 2 dhcp-prefix@pp2:1100::/64

ipv6 lan1/1 address dhcp-prefix@pp2:1100::1/64
ipv6 lan1/1 rtadv send 1 o_flag=on

 

こんな感じで設定を書きましたが、

ipv6 prefix 2 dhcp-prefix@pp2:11::/64

とかでも動くのでは、と思う方結構いると思うんです。でも動かないんです。。

 

今回のケースだと、プロバイダーから振ってくるアドレスは /56 でした。

IPv6 は 16バイト毎に “:” で区切って表現しますが、

今回の場合はすでに 56 バイト分指定されていますので、

xxxx:xxxx:xxxx:xxoo:oooo:oooo:oooo:oooo

こんな感じ ( o が指定できる場所 ) になります。

また、IPv6 のアドレスでは、高い位の 0 は省略できる事になっています。

(数学や算数での数字の表現と一緒です。)

つまり、 1234:56:7::1  は、 1234:0056:0007:0000:0000:0000:0000:0001 と同値です。

 

・・・たぶんヤマハルータ内の処理プログラムのバグのような気がするのですが

ipv6 prefix 2 dhcp-prefix@pp2:11::/64

と書いた場合に一度、 dhcp-prefix@pp2:0011::/64 として展開されるようで、

:xxxx:xxoo:oooo::  の部分に  :xxxx:xx00:11oo:: として当てはめられて、

/64 ですから、 :xxxx:xx00:oooo::  として配信されてしまうようです。

(解析等はしていませんので、何とも言えませんが・・。

 

一方で、

ipv6 prefix 2 dhcp-prefix@pp2:1100::/64

と書きますと、

:xxxx:xxoo:oooo::  の部分に  :xxxx:xx11:00oo:: として当てはめられて、

/64 ですから、 :xxxx:xx11:oooo::  として配信される、と。そういう感じ。

 

・・・ちょっとヤマハさんの中の人に聞いてみようかなぁと思ったのですが、

どうやら明日から夏期休業らしく・・。どこかで聞いてみよう・・。

windbg と gdb に関係した基本操作メモ。

毎回調べている気がするのでメモ。
(たぶんころころ書き加えていきます

 

作業内容 windbg gdb
実行 g r
ブレークポイント bp b
ブレークポイント
一覧
bl i b
(info breakpoint)
ブレークポイント
削除
bc d
ステップ実行
(Step over)
p n
ステップ実行
(Step Into)
t s
メモリ内容表示 d
d(a,b,c,d…)
d
x/(o,x,u,t,8xb..)
変数内容表示 dv (変数名) print (変数名)

p *0x000  みたいなのも可

レジスタの値 (レジスタウィンドウを見る)

dd eax..

i r

i r eax..

逆アセンブル結果 (Disassemble ウィンドウをみる) disas (関数名)
ソースコード (ウィンドウを見る) list (関数名)

 

・・とりあえず、良く忘れるので(

 

あといくつかメモ:

 

gcc でビルド時にデバッグ情報を入れる:
→ gcc –g でビルドしてやる

objdump で Intel 記法で眺められるようにする:
→ objdump –M intel  で眺める。
ex)  objdump –M intel –D a.out   .. のように。

VC コンパイラでデバッグ情報を入れる:
→ cl /Zi でビルドする。

VC コンパイラ環境で objdump っぽいことをする:
→ dumpbin a.exe /disasm  とかやると良い感じ。

RtlUnicodeString.. 系の一部のAPI は 32767 文字以上を設定できないみたいです。

どうも、みむらです。

ちょっとカーネルモードドライバを書いていまして、
RtlUnicodeStringPrintf 関数で文字列を設定する時に、
変数のサイズを 32767 文字以上に設定すると
STATUS_INVALID_PARAMETER になって動かないことがありました。

まさかと思って 32766 にすると動く。

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

UNICODE_STRING text = {0};

text.MaximumLength = 32767;
text.Buffer = ExAllocatePoolWithTag(PagedPool, text.MaximumLength, 0x3939);
text.Length = 0;

// STATUS_INVALID_PARAMETER で失敗する。
RtlUnicodeStringPrintf(&text,L"HELLO");


// STATUS_SUCCESS になって text に "HELLO" が代入される。
text.MaximumLength = 32766;
RtlUnicodeStringPrintf(&text,L"HELLO");

ExFreePoolWithTag(text.Buffer,0x3939);

でもって、同じ文字列操作系の命令でも

RtlUnicodeStringCat は動く。

 

もちろん、 UNICODE_STRING は

typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR  Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

こんな感じで定義されてる。 ( http://msdn.microsoft.com/ja-jp/library/ff564879(v=vs.85).aspx )

 

謎い。

 

ってことで MSDN の説明を注意深く読んでみました。

・・・そうしましたら、双方の “DestinationString” の説明に差を見つけました。

 

RtlUnicodeStringPrintf:

DestinationString [out]
A pointer to a UNICODE_STRING structure that receives a formatted string. RtlUnicodeStringPrintf creates this string from the formatting string that pszFormat specifies and the function’s argument list. The maximum number of characters in the string is NTSTRSAFE_UNICODE_STRING_MAX_CCH.

RtlUnicodeStringCat:

DestinationString [in, out]
A pointer to a UNICODE_STRING structure. This structure includes a buffer that, on input, contains a destination string to which the source string will be concatenated. On output, this buffer is the destination buffer that contains the entire resultant string. The source string is added to the end of the destination string. The maximum number of bytes in the structure’s string buffer is NTSTRSAFE_UNICODE_STRING_MAX_CCH * sizeof(WCHAR).

 

・・・なんと。。。

 

でもって ntstrsafe.h に定義されている NTSTRSAFE_UNICODE_STRING_MAX_CCH を見てみますと、

#define NTSTRSAFE_UNICODE_STRING_MAX_CCH    (0xffff / sizeof(wchar_t)) 

こんな感じで書いてあるんですね。

 

何で双方に違いがあるのかはよく分かりませんが・・。

でも、0xffff 文字書き込めるものと 0x7fff までしか書き込めないものとがある、

ということで。。

もし文字列操作系で、バッファサイズは足りているのに格納できない場合

NTSTRSAFE_UNICODE_STRING_MAX_CCH をオーバーしていないか確認してみると良いかもしれません。

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 をしていないので他からのアクセスがロックされる。)

ドライバが停止するタイミングでバッファをフラッシュしてクローズする・・とか。

(その間バッファリングが効くので、たぶん良い感じになる・・はず)

 

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

 

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

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