Wednesday, March 30, 2011

The Still Life Coffee House in Fremont Mural

The Mural

The title The Still Life Coffee House in Fremont Mural is not an official title that we know of but it is written on the wall so we’ll go with that. The mural is signed Parris, 1994 and is located here near the corner of Fremont and N 35th Street. On the web site Master of Street Art / Examples of Seattle Kitsch, the artist Parris is described as “the dean of Seattle street artists. His ‘primitive,’ linear designs appear everywhere in the city’s funkier districts…”.

The Still Life Coffee House has been gone for years now, but this mural remains. (The 35th Street Bistro is now in the old Still Life Coffee House space adjacent to the mural.) The mural is sort of jazzy-modern-cubist in an early 20th century way. (How’s that for artsy language?) We think of the following artists when looking at the mural: Fernand Léger (1881-1955), Paul Klee (1879 - 1940), Wassily Kandinsky (1866 - 1944), Thomas Hart Benson (1889 - 1975), and Jacob Lawrence (1917 - 2000). The more we stared at the picture before creating this blog post, the more remarkable the mural seemed.

Various Color Tweaks of the Mural

The Fremont “Fremont” Mural

The Mural au Naturel
Fremont Fremont Mural

We’ve been on a mural and graffiti kick lately. So while we are we'd like to point out this stylized rendering of the word “Fremont”. We guess you’d call it a mural. It’s fun to look at and it’s got tagging that somehow works in this situation. It is located here on Fremont Ave N between N 34th St and N 35th Street in an alley. Artist: unknown.

The Mural Color Tweaked
Fremont Fremont Mural without color

The Fremont Mural as of December 2014. Painting out tags.
The Mural as of December 2014. Painting out tags.

Saturday, March 26, 2011

The 46th Street Mural – Fremont

A Giraffe Peeks Out From the 46th Street Mural
46th Street Mural - Fremont

The new mural on the block in Fremont is the 46th Street Mural (location). The blog for the mural is here, the Facebook page for it here, and the site of the designer and art director, Todd Lown, is here. It looks fresh. We hope it stays that way and doesn’t fall to the same fate as the Aurora Bridge Mural just south of it which is accumulating graffiti. Some great night photos of the 46th Street Mural are here.

Views of the 46th Street Mural
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural - Fremont
46th Street Mural Plaque
46th Street Mural - Fremont

The Aurora Bridge Mural, Fremont and Graffiti

The Aurora Street Mural

The mural on N. 38th Street at Bridge Way under Aurora (location) is officially called the Aurora Bridge Mural (see photo of plaque below). We never knew what it was called until we walked up close to it and went back and forth over the surface to find the plaque which is located up high on the west end of the mural. The mural was created by Patrick Gabriel in 1996 and painted in 1997.

The mural has been restored (according to our memory) at least a few times since 1997 to clean up graffiti. It is now again starting to accumlate graffiti again in the form of “tagging”. From the highlights of a 2010 Graffiti Report conducted in Seattle, graffiti tags are defined as:

…simple names or symbols, often written in a stylized manner found in high volumes and in high-visible locations. Tags range from small single-color marks to large elaborate “pieces” in multiple colors and bubble-lettering. Seattle officials indicate that “tagging” is the City’s most common graffiti.
The full report cites statistics from the Portland Police Department that indicate that tagging accounted for 80% of the graffiti in Portland and goes on to give a less than flattering portrait of a tagger. He (and it is a he) is caucasian, educated and computer literate. He has an addictive personality, a chemical dependency, and is prone to violent behavior.

For more on graffiti in Fremont and past efforts to restore the 38th Street Mural, see FremontUniverse. For a little insight on sticker graffiti, see the MyBallard blog. On a related note, we were surprised to see that Aurora – the street - has its own web site with the tag line “Stories from Seattle’s Favorite Street.” Really, Seattle’s favorite street? According to the the full report cited above, parts of Aurora are definitely a hotspot for graffiti – see hotspot image.

Seattle Graffiti Hotspot Map (ref)
Seattle Graffiti Hotspot Map

Views of the Aurora Street Mural








Aurora Street Mural Plaque

Sunday, March 13, 2011

Io non ho paura – la musica

Io non ho paura - book cover frontIo non ho paura - book cover back 

We have been reading Io non ho paura (I’m Not Scared) by Niccolò Ammaniti in our Italian class at Three Things Italian and there are several pieces of music that are used in the story that we wanted to mention here. First, some background on the story. Ammaniti published the book in 2001 and it was made into a movie released in 2003. The story takes place during Italy’s anni di piombo (years of lead) which were a period of social and political turmoil in Italy from the late 1960s to the early 1980s. The turmoil was marked, in particular, by kidnappings, including the kidnapping and assassination of Aldo Moro. With this as background, the story is set in 1978 and involves a kidnapping of the young son of a rich northern family. The kidnapped boy is kept in a hole somewhere in the south of Italy and it’s upon this hole the main character Michele Amitrano stumbles. As the story unfolds Michele finds out why the boy is in the hole and who is involved. 

The musical references include: 

