2014年8月31日 (日)

DelphiでDropbox

Dropboxプロがパワーアップして、1TBで月額1200円になったので、HDDの肥やしとなっていた眠っていたコードをひっぱりだし、手直しと簡単なデモアプリを作ったのでDelphi用のDropboxライブラリを公開してみる。

DropboxライブラリをパクってWindows Liveライブラリを作ったので、動作環境はWindows Liveライブラリ と全く同じで、開発環境は2010で、Dropboxライブラリの動作環境はIE7以降が必要、デモアプリはVista以降と全く同じです・・

まずは、ダウンロードは

から。

主なユニットの構成も同じで、

  • Dropbox.pas
  • Dropbox.Client.pas
  • Dropbox.Protocol.Request.pas
  • Dropbox.Protocol.Response.pas

で、インターフェースベースで、スレッドセーフに作ってあります。Dropbox社によって標準でiOS, Java, Ruby, Pythonなどいつくかの言語・プラットフォーム用の開発キットが用意されているのですが、メソッド名とか開発キットをだいたいパクっているので、詳細はそのドキュメントHTTPのAPIのドキュメント を参考に・・・・dashdash

もちろん、Dropboxのアカウントが必要です。また、アプリを自分で登録 してApp KeyとApp Secretを発行してもらってください。SkyDriveと違い、Dropboxはアプリを登録すると、申請しない限り、Appフォルダの下にアプリ名のフォルダが作られ、そのフォルダにしかアクセスできないので、あえて、開発用アカウントを用意しなくても安全だと思います。

DropboxもSkyDriveと同じOAuth2を使っているので、認証方法はDelphiでWindows Live(SkyDrive) を参考に・・

具体的な使い方はデモアプリを見てください・・

Dropboxdemo1

Dropboxdemo2

フォルダの作成からファイルのアップロード、ダウンロード、移動、名前の変更、そして、特定のリビジョンの復元まで一通りできるようになってるので・・

1点だけ言うと、Dropboxではファイルのアップロードを分割してチャンクごとに行えるようになってるのですが、
ChunkedUploadFileFirstでアップロードするバイト配列を渡し、チャンクアップロードを開始します。UploadedChunkオブジェクトが返るので、そのUploadIDを使い残りのチャンクをChunkedUploadFileNextでアップロードします。すべてのチャンクをアップロードしたらCommitChunkedUploadFileでコミットします。こんな感じで使います。

というか、もうこのオンラインストレージネタはおなかいっぱいで飽きたdash

次になんか作るときは、いい加減、WinInet卒業して、クロスプラットフォームなHTTPライブラリに移行しようと思う。

2014年8月30日 (土)

DelphiでWindows Live(SkyDrive)・・・(5)

やる事がなくなったので、再びWindows Live。今回で5回目ですが、まず過去の記事のリンクを・・・

今回はファイルコンテンツやピクチャの条件ダウンロードと部分ダウンロード、要はHTTPの条件リクエストと部分ダウンロードに対応してみました(一部メソッドを変更しましたdashdash)。

AccessConditionオブジェクトでアクセス条件を指定します。

これは、HTTPのIf-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Sinceヘッダーそのまんまです。ここ からパクリましたdashdash。部分ダウンロードを行うには、RangesSpecifierオブジェクトで範囲を指定します。

HTTPでは現状バイトレンジしか定義されていませんが、そのバイトレンジを表すRangesSpecifierオブジェクトを作成するヘルパを用意してます。

一応、RFC2616のHTTPの仕様通り、2通りの指定方法を用意しましたが、現状、後者の末尾のバイト数(SuffixLength)を指定する方法はサーバーの方で対応してないようなので(HTTPの仕様通り、エラーではなく無視されて全体が返される)、前者の開始位置(FirstByte)と終了位置(LastByte)を指定する方を使って下さい。また、HTTPの仕様では複数のバイトレンジを一度に指定できるようになってますが、その場合は、自分でIRangesSpecifierインターフェースを実装して下さい(というか、サーバーが対応してるかは知りませんdashdash)。条件ダウンロード、部分ダウンロードのために戻り値のLiveResponsePropertiesオブジェクトで一部のHTTPヘッダにアクセスできるようにしてあります。

