| |
2009-05-05, 12:26 PM | #1 |
高级会员
注册日期: 06-11
帖子: 14579
精华: 1
现金: 224494 标准币
资产: 234494 标准币
|
【转帖】how do i create a link to an external database
how do i create a link to an external database?
how do i create a link to an external database? autocad can create links to records in external database tables (e.g. ms access) using odbc or ole db. how do i create such a link using opendwg? autocad links to external databases are stored in dwg file as xdata. if you know its structure you can add it "manually". dwgdirect has no high level api for it. sergey slezkin how to create object_ptr records? thanks, sergey. i do not have documentation on autocad's database link xdata, but i was able to create some test dxf files with autocad and decipher the necessary fields. unfortunately, in addition to xdata, database links also require object_ptr records. i can find no reference to those records in opendwg. when i ran odcopyex on a file containing them, the program deleted those records. autocad does not recognize database links without those records. can you tell me how to create object_ptr records with opendwg? dxf object_ptr object has class name casedlpntablerecord. include folder contains its header but dwgdirect reads in and saves this object as proxy object (does not delete it). this is because we noticed some problems with writing this object back to dwg file. sometimes if the object has some specific xdata for dco15 application autocad crashes if xdata was written to file unchanged. probably it contains some timestamps or check summs of something else. in other words this object is not implemented in dd. as a work around you can write your own implementation of this class - custom class derived from oddbobject, with class name casedlpntablerecord and dxf name object_ptr (see examples/excustobjs) but you may face the same problems we had... sergey slezkin thanks, sergey. for the sake of anyone else who might be trying to do the same thing i will mention that i could not create the c++ class casedlpntablerecord because opendwg already has a class of that name. i had to give it a different name. in order to make sure that the autocad classname was correct i had to create a modified version of the odrx_dxf_define_members macro that would allow me to specify one name for the c++ class and a different name for the autocad class. i have a question about the crash you were seeing. when i load a file containg db links into autocad 2002, then do some queries on the links, and finally exit autocad, sometimes i will get the following error: fatal error: unhandled access violation reading 0x0008 exception at 3004bed0h is that what you were seeing? i discovered i could get that same error without using opendwg to create the files. i created a small autocad file using only autocad 2002 and it would cause the crash, so i know opendwg is not to blame. i assume it is just a bug in autocad 2002. how do i read the xdata in an object_ptr? sergey, in addition to writing db links i also need to read them. i am running into a problem because the object_ptr has been converted to a proxy. i can convert that object to an oddbproxyextptr and can print out the originaldxfname of it (object_ptr), but how do i access the xdata in it? solved "how do i read the xdata in an object_ptr?" i solved the problem by creating the same casedlpntablerecordptr class that i created for writing. i cast the object to that class and now am able to access the xdata records. but xdata of oddbproxyobject can be accessed too since it's derived from oddbobject. sergey slezkin sergey, maybe i just don't know how to use oddbproxyobject. i have the following code: casedlpntablerecordcustomptr object = casedlpntablerecordcustom::cast( dictiter->getobject() ); it works fine. i changed it to: oddbproxyobjectptr object = oddbproxyobject::cast( dictiter->getobject() ); the cast returns a null object. what am i doing wrong? if casedlpntablerecordcustom class is not derived from oddbproxyobject the cast returns null. i suppose that it's derived from oddbobject. if so classes oddbproxyobject and casedlpntablerecordcustom are unrelated. if casedlpntablerecordcustom class is registered before database loading proxies representing this class (if they exist in the database) are converted to casedlpntablerecordcustom objects. sergey slezkin hi jkenwilliams (sorry i do not know your name), can you post the code snippet that uses the custom class to read and write database linkages (both from dxf and dwg). thanks a billion for any help on this. regards, saroja sample code to write external database linkages hi, saroja, here is the code to write linkages. it is the same for dxf and dwg, but i recommend you start with dxf so that you can examine the resulting file and compare it with what autocad produces. the code includes a few data types (like out_codes) and variables that are not defined in this snippet. i think you can probably figure out what you need without those definitions. i know the code will not compile as it is right now, since you don't have those definitions. ken williams /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% casedlpntablerecordcustom %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%% */ // the opendwg programmers found that autocad would sometimes crash if they // wrote records using the casedlpntablerecord class (in dxf it is called // object_ptr), so they disabled it. that is why i have to implement it here // as a custom class, because i cannot support db links without it. // the following is copied from excustobjs/excustobjexport.h #if defined(_msc_ver) #ifdef _toolkit_in_dll_ #ifdef excustobjs_dll_exports #define excustobjexport __declspec(dllexport) #else /* oda_gi_exports */ #define excustobjexport __declspec(dllimport) #endif /* oda_gi_exports */ #else /* _toolkit_in_dll_ */ #define excustobjexport #endif /* _toolkit_in_dll_ */ #else /* _msc_ver */ #define excustobjexport #endif /* _msc_ver */ // the following is adapted from excustobjs/excustobject.h & .cpp class excustobjexport casedlpntablerecordcustom : public oddbobject { public: // macro to declare oddb_declare_members(casedlpntablerecordcustom); casedlpntablerecordcustom() : oddbobject() { } virtual ~casedlpntablerecordcustom() { } }; typedef odsmartptr<casedlpntablerecordcustom> casedlpntablerecordcustomptr; // odrx_dxf_define_members_custom is a slightly modified version of odrx_dxf_define_members defined // in rxobject.h. i had to modify it because the class name that autocad must see is // casedlpntablerecord, but i cannot declare a c++ class of that name because opendwg already // defined it. #define odrx_dxf_define_members_custom(classname,acadclass name,parentclass,docreate,dwgver,maintver,nproxyfl ags,dxfname,appname)\ odrx_define_members(classname);\ odrxobjectptr classname:seudoconstructor() { odrxobjectptr pobj; docreate(classname, pobj); return pobj; }\ \ odrxobject* classname::queryx(const odrxclass* pclass) const\ { return :dqueryximpl<classname,parentclass>(this, pclass); }\ \ void classname::rxinit()\ {\ if (!classname::g_pdesc)\ classname::g_pdesc = ::newodrxclass(#acadclassname,parentclass::desc(), &pseudoconstructor,\ dwgver,maintver,nproxyflags,#dxfname,#appname);\ }\ \ void classname::rxuninit()\ {\ ::deleteodrxclass(g_pdesc);\ g_pdesc = 0;\ } odrx_dxf_define_members_custom(casedlpntablerecord custom, casedlpntablerecord, oddbobject, dbobject_constr, oddb::vac15, oddb::kmrelease0, oddbproxyentity::keraseallowed, object_ptr, ase-lpntablerecord) ***** after the odinitialize call, add this: casedlpntablerecordcustom::rxinit(); ***** at the end of the program, do this: casedlpntablerecordcustom::rxuninit(); oduninitialize(); // dwgdirect termination ***** when you want to add a db link, call this: /************************************************** *************************** * set a db link ************************************************** **************************/ static bool // out: true if success (currently always true) acadot_setdblink( oddbentityptr pentity, // in: entity to which db links will be attached out_codes* vcodes // in: acadot interface structure ) { odresbufptr temp; if( !opendwgstate.supportsappdco15 ) { opendwgstate.pdb->newregapp( db_application ); // add support for db application // create the two db dictionaries oddbdictionaryptr pmain = opendwgstate.pdb->getnamedobjectsdictionaryid().safeopenobject( oddb::kforwrite ); // create the dictionary that lists the data source, table name and link column name. opendwgstate.conldefdictionary = oddbdictionary::createobject(); // add new dictionary to the main dictionary. pmain->setat( "conldefdictionary", opendwgstate.conldefdictionary ); // create the dictionary that holds the db link records opendwgstate.aseindexdictionary = oddbdictionary::createobject(); pmain->setat( "ase_index_dictionary", opendwgstate.aseindexdictionary ); // add extended data fields odresbufptr xase = odresbuf::newrb( 1001 ); temp = xase; temp->setstring( db_application ); temp->setnext( odresbuf::newrb( 1040 ) ); temp = temp->next(); temp->setdouble( 0.0 ); // i don't know what this is for or if 0 is a valid value for it, but it appears to work ok. opendwgstate.aseindexdictionary->setxdata( xase ); char dbbasename[max_filespec]; filename_make( file_ret, vcodes->dbfilename.text, null, sizeof( dbbasename ), dbbasename ); opendwgstate.dbsourcename = dbbasename; opendwgstate.dbsourcename += "..."; opendwgstate.supportsappdco15 = true; } odstring linktemplatename = vcodes->dbtablename.text; linktemplatename += db_link_template; oddbxrecordptr tablexrecord = opendwgstate.asexrecords[ vcodes->dbtablename.text ]; if( tablexrecord.get() == null ) { // first time we have used this table. create the object_ptr that defines the link template. // note: i don't know what all of these fields mean. i discovered them by looking at a dxf // file i created in autocad. casedlpntablerecordcustomptr object_ptr = casedlpntablerecordcustom::createobject(); opendwgstate.conldefdictionary->setat( linktemplatename, object_ptr ); odresbufptr object_ptrres = odresbuf::newrb( 1001 ); temp = object_ptrres; temp->setstring( db_application ); temp->setnext( odresbuf::newrb( 1000 ) ); temp = temp->next(); temp->setstring( opendwgstate.dbsourcename + vcodes->dbtablename.text + "(" + linktemplatename + ")" ); temp->setnext( odresbuf::newrb( 1000 ) ); temp = temp->next(); temp->setstring( vcodes->dblinkcol.text ); temp->setnext( odresbuf::newrb( 1000 ) ); temp = temp->next(); temp->setstring( "" ); temp->setnext( odresbuf::newrb( 1000 ) ); temp = temp->next(); temp->setstring( "" ); temp->setnext( odresbuf::newrb( 1070 ) ); temp = temp->next(); temp->setint16( 4 ); temp->setnext( odresbuf::newrb( 1070 ) ); temp = temp->next(); temp->setint16( 0 ); temp->setnext( odresbuf::newrb( 1070 ) ); temp = temp->next(); temp->setint16( 0 ); temp->setnext( odresbuf::newrb( 1070 ) ); temp = temp->next(); temp->setint16( 0 ); temp->setnext( odresbuf::newrb( 1070 ) ); temp = temp->next(); temp->setint16( 1024 ); object_ptr->setxdata( object_ptrres ); // create the xrecord that will list the link records for this table tablexrecord = oddbxrecord::createobject(); tablexrecord->setmergestyle( oddb::kdrcignore ); opendwgstate.asexrecords[ vcodes->dbtablename.text ] = tablexrecord; oddbobjectid xrid = opendwgstate.aseindexdictionary->setat( linktemplatename, tablexrecord ); } // create extended data that references the db link record // (i determined the fields needed by creating a db link in autocad and // looking at the resulting dxf records. i don't know what they all mean.) odresbufptr xiter = odresbuf::newrb(1001); temp = xiter; temp->setstring(db_application); temp->setnext( odresbuf::newrb( 1071 ) ); temp = temp->next(); temp->setint32( 1 ); temp->setnext( odresbuf::newrb( 1071 ) ); temp = temp->next(); temp->setint32( 1 ); temp->setnext( odresbuf::newrb( 1000 ) ); temp = temp->next(); temp->setstring( linktemplatename ); temp->setnext( odresbuf::newrb( 1004 ) ); temp = temp->next(); odbinarydata binarylink; binarylink.resize( sizeof( odint32 ) ); *( (odint32*)binarylink.getptr() ) = vcodes->dblink; temp->setbinarychunk( binarylink ); pentity->setxdata(xiter); // create link information and add it to the xrecord odresbufptr xlink = odresbuf::newrb( 330 ); temp = xlink; temp->sethandle( pentity->getdbhandle() ); temp->setnext( odresbuf::newrb( 1004 ) ); temp = temp->next(); binarylink.resize( sizeof( odint32 ) * 4 ); odint32* binarydblinkdata = (odint32*)binarylink.getptr(); binarydblinkdata[0] = vcodes->dblink; // i don't know what the next 3 numbers mean, but in my tests autocad always gave them these values. binarydblinkdata[1] = 0; binarydblinkdata[2] = 0xffffffff; binarydblinkdata[3] = 0xffff0001; temp->setbinarychunk( binarylink ); tablexrecord->appendrbchain( xlink ); return true; } sample code to read external database linkages here is sample code for reading external database linkages. (i should mention that you need the db_application defined for writing as well. i forgot to include it with that sample.) #define db_application "dco15" // autocad's name for the database application the casedlpntablerecordcustom definition is the same as for writing files. (see other post.) read the link templates: /************************************************** ************************ * retrieve information about all link templates in the file ************************************************** *************************/ static void acadin_processlinktemplates() { opendwgstate.linktemplatesprocessed = true; // open main dictionary oddbdictionaryptr pmain = opendwgstate.pdb->getnamedobjectsdictionaryid().safeopenobject( oddb::kforread ); // get the dictionary containing the link templates oddbdictionaryptr linktemplatedict = (oddbdictionaryptr)pmain->getat( "conldefdictionary", oddb::kforread ); if( linktemplatedict.isnull() ) { ... set error code ... return; } // each object in this dictionary is a link template definition oddbdictionaryiteratorptr dictiter = linktemplatedict->newiterator(); for( ; !dictiter->done(); dictiter->next() ) { linktemplateinfo linktemplateinfo; linktemplateinfo.m_validlink = true; linktemplateinfo.m_errorcode = 0; casedlpntablerecordcustomptr object = casedlpntablerecordcustom::cast( dictiter->getobject() ); odresbufptr resbuf = object->xdata( db_application ); int stringnumber = 0; while( !resbuf.isnull() ) { if( resbuf->restype() == 1000 ) { stringnumber += 1; odstring string = resbuf->getstring(); switch( stringnumber ) { default: break; case 1: // should look like: "db_name...table_name(link_template_name)" { int tablenamepos = string.find( '.' ); if( tablenamepos < 0 ) { // cannot parse. linktemplateinfo.m_validlink = false; linktemplateinfo.m_tablename = ""; } else { // table name begins after 3 dots tablenamepos += 3; int parenpos = string.find( '(' ); if( parenpos < tablenamepos ) { // should never get here, but just in case ... linktemplateinfo.m_tablename = string.mid( tablenamepos ); } else { linktemplateinfo.m_tablename = string.mid( tablenamepos, parenpos - tablenamepos ); } } } break; case 2: linktemplateinfo.m_columnname = string; break; case 3: // i don't know what this string is for. for an integer column it is blank. // for an ascii column it is "1252". ignore it. break; case 4: // must be blank for an integer column if( linktemplateinfo.m_validlink && string != "" ) { linktemplateinfo.m_validlink = false; } break; case 5: // this is the name of the second link column (which i am not handling) if( linktemplateinfo.m_validlink ) { linktemplateinfo.m_validlink = false; } break; } } resbuf = resbuf->next(); } // end while opendwgstate.linktemplatehash[ dictiter->name().c_str() ] = linktemplateinfo; } } for each entity that has a db link: // db links odresbufptr resbuf = pent->xdata( db_application ); dblink dblink; while( !resbuf.isnull() ) { if( resbuf->restype() == 1000 ) { dblink.linktemplatename = resbuf->getstring(); if( !resbuf.isnull() ) resbuf = resbuf->next(); if( !resbuf.isnull() && resbuf->restype() == 1004 ) { odbinarydata binarylink = resbuf->getbinarychunk(); switch( binarylink.size() ) { default: // unsupported size (wrong column type) dblink.linkid = -1; break; case 2: // i have never seen a two-byte link, but just in case ... dblink.linkid = *(odint16*)binarylink.getptr(); break; case 4: dblink.linkid = *(odint32*)binarylink.getptr(); break; } acadin_dblinks.push_back( dblink ); } } if( !resbuf.isnull() ) resbuf = resbuf->next(); } // end while } thanks. it works.. ken, thanks a million - this is exactly what i needed. redefining the macro did the trick. regards, saroja what s hould we give these values "binarydblinkdata[2] = 0xffffffff" and "binarydblinkdata[3] = 0xffff0001" if we are handling text. thanks ramu |
GDT自动化论坛(仅游客可见) |
主题工具 | 搜索本主题 |
显示模式 | |
|
|
相似的主题 | ||||
主题 | 主题发起者 | 论坛 | 回复 | 最后发表 |
【转帖】headers and compiler configuration | yang686526 | DirectDWG | 0 | 2009-05-05 12:03 PM |
【转帖】dwf7import link error | yang686526 | DirectDWG | 0 | 2009-05-05 07:56 AM |
【转帖】adt static link settings | yang686526 | DirectDWG | 0 | 2009-05-04 03:50 PM |
【转帖】2005 link error | yang686526 | DirectDWG | 0 | 2009-05-04 02:57 PM |
【转帖】1.13 hostapp systemservices problems | yang686526 | DirectDWG | 0 | 2009-05-04 02:48 PM |