MS Exchangeメヌルデヌタベヌスのビュヌアの䜜成パヌト1



ある倧きな、倧きな囜では、小さくおかわいい人が䜏んでいたした。 そしお、この倧きな、たたはかなり倧きな囜の真ん䞭に深く深い穎が珟れるたで、すべおがうたくいきたした。 さお、私は蚀わなければならない、圌女は䞀人ではなく、そしおもちろん、すぐには珟れなかったが、誰もこれをもう芚えおいないだろうし、これはもはや誰にずっおも重芁ではない。



䞻なものはピットであり、それは倧きく、非垞に倧きく、むしろ無限であり、より正確には誰も知らなかった。 しかし、倧きな囜の政府は、ピットで䜕かをしなければならないこずを知っおいお、勉匷しお眠りに぀くこずに決めたした。 この矎しい状態にあった人たちの䞭で最も嚁圧的で噚甚で勇気があり、賢くお最も賢い人は䜕ず呌ばれおいたしたか。



そしお、最も予蚀的なセット。 圌らは勉匷し、眠りに萜ち、眠りに萜ち、勉匷し、勉匷し、眠りに萜ち、眠りに萜ち、勉匷したした。そしお、これは䜕床も繰り返されたせんでした。



私たちのほずんどのほずんどのヒヌロヌが去ったか、むしろ圌らは去らなければならなかったので、ピットが残っおいたした。 しかし、新しいヒヌロヌは、同じくらい小さく、同じで、たったく同じで、同じこずを始めたした。



䞀般に、これは1぀が残っおいる限り続きたした。 そしお、ある晎れた日、ピット自䜓は姿を消し、たったく別の堎所のどこかに珟れたした。そこで圌らは再び勉匷し、埋め始めたした...



すべおの偶然はランダムであり、すべおが架空のものであり、そのようなものはか぀おなかったため、珟実にはありえたせんでした 真実、真実、たあ、たたはほずんど真実。



そのようなピットの1぀に぀いおは、埌で説明したす。 しかし、以来 プラむバシヌポリシヌでは、その䞀郚のみを察象ずしたす。これには、 オヌプン゜ヌスず文曞化された APIのみを䜿甚しおアクセスできたす。



材料はほずんど排他的です 穀物のトピックに関する情報。



MS Exchange Serverがすべおのメヌルを保存する堎所を考えたこずがありたすか、それずも最䞋䜍レベルでどのように動䜜したすか これに぀いお少し説明したすので、ここに曞きたす。



譊告このトピックに深く入ろうずしないでください。あなたの人生は十分ではありたせん。 譊告した。



はじめに



MS Exchange Server以䞋、単にExchangeは、Microsoft補品ラむンの䞻芁補品の1぀です。 䞻な機胜に぀いおは、 wikiたたは公匏Webサむトで読むこずができたす。 芁するに、これはメヌル、カレンダヌ、その他のナヌザヌデヌタを操䜜するための䞀皮の「ハヌベスタヌ」であり、さたざたなMS補品SharePoint、TFSなどず十分に統合されおいたす。



ただし、この蚘事のフレヌムワヌクでは、゚ンドナヌザヌに提䟛するものではなく、このデヌタをどこから取埗し、どのAPIを䜿甚するかに関心がありたす。 Exchange 2010 メヌルボックスサヌバヌ のメヌルボックスの圹割でメヌルボックスデヌタベヌスを個別に読み取ろうずしたす。



Exchangeには、ナヌザヌがデヌタにアクセスできるいく぀かの゚ントリポむント CASサヌバヌ ず、これに䜿甚できるいく぀かのプロトコルOWA、RPCOutlook、POP3 / IMAP4などがありたす。



方法に関係なく、Exchangeぞのアクセスを取埗するず、すべおの芁求がメヌルボックスの圹割 Exchange 2007より前はこれが唯䞀の圹割でしたに転送されたす。 物理的に、これらのデヌタベヌスは* .edbファむル内のハヌドディスクにありたす。 これらは、ExchangeがむンストヌルされたディレクトリのMailbox \ <デヌタベヌス名>フォルダヌにありたす。 たた、デヌタベヌスのラむフサむクルに関連するトランザクションログやその他のファむルがそこに配眮されたすが、それらは必芁ありたせん。最も基本的なものは* .edbです。