Chapter 4. A snippet from Verdi’s La traviata. While Michele’s mother is spanking him, La traviata is playing on the radio. Italian: "Croce. Croce e delizia. Delizia al cor..." English: “Torment. Torment and delight of my heart…” 

Chapter 5. The song Bello Ciao is an Italian partisan song of World War II is sung by Barbara as she approaches the carob tree where Michele is hiding. "O partigiano, portami via, che mi devon seppellir. O partigiano, portami via. O bella ciao ciao ciao". English: "Oh partisan carry me away, because they must bury me. Oh partisan, carry me away. Oh goodbye beautiful, bye, bye." Michele has taken refuge in the tree because he did not want to share his bedroom with Sergio, a visitor to his home. The song reflects Michele rebelling against authority, his parents. 

Mina’s Parole, Parole, Parole where we find the not so nice Felice Natale (yes, that’s his name) singing it at the abandoned house where the kidnapped boy is hidden. Italian: "Tu sei come il vento che porta i violini e le rose. Parole, parole, parole. Ascoltami. Parole, parole, parole. Ti prego." English: “You are like the wind that brings violins and roses. Word, words, words. Listen to me. Words, words, words. I beg you.” We’ve always had a thing for Mina here at Travelmarx: Mina, Mina, Mina; Back to Mina, E Poi Mina, Bula Bula, and Carosello – Italian Ads. The use of the song here foreshadows the discovery by Michele that his parents' advice is hollow in terms of whom they say are the people in the world you have to watch out for. 

Chapter 6. Lucio Battisti’s Con il nastro rosa. In this scene Michele’s mamma, Teresa Amitrano, singing to herself in one of her few worry-free moments. Italian: "Inseguendo una libellula in un prato, un giorno che avevo rotto col passato." English: “Chasing a dragonfly in a meadow, a day that I had broken with the past.” By the end of the story, the past will creep up on Teresa. A nursery rhyme (filastrocca) about the cuckoo of Northern Italy that the tormented Felice sings as he catches Michele with the kidnapped boy. "L'inverno è passato, l'aprile non c'e più, e ritornato è maggio al canto del cucù, cu-cù, cu-cù, l'aprile non c'è più, e ritornato è maggio al canto del cucù." A nonliteral but phonetically better sounding translation is “The winter now is over, And April rains are gone. It’s May again, I know, For I hear the cuckoo’s song.” 

Chapter 10Paolo Conte’s Onda su onda. In this scene, Melichetti, a farmer, is singing the song late at night watched by Michele from the darkness. Italian: "Che acqua gelida qua, nessuno piú mi salverà. Son caduto dalla nave, son caduto, mentre a bordo c’era il ballo. Onda su onda…" English: “What freezing cold water here, no one will save me at all. I fell off the boat, I fell while on board there was a dance.” The song literally describes Michele's situation, out in the dark of night trying to find his way as he attempts to free the kidnapped boy. Michele was also figuratively thrown overboard from the ship that was his innocent youth.

Windows and IE Pinned Sites Functionality – For Sites That Don’t Enable The Functionality

Update 2023-12-19:  For the Edge browser, you simply go to the site (in a browser tab in Edge), select ".../More Tools/Pin to Taskbar". It seems to work for all sites.

Update 2013-04-14:  I just tried this with IE10 and Windows 8 and the steps shown here still work.
The Pinned Sites integration between Internet Explorer 9 and Windows 7 can be a time saver for sites you go to often. The goal of this post is to show one way of getting Windows 7 and IE9 Pinned Site functionality for a site that doesn’t implement the functionality.

The idea behind pinned sites is that you browse to a site in IE9, tear off the site, and attach it to the task bar in Windows 7. If the site implements special meta elements or implements some IE9-specific JavaScript then you get a richer taskbar item. For example, Facebook pinned to the task bar looks like this:

Facebook Pinned Site

Similarly, Amazon pinned to the taskbar looks like this:

Amazon Pinned Site

But what if the site you want to pin doesn’t implement the meta elements or the IE9-specific JavaScript? Or, you want to create your own customization of a pinned site? When we first read about the pinned sites functionality, we immediately thought that it would be cool for Windows 7 users to create their own pinned site customizations. But, on closer inspection we learned that the functionality is really targeted at web site owners allowing a richer experience for their users. But, with a little work you can customize a site that has pinned functionality built in or create pinned functionality for a site that doesn’t implement it. We’ll consider the latter case in this post. For example, at the time of writing, the Wikipedia site does not implement the pinned site functionality. If you browse to Wikipedia and pin the site you end up with the generic implementation of a pinned site that looks like this:

Wikipedia Pinned Site - Generic

With the simple trick shown here, you’ll be able to create a Wikipedia pinned site that is customized and looks like this:

Wikipedia Pinned Site Customization

The trick we’ll employ here is to use Fiddler, a web debugging proxy tool, to insert meta elements and custom JavaScript before rendering the page in the browser. Download Fiddler if you don’t already have it.

