PW API aaSpatial_SetLocation

Hi there,

I'm trying to set the spatial location for new empty documents in VB.net (with MS Visual Basic 2008 EE) and I've got problems with handling the arguments.

My application works like:

'Initializing
aaApi_Initialize(0)
'Logging in
aaApi_Login(...)
'Creating a new document
aaApi_CreateDocument(...)
'here I want to add a Spatial Location
aaSpatial_SetLocation(?,?,?,?,?)
'Adding environment attributes
aaApi_UpdateLinkDataColumnValue(...)
aaApi_UpdateEnvAttr(...)

I didn't find any SDK examples for aaSpatial_SetLocation() and I need to provide the following arguments:

[in]  objectType  Object (type) receiving the spatial location.
[in]  objectGuid  Object (GUID) receiving the spatial location.
[in]  accessRights  AASPATIAL_ACCESS_PUBLIC or AASPATIAL_ACCESS_PRIVATE.
[in]  srsGuid  Spatial reference system GUID.
[in]  pGeometry  Geometry in Well Known Binary (WKB) format.

To get GUID I thought of using the following:

aaApi_SelectDocument(docID, projID)
lpcguid=aaApi_GetDocumentGuidProperty(DOC_PROP_DOCGUID, idx)

Is that right and am I correct to use String type in vb.net to get the guid? My first try failed since I got nothing readable in lpcguid.

Next I should get SRS GUID with aaSpatial_ListSRS() and the WKB-geometry with something like spatialWKB_createPolygon().

Last but not least I need to know the access indicator and I want it to be public, but unfortunately I couldn't find the value of AASPATIAL_ACCESS_PUBLIC in the API documentation.

Now I'm using PW v8i SS1 but it'll be SS3 soon.

I hope someone may help me with that issue, thank you in advance,

Maik

Parents
  • You will need to define a structure for the GUID and pass it by reference. When the GUID is a return value you will have to use the type IntPtr and then marshel it to a GUID structure

    In C# I am using the following code for defining a GUID

    using System.Runtime.InteropServices;

    /// <summary>
    /// C style GUID structure
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct GUID {
        /// unsigned int
        public uint Data1;
        /// unsigned short
        public ushort Data2;
        /// unsigned short
        public ushort Data3;
        /// unsigned char[8]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        public byte[] Data4;
    }

    I also have the following utility methods defined in the static class that I use to encapsulate the ProjectWise API functions to convert the GUID to and from a .NET System.Guid as well as a string.

    /// <summary>
    /// Converts a C style GUID to a System.Guid
    /// </summary>
    /// <param name="value">The C style GUID</param>
    /// <returns>A System.Guid equivalent</returns>
    public static Guid FromGUID(GUID value) {
        return new Guid((Int32)value.Data1, (Int16)value.Data2, (Int16)value.Data3, value.Data4);
    }

    /// <summary>
    /// Converts a System.Guid to a C style GUID structure
    /// </summary>
    /// <param name="value">The System.Guid to convert</param>
    /// <returns>A C style GUID equivalent of the System.Guid</returns>
    public static GUID ToGUID(Guid value) {
        GUID result = new GUID();
        byte[] bytes = value.ToByteArray();

        result.Data1 = BitConverter.ToUInt32(bytes, 0);
        result.Data2 = BitConverter.ToUInt16(bytes, 4);
        result.Data3 = BitConverter.ToUInt16(bytes, 6);

        result.Data4 = new byte[8];
        for (int i = 8; i < bytes.Length; i++) {
            result.Data4[i - 8] = bytes[i];
        }
        return result;
    }

    /// <summary>
    /// Gets a string representation f a C style GUID structure
    /// </summary>
    /// <param name="guidStructure">The C style GUID structure</param>
    /// <returns>A string representation of the GUID</returns>
    public static string StringFromGUID(GUID guidStructure) {
        Guid guid = FromGUID(guidStructure);
        return guid.ToString("D");
    }

    I use the following code to marshal the pointer to a GUID structure

    IntPtr ptr = PWNative.aaApi_DmsDataBufferGetGuidProperty(_hBuffer, propID, index);
    if (ptr != IntPtr.Zero) {
        return PWNative.FromGUID((GUID)Marshal.PtrToStructure(ptr, typeof(GUID)));
    }

    Hopefully You should be able to sort out the equivalent VB code for the above C# snippets 

    Also do a search on the web for "Microsoft P/Invoke Interop Assistant". It is a tool that I have found very helpful in sorting out how to import and work with the ProjectWise SDK from managed code. It can generate both C# and VB code from the C declarations ( although you do have to dig through the header files to find the underlying types and amend the C declaration before it will generate the .NET code for you)

  • This works fine for me for getting Guids via aaApi_GetDocumentGuidProperty.

    But I can't figure out how to pass a Guid as a parameter to Boolean aaApi_GUIDGetDocumentFileSize64(Guid pDocGuid, UInt64 pFileSize)

    I get ERROR:50000 Failed to get object identifier by its GUID regardless.

    pFileSize is defined as

    [in] pFileSize Buffer to receive the file size.

     I have tried variations on the following

    public static extern Boolean aaApi_GUIDGetDocumentFileSize64(Guid pDocGuid, UInt64 pFileSize); 

    private UInt64 GUIDGetDocumentFileSize64(string pDocGuid)      

    { 

       UInt64 pFileSize = 0;         

       var guid = new Guid(pDocGuid);        

       var ret = PW_API.aaApi_GUIDGetDocumentFileSize64(guid, pFileSize);        

       return pFileSize;      

    }

    Any suggestions would be appreciated.

  • The setup of your function params should look similar to below.  Make sure to check return values of each function and call aaApi_GetLastErrorId() and aaApi_GetLastErrorMessage() to help explain any error conditions encountered.

    GUID doc1Guid = {0};
    UINT64 fileSize = 0;
    aaApi_GUIDGetDocumentFileSize64(&doc1Guid, &fileSize);

    HTH,

    Bob



  • Hi Bob,

     

    I’m still at sea here.

    UINT64 fileSize = 0; // OKGUID doc1Guid = {0}; // The compiler tells me that I can’t assign an array to a GUID type.

    But in any event, my function starts with a Guid string so I need to assign this value to the GUID. I attempt to do this by converting it to a system.Guid

    var guid = new Guid(inputString);

    and then convert that to a GUID as defined in the post above

    GUID doc1Guid =ToGUID(guid);

    I then pass this to the aaApi_GUIDGetDocumentFileSize64. The GUID parameters is input only, but the fileSize variable is where I look for the result, so I call it by by reference.

    aaApi_GUIDGetDocumentFileSize64(doc1Guid, ref fileSize)

    I don’t understand the & in your version. There doesn’t seem to be any valid use for & in C#. Do you mean the variables should be passed by reference ?

    aaApi_GUIDGetDocumentFileSize64(&doc1Guid, &fileSize)

    The function returns False, but the error message is only ERROR:50000 Failed to get object identifier by its GUID.

    My last attempt looks like this

         private UInt64 GUIDGetDocumentFileSize64(string pDocGuid)

           {

               UInt64 fileSize = 0;

               // convert string to a system.Guid

               var guid = new Guid(pDocGuid);

               // convert system.Guid to a GUID as defined in the post above

               GUID doc1Guid = ToGUID(guid);

               // The GUID parameter is input only, but the fileSize variable is where I look for the result, so I call it by by reference.

    var ret = PW_API.aaApi_GUIDGetDocumentFileSize64(doc1Guid, ref fileSize);
    return fileSize
    }

    The DllImport is

    public static extern Boolean aaApi_GUIDGetDocumentFileSize64( GUID pDocGuid, ref UInt64 pFileSize);

    regards

    René

  • Oops. I see that &VAR means addressof-expression VAR. But can only be used in unsafe context.

  • Rene,

    It is highly recommended to design, create, and maintain any Bentley specific code you develop to run natively within the host (in-process) product. e.g. ProjectWise Explorer (having a custom .dll), MicroStation (having a Native code MDL or Managed code MDL Addin), etc.  By creating, testing, and maintaining your code completely within process you can natively debug any problem with ease and prevent a slew of problem debugging and coding across different programming languages and underlying Microsoft Visual C runtime environments.  Once you create your custom code in-process and confirm proper operation then you can create public "exports" (or wrapper functions) that can be cleanly and simply called from your external (out-of-process) application with ease and reliability.

    This approach decouples your (mostly) Bentley specific code running in-process from the (mostly) Microsoft specific code for your external application/interface keeping code to a clean and maintainable minimum that any developer could extend or maintain.  If you choose not to follow this design approach then you are likely to encounter numerous problems including but not limited to:

    • Having to interpret and debug runtime errors that may likely have bearing on what the real underlying problem is, and spending a fair amount of time troubleshooting Microsoft calling conventions
    • Dealing with various Memory and Visual C runtime incompatibility issues; where each runtime may use a completely different implementation for a function or native data types that when allocating or passing across potentially different virtual address space boundaries and runtimes.could cause your application or host application to hang, crash, and/or loose or corrupt application data
    • Spend significant time and effort migrating a whole API's function declarations and types over to a language that we currently do not provide a more direct interface to.  e.g. Attempting to write C# import code to wrap and deal with each specific function required to be used and each data type a function requires. As you can imagine the duplication, potential for instability, and chance of error can be rather high.

    I hope you consider creating your custom code in-process since the API is ready to go, supportable, and easily debuggable with useful error messages that relate to the problem and not the calling convention that can save a lot of time - when things do go wrong.

    HTH,
    Bob



