プログラミングの最近のブログ記事

GDI+とOpenType

| コメント(0) | トラックバック(0)
PostScriptベースのOpenTypeフォントはGDI+でサポートされていないのですね。

作り直しているgnyaclockしか私が触っている中ではGDI+は使っていないのですが、FontFamilyオブジェクト作成時にFontFamilyNotFoundが返ります。

Windowsが嫌われる原因の一つを見た気がしました。私はDTPな人ではないのでレンダリングの綺麗汚いはMSゴシック・MS明朝以外ではよくわかりませんが、プログラマとしてこれはちょっと(自粛)でしょう。

曲がりなりにもマイクロソフトも策定に参加した規格でしょうに。さりとて例えばChooseFontでOpenTypeを排除する方法もなく、そもそもこの制限自体がどこにも書かれていない。お話になりません。

codeguruにQGraphicsTextというのものが公開されており、これを組み込むと描画できました。ちなみにGPLv2。

gnyaclockは元々GraphicsPath経由で描画していましたから、GraphicsPathを生成するこのクラスはすぐに使えましたが、中央揃えのような位置合わせはMatrixを使ってTransformする必要がありました。

gnyaclockは借り物なので、全ソースを公開しているとはいえGPLにするのもどうかと思い、QGraphicsTextをDLLに分離してみようと思っています。

Visual C++でvsscanf

| コメント(0) | トラックバック(0)
ウィンドウの位置を1エントリで自然に記録しようと"%d %d %d %d"で書き込んだら、読み出す時に困りました。
もちろんsscanf系で取れるのですが、ワンクッション挟むにはvsscanf系が必要で、これがVisual C++にはありません。

有名どころでは、Cライブラリの内部関数_input_l及び_winput_lを使う方法がありますが、内部関数なので静的リンク必須ですし、互換性は保証されません。

簡易実装する方法はいくらでもあります。数値以外がなく区切り文字が決まっているなら、strtokで切り出しながらsscanfに渡す手があるでしょう。

でももっともっと汎用的に、と調べていたら、面白いものを発見しました。
flipcode - vsscanf for Win32

va_listの中身は大抵の実装では引数(格納する変数へのポインタ)の配列なので、sscanf用に並べ直せばいいじゃないか、というものです。引数の生成はスタックの操作ですから、インラインアセンブラが使われています。

これも有用だったのですが、x64非対応です。x64ではインラインアセンブラ自体が使えなくなっていますし、呼び出し規則も違います。

というわけで、アセンブラ部をasmファイルに分け、x86/x64両対応にしてみました。
vsscanf.zip

同梱プロジェクはVisual Studio 2008用で、カスタムビルド規則として"Microsoft Macro Assembler"を有効にしてありますが、このままではx64が通りません。
Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin\x86_amd64
にあるml64.exeをml.exeにコピーする必要があります。

この辺はマイクロソフトの不備で、細工せずml64.exeを呼び出す方法がないのです。x86_amd64\ml.exeを用意するか、x64用カスタムビルド規則を作成するかしかありません。

flipcodeも私の移植版も、有効な%を数えてはva_listから取り出すので、%の方が多ければsscanf同様に即死するのでご注意。

(2013/02/06追記)
Visual Studio 2010だとml.exeとml64.exeを自動で切り替える仕組みが入っていました。
やはり追加カスタムビルド規則は2010以降への変換後が面倒になるので、x86_amd64\ml.exe用意が無難と言えるでしょう。

Windows 8のUSBスタック

| コメント(0) | トラックバック(0)
USBのポートリセットに使われていたIOCTL_USB_HUB_CYCLE_PORTがWindows 8で復活したようです。

IOCTL_USB_HUB_CYCLE_PORT control code (Windows Drivers)

Vistaで削除され、一部では困っていた人もいるのですが、XP終了間際で救済措置ということでしょうか。

SendMessageTimeoutのバグ

| コメント(0) | トラックバック(0)
何か変だと思ったら、条件は完全にはわかりませんが、SendMessageTimeout失敗時のGetLastErrorの戻り値が内部で更新されていない場合があるようです。

オンラインのMSDNライブラリ当該頁にもツッコミがありますが、少なくともVistaと7で、ハングしている可能性があるウィンドウに対し、SMTO_ABORTIFHUNGを指定して制御が即時に戻った場合に発生しています。

