Click here to Skip to main content
15,885,278 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
INTRODUCTION AND RELEVANT INFORMATION:

I am trying to learn to print with XPS Document API[^].

For a simple start, I have decided to draw a rectangle, and some text below it.

After going through the official examples[^] I was able to achieve my goal.

PROBLEM:

Basically, I have concatenated 2 code examples provided from the above link. Now I wanted to polish the code, mainly to use single brush to draw both rectangle and the text.

After rewriting the code, I get the following error:
C++
First-chance exception at 0x7555D3CF in XPS printing.exe: Microsoft C++ exception: SplException::THResultException at memory location 0x002CEF9C.

If there is a handler for this exception, the program may be safely continued.


SSCCEE:

Below is the function I rewrote. I have marked the crashing point with the appropriate comments.
C++
void XPS_TEST()
{
    IXpsOMObjectFactory *xpsFactory;

    HRESULT hr = S_OK;

    // Init COM for this thread if it hasn't 
    //  been initialized, yet.
    hr = CoInitializeEx(0, COINIT_MULTITHREADED);

    hr = CoCreateInstance(
        __uuidof(XpsOMObjectFactory),
        NULL,
        CLSCTX_INPROC_SERVER,
        __uuidof(IXpsOMObjectFactory),
        reinterpret_cast<LPVOID*>(&xpsFactory));

    if (SUCCEEDED(hr))
    {
        // Declare the variables used in this section.
        IOpcPartUri                   *opcPartUri = NULL;
        IXpsOMPackage                 *xpsPackage = NULL;
        IXpsOMDocumentSequence        *xpsFDS = NULL;
        IXpsOMDocumentCollection      *fixedDocuments = NULL;
        IXpsOMDocument                *xpsFD = NULL;
        IXpsOMPage                    *xpsPage = NULL;
        IXpsOMPageReferenceCollection *pageRefs = NULL;
        IXpsOMPageReference           *xpsPageRef = NULL;

        // test size of the document
        XPS_SIZE pageSize = { 200, 200 };

        // Create the package.
        hr = xpsFactory->CreatePackage(&xpsPackage);

        // Create the URI for the fixed document sequence part and then  
        //  create the fixed document sequence
        hr = xpsFactory->CreatePartUri(
            L"/FixedDocumentSequence.fdseq", &opcPartUri);
        hr = xpsFactory->CreateDocumentSequence(opcPartUri, &xpsFDS);
        // Release this URI to reuse the interface pointer.
        if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }

        // Create the URI for the document part and then create the document.
        hr = xpsFactory->CreatePartUri(
            L"/Documents/1/FixedDocument.fdoc", &opcPartUri);
        hr = xpsFactory->CreateDocument(opcPartUri, &xpsFD);
        // Release this URI to reuse the interface pointer.
        if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }

        // Create a blank page.
        hr = xpsFactory->CreatePartUri(
            L"/Documents/1/Pages/1.fpage", &opcPartUri);
        hr = xpsFactory->CreatePage(
            &pageSize,                  // Page size
            L"en-US",                   // Page language
            opcPartUri,                 // Page part name
            &xpsPage);
        // Release this URI to reuse the interface pointer.
        if (NULL != opcPartUri) { opcPartUri->Release(); opcPartUri = NULL; }

        // Create a page reference for the page.
        hr = xpsFactory->CreatePageReference(&pageSize, &xpsPageRef);

        // Add the fixed document sequence to the package.
        hr = xpsPackage->SetDocumentSequence(xpsFDS);

        // Get the document collection of the fixed document sequence
        //  and then add the document to the collection.
        hr = xpsFDS->GetDocuments(&fixedDocuments);
        hr = fixedDocuments->Append(xpsFD);

        // Get the page reference collection from the document
        //  and add the page reference and blank page.
        hr = xpsFD->GetPageReferences(&pageRefs);
        hr = pageRefs->Append(xpsPageRef);
        hr = xpsPageRef->SetPage(xpsPage);

        //======================== draw rectangle ====================//

        XPS_COLOR             xpsColor;
        IXpsOMSolidColorBrush *xpsFillBrush = NULL;
        // the brush I want to reuse !!
        IXpsOMSolidColorBrush *xpsStrokeBrush = NULL;

        // Set the fill brush color to RED.
        xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
        xpsColor.value.sRGB.alpha = 0xFF;
        xpsColor.value.sRGB.red = 0xFF;
        xpsColor.value.sRGB.green = 0x00;
        xpsColor.value.sRGB.blue = 0x00;

        // Use the object factory to create the brush.
        hr = xpsFactory->CreateSolidColorBrush(
            &xpsColor,
            NULL,          // color profile resource
            &xpsFillBrush);
        // The color profile resource parameter is NULL because
        //  this color type does not use a color profile resource.

        // Set the stroke brush color to BLACK.
        xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
        xpsColor.value.sRGB.alpha = 0xFF;
        xpsColor.value.sRGB.red = 0x00;
        xpsColor.value.sRGB.green = 0x00;
        xpsColor.value.sRGB.blue = 0x00;

        // Use the object factory to create the brush.
        hr = xpsFactory->CreateSolidColorBrush(
            &xpsColor,
            NULL, // This color type does not use a color profile resource.
            &xpsStrokeBrush);

        // test rectangle
        XPS_RECT                            rect = { 0, 0, 200, 20 };
        IXpsOMGeometryFigure                *rectFigure;
        IXpsOMGeometry                      *imageRectGeometry;
        IXpsOMGeometryFigureCollection      *geomFigureCollection;

        // Define the start point and create an empty figure.
        XPS_POINT                           origin = { rect.x, rect.y };
        hr = xpsFactory->CreateGeometryFigure(&origin, &rectFigure);
        // Define the segments of the geometry figure.
        //  First, define the type of each segment.
        XPS_SEGMENT_TYPE segmentTypes[3] = 
        {
            XPS_SEGMENT_TYPE_LINE,  // each segment is a straight line
            XPS_SEGMENT_TYPE_LINE,
            XPS_SEGMENT_TYPE_LINE
        };

        // Define the x and y coordinates of each corner of the figure
        //  the start point has already been defined so only the 
        //  remaining three corners need to be defined.
        FLOAT segmentData[6] = 
        {
            rect.x, (rect.y + rect.height),
            (rect.x + rect.width), (rect.y + rect.height),
            (rect.x + rect.width), rect.y
        };

        // Describe if the segments are stroked (that is if the segment lines
        //  should be drawn as a line).
        BOOL segmentStrokes[3] = 
        {
            TRUE, TRUE, TRUE // Yes, draw each of the segment lines.
        };

        // Add the segment data to the figure.
        hr = rectFigure->SetSegments(
            3,
            6,
            segmentTypes,
            segmentData,
            segmentStrokes);

        // Set the closed and filled properties of the figure.
        hr = rectFigure->SetIsClosed(TRUE);
        hr = rectFigure->SetIsFilled(TRUE);

        // Create the geometry object.
        hr = xpsFactory->CreateGeometry(&imageRectGeometry);

        // Get a pointer to the figure collection interface of the geometry...
        hr = imageRectGeometry->GetFigures(&geomFigureCollection);

        // ...and then add the figure created above to this geometry.
        hr = geomFigureCollection->Append(rectFigure);
        // If not needed for anything else, release the rectangle figure.
        rectFigure->Release();

        // when done adding figures, release the figure collection. 
        geomFigureCollection->Release();

        IXpsOMPath                *rectPath = NULL;
        IXpsOMVisualCollection    *pageVisuals = NULL;

        // Create the new path object.
        hr = xpsFactory->CreatePath(&rectPath);

        // Add the geometry to the path.
        //  imageRectGeometry is initialized outside of this example.
        hr = rectPath->SetGeometryLocal(imageRectGeometry);

        // Set the short description of the path to provide
        //  a textual description of the object for accessibility.
        hr = rectPath->SetAccessibilityShortDescription(L"Red Rectangle");

        // Set the fill and stroke brushes to use the brushes 
        //  created in the first section.
        hr = rectPath->SetFillBrushLocal(xpsFillBrush);
        hr = rectPath->SetStrokeBrushLocal(xpsStrokeBrush);

        // Get the visual collection of this page and add this path to it.
        hr = xpsPage->GetVisuals(&pageVisuals);
        hr = pageVisuals->Append(rectPath);

        // If not needed for anything else, release the rectangle path.
        rectPath->Release();

        // When finished with the brushes, release the interface pointers.
        if (NULL != xpsFillBrush) xpsFillBrush->Release();
        //******************** I have commented out below code, ****************//
        //******************** because I plan to use the brush to draw text ****//
        //if (NULL != xpsStrokeBrush) xpsStrokeBrush->Release();
        // When done with the geometry interface, release it.
        imageRectGeometry->Release();

        //========================= draw text =====================//
        GUID                          fontNameGuid;
        WCHAR                         guidString[128] = { 0 };
        WCHAR                         uriString[256] = { 0 };

        IStream                       *fontStream = NULL;
        IOpcPartUri                   *fontUri = NULL;
        IXpsOMFontResource            *fontResource = NULL;

        // Create font stream. 
        hr = xpsFactory->CreateReadOnlyStreamOnFile(
            // I have hardcoded Arial here, just for testing
            L"C:\\Windows\\Fonts\\Arial.ttf",
            &fontStream); 

        // Create new obfuscated part name for this resource using a GUID.
        hr = CoCreateGuid(&fontNameGuid);
        hr = StringFromGUID2(
            fontNameGuid,
            guidString,
            ARRAYSIZE(guidString));

        // Create a URI string for this font resource that will place 
        //  the font part in the /Resources/Fonts folder of the package.
        wcscpy_s(uriString, ARRAYSIZE(uriString), L"/Resources/Fonts/");

        // Create the part name using the GUID string as the name and 
        //  ".odttf" as the extension GUID string start and ends with 
        //  curly braces so they are removed.
        wcsncat_s(uriString, ARRAYSIZE(uriString),
            guidString + 1, wcslen(guidString) - 2);
        wcscat_s(uriString, ARRAYSIZE(uriString), L".odttf");

        // Create the font URI interface.
        hr = xpsFactory->CreatePartUri(
            uriString,
            &fontUri);
        // Create the font resource.
        hr = xpsFactory->CreateFontResource(
            fontStream,
            XPS_FONT_EMBEDDING_OBFUSCATED,
            fontUri,
            FALSE,     // isObfSourceStream
            &fontResource);
        if (NULL != fontUri) fontUri->Release();

        LPCWSTR unicodeString = L"Test string";

        // move test string below our rectangle
        origin.y += 30.0f;

        FLOAT                   fontEmSize = 7.56f;
        IXpsOMGlyphsEditor      *glyphsEditor = NULL;
        IXpsOMGlyphs            *xpsGlyphs = NULL;

        // Create a new Glyphs object and set its properties.
        hr = xpsFactory->CreateGlyphs(fontResource, &xpsGlyphs);
        hr = xpsGlyphs->SetOrigin(&origin);
        hr = xpsGlyphs->SetFontRenderingEmSize(fontEmSize);
        //*************** I GET A CRASH BELOW !!!! ***************//
        hr = xpsGlyphs->SetFillBrushLocal(xpsStrokeBrush); // <<--- 

        // Some properties are inter-dependent so they
        //    must be changed by using a GlyphsEditor.
        hr = xpsGlyphs->GetGlyphsEditor(&glyphsEditor);
        hr = glyphsEditor->SetUnicodeString(unicodeString);
        hr = glyphsEditor->ApplyEdits();

        // Add the new Glyphs object to the page
        hr = pageVisuals->Append(xpsGlyphs);

        // Release interface pointers.
        if (NULL != xpsGlyphs) xpsGlyphs->Release();
        if (NULL != glyphsEditor) glyphsEditor->Release();
        if (NULL != pageVisuals) pageVisuals->Release();
        //******************** Releasing the brush here *******//
        if (NULL != xpsStrokeBrush) xpsStrokeBrush->Release();

        //========================= write to file ====================//
        hr = xpsPackage->WriteToFile(
            L"C:\\Users\\Smiljkovic\\Desktop\\xpsTest.xps",
            NULL,                    // LPSECURITY_ATTRIBUTES
            FILE_ATTRIBUTE_NORMAL,
            FALSE);                  // Optimize Markup Size
        //========================== cleanup ==================//

        // Release interface pointer
        if (NULL != xpsPage) xpsPage->Release();
        if (NULL != pageRefs) pageRefs->Release();
        if (NULL != fixedDocuments) fixedDocuments->Release();
        if (NULL != xpsPageRef) xpsPageRef->Release();
        if (NULL != xpsFD) xpsFD->Release();
        if (NULL != xpsFDS) xpsFDS->Release();
        if (NULL != xpsPackage) xpsPackage->Release();

        xpsFactory->Release();
    }

    // Uninitialize COM when finished
    CoUninitialize();
}


