2009 年 8 月 のアーカイブ

スクリーンキーボード




















Vistaでソフトウェアキーボードというか、スクリーンキーボードを作ろうとしてハマったのでメモ。ただし、結局解決には至っていません。アプローチ変えてしまったので。

スクリーンキーボードを作るにあたって検索すると、imm32関連の関数を使ったサンプルが諸々出てくるのだけど、どうやらこの関数がWindows XPまでのもので、Vista以降はTSF(Text Services Framework)というものに置き換わっているらしい。

これに気づかず、

IntPtr hIMC = ImmGetContext(AHwnd);
IntPtr hKL = GetKeyboardLayout(0);
int dwSize = ImmGetConversionList(hKL, hIMC, AText, IntPtr.Zero, 0, GCL_CONVERSION);

とかやっていたのだけど、ImmGetConversionListの結果が必ず0になってしまう。

imm32 を使ったプログラムの互換性について

試してないけど、TSFのドキュメントは以下。

Text Services Framework Reference (Windows)

で、ここまでわかったところで、結局Linuxでやることになりました。


NSISで、C#で書かれた自作のDLLを使う




















NSISでインストーラ&アンインストーラを作っていて、インストール時に一緒にインストールしたAdobe AIRアプリケーションを、アンインストール時に同時にアンインストールしたいという要件。

Adobe AIRアプリケーションのアンインストールは、手動ならWindowsのコントロールパネルからできるので、ここの情報をレジストリから引っ張り出して実行すればOK。この辺は良くある手段。

で、レジストリからアンインストールに必要なプログラムの実行パス情報を持ってこようとしたら、Adobe AIRでは、Uninstall以下のサブキーの名前が毎回変わるので、検索する必要があることが発覚。

NSISにはレジストリの特定のキーを読む機能はあっても、キーを検索する機能はないので、DLLで拡張を作成。さっくり適当にC#でレジストリを検索して文字列を返すDLLを作成してみた。

ちなみにアンインストール情報はHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall以下にあります。

ということで、必要な動作はUninstall以下のサブキーを全部開いてDisplayNameが目的の名前と一致するサブキーを探すこと。ソースは以下。

SearchUninstall.cs

using System;
using System.Collections.Generic;
using System.Text;

namespace SearchUninstall
{
    public class SearchUninstall
    {
        public static string Find(string name)
        {
            string keyPath = "";

            // レジストリからの読み取り
            Microsoft.Win32.RegistryKey regkey =
                Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", false);
            // キーが存在しないときは null が返される
            if (regkey == null) return null;

            // 文字列を読み込む
            // 読み込む値が存在しないときは null が返される
            string stringValue = (string)regkey.GetValue("string");

            // sub以下のすべてのキー名を取得
            string[] keyNames = regkey.GetSubKeyNames();
            foreach (string k in keyNames)
            {
                string tmp_keyPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" + k;
                Microsoft.Win32.RegistryKey regkey2 =
                    Microsoft.Win32.Registry.LocalMachine.OpenSubKey(tmp_keyPath, false);
                try
                {
                    string displayName = (string)regkey2.GetValue("DisplayName");
                    if (displayName == name)
                    {
                        keyPath = tmp_keyPath;
                        break;
                    }
                }
                catch (Exception e)
                {
                    ;
                }
                finally
                {
                    regkey2.Close();
                }
            }

            //閉じる
            regkey.Close();

            return keyPath;
        }
    }
}

で、出来上がったDLLをNSISに読み込ませようとしても、どうにも動かない。

Dependency walkerで他のDLLと見比べてみると、C#で作られたDLLはエントリポイントが見えない。

そう言えばそんな話もあったような。

参考:WINAMP.COM | Forums – How can I use a C# DLL in NSIS

NSISからでは.NETのDLLが読めないらしい。ということで、見つけたのが以下。.NETのDLLを使えるようにするプラグイン。

Call .NET DLL methods plug-in – NSIS

CLR.zipをダウンロードしてきて、NSISをインストールしたフォルダのPluginsフォルダに入れておけばOK。

先に作ったSearchUninstall.dllはnsiファイルと同じ場所に入れておく。

で、nsiファイルは以下の感じ。Uninstall部分の必要部分を抜粋。