具体的に使うとこんな感じです。

 

後は、上記のGetUserPictureやGetFileContentメソッドはプッシュ型のメソッドですが、データを自分で引っ張るプル型のメソッドも追加しておきました。

LiveDownloaderオブジェクトは次のようになってます。

LiveDownloaderオブジェクトのGetStreamメソッドで得られるストリームを使って読み込みます。プル型なので、途中でキャンセルしたければ、そのまま破棄して下さい。

以上です。

HTTPの条件リクエストや部分ダウンロードの勉強のために実装してみたけど、色々おもろかったbearing

というか、完全に独りよがりライブラリ・・・

ダウンロードはいつも通りOneDriveから。

2014年3月20日 (木)

Delphiでスレッド

久しぶりにDelphi絡みのネタ

今回は2ち○んねるでスレッドの事が話題になってたので・・・サクっと記事を書いてみた。
Delphiでスレッドを扱う場合の基本はTThreadクラスを使う方法であるが、以前にTThreadクラスをすっとばして、

というネタを書いたが、今回は直接TThreadクラスを扱う場合についてちょっと書いてみるdashdash。といってもここで自分が言いたいことは前と同じで、要はGC(ガベージコレクタ)のない環境では、基本は通常の共有リソースへの排他制御などの他にスレッドの終了待ち合わせをしないとアクセスバイオレーションが発生するシナリオが多々あるのでどうにかしようということで・・・。

最近はDelphiでスレッドを扱う場合は、Delphiで非同期プログラミング で作った内部でスレッドプールを使う自前のライブラリを使ってたが、それ以前は、もちろんTThreadクラスを直接使ってこんな感じで使ってましたsweat01sweat01

まず、TThreadクラスを継承し、グループ化したTGroupedThreadクラスを用意して、別スレッドで実行する処理はこのTGroupedThreadクラスから派生させます。

で、後は、スレッドのグループを表すTThreadGroupクラスを用意します。スレッドの終了待ち合わせは個々のスレッドごとではなく、グループごとに行います。Shutdownメソッドでシャットダウンすると以降、新しいスレッドをグループに追加できなくなります。WaitForTerminationメソッドでグループ内のすべてのスレッドが終了する(厳密にはTGroupedThreadクラスのすべてインスタンスのデストラクタが呼ばれる)まで待ち合わせます。シャットダウンして、ペンディングのすべてのスレッドが終了したら、終了状態(IsTeminated = True)になる。

実際の実装は例えばこんな感じ。

こうしておけば、後は、実践的なアプリでは任意個のスレッドを任意のタイミングで起動したりするケースが多いと思いますが、それらの終了待ち合わせを簡単に扱えます。GUIをもつマルチスレッドアプリを色々作ってきたけど、とりあえず、もっと個別に色々制御したい場合もあると思うがこれで十分だったdash

ちなみに、2ち○んねるでFreeOnTerminate = Trueは使えないと言われてましたが、そんな事はない?と思います(なんか、設計・実装根本的にダメだったらどうしようdash)。

というより、このマルチスレッド・非同期での終了待ち合わせがうざいsweat01ので、マルチスレッドアプリ開発の観点から自分はさっさとデスクトップOS向けにもガベージコレクタを導入しろとずっと言い続けてます(ということで、スレッド三昧の自分は参照カウントされるDelphiのインターフェースばっか使ってるのである・・・)

2013年12月 1日 (日)

DelphiでObjective-Cのブロック

まず、最初に正規表現パックの方をちょっと更新しておきましたdashdash


更新内容はDelphiで正規表現(NSRegularExpression)で書きましたが、Delphiの文字列からNSStringオブジェクトを作成する時に、NSStringクラスのstringWithCharacters:length:クラスメソッドなどで作成すると、Delphiの文字列がコピーされる事になると思うのですが、文字列が巨大になるとオーバーヘッドになると思うので、コピーが発生しないinitWithCharactersNoCopy:length:freeWhenDone:インスタンスメソッドを使ってDelphi側とメモリを共有するようにしました(まぁ、これを気にするのは、テキストエディタとか作る時ぐらいだろうし、どうでもいいな・・・)。

