Last-Modified:2011/06/19
戻る

foobar2000プラグイン(曲情報をSSTPで送信)

foobar2000で曲を再生する際に、曲タイトル等をSSTPにてSSP等の伺か系ソフトウェアに 送信するプラグインの開発記録です。


最新版ダウンロード(rev 1.1)

目次

2011/06/02 概略仕様定義

foo_sstp_lyricsが公開中止になったため、演奏情報を伺かで表示する手段が無くなってしまった。で、「無いなら作ればいいじゃん」ということで開発開始。 まずはおおざっぱな仕様決め。

2011/06/02 開発環境構築

次、開発に必要な情報集め。

プロジェクト名は・・・1分ほど真剣に悩んだあげく、FooToSSTP_PlayinfoSenderとし、MFCを使用したdll(ソケットサポート有り)の設定でソリューション作成。 foobarSDKの指示通り、以下の3つのプロジェクトをSDKからコピーし、ソリューションに追加。

さらに、以下をコピーし自分のプロジェクト(FooToSSTP_PlayinfoSender)の追加の依存ファイルに追加。 そして自分のプロジェクトの依存関係ダイアログを開き、上記3つに依存するように設定し、念のため現段階でコンパイルやリンクができるか確認。
4>ビルドに成功しました。 4> 4>経過時間 00:00:07.69 ========== すべてビルド: 4 正常終了、0 失敗、0 スキップ ==========
よし、成功。というわけで、次のステップに進みます。

2011/06/02 foobar2000に、プラグインとして認識してもらう

foobar2000にプラグインとして認識してもらわないと、SSTPのテストとかもままならないので、まずはここから着手。 SDK曰く、SDKディレクトリ内のヘッダファイルはfoobar2000.h以外#includeするな!とのことなので、仰せのとおりにする。

さて、SDKのドキュメントをサクサク読んでいく。今回のソフトに関係しそうな、コンポーネントの構造(Structure of a component)以下の項目をまとめると・・。

・・・え、ドキュメントこれだけ?APIの説明は?インターフェースの説明は?

ちょっとしょんぼりしながら使用するインターフェースについて調べる。staticなオブジェクトとする場合はplay_callback_staticを継承し、play_callback_static_factory_tのstaticオブジェクトで生成するようだ。
これに対し、動的に生成するオブジェクトとする場合はplay_callback_impl_baseを継承して実装すれば良いようだ。
今回はどちらにしよう。・・・動的生成にしてみようか。
というわけでさくさく実装。