戻り値が0(ERROR_SUCCESS)だったり5(ERROR_ACCESS_DENIED)だったり、平たく言えば1460(ERROR_TIMEOUT)以外が入らない?

メッセージフックとDLL開放 (3)

| コメント(0) | トラックバック(0)
年初の(1)で書いた、WH_CALLWNDPROCRETで入れたら抜けないプロセスの話ですが、メッセージ専用ウィンドウかもしれません。

というのは、何気なく作ってみたら全く同じ挙動になったのです。メッセージ専用ウィンドウを持つアプリをMsgWnd.exe、ウィンドウプロシージャにグローバルフックを注入するアプリをProcHook.exeとして、
  • ProcHook.exeが先に立ち上がるとDLLが注入される
  • MsgWnd.exe先だと注入されない
  • MsgWnd.exeを終了させてもDLLは開放されない

話は単純で、ブロードキャストのメッセージが届かないために、ウィンドウプロシージャが呼ばれずフックが外れないため、DLLが開放されない、ということになります。

またWindowsによるDLL注入時にも解放時と同様、ウィンドウプロシージャが呼ばれる必要があると言えそうです。
とすれば、MsgWnd.exeが後からの場合はWM_CREATEその他、生成時のメッセージによってDLLが注入されると説明できます。

結局、メッセージ専用ウィンドウを列挙する方法がないため、(2)に書いた解決方法は変わらないだろうと思うのですが、フックDLLの開放ぐらいはOSで面倒を見てもらいたいものです。

(2012/12/18追記)
FindWindowExでHWND_MESSAGEを指定してループを回せば列挙できるのでした。WM_NULLを投げれば目的は達成できそうです。
sizeinspector.png備忘録です。

Xcodeでビルド時に"Content rectangle not entirely on screen with the menu bar"というWarningが出る場合の対処。

WindowのSize inspectorで画面内に収まるよう移動させれば解決します。或いはCenter Vertically及びCenter Horizontallyにしてしまうか。

左下原点だからか、コントロールが増えてウィンドウが大きくなってくると発生します。

リダイレクトとUnicode

| コメント(0) | トラックバック(0)
Windowsにおけるコンソールアプリの標準出力が実は、コードページ932(MS-DOS OEM)をデフォルトとしている、と最近気付いたお話です。

何となく知ってはいましたが、何を意味するのかわかっていませんでした。これが理由でwprintf等を使ってさえ、setlocaleしないと日本語が表示できなかったのです。

さてこれまた最近、リダイレクトに興味を持ちました。Microsoftが提供しているサンプルがあるのですが、これのUnicode入力をどうしたものかと調べてみたのです。

その中で上記CP932を理解したのですが、子プロセスになるコンソールアプリをUnicodeでコンパイルしようと何しようと、親プロセスのReadFileには常に8ビットで文字が取得されました。

唯一違ったのがcmd.exe /uによるUnicodeパイプ出力のコンソールで(ただし内部コマンド限定)、このことは子プロセス側で対応できる(すべき)だろうことを示唆していました。

ならば標準関数以外の方法でUnicode出力すればよいはず、ということで見つけたのがWriteConsoleでした。コンソール系APIは触ったことがなかったので未知の世界です。

WriteConsoleを使えば、setlocaleしなくても日本語を表示できたのですが、説明にある通りリダイレクトされたハンドルには出力できませんでした。代わりに、WriteFileで親プロセスにUnicode文字列を渡すことはできました。

# WriteFileをコンソールに使うと、8ビット幅のつもりで
# 文字列を出力されるお馴染みの問題が出ます

最終的に、GetStdHandle(STD_OUTPUT_HANDLE)で取得されるハンドルをGetFileTypeに渡し、FILE_TYPE_CHARの場合にWriteConsole、そうでなければWriteFileすることで解決しました。

...いびつなOSですね。

時限MessageBox

| コメント(0) | トラックバック(0)
Windows XP以降に実装されているUndocumentedなMessageBoxTimeoutというAPIが密かに知られていたらしいのですが、私は最近知りました。
で、これがuser32.libにも含まれていました。

というわけで、