ということで、タイトルのObjective-Cのブロック についてです。ブロックというのは、他の言語ではクロージャーとか呼ばれたりするものらしいですが、詳しくは、各自調べて下さいdashdash。ここでは、このブロックを引数に取るObjective-CのメソッドをDelphi側からどう呼ぶのか調べました。

例として、NSRegularExpressionクラスのenumerateMatchesInString:options:range:usingBlock:インスタンスメソッドを見てみます。

最初見た時、面食らいましたが、第4引数がブロックです。で、Delphi-ObjectiveCブリッジのソースコード(Macapi.ObjectiveC.pas)を読むかぎり、Objective-Cのブロックは、Delphi側ではメソッドポインタ型として宣言すればいいようです。ということで、上記の例をDelphi側にインポートすると、


こんな感じでしょうか。簡単ですねup。ただし、更にソースを追っていくと、Macapi.OCBlocks.pasにGetObjCBlockという関数があるのですが、この実装を見ると、

OSXプラットフォームでは空になります。ということで、Delphi XE4では、iOSプラットフォームでは呼べるようですが、OSXプラットフォームではブロックを引数に取るメソッドを簡単に呼べません。ぬぅぅぅぅ weep

注意:ソースコードをちょっと読んで勝手に判断したので、間違ってるかもしれませんsweat02

というか、自分で思ってたが、中身薄いブログばっか書いてるな・・・

2013年11月23日 (土)

Delphiで正規表現(Onigumo)

パクりシリーズの第3弾です。鬼雲版です。鬼雲 (Onigumo)というのは鬼車 (Oniguruma)を改良したエンジン(Oniguruma-mod)とのことで、Ruby 2.0の正規表現エンジンとして使われているそうです。まぁ、自分としてはどちらでも良かったので、最初は鬼車を使う前提でしたが、64bit環境でビルドすると、64ビットから32ビットへの縮小変換の警告??が大量に出るので、鬼雲を使うことにしました。こちらの方はヘッダファイルを見ると、64bit環境も考慮してるようです。

ということで、RegExPackを更新しておきました。

まず、プラットフォームはWin32/Win64に対応してます。現状は動的リンクのみですが、いずれ静的リンクに可能なら対応しようと思ってます。動的リンクする場合は、各プラットフォーム毎のフォルダ(Win32、Win64)内の、onig.dllを実行時にこれらが見つけられるように、パスを通すか適切な位置に配置して下さい。自分で鬼雲をソースからビルドする場合。詳しいビルド方法は鬼雲のドキュメントを参照ということにしますが、エクスポートされた変数をDelphiから参照できるようにするために、一部ソースを修正する必要があります。詳しくはreadme.txtを・・

鬼雲というより元の鬼車が正規表現の文法を切り替えられるようになってるのですが、これにも対応しておきました。

文法を引数に取るコンスタクタを追加しておきました。指定しない場合は、デフォルトのrsPerlです。

ちなみに、当初はOSXにも対応しようとしてて、libonig.dylibをビルドしたのですが、これをダンプして中身を見ると、libonig.dylibが依存してる他のライブラリの相対パスなどではなく自分の環境依存の絶対パスが含まれていて、ちょっと、OSXの事がまだよくわかってなく、これを配布しても他の環境で動くのか不明なので、現状は自分でビルドして・・dashdash

 

2013年11月 9日 (土)

Delphiで正規表現(NSRegularExpression)

前回のDelphiで正規表現(VBScript) で書いたように、CocoaのNSRegularExpressionを使ってラップしたSystem.RegularExpressions.NSRegularExpressesion.pasを完成させましたdashdashdash

ということで、RegExPackの方を更新しておきました。詳しくはreadme.txtを。


Delphi XE4以降が必要です。

