無料ブログ作成サービス JUGEM
libmp3lameのAPIを使ったデコード

「libmp3lame」というLAMEに付属しているライブラリを使った、MP3ファイルのデコードを行う方法を簡単に紹介します。

が、サンプルとして書いたソースコードがそれほど短くなかったため、内容を自サイトに掲載しました。

こちらからアクセスしてください→ libmp3lameのAPIを使ったデコード

| ジェット | 全般 | comments(0) | trackbacks(0) | pookmark |
twitter APIのrelated_resultsの使い方

twitterのREST APIではいろいろなAPIが提供されていますが、「#newtwitter and the API」には(2010年時点で)新しいAPIが紹介されています。

ほとんどのAPIは最初のリンクで示した公式ドキュメントでも紹介されていますが、「GET /1/related_results/show/:id.{format} 」だけは公式ドキュメントに紹介されていません。なのでここに簡単に書いてみます。といっても分かる範囲でですが…

URL

http://api.twitter.com/1/related_results/show/:id.format

パラメーター

:idrequiredstatus IDを指定します。(「.../show/123456789.json」などのように指定)
include_entitiesoptional1やtrueを設定すると戻り値にEntityが入るようになります。

その他

OAuthは任意ですが、Rate-Limits(「API」とも呼ばれる)は消費されます。メソッドはGET、formatはjson(他は未確認)が使えます。

戻り値(response)の形式 - JSONの場合

[
  {
    "results": [
      {
        "score": 1,
        "annotations": {
          "ConversationRole": "Ancestor"
        },
        "kind": "Tweet",
        "value": { <status data object> }
      },
      {
        "score": 1,
        "annotations": {
          "ConversationRole": "Descendant"
        },
        "kind": "Tweet",
        "value": { <status data object> }
      },
      ...
    ],
    "resultType": "Tweet",
    "groupName": "TweetsWithConversation",
    "annotations": {
      "FromUser": <screen name>
    },
    "score": 1
  }
]

ルートオブジェクトは配列ですが、配列の要素は通常オブジェクトデータ1つのみです。"results"の中に含まれる"value"にはstatusオブジェクト(ツイートデータ)が入り、include_entitiesが有効の場合はこの中にentitiesが入ります。"FromUser"はスクリーン名(一般的にIDと認識されている文字列)です。注意点として、"annotations"内のオブジェクトはなぜかプロパティー名が大文字で始まります。

"annotations"内の"ConversationRole"には、元statusとの関係が入ります。確認できている値は「Ancestor」(返信元など)、「Descendant」(元statusに対する返信など)、「Fork」(元statusに対する返信?)があります。

なお、"results"の中にはidで指定したstatusは含まれません。

このAPIを使えば、in_reply_to_status_idをたどるよりも少ないRate-Limits消費量でツイートを取得できます。

| ジェット | twitter | comments(0) | trackbacks(0) | pookmark |
Vista以降のテーマを用いた「アニメーション」描画のやり方

Vista以降の標準コントロールでは、テーマ(一部を除く)を有効にしているとマウスカーソルを合わせるとじわりとハイライトし、カーソルを別の場所に移動するとフェードアウトするように元の状態に戻るような描画がなされます(クロスフェード)。

アニメーションを気にしない場合は、DrawThemeBackground関数(英語)などを使うだけでテーマを用いた描画が可能なのですが、他のコントロールに合わせてアニメーションを入れたい場合は、BeginBufferedAnimation関数(英語)やそれに関係する関数を使って描画を行う必要があります。

ただし描画側でアニメーションのためのタイマーを自力で作成する必要は無く、アニメーション前と後の(絵の)状態を描画してあげるだけでAPI側が自動的にアニメーションを行ってくれるため、わりと簡単にアニメーションを実現することが出来ます。

以下はWM_PAINT時にボタンの描画(と仮定)を行う際にアニメーション処理を加えたコードの例です。(具体的なボタンの描画は「DrawMyButton」というユーザー定義の描画関数が行うとします。)

