VBAで System.Windows.Clipboardを使いたい

タグの編集
投稿者 ひでと  (社会人) 投稿日時 2018/9/5 09:22:56
お世話になります。
VBで入力したクリップボードの内容を、Wordから使用したいと思います。
検索するとオフィスクリップボード?のことが出てきますが、表題に関する資料が見つけられません。
WordのVBAの使用方法になるのかと思いますが、アドバイスを御願いします。
Wordの文章の中でクリップボードの内容を加工処理して、数式(OMath)として貼り付けしたいのです。
投稿者 (削除されました)  () 投稿日時 2018/9/5 13:23:05
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/9/5 13:31:04
Word VBA から行えるのは、コピー&ペースト程度に限られますので、
そうした内容の加工は Word VBA だけでは無理ですね。
標準機能から行えるのは、せいぜいテキストの加工ぐらいでしょう。

より高度なやりとりが必要なら、OpenClipboard を始めとする
Win32 API 群を呼び出すことになります。
http://www.geocities.co.jp/SiliconValley-Bay/9960/vb/api/clip.html

CreateObject で InternetExplorer.Application から呼び出せる
IE の .clipboardData オブジェクトも、扱えるのは text 形式と url 形式だけですし。
HTML5 の Clipboard API も、今回の要件を満たせなさそう。
http://officetanaka.net/excel/vba/tips/tips81.htm

それ以外だと、VB6 や .NET 等で、クリップボード操作用の ActiveX DLL 等を
作成して、それを Excel から CreateObject で利用することもできます。
VBA 以外の開発知識と開発環境が必要になりますけれど。


> 検索するとオフィスクリップボード?のことが出てきますが
それは、Word.Application の ShowClipboad メソッドでトグル表示できる
[Office クリップボード] 作業ウィンドウのことではないでしょうか。


> 表題に関する資料が見つけられません。
> VBAで System.Windows.Clipboardを使いたい
System.Windows.Clipboard クラス(System.Windows.Clipboard.dll)や
System.Windows.Forms.Clipboard クラス(System.Windows.Forms.dll)は
.NET から利用するためのものなので、Word からは直接呼び出せません。
投稿者 ひでと  (社会人) 投稿日時 2018/9/5 16:32:30
>魔界の仮面弁士様、ありがとうございます。
Wordの貼り付けで、クリップボードに保存していたテキストデータが貼り付けできていたので、簡単に出来るかな?と思っていました。
APIまでとなるとハードルが高いです。

そこで、WordのClipboadにVBからデータを送ることも考えるのですが、
この場合、WordのClipboadが開いているBookに固有のものですと、処理が難しくなりそうです。

疑問に思うのは、Wordで貼り付けをするとSystem.Windows.Clipboardの内容が貼り付けできることです。
そうすると、WordのClipboadにデータが転送されているということだと思うのですが、これを利用することは出来ないのでしょうか。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/9/5 17:00:12
まずは、今回扱う予定のクリップボードフォーマットを教えてください。
CF_TEXT では無いのですよね?
https://docs.microsoft.com/en-us/windows/desktop/dataxchg/standard-clipboard-formats

形式が不明な場合は、ツール等で調べておいてください。
再加工が目的なら、フォーマットがわからないと先に進めませんので。
https://www.officedaytime.com/clipmm/
http://www.freeclipboardviewer.com/


> そこで、WordのClipboadにVBからデータを送ることも考えるのですが、
クリップボードは全アプリケーションで共有されるものなので、
「WordのClipboad」という表現には違和感があります。😅
Word VBA には Clipboard という名のオブジェクトやメンバーも無いはずですし。

また、ここでいう「VB」とは、どのバージョンの Visual Basic のことでしょうか?
System.Windows.Clipboard というからには、.NET Framework 3.0 以降のはずなので、
とりあえず VB2005 以下ということは無さそうですが。


> WordのClipboadにデータが転送されているということだと思うのですが
どういう意味でしょうか?
(クリップボードの仕組みを、正しく理解されていないような気が…)
投稿者 ひでと  (社会人) 投稿日時 2018/9/6 16:24:51
ありがとうございます。

>まずは、今回扱う予定のクリップボードフォーマットを教えてください。
>CF_TEXT では無いのですよね?
>https://docs.microsoft.com/en-us/windows/desktop/dataxchg/standard-clipboard-formats

>形式が不明な場合は、ツール等で調べておいてください。
>再加工が目的なら、フォーマットがわからないと先に進めませんので。
>https://www.officedaytime.com/clipmm/
>http://www.freeclipboardviewer.com/
クリップボードの仕組みは良く理解しておりません。
クリップボードに代入しているのはVB2005でMy.Computer.Clipboard.SetText() を使用しています。


> そこで、WordのClipboadにVBからデータを送ることも考えるのですが、
>クリップボードは全アプリケーションで共有されるものなので、
>「WordのClipboad」という表現には違和感があります。😅
>Word VBA には Clipboard という名のオブジェクトやメンバーも無いはずですし。
WordにはClipboardが無いのですね。
WordのVBAでPasteメソッドで貼り付けできるので、なにかのデータ領域が有るのだと思い、VB2005から利用できないかと思ったわけです。