ところで、Cocoa APIを使って初めて本格的にプログラミングしたのですが、Cocoaで文字列を扱う場合は、NSStringクラスを使うことは知っていて、NSRegularExpressionクラスでもパターン、検索対象の文字列をNSStringクラスとしてやりとりするのですが、最後まで壮大な勘違いをしてましたdashdash

NSStringクラスというはUTF-16が基本sign02なんですね・・・・ずっと、UTF-8だと思ってましたsweat02sweat02。だから、NSStringのlengthプロパティはバイト(UTF-8のコードユニット)数ではなく、ワード(UTF-16のコードユニット)数を返す・・dashdashdash。ということで、Delphiのstring型と相性いいじゃんupupSystem.RegularExpressions.NSRegularExpressesion.pasの実装で、気付くまで、UTF16->UTF8へ変換したり、インデックスを調整するコードをごりごり書いていた・・・・

ということで、Delphi標準のMacapi.Foundation.pasに、Delphiのstring型をNSStringに変換するNSSTR関数がありますが、

思いっきりUTF-16<->UTF-8の変換のオーバーヘッドが発生してるようーーなsweat02sweat02。NSTR関数は使わないで、やるなら、NSStringクラスのstringWithCharacters:length:クラスメソッドと、getCharacters:range:インスタンスメソッドを使って、

ちなみに、stringWithCharacters:length:だと、指定した文字列がコピーされてメモリ上に重複してに持つことになるので、initWithCharactersNoCopy:length:freeWhenDone:インスタンスメソッドを使えば、Delphi側とメモリを共有できるっぽいup

このまま、調子に乗って、モチベーションが続く限り、次はUTF-16のPCRE版か、Oniguruma(Onigumo)版かな????

元々は、RFC 6265のクッキー仕様を実装しようとしてて、そこで正規表現使おうかなと思って、今は、クッキー仕様の実装ほったらかしで脱線しすぎ・・

2013年11月 4日 (月)

Delphiで正規表現(VBScript)

Delphiで正規表現とか今さら感がありますが・・

Delphiで正規表現というと、新しいものではDelphi XEでPCREを使った正規表現が使えるようになり、また、日本人でネットでDelphiのライブラリ等を公開してる人はほぼ全滅してて、その中でも公開している希望の星のSkRegEx があります(後、もちろん鬼車も) が、今まで自分は正規表現が必要な時は、だいたい、 ライセンスの問題に煩わされないVBScriptのRegExpオブジェクトを使ってきました。

で、今回、また、正規表現を使う必要があり、また、VBScriptのRegExpオブジェクトを使うつもりですが、せっかくなので、Delphi XEで導入された.NETライクな正規表現ライブラリ風に使えるように、脱線してRegularExpressions.pasをパクってみました。

System.RegularExpressions.VBScript.pasです。

ダウンロードはSkyDriveから。使用は各自の責任で・・



ちなみに、ラップしたVBScript.RegExpオブジェクトが貧弱なので、一部の機能はサポートしていません。詳しくは同梱のreadme.txtを・・また、ラップしたVBScript.RegExpはCOMオブジェクトなので、使用する前にCoInitialize(Ex)などで初期化してださい(メインスレッド以外で使う場合)。

Delphi XE以降が必要です。

後、更に脱線してOSX/iOS?向けにCocoaのNSRegularExpression をラップしたSystem.RegularExpressions.NSRegularExpressesion.pasも作成中なのですが、間に合わなかったので、次回・・・?dash

まぁ、特定のプラットフォーム向けのソフトウェアを作成する場合はこのように特定のプラットフォームに依存したエンジンを使ってもいいですが、クロスプラットフォーム向けのソフトウェアを作成する時に、プラットフォーム毎に正規表現エンジンを切り替えると、エンジン毎の正規表現の文法も違うことですし、おとなしく、エンジンがクロスプラットフォームなPCREを使っているDelphi標準のを使うか、Pure PascalなSkRegExを使った方が身のためですねdashdash

ここまでくると、次は、鬼車をラップして同じことをやりたくなってきたが、誰か代わりにつくってdashdash