HWND hWnd;          // ウィンドウのハンドル
BOOL bPushed;       // ボタンが押されているかどうか
BOOL bDisabled;     // ボタンが無効になっているかどうか
BOOL bHover;        // マウスポインタがボタン上にあるかどうか
BOOL bOldPushed;    // bPushedが変更される直前の値
BOOL bOldDisabled;  // bDisabledが変更される直前の値
BOOL bOldHover;     // bHoverが変更される直前の値

    HDC hDC;
    PAINTSTRUCT ps;
    hDC = BeginPaint(hWnd, &ps);
    // アニメーション実行中のタイマーから呼び出されたかどうか調べる
    // (タイマー内から呼び出された場合は自動的に描画が行われる)
    if (!BufferedPaintRenderAnimation(hWnd, hDC))
    {
        // ボタンの領域を取得する
        RECT rcClient;
        GetClientRect(hWnd, &rcClient);

        // 古い状態と新しい状態からフラグを設定する
        int iFromState, iToState;
        if (bOldPushed)
            iFromState = PBS_PRESSED;
        else if (bOldDisabled)
            iFromState = PBS_DISABLED;
        else
            iFromState = (bOldHover ? PBS_HOT : PBS_NORMAL);
        if (bPushed)
            iToState = PBS_PRESSED;
        else if (bDisabled)
            iToState = PBS_DISABLED;
        else
            iToState = (bHover ? PBS_HOT : PBS_NORMAL);
        // 状態が変化しているか調べる
        if (iFromState != iToState)
        {
            // 実行中のアニメーションを止める(実行中の場合止めないと予期しない表示に…)
            BufferedPaintStopAllAnimations(hWnd);
            // ボタンのテーマオブジェクトを取得する
            HTHEME hTheme = OpenThemeData(hWnd, L"Button");

            BP_ANIMATIONPARAMS animParams = { sizeof(BP_ANIMATIONPARAMS) };
            animParams.style = BPAS_LINEAR; // 現在はこのフラグのみ使用可能
            if (hTheme)
            {
                // フェードする時間を取得する
                GetThemeTransitionDuration(hTheme, BP_PUSHBUTTON, iFromState, iToState,
                    TMT_TRANSITIONDURATIONS, &animParams.dwDuration);
                CloseThemeData(hTheme);
            }
            else
            {
                // テーマが取得できなかったのでフェードする時間を適当に設定する
                // (0に設定するとフェードせずにすぐ切り替えることが出来る)
                animParams.dwDuration = (bHover) ? 200 : 800;
            }

            // アニメーションバッファを作成する
            HDC hDCFrom = NULL, hDCTo = NULL;
            HANIMATIONBUFFER hab = BeginBufferedAnimation(hWnd, hDC, &rcClient,
                BPBF_COMPATIBLEBITMAP, NULL, &animParams, &hDCFrom, &hDCTo);
            if (hab)
            {
                // 作成に成功した場合、hDCFromとhDCToにそれぞれ
                // 変更前後のグラフィックを描画する
                // (hDCFromやhDCToがNULLになる場合もある)
                if (hDCFrom)
                    DrawMyButton(hDCFrom, &rcClient, iFromState);
                if (hDCTo)
                    DrawMyButton(hDCTo, &rcClient, iToState);

                // 描画が終了したらバッファを解放してアニメーションを開始する
                EndBufferedAnimation(hab, TRUE);
            }
            else
            {
                // バッファが作成できなかった場合は普通の方法で描画を行う
                DrawMyButton(hDC, &rcClient, iToState);
            }

            // 古いフラグを更新する
            bOldPushed = bPushed;
            bOldDisabled = bDisabled;
            bOldHover = bHover;
        }
        else
        {
            // 状態変化がない場合は普通の方法で描画を行う
            DrawMyButton(hDC, &rcClient, iToState);
        }
    }
    EndPaint(hWnd, &ps);

BeginBufferedPaint関数という(BeginBufferedAnimationと引数が若干似ている)描画時に便利な関数がありますが、これとBeginBufferedAnimation関数を同時に利用すると上手くアニメーションを行えない場合があります。詳しい追跡調査が出来ていませんが、BeginBufferedPaint関数を使う場合は注意する必要があります。

参考:

| ジェット | Win32API | comments(3) | trackbacks(0) | pookmark |
64ビット版VBA用の64ビット版COMライブラリを作る