少し掘り䞋げるず、ExchangeはExtensible Storage EngineESEを䜿甚しおデヌタベヌスのコンテンツにアクセスしおいるこずがわかりたす。 そしお、掘り䞋げるず、ESE関数の実装がラむブラリese.dllたたはesent.dllにあるこずが明らかになりたす。 これは、 すべおの Exchange 操䜜の䞭栞です 。 ESEは、デヌタベヌスを操䜜するための広範なツヌルセットを提䟛したす。 関数、定数、構造、および必芁なすべおの説明は、 ここにありたす 。 残念ながら、このドキュメントは長い間曎新されおいないため、Exchange 2010に登堎した機胜は倚くありたせんが、このトピックでは必芁ありたせん。 ese.dllは、Exchangeメむンディレクトリ内のBinフォルダヌにありたす。



ESEの説明を読んだ埌、デヌタが衚圢匏で保存されおいるこずが明らかになりたす。 列ず行を持぀テヌブルのセット。 テヌブルセルは、さたざたな皮類のほか、ベヌスのすべおの機胜むンデックス、怜玢などを䜿甚できたす。



合蚈で、Exchangeはデヌタベヌスを.edb拡匵子を持぀ファむルの圢匏でメヌルボックスの圹割に栌玍し、ESEese.dllのおかげでそれらにアクセスするこずを知っおいたす。 これで十分であり、コヌディングを開始できたす。



デヌタベヌス内のテヌブル、およびすべおの列ず列をリストしたす。 もちろん、それらが䜕を意味するのかは決しおわかりたせん。 このこずを知っおいるのはMSだけです。 これらの列には、ナヌザヌのフォルダヌ名から文字で終わるメヌルボックスに関連するほがすべおの情報が含たれおいるはずですが、リバヌス゚ンゞニアリングのタスクが既にどこにあるかを理解するこずはここでは考慮されたせん。



プログラミング



準備する


最初に必芁なもの

Exchangeで、ベヌスを䜜成し、それを読み取ろうずしたす。 これを行うには、 Exchange管理コン゜ヌル  EMC を䜿甚できたす。 手順に぀いおは説明したせん。 このトピックに関する倚くの情報がむンタヌネット䞊にありたす。 このデヌタベヌスに1人のナヌザヌを同じEMCを介しお䜜成しお、コンテンツが含たれ、このナヌザヌがこのメヌルボックスにログむンしお、たずえばOWAを介しおすべおが正しく行われおいるこずを確認したす。 その埌、メヌルボックスディレクトリに移動し、デヌタベヌスの名前のフォルダヌずその䞭のEDBファむルを探したす。 ベヌスをコピヌする前に、EMCを䜿甚しおアンマりントしたす。 すべお、実隓の基瀎がありたす。 たずえば、将来のプロゞェクトのディレクトリにコピヌしたす。



Binフォルダヌからese.dllをコピヌしたす。これにより、デヌタベヌスを操䜜できたす。



Visual Studioでは、コン゜ヌルC ++プロゞェクトを䜜成したす。 ここに重芁なニュアンスがありたす Exchange 2010以前のすべおのバヌゞョンずは異なりは64ビットバヌゞョンのみであるため、x64をサポヌトするプロゞェクトを䜜成する必芁がありたす。 そうしないず、単にアドレススペヌスにese.dllをロヌドできたせん。 したがっお、アプリケヌションをテストするには、64ビットバヌゞョンのOSが必芁であり、Exchange自䜓で確実にテストできたすが、この目的のためにWindows 7でワヌクステヌションを䜿甚したす。たた、UnicodeバヌゞョンのAPIを䜿甚したす。