>また、ここでいう「VB」とは、どのバージョンの Visual Basic のことでしょうか?
>System.Windows.Clipboard というからには、.NET Framework 3.0 以降のはずなので、
>とりあえず VB2005 以下ということは無さそうですが。
VB2005を未だに使用しています。

> WordのClipboadにデータが転送されているということだと思うのですが
>どういう意味でしょうか?
>(クリップボードの仕組みを、正しく理解されていないような気が…)

マイクロソフトのオフィスに共通する、クリップボードに相当するデータ領域が有るのかと想像していました。 
>Range.Paste メソッド 
>クリップボードの内容を、指定範囲に挿入します。
と有り、「Office クリップボードを使用する」等の表現がネット上でありましたので。

その領域はVBから参照できるSystem.Windows.Clipboardの領域とは別領域で、
Wordで貼り付けをすると、System.Windows.ClipboardのデータをWordがオフィスのクリップボードにコピーし、さらにその内容をDocumentに貼り付けるという処理がWordで自動で行われるのかと想像していました。

現状、WordのVBA上で、RangeオブジェクトにPastし、RangeオブジェクトをDeleteする、その間にデータを取得してみようと以下のようにしてみました。
Function ClipbordText() As String
    Dim objRange As Range
    Set objRange = Selection.Range
    objRange.Paste
    ClipbordText = objRange.Text
    objRange.Delete
End Function
これで、VB2005のMy.Computer.Clipboard.SetText()で作成したClipboardの中のテキストが取得できました。今回の目的としては、このテキストを加工して、再びPastすることでしたので、一応目的は達成しているように思います。
ただ、クリップボードの内容(仕組み)については理解していないとのご指摘、ごもっともですので、勘違いを指摘いただければ助かります。



投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/9/6 18:32:17
そういうバージョン情報は、最初に書いておいていただけると助かります。

> クリップボードに代入しているのはVB2005でMy.Computer.Clipboard.SetText() を使用しています。

それは System.Windows.Clipboard クラスではなく
System.Windows.Forms.Clipboard クラスです。紛らわしいですよね…。
(といっても、両者に機能的な差異は無いのですが)

System.Windows.Clipboard という名のクラスも存在しているのですが、
それは、.NET Framework 3.0 以降で利用可能な
WPF (Windows Presentation Foundation) 用のクラスなのです。

VB2005 が対応しているのは .NET Framework 2.0 ですので、
そもそも WPF アプリケーションを作成できません。


また My.Computer の Clipboard プロパティが返すオブジェクトは、
Microsoft.VisualBasic.MyServices 名前空間の ClipboardProxy クラスです。

これの SetText メソッドでは、内部的に、
System.Windows.Forms.Clipboard クラスを呼び出しています。
System.Windows.Clipboard クラスでありません。


> クリップボードの仕組みは良く理解しておりません。

まずは前提知識の確認。
下記の 3 点についてはご存知でしょうか?


(1)クリップボードは、アプリケーションごとに所有されているものではなく、OS 全体で単一のリソースです。


(2)一回のコピー操作では、一つのデータを表すために、同時に複数の形式のデータが書き込まれます。
これについては、Excel シート上で右クリックしたときに「形式を選択して貼り付け」という選択肢を選んでみると分かりやすいでしょう。
(Excel で対応していない形式は表示されないですけど)


(3)貼り付け操作時には、そのアプリが対応している形式が記録されているかどうかを調べ、それを貼り付けます。
対応形式が複数あった場合、どの形式で貼り付けられるのかは、貼り付け先アプリケーションの実装次第です。



たとえば、クリップボード上に画像をコピーしておいても、
それをメモ帳に貼ることができないことはご存知ですよね。
その理由が上記 (3) というわけです。

メモ帳は画像形式は扱えないので、貼り付け操作が行われません。
ただし画像形式と同時に、テキストや Unicode テキスト形式が
保持されている場合は、そのテキストが貼られることになります。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/9/6 18:45:39
>> Word VBA には Clipboard という名のオブジェクトやメンバーも無いはずですし。
> WordにはClipboardが無いのですね。
Word ではなく Word VBA の話をしています…。

VBA エディタ上で [F2] キーを押して表示されるオブジェクト ブラウザー上にも
「Clipboard」という名のオブジェクトは用意されていませんよね、という話。

ちなみに VB2005 には「Clipboard」という名のオブジェクトが用意されていて、
たとえば、現在保持されているデータ形式の一覧を得るといったこともできます。

ListBox1.Items.Clear()
For Each fmt As String In Clipboard.GetDataObject().GetFormats()
    ListBox1.Items.Add(fmt)
Next


メモ帳のテキストをコピーしていた場合は、"Text" や "UnicodeText" 形式、
エクスプローラーでファイルを選択してコピーした場合には
"File" や "FileW" 形式などが並んでいることを確認できます。


> WordのVBAでPasteメソッドで貼り付けできるので、
はい。Copy / Cut / Paste メソッドを通じての単純操作程度であれば、
Word VBA からでも行えます。

