From 6d54a9deb2b9e7226c1fb543632be45bb2e30138 Mon Sep 17 00:00:00 2001 From: eap Date: Fri, 12 Mar 2021 19:29:02 +0300 Subject: [PATCH] Check mesh size before export --- src/Driver/Driver_Mesh.h | 28 +++++++-- src/DriverCGNS/DriverCGNS_Write.cxx | 7 ++- src/DriverGMF/DriverGMF_Write.cxx | 3 + src/DriverMED/DriverMED_W_SMESHDS_Mesh.cxx | 11 +++- src/DriverUNV/DriverUNV_W_SMDS_Mesh.cxx | 8 ++- src/SMESH/SMESH_Mesh.cxx | 69 +++++++++++++++++---- src/SMESH/SMESH_Mesh.hxx | 10 +++ src/SMESHGUI/SMESHGUI.cxx | 36 ++++++----- src/SMESHUtils/SMESH_TryCatch.hxx | 8 +-- src/SMESH_I/SMESH_Mesh_i.cxx | 72 ++++++++++++++-------- 10 files changed, 190 insertions(+), 62 deletions(-) diff --git a/src/Driver/Driver_Mesh.h b/src/Driver/Driver_Mesh.h index bd6a71406..30ab4e286 100644 --- a/src/Driver/Driver_Mesh.h +++ b/src/Driver/Driver_Mesh.h @@ -28,6 +28,7 @@ #define _INCLUDE_DRIVER_MESH #include "SMESH_ComputeError.hxx" +#include "SMDS_Mesh.hxx" #include #include @@ -50,12 +51,13 @@ class MESHDRIVER_EXPORT Driver_Mesh enum Status { DRS_OK, - DRS_EMPTY, // a file contains no mesh with the given name - DRS_WARN_RENUMBER, // a file has overlapped ranges of element numbers, - // so the numbers from the file are ignored - DRS_WARN_SKIP_ELEM, // some elements were skipped due to incorrect file data + DRS_EMPTY, // a file contains no mesh with the given name + DRS_WARN_RENUMBER, // a file has overlapped ranges of element numbers, + // so the numbers from the file are ignored + DRS_WARN_SKIP_ELEM, // some elements were skipped due to incorrect file data DRS_WARN_DESCENDING, // some elements were skipped due to descending connectivity - DRS_FAIL // general failure (exception etc.) + DRS_FAIL, // general failure (exception etc.) + DRS_TOO_LARGE_MESH // mesh is too large for export }; void SetMeshId(int theMeshId); @@ -70,6 +72,22 @@ class MESHDRIVER_EXPORT Driver_Mesh virtual SMESH_ComputeErrorPtr GetError(); + //virtual bool CanExportMesh() const { return false; } //= 0; + + // check if a mesh is too large to export it using IDTYPE; + // check either max ID or number of elements + template< typename IDTYPE > + static bool IsMeshTooLarge( const SMDS_Mesh* mesh, bool checkIDs ) + { + if ( sizeof( IDTYPE ) < sizeof( smIdType )) + { + const smIdType maxNB = std::numeric_limits< IDTYPE >::max(); + return (( checkIDs ? mesh->MaxNodeID() : mesh->NbNodes() ) > maxNB || + ( checkIDs ? mesh->MaxElementID() : mesh->NbElements() > maxNB )); + } + return false; + } + protected: std::string myFile; std::string myMeshName; diff --git a/src/DriverCGNS/DriverCGNS_Write.cxx b/src/DriverCGNS/DriverCGNS_Write.cxx index bd6bcff2f..0643273cb 100644 --- a/src/DriverCGNS/DriverCGNS_Write.cxx +++ b/src/DriverCGNS/DriverCGNS_Write.cxx @@ -248,6 +248,9 @@ Driver_Mesh::Status DriverCGNS_Write::Perform() if ( !myMesh || myMesh->GetMeshInfo().NbElements() < 1 ) return addMessage( !myMesh ? "NULL mesh" : "Empty mesh (no elements)", /*fatal = */true ); + if ( Driver_Mesh::IsMeshTooLarge< cgsize_t >( myMesh, /*checkIDs =*/ false)) + return DRS_TOO_LARGE_MESH; + // open the file if ( cg_open(myFile.c_str(), CG_MODE_MODIFY, &_fn) != CG_OK && cg_open(myFile.c_str(), CG_MODE_WRITE, &_fn) != CG_OK ) @@ -282,7 +285,9 @@ Driver_Mesh::Status DriverCGNS_Write::Perform() else if ( meshDim == 2 ) nbCells = myMesh->NbFaces(); - cgsize_t size[9] = { myMesh->NbNodes(), nbCells, /*NBoundVertex=*/0, 0,0,0,0,0,0 }; + cgsize_t size[9] = { FromIdType( myMesh->NbNodes() ), + FromIdType( nbCells ), + /*NBoundVertex=*/0, 0,0,0,0,0,0 }; int iZone; if ( cg_zone_write( _fn, iBase, "SMESH_Mesh", size, CGNS_ENUMV( Unstructured ), &iZone) != CG_OK ) diff --git a/src/DriverGMF/DriverGMF_Write.cxx b/src/DriverGMF/DriverGMF_Write.cxx index 69388298a..ceaad2197 100644 --- a/src/DriverGMF/DriverGMF_Write.cxx +++ b/src/DriverGMF/DriverGMF_Write.cxx @@ -100,6 +100,9 @@ Driver_Mesh::Status DriverGMF_Write::Perform() { Kernel_Utils::Localizer loc; + if ( Driver_Mesh::IsMeshTooLarge< int >( myMesh, /*checkIDs =*/ false)) + return DRS_TOO_LARGE_MESH; + const int dim = 3, version = sizeof(double) < 8 ? 1 : 2; int meshID = GmfOpenMesh( myFile.c_str(), GmfWrite, version, dim ); diff --git a/src/DriverMED/DriverMED_W_SMESHDS_Mesh.cxx b/src/DriverMED/DriverMED_W_SMESHDS_Mesh.cxx index 375572dd5..bd5e48f47 100644 --- a/src/DriverMED/DriverMED_W_SMESHDS_Mesh.cxx +++ b/src/DriverMED/DriverMED_W_SMESHDS_Mesh.cxx @@ -343,12 +343,21 @@ namespace } } +//================================================================================ +/*! + * \brief Write my mesh + */ +//================================================================================ + Driver_Mesh::Status DriverMED_W_SMESHDS_Mesh::Perform() { Status aResult = DRS_OK; try { //MESSAGE("Perform - myFile : "<( myMesh, /*checkIDs =*/ true )) + return DRS_TOO_LARGE_MESH; + // Creating the MED mesh for corresponding SMDS structure //------------------------------------------------------- string aMeshName; @@ -559,7 +568,7 @@ Driver_Mesh::Status DriverMED_W_SMESHDS_Mesh::Perform() aTCoordSlice[2] = 0.; // node number - int aNodeID = aCoordHelperPtr->GetID(); + TInt aNodeID = FromIdType( aCoordHelperPtr->GetID() ); aNodeInfo->SetElemNum( iNode, aNodeID ); #ifdef _EDF_NODE_IDS_ aNodeIdMap.insert( aNodeIdMap.end(), make_pair( aNodeID, iNode+1 )); diff --git a/src/DriverUNV/DriverUNV_W_SMDS_Mesh.cxx b/src/DriverUNV/DriverUNV_W_SMDS_Mesh.cxx index 95da34525..da3381285 100644 --- a/src/DriverUNV/DriverUNV_W_SMDS_Mesh.cxx +++ b/src/DriverUNV/DriverUNV_W_SMDS_Mesh.cxx @@ -45,6 +45,10 @@ Driver_Mesh::Status DriverUNV_W_SMDS_Mesh::Perform() { Kernel_Utils::Localizer loc; Status aResult = DRS_OK; + + if ( Driver_Mesh::IsMeshTooLarge< int >( myMesh, /*checkIDs =*/ false)) + return DRS_TOO_LARGE_MESH; + #if defined(WIN32) && defined(UNICODE) std::wstring aFile = Kernel_Utils::utf8_decode_s(myFile); std::ofstream out_stream(aFile.c_str()); @@ -256,11 +260,11 @@ Driver_Mesh::Status DriverUNV_W_SMDS_Mesh::Perform() EXCEPTION(runtime_error,"ERROR: Output file not good."); } catch(const std::exception& exc){ - INFOS("Follow exception was cought:\n\t"<::iterator it = _mapGroup.begin(); @@ -1644,6 +1678,11 @@ void SMESH_Mesh::ExportCGNS(const char * file, } #endif + SMESH_CATCH( SMESH::throwSalomeEx ); + + if ( res == Driver_Mesh::DRS_TOO_LARGE_MESH ) + throw TooLargeForExport("CGNS"); + if ( res != Driver_Mesh::DRS_OK ) throw SALOME_Exception("Export failed"); } @@ -1658,12 +1697,20 @@ void SMESH_Mesh::ExportGMF(const char * file, const SMESHDS_Mesh* meshDS, bool withRequiredGroups) { + Driver_Mesh::Status status; + SMESH_TRY; + DriverGMF_Write myWriter; myWriter.SetFile( file ); myWriter.SetMesh( const_cast( meshDS )); myWriter.SetExportRequiredGroups( withRequiredGroups ); - myWriter.Perform(); + status = myWriter.Perform(); + + SMESH_CATCH( SMESH::throwSalomeEx ); + + if ( status == Driver_Mesh::DRS_TOO_LARGE_MESH ) + throw TooLargeForExport("GMF"); } //================================================================================ diff --git a/src/SMESH/SMESH_Mesh.hxx b/src/SMESH/SMESH_Mesh.hxx index 79e349f11..e93fda65e 100644 --- a/src/SMESH/SMESH_Mesh.hxx +++ b/src/SMESH/SMESH_Mesh.hxx @@ -245,12 +245,22 @@ class SMESH_EXPORT SMESH_Mesh */ typedef TopTools_IndexedDataMapOfShapeListOfShape TAncestorMap; const TAncestorMap& GetAncestorMap() const { return _mapAncestors; } + /*! * \brief Check group names for duplications. * Consider maximum group name length stored in MED file */ bool HasDuplicatedGroupNamesMED(); + /*! + * \brief Exception thrown by Export*() in case if a mesh is too large for export + * due to limitation of a format + */ + struct TooLargeForExport : public std::runtime_error + { + TooLargeForExport(const char* format):runtime_error(format) {} + }; + void ExportMED(const char * theFile, const char* theMeshName = NULL, bool theAutoGroups = true, diff --git a/src/SMESHGUI/SMESHGUI.cxx b/src/SMESHGUI/SMESHGUI.cxx index 476167a29..588829a79 100644 --- a/src/SMESHGUI/SMESHGUI.cxx +++ b/src/SMESHGUI/SMESHGUI.cxx @@ -992,11 +992,19 @@ namespace aMesh->ExportGMF( aMeshOrGroup, aFilename.toUtf8().data(), toCreateGroups ); } } - catch (const SALOME::SALOME_Exception& S_ex){ + catch (const SALOME::SALOME_Exception& S_ex) + { wc.suspend(); - SUIT_MessageBox::warning(SMESHGUI::desktop(), - QObject::tr("SMESH_WRN_WARNING"), - QObject::tr("SMESH_EXPORT_FAILED") + SalomeApp_Tools::ExceptionToString(S_ex)); + if ( S_ex.details.type == SALOME::COMM && // communicate about too large mesh + strncmp( "format=", S_ex.details.sourceFile.in(), 7 ) == 0 ) + + SUIT_MessageBox::critical(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr(S_ex.details.text.in() )); + else + SUIT_MessageBox::warning(SMESHGUI::desktop(), + QObject::tr("SMESH_WRN_WARNING"), + QObject::tr("SMESH_EXPORT_FAILED") + SalomeApp_Tools::ExceptionToString(S_ex)); wc.resume(); } } @@ -5131,7 +5139,7 @@ bool SMESHGUI::activateModule( SUIT_Study* study ) lab = lab + tr("INFO_COMPUTE") + "
"; lab = lab + tr("INFO_REFINE") + ":"; items << wrap(tr("INFO_REFINE_LOCAL_SIZE"), "li") - << wrap(tr("INFO_REFINE_SUBMESH"), "li"); + << wrap(tr("INFO_REFINE_SUBMESH"), "li"); lab = lab + wrap(items.join(""), "ul"); items.clear(); @@ -5139,22 +5147,22 @@ bool SMESHGUI::activateModule( SUIT_Study* study ) gb = app->infoPanel()->addGroup(tr("INFO_GRP_IMPORT_MESH")); items << wrap("UNV", "li") - << wrap("MED", "li") - << wrap("STL", "li") - << wrap("CGNS", "li") - << wrap("SAUV", "li") - << wrap("GMF", "li"); + << wrap("MED", "li") + << wrap("STL", "li") + << wrap("CGNS", "li") + << wrap("SAUV", "li") + << wrap("GMF", "li"); lab = tr("INFO_AVAILABLE_FORMATS") + ":" + wrap(items.join(""), "ul"); items.clear(); - + app->infoPanel()->addLabel(lab, gb); gb = app->infoPanel()->addGroup(tr("INFO_GRP_CHECK_MESH")); lab = tr("INFO_DISPLAY") + "
"; items << wrap(tr("INFO_QUALITY_AREA"), "li") - << wrap(tr("INFO_QUALITY_VOLUME"), "li") - << wrap(tr("INFO_QUALITY_ASPECT_RATION"), "li") - << wrap("...", "li"); + << wrap(tr("INFO_QUALITY_VOLUME"), "li") + << wrap(tr("INFO_QUALITY_ASPECT_RATION"), "li") + << wrap("...", "li"); lab = lab + tr("INFO_QUALITY_INFO") + ":" + wrap(items.join(""), "ul"); items.clear(); lab = lab + tr("INFO_CLIPPING"); diff --git a/src/SMESHUtils/SMESH_TryCatch.hxx b/src/SMESHUtils/SMESH_TryCatch.hxx index eacb39a85..a718eac92 100644 --- a/src/SMESHUtils/SMESH_TryCatch.hxx +++ b/src/SMESHUtils/SMESH_TryCatch.hxx @@ -65,11 +65,14 @@ //------------------------------------------------------------------------------------- // A macro makes description of a caught exception and calls onExceptionFun(const char*). -// Two onExceptionFun() are defined here: SMESH::throwSalomeEx() and SMESH::doNothing(). +// Several onExceptionFun() are defined here: throwSalomeEx(), doNothing() and returnError(). // To add your own catch close, define SMY_OWN_CATCH macro before including this file. #define SMESH_CATCH( onExceptionFun ) \ } \ + \ + SMY_OWN_CATCH \ + \ catch (Standard_Failure& ex) \ { \ SMESH_Comment text("OCCT Exception: "); \ @@ -92,9 +95,6 @@ { \ SMESH_CAUGHT onExceptionFun( ex.what() ); \ } \ - \ - SMY_OWN_CATCH \ - \ catch (...) \ { \ SMESH_CAUGHT onExceptionFun("Unknown Exception caught"); \ diff --git a/src/SMESH_I/SMESH_Mesh_i.cxx b/src/SMESH_I/SMESH_Mesh_i.cxx index 75e3de93b..50a7e8910 100644 --- a/src/SMESH_I/SMESH_Mesh_i.cxx +++ b/src/SMESH_I/SMESH_Mesh_i.cxx @@ -84,8 +84,15 @@ #include -// to pass CORBA exception through SMESH_TRY -#define SMY_OWN_CATCH catch( SALOME::SALOME_Exception& se ) { throw se; } +// to pass CORBA exception and TooLargeForExport exception through SMESH_TRY +#define SMY_OWN_CATCH \ + catch( SALOME::SALOME_Exception& se ) { throw se; } \ + catch( ::SMESH_Mesh::TooLargeForExport& ex ) \ + { SALOME::ExceptionStruct se = { \ + SALOME::COMM, \ + CORBA::string_dup(SMESH_Comment("Mesh is too large for export in format ") << ex.what()), \ + CORBA::string_dup(SMESH_Comment("format=") << ex.what() ), 0 }; \ + throw SALOME::SALOME_Exception( se ); } #include "SMESH_TryCatch.hxx" // include after OCCT headers! @@ -461,8 +468,7 @@ SMESH::DriverMED_ReadStatus SMESH_Mesh_i::ImportCGNSFile( const char* theFileNa char* SMESH_Mesh_i::GetVersionString(CORBA::Long minor, CORBA::Short nbDigits) { - string ver = DriverMED_W_SMESHDS_Mesh::GetVersionString(minor, - nbDigits); + string ver = DriverMED_W_SMESHDS_Mesh::GetVersionString(minor, nbDigits); return CORBA::string_dup( ver.c_str() ); } @@ -3732,11 +3738,11 @@ string SMESH_Mesh_i::prepareMeshNameAndGroups(const char* file, */ //================================================================================ -void SMESH_Mesh_i::ExportMED(const char* file, - CORBA::Boolean auto_groups, - CORBA::Long version, - CORBA::Boolean overwrite, - CORBA::Boolean autoDimension) +void SMESH_Mesh_i::ExportMED(const char* file, + CORBA::Boolean auto_groups, + CORBA::Long version, + CORBA::Boolean overwrite, + CORBA::Boolean autoDimension) { //MESSAGE("MED minor version: "<< minor); SMESH_TRY; @@ -3763,10 +3769,9 @@ void SMESH_Mesh_i::ExportMED(const char* file, */ //================================================================================ -void SMESH_Mesh_i::ExportSAUV (const char* file, - CORBA::Boolean auto_groups) +void SMESH_Mesh_i::ExportSAUV( const char* file, CORBA::Boolean auto_groups ) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); @@ -3774,6 +3779,8 @@ void SMESH_Mesh_i::ExportSAUV (const char* file, TPythonDump() << SMESH::SMESH_Mesh_var( _this()) << ".ExportSAUV( r'" << file << "', " << auto_groups << " )"; _impl->ExportSAUV(file, aMeshName.c_str(), auto_groups); + + SMESH_CATCH( SMESH::throwCorbaException ); } @@ -3785,18 +3792,20 @@ void SMESH_Mesh_i::ExportSAUV (const char* file, void SMESH_Mesh_i::ExportDAT (const char *file) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); - // Update Python script // check names of groups checkGroupNames(); + // Update Python script TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportDAT( r'" << file << "' )"; // Perform Export PrepareForWriting(file); _impl->ExportDAT(file); + + SMESH_CATCH( SMESH::throwCorbaException ); } //================================================================================ @@ -3807,18 +3816,20 @@ void SMESH_Mesh_i::ExportDAT (const char *file) void SMESH_Mesh_i::ExportUNV (const char *file) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); - // Update Python script // check names of groups checkGroupNames(); + // Update Python script TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportUNV( r'" << file << "' )"; // Perform Export PrepareForWriting(file); _impl->ExportUNV(file); + + SMESH_CATCH( SMESH::throwCorbaException ); } //================================================================================ @@ -3829,13 +3840,13 @@ void SMESH_Mesh_i::ExportUNV (const char *file) void SMESH_Mesh_i::ExportSTL (const char *file, const bool isascii) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); - // Update Python script // check names of groups checkGroupNames(); + // Update Python script TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportSTL( r'" << file << "', " << isascii << " )"; @@ -3847,6 +3858,8 @@ void SMESH_Mesh_i::ExportSTL (const char *file, const bool isascii) // Perform Export PrepareForWriting( file ); _impl->ExportSTL( file, isascii, name.in() ); + + SMESH_CATCH( SMESH::throwCorbaException ); } //================================================================================ @@ -4249,7 +4262,7 @@ void SMESH_Mesh_i::exportMEDFields( DriverMED_W_Field& fieldWriter, void SMESH_Mesh_i::ExportPartToDAT(::SMESH::SMESH_IDSource_ptr meshPart, const char* file) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); @@ -4260,6 +4273,8 @@ void SMESH_Mesh_i::ExportPartToDAT(::SMESH::SMESH_IDSource_ptr meshPart, TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportPartToDAT( " << meshPart << ", r'" << file << "' )"; + + SMESH_CATCH( SMESH::throwCorbaException ); } //================================================================================ /*! @@ -4270,7 +4285,7 @@ void SMESH_Mesh_i::ExportPartToDAT(::SMESH::SMESH_IDSource_ptr meshPart, void SMESH_Mesh_i::ExportPartToUNV(::SMESH::SMESH_IDSource_ptr meshPart, const char* file) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); @@ -4281,6 +4296,8 @@ void SMESH_Mesh_i::ExportPartToUNV(::SMESH::SMESH_IDSource_ptr meshPart, TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportPartToUNV( " << meshPart<< ", r'" << file << "' )"; + + SMESH_CATCH( SMESH::throwCorbaException ); } //================================================================================ /*! @@ -4292,7 +4309,7 @@ void SMESH_Mesh_i::ExportPartToSTL(::SMESH::SMESH_IDSource_ptr meshPart, const char* file, ::CORBA::Boolean isascii) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); @@ -4308,6 +4325,8 @@ void SMESH_Mesh_i::ExportPartToSTL(::SMESH::SMESH_IDSource_ptr meshPart, TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportPartToSTL( " << meshPart<< ", r'" << file << "', " << isascii << ")"; + + SMESH_CATCH( SMESH::throwCorbaException ); } //================================================================================ @@ -4322,7 +4341,7 @@ void SMESH_Mesh_i::ExportCGNS(::SMESH::SMESH_IDSource_ptr meshPart, CORBA::Boolean groupElemsByType) { #ifdef WITH_CGNS - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); @@ -4344,6 +4363,9 @@ void SMESH_Mesh_i::ExportCGNS(::SMESH::SMESH_IDSource_ptr meshPart, TPythonDump() << SMESH::SMESH_Mesh_var(_this()) << ".ExportCGNS( " << meshPart<< ", r'" << file << "', " << overwrite << ")"; + + SMESH_CATCH( SMESH::throwCorbaException ); + #else THROW_SALOME_CORBA_EXCEPTION("CGNS library is unavailable", SALOME::INTERNAL_ERROR); #endif @@ -4359,7 +4381,7 @@ void SMESH_Mesh_i::ExportGMF(::SMESH::SMESH_IDSource_ptr meshPart, const char* file, bool withRequiredGroups) { - Unexpect aCatch(SALOME_SalomeException); + SMESH_TRY; if ( _preMeshInfo ) _preMeshInfo->FullLoadFromFile(); @@ -4372,6 +4394,8 @@ void SMESH_Mesh_i::ExportGMF(::SMESH::SMESH_IDSource_ptr meshPart, << meshPart<< ", r'" << file << "', " << withRequiredGroups << ")"; + + SMESH_CATCH( SMESH::throwCorbaException ); } //============================================================================= @@ -5526,7 +5550,7 @@ SMESH::smIdType_array* SMESH_Mesh_i::GetElementsByNodes(const SMESH::smIdType_ar std::vector elems; mesh->GetElementsByNodes( nn, elems, (SMDSAbs_ElementType) elemType ); result->length( elems.size() ); - for ( smIdType i = 0; i < elems.size(); ++i ) + for ( size_t i = 0; i < elems.size(); ++i ) result[i] = elems[i]->GetID(); } return result._retn(); -- 2.39.2