Fb2kToSstpPlayInfo.h #pragma once #include "foobar2000.h" class CFb2kToSstpPlayInfo : public play_callback_impl_base { public: CFb2kToSstpPlayInfo(void); ~CFb2kToSstpPlayInfo(void); };
Fb2kToSstpPlayInfo.cpp #include "StdAfx.h" #include "Fb2kToSstpPlayInfo.h" CFb2kToSstpPlayInfo::CFb2kToSstpPlayInfo(void) { } CFb2kToSstpPlayInfo::~CFb2kToSstpPlayInfo(void) { }
FooToSSTP_PlayinfoSender10.cpp DECLARE_COMPONENT_VERSION("foo_ToSSTP_PlayinfoSender","1.0","about message goes here"); VALIDATE_COMPONENT_FILENAME("foo_ToSSTP_PlayinfoSender10.dll");
さてさてコンパイル。
Link: 1> ライブラリ Visual Studio 2010\Projects\FooToSSTP_PlayinfoSender10\Debug\FooToSSTP_PlayinfoSender10.lib とオブジェクト Visual Studio 2010\Projects\FooToSSTP_PlayinfoSender10\Debug\FooToSSTP_PlayinfoSender10.exp を作成中 1>Fb2kToSstpPlayInfo.obj : error LNK2019: 未解決の外部シンボル "void __cdecl pfc::myassert(wchar_t const *,wchar_t const *,unsigned int)" (?myassert@pfc@@YAXPB_W0I@Z) が関数 __catch$??$service_release_safe@Vmetadb_handle@@@@YAXPAVmetadb_handle@@@Z$0 で参照されました。 1>stdafx.obj : error LNK2001: 外部シンボル ""void __cdecl pfc::myassert(wchar_t const *,wchar_t const *,unsigned int)" (?myassert@pfc@@YAXPB_W0I@Z)" は未解決です。 1>Fb2kToSstpPlayInfo.obj : error LNK2019: 未解決の外部シンボル "void __cdecl _standard_api_create_internal(class service_ptr_t &,struct _GUID const &)" (?_standard_api_create_internal@@YAXAAV?$service_ptr_t@Vservice_base@@@@ABU_GUID@@@Z) が関数 "void __cdecl standard_api_create_t(class service_ptr_t &)" (??$standard_api_create_t@Vplay_callback_manager@@@@YAXAAV?$service_ptr_t@Vplay_callback_manager@@@@@Z) で参照されました。 1>Fb2kToSstpPlayInfo.obj : error LNK2001: 外部シンボル ""public: static struct _GUID const play_callback_manager::class_guid" (?class_guid@play_callback_manager@@2U_GUID@@B)" は未解決です。 1>Visual Studio 2010\Projects\FooToSSTP_PlayinfoSender10\Debug\FooToSSTP_PlayinfoSender10.dll : fatal error LNK1120: 外部参照 3 が未解決です。 1> 1>ビルドに失敗しました。
がーん。でもよくみるとpfc等先ほど追加したプロジェクトの名前が見える。ということは、生成したlibがうまくインポートできていないのか・・・。ということで、以下を実施。
1>ビルドに成功しました。 1> 1>経過時間 00:00:02.08 ========== ビルド: 1 正常終了、0 失敗、3 更新不要、0 スキップ ==========
よっしゃ通った!さっそくdllをcomponentsフォルダにコピーして・・・って認識されない・・・。
あ、dll名はfoo_*.dllの形になっている必要があるのか。気をとりなおして・・・

よし、無事起動もしたしバージョン番号等も見れた。さっそく自作クラスのオブジェクトを生成させよう。
FooToSSTP_PlayinfoSender10.cpp CFooToSSTP_PlayinfoSender10App::CFooToSSTP_PlayinfoSender10App() { m_pPlayInfo=new CFb2kToSstpPlayInfo; } CFooToSSTP_PlayinfoSender10App::~CFooToSSTP_PlayinfoSender10App() { delete m_pPlayInfo; }
foobar2000.exe によってブレークポイントが発生しました
ぎゃあああああああああorz。現象を追っていくと、どうもfoobar本体に対するポインタを貰う前に自作オブジェクトが生成されてしまい、エラーを起こしているようだ。ということで、play_callback_staticを継承するようにコード変更。
Fb2kToSstpPlayInfo.h #pragma once class CFb2kToSstpPlayInfo : public play_callback_static { public: CFb2kToSstpPlayInfo(void); ~CFb2kToSstpPlayInfo(void); void on_playback_starting(play_control::t_track_command p_command,bool p_paused); void on_playback_new_track(metadb_handle_ptr p_track); void on_playback_stop(play_control::t_stop_reason p_reason); void on_playback_seek(double p_time); void on_playback_pause(bool p_state); void on_playback_edited(metadb_handle_ptr p_track); void on_playback_dynamic_info(const file_info & p_info); void on_playback_dynamic_info_track(const file_info & p_info); void on_playback_time(double p_time); void on_volume_change(float p_new_val); unsigned int get_flags(void); private: unsigned int m_CallFlag; };
Fb2kToSstpPlayInfo.cpp #include "StdAfx.h" #include "Fb2kToSstpPlayInfo.h" CFb2kToSstpPlayInfo::CFb2kToSstpPlayInfo(void) { m_CallFlag=flag_on_playback_new_track; } CFb2kToSstpPlayInfo::~CFb2kToSstpPlayInfo(void) { } void CFb2kToSstpPlayInfo::on_playback_starting(play_control::t_track_command p_command,bool p_paused) { } void CFb2kToSstpPlayInfo::on_playback_new_track(metadb_handle_ptr p_track) { } void CFb2kToSstpPlayInfo::on_playback_stop(play_control::t_stop_reason p_reason) { } void CFb2kToSstpPlayInfo::on_playback_seek(double p_time) { } void CFb2kToSstpPlayInfo::on_playback_pause(bool p_state) { } void CFb2kToSstpPlayInfo::on_playback_edited(metadb_handle_ptr p_track) { } void CFb2kToSstpPlayInfo::on_playback_dynamic_info(const file_info & p_info) { } void CFb2kToSstpPlayInfo::on_playback_dynamic_info_track(const file_info & p_info) { } void CFb2kToSstpPlayInfo::on_playback_time(double p_time) { } void CFb2kToSstpPlayInfo::on_volume_change(float p_new_val) { } unsigned int CFb2kToSstpPlayInfo::get_flags(void) { return m_CallFlag; }
これはさくっと動作。ブレークポイントを使って、再生開始時にon_playback_new_trackが呼び出されていることも確認。 となると、次は演奏情報のゲットかな。

