How to open documents silently

Solution Partner Phenom Solution Partner Phenom
Solution Partner Phenom

Over in the Solid Edge Developer Forum, there has been an ongoing discussion titled Memory and handle leak in SolidEdge when opening and closing parts?. As always, it has been an interesting dicussion and there has been a lot of good knowledge sharing. Of particular interest though, on page 3 of the discussion, community member RDH shared the following tidbit of code with a very interesting trick.

 

case 1:
{
    // This command demonstrates a little know method of opening a Part document without
    // creating a window. It relies on setting the option input arg to an unsigned int. The
    // value of the int is eight.
    #define JDOCUMENTPROP_NOWINDOW 0x00000008
    DocumentsPtr pDocs = GetApplicationPtr()->Documents;
    _bstr_t FullName;
    
    if( NULL != pDocs )
    {
        PartDocumentPtr pPartDoc;
        VARIANT vOption;
        V_VT( &vOption ) = VT_UI4;
        V_UI4( &vOption ) = (unsigned long )JDOCUMENTPROP_NOWINDOW;
        pPartDoc = pDocs->Open( "c:\\temp\\block.par", vOption );
        
        if( NULL != pPartDoc )
        {
            FullName = pPartDoc->FullName;
            pPartDoc->Close();
        }
    }
    break;
}

In his example C++ code, he is able to "silently" open a Solid Edge document. By silent, I mean that he is opening the document without any windows being created in the GUI. If you study the code closely, you'll notice that he is passing a value of "8" to the 2nd parameter of the Documents.Open() method. If we examine the definition of that method,

 

[id(0x00000006), helpstring("Opens a specified document."), helpcontext(0x0000c39e)]
IDispatch* Open(
                [in] BSTR Filename, 
                [in, optional] VARIANT DocRelationAutoServer, 
                [in, optional] VARIANT AltPath, 
                [in, optional] VARIANT RecognizeFeaturesIfPartTemplate, 
                [in, optional] VARIANT RevisionRuleOption, 
                [in, optional] VARIANT StopFileOpenIfRevisionRuleNotApplicable);

we will notice that the 2nd parameter is named "DocRelationAutoServer". If you're scratching your head right now, I am too. To say that this falls into the "Not obvious at all" category would be an understatement. Regardless, the trick exists and does work. I've tested it will all Solid Edge document types with success. In my test, I converted the code to C# as shown below. Note that the C++ ULONG\LONG translates to C# uint\int. Best that I can tell, the API is mostly only checking the value. I've tested with C# ulong,long,uint & int and all worked.

 

using SolidEdgeCommunity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SENoWindowTest
{
    class Program
    {
        static void Main(string[] args)
        {
            //var filename = @"C:\Program Files\Solid Edge ST8\Training\Coffee Pot.asm";
            //var filename = @"C:\Program Files\Solid Edge ST8\Training\Test Drive_Part_QS1.dft";
            //var filename = @"C:\Program Files\Solid Edge ST8\Training\bar.par";
            var filename = @"C:\Program Files\Solid Edge ST8\Training\simulation\brace.psm";

            var application = SolidEdgeUtils.Connect();
            var documents = application.Documents;

            // uint and int both work.
            //uint JDOCUMENTPROP_NOWINDOW = 0x00000008;
            int JDOCUMENTPROP_NOWINDOW = 0x00000008;

            var document = (SolidEdgeFramework.SolidEdgeDocument)documents.Open(filename, JDOCUMENTPROP_NOWINDOW);
            document.Close(false);
        }
    }
}

Taking a step back and looking at this objectively, my opinion is that while I'm grateful that this option exists, it's usability rating is pretty low. If you didn't read the original post or see this blog post, it's likely you would have never known about this ability of the API. In my opinion, the API should be enhanced with something like Documents.OpenInBackground(). That is much more obvious and usable.

 

This post wouldn't be complete if I didn't mention that as a responsible community member, I immediately filed an Incident Report (IR) with GTAC. IR 7520370 was created and is titled "Add ability to open documents silently via API". My hopes are that Siemens will consider enhancing the API to formally support this option. If you agree, you can call GTAC referencing IR 7520370 and request that your name be added. My understanding is that the more people that call in and add their name, the greater the weight of the IR during the prioritization process.

Comments
Phenom

Great post Jason.

Thanks for covering a most sought after topic among programmers, both new and experienced.

 

Somewhere in one of the posts I saw this usage:

ObjDoc = GetObject(strFileName)

 

which too opens a document in invisible mode and

again an example of 'not obvious'.