したがっお、新しく䜜成されたプロゞェクトでは、x64およびUnicode䞀般-Unicode文字セットを䜿甚がサポヌトされおいるこずを確認したす。 次に、ESEのメむンヘッダヌファむルを接続したす。

#include <esent.h>
このファむルには、VS 2008以降のスタゞオず共にSDKが付属しおいたす。

stdafx.hで、JETバヌゞョンESEで2぀の定矩を远加し、ナニコヌドバヌゞョンのAPIを䜿甚するこずを瀺したす。

#define JET_UNICODE

#define JET_VERSION 0x0600


さお、デヌタベヌスから䜕を取埗したいかを決定する必芁がありたす。 ESEは、テヌブル、列、および行を持぀デヌタベヌスであり、これから抜出しようずするものがたさにテヌブル、列、および行です。 これを行うには、次の構造を準備したす。

typedef struct tagDBColumnsInfo

{

std :: wstring sColumnName ;

std :: vector < std :: wstring > sColumnValues ;

} SDBColumnInfo ;



typedef struct tagDBTableInfo

{

std :: wstring sTableName ;

std :: vector < SDBColumnInfo > sColumnInfo ;

} SDBTableInfo ;



typedef struct tagDBTablesInfo

{

std :: wstring sDBName ;

std :: vector < SDBTableInfo > sTablesInfo ;

} SDBTablesInfo ;


最初に行うこずは、DLL自䜓をロヌドするこずです。これは、通垞どおり:: LoadLibrary...を䜿甚しお行いたす。

ese.dllから関数を動的にロヌドし、次の関数が必芁になりたす。



ベヌス開口郚


必芁な関数を正垞にロヌドしたら、デヌタベヌスの盎接読み取りを開始したす。 MSDNによるず、 JET_paramDatabasePageSize esent.hパラメヌタヌを蚭定しお、 デヌタベヌスのペヌゞサむズを指定する必芁がありたす。 これが問題の出番です。 EDBファむルのみを持぀この倀を芋぀けるこずはできたせんが、デヌタベヌスを開かない堎合は正確に指定する必芁がありたす。 これはeseutilsExchangeに含たれおいたすを䜿甚しお実行できたすが、少し異なるパスを䜿甚しお、この倀は同じバヌゞョンのExchangeで䞀定であり、垞に4096の倍数であるこずがわかりたした。Exchange2010では32768であるこずが実隓的にわかりたした



たず、ペヌゞサむズを蚭定したす。

JET_ERR jRes = _JetSetSystemParameter  NULL 、 NULL 、JET_paramDatabasePageSize、 32768 、 NULL  ;




JET_ERRは、゚ラヌコヌドを含む単なるlongです。 JetGetSystemParameter関数ala :: FormatMessage...を䜿甚しお、このコヌドをテキスト蚘述に倉換できたす。

JetGetSystemParameter  m_instance、m_sesid、JET_paramErrorToString、

reinterpret_cast < JET_API_PTR * >   jeterror  、cBuff、MAX_BUFFER_SIZE  ;


゚ラヌコヌドの解析の䟿宜䞊、次のマクロを䜿甚したすm_cLogは私の内郚ログクラスです。

#define WRITE_TO_LOG_AND_RETURN_IF_ERRORjeterror\

ifjeterror{\

char cBuff [MAX_BUFFER_SIZE] = {0}; \

ifm_instance_JetGetSystemParameterm_instance、m_sesid、\

JET_paramErrorToString、reinterpret_cast <JET_API_PTR *>jeterror、cBuff、MAX_BUFFER_SIZE; \

m_cLog.writem_sEDBPath、cBuff、jeterror、__ FILE __、__ LINE__; \

return jeterror; }




次に、Exchange固有のコヌルバックを無効にする必芁がありたす。 それらに぀いおは䜕も知りたせん

jRes = _JetSetSystemParameter  NULL 、 NULL 、JET_paramDisableCallbacks、 true 、 NULL  ;




次に、デヌタベヌスを操䜜するための新しいむンスタンス JET_INSTANCE m_instanceを䜜成したす。

jRes = _JetCreateInstance   m_instance、 NULL  ;




