Delete and modify XData fields (V8i, cpp)

I would like to store some information to XData fields, so it could also be saved to AutoCad models. From the mdl API I have found functions to save and read XData but how can I delete or modify certain fields?

Lets say for example that I want to save an ID to an element and for some reason the ID changes so I need to change the ID in a model. I could not found any functions to modify existing XData. There is a function to delete the whole XData (I think) but how to delete only my own data binded to my application?

  • If I read the XData with mdlLinkage_extractXDataLinkage, how do I parse the output? It seems to contain the strings that I have written, but I'm still not sure what the other data is.

  • To parse out xdata you'd have to walk through each of DXF group codes stored in the xdata linkage, something like this:

         if (mdlLinkage_extractXDataLinkage (&pXData, &numBytes, pElement) == SUCCESS
            for (int count=0; pXData != NULL && count < numBytes; pXData += size, count += 2*sizeof(short) + 4 + size;)
                switch (GetNextXDataGroupCode(size, &pXData))
                    case 1005:
                            // object handle/element ID

    Where you move your linkage pointer via:

    short    GetNextXDataGroupCode
    short&   xdataSize,  /* <= size of xdata item */
    byte     **ppXData   /* <=> pointer to XData buffer */
        short       xdataType;

        memcpy (&xdataType, *ppXData, sizeof xdataType);
        *ppXData += sizeof (short);

        memcpy (&xdataSize, *ppXData, sizeof (short));
        *ppXData += 3 * sizeof (short);

        return  xdataType;

    You may find XDATA group codes definitions from Adesk website:

    To delete your own xdata you can use the same iteration through entire linkage, copy all those until you stop at your own application name, then skip through until you hit next application name.  Upon finishing the process, add back your changed xdata to the copy and replace the existing linkage on the element.  There are a couple of mdlLinkage functions that can make the process easier but they are not currently published.

  • Unknown said:
    There are a couple of mdlLinkage functions that can make the process easier but they are not currently published

    Tantalysing!  Would Bentley Systems like to publish them?  I'm in favour of eliminating magic code such as:

    count += 2*sizeof(short) + 4 + size

    Should we have to write opaque calculations like that in the 21st century?  It's enough to drive one to C#.

    Regards, Jon Summers
    LA Solutions

  • Our initial thought was that, if XDATA were anything useful it would be only on AutoCAD, as MicroStation certainly does not make use of it.  MicroStation users may only need it for DWG round trip purpose.  In rare occasions if someone ever needs it, he/she presumably knows XDATA perhaps because he/she has used it in his/her apps on AutoCAD, therefore he/she does not need much assistance for that to work on MicroStation.

    If XDATA turns out to be something more useful than we initially thought, then we should publish these API's.  Anyone else out there uses XDATA on MicroStation?


  • Thanks for the code Don.

    I managed to write some working code according to your example. Could you tell if there are lots of mistakes?

    ElementRef deleteXData(ElementRef elemRef)
       int status = SUCCESS;
       Bentley::Ustn::ElemDescrSmartP eDSP(elemRef, ACTIVEMODEL);

       // buffer for extracted xdata
       byte * buffer = NULL;
       UInt32 bufferIndex = 0;
       UInt32 bufferSize = 0;
       status = mdlLinkage_extractXDataLinkage(&buffer, &bufferSize, &eDSP->el);
       if (status != SUCCESS)
          // there may not be any linkage
          return elemRef;

       // buffer in here
       UInt32 copyIndex = 0;
       byte * copyBuffer = new byte[bufferSize];
       memset(copyBuffer, NULL, bufferSize);

       ElementID id;
       ElementID swappedId = _byteswap_uint64(applicationId);

       // don't write own data
       bool write = true;
       // when own xdata ends
       bool end = false;

       // for storing parsed values
       // command chars
       char c;
       // block's data size
       short size;

       // to copy buffer blocks
       byte * blockStart = NULL;
       UInt32 copyBlockSize = 0;
       while (bufferIndex < bufferSize)
          blockStart = &buffer[bufferIndex];
          switch (getNextXDataBlock(size, buffer, bufferIndex))
          case DWGXDATA_Application_Name:
             id = 0;
             memcpy(&id, &buffer[bufferIndex], size);
             if (id == swappedId)
                // own data found, so stop writing
                write = false;
          case DWGXDATA_ControlString:
             memcpy(&c, &buffer[bufferIndex], size);
             if (c == '}')
                // start writing again
                end = true;

          if (write)
             // write to copy buffer
             copyBlockSize = 4 * sizeof(short) + size;
             if (bufferSize - copyIndex >= copyBlockSize)
                memcpy(&copyBuffer[copyIndex], blockStart, copyBlockSize);
                copyIndex += copyBlockSize;

          if (end)
             write = true;
          // increment buffer index
          bufferIndex += size;

       // write new buffer
       if (copyIndex > 0)
          status = mdlLinkage_setXDataLinkage(eDSP.GetPtr(), copyBuffer, copyIndex);
          // if buffer is empty delete whole linkage
          status = mdlLinkage_deleteXDataLinkage(&eDSP->el);
          if (status)
             status = SUCCESS;

       // rewrite to model
       UInt32 filePos = mdlElmdscr_rewrite(eDSP.GetPtrVal(), NULL,

       // update reference
       ElementRef newElemRef = mdlModelRef_getElementRef(ACTIVEMODEL, filePos);

       if (status != SUCCESS)
          printf("Delete Attribute failed (status %d)\n", status);

       delete[] copyBuffer;
       return newElemRef;

    short getNextXDataBlock(short & size, byte * buffer, UInt32 & index)
       short type;
       short tempSize = 0;

       // get group type
       memcpy(&type, &buffer[index], sizeof(short));
       index += sizeof(short);

       // get data blocks length
       memcpy(&tempSize, &buffer[index], sizeof(short));
       // move over length and skip 2 following fields
       index += 3 * sizeof(short);

       size = tempSize;
       return type;

  • Assuming your swappedId logic works correctly, I did not spot any obvious error.  I'm sure you can verify the ID swap logic easily through your debugging.

    The only suggestion I have is to not rely on group control string '}'.  This is because that not all AutoCAD developers follow the best practice guidelines per Autodesk's DXF file format and the group control strings may not present in their XDATA.  Most developers would put control strings in there, but not all do.  The only reliable check is DWGXDATA_Application_Name(1001 presumably).  You may want to use your regapp name as a switch to turn on/off your copy operation.