Office 2010(以降?)は32ビット版と64ビット版の両方が提供されており、それぞれVBAも32ビット版と64ビット版として組まれています。このとき、32ビット版は今まで通りのVBAですが、64ビット版のVBAは「Declareステートメント」を初めとしていくつかの機能が64ビット版Windowsに合わせるように改良が加えられています。

また、VBAでは通常のVisual Basic(6.0シリーズまで)と同様に「参照設定」から外部COMライブラリを読み込むことがでますが、このCOMライブラリは32ビット版と64ビット版の両方を読み込むことが可能です。

ここで64ビット版のCOMライブラリを作成したいとなった場合に、どうしても(インターフェイス以外の)ポインタデータを扱いたい時、ポインタの型をどうするかという問題があります。32ビット版であれば単純にIDLファイルに

typedef long LPVOID;

などと定義しておいて使いまわせばよかったのですが、64ビット版ではポインタ型を「long」として使うことはできません。そこで、(MIDLのキーワードである)「hyper」や「__int64」などを用いてもいいのですが、新しいMIDL(バージョン5.01より上)では「__int3264」というキーワードが使用できます。なおWindows標準ヘッダーである「basetsd.h」をインポートすると「LONG_PTR」型などを使用することが出来るため、こちらを使用するのが望ましいとされています。

実際の使い方は以下のようになります。

「basetsd.h」を使わない場合

#if __midl > 501
typedef __int3264 LPVOID;
#else
#ifdef _WIN64 // 適宜ビルドオプションなどで「_WIN64」の定義を切り替える
typedef hyper LPVOID;
#else
typedef long LPVOID;
#endif
#endif // __midl > 501

「basetsd.h」を使う場合

import "basetsd.h";

typedef LONG_PTR LPVOID;

ちなみに、64ビット版VBAでは「LongPtr」型と「LongLong」型が導入されており、MIDLにおける型との対応は以下の通りになります。

MIDLVB
shortInteger
intLong
longLong
hyperLongLong
__int3264LongPtr
| ジェット | Win32API | comments(0) | trackbacks(0) | pookmark |
Shell Extensionメモ
JUGEMテーマ:コンピュータ

Shell Extension(シェル拡張)のプログラミング時のちょっとした注意事項をメモしておきます。

SHShellFolderView_Messageの第1引数「hwnd」に指定するウィンドウはIShellViewのウィンドウではなくIShellBrowserのウィンドウ

内部で「hwnd」に対し「WM_USER+7」(WM_GETISHELLBROWSERなどと呼ばれてるメッセージ)を送り、戻り値に対して「IShellBrowser::QueryActiveShellView」を呼び出しています。SFVM_WINDOWCREATEDなどでビューのハンドルを保持している場合は、ほとんどの場合ビューの親ウィンドウがIShellBrowserのウィンドウになっているため、GetParent関数を使って戻り値を渡すと良いと思います。

・SHShellFolderView_Messageの多くのメッセージで指定するITEMIDLISTは「PCUITEMID_CHILD」

SFVM_ADDOBJECTやSFVM_UPDATEOBJECTのLPARAMで必要なITEMIDLIST(※SFVM_UPDATEOBJECTのLPARAMはLPITEMIDLISTの配列)は、絶対パスの「PCIDLIST_ABSOLUTE」ではなくビューの子要素に当たる「PCUITEMID_CHILD」(PCITEMID_CHILD、ITEMID_CHILDのポインタ)を指定します。また、メッセージを送るために新たにメモリを割り当てる必要はない模様です。(内部でCoTaskMemFreeが呼ばれることがない模様)SFVM_ADDOBJECTに指定する要素や、SFVM_UPDATEOBJECTの2番目の要素( ((LPITEMIDLIST)lParam)[1] に当たる値)などに対しては、内部でILFree関数やCoTaskMemFree関数が呼び出されるため、指定する際は必ず新たなメモリを割り当てて指定してください。(2011/06/30追記)

・Windows Vista以降のCDefViewで詳細表示にした時の、カラムからのグループ化表示のポイント

「詳細表示」(Details、FVM_DETAILS)モードで、カラムヘッダーの端にある下矢印をクリックすると、グループ化するためのチェックリストが表示されます。

カラムヘッダーのグループ化リスト