What would you say about the usability rating for this ?

Can you shed some light on what happens under the hood in this case ?

 

~Tushar Suradkar

 

Solution Partner Phenom

Jason,

 

I already knew about this flag and am using it actively in our Mold Tooling application, but never took the time to request some "official" API support.

Thanks for creating the ER, and just for that, you already deserve my Kudos!

Solution Partner Phenom

@Tushar The GetObject Function is a Microsoft API. Everything you want to know about that function is on the linked page. This works with Solid Edge because of how Solid Edge is registered with COM in Windows when it's installed. While I agree it's "not obvious" to most that you can do this, I wouldn't blame Solid Edge. To your point though, we can certainly cover it better in the SDK documentation and I can make a new blog post about it.

 

Thanks for the feedback.

Siemens Valued Contributor Siemens Valued Contributor
Siemens Valued Contributor

Jason,

 

Did you test this with a draft document? Seems like I have seen a "ghost" frame appear briefly when opening with no UI.

Solution Partner Phenom

@RDH I did. There is a remarked out line of code in my example where I tested it. I also just tried it again to be sure and it seemed to work as expected.

Siemens Valued Contributor Siemens Valued Contributor
Siemens Valued Contributor

Tushar,

 

There isn't much of a difference as far as Solid Edge but there is a difference. However it may be easier to just use COM to open the document in the server directly as that's a cool one-liner. Also, there is yet another way to open a document with no UI activated that is probably analogous to the code in your post. The code I previously posted was from a very old add-in I have that I give out every so often to people asking how to open a document without a UI. The add-in also has this code that shows an alternative.

 

// This shows how to get a dispatch pointer to a document using IMoniker::BindToObject.

// The actual use of this method of opening a file should include code that

// makes sure the file is not opened in Edge.

IMonikerPtr pMon = NULL;

CreateFileMoniker(L"c:\\temp\\block.par", &pMon );

if( NULL != pMon )

{

LPBC lpBdCtx = NULL;

::CreateBindCtx(0, &lpBdCtx);

_bstr_t Name;

if( lpBdCtx )

{

IDispatchPtr pDocDisp;

pMon->BindToObject( lpBdCtx, NULL, IID_IDispatch, (LPVOID*)&pDocDisp );

if( NULL != pDocDisp )

{

PartDocumentPtr pPartDoc = pDocDisp;

if( NULL != pPartDoc )

{

// Close the file or else Edge won't be able to open it later.

pPartDoc->Close();

Name = pPartDoc->FullName;

}

}

lpBdCtx->Release();

}

}

 

Note the comment about checking if the file is open in Edge. Been a long time since I wrote this but I think the comment is just due to the fact I blindly close the document. If a document is already opened and you make one of these calls, we simply return the document interface to the already opened document. Hence if the document is already opened, I probably should not call Close (might not be user friendly). But buyer beware! If the file wasn't open in edge, and you fail to close it, the user cannot "open" the file manually! Well actually, the user can't force a way to display the file. It is technically opened in Edge but it has no windows and no real connection to the documents collection. So it won't show up in the "switch windows" code and Edge doesn't really have any UI that lets a user force a window on the document to activate. That is both your code and the code above should be used with caution.

 

Now this brings me to the major difference in using the open trick I posted and Tushar's method (or the one I mentioned in this post). When opening the document via the documents collection, the documents collection "knows" about the document. This is a subtle point but it has one implication. If we open and don't close, the user can open (via MRU, file open etc., etc.) the file. The UI will activate - the environment loads, docking panes show up ... everything but a window for the document. But the user can go to the view tab and run the new window command and a window will be create. There is one other obvious difference between using the documents collection and not - Edge doesn't have to be running. That is, COM will start the server (Edge) and it too will run in the background unless the user sets the visible flag to true.

 

Basically these various issues or odd behaviors are one reason I have never "offically" created an API to open a document sans window(s). One has to be very careful about how it is used. So don't let some COM error (exception in .NET) cause you to fail to close the document if you should! And don't close it if you shouldn't.

 

Solution Partner Phenom

Awesome tips RD. Thanks!

Enthusiast

Thanx for that, but I found it years before. Try some other values to see amazing effects ;-)

 

This does not really open documents "silent" as I would await it, because there will be a question dialog of SE to ask if an incontext part should be opened in the context of its assembly... (for example).

 

And: Solid Edge will crash if you do some image export of the unshown window and view, whichs object are still available.

 

So anyone? Can I really open a SE document "silent" - which means: without any user interaction in a window?

 

regards,