ちなみに、Delphiの正規表現で使われてるPCREですが、去年のバージョン当たりから、UTF-16のエンコーディングに対応し始めて、これを使えば現状のUTF-8<->UTF-16の変換オーバーヘッドがなくなりますねup

2013年10月 6日 (日)

CryptoAPI and CNG for Delphi

ヘッダ移植シリーズdashdashの続きです。
前から気が向いた時にちょくちょく進めてたのがとりあえず公開してもいいかなと言うレベルに達したので、公開します。

WindowsのCryptoAPICNG(Cryptography API:Next Generation)です。 まぁ、いつも通りMPL/LGPLのデュアルライセンスなJEDI Windows API の方にもありますが・・・

CryptoAPIからは

  • Winapi.WinCrypt.pas(wincrypt.h)

CNGからは

  • Winapi.BCrypt.pas(bcrypt.h)
  • Winapi.NCrypt.pas(ncrypt.h)
  • Winapi.NCryptProtect.pas(ncryptprotect.h)
  • Winapi.SslProvider.pas(sslprovider.h)

で、あとはおまけでSSPI(Security Support Provider Interface) から

  • Winapi.Sspi.pas(sspi.h)
  • Winapi.Schannel.pas(minschannel.h,schannel.h)

いつも通りdelayedディレクティブを使っているのでDelphi 2010以降が必要です。JEDIのヘッダは更新止まってると思いますが、こっちはWindows 8 SDK相当です。移植ミスは勝手に修正して、使用は自己責任でdashdash

ダウンロードはSkyDriveから。

2013年5月27日 (月)

本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(クラスインポート編)

今回は

本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(クラスインポート編)

です。Windows APIを呼ぶ感覚であんま深く考えないでCocoa APIを呼べるのかなと思ってたのですが、最低限押えておかなきゃいけないことが、結構あるじゃねぇか・・・

ということで、まずは基本的なObjective-Cのクラスのインポートの仕方ですが、既存のインポートの定義を真似ればいいでしょう。例えば、NSString クラスのMacapi.Foundationユニットのインポートは次のようになっています。

見てわかるように、Objective-Cのクラスのクラスメソッド、インスタンスメソッドを定義するDelphiのインターフェース型をそれぞれ用意して、それらを型パラメータとして、TOCGenericImportジェネリッククラスを特化するだけです。

難しくないでしょう。

ちなみに、Objective-Cのクラスのインスタンスメソッドを定義するDelphiのインターフェース型の名前(上記の例ではNSString)で内部でObjecitve-Cのランタイムにクラス情報を問い合わせてるので、ここはインポートするObjective-Cのクラスの名前としっかり合わせておきましょう(もちろん、Case-Sensitiveです)。クラスメソッドを定義するDelphiのインターフェース型の名前(上記の例ではNSStringClass)とTOCGenericImportジェネリッククラスの特化した型の名前(上記の例ではTNSString)は適当でもかまいませんが、ひねくれずに、同じように命名しておけばいいと思います。

また、Delphiのインターフェース型というのは元々、WindowsのCOM(Component Object Model)のインターフェースを扱うために導入されたとの事で、COMのインターフェースを定義する時は、基本的にメソッドの名前よりメソッドを宣言する順番が意味を持ちますが、Delphi-ObjectiveCブリッジでの実装(というより、Objective-Cランタイムでは文字列をベースにしたセレクタというものでメソッドを識別して呼び出す)では、メソッドを宣言する順番は意味を持ちませんが、逆にメソッドの名前(とパラメータの名前)が意味を持ちますので、自分でインポートする時、使用しないメソッドを定義しなくても問題はありません(更に言うと、継承元のインターフェースも別にNSObjectやNSObjectClassである必要もなし・・嘘でした、TOCGenericImportジェネリッククラスの型パラメータにインターフェース型の制約がかかってますね)。

ここまでを踏まえて、次はObjective-Cのオブジェクトのやり取りの仕方を見ていきます。