䜜成されたむンスタンスを初期化しお、デヌタベヌスの操䜜を開始したす。

jRes = _JetInit   m_instance  ;




新しいセッションの開始JET_SESID m_sesid

jRes = _JetBeginSession  m_instance、  m_sesid、 NULL 、 NULL  ;




EDBファむルを接続したす。

jRes = _JetAttachDatabase  m_sesid、L "demo.edb" 、JET_bitDbReadOnly  ;




そしおそれを開きたす

jRes = _JetOpenDatabase  m_sesid、L "demo.edb" 、 NULL 、  m_dbid、JET_bitDbReadOnly  ;




合蚈。すべおの関数がJET_errSuccessを返した堎合、デヌタベヌスは開いおいたす。぀たり、コンテンツの読み取りを開始できたす。



次はコヌドです。 持っおくるから この件に぀いおは、午埌に火が぀いおいる圌を芋぀けるこずはできたせん。



リスト衚


列挙するには、次の関数を䜜成したす。

JET_ERR CJetDBReaderCore :: EnumRootTables  SDBTablesInfo および sDBTablesInfo 

{

sDBTablesInfo。 sDBName = m_sEDBPath ;

JET_ERR jRes = OpenTable  ROOT_TABLE  ;

if  jRes == JET_errSuccess 

{

JET_COLUMNBASE sNameInfo、

sTypeInfo ;

if   ReadFromTable  ROOT_TABLE、NAME_COLUMN、sNameInfo  &&

 ReadFromTable  ROOT_TABLE、TYPE_COLUMN、sTypeInfo  

{

JET_RETRIEVECOLUMN sJetRC [ 2 ] ;

sJetRC [ 0 ] columnid = sNameInfo。 columnid ;

sJetRC [ 0 ] cbData = sNameInfo。 cbMax ;

sJetRC [ 0 ] itagSequence = 1 ;

sJetRC [ 0 ] grbit = 0 ;

CHAR szName [ MAX_BUFFER_SIZE ] ;

sJetRC [ 0 ] pvData = szName ;



sJetRC [ 1 ] 。 columnid = sTypeInfo。 columnid ;

sJetRC [ 1 ] 。 cbData = sTypeInfo。 cbMax ;

sJetRC [ 1 ] 。 itagSequence = 1 ;

sJetRC [ 1 ] 。 grbit = 0 ;

WORD wType ;

sJetRC [ 1 ] 。 pvData =  wType ;



する

{

jRes = GetColumns  ROOT_TABLE、sJetRC、 2  ;

if  jRes  = JET_errSuccess  return jRes ;

if  wType == 1 

{

szName [ sJetRC [ 0 ] 。 cbActual ] = 0 ;



SDBTableInfo sTableInfo ;

std :: string tmp  szName  ;

sTableInfo。 sTableName assign  tmp。begin   、tmp.end    ;



sDBTablesInfo。 sTablesInfo 。 push_back  sTableInfo  ;

}



} while   TableEnd  ROOT_TABLE   ;

}



jRes = CloseTable  ROOT_TABLE  ;

}



return jRes ;

}


どこで



コヌドでわかるように、最初にルヌトテヌブルを開きたす。これは、 JetOpenTable関数を䜿甚しお行いたす。

JET_ERR CJetDBReaderCore :: OpenTable  std :: wstring sTableName 

{

std :: map < std :: wstring 、JET_TABLEID > :: const_iterator iter = m_tables。 怜玢  sTableName  ;

if  iter == m_tables。end   

{

JET_TABLEID tableid  0  ;

JET_ERR jRes = _JetOpenTable  m_sesid、m_dbid、 sTableName。C_str   、 NULL 、

0 、JET_bitTableReadOnly、  tableid  ;

WRITE_TO_LOG_AND_RETURN_IF_ERROR_2  jRes 



m_tables [ sTableName ] = tableid ;

}



return JET_errSuccess ;

}


次に、ReadFromTable内の列に関する情報を取埗したす。 コンテンツを取埗するには圌女のIDが必芁です。

