如何:使用 XPS 打印 API 进行打印
本主题介绍如何使用 XPS 打印 API 从 Windows 应用程序打印。
XPS 打印 API 使本机 Windows 应用程序能够打印 XPS 文档。 应用程序可以使用 XPS 文档 API 创建 XPS 文档。 常见 XPS 文档编程任务帮助主题介绍了如何执行此操作。 创建 XPS 文档后,应用程序可以使用 XPS 打印 API 进行打印。
使用 XPS 打印 API 从应用程序打印文档涉及以下步骤。
- 初始化 COM 接口
- 创建完成事件
- 启动 XPS 打印作业
- 创建 IXpsOMPackageWriter 接口
- 关闭 IXpsOMPackageWriter 接口
- 关闭打印作业流
- 等待完成事件
- 发布资源
XPS 打印 API 需要 XPS 文档才能打印。 在以下示例中,XPS 打印 API 在将 XPS 文档发送到打印机时创建该文档。 通过使用 XPS 文档 API 并将其维护为 XPS OM 或将 XPS OM 保存为 XPS 文档,还可以在不将其发送到打印机的情况下创建 XPS 文档。 有关使用 XPS OM 的详细信息,请参阅 XPS 文档 API。
初始化 COM 接口
初始化 COM 接口(如果应用程序尚未这样做)。
// Initialize the COM interface, if the application has not
// already done so.
if (FAILED(hr = CoInitializeEx(0, COINIT_MULTITHREADED)))
{
fwprintf(stderr,
L"ERROR: CoInitializeEx failed with HRESULT 0x%X\n", hr);
return 1;
}
创建完成事件
创建完成事件,当打印后台处理程序从应用程序收到整个文档时, XPS 打印 API 使用该事件通知应用程序。 XPS 打印 API 还支持进度事件,以便应用程序可以了解其他假脱机活动。
// Create the completion event
completionEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!completionEvent)
{
hr = HRESULT_FROM_WIN32(GetLastError());
fwprintf(stderr,
L"ERROR: Could not create completion event: %08X\n", hr);
}
启动 XPS 打印作业
通过调用 StartXpsPrintJob 启动 XPS 打印作业。 StartXpsPrintJob 返回应用程序将向其发送要打印的文档的流。
// Start an XPS Print Job
if (FAILED(hr = StartXpsPrintJob(
printerName,
NULL,
NULL,
NULL,
completionEvent,
NULL,
0,
&job,
&jobStream,
NULL
)))
{
fwprintf(stderr,
L"ERROR: Could not start XPS print job: %08X\n", hr);
}
创建 IXpsOMPackageWriter 接口
通过在 StartXpsPrintJob 返回的流上调用 IXpsOMObjectFactory::CreatePackageWriterOnStream 来创建 IXpsOMPackageWriter 接口。
// Create an XPS OM Object Factory. If one has already been
// created by the application, a new one is not necessary.
if (SUCCEEDED(hr))
{
if (FAILED(hr = CoCreateInstance(
__uuidof(XpsOMObjectFactory),
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&xpsFactory))))
{
fwprintf(
stderr,
L"ERROR: Could not create XPS OM Object Factory: %08X\n",
hr);
}
}
// Create the Part URI for the Fixed Document Sequence. The
// Fixed Document Sequence is the top-level element in the
// package hierarchy of objects. There is one Fixed Document
// Sequence in an XPS document.
//
// The part name is not specified by the XML Paper Specification,
// however, the name used in this example is the part name
// used by convention.
//
if (SUCCEEDED(hr))
{
if (FAILED(hr = xpsFactory->CreatePartUri(
L"/FixedDocumentSequence.fdseq",
&partUri)))
{
fwprintf(stderr,
L"ERROR: Could not create part URI: %08X\n", hr);
}
}
// Create the package writer on the print job stream.
if (SUCCEEDED(hr))
{
if (FAILED(hr = xpsFactory->CreatePackageWriterOnStream(
jobStream,
TRUE,
XPS_INTERLEAVING_ON,
partUri,
NULL,
NULL,
NULL,
NULL,
&packageWriter
)
)
)
{
fwprintf(
stderr,
L"ERROR: Could not create package writer: 0x%X\n",
hr);
}
}
// Release the part URI interface.
if (partUri)
{
partUri->Release();
partUri = NULL;
}
对于此打印作业中的每个文档,启动一个新文档,然后向该文档添加页面。
启动新文档
通过调用 IXpsOMPackageWriter::StartNewDocument 在包编写器中启动新文档。 如果在调用此方法时打开文档,则会关闭该文档并打开一个新文档。
// Create the Part URI for the Fixed Document. The
// Fixed Document part contains the pages of the document.
// There can be one or more Fixed Documents in an XPS document.
//
// The part name is not specified by the XML Paper Specification,
// however, the name format used in this example is the format
// used by convention. The number "1" in this example must be
// changed for each document in the package. For example, 1
// for the first document, 2 for the second, and so on.
//
if (SUCCEEDED(hr))
{
if (FAILED(hr = xpsFactory->CreatePartUri(
L"/Documents/1/FixedDocument.fdoc",
&partUri)))
{
fwprintf(
stderr,
L"ERROR: Could not create part URI: %08X\n",
hr);
}
}
// Start the new document.
//
// If there was already a document started in this page,
// this call will close it and start a new one.
if (SUCCEEDED(hr))
{
if (FAILED(hr = packageWriter->StartNewDocument(
partUri,
NULL,
NULL,
NULL,
NULL)))
{
fwprintf(
stderr,
L"ERROR: Could not start new document: 0x%X\n",
hr);
}
}
// Release the part URI interface
if (partUri)
{
partUri->Release();
partUri = NULL;
}
添加页面
调用 IXpsOMPackageWriter::AddPage ,将应用程序中的每个文档页面写入包编写器中的新文档。
注意
假定应用程序在此步骤之前已创建页面。 有关创建文档页和向其添加内容的详细信息,请参阅 常见 XPS 文档编程任务。
if (SUCCEEDED(hr))
{
// Add the current page to the document.
if (FAILED(hr = packageWriter->AddPage(
xpsPage,
&pageSize,
NULL,
NULL,
NULL,
NULL
)))
{
fwprintf(
stderr,
L"ERROR: Could not add page to document: %08X\n",
hr);
}
}
关闭 IXpsOMPackageWriter 接口
为此打印作业编写所有文档后,调用 IXpsOMPackageWriter::Close 以关闭包。
if (SUCCEEDED(hr))
{
if (FAILED(hr = packageWriter->Close()))
{
fwprintf(
stderr,
L"ERROR: Could not close package writer: %08X\n",
hr);
}
}
关闭打印作业流
通过调用 Close 关闭打印作业流,这会告知打印后台处理程序整个打印作业已由应用程序发送。
if (SUCCEEDED(hr))
{
if (FAILED(hr = jobStream->Close()))
{
fwprintf(
stderr,
L"ERROR: Could not close job stream: %08X\n",
hr);
}
}
else
{
// Only cancel the job if we succeeded in creating a job.
if (job)
{
// Tell the XPS Print API that we're giving up.
// Don't overwrite hr with the return from this function.
job->Cancel();
}
}
等待完成事件
等待打印作业的完成事件。
if (SUCCEEDED(hr))
{
wprintf(L"Waiting for job completion...\n");
if (WaitForSingleObject(completionEvent, INFINITE) !=
WAIT_OBJECT_0)
{
hr = HRESULT_FROM_WIN32(GetLastError());
fwprintf(
stderr,
L"ERROR: Wait for completion event failed: %08X\n",
hr);
}
}
发出完成事件信号后,调用 GetJobStatus 以获取作业状态。
if (SUCCEEDED(hr))
{
if (FAILED(hr = job->GetJobStatus(&jobStatus)))
{
fwprintf(
stderr,
L"ERROR: Could not get job status: %08X\n",
hr);
}
}
if (SUCCEEDED(hr))
{
switch (jobStatus.completion)
{
case XPS_JOB_COMPLETED:
break;
case XPS_JOB_CANCELLED:
fwprintf(stderr, L"ERROR: job was cancelled\n");
hr = E_FAIL;
break;
case XPS_JOB_FAILED:
fwprintf(
stderr,
L"ERROR: Print job failed: %08X\n",
jobStatus.jobStatus);
hr = E_FAIL;
break;
default:
fwprintf(stderr, L"ERROR: unexpected failure\n");
hr = E_UNEXPECTED;
break;
}
}
释放资源
作业状态指示完成后,释放用于此打印作业的接口和资源。
if (packageWriter)
{
packageWriter->Release();
packageWriter = NULL;
}
if (partUri)
{
partUri->Release();
partUri = NULL;
}
if (xpsFactory)
{
xpsFactory->Release();
xpsFactory = NULL;
}
if (jobStream)
{
jobStream->Release();
jobStream = NULL;
}
if (job)
{
job->Release();
job = NULL;
}
if (completionEvent)
{
CloseHandle(completionEvent);
completionEvent = NULL;
}