Before getting started, take a look at the developer documentation to understand how the meta elements and IE9-specific JavaScript are specified. Notice how in the Amazon pinned site image shown above there are “favorites” and “tasks” categories. The items in the “tasks” categories are created with a meta element like this (broken into several lines to make it readable):
<meta
name='msapplication-task'
content='name=Login - English;
action-uri=http://en.wikipedia.org/w/index.php?title=Special:UserLogin&returnto=Main_Page;
icon-uri=http://www.wikipedia.org/favicon.ico'/>


The items in the “favorites” categories (called a jumplist) are created programmatically with JavaScript code like this:

window.external.msSiteModeAddJumpListItem(
'Wikiquote','http://en.wikiquote.org/wiki/Main-Page',
'http://en.wikiquote.org/favicon.ico');


The steps for creating pinned site functionality for Wikipedia are:

1. Start Fiddler.

2. Customize the Rules.js for Fiddler

Fiddler Rules

3. Find the OnBeforeResponse(oSession: Session) function in Rules.js and after the last if statement add the code below. The JavaScript snippet below can be downloaded from here.

// Wikipedia Pinned Site Customization
if ((oSession.oResponse.headers.ExistsAndContains("Content-Type", "html")) &&
(oSession.HostnameIs("www.wikipedia.org"))) {
// Remove any compression or chunking
oSession.utilDecodeResponse();
var oBody = System.Text.Encoding.UTF8.GetString(oSession.responseBodyBytes);

// Add our metadata tags and JavaScript
var oRegEx = /<head>/i;
var extraMetaData = "<head>\r"
extraMetaData += "<meta name='msapplication-task' content='name=Login - English;action-uri=http://en.wikipedia.org/w/index.php?title=Special:UserLogin&returnto=Main_Page;icon-uri=http://www.wikipedia.org/favicon.ico'/>";
extraMetaData += "<meta name='msapplication-task' content='name=Entra - Italiano;action-uri=http://www.wikipedia.org/w/index.php?title=Speciale:Entra&returnto=Pagina_principale;icon-uri=http://www.wikipedia.org/favicon.ico'/>";
var extraScript = "<script type='text/javascript'>" +
"try {" +
"window.external.msSiteModeCreateJumplist('Wikipedia Favorites');" +
"window.external.msSiteModeAddJumpListItem('Wikiquote','http://en.wikiquote.org/wiki/Main-Page','http://en.wikiquote.org/favicon.ico');" +
"window.external.msSiteModeAddJumpListItem('Wikinews', 'http://en.wikinews.org/wiki/Main_Page', 'http://en.wikinews.org/favicon.ico');" +
"window.external.msSiteModeAddJumpListItem('Wikisource','http://en.wikisource.org/wiki/Main-Page', 'http://en.wikisource.org/favicon.ico');" +
"window.external.msSiteModeAddJumpListItem('Wikispecies','http://en.wikispecies.org/wiki/Main-Page', 'http://species.wikimedia.org/favicon.ico');" +
"window.external.msSiteModeAddJumpListItem('Wikiversity','http://en.wikiversity.org/wiki/Wikiversity', 'http://en.wikiversity.org/favicon.ico');" +
"window.external.msSiteModeAddJumpListItem('Wikipedia - Italiano','http://it.wikipedia.org/wiki/Pagina_principale', 'http://it.wikipedia.org/favicon.ico');" +
"} catch (ex) {} <\/script>";
oBody = oBody.replace(oRegEx, extraMetaData + extraScript);

// Set the response body with the changes
oSession.utilSetResponseBody(oBody);
}


4. Save the rules so Fiddler can pick up the changes you made.

5. Make sure you are capturing web traffic (see the bottom left corner of Fiddler).

6. Open Internet Explorer and go to http://www.wikipedia.org/, with Fiddler running and capturing traffic. Hit CTRL + F5 to make sure you get a fresh page and not a cached page.

7. Pin the site to the Windows 7 taskbar and you should have the Wikipedia pinned site customized as shown above.

8. After you have created the pinned site, remove the code from Fiddler’s custom rules because you don’t need it anymore.

What the snippet of JavaScript code does is search for the head element of the page and right before it is rendered in the browser it adds the meta elements and IE9-specific JavaScript. Fiddler provides the option with the OnBeforeResponse function to do this work right before the page is rendered to the browser.

Where are pinned sites kept in Windows 7? Pinned site are here:
C:\Users\[USER]\AppData\Local\Microsoft\Internet Explorer\Pinned Sites.

Wednesday, March 9, 2011

C++ Console Application to Get Comments from a Microsoft Word File

Update 2019-02-19:  Many thanks to Brian Thomas @ Kutana Software for helping getting this run. The C++ project is on Github at https://github.com/travelmarx/travelmarx-blog/tree/master/ExtractComments.

Update 2019-02-11:  Based on a comment received (see below), we reviewed this code and found a number of problems.

  1. That the presentation of the code below is bad having been escaped poorly when inserted.
  2. The code itself (when corrected for escape problems) when put into VS 2017 gave a number of syntax errors.
  3. The status of OPC has changed such that getting the C++ project to build in VS wasn't obvious. Our trouble was around getting the right System.IO.Packaging and WindowsBase references - we think. Some of the original SDK Samples for Windows 7 are in Github here.