JET_ERR CJetDBReaderCore :: ReadFromTable 

std :: wstring sTableName、

std :: wstring sColumnName、

JET_COLUMNBASE  sColumnBase 

{

std :: map < std :: wstring 、JET_TABLEID > :: const_iterator iter = m_tables。 怜玢  sTableName  ;

if  iter  = m_tables。end   

{

JET_ERR jRes = _JetGetColumnInfo  m_sesid、m_dbid、 sTableName。C_str   、

sColumnName。 c_str   、  sColumnBase、 sizeof  JET_COLUMNBASE  、JET_ColInfoBase  ;

WRITE_TO_LOG_AND_RETURN_IF_ERROR_2  jRes 

}



return JET_errSuccess ;

}


IDをJET_RETRIEVECOLUMN構造䜓に入力し、GetColumns内でJetRetrieveColumnsを実行しおテヌブル名を取埗したす。

JET_ERR CJetDBReaderCore :: GetColumns 

std :: wstring sTableName、

JET_RETRIEVECOLUMN * sJetRC、

INT nCount 

{

std :: map < std :: wstring 、JET_TABLEID > :: const_iterator iter = m_tables。 怜玢  sTableName  ;

if  iter  = m_tables。end   

{

JET_ERR jRes = _JetRetrieveColumns  m_sesid、iter- > second、sJetRC、nCount  ;

WRITE_TO_LOG_AND_RETURN_IF_ERROR_2  jRes 

}



return JET_errSuccess ;

}


これでテヌブルのリストが取埗できたした。次に列のコンテンツの取埗に進みたしょう。 各テヌブルに぀いお、その䞭の列のリストを受け取り、受け取ったずきに、この情報を構造に保存したす。



列をリストしたす


次の関数を䜜成したす。

JET_ERR CJetDBReaderCore :: EnumColumns 

SDBTableInfo  sTableInfo、

std :: list < SColumnInfo >  sColumnsInfo 

{

if   OpenTable  sTableInfo。sTableName  

{

JET_COLUMNLIST sColumnInfo ;

GetTableColumnInfo  sTableInfo。STableName、  sColumnInfo  ;

MoveToFirst  sTableInfo。STableName  ;



char szNameBuff [ MAX_BUFFER_SIZE ] ;

する

{

SColumnInfo ci ;

JET_RETRIEVECOLUMN sJetRC [ 4 ] ;



sJetRC [ 0 ] columnid = sColumnInfo。 columnidcolumnname ;

sJetRC [ 0 ] cbData = sizeof  szNameBuff  ;

sJetRC [ 0 ] itagSequence = 1 ;

sJetRC [ 0 ] grbit = 0 ;

sJetRC [ 0 ] pvData = szNameBuff ;



sJetRC [ 1 ] 。 columnid = sColumnInfo。 columnidcolumnid ;

sJetRC [ 1 ] 。 cbData = sizeof  DWORD  ;

sJetRC [ 1 ] 。 itagSequence = 1 ;

sJetRC [ 1 ] 。 grbit = 0 ;

sJetRC [ 1 ] 。 pvData =  ci。 dwId ;



sJetRC [ 2 ] 。 columnid = sColumnInfo。 columnidcoltyp ;

sJetRC [ 2 ] 。 cbData = sizeof  DWORD  ;

sJetRC [ 2 ] 。 itagSequence = 1 ;

sJetRC [ 2 ] 。 grbit = 0 ;

sJetRC [ 2 ] 。 pvData =  ci。 dwType ;



sJetRC [ 3 ] 。 columnid = sColumnInfo。 columnidcbMax ;

sJetRC [ 3 ] 。 cbData = sizeof  DWORD  ;

sJetRC [ 3 ] 。 itagSequence = 1 ;

sJetRC [ 3 ] 。 grbit = 0 ;

sJetRC [ 3 ] 。 pvData =  ci。 dwMaxSize ;



GetColumns  sTableInfo。STableName、sJetRC、 4  ;



ci。 sName 。 assign  reinterpret_cast < wchar_t * >  sJetRC [ 0 ] .pvData  、sJetRC [ 0 ] .cbActual / 2  ;



SDBColumnInfo sDBColumnInfo ;

sDBColumnInfo。 sColumnName = ci。 sName ;



sColumnsInfo。 push_back  ci  ;

sTableInfo。 sColumnInfo 。 push_back  sDBColumnInfo  ;

}

while   TableEnd  sTableInfo.sTableName   ;



CloseTable  sTableInfo。STableName  ;

}



return JET_errSuccess ;

}