2011/06/02 演奏情報の取得

VisualStudioの出力ウィンドウに出すようにしてみる。

Fb2kToSstpPlayInfo.cpp void CFb2kToSstpPlayInfo::on_playback_new_track(metadb_handle_ptr p_track) { try{ file_info_impl info; CString buff; p_track->get_info(info); if(info.meta_exists("TITLE")){ buff.Format(_T("Now playing:%S\n"),info.meta_get("TITLE",0)); OutputDebugString(buff); } } catch(std::exception ex) { OutputDebugString(reinterpret_cast(ex.what())); } }
・・・あれ。たしかfoobar2000.hに
foobar2000.h #ifndef UNICODE #error Only UNICODE environment supported. #endif
って書いてあったのに、なんでmeta_existsとかの引数はchar*なんだ?まあいいや、コンパイル通ったし。実行!
Now playing: a|?a ae a2≫
なぜに化けるぅぅぅ!!!!
やっぱりさっきのchar*が引っかかる。wchar_tじゃなきゃおかしいのに。
とりあえずUnicodeについて調べてみると・・・。 なにいいいい、
符号単位が16ビットなutf-16と、符号単位が8ビットなutf-8があるのか!
foobar2000はcharだったからおそらくutf-8。自分はwchar_tだったからutf-16。そりゃだめだ。
ということは、扱いやすくするために変換しなきゃいけないのね・・・。
英語圏の人なら、ASCIIコードと扱い変わらないかもしれないけどさあ・・ぶつぶつ。
Fb2kToSstpPlayInfo.cpp void CFb2kToSstpPlayInfo::on_playback_new_track(metadb_handle_ptr p_track) { try{ file_info_impl info; t_size index; t_size u16buffsize; wchar_t *u16buff=NULL; CString buff; p_track->get_info(info); if((index=info.meta_find("TITLE"))!=pfc_infinite){ u16buffsize=MultiByteToWideChar(CP_UTF8, 0, info.meta_enum_value(index, 0), -1, NULL, 0); if(u16buffsize!=0){ u16buff=new wchar_t[u16buffsize]; if(MultiByteToWideChar(CP_UTF8, 0, info.meta_enum_value(index, 0), -1, u16buff, u16buffsize)){ buff.Format(_T("Now playing:%s\n"), u16buff); OutputDebugString(buff); } } delete[] u16buff; } } catch(std::exception ex) { OutputDebugString(reinterpret_cast(ex.what())); } }
これならどうだ!?
Now playing: 始まり
おっしゃあ!できた!MFCなコード(CStringね)も動いてる!
んじゃ、この抽出&UTF16変換な部分を別関数にしておいて、こんどはSSTPクライアントの部分に着手するか。
Fb2kToSstpPlayInfo.cpp // file_infoからmetaデータを抽出し、CString型に保存する bool CFb2kToSstpPlayInfo::GetMetaInfo(const file_info &info, const char* p_MetaName, t_size p_value_number, CString *pResult) { wchar_t *u16buff=NULL; try{ t_size index; t_size u16buffsize; if((index=info.meta_find(p_MetaName))!=pfc_infinite){ // バッファサイズ取得 u16buffsize=MultiByteToWideChar(CP_UTF8, 0, info.meta_enum_value(index, p_value_number), -1, NULL, 0); if(u16buffsize!=0){ u16buff=new wchar_t[u16buffsize]; // 変換実行! if(MultiByteToWideChar(CP_UTF8, 0, info.meta_enum_value(index, p_value_number), -1, u16buff, u16buffsize)){ pResult->Format(_T("%s"), u16buff); delete[] u16buff; return true; } delete[] u16buff; } } } catch(std::exception ex) { delete[] u16buff; OutputDebugString(reinterpret_cast(ex.what())); } return false; } void CFb2kToSstpPlayInfo::on_playback_new_track(metadb_handle_ptr p_track) { try{ file_info_impl info; CString buff; CString output; p_track->get_info(info); GetMetaInfo(info, "TITLE", 0, &buff); output.Format(_T("Now playing:%s\n"), buff); OutputDebugString(output); } catch(std::exception ex) { OutputDebugString(reinterpret_cast(ex.what())); } }

