logo

English

이곳의 프로그래밍관련 정보와 소스는 마음대로 활용하셔도 좋습니다. 다만 쓰시기 전에 통보 정도는 해주시는 것이 예의 일것 같습니다. 질문이나 오류 수정은 siseong@gmail.com 으로 주세요. 감사합니다.

VC++(MFC)에서 MDB 생성 / 압축 / 연동관리자

by digipine posted Oct 29, 2017
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print
?

Shortcut

PrevPrev Article

NextNext Article

Larger Font Smaller Font Up Down Go comment Print

□ 개 요

○ Access로 생성하는 DB 파일인 MDB 파일의 생성과 압축 및 기본 DB연동(데이터의 추가, 삭제, 검색, 수정)에 대한 내용

○ 다이얼로그 기반의 MFC 프로젝트 상에서 위의 작업들을 수행하는 CDataBase라는 사용자 정의 클래스를 만들고 구현

 

□ 준비 작업

○ stdafx.h에 다음을 import 한다.

 

/* DB 사용을 위한 설정*/

#import "C:\Program Files\Common Files\System\ADO\msado15.dll" rename("EOF", "EndOfFile")

 

/* DB 파일 압축을 위한 설정*/

#import "C:\Program Files\Common Files\System\ado\msjro.dll" no_namespace

 

/* DB 파일 생성을 위한 설정*/

#import "c:\Program Files\Common Files\system\ado\msadox.dll"

using namespace ADODB;

using namespace ADOX;

 

※ ADO(msado15.dll) 자체에서는 DB 파일 생성과 압축에 대한 라이브러리가 없기 때문에 각각에 필요한 DLL 파일을 Import 해야 한다. 하지만 ADOX(msadox.dll)와 단순히 같이 Import하면 충돌하기 때문에 서로 namespace를 지정해 준다. 또한 인터넷에 돌아다니는 일부 코드는 msadox.dll를 Import할 때 rename 혹은 no_namespace 옵션을 주곤 하는데 이를 위와 같이 해제한다.

 

○ 초기화 작업을 수행한다.

=> 프로젝트의 메인파일 (TestDlg.cpp)의 BOOL CTestDlgAPP::InitInstance()에 다음 내용을 추가

 

/* Ole 컨트롤의 지원을 위한 작업을 가능하게 함*/

AfxEnableControlContainer();

if (!AfxOleInit())

{

    return FALSE;

}

 

=> 또한 DB를 실제적으로 사용 / 관리하는 CDataBase 클래스의 생성자와 소멸자에 다음 내용을 추가

 

CDataBase::CDataBase(void)

{

    ...(생략)...

    /* OLE를 초기화하고 DLL을 셋업*/

    ::CoInitialize(NULL);

    ...(생략)...

}

 

CDataBase::~CDataBase(void)

{

    ...(생략)...

    ::CoUninitialize();

    ...(생략)...

}

 

 

 

 

□ ADOX를 이용한 MDB 파일 생성 (msadox.dll)

○ 준비 작업

 

/* Catalog(Database), Table, Column의 Object에 대한 ADOX Object 포인터 정의*/

ADOX::_CatalogPtr m_pCatalog = NULL;

ADOX::_TablePtr m_pTable = NULL;

ADOX::_ColumnPtr m_pColumn = NULL;

ADOX::_IndexPtr m_pIndex = NULL;

 

/* 각 Object에 대한 Instance 생성*/

m_pCatalog.CreateInstance(__uuidof (Catalog));

m_pTable.CreateInstance(__uuidof (Table));

m_pIndex.CreateInstance(__uuidof(Index));

 

/* Database 생성*/