#ifdef IDTIMEOUT
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int WINAPI MessageBoxTimeoutA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType,WORD wLanguageId,DWORD dwMilliseconds);
int WINAPI MessageBoxTimeoutW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType,WORD wLanguageId,DWORD dwMilliseconds);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#ifdef UNICODE
#define MessageBoxTimeout  MessageBoxTimeoutW
#else
#define MessageBoxTimeout  MessageBoxTimeoutA
#endif // !UNICODE
#endif // IDTIMEOUT

IDTIMEOUTはXP以降でだけ定義されるので、そのまま流用。
これは名前の通りタイムアウト時の戻り値ですが、MB_OKの場合は常時IDOKが返るので注意。

wLanguageIdはMessageBoxExの説明を参照。通常は0。

MessageBox系APIは全て内部でこれを呼んでいる(dwMillisecondsに0xFFFFFFFF)と書いているサイトもありました。果たして49.7日で閉じるか否か。恐らくWaitForSingleObject同様に無期限扱いになるのでしょう。

今さらXPより前もないですが、ないOSではフックを仕掛けてWM_INITDIALOGの中でSetTimerして、ということになるのでしょう。別スレッドからPostQuitMessageする方法は、親ウィンドウを指定した場合に固まるようなので。
拙作のウィンドウクラスに細工をしたらハマったというお話です。

きっかけはプロパティリスト(SetProp等のあれ)が遅い、と小耳に挟んだことでした。大したウィンドウを作るわけでもありませんが、更新頻度が異様に高いアプリは作ることがあるので、別の方法を試してみようとしたのです。

プロパティリストにはthisポインタを入れておき、メンバ関数でメッセージを処理するというお馴染みの使い方ですが、代わりといってもWTLみたいな頭おかしい実装は技量が及ばないので、静的std::mapにしました。

# 旧BorlandのVCLもWTLと同じ方法だったようですね

これを使って、PnPメッセージを処理するためダミーウィンドウを別スレッド上に実装した、ハードウェアを操作するクラスを書きました。

動作の確認としてコンソールアプリでグローバルに定義し、mainを抜けると...手元ではウィンドウクラスのオブジェクトよりstd::mapオブジェクトが先に破棄され、ウィンドウプロシージャ内でアクセス違反が発生してしまいました。

グローバルのオブジェクトは生成・破棄の順が規格上は未定義で、例えば何かのクラスに従属させるような制御方法もないとのこと。ちなみにマイクロソフトの場合、コンパイル時の出現順に生成し、破棄はその反対順だそうな。

「グローバル」というより静的な(staticに限らず)オブジェクトの寿命をコンパイラが区別できないという話らしく、静的メンバにしても意味はありません。

手っ取り早い回避法は、
  1. プロパティリストに戻す
  2. 後始末関数で明示的にDestroyWindowできるようにする
  3. グローバルで定義せず、他の関数からはポインタ経由とする
といったところでしょうか。

ちゃんとやれば自然と3.になりますが、2.が無難なところでしょうか。デストラクタからも後始末関数を呼んでおけば(始末済みチェックの上で)、3.では明示的に呼ばずに済みます。

Xorshiftのライセンス

| コメント(0) | トラックバック(0)
歴史が新しい擬似乱数アルゴリズムとしてXorshiftというものがあります。偏りもあるという話ですが、何しろ速い。シードに注意しつつ、乱数パディングぐらいには適任と思います。

さて、これがGPLだという噂があります。そもそもアルゴリズムに著作権は発生しませんが、サンプルコード流用の場合にどうなるか。

発表されたのは"Journal of Statistical Software"という雑誌で、この雑誌自体がCreative Commons Attribution 3.0 UnportedとGPLv2を明言しています。論文自体が前者、ソースコードが後者です。

他の論文ではソースコードがSupplementsとして別途ダウンロードできるのですが、Xorshiftの論文は記事自体しかありません。ソースコードは別途ASCIIファイルで提出せよ、と手順のところにありますので、記事中のソースコードは記事扱いと見るのが自然です。

また論文中のソースコードはとても短く、計算部分はわずか1行。元々GNUは短いソースコードへのGPL適用は「やめとけ」というスタンスですし、これだけ短いと同じアルゴリズムなら誰が書いても同じようなコードになるでしょう。

以上のことから、XorshiftがGPLというのは迷信だと考えます。ただCreative Commonsではあるので、文章として引用した際に原著作者を明記する必要はあります。
<<前のページへ 12345678

アーカイブ

ウェブページ

Powered by Movable Type 5.2.13

ホームページ