ここで、テヌブルを再び開きたすが、ルヌトではなく、前の手順で芋぀けたテヌブルを開きたす。



次に、すべおの列に関する情報を取埗する必芁がありたす。このため、最初のポむンタヌを取埗し、最埌の列に1぀ず぀移動したす。

JET_ERR CJetDBReaderCore :: MoveToFirst  std :: wstring sTableName 

{

std :: map < std :: wstring 、JET_TABLEID > :: const_iterator iter = m_tables。 怜玢  sTableName  ;

if  iter  = m_tables。end    //既に開いおいる堎合

{

JET_ERR jRes = _JetMove  m_sesid、iter- > second、JET_MoveFirst、 0  ;

BOOL bIsEmpty =  jRes == JET_errNoCurrentRecord  ;

if  bIsEmpty  jResを返したす。 //空の堎合は無芖したす

WRITE_TO_LOG_AND_RETURN_IF_ERROR_2  jRes  ;

}



NO_ERRORを返したす。

}


JET_ERR CJetDBReaderCore :: GetTableColumnInfo 

std :: wstring sTableName、

JET_COLUMNLIST * pCl、

BOOL bReplaceOld 

{

JET_ERR jRes = JET_errSuccess ;

std :: map < std :: wstring 、JET_TABLEID > :: むテレヌタ iter = m_tables。 怜玢  sTableName  ;

if  iter  = m_tables。end   

{

jRes = _JetGetTableColumnInfo  m_sesid、iter- > second、 NULL 、pCl、

sizeof  JET_COLUMNLIST  、JET_ColInfoList  ;

WRITE_TO_LOG_AND_RETURN_IF_ERROR_2  jRes 



if  bReplaceOld  //最埌にテヌブルを開く必芁がない堎合

{

jRes = CloseTable  sTableName  ;

m_tables [ sTableName ] = pCl- > tableid ;

}

他に

{

jRes = _JetCloseTable  m_sesid、pCl- > tableid  ;

WRITE_TO_LOG_AND_RETURN_IF_ERROR_2  jRes 

}

}



return jRes ;

}


申し蚳ありたせんが、投皿の最初のバヌゞョンは切断され、私は時間内に応答するこずができたせんでした。明らかに、蚘事のサむズには䜕らかの皮類の制限があるため、2぀の郚分に分割されたす。

継続するには...



次のパヌトでは、ベヌスを読んで結論を導き、ベヌスから「匕き裂かれた」可胜性のあるデヌタの䟋を芋おいきたす。



PSこのコヌドは、投皿甚に倉曎されたバヌゞョンです。 したがっお、コヌドにはいく぀かの欠陥があり、機胜が切り詰められおいる堎所にはギャグがありたす。 それらに泚意を払っおはいけたせん、これは生産ではありたせんが、 実際の䟋を瀺したかったのです。 コヌドは完党に機胜しおおり、むンタヌネット䞊に配眮できるず同時に、ペヌゞ䞊のすべおのスペヌスを「食べない」ように蚘述されおいたす。 ご理解いただきありがずうございたす。



PPS私は、詳现のために、この情報が広範囲の人々にずっお有甚であるずは思わないが、それが1人でも圹立぀ならば、私はうれしいです、そしお、このポストに費やされた時間は報われるでしょう。



関連リンク
  1. MSDNのExtensible Storage Engine
  2. ロシア語の蚘事 ESE APIの䜿甚に関するRuNetの蚘事はほが唯䞀
  3. ESE機胜の䟋



All Articles