Visio文書を外部のC++/MFCアプリやVBA、C#から「読み取り専用で開きたい」「ステンシルはドック表示で開きたい」「非表示のまま裏で開きたい」――そう思って調べると、OnFileOpen や Documents.OpenEx など似たキーワードが多数出てきて混乱しがちです。この記事では、VisioのUIにある「開く」ボタンが内部で何を呼んでいるのか、そして実際にオプション付きで文書を開くための最短ルートを、C++(MFC)/VBA/C#のコード例付きで整理します。
Visioの「開く」ボタンと OnFileOpen の関係
まずは、よく混同される2つの世界――「MFCアプリの世界」と「Visioアプリ(COMオブジェクト)の世界」をきちんと切り分けておきます。
| コンテキスト | UIの「開く」ボタン | 実際に呼ばれるもの | ポイント |
|---|---|---|---|
| MFCアプリケーション | メニュー[ファイル] > [開く]やツールバーの「フォルダ」アイコン | ID_FILE_OPEN → CWinApp::OnFileOpen | 標準のファイルダイアログを出し、選ばれたファイルを開くハンドラー |
| Visio本体 | Visioの[ファイル] > [開く]ボタン | VisioオブジェクトモデルのDocuments.Open / Documents.OpenEx | 内部的にはこれらと同等の処理で文書を開いている |
つまり、MFCアプリの「OnFileOpen」はあくまで自分のアプリ用のハンドラーであり、VisioをCOM経由で操作するときに使う関数ではありません。Visio文書を「オプション付きで開く」場合、狙うべきは Documents.OpenEx です。
MFC側での「開く」ボタンの正体
MFCアプリでは、メニューの[ファイル] > [開く]は通常以下のような流れになっています。
- メニューやボタンから
ID_FILE_OPENコマンドが発行される - MFCフレームワークが
CWinApp::OnFileOpenを呼び出す OnFileOpen内でファイルダイアログを表示し、選択されたパスをOpenDocumentFileに渡して文書を開く
ソースコードでは概ね以下のような形になります(実装はMFCの内部に隠れています)。
// フレームに "開く" に対応するコマンドを送る
AfxGetMainWnd()->SendMessage(WM_COMMAND, ID_FILE_OPEN);
// あるいはアプリクラスから直接呼ぶ
AfxGetApp()->OnFileOpen();
// 既知のパスを直接開きたいなら
AfxGetApp()->OpenDocumentFile(L"C:\\path\\file.vsdx");
ここで重要なのは、OnFileOpen 自体には「読み取り専用で」「非表示で」などのオプション引数は存在しないことです。凝った開き方をしたい場合は、OnFileOpen をオーバーライドして、内部でVisioの OpenEx を叩く構成にするのが現実的です。
Visio側での「開く」ボタンの正体
Visio本体のリボンやメニューにある[開く]は、ユーザーから見ると単に「ファイルを開く」ボタンですが、内部的にはほぼ次のような処理を行っていると考えてよいです。
- ファイルダイアログでパスを取得
Application.Documents.Open()またはOpenEx()で文書を開く
Visioのオブジェクトモデルでは、対象となるオブジェクトは Application.Documents コレクションであり、これに対して以下の2通りのメソッドが用意されています。
| メソッド | シグネチャ(簡略) | 用途 |
|---|---|---|
Documents.Open | Open(path As String) | オプションなしで標準的に開く |
Documents.OpenEx | OpenEx(path As String, flags As VisOpenSaveArgs) | 読み取り専用やドック表示など、詳細オプションを指定して開く |
Visioの「開く」と同じ処理をしつつ、さらに細かい制御(読み取り専用・ドック表示・非表示など)を行いたい場合は、Documents.OpenEx 一択と考えて問題ありません。
Documents.Open と OpenEx の違い
両者の差は非常にシンプルです。Open は「普通に開く」だけですが、OpenEx は第2引数で様々なフラグ(VisOpenSaveArgs)を指定できます。
| 項目 | Documents.Open | Documents.OpenEx |
|---|---|---|
| 読み取り専用で開く | 不可 | visOpenRO フラグを指定 |
| ステンシルをドック表示する | 不可 | visOpenDocked フラグを指定 |
| 非表示で開く | 不可 | visOpenHidden フラグを指定 |
| コピーとして開く | 不可 | visOpenCopy フラグを指定 |
| 複数フラグの組み合わせ | 不可 | visOpenRO Or visOpenDocked のように合成 |
したがって、「VisioのUIにある開くボタン“相当”をしつつ、さらにオプションも指定したい」ときには、OpenではなくOpenEx(path, flags) を使うのが最短ルートです。
VisOpenSaveArgs の代表的なフラグ
よく使うフラグだけを用途別に整理しておきます。
| フラグ名 | 想定する主な用途 | 備考 |
|---|---|---|
visOpenRO | 文書を読み取り専用で開く | レイアウトツールなど、ユーザーに保存させたくない場合に便利 |
visOpenDocked | ステンシル(*.vssxなど)をウィンドウ左側にドック表示 | 通常の図面(*.vsdx)にはあまり意味がない |
visOpenHidden | ウィンドウを表示せずに開く | バッチ処理やサーバーサイド自動処理で使う |
visOpenCopy | 元ファイルを変更せず、コピーを開いて編集 | テンプレートファイルからの派生図面を作成するときなど |
visOpenRW | 読み書き可能として開くことを明示 | 明示したい場合に使用(通常は省略可) |
これらのフラグは整数値として定義されているため、VB/VBAでは Or、C++/C# ではビットOR演算子 | を使って組み合わせます。
' VBA の例
flags = visOpenRO Or visOpenDocked
// C# の例
short flags = (short)(Visio.VisOpenSaveArgs.visOpenRO
| Visio.VisOpenSaveArgs.visOpenDocked);
Q2: OnFileOpen を「オプション付き」で呼ぶには?という疑問への答え
ここまで整理すると、Q2に対する答えは次のようになります。
OnFileOpenは MFC のメンバー関数であり、VisioのCOM APIとは別物。OnFileOpen自体にオプション引数は存在しない。- Visio文書をオプション付きで開きたい場合は、MFCアプリ側で
OnFileOpenをオーバーライドし、内部からVisio.Documents.OpenEx(path, flags)を呼ぶのが現実的。
つまり、OnFileOpen を直接「オプション付きで呼び出す」のではなく、OnFileOpen の中で Visio の OpenEx を使うという構成になります。
実際の流れは以下のようなイメージです。
- ユーザーが自作MFCアプリの[開く]ボタンを押す
OnFileOpenが呼ばれる- ファイルダイアログでVisio文書(*.vsdx など)のパスを取得
- そのパスを使って
Visio.ApplicationのDocuments.OpenEx(path, flags)を呼び出す
以下のサンプルコードでは、この「OnFileOpenの中でVisioをOpenExで開く」イメージを具体化します。
C++(MFC)から Visio を読み取り専用+ドック表示で開く
まずは、もっとも典型的なシナリオである「C++(MFC)アプリからVisioを起動し、文書を読み取り専用+ステンシルをドック表示で開く」例です。
#include <afxdisp.h> // AfxOleInit など
#import "Visio.exe" rename_namespace("Visio") // Visio の型ライブラリ(パスは環境に合わせて調整)
void OpenVisioAsRODocked(const wchar_t* path)
{
// 1. COM/OLE 初期化(MFCアプリの InitInstance などで一度だけ呼ぶのがベスト)
if (!AfxOleInit())
{
// TODO: エラー処理(メッセージボックスなど)
return;
}
// 2. Visio.Application オブジェクトを起動
Visio::ApplicationPtr app;
HRESULT hr = app.CreateInstance(__uuidof(Visio::Application));
if (FAILED(hr) || app == nullptr)
{
// TODO: Visio がインストールされていない、起動できない等のエラー処理
return;
}
// 3. 必要なら Visio 自体を表示
app->PutVisible(VARIANT_TRUE);
// 4. フラグを組み合わせる(読み取り専用+ドック表示)
short flags = Visio::VisOpenSaveArgs::visOpenRO
| Visio::VisOpenSaveArgs::visOpenDocked;
// 5. Documents コレクション経由で OpenEx を呼ぶ
Visio::DocumentsPtr docs = app->Documents;
Visio::DocumentPtr doc = docs->OpenEx(_bstr_t(path), flags);
// 6. doc を保持しておけば、後続でページやシェイプにアクセスできる
}
ポイントを箇条書きで整理すると次の通りです。
AfxOleInitを忘れない(生のWin32アプリならCoInitializeEx)。- 32bit Visio なら呼び出し側も 32bit、64bit Visio なら 64bit ビルドに揃える。
#import "Visio.exe"のパスは環境に応じて書き換える(Officeのインストール場所)。OpenExに渡すパスは、実運用ではフルパスを推奨(相対パスだとトラブルの原因になりやすい)。
MFCの OnFileOpen から呼び出す例
先ほどの関数を、MFCアプリの CWinApp::OnFileOpen から呼ぶイメージは次のようになります。
void CMyApp::OnFileOpen()
{
// 1. ファイルダイアログを表示してユーザーに Visio ファイルを選ばせる
CFileDialog dlg(TRUE, L"vsdx", nullptr,
OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
L"Visio 図面 (*.vsdx)|*.vsdx|すべてのファイル (*.*)|*.*||");
if (dlg.DoModal() != IDOK)
{
return; // キャンセル
}
CString path = dlg.GetPathName();
// 2. 選ばれたファイルを Visio で読み取り専用+ドック表示で開く
OpenVisioAsRODocked(path);
}
このように、MFCの「開く」ハンドラーである OnFileOpen と、Visioの OpenEx を「橋渡し」する設計にしておくと、アプリ全体の見通しが良くなります。
VBA から Visio を読み取り専用+ドック表示で開く
Visio内部のマクロであれば、よりシンプルに Application.Documents.OpenEx を呼ぶだけで済みます。
Sub OpenRODocked()
' Visio を表示
Application.Visible = True
' フラグを合成(読み取り専用+ドック表示)
Dim flags As Integer
flags = visOpenRO Or visOpenDocked
' OpenEx でオプション付きで開く
Application.Documents.OpenEx "C:\Path\Diagram.vsdx", flags
End Sub
Visioのマクロとして手早く試すには、このVBA例がもっとも手軽です。「どのフラグを組み合わせるとどんな挙動になるか」を確認するためのサンドボックスとしても役立ちます。
C#(.NET Interop)から Visio 文書を開く
C# から Microsoft.Office.Interop.Visio を使って同じことを行う場合は、次のようなコードになります。
using Visio = Microsoft.Office.Interop.Visio;
class VisioSample
{
public void OpenVisioDoc()
{
// 1. Visio.Application を起動
var app = new Visio.Application();
// 2. フラグを合成
short flags = (short)(
Visio.VisOpenSaveArgs.visOpenRO |
Visio.VisOpenSaveArgs.visOpenDocked
);
// 3. Documents.OpenEx で開く
Visio.Document doc = app.Documents.OpenEx(
@"C:\Path\Diagram.vsdx",
flags
);
// 4. 必要に応じてウィンドウやページを操作
}
}
あらかじめ Visual Studio の「参照の追加」で Microsoft.Office.Interop.Visio を追加しておくことを忘れないようにしましょう。
MFC側で「ボタンを押したのと同じ動き」を起こす方法
自作MFCアプリ内だけで話を完結させたい場合、「UIの[開く]ボタンと同じ動作をコードから発火させる」こともできます。これはVisioではなく、あくまでMFCアプリ自身の話です。
// UI の「開く」相当を発生させる
AfxGetMainWnd()->SendMessage(WM_COMMAND, ID_FILE_OPEN);
// もしくは(アプリが OnFileOpen をオーバーライドしていれば)直接呼ぶ
AfxGetApp()->OnFileOpen();
// 既知のパスを直接開く(Doc/View 構造の場合)
AfxGetApp()->OpenDocumentFile(L"C:\\path\\file.vsdx");
ここで開く対象が Visio 文書であっても、MFCの観点では「単なるファイル」でしかありません。Visio側のオプション付き動作(読み取り専用・ドック表示など)を制御したいなら、最終的にはやはり Visio の OpenEx を叩く必要があることを忘れないようにしましょう。
よくあるハマりどころと対処法
Visio自動化+MFC/COM周りでつまずきやすいポイントを、原因と対処法付きで一覧にしておきます。
| 症状 | 主な原因 | 対処法 |
|---|---|---|
OpenEx で例外/アクセス違反が発生 | COM/OLE 初期化忘れ、または 32/64bit の不一致 | AfxOleInit / CoInitializeEx を起動時に実行し、ビルドのビット数を Visio と合わせる |
OpenEx が「ファイルが見つからない」エラー | 相対パスの解決に失敗している | フルパスを渡す/ワークディレクトリを明示的に設定する |
visOpenDocked を指定してもドックされない | 図面(*.vsdx)に対して指定している | visOpenDocked は主にステンシル(*.vssx 等)に対して有効である点を理解する |
OnFileOpen にオプションを渡したい | OnFileOpen はパラメータ無しの設計 | アプリ側で OnFileOpen をオーバーライドし、内部で OpenEx を呼ぶように設計する |
| Visioが終了してくれない | COM参照が残っている/明示的に Quit していない | app.Quit() を呼び、スマートポインタや RCW をスコープから外す |
シナリオ別の使い分け指針
最後に、「どの場面で何を使えばよいか」を一枚の表にまとめます。
| やりたいこと | おすすめのAPI/方法 | 備考 |
|---|---|---|
| Visio内部でマクロからオプション付きで開きたい | Application.Documents.OpenEx(path, flags) | VBAから簡単に試せる。フラグの動作確認にも最適。 |
| 外部C++/MFCアプリからVisio文書を読み取り専用で開きたい | Visio.Application を COM で生成し、Documents.OpenEx | AfxOleInit を必ず実行し、ビルドのビット数をVisioと合わせる。 |
| 外部C#アプリからVisioを操作したい | Microsoft.Office.Interop.Visio の Application.Documents.OpenEx | 参照設定を追加し、VisOpenSaveArgs をビットORで組み合わせる。 |
| 自作MFCアプリの[開く]ボタンと同じ動きをさせたい | AfxGetMainWnd()->SendMessage(WM_COMMAND, ID_FILE_OPEN) | Visio操作とは無関係。自アプリのファイル開き動作を再現したいときに使用。 |
| Visioをユーザーに見せずに裏で処理したい | OpenEx(path, visOpenHidden) など | サーバーサイド自動処理や帳票生成などで便利。エラーハンドリングは特に慎重に。 |
まとめ
内容をあらためて整理すると、次のようになります。
- Q1(「開く」ボタンはどの関数?)
MFCアプリでは、UIの[開く]はID_FILE_OPEN→CWinApp::OnFileOpenに紐付きます。Visio本体の[開く]は、内部的にはDocuments.Open/Documents.OpenExと同等の処理で文書を開いていると考えられます。 - Q2(OnFileOpenをオプション付きで呼びたい)
OnFileOpen自体にオプション引数はありません。代わりに、アプリ側でOnFileOpenをオーバーライドし、その中で Visio のDocuments.OpenEx(path, flags)を呼び出すことで、「読み取り専用」「コピーとして開く」「非表示で開く」などの挙動を柔軟にコントロールできます。
Visioの自動化は、「どの世界のAPIの話なのか(MFCなのか、Visioなのか)」を意識して切り分けるだけで一気に理解しやすくなります。OnFileOpen はMFCの世界、オプション付きで文書を開くのはVisioの Documents.OpenEx の仕事――この2つの役割を押さえておくだけで、実装時の迷いがかなり減るはずです。

コメント