; アンインストーラ
Section "Uninstall"
  ; アンインストールで使用するプラグインの準備
  InitPluginsDir
  SetOutPath $PLUGINSDIR
  File "SearchUninstall.dll"

  ; AIRアプリケーションのアンインストーラを探す
  CLR::Call /NOUNLOAD "SearchUninstall.dll" "SearchUninstall.SearchUninstall" "Find" 1 "HogeHogeAir"
  pop $0
  CLR::Destroy

  ReadRegStr $4 HKLM $0 "UninstallString"

  ; AIRアプリケーションのアンインストーラをPassiveモード(自動処理)で実行する
  ExecWait "$4 /passive"

  ; 後片付け
  Delete "$PLUGINSDIR\SearchUninstall.dll"
SectionEnd

これで、アンインストール時にAdobe AIRアプリケーションのアンインストーラも勝手に起動して削除できる。

CLR超便利。プラグインを作るのが相当楽に。


FON2202のファームウェアのビルド




















La Fonera2.0ことFON2202のファームウェアのビルドに成功したのでメモ。

環境はDebian lenny。主な手順はここ。

build fon-ng

でもこのままやってもビルドできませんでした。

まずは手順にある通り、必要なパッケージをインストール。

$ sudo aptitude install subversion g++ libdigest-crc-perl ncurses-dev zlib1g-dev gawk bison flex autoconf intltool-debian intltool

他のサイトで、以下も必要みたいな記事を読んだのでインストール。

$ sudo aptitude install gettext pkg-config libz-dev ccache libncurses-dev unzip

libdigest-crc-perlがaptだと古いという話なので、CPANから最新をインストール。

$ cpan
CPAN> install Digest
CPAN> install Digest::CRC
CPAN> install YAML

で、手順通りレポジトリからtrunkを落とすとビルドできないので、tags/Flipper-2.2.5.0をダウンロードします。

$ svn co http://svn.fonosfera.org/fon-ng/tags/Flipper-2.2.5.0
$ cd Flipper-2.2.5.0/
$ ./install.sh
$ cd openwrt/
$ make menuconfig

メニュー画面ではとりあえず何もせずにそのままExitで設定を保存。

いざmake。V=99は詳細表示オプション。なくてもいい。

$ make V=99

超時間かかる。Core2QudaのVista上のVirtualPCで2時間くらい。

で、完了。

$ ll bin/
合計 8388
-rw-r–r– 1 dev dev   65536 2009-08-13 09:24 loader.bin
-rwxr-xr-x 1 dev dev   11860 2009-08-13 09:24 meraki-loader.elf
-rwxr-xr-x 1 dev dev 3039219 2009-08-13 09:24 openwrt-fonera2-vmlinux.elf
-rw-r–r– 1 dev dev  786432 2009-08-13 09:24 openwrt-fonera2-vmlinux.image
-rw-r–r– 1 dev dev 4653060 2009-08-13 09:24 openwrt-fonera2.image
drwxr-xr-x 3 dev dev    4096 2009-08-13 08:32 packages

packagesディレクトリにはipk形式のパッケージファイル一式ができてます。
これが
http://www.fonera.be/rep/FON2202/index.php
これってことか。サイトにはpackagesディレクトリに含まれていないものもたくさんあるけど。
逆に言うと、ipkファイルを作る必要性はなさそう。

このままでは正しくファームウェアを書き換えられたかわからないので、ちょっと軽くいじっておきます。
Fonera->facebookをスペースでチェックを外す

ちょっとここで通常のファームウェアの構成を調べる。

$ wget http://download.fonosfera.org/LATEST/20090421_FON2202_2.2.5.0_Flipper_DEVELOPER.tar.gz
$ tar xvzf ./20090421_FON2202_2.2.5.0_Flipper_DEVELOPER.tar.gz
20090421_FON2202_2.2.5.0_Flipper_DEVELOPER.image
upgrade
fonera20_fon.image

できたファイルはディレクトリを含まず上記3つ。

$ ll
-rw-r–r– 1 dev dev 4653060 2009-04-22 01:05 20090421_FON2202_2.2.5.0_Flipper_DEVELOPER.image
-rw-r–r– 1 dev dev 4529210 2009-04-22 02:50 20090421_FON2202_2.2.5.0_Flipper_DEVELOPER.tar.gz
lrwxrwxrwx 1 dev dev      48 2009-08-13 19:25 fonera20_fon.image -> 20090421_FON2202_2.2.5.0_Flipper_DEVELOPER.image
-rwxr-xr-x 1 dev dev     305 2009-04-01 00:57 upgrade