QUESTION:

How can I use the same brush ( xpsStrokeBrush from the above example ) for drawing both the text and the rectangle outline?
Posted
Updated 29-Nov-15 5:01am
v2
Comments
George Jonsson 28-Nov-15 23:10pm    
Have you checked the status and members of xpsStrokeBrush after it is created and up to the crash in the debugger?
If you add xpsStrokeBrush to the watch window, and monitor what happens with the variable step by step, you will most likely find the problem.
Most likely it is a memory overwrite problem.
AlwaysLearningNewStuff 29-Nov-15 11:00am    
I have scattered breakpoints all over the code. Watching the xpsStrokeBrush variable, it never changed it's value ( after its creation in code ).

I have tried releasing the brush and recreating it again with the same values, before entering problematic section in my code. That worked, there was no crash anymore...

It seems that problem lies elsewhere... Thanks for trying to help...

1 solution

In the MDSN stands for the SetStrokeBrushLocal: "After you call SetStrokeBrushLocal, the stroke brush lookup key is released" , so you do the "evil over-releasing". (It also is the meaning of the error message)

Tip: do COM-stuff like object creation only once, especially CoInitialize and CoUninitialize.

Welcome in COM-Hell ;-)
 
Share this answer
 
Comments
AlwaysLearningNewStuff 7-Dec-15 23:01pm    
Let me get this straight: the only options I have are to Clone the brush or recreate it again?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900