2011/06/02 SSTPサーバーとの通信

SSTPサーバーとの通信はすぐに終わる保証が無いため、別スレッドで行うことにする。
CAsyncSocketクラスで手抜きしつつ、テキストデータをSSTPに従い送受信。
テキストのフォーマットは・・・・え、UTF-8?
string_base型でデータ保持するように書き換えて、UTF-8で一貫して保持するようにするか。
さらばUTF16。君はデバッグ時だけ使うことにするよ。

細かいことはさておき、こんなんなった。ほぼ一発動作。

Fb2kToSstpPlayInfo.cpp

2011/06/03 設定画面の追加

最低限の機能はできたけど、できればフォーマットとかを弄りたい!ということで設定ダイアログを追加します。まずはfoo_sampleをベースに。
ダイアログ部分はWTL(Windows Template Library)が必須の模様。残念、使い慣れたMFCはだめぽ。WTLは
SourceForgeから入手できる。
stdafx.hをincludeしているとCStringが名前の衝突起こすようなので、ダイアログのソースからはstdafxを外し、そこだけWTLにしてみる。

1> Pref.cpp 1> TmSchema.h is obsolete. Please include vssym32.h instead. 1>Link: 1> ライブラリ visual studio 2010\Projects\FooToSSTP_PlayinfoSender10\Debug\foo_ToSSTP_PlayinfoSender10.lib とオブジェクト visual studio 2010\Projects\FooToSSTP_PlayinfoSender10\Debug\foo_ToSSTP_PlayinfoSender10.exp を作成中 1>Pref.obj : error LNK2019: 未解決の外部シンボル "void __cdecl WIN32_OP_FAIL(void)" (?WIN32_OP_FAIL@@YAXXZ) が関数 "public: __thiscall preferences_page_instance_impl::preferences_page_instance_impl(struct HWND__ *,class service_ptr_t)" (??0?$preferences_page_instance_impl@VCPref@@@@QAE@PAUHWND__@@V?$service_ptr_t@Vpreferences_page_callback@@@@@Z) で参照されました。 1>visual studio 2010\Projects\FooToSSTP_PlayinfoSender10\Debug\foo_ToSSTP_PlayinfoSender10.dll : fatal error LNK1120: 外部参照 1 が未解決です。 1> 1>ビルドに失敗しました。
よし、コンパイルは通った。あとはリンクか。そういえば、ヘルパーのプロジェクトあったから、それをソリューションに追加して、出力先やら追加ライブラリやらをいじれば大丈夫だろう。 これで無事リンクも成功、動作。あとはこれを自分用にいじるだけ。

2011/06/05 設定画面の改造

見た目改造のポイントは以下のとおり。

GUID関係の雰囲気はこんなかんじ。
// These GUIDs identify the variables within our component's configuration file. // {7EAEF188-437C-4C2A-889D-88DD9CCC3413} static const GUID guid_cfg_Port = { 0x7eaef188, 0x437c, 0x4c2a, { 0x88, 0x9d, 0x88, 0xdd, 0x9c, 0xcc, 0x34, 0x13 } }; const unsigned int default_cfg_Port=9821; static cfg_uint cfg_Port(guid_cfg_Port, default_cfg_Port);

さっそくいくつか変数を作り実行したが、文字列が化けて表示される・・・orz
どうやら、SetDlgItemTextがUTF-16かASCIIしか受け付けないためのようだ。ということでUTF-8からUTF-16に変換し、無事動作。
変換関数、作っててよかった!

Pref.h

Pref.cpp

