上周处理了一个有趣的问题,与垃圾回收机制有关。当获取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所返回的对象到主调用函数。保护了目标对象不释放。
作者:叶雄进文章来源:http://blog.csdn.net/joexiongjin/article/category/782739
欢迎光临 EaBIM (https://eabim.net/) | Powered by Discuz! X3.2 |