fonera20_fon.imageはシンボリックリンクでimageファイルの実体へ。で、upgradeがシェルスクリプトになってて、これが実行される、と。

$ cat upgrade
#!/bin/ash

VERSION=$(cat /etc/fon_version)
FAILSAFE=$(cat /etc/failsafe)

if [ "$VERSION" = "2.2.1" -o "$VERSION" = "2.2.2" -o "$VERSION" = "2.2.3" -o "$VERSION" = "2.2.4" -o "$FAILSAFE" = 1 ]
then
mtd -r write fonera20_fon.image image > /dev/null 2>&1
#(sleep 4; reboot) &
return 0

else return 1; fi

なるほど、バージョンチェックして必要ならmtdコマンドで書き換え実行ということね。ということは、すでに2.2.5になってるこのFONでは上書きできないということか。

というわけで書き換える。

$ vi ./upgrade

シンプルに以下の感じに。

#!/bin/ash

mtd -r write fonera20_fon.image image > /dev/null 2>&1
return 0

で、リンク先を書き直し。tar.gz作成。

$ rm fonera20_fon.image
$ ln -s openwrt-fonera2.image fonera20_fon.image
$ tar zcvf openwrt-fonera2.tar.gz openwrt-fonera2.image fonera20_fon.image upgrade

で、できあがったopenwrt-fonera2.tar.gzを管理画面経由でFONにアップロード。

Firmware Update
Failed to identify upload.

Σ(゚Д゚)ガーン

なんだろ。ちなみに公開されているtar.gzを展開→再圧縮しただけでも反映に失敗したので、何かtarの作り方に秘密があるっぽい。
仕方ないのでimageファイルを転送して、FON上のSSHで反映することに。
以下、FONのSSH。

# cd /tmp/
# mtd -r write fonera20_fon.image image
Unlocking image …
Writing from fonera20_fon.image to image …  [w]
Rebooting …

で、どうやら書き込み完了。wktkしながらブラウザで確認…

緑の画面から書き変わってオレンジの画面に…あれ?SSHが効かない…?ん?Facebookのアイコンは残ったまま…?

Facebookは画面から消すのに何か別の作業がいるのかもしれないと思うことにして、SSHは…?プラグインのところにも出てこないし…

しかもブラウザからファーム書き戻そうとしてもバージョンが同じだから上書きできないし!!

\(^o^)/オワタ

シリアルケーブル手に入れるまで放置で…

もしくは管理画面からアップできるtar.gzの作り方、か。

※追記(2009/10/12)

lostmanさんに教えていただいた方法で復旧できました。
FON2202をFailSafeモードで復旧させる : blog.loadlimit – digital matter –


FON2202の電源を入れたら無線クライアントとして繋がるようにする




















引き続き、La Fonera 2.0ことFON2202をいじる。

電源を入れたら自動で無線LANに接続して、動作を開始するようにする。これができれば電源ケーブルだけで取り回しがかなり自由に。あ、SSH開放したDEVELOPERファームを入れていること前提です。

無線LANに接続するところの参考サイトはこちら。
仙石浩明の日記: La Fonera を無線LAN 端末として使ってみる

まずはFONのFREESPOTとして待ち受けている無線を止めるところから。
というかアレなので一回全部止めてしまいましょう。
# wlanconfig ath0 destroy
# wlanconfig ath1 destroy

で、新たに1つ、staモード、通常の無線クライアントとしてath0を作成。
# wlanconfig ath0 create wlandev wifi0 wlanmode sta

接続先の設定。
# iwconfig ath0 essid "HogeHoge"
# iwconfig ath0 key s:hogehogewep00
# iwpriv ath0 mode 11g

で、起動。
# ifconfig ath0 up

一応これだけでアクセスポイントには接続できているはずなのだけど、IPは勝手に設定したりしないので、DHCPクライアントを使って取得。
# udhcpc -i ath0

では早速Pingを…
# ping www.google.co.jp
ping: bad address ‘www.google.co.jp’

あれ?

名前解決を…
# nslookup www.google.co.jp
Server:    xxx.xxx.xxx.xxx
Address 1: xxx.xxx.xxx.xxx

nslookup: can’t resolve ‘www.google.co.jp’: Name or service not known

おぉぉ