詳細表示の項目数はIShellFolder2のMapColumnToSCIDで列挙した分だけになりますが、このときに返すSHCOLUMNIDをWindows標準のPROPERTYKEYにすると、画像のように対応するグループ化方法が提示されます(画像の例はEasySFTPでPKEY_DateModifiedを利用)。逆に独自のグループ化を行いたい場合は、返すSHCOLUMNID(PROPERTYKEY)をオリジナルにし、ICategoryProviderを実装後CreateViewObjectで「IID_ICategoryProvider」をトラップする必要があります。

参考: 仮想フォルダサンプル(カテゴリ) - EternalWindows

※Windows標準のPROPERTYKEYを使うとGetDetailsOfの代わりにGetDetailsExが呼ばれるようになり、文字列へのフォーマット方法がWindows標準になってしまいます。

ファイルの種類ごとに対応付けられているIThumbnailProviderを取得

レジストリからShellExを参照し、「{e357fccd-a995-4576-b01f-234630154e96}」(IID_IThumbnailProvider)に設定されているCLSIDからインスタンスを作成すると取得できます。具体的なコード例はEasySFTPのShellDLL.cpp内の「MyCreateThumbnailProviderFromFileName」関数を参照してください。

| ジェット | Win32API | comments(0) | trackbacks(0) | pookmark |
IShellBrowserのBrowseObjectが呼び出されるようにする

(殴り書きの文章で読みづらくスミマセン…)

※Windows 7では以下の方法ではうまくいかないようです。2010/05/26追記
※Windows 7でうまくいく方法を追記しました。また、「SID_SShellBrowser」の表記を加えました。2010/05/29追記

IShellBrowserBrowseObjectメソッドは、IShellViewのウィンドウ内でフォルダがダブルクリックされたときなど、「ブラウザ」内で別の項目に遷移するときに呼び出されます。

ところが、Windows ExplorerによるIShellViewの実装である内部クラス「CDefView」(厳密にはさらに別の内部クラス)は、フォルダアイテムがダブルクリックされたときに単にIShellBrowserのBrowseObjectを呼び出すことはしません。IShellViewはCreateViewWindowによるウィンドウ作成時にIShellBrowserインスタンスが渡されるためそれを利用すればBrowseObjectを呼び出すことは可能ですが、CDefViewはそのインスタンスに対してIServiceProvider::QueryServiceを呼び出して新たにIShellBrowserのインスタンスを得て、そのインスタンスに対してBrowseObjectを呼び出そうとします。しかも、QueryServiceを呼び出すとき「guidService」に指定する値は「SID_STopLevelBrowser」ではなく「SID_SShellBrowser」(IID_IShellBrowserと同値)が渡されます

※ここでIShellBrowserインスタンスを取得できなかった場合、CDefViewは新たにWindows Explorerを起動してフォルダを表示しようとします。XPとそれ以前ではDDE通信を使ってその情報をブロードキャストするためそれを捉える事が出来ますが、Vista以降ではDDE通信は使われないためこの方法は使えません。
※Windows 7では「SID_SShellBrowser」の代わりに「SID_SInPlaceBrowser」(= {1D2AE02B-3655-46CC-B63A-285988153BCA})が渡されます。この定数はWindows SDK 7.0以降のShlGuid.hに定義されています。(参考: 「In-Place Shell Navigation with the WebBrowser Control on Windows 7 - EricLaw's IEInternals - Site Home - MSDN Blogs」)

したがって、IShellViewからのフォルダ遷移を自前のアプリケーションで処理したい場合は、BrowseObjectメソッドを実装すると同時に、IServiceProvider::QueryServiceメソッドで「guidService」が「SID_SShellBrowser」(および「SID_SInPlaceBrowser」)であるときの処理も実装する必要があります。

ちなみにこの方法で実装を行うと、ICommDlgBrowserを実装せずとも自前のアプリケーションでフォルダ遷移を行うことができるようになります。

P.S. Windows XP (SP2以降?)などで上記の実装をした場合、「マイ ドキュメント」など一部のフォルダをダブルクリックしても何も反応しないことがあります。この場合はIInternetHostSecurityManagerを実装し、QueryServiceで「SID_SInternetHostSecurityManager」を捉えるようにすると反応するようになります。

| ジェット | Win32API | comments(0) | trackbacks(0) | pookmark |