まずはObjective-Cのオブジェクトへのポインタを入力パラメータとして渡すパターンを見てみます。例えば、NSURL クラスのURLWithStringクラスメソッドを見てみます。

これのMacapi.FoundationユニットのDelphiのインポートの定義を見てます。

入力パラメータの型が対応するDelphiのインターフェース型になってますが、この場合、Delphi-ObjectiveCブリッジの方で自動でDelphiのオブジェクトによってラップされた内部のObjective-Cのオブジェクトのポインタを取り出してくれます。また、次のようにポインタ型で宣言する場合は手動で取り出したりして渡しますが、あえて、こんな面倒な事を誰もしたくないでしょう。

次はObjective-Cのオブジェクトへのポインタを戻り値として返すパターンを見てます。例えば、NSURLクラスのhostインスタンスメソッドを見てます。

これのMacapi.FoundationユニットのDelphiのインポートの定義を見ると、

戻り値の型が対応するDelphiのインターフェース型になってますが、この場合、Delphi-ObjectiveCブリッジの方で自動でDelphiのオブジェクトでラップしてくれますdashdash。ポインタ型で宣言する場合は手動でラップします。

ちなみに、Macapi.Foundationユニットを眺めるとObjective-Cのオブジェクトを戻り値として返すメソッドでも戻り値の型をポイ ンタ型で定義してたり、Delphiのインターフェース型で定義してたりと一貫性がすごく見られないんですけどどういうことなんでしょうかsign02sign02誰か知ってる人がいたら教えて下さいsweat02

で、これで事足りればいいのですが、Cocoa APIを眺めるともう1つパターンがあります。Objective-Cのオブジェクトのポインタを出力パラメータとして返すメソッドです。例えば、NSURLConnection クラスのsendSynchronousRequestインスタンスメソッドを見てみます。

これのMacapi.FoundationユニットのDelphiのインポートの定義を見てます。

とりあえず、第2引数の定義がずっこけてますよねsign02ということで、第3引数と同じように再定義しておきます。

Objective-Cのオブジェクトをポインタとして受け取るように定義したので、いつも通り自分でラップする必要があります。

でも、これだと、いちいちラップするのが面倒なので、戻り値と同じように自動でラップしてもらおうと、例えば、outパラメータを使って、

とDelphiのインターフェース型として定義しても駄目なんですね。現状、戻り値しか自動でラップしてくれません。

というように、中途半端感が拭えなく、本当はあまり何もしてくれないんですdash

うぬぬぬ・・・

ちなみに、これは、エンバカデロによる正式な仕様でもありません。ドキュメントが全くない?ので、Delphi-ObjectiveCブリッジの現状の実装から勝手に読み取ったものですので・・・dashdash

2013年5月23日 (木)

本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)・・・おまけ

前回 のおまけです。前回の最後に書いたように、、Objective-Cのオブジェクトを保持しておこうとしたら、一々、retainだのreleaseだの面倒くさいです。

というか、そもそも、せっかくObjective-CのオブジェクトをDelphiのオブジェクトでラップして自動参照カウントが働くDelphiのインターフェース型を経由して操作させるのなら、ラップする段階でDelphiのオブジェクトに所有権を必ず移して管理させれば楽なような気がするが・・??

現状の実装として、Macapi.ObjectiveCユニットで定義されてるTOCImportクラスがObjective-Cのオブジェクトのポインタをラップしていているのですが、

ポインタ型のIDがObjective-Cのオブジェクトのポインタと思って問題ないのですが、本当に何もしてないんです。

これをラップする段階で所有権をDelphiのオブジェクトに持たせてあげれば、いちいち、Delphiから操作する時にretain/releaseをする必要なくなるんじゃないかなぁーーと。

イメージとしてはこんな感じに。

で、Wrapメソッドもパラメータをとりあえず追加して同じように

まぁ、こんな事すると他の問題があるのかもしれませんが。


«本当はあまり何もしてくれないDelphi-ObjectiveCブリッジ(メモリ管理編)

自作ソフトウェア

無料ブログはココログ

メモ