Hello,
I am facing an error while initializing an instance of AcDbDatabase class. I have tried creating a pointer instance as well but even that fails with the same exception (AccessViolationException)
I need to read AcDbDatabaseSummaryInfo using acdbGetSummaryInfo method. That requires database as a parameter. If there's some other method to get SummaryInfo of the file?
void ReadProperties(std::wstring filename)
{
std::wcout << "Starting to read database info" << std::endl;
AcDbDatabase database(Adesk::kFalse);
std::wcout << "empty database cretaed" << std::endl;
std::wcout << L"Method call end" << std::endl;
}
Following is the detailed exception,
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at AcDbDatabase.{ctor}(AcDbDatabase* , Boolean , Boolean )
at Bentley.RealDwgWrapperDotNet.RDwgExtractor.ReadProperties(basic_string<wchar_t\,std::char_traits<wchar_t>\,std::allocator<wchar_t> >* filename) in C:\Users\umar.aslam\Documents\Visual Studio 2022\Projects\RealDWGWrapper\RealDWGWrapper\RDwgExtractor.cpp:line 81
Any help in this regard?
Regards,
Umar
Solved! Go to Solution.
Solved by moogalm. Go to Solution.
You should not create an AcDbDatabase object on the stack. Always use new and delete:
void ReadProperties(std::wstring filename)
{
std::wcout << "Starting to read database info" << std::endl;
AcDbDatabase *database = new AcDbDatabase(Adesk::kFalse);
std::wcout << "empty database cretaed" << std::endl;
std::wcout << L"Method call end" << std::endl;
delete database;
}
I don't see any reason why db object shouldn't be created on stack.
int _tmain(int argc, TCHAR* argv[])
{
acdbSetHostApplicationServices(&gCreatentHostApp);
acdbValidateSetup(AcLocale(L"en", L"us"));
AcDbDatabase db(Adesk::kFalse);
db.readDwgFile(L"D:\\Temp\\oneRing.dwg");
acdbHostApplicationServices()->setWorkingDatabase(&db);
AcDbDatabaseSummaryInfo* info = nullptr;
acdbGetSummaryInfo(&db, info);
ACHAR* pPropertyString = NULL;
if (info != nullptr) {
info->getTitle(pPropertyString);
std::wcout << L"Title:"<<pPropertyString<< std::endl;
}
acdbCleanUp();
return 0;
}
Some more beautification -
template<typename ...Args>
void log(Args && ...args)
{
(std::wcout << ... << args);
}
#define GET_SUMMINFO_STRING(PROPERTY_NAME,strProp) { \
AcDbDatabaseSummaryInfo* pSummaryInfo = nullptr; \
if (!eOkVerify(acdbGetSummaryInfo(acdbHostApplicationServices()->workingDatabase(), pSummaryInfo))) \
return -1; \
ACHAR* pPropertyString = nullptr; \
if (!eOkVerify(pSummaryInfo->get##PROPERTY_NAME(pPropertyString))) \
return -1; \
if (pPropertyString != nullptr) { \
strProp = pPropertyString; \
log(#PROPERTY_NAME,L"-------------------------", strProp, L"\n"); \
delete pPropertyString; \
} \
}
int _tmain(int argc, TCHAR* argv[])
{
acdbSetHostApplicationServices(&gCreatentHostApp);
acdbValidateSetup(AcLocale(L"en", L"us"));
AcDbDatabase db(Adesk::kFalse);
db.readDwgFile(L"D:\\Temp\\oneRing.dwg");
acdbHostApplicationServices()->setWorkingDatabase(&db);
std::wstring strProp;
GET_SUMMINFO_STRING(Title, strProp);
GET_SUMMINFO_STRING(Subject, strProp);
GET_SUMMINFO_STRING(Comments, strProp);
GET_SUMMINFO_STRING(Author, strProp);
GET_SUMMINFO_STRING(Keywords, strProp);
GET_SUMMINFO_STRING(LastSavedBy, strProp);
GET_SUMMINFO_STRING(RevisionNumber, strProp);
GET_SUMMINFO_STRING(HyperlinkBase, strProp);
acdbCleanUp();
return 0;
}
I can't give you a profound explanation why you shouldn't create an AcDbDatabase on stack.
AcDbDatabase and AcDbObject are both derived from AcHeapOperators. This is what the ARX docs say:
"Objects that inherit from AcHeapOperators are guaranteed to be allocated on the AcDb heap. AcHeapOperators resolves memory allocation conflicts for objects in applications that are built with a version of the C runtime library that differs from the one used by AutoCAD's memory manager. "
A second argument might be that I haven't ever seen any ARX sample that created an AcDbDatabase on stack.
I assume that it leads to problems if you create any AcHeapOperators-derived object on the stack while it's memory is allocated on the AcDb heap. I would suggest that you simply try to use new and delete and see if it helps.
Well, the quote from the ARX doc is true in the past but doesn’t hold good anymore.
I had quick chat with one of Sr architects of AutoCAD what was the rationale behind the doc –
@tbrammer wrote:
I can't give you a profound explanation why you shouldn't create an AcDbDatabase on stack.
AcDbDatabase and AcDbObject are both derived from AcHeapOperators. This is what the ARX docs say:
"Objects that inherit from AcHeapOperators are guaranteed to be allocated on the AcDb heap. AcHeapOperators resolves memory allocation conflicts for objects in applications that are built with a version of the C runtime library that differs from the one used by AutoCAD's memory manager. "
In the past, we had issues with different compiler (really runtime) versions in ARX apps vs. AutoCAD. Using the AcHeapOperator made sure that allocation and deletion get to the same runtime.
but nowadays, we require that the ARX app is built with the same VC++ release as AutoCAD, so there is no longer any reason at all for that AcHeapOperators.
You need to create AcDbObjects on the heap if you want to hand their control over to AutoCAD for proper deallocation - that is, if your created AcDbObject's lifetime extends the lifetime of your stack.
I.E. if you return from your function, but the AcDbObject you created stays alive (for example, if you added it to the current drawing). Then, of course, you need to allocate it on the heap.
but for code like I have given in the post, the AcDb object's lifetime ends with the lifetime of the stack/function. In this case, it is valid and preferable to use the stack.
The AcHeapOperator made sure that if you create an AcDb object on the heap, it goes to the right memory pool. But it never was required for the AcDb object to be allocated on the heap. They could always be created on the stack.
And these days (the latest couple of AutoCAD releases), the AcHeapOperator just calls the CRT malloc() function - the same happens for objects that are not derived from AcHeapOperator. So there is no reason any longer for that AcHeapOperator. We just left it in the public API for compatibility reasons.
Thanks for the explanation, @moogalm. So you say that it is ok to create an AcDbDatabase on the stack?
In fact I tried @muaslam's code in my own RealDWG project and it ran smooth.
Do you have other ideas for the exception reported from @muaslam that seems to arise from the AcDbDatabase constructor?
Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at AcDbDatabase.{ctor}(AcDbDatabase* , Boolean , Boolean )
at Bentley.RealDwgWrapperDotNet.RDwgExtractor.ReadProperties(basic_string<wchar_t\,std::char_traits<wchar_t>\,std::allocator<wchar_t> >* filename) in C:\Users\umar.aslam\Documents\Visual Studio 2022\Projects\RealDWGWrapper\RealDWGWrapper\RDwgExtractor.cpp:line 81
We are talking about a RealDWG project here. It looks as if you are using RealDWG for MicroStation.
@muaslam are you developing Bentley.RealDwgWrapperDotNet yourself or do you use it as 3rd party product?
Maybe the provider Bentley can support you then.
Yes, if you would call new and delete on a given object in the same function, then don't use it at all, creating object on stack is more elegant.
I don't see enough information to comment on the exception, @muaslam if you can provide simple project that would be great to understand.
AccessViolationException are imposibble in managed code, this occurs only when a managed code is interacting with unmanaged c++ code, looks like this what is happening at your end.
Indeed I missed setting Host Application Services. And once, I set Host Application Services like done by moogalm, it works fine for me. I have accepted his answer as a solution.