m_pCatalog->Create((const char *)(CString("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb"));

 

※ ADOX::_CatalogPtr 등과 같이 네임스페이스를 정확히 명시해 주지 않으면 충돌 에러

 

○ Table과 Column 생성 및 인덱스 설정

 

/* 테이블 생성(Chart) */

m_pTable->PutName((const char *)strChartTable);

 

/* 필드 생성(Chart Table) */

m_pTable->Columns->Append("r_Code", ADOX::adVarWChar, 10); // 텍스트(3번째 인자는 크기)

m_pTable->Columns->Append("r_Name", ADOX::adVarWChar, 20); // 텍스트

m_pTable->Columns->Append("r_SendDate", ADOX::adVarWChar, 10); // 텍스트

m_pTable->Columns->Append("r_CheckDate", ADOX::adVarWChar, 10); // 텍스트

m_pTable->Columns->Append("r_Contents", ADOX::adLongVarWChar, 0); // 메모(3번째 인자 0)

m_pTable->Columns->Append("r_Number", ADOX::adInteger, 0); // 정수형(3번째 인자 0)

 

/* 인덱스와 기본키 설정(기본키를 지정하고 중복 불가능한 Unique한 인덱스 지정), 인덱스 이름은 마음대로 */

m_pIndex->Name = "CodeIndex";

m_pIndex->Columns->Append("r_Code", ADOX::adVarWChar, 0);

m_pIndex->PutPrimaryKey(-1);// 기본키 지정

m_pIndex->PutUnique(-1);// 중복 불가능

 

/* 설정한 인덱스를 테이블에 붙여넣는다. */

m_pTable->Indexes->Append(_variant_t((IDispatch*)m_pIndex));

 

/* 설정한 테이블을 붙여넣는다. 하나의 테이블 생성 후 제일 마지막에 하는 작업 */

m_pCatalog->Tables->Append(_variant_t((IDispatch*)m_pTable));

 

 

 

○ 기타 주의 및 특이 사항

=> 중복 가능한 인덱스를 간단히 만들고자 할 경우

 

/* 중복 가능한 인덱스를 만들때는 밑에 한줄만 추가하면 됨(기본키 지정은 안됨) */

m_pTable->Indexes->Append("CodeIndex", "r_Code");

 

 

 

=> 여러개의 Table 생성시

 

/* 첫 번째 테이블 */

m_pTable.CreateInstance(__uuidof (Table));

m_pTable->PutName(...);

m_pTable->Columns->Append(...);

m_pCatalog->Tables->Append(...);

m_pTable = NULL;

 

/* 두 번째 테이블 */

m_pTable.CreateInstance(__uuidof (Table));

m_pTable->PutName(...);

m_pTable->Columns->Append(...);

m_pCatalog->Tables->Append(...);

m_pTable = NULL;

 

※ 위와 같이 반복적으로 해주면 됨(m_pTable->ParentCatalog = m_pCatalog;)와 같은 코드는 일련 번호 속성을 주는 것이 아닌 필드라면 생략 가능

 

 

=> DB의 필드 속성을 adNumeric나 adDecimal로 하여 생성할 경우

 

m_pTable->Columns->Append("r_Test", ADOX::adNumeric, 10);

 

/* adNumeric, adDecimal일 경우,정밀도(Precision), 배율(NumericScale) 설정 필수, 안하면 런타임에러, 설정을 위해 adNumeric 필드의 컬럼을 얻어와 필요한 설정 작업 수행 */

m_pColumn = m_pTable->Columns->GetItem("r_Test");

m_pColumn->Precision = 8;

m_pColumn->NumericScale = 4;

 

 

 

=> NULL을 사용하는 설정

 

/* Null을허용하는설정*/

m_pColumn = m_pTable->Columns->GetItem("r_Test");

m_pColumn->Attributes = adColNullable;

 

 

 

=> 자동 증가(Auto Increment) 속성을 가진 정수형 필드 생성

 

/* "r_Number"필드의 일렬번호(Auto Increment) 속성을 주기위해 해줘야 함, 안하면 런타임 에러 */

m_pTable->ParentCatalog = m_pCatalog;

 

/* Auto Increment 속성으로 설정, 즉 값이 자동 증가함. TRUE로 하면 런타임 에러, true로 해야 함 */

m_pTable->Columns->GetItem("r_Number")->Properties->GetItem("AutoIncrement")->Value = true;

 

/* long으로 캐스팅 해줘야 함, 안하면 런타임 에러*/

_variant_t varValue1( (long) 1 );

m_pTable->Columns->GetItem("r_Number")->Properties->GetItem("Seed")->Value = varValue1;

m_pTable->Columns->GetItem("r_Number")->Properties->GetItem("Increment")->Value = varValue1;

 

 ※ "AutoIncrement", "Seed", "Increment" 문자열은 이미 내부적으로 속성이름으로서 정의되어 있음

 

 

 

 

□ MDB 파일 압축 (msjro.dll)

○ MDB 파일은 계속적인 DB의 추가, 삭제, 업데이트 등의 작업에 따라 용량이 계속 커지게 됨으로 압축을 해주는 것이 좋음

 

IJetEnginePtr jet(__uuidof(JetEngine));

 

/* mdb 파일압축*/

jet->CompactDatabase( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=data.mdb;","Provider=Microsoft.Jet.OLEDB.4.0;Data Source=__tmp__.mdb; Jet OLEDB:Engine Type=5;");

 

/* 압축된Temp 파일을원본파일로바꿈*/

DeleteFile("data.mdb");

MoveFile("__tmp__.mdb", "data.mdb");

 

※ 해당 MDB 파일에 연결된 Connection이 없어야 합니다.

※ 원래 DB 파일인 data.mdb를 압축하는데 중간에 __tmp__.mdb라는 임시파일로 생성하여 압축,따라서 다시 압축된 임시파일을 원래의 DB파일로 바꿔야함.

 

 

 

 

□ MDB 연동 (msado15.dll)

○ DB 연결

 

HRESULT hr = S_OK;

ADODB::_ConnectionPtr m_pConnection;

 

/* 연결 인스턴스 생성*/

hr = m_pConnection.CreateInstance(__uuidof(Connection));

 

if(SUCCEEDED(hr))

{

    /* DB 연결*/

    m_pConnection->ConnectionString = _bstr_t("Provider=Microsoft.Jet.OLEDB.4.0; Data Source=data.mdb;");

    m_pConnection->Open("", "", "", adModeUnknown);

}

 

※ ADODB::_ConnectionPtr 클래스는 보통 사용하는 클래스의 멤버 변수로서 전역으로 선언한다.즉, 여기서는 CDataBase 클래스의 멤버변수로 선언하여 사용하나 설명의 편의상 위와 같이 지역변수 같이 선언하였다.

 

 

○ DB 연결 해제

 

m_pConnection->Close();

m_pConnection = NULL;

 

 

 

○ DB에 레코드 추가

 

CString query;// 쿼리문이 저장될 변수

 

/* 쿼리*/

query = "INSERT INTO Chart (r_Code, r_Name) values ('0123456', '홍길동')";

 

_bstr_t executeQuery = query;

 

/* 트랜잭션 시작*/

m_pConnection->BeginTrans();

 

/* "추가" 쿼리 실행*/

m_pConnection->Execute(executeQuery, NULL, adCmdText);

 

/* 트랜잭션 종료*/

m_pConnection->CommitTrans();

 

※ 예외 처리를 추가하여 Commit에 실패하면 m_pConnection->RollbackTrans(); 코드를 통해 롤백하도록 한다.

 

 

○ DB의 레코드 삭제

 

CString query;// 쿼리문이 저장될 변수

 

/* 쿼리*/

query = "DELETE FROM Chart WHERE r_Code='0123456'";

 

_bstr_t executeQuery = query;

 

/* 트랜잭션시작*/

m_pConnection->BeginTrans();

 

/* "삭제" 쿼리실행*/

m_pConnection->Execute(executeQuery, NULL, adCmdText);

 

/* 트랜잭션종료*/

m_pConnection->CommitTrans();

 

※ 예외 처리를 추가하여 Commit에 실패하면 m_pConnection->RollbackTrans(); 코드를 통해 롤백하도록 한다.

 

 

○ DB의 레코드 수정

 

CString query;// 쿼리문이 저장될 변수

 

/* 쿼리*/

query = "UPDATE Chart SET r_Name='홍길동' WHERE r_Code='0123456'";

 

_bstr_t executeQuery = query;

 

/* 트랜잭션시작*/

m_pConnection->BeginTrans();

 

/* "추가" 쿼리실행*/

m_pConnection->Execute(executeQuery, NULL, adCmdText);

 

/* 트랜잭션종료*/

m_pConnection->CommitTrans();

 

※ 예외 처리를 추가하여 Commit에 실패하면 m_pConnection->RollbackTrans(); 코드를 통해 롤백하도록 한다.

 

 

○ DB의 모든 레코드 검색

 

int count = 0;

ADODB::_RecordsetPtr record;// DB의 레코드셋

 

_bstr_t bKey;

_bstr_t bKeyContents;

 

CString query;// 쿼리문이 저장될 변수

CString m_Index[MAXCOUNT];

CString m_Contents[MAXCOUNT];

 

/* 쿼리*/

query = "SELECT * FROM MacroData";

 

_bstr_t executeQuery = query;

 

/* 쿼리실행*/

record = m_pConnection->Execute(executeQuery, NULL, adCmdText);

 

while(!record->EndOfFile)

{

    /* 레코드의 각 필드에서 해당정보를 가져옴*/

    bKey = record->Fields->GetItem("r_Key")->Value;

    bKeyContents = record->Fields->GetItem("r_KeyContents")->Value;

 

    m_Index[count].Format("%s", (LPCSTR)bKey);

    m_Contents[count].Format("%s", (LPCSTR)bKeyContents);

 

    /* 다음레코드로진행*/

    record->MoveNext();

    count++;

}

 

/* 레코드셋 해제*/

record->Close();

record = NULL;

 

※ msado15.dll만 Import하여 ADO만 사용하는 경우에는 while(!record->EndOfFile) 로 하면 컴파일 에러 발생, while(!pRecordSet->GetadoEOF())을 사용하도록 한다.

 

 

○ DB의 특정 레코드 검색

 

ADODB::_RecordsetPtr record;// DB의 레코드셋

 

_bstr_t bCode;

_bstr_t bName;

 

CString query;// 쿼리문이 저장될 변수

CString m_Code;

CString m_Name;

 

/* 쿼리*/

query = "SELECT * FROM Chart WHERE r_Code='0123456'";

 

_bstr_t executeQuery = query;

 

/* 쿼리 실행*/

record = m_pConnection->Execute(executeQuery, NULL, adCmdText);

 

/* 레코드의 각 필드에서 해당 정보를 가져옴 */

bCode = record->Fields->GetItem("r_Code")->Value;

bName = record->Fields->GetItem("r_Name")->Value;

 

/* 해당 정보를 입력 */

m_Code.Format("%s", (LPCSTR)bCode);

m_Name.Format("%s", (LPCSTR)bName);

 

/* 레코드셋 해제 */

record->Close();

record = NULL;

 

 

 

=> 레코드의 필드 값이 정수형일 때

 

int count = record->GetCollect("macroCount").intVal;

 

 

 

※ DB 작업 수행 간에는 try, catch로 예외처리를 해주는 것이 좋다.예외는 _com_error& 형태 이므로 catch(_com_error& e)로 하면 되고 e.Error()로 에러 코드 번호를 알 수 있다.

 

※ DB의 필드 타입 종류와 프로그래밍 코드와의 맵핑 정보는 MSDN을 참고

 

 

 

□ UDL 파일 사용

○ xxx.udl 파일을 작성

=> udl 파일을 만드는 방법은 메모장 같은 텍스트 에디터 파일을 비운상태에서, "xxx.udl" 형태로 파일명을 지정한 후, 저장

 

○ 설 정

=> "xxx.udl"를 선택 후, 우측버튼을 클릭해서 속성메뉴를 선택

 

 

=> 공급자 탭에서, ADO에서 사용하는 공급자를 선택

※ 여기에서는 mdb 파일을 사용하므로, "Microsoft Jet 3.51 OLE DB Provider", "Microsoft Jet 4.0 OLE DB Provider" 등을 선택

 

=> 연결 탭에서 데이터베이스명을 입력

※ 옆의 파일 찾기 버튼을 클릭하여 사용할 mdb 파일을 선택

 

○ 연결 테스트

=> 연결 테스트 버튼을 눌러 테스트를 실행

 

○ udl 파일 사용시 DB 연결

 

HRESULT hr = S_OK;

_ConnectionPtr m_pConnection;

 

/* 연결 인스턴스 생성*/

hr = m_pConnection.CreateInstance(__uuidof(Connection));

 

if(SUCCEEDED(hr))

{

    /* DB 연결*/

    hr = m_pConnection->Open(_bstr_t(L"File Name=data.udl;"), _bstr_t(L""), _bstr_t(L""), adModeUnknown);

 

    if(SUCCEEDED(hr))

    {

        // 연결 성공시 수행작업

    }

}

TAG •

List of Articles
No. Subject Author Date Views
45 Customizing GINA, Part 2 digipine 2017.10.28 96711
44 C# 으로 구현한 화면 캡춰 클래스 1 digipine 2017.11.02 33969
43 [C#] 코드 실행 시간 측정 및 DateTime 스트링으로 변환 포맷 lizard2019 2019.01.23 23016
42 Customizing GINA, Part 1 digipine 2017.10.28 21759
41 VC++ UTF8 변환 관련 매크로 digipine 2017.11.02 8779
40 The .Net Developer's Guide to Directory Services Programming digipine 2017.10.29 7168
39 [C#] 프로그램 종료 방법 lizard2019 2019.01.23 6789
38 [API Hooking] Dll Injection 하는 방법 digipine 2017.10.29 5225
37 [WIN32] Process ID로 HWND 구하기 digipine 2017.10.29 5019
36 [WIN32] 실행 중인 프로세스를 외부에서 강제로 종료, 안전한 TerminateProcess digipine 2017.10.29 3460
35 [Windows] DOS 명령어 실행하고 결과 스트링 가져오는 샘플 코드 digipine 2017.11.02 2689
» VC++(MFC)에서 MDB 생성 / 압축 / 연동관리자 digipine 2017.10.29 2654
33 [WIN32] API Hook 정리 문서 digipine 2017.10.29 1970
32 [VC++, WInAPI] 폴더를 통채로 지우기, 서브 폴더 포함, DeleteAllFiles digipine 2017.10.29 1763
31 [WIN32, WINCE] 디스크 용량 구하는 방법 API GetDiskFreeSpaceEx digipine 2017.10.29 1569
30 [Win API]프로세스 아이디와 윈도우 핸들을 이용 파일명 구하기 digipine 2017.10.29 1393
29 C# - 한글로된 폰트명 처리 방법 개선 (Font Name Localization) digipine 2017.11.02 1392
28 RPC에 대하여... (1) : RPC 가 사용하는 TCP/IP 포트는 ? digipine 2017.10.29 1295
27 GINA(Graphical Identification aNd Authentication), SAS(Secure Attention Sequence) digipine 2017.10.29 1258
26 RPC에 대하여... (2) : RPC 가 사용하는 포트를 바꿔보자 digipine 2017.10.29 1118
Board Pagination Prev 1 2 3 Next
/ 3