By Adam Nagy
Many different types of objects support the GetReferenceKey function that enables you to store information about a given object that will enable you to find them again using the ReferenceKeyManager of the Document. If you wanted to support all of them using early-binding, i.e. you explicitly declare all the object types you are supporting, that would be a lot of work. It's probably better to use late-binding in such a case - i.e. we do not care what type of object we are dealing with, we'll try to call GetReferenceKey on it. If it supports it then it will execute fine, otherwise the function will not be found on it.
Just as it was discussed in this post, in .NET it's quite easy now to do late binding, but in C++ it's not, so we'll use the AutoWrap function that was introduced in the other blog post.
It could also be tricky to figure out what exact VARIANT type combination a function expects. The simplest might be to check the rxinventor.tli file that the compiler creates for us when our project is referencing the inventor type library. There you'll find all the functions, including this as well:
#pragma implementation_key(7455) inline HRESULT Sheet::GetReferenceKey ( SAFEARRAY * * ReferenceKey, long KeyContext ) { return _com_dispatch_raw_method( this, 0x7f000016, DISPATCH_METHOD, VT_EMPTY, NULL, L"\x6011\x0003", ReferenceKey, KeyContext); }
It shows what types the IDispatch::Invoke will require for the given function. ReferenceKey parameter's VARIANT type should be hexadecimal 0x6011. You can check the values e.g. from the "c:\Program Files (x86)\Windows Kits\8.0\Include\shared\wtypes.h" file. Some values there are in decimal and some in hexadecimal, so easiest is to convert all of them to hexadecimal:
- ReferenceKey's type is 0x6011. VT_BYREF = 0x4000, VT_ARRAY = 0x2000, VT_UI1 = 17 = 0x0011. If you add them together it's 0x6011
- KeyContext's type is 0x003, which is VT_I4.
If we already have an object e.g. a Face stored in pFace and the context index in lContext then this is how we could get the face's reference key. This code shows how you could do it with SAFEARRAY or CComSafeArray:
// This is what the early-biding version would // look like SAFEARRAY * refKeys = NULL; pFace->GetReferenceKey(&refKeys, lContext); // This is the late-binding version // Important to set myarray to NULL otherwise we could // get an error SAFEARRAY * myarray = NULL; VARIANT param1; VariantInit(¶m1); param1.vt = VT_ARRAY | VT_BYREF | VT_UI1; param1.pparray = &myarray; VARIANT param2; VariantInit(¶m2); param2.vt = VT_I4; param2.lVal = lContext; VARIANT varResult; VariantInit(&varResult); Result = AutoWrap( DISPATCH_METHOD, &varResult, pFace, _T("GetReferenceKey"), 2, param2, param1); // You can also wrap the result in CComSafeArray // Need to include "atlsafe.h" CComSafeArray<byte> saRefKey; Result = saRefKey.Attach(myarray);
It's useful to point out, because I missed it too before :), that the value you get back from CreateKeyContext and LoadContextFromArray is just an index of the context data, not the data itself. So if you want to make sure that you can find your BREP object in another session, then you'll have to follow all the steps mentioned in this post: get the context data using SaveContextToArray and then save it somewhere.