Comparing the OPC Managed-Code and Native-Code APIs

Windows 7 incorporates a new set of native-code APIs that provide integrated support for the ECMA 376-2 (2006) Open Packaging Conventions (OPC) standard.  These new native-code Packaging APIs provide similar functionality to what had previously been available in .NET managed-code through the System.IO.Packaging APIs.

This article highlights programming differences between the native-code and managed-code APIs, and will be useful to developers who are looking to transition applications from one API environment to the other.  Both OPC APIs are compliant with the ECMA 376-2 (2006) standard, and files created by either API set can be compatibly accessed by the other.

Differences Between the OPC Managed-code and Native-code APIs

Zip Encoding:
The managed-code System.IO.Packaging APIs create packages using the ZIP32 encoding by default. You cannot specify which encoding to use when creating the package. If the package size exceeds the limits imposed by ZIP32 encoding, the package is automatically saved using ZIP64 encoding. The native-code Packaging APIs differ in that they create packages using ZIP64 encoding by default. You can specify in the IOpcFactory::WritePackageToStream API flags to use ZIP32 encoding, however, if the package size exceeds the limits imposed by ZIP32 encoding, the API will fail and the package will not be saved.

In-place Editing:
The managed-code System.IO.Packaging APIs allow you to open a package, make changes to the content, and then save the changes back to the package. The native-code Packaging APIs differ in that they do not allow you to edit a package in place. They can read a package using the IOpcFactory::ReadPackageFromStream API, make edits, and then save to another package file via the IOpcFactory::WritePackageToStream API. Using the APIs to simultaneously read and write to the same stream, or to open two streams to the same file at the same time is not supported.

Core properties API:
The native-code API currently does not provide any equivalent of the managed-code PackageProperties class, which provides ability to read and edit the Core Properties part. When using the native-code APIs you will have to manually get the Core Properties part from the package, parse it, and then edit its contents. The MSDN Set Author Sample shows how to access and update the contents of the Core Properties part.

Pack URI support:
The managed-code PackUriHelper class supports creating URIs based on the Pack URI scheme* (pack://) that address both parts in a package and an entire package. The native-code Packaging APIs only make use of part URIs, and as such, only support creating and operating with the path component of Pack URIs that represent part names (IOpcFactory::CreatePartUri and IOpcPartUri).
*ECMA 376-2 (2006), Annex B

PackWebRequest/PackWebResponse:
The managed-code API includes PackWebRequest and PackWebResponse classes that allow you to access content within a URI-addressable package stored on a website. PackWebRequest and PackWebResponse are based on the managed-code WebRequest and WebResponse classes that support URI requests to access resources over the web. The native-code API provides no equivalent to this feature.

Content Types Stream missing in the Package:
The ECMA 376-2 (2006) OPC standard is unclear as to what to do if the [Content_Types].xml stream is missing. If the [Content_Types].xml stream is missing the managed-code Package.Open method will open the package, as if it were empty. (If the [Content_Types].xml stream is missing, none of the parts in the package are recognized as valid parts.) The native-code IOpcFactory::ReadPackageFromStream API, however, considers a package without a [Content_Types].xml stream an error, and will fail if there is no [Content_Types].xml stream.

Compression for Relationships Parts:
In the managed-code APIs, the relationships parts use the same compression as the source part; and for the package relationships part, no compression is used. In the native-code API, all relationships parts use a default compression of OPC_COMPRESSION_NORMAL.

Accessing Relationships Parts:
The managed-code APIs allow you to use the Package.GetPart API with the name of the relationships part to get that part and its content. They also allow you to perform all operations that are permitted on parts.

The native-code Packaging APIs do not allow direct access to relationships parts. IOpcPartSet::GetPart will return an error if requesting a part with a relationships part name. Instead you can call IOpcPart::GetRelationshipSet or IOpcPackage::GetRelationshipSet to get the IOpcRelationshipSet object. The IOpcRelationshipSet object provides access to the individual relationships, and a read-only stream to the relationships markup.

Editing Relationship Stream:
The managed-code APIs allow you to get the stream for a relationship part's content, and edit it. The native-code IOpcRelationshipSet::GetRelationshipsContentStream API only provides a read-only stream to the relationship part's content. To add or delete relationships from the IOpcRelationshipSet, you need to use the CreateRelationship and DeleteRelationship APIs.

- Ali Naqvi