What to do? First, here is the code on Github that was intended. This addressed problems 1 and 2. We made some slight changes to the code to remove syntax errors (using SysAllocString in several spots) as well as changing stdafx.h to pch.h. The name of the precompiled library isn't fixed, just that for C++ console apps in VS 2017, pch.h was used.

For problem 3, we are working on a possible solution (see update above). If you  are interested in running extracting comments using .NET (C# and VB), see How to: Retrieve comments from a word processing document (Open XML SDK).

Begin Initial Post

Output from Comment Extraction

The goal of this post is to show how to construct a C++ console application that will extract comments from a Word document. This post builds on a previous post which showed extracting comments from a Microsoft Word document (2007 or greater). In the previous post, Getting Comments from a Microsoft Word File: Leveraging the OPC Format, we did the extraction by changing the extension of the Word document and accessing the files directly in the ZIP structure. In this post, we take the Word document as is and use a console application written in C++/COM and leveraging the OPC API to directly access the comments. The code shown here was run in Visual Studio 2010 on Windows 7.

The key to the console application logic is to understand the document parts of the Word XML format. When we crack open the Word ZIP file we could get the comments file directly. Using the API we have to follow the pattern set out in the API. The pattern for a Word document is discussed here on MSDN and here. The main document part (../word/document.xml) is the main part in the package and that the comments part (../word/comments.xml) has a relationship to the main document part that can be used to obtain the coments. On our first try, we kept trying to get the comments part directly from the package relationships which didn't work. However, once we got the document part from the package (see the FindPartByRelationshipType method in the program below), we then could use the same logic to get the comments part from the document part.

A crucial part of the console application are the definitions of content types and relationship types of parts to parts. These definitions are defined in the header file (ExtractComments.h) for this application. For example, the content type of the comments part is:

application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml

The relationship of the comments part to the document part:

http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments

Note: In this console application we did not deal with the fact that comments in a Word document can contain more than just text. In the previous post we did deal with hyperlinks as example of content besides text in comments. These improvements to this code would need to be added here. Specifically, if you look at the ECMA-376 part1 for the docx format, you can find the details of what a comment can contain and it includes charts, diagrams, hyperlinks, images, video, and embedded content.

The code shown here was build starting from the SDK samples provides with the OPC SDK Samples for Windows 7. In particular we started from the SetAuthor project inside of the AllOPCSamples.zip. We changed the SetAuthor program to suit our purpose here. The console application takes a file name as an argument. In Visual Studio, set the file name under the configuration properties of the project as shown below.

Visual Studio Console App Configuration

The code is shown below and as well as links for downloading it. Before getting to the code here is a sketch of the pseudo-logic of the code. We use the syntax of (x,y) -> z to mean x and y are used to return z. A bit simplistic, but helps clarify what is coming in and what is going out.
//pseudo-code
wmain
    COM Initilization of Thread
    CoCreateInstance of Opc Factory : () -> factory
    Load Package : (factory, fileName) -> package
    Find Document Part in Package : (package) -> documentPart
    Find Comments Part in Package : (package, documentPart) -> commentsPart
    Print Core Properties (package) -> output
    Print Comments (commentsPart) -> output

Load Package
(factory, fileName) -> package
    Create Stream on File : (factory, fileName, options) -> sourceFileStream
    Read Package from Stream : (factory, sourceFileStram, options) -> package

Find Document In Package
(package) -> documentPart
    relationshipType = http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument
    contentType = application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml
    Find Part by Relationship Type : (package, NULL, relationshipType, contentType) -> documentPart

Find Core Properties Part
(package) -> documentPart
    relationshipType = http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties
    contentType = application/vnd.openxmlformats-package.core-properties+xml
    Find Part by Relationship Type : (package, NULL, relationshipType, contentType) -> documentPart

Find Comments in Package
(package, documentPart) -> commentsPart
    relationshipType = http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments
    contentType = application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml
    Find Part by Relationship Type* : (package, documentPart, relationshipType, contentType) -> commentsPart

Find Part By Relationship Type
(package, parentPart, relationshipType, contentType) -> part
    Get Part Set : (package) -> partSet
    Get Relationship Set
    if (parentPart == NULL) then (package) -> packageRels
    else (parentPart) -> packageRels
    Get Enumerator for Type : (packageRels, relationshipType) -> packeRelsEnum
    Get Current : (packageRelsEnum) -> currentRel
    Resolve Target Uri to Part : (currentRel) -> partUri
    Part Exists : (partSet, partUri) -> partExists
    if (partExists) {
        Get Current Part : (partSet, partUri) -> currentPart
        Get Current Part Content Type : (currentPart) -> currentContentType
        if (currentContentType equals contentType)
        { // found the part }
    }

Resolve Target URI to Part
(relationship) -> resolvedUri

Print Comments
(commentsPart) -> output
    Get DOM from Part : (commentsParts, namespace) -> commentsDom
    Select Nodes : (commentsDom) -> commentsNodeList
    for each {
        Get Attributes of Comment Node
          Get Text of Comment Node
    }

Get Text of Comment Node
(node) -> output

Get Attributes of Comment Node
(node) -> output

Print Core Properties
(package) -> output
    Find Core Properties : (package) -> corePropertiesPart
    Get DOM from Part : (corePropertiesPart, namespace) -> corePropertiesDom
    Select Single Node : (corePropertiesDom, nodeName) -> nodeFound
    // work with nodeFound

Get DOM from Part
(part, namespace) -> XmlDocument


The header file for the console application can be downloaded here and is shown below.
#include "msopc.h"
#include "msxml6.h"
#include "stdafx.h"

HRESULT LoadPackage(IOpcFactory *factory, LPCWSTR packageName, IOpcPackage **outPackage);
HRESULT FindDocumentInPackage(IOpcPackage *package, IOpcPart  **documentPart);
HRESULT FindCommentsInPackage(IOpcPackage *package, IOpcPart  *parentPart, IOpcPart  **documentPart);
HRESULT FindPartByRelationshipType(IOpcPackage *package, IOpcPart *parentPart, LPCWSTR relationshipType, LPCWSTR contentType, IOpcPart **part);
HRESULT ResolveTargetUriToPart(IOpcRelationship *relativeUri, IOpcPartUri **resolvedUri);
HRESULT PrintCoreProperties(IOpcPackage *package);
HRESULT PrintComments(IOpcPart *part);
HRESULT GetAttributesOfCommentNode(IXMLDOMNode *node);
HRESULT GetTextofCommentNode(IXMLDOMNode *node);
HRESULT FindCorePropertiesPart(IOpcPackage *package, IOpcPart **part);
HRESULT DOMFromPart(IOpcPart *part, LPCWSTR selectionNamespaces, IXMLDOMDocument2 **document);

static const WCHAR g_officeDocumentRelationshipType[] =
    L"http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
static const WCHAR g_wordProcessingContentType[] =
    L"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml";
static const WCHAR g_corePropertiesRelationshipType[] =
    L"http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties";
static const WCHAR g_corePropertiesContentType[] =
    L"application/vnd.openxmlformats-package.core-properties+xml";
static const WCHAR g_commentsRelationshipType[] =
 L"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments";
static const WCHAR g_commentsContentType[] =
 L"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml";
static const WCHAR g_corePropertiesSelectionNamespaces[] =
    L"xmlns:cp='http://schemas.openxmlformats.org/package/2006/metadata/core-properties' "
    L"xmlns:dc='http://purl.org/dc/elements/1.1/' "
    L"xmlns:dcterms='http://purl.org/dc/terms/' "
    L"xmlns:dcmitype='http://purl.org/dc/dcmitype/' "
    L"xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'";
static const WCHAR g_commentsSelectionNamespaces[] =
 L"xmlns:wpc='http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas' "
    L"xmlns:mc='http://schemas.openxmlformats.org/markup-compatibility/2006' "
    L"xmlns:o='urn:schemas-microsoft-com:office:office' "
    L"xmlns:r='http://schemas.openxmlformats.org/officeDocument/2006/relationships' "
    L"xmlns:m='http://schemas.openxmlformats.org/officeDocument/2006/math' "
 L"xmlns:v='urn:schemas-microsoft-com:vml' "
 L"xmlns:wp14='http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing' "
 L"xmlns:wp='http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing' "
 L"xmlns:w10='urn:schemas-microsoft-com:office:word' "
    L"xmlns:w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' "
 L"xmlns:w14='http://schemas.microsoft.com/office/word/2010/wordml' "
 L"xmlns:wpg='http://schemas.microsoft.com/office/word/2010/wordprocessingGroup' "
 L"xmlns:wpi='http://schemas.microsoft.com/office/word/2010/wordprocessingInk' "
 L"xmlns:wne='http://schemas.microsoft.com/office/word/2006/wordml' "
 L"xmlns:wps='http://schemas.microsoft.com/office/word/2010/wordprocessingShape' ";


The main code file for the console application can be download here and is shown below.
// ExtractComments.cpp : Defines the entry point for the console application.

#include "ExtractComments.h"
#include "stdio.h"
#include "windows.h"
#include "shlobj.h"
#include 
#include "util.h"
using namespace std;

int wmain(int argc, wchar_t* argv[])
{
 if (argc != 2)
 {
  wprintf(L"Usage: ExtractComments.exe \n");
  exit(0);
 }
 wprintf(L"Starting.\n");
 LPCWSTR pFileName = argv[1];
 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

 if (SUCCEEDED(hr))
 {
  IOpcPackage * package = NULL;
  IOpcPart * documentPart = NULL;
  IOpcFactory * factory = NULL;
  hr = CoCreateInstance(
      __uuidof(OpcFactory),
      NULL,
      CLSCTX_INPROC_SERVER,
      __uuidof(IOpcFactory),
      (LPVOID*)&factory
      );
  if (SUCCEEDED(hr))
  {
   wprintf(L"Created factory.\n");
   hr = ::LoadPackage(factory, pFileName, &package);
   // See command arguments in project properties for specification of file to read.
  }
  if (SUCCEEDED(hr))
  {
   wprintf(L"Loaded package.\n");
   hr = ::FindDocumentInPackage(package, &documentPart);

  }
  IOpcPart *commentsPart;
  if (SUCCEEDED(hr))
  {
   wprintf(L"Found document in package.\n");
   hr = ::FindCommentsInPackage(package, documentPart, &commentsPart);
  }
  if (SUCCEEDED(hr))
  {
   wprintf(L"Found comments in package.\n");
   hr = ::PrintCoreProperties(package);
  }
  if (SUCCEEDED(hr))
  {
   wprintf(L"Found core properties in package.\n");
   hr = ::PrintComments(commentsPart);
  }
  if (SUCCEEDED(hr))
  {
   wprintf(L"Found comments in package.\n");
  }

  // Release resources
  if (package)
  {
   package->Release();
   package = NULL;
  }

  if (documentPart)
  {
   documentPart->Release();
   documentPart = NULL;
  }

  if (factory)
  {
   factory->Release();
   factory = NULL;
  }
  CoUninitialize();
 }
 return 0;
}

HRESULT LoadPackage(
 IOpcFactory *factory,
 LPCWSTR packageName,
 IOpcPackage **outPackage)
{
 IStream * sourceFileStream = NULL;
 HRESULT hr = factory->CreateStreamOnFile(
     packageName,
     OPC_STREAM_IO_READ,
     NULL,
     0,
     &sourceFileStream);
 if (SUCCEEDED(hr))
 {
  hr = factory->ReadPackageFromStream(
     sourceFileStream,
     OPC_CACHE_ON_ACCESS,
     outPackage);
 }
 if (sourceFileStream)
 {
  sourceFileStream ->Release();
  sourceFileStream = NULL;
 }
 return hr;
}
HRESULT FindDocumentInPackage(
 IOpcPackage *package,
 IOpcPart   **documentPart)
{
  return ::FindPartByRelationshipType(
  package,
  NULL,
  g_officeDocumentRelationshipType,
  g_wordProcessingContentType,
  documentPart);

}
HRESULT FindCommentsInPackage(
 IOpcPackage *package,
 IOpcPart   *documentPart,
 IOpcPart   **commentsPart)
{
  return ::FindPartByRelationshipType(
  package,
  documentPart,
  g_commentsRelationshipType,
  g_commentsContentType,
  commentsPart);

}
HRESULT FindCorePropertiesPart(
  IOpcPackage * package,
  IOpcPart **part)
{
 return ::FindPartByRelationshipType(
    package,
    NULL,
    g_corePropertiesRelationshipType,
          g_corePropertiesContentType,
    part);
}
HRESULT FindPartByRelationshipType(
 IOpcPackage *package,
 IOpcPart *parentPart,
 LPCWSTR relationshipType,
 LPCWSTR contentType,
 IOpcPart **part)
{
 *part = NULL;
 IOpcRelationshipSet * packageRels = NULL;
 IOpcRelationshipEnumerator * packageRelsEnum = NULL;
 IOpcPartSet * partSet = NULL;
 BOOL hasNext = false;

 HRESULT hr = package->GetPartSet(&partSet);

 if (SUCCEEDED(hr))
 {
  if (parentPart == NULL)
  {
   hr = package->GetRelationshipSet(&packageRels);
  }
  else
  {
   hr = parentPart->GetRelationshipSet(&packageRels);
  }
 }
 if (SUCCEEDED(hr))
 {
  hr = packageRels->GetEnumeratorForType(
    relationshipType,
    &packageRelsEnum);
 }
 if (SUCCEEDED(hr))
 {
  hr = packageRelsEnum->MoveNext(&hasNext);
 }
 while (SUCCEEDED(hr) && hasNext && *part == NULL)
 {
  IOpcPartUri * partUri = NULL;
  IOpcRelationship * currentRel = NULL;
  BOOL partExists = FALSE;

  hr = packageRelsEnum->GetCurrent(¤tRel);
  if (SUCCEEDED(hr))
  {
   hr = ::ResolveTargetUriToPart(currentRel, &partUri);
  }
  if (SUCCEEDED(hr))
  {
   hr = partSet->PartExists(partUri, &partExists);
  }
  if (SUCCEEDED(hr) && partExists)
  {
   LPWSTR currentContentType = NULL;
   IOpcPart * currentPart = NULL;
   hr = partSet->GetPart(partUri, ¤tPart);
   IOpcPartUri * name = NULL;
   currentPart->GetName(&name);
   BSTR displayUri = NULL;
   name->GetDisplayUri(&displayUri);
   wprintf(L"currentPart: %s\n", displayUri);
   if (SUCCEEDED(hr) && contentType != NULL)
   {
    hr = currentPart->GetContentType(¤tContentType);
    wprintf(L"contentType: %s\n", currentContentType);
    if (SUCCEEDED(hr) && 0 == wcscmp(contentType, currentContentType))
    {
     *part = currentPart;  // found what we are looking for
     currentPart = NULL;
    }
   }
   if (SUCCEEDED(hr) && contentType == NULL)
   {
    *part = currentPart;
    currentPart = NULL;
   }
   CoTaskMemFree(static_cast(currentContentType));
   if (currentPart)
   {
    currentPart->Release();
    currentPart = NULL;
   }
  }
  if (SUCCEEDED(hr))
  {
   hr = packageRelsEnum->MoveNext(&hasNext);
  }
  if (partUri)
        {
            partUri->Release();
            partUri = NULL;
        }

        if (currentRel)
        {
            currentRel->Release();
            currentRel = NULL;
        }
 }
     if (SUCCEEDED(hr) && *part == NULL)
    {
        // Loop complete without errors and no part found.
        hr = E_FAIL;
    }

    // Release resources
    if (packageRels)
    {
        packageRels->Release();
        packageRels = NULL;
    }

    if (packageRelsEnum)
    {
        packageRelsEnum->Release();
        packageRelsEnum = NULL;
    }

    if (partSet)
    {
        partSet->Release();
        partSet = NULL;
    }
 return hr;
}
HRESULT ResolveTargetUriToPart(
 IOpcRelationship *relationship,
 IOpcPartUri **resolvedUri
 )
{
 IOpcUri * sourceUri = NULL;
 IUri * targetUri = NULL;
 OPC_URI_TARGET_MODE targetMode;
 HRESULT hr = relationship->GetTargetMode(&targetMode);
 if (SUCCEEDED(hr) && targetMode != OPC_URI_TARGET_MODE_INTERNAL)
 {
  return E_FAIL;
 }
 if (SUCCEEDED(hr))
 {
  hr = relationship->GetTargetUri(&targetUri);
 }
 if (SUCCEEDED(hr))
 {
  hr = relationship->GetSourceUri(&sourceUri);
 }
 if (SUCCEEDED(hr))
 {
  hr = sourceUri->CombinePartUri(targetUri, resolvedUri);
 }
 if (sourceUri)
 {
  sourceUri->Release();
  sourceUri = NULL;
 }
 if (targetUri)
 {
  targetUri->Release();
  targetUri = NULL;
 }
 return hr;
}
HRESULT PrintComments(
 IOpcPart *commentsPart)
{
 IXMLDOMDocument2 * commentsDom = NULL;

 HRESULT hr = ::DOMFromPart(
    commentsPart,
    g_commentsSelectionNamespaces,
    &commentsDom);
 if (SUCCEEDED(hr))
 {
  IXMLDOMNodeList * commentsNodeList = NULL;
  BSTR text = NULL;
  hr = commentsDom->selectNodes(
   L"//w:comment",
   &commentsNodeList);
  if (SUCCEEDED(hr) && commentsNodeList != NULL)
  {
   // Iterate through comment nodes
   // http://msdn.microsoft.com/en-us/library/ms757073(VS.85).aspx
   long nodeListLength = NULL;
   hr = commentsNodeList->get_length(&nodeListLength);

   for (int i = 0; i < item =" NULL;" hr =" commentsNodeList-">get_item(i, &item);
    SUCCEEDED(hr) ? 0 : throw hr;

    ::GetAttributesOfCommentNode(item);
    ::GetTextofCommentNode(item);
   }

  }
  // Release resources
        if (commentsNodeList)
        {
            commentsNodeList->Release();
            commentsNodeList = NULL;
        }
 }
 // Release resources
    if (commentsPart)
    {
        commentsPart->Release();
        commentsPart = NULL;
    }

    if (commentsDom)
    {
        commentsDom->Release();
        commentsDom  = NULL;
    }

 return hr;
}
HRESULT GetTextofCommentNode(
 IXMLDOMNode *node
 )
{
 BSTR bstrQueryString1 = ::SysAllocString(L"w:p");
 BSTR bstrQueryString2 = ::SysAllocString(L"w:r");
 BSTR commentText = NULL;
 IXMLDOMNodeList *resultList1 = NULL;
 IXMLDOMNodeList *resultList2 = NULL;
 IXMLDOMNode *pNode, *rNode = NULL;

 long resultLength1, resultLength2;

 HRESULT hr = node->selectNodes(bstrQueryString1, &resultList1);
 SUCCEEDED(hr) ? 0 : throw hr;
 hr = resultList1->get_length(&resultLength1);
 if (SUCCEEDED(hr))
 {
  resultList1->reset();
  for (int i = 0; i <>get_item(i, &pNode);
   if (pNode)
   {
    //wprintf(L"--Found a w:p node.\n");
    wprintf(L"\n");
    pNode->selectNodes(bstrQueryString2, &resultList2);
    SUCCEEDED(hr) ? 0 : throw hr;
    hr = resultList2->get_length(&resultLength2);
    if (SUCCEEDED(hr))
    {
     resultList2->reset();
     for (int j = 0; j <>get_item(j, &rNode);
      if (rNode)
      {
       rNode->get_text(&commentText);
       //wprintf(L"----Found a w:r node. \n");
       wprintf(commentText);
      }
     }
    }

   }
  }
 }

 ::SysFreeString(bstrQueryString1);  ::SysFreeString(bstrQueryString2);
 bstrQueryString1 = NULL;            bstrQueryString2 = NULL;
 resultList1->Release();    resultList2->Release();
 resultList1 = NULL;     resultList2 = NULL;
 pNode->Release();     rNode->Release();
 pNode = NULL;      rNode = NULL;
 return hr;
}
HRESULT GetAttributesOfCommentNode(
 IXMLDOMNode *node
 )
{
 VARIANT commentAuthorStr, commentDateStr;
 BSTR bstrAttributeAuthor = ::SysAllocString(L"w:author");
 BSTR bstrAttributeDate = ::SysAllocString(L"w:date");

    // Get author and date attribute of the item.
 //http://msdn.microsoft.com/en-us/library/ms767592(VS.85).aspx
 IXMLDOMNamedNodeMap *attribs = NULL;
    IXMLDOMNode *AttrNode = NULL;
 HRESULT hr = node->get_attributes(&attribs);
 if (SUCCEEDED(hr) && attribs)
 {
  attribs->getNamedItem(bstrAttributeAuthor, &AttrNode);
  if (SUCCEEDED(hr) && AttrNode)
  {
   AttrNode->get_nodeValue(&commentAuthorStr);
  }
  AttrNode->Release();
  AttrNode = NULL;
  attribs->getNamedItem(bstrAttributeDate, &AttrNode);
  if (SUCCEEDED(hr) && AttrNode)
  {
   AttrNode->get_nodeValue(&commentDateStr);
  }
  AttrNode->Release();
  AttrNode = NULL;
 }
 attribs->Release();
 attribs = NULL;

 wprintf(L"\n-------------------------------------------------");
 wprintf(L"\nComment::\nAuthor: %s, Date: %s\n", commentAuthorStr.bstrVal, commentDateStr.bstrVal);

 ::SysFreeString(bstrAttributeAuthor); ::SysFreeString(bstrAttributeDate);
 bstrAttributeAuthor = NULL;    bstrAttributeDate = NULL;

 return hr;
}
HRESULT PrintCoreProperties(
 IOpcPackage *package)
{
 IOpcPart * corePropertiesPart = NULL;
 IXMLDOMDocument2 * corePropertiesDom = NULL;

 HRESULT hr = ::FindCorePropertiesPart(
     package,
     &corePropertiesPart);
 if (SUCCEEDED(hr))
 {
  hr = ::DOMFromPart(
    corePropertiesPart,
    g_corePropertiesSelectionNamespaces,
    &corePropertiesDom);
 }
 if (SUCCEEDED(hr))
 {
  IXMLDOMNode * creatorNode = NULL;
  BSTR text = NULL;
  hr = corePropertiesDom->selectSingleNode(
    L"//dc:creator",
    &creatorNode);
  if (SUCCEEDED(hr) && creatorNode != NULL)
  {
   hr = creatorNode->get_text(&text);
  }
  if (SUCCEEDED(hr))
  {
   wprintf(L"Author: %s\n", (text != NULL) ? text : L"[missing author info]");
  }
  // Release resources
        if (creatorNode)
        {
            creatorNode->Release();
            creatorNode = NULL;
        }

        SysFreeString(text);

  // put other code here to read other properties
 }
 // Release resources
    if (corePropertiesPart)
    {
        corePropertiesPart->Release();
        corePropertiesPart = NULL;
    }

    if (corePropertiesDom)
    {
        corePropertiesDom->Release();
        corePropertiesDom  = NULL;
    }
 return hr;
}

HRESULT DOMFromPart(
 IOpcPart * part,
 LPCWSTR selectionNamespaces,
 IXMLDOMDocument2 **document)
{
 IXMLDOMDocument2 * partContentXmlDocument = NULL;
 IStream * partContentStream = NULL;

 HRESULT hr = CoCreateInstance(
     __uuidof(DOMDocument60),
     NULL,
     CLSCTX_INPROC_SERVER,
     __uuidof(IXMLDOMDocument2),
     (LPVOID*)&partContentXmlDocument);
 if (SUCCEEDED(hr) && selectionNamespaces)
 {
  AutoVariant v;
  hr = v.SetBSTRValue(L"XPath");
  if (SUCCEEDED(hr))
  {
   hr = partContentXmlDocument->setProperty(L"SelectionLanguage", v);
  }
  if (SUCCEEDED(hr))
  {
   AutoVariant v;
   hr = v.SetBSTRValue(selectionNamespaces);
   if (SUCCEEDED(hr))
   {
    hr = partContentXmlDocument->setProperty(L"SelectionNamespaces", v);
   }
  }
 }
 if (SUCCEEDED(hr))
 {
  hr = part->GetContentStream(&partContentStream);
 }
 if (SUCCEEDED(hr))
 {
  VARIANT_BOOL isSuccessful = VARIANT_FALSE;
  AutoVariant vStream;
  vStream.SetObjectValue(partContentStream);
  hr = partContentXmlDocument->load(vStream, &isSuccessful);
  if (SUCCEEDED(hr) && isSuccessful == VARIANT_FALSE)
  {
   hr = E_FAIL;
  }
 }
 if (SUCCEEDED(hr))
 {
  *document = partContentXmlDocument;
  partContentXmlDocument = NULL;
 }
 // Release resources
    if (partContentXmlDocument)
    {
        partContentXmlDocument->Release();
        partContentXmlDocument = NULL;
    }

    if (partContentStream)
    {
        partContentStream->Release();
        partContentStream = NULL;
    }
 return hr;
}