VBAでDocuWorksAPIを利用して複数のDocuWorks文書からページ順を指定して合成する


## はじめに
前回の記事、「[VBAでDocuWorksAPIを利用して複数のDocuWorks文書を合成して名前をつけて保存する](https://qiita.com/Hacomo/items/6aaa0168c383fbdc0898)」では、ファイル単位の合成はできますが、実際の業務では複数の様式が混在したファイルを作成したり、整理する必要があります。
DocuWorksDeskではページ操作に関連する機能が利用可能ですが、ページ順を指定しての並び替え、合成を自動化することはできません。
今回は「__XDW_GetPage___」と「___XDW_MergeXdwFiles__」を組み合わせることで、これを実現します。

## 目的
単一または複数のDocuWorks文書のページを指定順で合成したい。

## 処理フロー
１　合成用パス・ページ配列と保存用パスを取得
２　__XDW_GetPage__により合成用Docuworks文書の指定ページを複製し、合成用一時ファイルとして保存する
３　合成用一時ファイルを__XDW_MergeXdwFiles__により合成する
４　合成用一時ファイルを削除する
　
<details><summary>**サンプルコード**</summary><div>

```vb
Option Explicit

'XDW_GetPage   DocuWorksファイルの指定のページをコピーして、別の1つのDocuWorks文書にする。
Private Declare PtrSafe Function XDW_GetPage _
    Lib "xdwapi.dll" ( _
        ByVal handle As LongPtr, _
        ByVal nPage As Long, _
        ByVal lpszOutputPath As String, _
        Optional ByVal reserved As String = vbNullString) As Long

'XDW_OpenDocumentHandle  DocuWorksファイルにアクセスするためのハンドルを得る。pHandleはAPIにより変更されるため、ByRefで定義
Private Declare PtrSafe Function XDW_OpenDocumentHandle _
    Lib "xdwapi.dll" ( _
        ByVal lpszFilePath As String, _
        ByRef pHandle As LongPtr, _
        ByRef pMode As XDW_OPEN_MODE) As Long

'XDW_CloseDocumentHandle  DocuWorksファイルにアクセスするためのハンドルを解放する。
Private Declare PtrSafe Function XDW_CloseDocumentHandle _
    Lib "xdwapi.dll" ( _
        ByVal handle As LongPtr, _
        Optional ByVal reserved As String = vbNullString) As Long

'XDW_MergeXdwFiles  複数のDocuWorks文書を合成して1つのDocuWorks文書を生成する。
Private Declare PtrSafe Function XDW_MergeXdwFiles _
    Lib "xdwapi.dll" ( _
        ByRef lpszInputPaths As LongPtr, _
        ByVal nFiles As Long, _
        ByVal lpszOutputPath As String, _
        Optional ByVal reserved As String = vbNullString) As Long

Private Type XDW_OPEN_MODE
    nSize As Long
    nOption As Long
End Type

'******************************************************************************************************
'機能　単一または複数のDocuWorks文書の指定ページ順に合成してDocuWorks文書を生成する。
'引数　inputDwPaths　束ねるDW文書のファイルパス配列 /inputDwPages　束ねるDW文書のページ配列 /outputDwPath　出力パス
'戻値　XDWAPIエラーコード(Long型,16進数)
'備考  合成パス配列の要素数≦ページ番号配列の要素数であること
'******************************************************************************************************
Public Function MergeDwFilesFromPage( _
    ByVal inputDwPaths As Variant, _
    ByVal inputDwPages As Variant, _
    ByVal OutputPath As String) As Long

'合成用DocuWorks文書を作成元Docuworks文書の指定ページから作成
Dim inputL As Long: inputL = LBound(inputDwPaths)
Dim inputU As Long: inputU = UBound(inputDwPaths)
Dim OutputFolderPath As String: OutputFolderPath = Mid$(OutputPath, 1, InStrRev(OutputPath, "\"))

Dim TempDwPaths() As String: ReDim TempDwPaths(inputL To inputU)
Dim TempDwSJISPaths() As String: ReDim TempDwSJISPaths(inputL To inputU)
Dim TempDwPtrs() As LongPtr: ReDim TempDwPtrs(inputL To inputU)

'DW_OPEN_READONLY=0/DW_OPEN_UPDATE=1
Dim OpenMode As XDW_OPEN_MODE
OpenMode.nOption = 0&
OpenMode.nSize = LenB(OpenMode)

Dim pHandle As LongPtr
Dim i As Long
For i = inputL To inputU

    TempDwPaths(i) = OutputFolderPath & "dwMergeTemp" & i & ".xdw"
    
    'XDW_MergeXdwFilesのための文字コード変換とポインタ取得
    TempDwSJISPaths(i) = StrConv(TempDwPaths(i), vbFromUnicode)
    TempDwPtrs(i) = StrPtr(TempDwSJISPaths(i))
    
    'DocuWorksAPI:XDW_GetPageで合成用一時ファイル作成
    XDW_OpenDocumentHandle inputDwPaths(i), pHandle, OpenMode
    XDW_GetPage pHandle, inputDwPages(i), TempDwPaths(i)
    XDW_CloseDocumentHandle pHandle
Next

'DocuWorksAPI:XDW_MergeXdwFilesの呼び出し
Dim fileNum As Long: fileNum = inputU - inputL + 1
MergeDwFilesFromPage = XDW_MergeXdwFiles(TempDwPtrs(inputL), fileNum, OutputPath)

'合成用一時ファイルの削除
For i = inputL To inputU
    Kill TempDwPaths(i)
Next

End Function
```
</div>

## 解説
__XDW_GetPage__を利用するには、__XDW_OpenDocumentHandle__によりDocuWorksのハンドルを取得、XDW_GetPageを呼び出したあと、__XDW_CloseDocumentHandle__によりハンドルを解放する必要があります。
この際、__XDW_OpenDocumentHandle__の引数___pHandle___はAPIにより変更されるため、ByRefで定義しています。

## 補足
DocuWorksAPIには他に「XDW_InsertDocument」というDocuWorks文書に他のDocuWorks文書を挿入する関数が用意されていますが、APIの処理時間により、一時ファイルの削除を行うKillステートメントでエラーとなる場合があるため、XDW_MergeXdwFilesを利用するほうがコードの量も動作も安定します。

## 最後に
今回のサンプルは、実際の業務でDocuWorksAPIを活用していく上でかなり汎用性の高い処理になります。
・ワード等からの差し込み文書を束ね直して印刷原稿を作成
・フォーム用DwCtrlコントロールのPrintNoDialog/PrintWithDialogメソッドによる印刷処理用の一時ファイル作成
・ランダム順スキャン文書の分類
など、利用の幅がかなり広くなります。