访问实体的几何数据时,抛出 SEHException错误(只有Release版出现)

    技术2026-09-29  4

     

    上周处理了一个有趣的问题,与垃圾回收机制有关。当获取Revit中对象的几何信息,在一个函数来获得构件的Solid。然后在Caller中对solid包含的面进行遍历访问。加载运行编译的debug版时,没有问题,当用Release版时,出现SEHExpection异常。

     

        --AccessViolationException - Attempted to read or write protected memory. This is often an indication that other          memory is corrupt.

     

    这个问题的本质是有关垃圾回收机制的。因为大家常常用到几何数据访问,这里给大家分享下产生的原因和修改方法。

     

    源代码大概样子是这样的:

    注:在你运行的时候不一定出现这个错误,与机器的资源,内存,计算过程等有关。

     

    [TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Manual)][RegenerationAttribute(Autodesk.Revit.Attributes.RegenerationOption.Manual)]public class Lab1PlaceGroup : IExternalCommand{  public Result Execute(    ExternalCommandData commandData,    ref string message,    ElementSet elements)    {      //Get application and document objects      UIApplication uiApp = commandData.Application;      Document doc = uiApp.ActiveUIDocument.Document;

          Transaction trans = new Transaction(doc, "ExComm");      trans.Start();

          //Define a Reference object to accept the pick result.      Reference pickedRef = null;

          //Pick an object      Selection sel = uiApp.ActiveUIDocument.Selection;      pickedRef = sel.PickObject(ObjectType.Element,        "Please select a family instance");      Element elem = pickedRef.Element;            Options geoOptions = uiApp.Application.Create.NewGeometryOptions();      SolidArray solids = new SolidArray();      GeometryElement geoElement = elem.get_Geometry(geoOptions);

          //get the transformed solid elements      GetSolids(geoElement, ref solids);

          SolidArrayIterator solidIter = solids.ForwardIterator();      solidIter.Reset();      FaceArray faces = null;      while (solidIter.MoveNext())      {        faces = null;        Autodesk.Revit.DB.Solid sol = solidIter.Current as Autodesk.Revit.DB.Solid;        faces = sol.Faces;        if (faces.Size > 0) //抛出 SEHException错误,没有规律的出现。        {          TaskDialog.Show("Get faces", "the solid has " + faces.Size.ToString() + "faces");        }      }

               trans.Commit();

          return Result.Succeeded;    }

      public void GetSolids(GeometryElement geomElem, ref SolidArray solids)  {    foreach (Autodesk.Revit.DB.GeometryObject geomObj in geomElem.Objects)    {

          Autodesk.Revit.DB.Solid solid = geomObj as Autodesk.Revit.DB.Solid;      if (null != solid)      {        if (solid.Faces.Size > 0)        {          {            solids.Append(solid);            continue;          }        }      }

          Autodesk.Revit.DB.GeometryInstance geomInst = geomObj as Autodesk.Revit.DB.GeometryInstance;      if (null != geomInst)      {        Autodesk.Revit.DB.GeometryElement transformedGeomElem = geomInst.GetInstanceGeometry((geomInst.Transform);             GetSolids(transformedGeomElem, ref solids);      }    }  }  }

     

    错误原因是solid对象从GetInstanceGeometry 函数返回的transformedGeomElem 中获得。当退出拷贝solid的GetSolids()函数回到主调用函数后,操作系统把GetInstanceGeometry 返回的transformedGeomElem 的内存回收了。这一垃圾回收,导致返回到主条用函数的solidArray失去了数据源数据。在debug版,所有的变量占用的内存收保护没有被释放掉,所以debug版运行正常。在Release版垃圾回收机制起作用,把有用的内存数据释放了。所以我们再访问时,这块内存被保护起来了。所以错误信息包含 Attempted to read or write protected memory。

    解决办法有两个:

    1. 把读取solid数据的代码放在遍历solid的代码后面,处于同一个函数中。这样可能transformedGeomElem 还不能释放。

    2. 增加一个对象引用,指向返回的transformedGeomElem 对象,这样也不会释放,因为有人正引用它。

     

    下面是修改后的代码。

     

    [TransactionAttribute(Autodesk.Revit.Attributes.TransactionMode.Manual)][RegenerationAttribute(Autodesk.Revit.Attributes.RegenerationOption.Manual)]public class Lab1PlaceGroup : IExternalCommand{  public Result Execute(    ExternalCommandData commandData,    ref string message,    ElementSet elements)  {    //Get application and document objects    UIApplication uiApp = commandData.Application;    Document doc = uiApp.ActiveUIDocument.Document;

        Transaction trans = new Transaction(doc, "ExComm");    trans.Start();

        //Define a Reference object to accept the pick result.    Reference pickedRef = null;

        //Pick an object    Selection sel = uiApp.ActiveUIDocument.Selection;    pickedRef = sel.PickObject(ObjectType.Element,      "Please select a family instance");    Element elem = pickedRef.Element;

        Options geoOptions = uiApp.Application.Create.NewGeometryOptions();    SolidArray solids = new SolidArray();    GeometryElement geoElement = elem.get_Geometry(geoOptions);

        GeometryElement refGeometry = null;  //对象保护,防止几何数据被释放掉了,后面产生SEHException.    //get the transformed solid elements    GetSolids(geoElement, ref solids,ref refGeometry);

        SolidArrayIterator solidIter = solids.ForwardIterator();    solidIter.Reset();    FaceArray faces = null;    while (solidIter.MoveNext())    {      faces = null;      Autodesk.Revit.DB.Solid sol = solidIter.Current as Autodesk.Revit.DB.Solid;      faces = sol.Faces;      if (faces.Size > 0) //这里再也不会出现问题了。      {        TaskDialog.Show("Get faces", "the solid has " + faces.Size.ToString() + "faces");      }    }

        trans.Commit();

        return Result.Succeeded;  }

      public void GetSolids(GeometryElement geomElem, ref SolidArray solids, ref GeometryElement refGeometry)  {    foreach (Autodesk.Revit.DB.GeometryObject geomObj in geomElem.Objects)    {

          Autodesk.Revit.DB.Solid solid = geomObj as Autodesk.Revit.DB.Solid;      if (null != solid)      {        if (solid.Faces.Size > 0)        {          {            solids.Append(solid);            continue;          }        }      }            Autodesk.Revit.DB.GeometryInstance geomInst = geomObj as Autodesk.Revit.DB.GeometryInstance;      if (null != geomInst)      {                refGeometry = geomInst.GetInstanceGeometry(geomInst.Transform);                GetSolids(refGeometry, ref solids,ref refGeometry);      }    }  }}

     

    这里增加了refGeometry 来指向GetInstanceGeometry所返回的对象到主调用函数。保护了目标对象不释放。

     

    最新回复(0)