とか思ったら、なんとpfc::stringcvtに、string_utf8_from_wide_tとか、string_wide_from_utf8とかあるじゃないですか。そっち利用する方向で書き換え。

2011/06/05 titleformatの解釈

titleformatを解釈させる方法がなかなか分からなかったが、結局以下のようになった。

  1. 文字列をコンパイルする。
  2. playback_format_title等を呼び出し、解釈させる。
文字列のコンパイルは、titleformat_object_wrapperクラスを生成することで自動的に行える。
void CPref::OnChanged() { const int buffsize=1024; wchar_t buff[buffsize]; static_api_ptr_t pbc; pfc::string8 str8res; // アクティブなコントロールが自動フォーマット処理対象なら、表示を更新 int id=::GetDlgCtrlID(GetFocus()); if(id==IDC_DEFAULTMESSAGE || id==IDC_REF0 || id==IDC_REF1 || id==IDC_REF2 || id==IDC_REF3 || id==IDC_REF4){ GetDlgItemText(id, buff, buffsize); pfc::stringcvt::string_utf8_from_wide_t <> str8buff(buff, buffsize); titleformat_object_wrapper tow(str8buff); pbc->playback_format_title(NULL, str8res, tow, NULL, playback_control::display_level_all); pfc::stringcvt::string_wide_from_utf8 wcharres(str8res); SetDlgItemText(IDC_SAMPLE, wcharres); } //tell the host that our state has changed to enable/disable the apply button appropriately. m_callback->on_state_changed(); }
ここまでで、設定画面は完成。


2011/06/11 SSTP部に設定したtitleformatを適用する

設定画面を作ったので、これにSSTP部をあわせる。 cfg_*はグローバル変数とし、ファイルをまたいで共有するようにした。うーん、構造体にまとめてればすっきりしたんだが。失敗したなあ。
Fb2kToSstpPlayInfo.cpp void CFb2kToSstpPlayInfo::on_playback_new_track(metadb_handle_ptr p_track) { try{ pfc::string8 str8res; static_api_ptr_t pbc; service_ptr_t script; static_api_ptr_t()->compile_force(script,cfg_DefaultMsg); pbc->playback_format_title(NULL, m_PlayInfo.defaultdata, script, NULL, playback_control::display_level_all); static_api_ptr_t()->compile_force(script,cfg_Ref0); pbc->playback_format_title(NULL, m_PlayInfo.ref0, script, NULL, playback_control::display_level_all); static_api_ptr_t()->compile_force(script,cfg_Ref1); pbc->playback_format_title(NULL, m_PlayInfo.ref1, script, NULL, playback_control::display_level_all); static_api_ptr_t()->compile_force(script,cfg_Ref2); pbc->playback_format_title(NULL, m_PlayInfo.ref2, script, NULL, playback_control::display_level_all); static_api_ptr_t()->compile_force(script,cfg_Ref3); pbc->playback_format_title(NULL, m_PlayInfo.ref3, script, NULL, playback_control::display_level_all); static_api_ptr_t()->compile_force(script,cfg_Ref4); pbc->playback_format_title(NULL, m_PlayInfo.ref4, script, NULL, playback_control::display_level_all); m_Port=cfg_Port; m_targetaddr=pfc::stringcvt::string_wide_from_utf8(cfg_TargetAddr); PostSSTP(); } catch(std::exception ex) { OutputDebugString(ex.what()); } } // SSTPサーバーにメッセージを投げる void CFb2kToSstpPlayInfo::PostSSTP() { CreateThread(NULL, 0, ThrowAwayThread, this, 0, NULL); } // SSTPサーバーとの通信スレッド DWORD WINAPI CFb2kToSstpPlayInfo::ThrowAwayThread(LPVOID lpParameter) { DWORD er; try{ CFb2kToSstpPlayInfo* pObj; pObj=static_cast(lpParameter); SPlayInfo data=pObj->m_PlayInfo; pfc::string8 sendbuff, recvbuff; char buff[SSTP_MAXSIZE]; // 送信準備 CAsyncSocket socket; AfxSocketInit(NULL); if(socket.Create(0, SOCK_STREAM, 0, NULL)==0){ er=GetLastError(); throw std::exception("Failed to create socket\n"); } DWORD arg=0; if(socket.IOCtl(FIONBIO, &arg)==0){ er=GetLastError(); throw std::exception("Failed to change blocking mode\n"); } if(socket.Connect(pObj->m_targetaddr, pObj->m_Port)==0){ er=GetLastError(); throw std::exception("Failed to connect to SSTP host\n"); } // SSTPヘッダ生成 sendbuff ="NOTIFY SSTP/1.1\r\n"; sendbuff+="Sender: foobar2000ToSSTP\r\n"; sendbuff+="Event: OnMusicPlay\r\n"; sendbuff+="Charset: UTF-8\r\n"; sendbuff+="Reference0: "; sendbuff+= data.ref0; sendbuff+= "\r\n"; sendbuff+="Reference1: "; sendbuff+= data.ref1; sendbuff+= "\r\n"; sendbuff+="Reference2: "; sendbuff+= data.ref2; sendbuff+= "\r\n"; sendbuff+="Reference3: "; sendbuff+= data.ref3; sendbuff+= "\r\n"; sendbuff+="Reference4: "; sendbuff+= data.ref4; sendbuff+= "\r\n"; sendbuff+="Script: "; sendbuff+= data.defaultdata; sendbuff+= "\r\n"; sendbuff+="ScriptOption: nobreak\r\n"; sendbuff+="\r\n"; if(sendbuff.get_length()>SSTP_MAXSIZE) throw std::exception("SSTP header too large\n"); // 送信&受信 OutputDebugString(sendbuff); if(socket.Send(sendbuff.get_ptr(), sendbuff.get_length())==SOCKET_ERROR){ er=GetLastError(); throw std::exception("Failed to send data to SSTP host\n"); } if(socket.Receive(reinterpret_cast(buff), SSTP_MAXSIZE)<=0){ // 受信はするけどなにもしない er=GetLastError(); throw std::exception("Failed to recv data from SSTP host\n"); } recvbuff=buff; OutputDebugString(recvbuff.get_ptr()); socket.Close(); } catch(std::exception ex){ // 全てのstd::exceptionを受け流す LPVOID lpMsgBuf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL); OutputDebugString(ex.what()); OutputDebugString((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return 0; } return 1; }
コンパイルもさくっと通り、無事動作。


2011/06/16 DirectSSTP調査

一部SSTPサーバーはDirectSSTPにしか対応していないので、DirectSSTPも実装することに。たしか、FMO(FileMapping Object)取得して、なんか するんだったよな・・・。調査開始。

ふむ、これなら実装は楽そうだ。
スクリプト格納に必要なメモリを確保して、それに関する情報をcopydatastructに格納し、メッセージをポーンとサーバーのhWndに投げればいいのね。
そしてそのhWndはFMO:sakuraに格納されている、と。今日はここまで。

2011/06/18 DirectSSTP実装

いよいよDirectSSTPの実装。とりあえず、ゴーストもSSTPサーバーも一つずつしかないと仮定しよう。
全体の流れとしては・・・。

え、WM_COPYDATAが投げ返されるのか。まずいな、foobar2000本体は処理してくれないぞ?
それでも問題ないといえば問題ないのだが・・・。できれば自分で受け取って処理したいな。
いっそ、偽窓でも作って、そこで処理させるか?うーむ。どのくらい処理重いんだろう。
ええい、作っちゃえ!

というわけで、コードの流れとしては・・・

・・・案外面倒だなあ。とりあえずFMOオブジェクト開くところまで実装しようか。
Fb2kToSstpPlayInfo.cpp // SSTPサーバーとのDirectSSTP通信スレッド DWORD WINAPI CFb2kToSstpPlayInfo::ThrowAwayDirectSSTP(LPVOID lpParameter) { HANDLE hMutex, hFMO; CFb2kToSstpPlayInfo* pObj; pObj=static_cast(lpParameter); SPlayInfo data=pObj->m_PlayInfo; pfc::stringcvt::string_wide_from_utf8 fmo(cfg_FMOName); LPCWSTR p=fmo; // Check if SSTP server has created FMO hMutex=OpenMutexA(0, FALSE, cfg_FMOName); if(hMutex==NULL) return 1; CloseHandle(hMutex); // Open FMO hFMO=OpenFileMapping(FILE_MAP_READ, FALSE, p); if(hFMO==NULL) return 1; // Get FMO size char *pFMO=static_cast(MapViewOfFile(hFMO, FILE_MAP_READ, 0, 0, 4)); if(pFMO==NULL){ CloseHandle(hFMO); return 1; } DWORD fmoSize=*reinterpret_cast(pFMO); CloseHandle(hFMO); return 1; }
あれ?mutexが成功しない・・・(そもそもロックしないコードだけど)。ちゃんとFMO名を"sakura"にしたんだけどなあ。
もしかしてFMO名が違うのか?ということで調査してみた。
まとめると、 じゃあ、mutexはSakuraFMOで取得し、失敗したらmutex不使用のままFMOアクセス試みるようにしてみよう。ああ、デンジャラス・・・

その後の、FMO解析部はとりあえず手抜きで、頭からFMO情報読み込んで最初のhWnd情報読み込むことにする。
Fb2kToSstpPlayInfo.cpp // Get FMO size char *pFMO=static_cast(MapViewOfFile(hFMO, FILE_MAP_READ, 0, 0, 4)); if(pFMO==NULL){ if(hMutex){ ReleaseMutex(hMutex); CloseHandle(hMutex); } CloseHandle(hFMO); return 0; } DWORD fmoSize=*reinterpret_cast(pFMO); // Read FMO pFMO=static_cast(MapViewOfFile(hFMO, FILE_MAP_READ, 0, 0, fmoSize)); unsigned int ptrCount=4; pfc::string8 buff, entry, field, item; int entryEnd, fieldEnd, itemEnd; HWND targetWnd; // search hWnd targetWnd=0; while(ptrCount(atoi(item)); break; } ptrCount+=itemEnd+2; } ReleaseMutex(hMutex); CloseHandle(hMutex); CloseHandle(hFMO);
あとはポイっとメッセージを投げるだけなんだけど、これどこかのhWndにresponseが帰ってくるんだよね。
その受け手も念のため作っておく。
あと、COPYDATASTRUCTのdwDataに通信ポート番号を念のためセット。
某ドキュメントでは「9801」だったけどね。きっと可変が正解でしょう。
あと、受信待ちは念のため強制タイムアウト付きで。とりあえず約1秒に設定。
どうせこれは別スレッドで走っているので、100秒待っても支障はないとは思うんだけど。
if(targetWnd){ CDummyDlg dlg; // ただのSSTP通信の受け手。 CFb2kToSstpPlayInfo* pObj; pfc::string8 sendbuff; pObj=static_cast(lpParameter); dlg.Create(IDD_DUMMY); SPlayInfo data=pObj->m_PlayInfo; data.hWnd=pfc::format_int::format_int(t_int64(reinterpret_cast(dlg.m_hWnd))); sendbuff=CreateSSTPHeader(data); COPYDATASTRUCT cds; cds.cbData=sendbuff.length(); cds.lpData=static_cast(const_cast(sendbuff.get_ptr())); cds.dwData=pObj->m_Port; SendMessage(targetWnd, WM_COPYDATA, NULL, (LPARAM)(&cds)); int i; for(i=0;i<100 && !dlg.m_bRecv;i++) Sleep(10); dlg.DestroyWindow(); }
以下、実行画面や結果。

NOTIFY SSTP/1.1 Sender: foobar2000ToSSTP Event: OnMusicPlay Charset: UTF-8 HWnd: 853952 Reference0: 南の島のハメハメハ大王 Reference1: ? Reference2: NHKみんなのうた ベスト50 Reference3: 14 Reference4: 2:28 Script: NHKみんなのうた ベスト50の南の島のハメハメハ大王を演奏中だよ。 ScriptOption: nobreak SSTP/1.1 200 OK スレッド 'Win32 スレッド' (0x85c) はコード 1 (0x1) で終了しました。


無事、動作。DirectSSTPメインなSSTPViewerでも動作確認!
まだIfGhostが未実装だけど、仕様検討大変そうだし、現行バージョンでリリースしちゃえ。
でもドキュメントはまだ出来ていないから、ここでこそっとアップにしよう。
来週あたりドキュメント作成して、vectorに公開・・・かなあ。