どうやら調べたらFirewallらしい。Firewall有効にしていると、無線側からのSSHもPingも通らない。
今回はLAN内に設置するため、その辺のセキュリティはいらないので、さっくりとFirewallを外すことに。

# /etc/init.d/firewall stop
# nslookup www.google.co.jp
Server:    xxx.xxx.xxx.xxx
Address 1: xxx.xxx.xxx.xxx

Name:      www.google.co.jp
Address 1: 66.249.89.99 jp-in-f99.google.com
Address 2: 66.249.89.147 jp-in-f147.google.com
Address 3: 66.249.89.104 jp-in-f104.google.com

おぉ、できたー。

…と、喜んだのもつかの間。1分ほど経つとath0の接続が切れる現象が。
色々調べて見ると、どうやらchillispotというものが起動して、設定を書きなおしてるらしいということが判明。
なるほど、無線アクセスポイントを構築するプログラムとのこと。
いらないね。

というわけで、起動を解除…しようとしたがどうにもどこから起動しているのかがわからない…
WANが有効になったことをトリガに起動されているらしいのだけど…
/etc/hotplug.d/以下にnetとifaceというディレクトリがあって、その辺を探ってみたのだけど、どうにも止められず。

起動するものはしょうがないということで、起動スクリプトを書き換えて対処。
# vi /etc/init.d/chillispot
do_start() {
の次の行に
return 1
として、即終了させておく。

この辺、OpenWRTにしてあれば入ってないと思うので無用な苦労しなくていいはず。
次回の課題だなぁ。

まとめ。
電源入で自動接続の設定はchillispotの編集をした後、適当に/root/にでもシェルスクリプトを作って起動時に実行するように仕込むだけ。
# vi /root/wlaninit.sh

#!/bin/sh

wlanconfig ath0 destroy
wlanconfig ath1 destroy
wlanconfig ath0 create wlandev wifi0 wlanmode sta 
iwconfig ath0 essid "HogeHoge" 
iwconfig ath0 key s:hogehogewep00
iwpriv ath0 mode 11g

ifconfig ath0 up
udhcpc -i ath0
/etc/init.d/firewall stop

で保存。

# chmod +x /root/wlaninit.sh
# cd /etc/rc.d/
# ln -s /root/wlaninit.sh S96wlaninit

いじょ。一度起動してから終了させるのが非常に無駄くさいがアクセスできなくなったりしてハマれる時間がないので今回はこれで。


マウスを動かしただけでCPU使用率が100%に行く現象




















Windowsを使っていると、だんだんと重くなってきて、マウスもガクガク飛ぶようになった。うーん?以前はこんなことなかったけどなぁと思って原因究明に乗り出すことに。

使用OSはWindows Vista Ultimate 32bit。半日使ってると重くなってきて、1日経つとまともにブラウザも扱えない。

Core2Quadでメモリ4GBな上、ブラウザしか起動していなくても遅いので、マシンスペック云々ではないことを先に断っておきます。

まぁ、結論から言うとDisplayLink製のUSBモニタが問題でした。というか、それに付随するアプリケーションとサービスか。当該のプロセスは以下の3つ。

  • DisplayLinkUI.exe
  • DisplayLinkService.exe
  • DisplayLinkManager.exe

ServiceとManagerは全ユーザのプロセスを表示で、管理権限にならないと削除できないので注意。

3つ落としてやったら途端に快適な環境が復活しました。これ単体ではさほどCPU食ったりしないので、意外と気づきにくいかも?ちなみに使ってるUSBモニタはSAMSUNGのSyncMaster U70。

OS起動時に2回に1回ブルースクリーンになったり、色々あやしさは感じていたのだけど、ドライバの出来が良くないなぁ…USBサブモニタ流行ってるから、同じような現象が大量発生しているんじゃないかと不安だわ。

調べてみたら、バージョンは4.6.17952.0。付属のCDから一度バージョンアップしてます。

今見たら新しいメジャーバージョン(5.1)出てたので試してみます。この分野は可能性感じるし、U70自体は結構気に入ってるので、早々に安定してくれることを期待。

ダウンロードはこちらから。

DisplayLink: DisplayLink: Windows Drivers

ウィルスバスター2009とか疑ってすまんかった。同じような現象に悩まされている人の一助になれば幸いです。

追記

5.1にしたら遅くなる現象は直った模様。USBモニタ上でのマウスポインタの動作が遅くなったけど、別にクリティカルじゃないのでOK。