아래의 간단한 XML 코드를 MSXML을 통해 생성하고 파일로 저장해보겠습니다.
<?xml version="1.0"?> <document> </document>
먼저 XML 문서를 다루기 위한 XMLDOMDocument 인스턴스를 생성합니다. COM 클래스는 이름 끝에 Ptr이라 붙는 자료형을 제공하는데 이는 일종의 스마트포인터(smart pointer)로서 자신이 선언된 스코프가 끝날 때 메모리를 자동으로 해제하도록 만들어진 것입니다. 아래와 같이 IXMLDOMDocumentPtr형의 변수를 선언하고 CreateInstance 메서드를 호출합니다. 매개변수로는 XMLDocument의 CLSID를 전달합니다. HRESULT를 반환하며 SUCCEEDED 매크로를 통해 성공 또는 실패를 판별할 수 있습니다.
/* ex03.cpp */ #include <windows.h> #import <msxml4.dll> int main(int argc, char * argv[]) { if (SUCCEEDED(::CoInitialize(NULL))) { IXMLDOMDocumentPtr xmlDocument; if(!SUCCEEDED(xmlDocument.CreateInstance(CLSID_XMLDocument))) return -1; ::CoUninitialize(); } rerurn 0; }
인스턴스 생성에 성공하였다면 xmlDocument 인스턴스를 통해 XML 구성요소를 생성하고 추가해나가면 됩니다. document라고 이름 붙인 루트 엘리먼트를 추가하기에 앞서 맨 처음 등장하는 <?xml version="1.0"?> 이라는 부분을 추가합니다. 이는 IXMLDOMProcessingInstruction 클래스로 제공됩니다.
/* ex04.cpp */ #include <windows.h> #import <msxml4.dll> int main(int argc, char * argv[]) { if (SUCCEEDED(::CoInitialize(NULL))) { { BSTR bstr1, bstr2, bstr3; IXMLDOMDocumentPtr xmlDocument; IXMLDOMProcessingInstructionPtr xmlProcessingInstruction; if (SUCCEEDED(xmlDocument.CreateInstance(CLSID_XMLDocument))) return -1; /* <?xml version="1.0"?> 부분을 만들기 위한 소스 */ /* version 항목에는 1.0의 값을 부여 */ bstr1 = ::SysAllocString(TEXT("xml")); bstr2 = ::SysAllocString(TEXT("version=\"1.0\"")); /* 지정한 항목과 값으로 ProcessingInstruction을 생성 마지막 매개변수를 통해 생성된 인스턴스가 반환됨. */ xmlDocument->createProcessingInstruction(bstr1, bstr2, &xmlProcessingInstruction); /* 할당한 문자열 메모리는 사용이 끝났으므로 해제 */ ::SysFreeString(bstr2); ::SysFreeString(bstr1); /* XMLDocument에 추가 */ xmlDocument->appendChild(xmlProcessingInstruction, NULL); /* 현재까지 만들어진 문서를 문자열로 얻기 */ xmlDocument->get_xml(&bstr3); /* 문서의 내용을 출력 */ ::wprintf(L"%s", (const wchar_t *)bstr3); } ::CoUninitialize(); } rerurn 0; }
COM은 문자열을 전달할 때 BSTR이라는 방식으로 전달합니다. 문자열에는 크게 C-Style 문자열과 BSTR(Basic String)이 있는데, C-Style은 문자열 끝에 NULL이 삽입되는 문자열이고 BSTR은 NULL을 사용하지 않는 대신 문자열의 길이를 명시하는 방식을 사용합니다. C-Style 문자열에서 BSTR로 변환하기 위해 SysAllocString이 사용된 것입니다.
~Ptr로 끝나는 클래스들은 스코프를 벗어날 때 소멸자가 실행되면서 메모리가 해제된다고 하였습니다. 그런데 스코프가 끝나기 전에 ::CoUninitialize() 함수가 실행되어 COM 관련 메모리가 이미 해제되어버렸기 때문에 ~Ptr의 소멸자에서 해제할 메모리가 없어지므로, 프로그램이 종료될 때 100% 이 부분에서 오류가 날 것입니다. 이를 방지하려면 위와 같이 ~Ptr클래스의 인스턴스를 선언하고 사용하는 부분을 스코프로 한번 더 묶어주면 오류가 없습니다.
이제 document라고 이름붙인 루트 엘리먼트를 삽입하겠습니다.
/* ex05.cpp */ #include <windows.h> #include <stdio.h> #include <locale.h> int main() { ::setlocale(LC_ALL, ""); if (SUCCEEDED(::CoInitialize(NULL))) { { BSTR bstr1, bstr2, bstr3; IXMLDOMDocumentPtr xmlDocument; IXMLDOMProcessingInstructionPtr xmlProcessingInstruction; IXMLDOMElementPtr xmlElement; if (!SUCCEEDED(xmlDocument.CreateInstance(__uuidof(DOMDocument)))) { ::wprintf(L"xmlDocument가 생성되지 않았습니다.\n"); ::CoUninitialize(); return -1; } bstr1 = ::SysAllocString(L"xml"); bstr2 = ::SysAllocString(L"version=\"1.0\""); xmlDocument->createProcessingInstruction(bstr1, bstr2, &xmlProcessingInstruction); ::SysFreeString(bstr2); ::SysFreeString(bstr1); xmlDocument->appendChild(xmlProcessingInstruction, NULL); /* <document> </document> 부분 추가 */ bstr1 = ::SysAllocString(L"document"); xmlDocument->createElement(bstr1, &xmlElement); ::SysFreeString(bstr1); xmlDocument->appendChild(xmlElement, NULL); xmlDocument-gt;get_xml(&bstr3); ::wprintf(L"%s", (const wchar_t *)bstr3); } ::CoUninitialize(); } return 0; }
createElement를 통해 태그의 이름이 document인 엘리먼트를 만들었고 이를 xmlDocument에 직접 추가하여 루트 엘리먼트로 지정했습니다. get_xml로 결과를 확인해 보겠습니다. document 태그 내부의 내용이 비어있으므로 <document></document> 대신 <document />와 같이 축약되어 나타납니다.
'Application Programming Interface > Windows API' 카테고리의 다른 글
Visual C++로 MSXML 사용하기 #4 (0) | 2014.08.12 |
---|---|
Visual C++로 MSXML 사용하기 #3 (0) | 2014.08.12 |
Visual C++로 MSXML 사용하기 #1 (1) | 2014.08.11 |
waveOut 함수 사용 예제 #2 (0) | 2014.05.15 |
waveOut 함수 사용 예제 #1 (0) | 2014.05.14 |