Excelの埋め込みオブジェクトのファイル保存を自動化しようとして、まだできていない話

# 目的
Excelに埋め込まれたpdfとかのファイルを楽に保存したい。

# 結論
クリップボード経由でやれなくはなさそうだが、
かなり面倒くさそうなので、自作するのはお勧めできない。

結局できていないですが、
判ったところまでを残したいので投稿します。

# やってみたこと(1/2) - VBA or C#からExcel操作する
埋め込みオブジェクトは [ _OLEObject](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.office.interop.excel._oleobject?view=excel-pia)のようである。
プロパティやメソッドを探すも、使えそうなものが見つからなかった。
（埋め込み元のアプリで開いたり(Verb()メソッド)、
埋め込みオブジェクトとしてクリップボードにコピーさせる(Copy()メソッド)くらいしかできなかった。
Copy()でテンポラリファイルができるというblogもあったが、できず。）
<details><summary> _OLEObject に関するメモ </summary><div>

+  ` _OLEObject`インタフェースは、`Microsoft Excel 12.0 Object Library` に属している（環境により異なるかも）。
+ ` _OLEObject`は、sheetオブジェクトのもつ `OLEObjects()` を呼び出して、`foreach`とかで要素として取り出せる。
+ ` _OLEObject`の一部のメソッドやプロパティ(.Objectとか)は、C#で作ってみたが実行時に例外吐いて落ちる。。。

</div></details>

# やってみたこと(2/2) - クリップボードのデータを保存する
今回の本題。
Excel Sheet上の埋め込みオブジェクトをコピーすると、クリップボードにちゃんとデータが入る。

<details><summary> クリップボードからEmbedded Objectをファイルに保存するソースコード </summary><div>

```csharp
IDataObject data = Clipboard.GetDataObject();
if (data != null) {
    // 関連付けられているすべての形式を列挙する
    //   foreach(string fmt in data.GetFormats(){Console.WriteLine(fmt);}

    if (data.GetDataPresent("Embedded Object")) {
        dynamic obj = data.GetData("Embedded Object");
        if ( obj is MemoryStream ) {
            var ms = (MemoryStream)obj;
            using ( var fs = new FileStream("testout.dat", FileMode.Create) ) {
                ms.WriteTo(fs);
            }
        }
    }
}

// Mainメソッドに [STAThread] つけ忘れなきよう・・・

```

</div></details>

上の処理で、クリップボードのデータをファイルに保存できる。
問題はこれが元データ（EXCELに埋め込まれる前のファイル）のままではなく、謎な形式・・というかOLEになっている。
バイナリエディタで開いてみると先頭が`D0 CF 11 E0 A1 B1 1A E1`となっていて、調べてみた結果、
[[MS-CFB]: Compound File Binary File Format](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b) ・・・ ([仕様書(Rev 9.0)](https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-CFB/%5bMS-CFB%5d.pdf))
という形式らしい。

ファイルシステムを埋め込んだような構造で、割とめんどくさい。。
GitHubさがせばありそう。

# 参考になりそうなサイト

+ [[MS-CFB]: Compound File Binary File Format](https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/53989ce4-7b05-4f8d-829b-d08d6148375b) 
+ [MFC/C++](https://docs.microsoft.com/en-us/cpp/mfc/containers-compound-files?view=vs-2019)
+ https://www.decalage.info/fr/ole_extradata

# おまけ

<details><summary>バイナリエディタ(BZ Editor)向けの構造体定義の一部を張っておく</summary><div>
BZ.DEF に下記を付け足し。

```C
struct CFB_HEADER {
	BYTE	signature [8];
	BYTE	clsid [16];
	WORD	minorVersion;
	WORD	majorVersion;
	WORD	byteOrder;
	WORD	sectorSize;
	WORD	miniStream;
	BYTE	reserved1[6];
	DWORD	numOfDirSectors;
	DWORD	numOfFatSectors;
	DWORD	dirStartSectLoca;
	DWORD	tranSignature;
	DWORD	miniStreamSizeCo;
	DWORD	miniFatStartSectLoca;
	DWORD	numOfMiniFatSectors;
	DWORD	difatStartSectLoca;
	DWORD	numOfDifatSectors;
	DWORD	difat[109];
} cfb_header;

struct CFB_DIR_ENTRY {
	BYTE	nameUtf16 [64];
	WORD	nameLen;
	BYTE	ObjectType;
	BYTE	ColorFlag;
	DWORD	LeftSiblingID;
	DWORD	RightSiblingID;
	DWORD	ChildID;
	BYTE	CLSID [16];
	DWORD	StateBits;
	BYTE	CreationTime [8];
	BYTE	ModifiedTime [8];
	DWORD	StartSectLoca;
	DWORD	StreamSize[2];
} cfb_dir_entry;

```
</div></summary>

<details><summary>乱暴なやりかた：</summary><div>
Sectorの概念を無視してデータが連番のセクターに保存されていると仮定すれば、

Name(※UTF-16)が"CONTENTS"のdirectory entry上の
(StartingSectorLocation + 1) * 0x0200 = (下記例では 0x2600) から
StreamSizeだけデータを取り出せば取り出せそうだが、
ファイルフォーマットを完全に無視しているので危険。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/ccd5d210-6663-7d28-71d7-a950e09881b0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/2f468f90-6a75-6eec-ba7c-6e1a74cdb9e2.png)

</div></summary>