Reply
  • Rene,

    It is highly recommended to design, create, and maintain any Bentley specific code you develop to run natively within the host (in-process) product. e.g. ProjectWise Explorer (having a custom .dll), MicroStation (having a Native code MDL or Managed code MDL Addin), etc.  By creating, testing, and maintaining your code completely within process you can natively debug any problem with ease and prevent a slew of problem debugging and coding across different programming languages and underlying Microsoft Visual C runtime environments.  Once you create your custom code in-process and confirm proper operation then you can create public "exports" (or wrapper functions) that can be cleanly and simply called from your external (out-of-process) application with ease and reliability.

    This approach decouples your (mostly) Bentley specific code running in-process from the (mostly) Microsoft specific code for your external application/interface keeping code to a clean and maintainable minimum that any developer could extend or maintain.  If you choose not to follow this design approach then you are likely to encounter numerous problems including but not limited to:

    • Having to interpret and debug runtime errors that may likely have bearing on what the real underlying problem is, and spending a fair amount of time troubleshooting Microsoft calling conventions
    • Dealing with various Memory and Visual C runtime incompatibility issues; where each runtime may use a completely different implementation for a function or native data types that when allocating or passing across potentially different virtual address space boundaries and runtimes.could cause your application or host application to hang, crash, and/or loose or corrupt application data
    • Spend significant time and effort migrating a whole API's function declarations and types over to a language that we currently do not provide a more direct interface to.  e.g. Attempting to write C# import code to wrap and deal with each specific function required to be used and each data type a function requires. As you can imagine the duplication, potential for instability, and chance of error can be rather high.

    I hope you consider creating your custom code in-process since the API is ready to go, supportable, and easily debuggable with useful error messages that relate to the problem and not the calling convention that can save a lot of time - when things do go wrong.

    HTH,
    Bob



Children
No Data