Cut、Copy、Paste 内容が同期されたRichTextBox(その7)

ドラッグ&ドロップの処理 内容が同期されたRichTextBox(その8)

ファイルを挿入するプログラムを作成しましたが、OLEオブジェクトを編集した場合、データの同期ができません。そこで今回はこれを可能にする方法を考えます。アドバイザリーシンクを使えばできそうです。

まずOLEオブジェクトが開かれるときにIOleObject.Adviseを実行すれば、編集が保存されたり終了したときに通知を受け取ることができます。そのときにデータを同期させる処理を実行すればいいわけです。

さっそくやってみましょう。

OLEオブジェクトをダブルクリックすると編集可能になりますが、このときにIOleObject.Adviseを実行する必要があります。

MouseDoubleClickイベントを使えばできそうなのですが、OLEオブジェクトをダブルクリックしても、このイベントは起きません。そこでWndProcをオーバーライドする必要があります。

RichTextBoxEx1_OleDoubleClickをどう書くか?

まずオブジェクトを開くためにはIOleObjectを取得する必要があります。その処理をしているのが以下のプログラムです。

そのまえに以前のプラグラムをちょっと修正。

RichTextBoxにBitmapを挿入する方法

これでIOleObjectやIOleClientSiteが取得できるようになります。

IOleObject oleObject = RichTextBoxOle.RichTextBoxOle.GetOleObject(richTextBoxEx1, richTextBoxEx1.SelectionStart);

データを同期させたいので開くOLEオブジェクトは別のRichTextBoxにコピーしてそこで開くことにします。RichTextBoxはXAdviseSink内におき、XAdviseSinkオブジェクトはRichData内におきます。

IOleObjectが取得できたらIOleObject.DoVerbを実行します。

int ret = oleObject.DoVerb(0, null, null, 0, IntPtr.Zero, null);

戻り値が0ならOLEオブジェクトを開くことに成功しています。それ以外の値なら失敗です。

OLEオブジェクトを開くことが成功しているのであればIAdviseSinkをセットします。

IAdviseSinkを継承したクラス、XAdviseSinkを作成します。そのためには Microsoft.VisualStudio.OLE.Interopを追加する必要があります。

これでOleオブジェクトの編集が終了すると、IAdviseSink.OnClose()が呼ばれます。

IAdviseSink.OnClose()が呼ばれたときに対応するOleオブジェクトを変更すればいいのですが、そのためには対応しているOleオブジェクトがわかるようにしておく必要があります。さて、どうすればいいのでしょうか?

Oleオブジェクトの位置を記憶しておき、文書が変更されたときは適切に調整する
Oleオブジェクトが編集されているときは文書の編集を禁止する

後者が簡単でよいと思います。

文書の編集を禁止するにはKeyDownイベントを無視、ドラッグドロップイベントを無視することで実現できます。

このプログラムにはちょっと困った部分があります。それはフォルダのショートカットを貼り付けて開いた場合、IAdviseSink.OnCloseが実行されないらしく、編集することができなくなる点です。さて困った・・・。

そこで思いついた方法が、oleObject.DoVerbを実行する前と後では、プロセスの数が増えているはずだ、そこで

int i = 0;
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.ProcessName != “explorer”)
i++;
}
return i;

として、explorer以外のプロセスの数を数えてみることにしました。OleOLEオブジェクトがフォルダのショートカットの場合、ダブルクリックしてもexplorer以外のプロセス数は増えないはずなので、これで場合分けできないかと・・・。

なんかインチキくさい方法だな。もっといい方法があるはずなのだが・・・。