Cool

 

Siemens Valued Contributor Siemens Valued Contributor
Siemens Valued Contributor

If you turn off display alerts, that dialog should not appear. Try Application:Smiley Very HappyisplayAlerts(VARIANT_FALSE). If that doesn't supress the dialog, then that is a problem that should be reported to GTAC.

 

And if you try to do view based manipluations, you will have to show a window. Any crash should be reported to GTAC and we will check to see if the document was opened with no UI and return a COM error code. That's assuming the "crash" is really someting like a system access violation, invalid instruction etc., etc. and isn't related to the failure of the client to catch COM errors in .NET, or via c++ compiler support, which are turned into .NET or c++ exceptions. That is, an exception is how errors are communicated and a failure of a client to catch said exceptions is not a problem in Solid Edge but with the client. Basically, don't bypass the viewing/OpenGL pathway and expect that pathway to operate correctly. Edge shouldn't crash but you should not be surprised when something related to it fails. Another example would be opening a file read-only and then calling Save. I would expect a COM error (E_FAIL) would be returned from the Save API and .NET will throw an exception, which a .NET client can catch, examine and handle, without causing a crash. But if you don't catch the exception, it is unhandled and the system will bring the process down.

 

There are some cases where Edge can catch a COM exception and handle it (by ignoring it). For example, when firing an event to an external client. If the client makes an API call that returns a COM error and fails to catch it, since the event firing function is on the call stack, it can catch the exception at the call point. Generally though it isn't good practice (my opinion) to be in the business of ignoring errors at points where there is no context. How would the code that failed to catch the exception know it sometimes abrubtly loses its trail of execution? What code did it expet to execute after the point of exception fails to execute and what are the implications?

 

Perhaps there is a consensus that Edge should catch any COM exception and ignore it?

Siemens Valued Contributor Siemens Valued Contributor
Siemens Valued Contributor

That emoticon was not on purpose. That was supposed to be "DisplayAlerts".

 

By the way, if you post a bit of code you use after opening a document no UI that causes an issue, I can dump it into a test driver and see what happens.

Siemens Valued Contributor Siemens Valued Contributor
Siemens Valued Contributor

I just tried two things. After opening a part no UI, I got a Window and called PrintOut. I got a COM error/exception (E_ABORT). Since I did the call inside a try/catch, the process lived on.

 

I then obtained the view from the window and called SaveAsImage. No COM error and the image was saved out (I saved BMP).

 

I also added code to set the Window Visible property to VARIANT_TRUE and then called Window Activate. The part environment opened and the document window appeared.

 

I didn't try with assembly or a draft file. So, results may vary.

 

DocumentsPtr pDocs = GetApplicationPtr()->Documents;

VARIANT_BOOL bCurDisplayAlerts = GetApplicationPtr()->DisplayAlerts;

GetApplicationPtr()->DisplayAlerts = VARIANT_FALSE;

try

{

_bstr_t FullName;

if( NULL != pDocs )

{

PartDocumentPtr pPartDoc;

VARIANT vOption;

V_VT( &vOption ) = VT_UI4;

V_UI4( &vOption ) = (unsigned long )JDOCUMENTPROP_NOWINDOW;

pPartDoc = pDocs->Open( "c:\\temp\\block.par", vOption );

if( NULL != pPartDoc )

{

FullName = pPartDoc->FullName;

WindowsPtr pWindows = pPartDoc->Windows;

int nCount = pWindows->Count;

if( nCount )

{

for( long index = 0; index < nCount; ++index )

{

WindowPtr pWindow = pWindows->Item( index+1 );

if( pWindow )

{

try

{

pWindow->PrintOut();

}

catch( _com_error &e )

{

e;// E_ABORT error code if no view is active

}

ViewPtr pView = pWindow->View;

if( pView )

{

try

{

pView->SaveAsImage( L"c:\\temp\\partsave.bmp", (long)100, (long)100, (long)0, (long)0 /* 0 means width/height in pixels*/, (long)24, seImageQualityLow, VARIANT_FALSE );

}

catch( _com_error &e )

{

e;

}

}

VARIANT_BOOL bIsVis = pWindow->Visible;

int nState = pWindow->WindowState;

pWindow->Visible = VARIANT_TRUE;

pWindow->Activate();

}

}

}

pPartDoc->Close();

}

}

}

catch( _com_error &e )

{

e;

}

GetApplicationPtr()->DisplayAlerts = bCurDisplayAlerts;

 

Enthusiast

Great. Thank you, Works!

Top Kudoed Posts