しかし上記 VB2005 の例のように、クリップボード内に格納されている
データ形式を列挙したり、あるいは特定の形式のみを再加工して
書き戻すためのクラスやメソッドは、「標準では」用意されていません。
(Word 上での列挙や加工が必要な場合は、前々回の回答をご覧ください)


> マイクロソフトのオフィスに共通する、クリップボードに相当するデータ領域が有るのかと想像していました。 
クリップボードは、OS が 1 つだけ管理している、データ共有のためのリソースです。

その「Windows 上で単一の共有資源」であるクリップボードを、
VB2005 と Word VBA のそれぞれから読み書きしているだけです。

他のアプリがコピー操作を行うと、前に保持されていた内容が失われるのも
単一の共有資源であるがゆえのことです。


> 「Office クリップボードを使用する」等の表現がネット上でありましたので。
OS からクリップボード変更通知(クリップボード チェイン)を受け取ると、
Office はその内容を履歴として退避する機能があります。
それが Office クリップボード。

Word VBA の Application.ShowClipboard メソッドによって
Word にトグル表示されるペインがこれですね。

しかしこれは、あくまでデータを複製しているだけあり、
「Windows のクリップボード」はあくまでも一つだけです。

また、この履歴データの内容を直接再加工するような機能は
Word VBA には用意されていないと思います。
投稿者 (削除されました)  () 投稿日時 2018/9/6 19:37:15
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2018/9/6 19:50:17
> My.Computer.Clipboard.SetText() を使用しています。

上記だと、文字列データしか受け渡せませんね。

データを文字列としてだけではなく、「複数の形式」でコピーしたい場合は、
下記の構文を使うことができます。

Dim data As New DataObject()
data.SetData(形式1, 形式1のデータ)
data.SetData(形式2, 形式2のデータ)
data.SetData(形式3, 形式3のデータ)
Clipboard.SetDataObject(data, True)




> 数式(OMath)として貼り付けしたいのです。 

次は OMath の話。

現行の Word VBA で OMath を書くために、たとえばこんなコードを使えますよね。
Microsoft 数式エディタ時代の Word だとダメですが。


Dim objEq As Word.OMath
'摂氏と華氏の変換式 
objRange.Text = "Celsius=(5/9)(Fahrenheit-32)"
Set objRange = objRange.OMaths.Add(objRange)
Set objEq = objRange.OMaths(1)
objEq.BuildUp



Word 上に書いたこの数式をコピーしてみると、その時のクリップボード内には
MathML 形式、リッチテキスト形式、HTML 形式、Unicode テキスト形式、
メタファイル画像形式などなど、数多くの形式でコピーされていることを確認できるかと思います。

格納されている形式を確認する方法は、既に何通りか紹介しているので
先の回答をご覧いただければと思います。


でもって、Word の数式で扱われるクリップボードデータでは、
主となる形式は MathML 形式となっています。
これは下記のような XML データとして保持されています。

<?xml version="1.0" encoding="UTF-16"?>
<mml:math
 xmlns:mml="http://www.w3.org/1998/Math/MathML"
 xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math">
  <mml:mi mathvariant="normal">Celsius</mml:mi>
  <mml:mo>=</mml:mo>
  <mml:mfenced separators="|">
    <mml:mrow>
      <mml:mfrac>
        <mml:mrow>
          <mml:mn>5</mml:mn>
        </mml:mrow>
        <mml:mrow>
          <mml:mn>9</mml:mn>
        </mml:mrow>
      </mml:mfrac>
    </mml:mrow>
  </mml:mfenced>
  <mml:mfenced separators="|">
    <mml:mrow>
      <mml:mi mathvariant="normal">Fahrenheit</mml:mi>
      <mml:mo>-</mml:mo>
      <mml:mi mathvariant="normal">32</mml:mi>
    </mml:mrow>
  </mml:mfenced>
</mml:math>



上記をコピーして、Word に貼ってみてください。同じ数式が現れるかと思います。
(この時、先頭の <?xml の前に改行や空白等が入らないように注意してください)

といっても、ブラウザーから直接 Word にコピペすると、
『HTML 形式』が優先されて貼り付けられてしまう可能性がありますので、
一度、ただのテキスト形式に変換しておく必要があります。

ここでは上記を、一度「メモ帳」に貼りつけて、それをメモ帳で全選択してから
テキストしてコピーしなおし、それを Word 上に貼り付けてみてください。

これにより『テキスト形式』に変化しますので、数式が現れるかと思います。


この方法を応用すれば、Word VBA 単体でも
objRange.PasteSpecial , , , , WdPasteDataType.wdPasteText
のようにして、MathML から 数式(OMath) を貼り付けることが可能となります。
もしもこれを
objRange.PasteSpecial , , , , WdPasteDataType.wdPasteHTML
とすれば、HTML 形式で貼り付けられることになりますね。

ただし Word 2007 以降であったとしても、2003 までの旧型式(*.doc)のファイルに対して
呼び出した場合は、数式ではなく XML 文字列のまま貼り付けられてしまうという点と、
WdPasteDataType で指定した形式のデータがクリップボードに無い場合には
実行時エラーになってしまうという点については注意しておいてください。