Windows 7購入したので、Windows 8は購入しないけど![]()
。
Windows 8に追加される予定のAPIを眺めててWindows Runtime以外の自分的に気になるものを・・。
まず、インターネット絡みだと、Web Sockets。
WinHttp でも、WinHttpWebSocketで始まる一連のAPIが追加されるようです。WinHttpは、後はWPAD(Web Proxy Auto-Discovery)絡みも拡張されて新たなAPIが追加されるっぽい。
後は、COMオブジェクトでネイティブから利用できるXML HTTP Request 2 略してXHR2?。
WinInetやWinHttpを直接扱うと、だるいんです
(WinHttpにはCOMラッパーも用意されてるが)。XML HTTP Request 2というぐらいだから、その前のバージョンもあるんですが、大きな違いは、IXMLHTTPRequest2::SetCustomResponseStream 。これで、レスポンスを特定のストリームに受信できるようになるわけで、以前はこの仕組みがなかったので、巨大なファイルをダウンロードする時に、直接ファイルに保存とかできなかったのですが、これで解決
。
インターネット以外だと、今更感があるが、Rich Edit 。OS付属のRich Editコントロールはずっとバージョンが4.1のままだったんだが、どうもRich Edit 8.0とパワーアップするっぽい。
TOM(Text Object Model) がバージョン2になって、ネイティブイメージサポートなど、画像の挿入やRichEdit Friendly Name Hyperlinks の扱いが楽に。
ちなみに、Twitter APIの変更などで動かなくなって、放置しているPiyotter は、タイムラインの描画にRich Editコントールの機能つかって描画してました。
Rich Editコントロールの各バージョンの機能については
これぐらいしかおもしろそうなのない![]()
。
前回 簡単な固定スレッド数のスレッドプールを実装してみたが、自分で実装する事はバグの温床になりかねないわけで、最初はWindows Vistaで追加された新しいスレッドプールAPIを利用して実装しようとして、断念してましたが、せっかくなので。
まず、Vistaで追加された新しいスレッドプールAPIについては、
を参考に。
新しいスレッドプールAPIはプロセスに複数のスレッドプールを作成できたり、ペンディングの操作をキャンセルできたり、何より天下のマイクロソフト様が実装してるので、使わない手はないのですが、ちょっと、問題が・・・
Delphiでは、サブスレッドとGUIスレッドであるメインスレッドを簡単に同期するための仕組みとしてTThread.Synchronizeメソッドがあるわけですが、メインスレッド側で待機操作をする場合、定期的にCheckSynchronizeメソッドを呼ばないと、サブスレッド側でTThread.Synchronizeされてた時に、デッドロックが発生しますが、Vistaで新しく追加されたスレッドプールAPIの、例えば、ワークコールバックが完了するまで待機するWaitForThreadpoolWorkCallbacks APIを見ると・・・
タイムアウトを指定できません。要するに定期的にCheckSynchronizeメソッドを呼べないわけで、あぼーん![]()
。
TThread.SynchorizeはDelphi独自のメカニズムで同じような事を直接WinAPIのSendMessageでやっても、デッドロック(これからは、TThread.Queue(PostMessage)の時代?)。
単純に1対1にマッピングはできなくてある程度自分で作り込む必要あるっぽいね
。
まぁ、とりあえず、Vistaで追加された新しいスレッドプールAPIのヘッダだけでも
Delphiがウンコ環境たる理由は標準で非同期操作がサポートされていないことです(他にも色々ないないだらけで、ウンザリなんですが・・)。JavaではFuture、.NETではIAsyncResultを使ったAPM (Asynchronous Programming Model)、EAP (Event-based asynchronous pattern)や最新の.NET 4ではTaskを使ったTask Parallel Library など非同期処理がお手軽にできるのですが、Delphiには標準でねぇ・・・・
ということで、勉強をかねて色々調べて簡単な実装をしてみました。最初は.NETのIAsyncResultをぱくろうとしましたが、GC(ガベージコレクタ)のない環境では実際に使ってみるとちょい無理あるということがわかったのでしょぼくれていたのですが、いいのを見つけました。Windows8でサポートされるというWinRTのIAsyncInfo(IAsyncAction,IAsyncOperation<TResult>)です(要するに.NETの簡易版Taskクラスじゃ?)。
実装したスレッドプールはワーカースレッド数が固定で変更はできません(最小・最大スレッド数を指定して、負荷に応じて実際のスレッド数をその範囲で増減なんて自分には実装できないので・・)。各メソッドの意味は実際にWinRTを試したわけじゃないですが、だいたい同じだと思うので、そのまんまMSDNのWinRTのWindows.Foundation namespace やWindows.System.Threading namespace の該当する説明を参考に・・
使い方は、TThreadPool.RunAsyncメソッドで非同期で実行する操作をパラメータとして渡します。RunAsyncメソッドを呼んだだけでは非同期操作が開始されません。開始するには戻り値のIAsyncAction.Startメソッドを呼びます。
非同期操作の終了を待ち合わせるにはIAsyncAction.GetResultsメソッドを呼びます。Startメソッドで開始された非同期操作は終了時に必ずIAsyncAction.Completedイベントが発生します(Cancelメソッドでキャンセルされた場合でも発生)。
実用的なアプリは通常、任意の数の非同期操作を行うと思いますが、上記だけじゃ役不足で、そのための仕組みがThreading.AsyncUtils.pasに定義されている、TAsyncGroupクラスです。
非同期操作をグループとして管理するクラスです。TThreadクラスを直接派生させる場合も同じですが、基本、GCのない環境では非同期操作の終了待ち合わせをしないと死にます。例えば、非同期操作の内部で、TThread.Synchronizeメソッドなどで、フォームのメンバにアクセスする場合、非同期操作が完了する前にフォームが閉じられたりすると、アクセスバイオレーションが発生します。TAsyncGroupクラスはグループとして管理するクラスというわりには、何もしない優れものクラス
でAddメソッド、Removeメソッドで自前で追加・削除してください・・![]()
Closeメソッドでグループをクローズします。クローズした後は追加できません。WaitForEmptyでグループが空になるまで待機します。要するにこれで終了待ち合わせを行います。
IAsyncInfo.Startメソッドで開始する前に必ずTAsyncGroup.Addで追加、IAsyncInfo.Completedイベント内などで必ずTAsyncGroup.Removeで削除をペアで・・
グルーピングする粒度は作るアプリの種類や作り方によりけりですが、とりあえず、フォームごとにAsyncGroupを用意すれば十分だと・・例えば、こんな感じで。
デモアプリは現状ありませんが、とりあえず、作りかけで放置している、Tumblrビューワに組み込んだところ、えらい速くなったような気がぁ・・
組み込む前は1画像毎にダウンロード用のスレッドを生成してたので、そりゃものすごい回数のスレッドの生成・破棄が発生していた・・固定スレッドのプールといえど、ないよりはずっとまし![]()
まぁ、実際のアプリで使う場合は他の人が作った高機能なOmniThreadLibraryやAsyncCalls とか使った方がいいと思うが、いちいちクレジットの表記とかライセンス絡みがだるすぎ・・・
というか、Delphiが最低限こんな感じの仕組みなりを標準で用意してくれりゃ、前に作ったDelphi用のWindows Liveライブラリ に、非同期バージョンのメソッドILiveClient.EnumerateResourcesAsync(...): IAsyncOperation<IEnumerable<IResource>>とか追加してみたいんだが・・
ダウンロードはSkyDriveから
Delphi 2010以降が必要です。
Unicode Character Databaseを閲覧するためのUCDViewer 1.2.0をリリースしました。
主にUIを変更しました。まず、文字毎に新しいタブを開けるようになりました。
「Characters」タブで特定の文字をダブルクリックすると新しいタブが開きます。
また、これに合わせて各文字を比較するにはタブだと比較しずらいと思うので、MDIモードを追加しました。「View」メニューの「Tabbed UI」でタブモードとMDIモードを切り替えて下さい。
後は、最新のUnicode 6.1.0のUCDが読めなかったので修正など。バグフィックスです。
ダウンロードはSkyDriveから。
最後にいくつか補足しておきます。
まずは、ページングについて。Live SDKのREST APIではページングをサポートしてるのですが、公開したライブラリでもサポートしています。LiveClientオブジェクトのEnumerateXXX系のメソッドで、Pagingオブジェクト(IPagingインターフェース)を引数に取るメソッドがそうです。
PagingオブジェクトはTLive.CreatePagingメソッドで作成します。
ページングをする時、EnumerateXXX系で返されるIEnumerable<XXX>オブジェクトはIContinuableインターフェースを実装します。
IContinuableのPrevious、Nextプロパティで1つ前、1つ後のPagingオブジェクトが取得できるので、続きを取得するには、これらのPagingオブジェクトを使って取得できます。
例えば、10件ずつ、すべて取得するコードは次のようになります。
次は、IStreamについて。ファイルを作成したり、既存のファイルの中身を変更するためのメソッド、CreateFileやUpdateFileContent、また、ファイルの中身をダウンロードするためのGetFileContentメソッドは引数にIStreamを取ったり、返したりしますが、通常他のライブラリではTStreamを引数に取ったりするので、相互変換する必要があります。
TStreamからIStreamへの変換は、Classes.pasに定義されてる一度は目にしたことがあると思うTStreamAdapterを使えばいいのですが、その逆のIStreamからTStreamへの変換は、最初は逆はないのか??と思ってましたが、AxCtrls.pasにTOleStreamクラスが定義されてるので、これを使ったりしてください。
後は、プロキシとかのタイムアウトの設定なんですが、これは内部で使用しているWinInetのInternet(セッション?)ハンドルがLiveClientオブジェクトのInternetプロパティで取得できるので、後はWinInetの関数で設定してください。
今回は前回までのログイン・認証を踏まえて、実際にSkyDriveにアクセスしてみる。
読み込むには、wl.skydriveスコープ、読み書きするにはwl.skydrive_updateスコープが必要なので指定しておいて下さい。
SkyDrive上のコンテンツはロカールファイルシステムと同じようにファイルやフォルダから構成され公開したライブラリではIResourceインターフェースを実装したResourceオブジェクトで表されます。LiveClientオブジェクトのSkyDriveを読み書きするためのメソッドは基本的にこのResourceオブジェクトを返します(例外がありますが・・・)。IResourceインターフェースは次のようになります。
ResourceTypeプロパティはリソースの種類を表します。
の6種類あります(これらの定数はTResourceTypeクラスに定義)。まぁ、要するに、大別すると、fileとfolderに分けられ、fileであっても、画像ファイルならpicture、動画ファイルならvideoとなります。albumはfolderの特殊なケースです。fileかfolderかを判定するヘルパメソッドも容易してあります。
また、fileなら、IResourceインターフェースを継承したIFileResourceインターフェース、folderならIFolderResourceインターフェースも実装しているので、fileやfolderに特有の情報も取得できます。
同様に、IPhotoFileResourceやIVideoFileResourceとかもあるので、詳しくは、ソースを見てください。
各プロパティの意味はLive SDK REST API のFileやFolderオブジェクトなどの項を参照してください(ほとんどそのまま落とし込んでいるので・・)。
ここまでが基本で、実際に読み書きしてます。
例えば、ルートフォルダの中身を列挙する場合、まず、ルートフォルダの情報をLiveClientオブジェクトのGetRootFolderメソッドで取得します。Resourceオブジェクトで返されます。返されたResourceオブジェクトのIDを使い、LiveClientオブジェクトのEnumerateResourcesメソッドで列挙します。
IEnumerable<IResource>が返されるのでfor-inループで列挙して下さい。コードは例えば次のようになります。
これらと再帰を組み合わせれば、すべての中身を列挙できます。具体的にはLiveDemoアプリを参照。
また、フォルダを作成するには、親フォルダのIDや名前を指定して、CreateFolderメソッド、フォルダの名前などの変更やフォルダを削除するには、UpdateResource,DeleteReourceメソッドをターゲットのリソースのIDを指定して呼んで下さい。
同様に、ファイルを作成するには、CreateFileメソッドでアップロードするファイルの中身をIStreamで指定して呼んで下さい。
既に存在するファイルの中身を変更するには、UpdateFileContentメソッドを使います。この2つのメソッドだけIResourceを返しません![]()
。ファイルのダウンロードはGetFileContentメソッドを使います。ファイル名の変更や削除はフォルダの場合と同じUpdateReource,DeleteResourceメソッドを使います(ちなみに、現状、REST APIを使ってアップロードできるファイルの種類はテキストファイル、Officeドキュメント、画像や動画の一部などにサーバーによってえらい制限されてますので、まじで使えん・・・![]()
)
他にもMoveResourceメソッドでファイル・フォルダなどの既存のリソースを移動できますが、CopyFileでは名前が示す通り、ファイルしかコピーできません。
具体的な使い方はLiveDemoアプリ見てくださいということで、ここでは手抜きします。
LiveDemoアプリの画面はこんな感じです(最初に、ClientIDなどを求められるので各自用意)。
右クリックでコンテキストメニューがでます。そこから例えば、「プロパティ」を選択すると、
また、ツリー上でドラッグ&ドロップよる、リソースの移動ができます。
まぁ、あくまでデモなのですべてではないですが、ファイルのアップロードやフォルダの作成など主要な操作はとりあえずカバーしてます。
というか、ほぼ似た作りのDropboxライブラリそして、放置されているPiyotterで使われてるTwitterライブラリもあるんだが、気が向いたら公開します(公開する場合、Demoアプリ作ったり、多少手直しが必要なので・・・だるい
)。
以前 にDelphi用のヘッダを公開した同期フレームワークMicrosoft Sync Frameworkを試すために、最初はDropboxと組み合わせて色々遊んでいたが、Dropbox APIの挙動で一部不満
な部分があったので急遽、SkyDriveに変更したのだが、せっかくライブラリっぽく仕上げたので、公開してみる。
と、いっても最近のDelphiはクロスプラットフォーム開発環境にまっしぐらであるが、作ったライブラリは内部でWinInetを使って環境依存しまくりなので
。また、.NETはAsyncResult、TaskだのJavaはFutureだの非同期操作がまっさかりであるが、非同期操作はサポートしてないので(いい加減にしろよ、ウンコDelphi)・・・。特にSkyDriveのようにファイルのアップロード・ダウンロードなど巨大なデータをやり取りする場合、非同期操作のキャンセルができんとちょっと・・
まぁ、スレッドセーフには作ってあるので、自前でスレッド作って、同時にアクセスするのは問題ないんだが、肝心のキャンセルはできません
。
ということで、ダウンロードは
から。
使用する場合は、LiveDemoフォルダ以外のユニットを全部プロジェクトに追加して下さい。
開発環境はDelphi2010です。XE,XE2ならまだしも2009は無理かも。ライブラリの動作環境は、Internet Explorer7以降が必要です。内部でのUrlの操作にIUriというものを使っているので。LiveDemoフォルダにLiveDemoアプリがありますが、こちらはVista以降が必要です。
事前準備として、Liveの基本的な知識はLive SDKのREST APIのドキュメント やWindows Live Developer Center で学んでおいてください。Windows Liveのアカウントももちろん必要です(開発用のアカウントを作成する事をお勧めします)。後、アプリを自分で登録して、クライアントIDを発行してもらう必要がありますので。Registering Your Application with Windows Live を参考に登録してください。
まずは、主なユニットから
Live.pasでLiveに関する基本的なインターフェースなどを定義しています。Live.Protocol.Request.pasとLive.Protocol.Response.pasで、LiveのREST APIの低レベルなラッパーを実装して、これらを使いLive.Client.pasで、LiveClientオブジェクトを実装しています。
通常は使用するユニットのuses節にLive.pasとLive.Client.pasの2つだけを追加すればいいはずです。この2つの中身を見れば分かると思いますが、すべてinterfaceベースですので、オブジェクトの破棄とか気にする必要はありません。
ということで簡単な使い方。
まず、TLive.CreateClientContextメソッドでクライアントアプリに関する情報を表すClientContextオブジェクト(IClientContextインターフェース)を作成します。
AClientID,AClientSecrentには発行されたクライアントIDとクライアントシークレットを指定します。クライアントシークレットはアプリ登録時に発行されますが、モバイルアプリとしてマークすればここでは、指定しなくてもよさそうです。RedirectUriにはアプリ登録時に指定したUriを指定します。登録時に指定していない場合というかデスクトップアプリの場合は指定しなくてよさげなのですが、その時はデスクトップアプリ用の特殊なリダイレクトUriであるhttps://oauth.live.com/desktopを指定して下さい。必要であれば、ALocaleにはTLocaleクラスで定義されてるロケールを。
次に、これを元にTLiveClientFactory.CreateClientメソッドでLiveClientオブジェクト(ILiveClientインターフェース)を作成します。
ここで、OAuth 2.0 を使って、ユーザーからユーザーの保護されたリソースにアクセスするための許可を取る必要があります。Windows LiveではOAuth 2.0で定義されたImplict grant、Authorization code grantと独自のgrantの計3つ?サポートされていますが、LiveClientオブジェクトはAuthorization code grantだけをサポートします。
まず、LiveClientオブジェクトのCreateAuthorizationUriメソッドでユーザーから認可を得るためのUriを求めます。
AScopesでスコープを指定します。OAuth 2.0ではスコープというもので、アクセスできる情報のレベルを制御できるようになっています。Windows Liveの場合、定義されているスコープの種類と意味はLive SDKのドキュメントのScopes and permissionsを参考に。
また、型がIEnumerable<string>になっていますが、これはヘルパメソッドを用意してあるので、自前で実装する必要はありません。
通常は、LiveDemoアプリのようにTWebBrowserコントロールをホストしたりして自動化するのですが、ここでは、手動でやりますので、求めたUriを自分でブラウザを起動して、開きます。
Windows Liveにログインしていなければ、まず、ログインページが表示されます。
ログインすると、アプリがユーザーの保護されたリソースにアクセスしてよいかを許可・拒否するページに移動します。
表示される中身は先ほど指定したスコープやロケールで変わります。上の画面ではロケールにTLocale.Japaneseを指定したので日本語で表示されています。ここでは、「はい」を選択して許可します。すると、先ほど指定したRedirectUriにブラウザがリダイレクトします。
RedirectUriにAuthorization codeが含まれてるので、それを抜き出し、LiveClientオブジェクトのLoginメソッドに渡してログインします。
成功すると認証に関する情報を表すAuthContextオブジェクト(IAuthContextインターフェース)が返されますが、ほっといていいでしょう(LiveClientオブジェクトのAuthContextプロパティにセットされるので)。以上をまとめると次のよう感じになります。
これで、やっと、SkyDriveにアクセスできるようになるのですが、長くなったので、次回に続く・・ということなんですが、OAuth 2.0に関してちょっと補足しておきます。
OAuth 2.0ではユーザーからの許可を取ると、クライアントアプリにAccess Token(AuthContextオブジェクトのAccessTokenプロパティ)というものが発行され、以後、クライアントアプリはこのAcesss Tokenを使ってユーザーの保護されたリソースにアクセスするのですが、発行されてからある期間(AuthContextオブジェクトのExpiresInプロパティ)すぎると失効したりします。
Windows Liveの場合、現状、3600秒つまり1時間でAccess Tokenが失効するっぽいので、失効したら再取得する必要があります。新しいAccess Tokenを取得するために上記のフローを繰り返すのではあんまりなので、OAuth 2.0にはRefresh Token(AuthContextオブジェクトのRefreshTokenプロパティ)という仕組みが用意され、これを使い簡単に新しいAccess Tokenを取得できます。Windows Liveの場合、要求するスコープにwl.offline_accessを含めるとRefresh TokenがAccess Tokenと共に発行されるようですので、必要ならばこのスコープを最初に指定しておいて下さい。
LiveClientオブジェクトでRefresh Tokenを使って、Access Tokenを再取得する場合は、LiveClientオブジェクトのRefreshAuthContextメソッドを呼んでください。
Refresh Tokenを使って、Access Tokenを取得するとき、以前に指定したスコープと同じかそれより狭い範囲のスコープの新しいAccess Tokenを取得できますが、通常はANewScopesパラメータにnilを指定すれば、以前と同じスコープのAcesss Tokenになるので、nilでいいでしょう。ちなみに、Windows Liveの場合、このRefresh Tokenの有効期間は1年くらいってどっかみたような・・
まぁ、自前でやるのはだるいので、LiveClientオブジェクトにはAutoLoginプロパティがあります。このプロパティがTrueの時、Refresh Tokenが発行されていて、現在のAccess Tokenが失効していそうなら(ファジーです)、自動で、再取得します。デフォルトはTrueです。PCの日付をある程度合わせておいてね
。
UCDViewer 1.1.0をリリースしました。
UCDViewerって何?って人はここを。
まぁ、既にほぼ完成?してて、追加する機能も見当たらないのですが、PropertyAliases.txtとPropertyValueAliases.txtを使った表示に対応しました。
UCDのXMLファイルには各プロパティのabbreviated name?が記述されてて、それをそのまま表示しているのですが、long names?を使った表示に対応しました。
上記のファイルをunicode.orgからダウンロードして、UCDのXMLファイルと同じディレクトリに置いて下さい。XMLファイルの読み込み時に上記ファイルがあれば、自動的に読み込まれます。なければ、今まで通りの表示になります。
例えば、
が
になります。
ダウンロードはSkyDriveから。
Microsoft Sync FrameworkのSDKだけをDelphiに移植しました。
バージョンは2.1です。SyncFramework SDK自体のダウンロードはMicorosoftのサイトから。
ファイル構成がCのそれと微妙に違いますが。
です。移植ずっこけてるかもしれんませんが
。使用は各自の責任で
。
最近のコメント