C语言通过MSXML6.0读写XML文件(同时支持char[]和wchar_t[]字符数组)

开发环境:Visual Studio 2010
运行环境:Windows XP SP3

第一节 读取XML文件(使用wchar_t[]字符数组)

读取XML文件可使用IXMLDOMDocument_load函数。

/* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */
#define COBJMACROS
#include <stdio.h>
#include <MsXml6.h>

#pragma comment(lib, "msxml6.lib")

/*
参考资料:
 (1) VARIANT结构体: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373687(v=vs.85).aspx
     VARIANT用于表示一个弱类型的变量
 (2) BSTR字符串: https://msdn.microsoft.com/en-us/library/windows/desktop/ms221069(v=vs.85).aspx
     BSTR字符串是用于COM组件对象模型的字符串格式, 字符串以表示字符串长度的4字节整数开始, 然后跟上UTF-16编码的wchar_t字符串(包括\0结束标志)。BSTR类型的变量是一个指针, 指向字符串的第一个字符处
	  例如, 一段起始地址为1000的内存空间, 则1000~1003这四个字节存放字符串的长度, 1004开始才是字符串的真正内容, BSTR变量应指向1004而不是1000
	  wchar_t *用于保存UTF-16编码格式的字符串, 用wprintf的%ls输出
	  char *可以用来保存任意编码格式的字符串, 但只有ANSI格式才能使用printf的%s正确显示出来
	  在简体中文版操作系统下, ANSI就是GB2312编码
	  MultiByteToWideChar函数可以将char *字符串转换为wchar_t *字符串
	  WideCharToMultiByte函数可以将wchar_t *字符串转换为char *t字符串
	  SysAllocString函数可以将wchar_t *字符串转换为BSTR字符串
 (3) IXMLDOMDocument接口: https://msdn.microsoft.com/en-us/library/windows/desktop/dd892951(v=vs.85).aspx
 (4) 组件对象模型COM: 
     https://msdn.microsoft.com/en-us/library/windows/desktop/ff485848(v=vs.85).aspx
     https://msdn.microsoft.com/en-us/library/windows/desktop/ms680573(v=vs.85).aspx
 (5) Windows窗口程序: https://msdn.microsoft.com/en-us/library/windows/desktop/ff381409(v=vs.85).aspx
*/

// C语言自带的wprintf函数无法打印中文
// 所以这里重新实现一个能打印中文的my_wprintf函数
int my_wprintf(const wchar_t *format, ...)
{
	int n;
	va_list list;
	wchar_t str[1024];
	DWORD ret;
	HANDLE console;

	va_start(list, format);
	n = _vsnwprintf_s(str, _countof(str), _countof(str) - 1, format, list);
	va_end(list);

	console = GetStdHandle(STD_OUTPUT_HANDLE);
	WriteConsoleW(console, str, (DWORD)wcslen(str), &ret, NULL);
	return n;
}

// 显示元素节点的属性值 (元素节点是一种节点)
void display_attribute(IXMLDOMElement *elem, const wchar_t *name, int intval)
{
	int num;
	BSTR bstr; // 表示一个字符串
	VARIANT variant; // 表示一个弱类型的变量

	bstr = SysAllocString(name); // 必须用SysAllocString函数把wchar_t *字符串转换成BSTR字符串
	IXMLDOMElement_getAttribute(elem, bstr, &variant);
	SysFreeString(bstr); // 使用完字符串后必须释放

	my_wprintf(L"[Attribute] name=%ls, value=%ls", name, variant.bstrVal); // BSTR和wchar_t *字符串都用%ls输出
	if (intval)
	{
		num = _wtoi(variant.bstrVal); // 转换为整形
		printf(", 2*value=%d", 2 * num);
	}
	printf("\n");

	SysFreeString(variant.bstrVal); // 保存在Variant中的BSTR也必须释放掉
}

// 显示节点名称
void display_nodename(IXMLDOMNode *node)
{
	BSTR bstr;

	IXMLDOMNode_get_nodeName(node, &bstr);
	my_wprintf(L"[Node] name=%ls\n", bstr); // 使用%ls打印BSTR字符串内容
	SysFreeString(bstr); // 使用完字符串后必须释放
}

// 显示节点中的文本内容 (方法一)
void display_content(IXMLDOMNode *node)
{
	BSTR text;

	IXMLDOMNode_get_text(node, &text);
	my_wprintf(L"[Text] %ls\n", text);
	SysFreeString(text);
}

// 显示节点中的文本内容 (方法二)
// 文本内容是文本节点, 是node节点下的子节点
/*
void display_content(IXMLDOMNode *node)
{
	IXMLDOMNode *child;
	VARIANT value;

	IXMLDOMNode_get_firstChild(node, &child); // 获取文本节点
	IXMLDOMNode_get_nodeValue(child, &value); // 得到文本节点的内容
	my_wprintf(L"[Text] %ls\n", value.bstrVal);

	SysFreeString(value.bstrVal);
	IXMLDOMNode_Release(child);
}
*/

// 获取节点中的XML文本内容
void display_inner_xml(IXMLDOMNode *node)
{
	BSTR bstr;

	IXMLDOMNode_get_xml(node, &bstr);
	my_wprintf(L"[InnerXML] %ls\n", bstr);
	SysFreeString(bstr);
}

void read_xml(IXMLDOMDocument *xmldoc)
{
	long i, len;
	IXMLDOMElement *root;
	IXMLDOMElement *elem;
	IXMLDOMNode *root_node;
	IXMLDOMNode *item;
	IXMLDOMNodeList *list;

	// 根节点
	IXMLDOMDocument_get_documentElement(xmldoc, &root);
	IXMLDOMNode_QueryInterface(root, &IID_IXMLDOMNode, &root_node); // 把element node变为node
	display_nodename(root_node);
	display_attribute(root, L"id", 1);
	display_inner_xml(root_node);
	printf("\n");

	// 根节点下的子节点
	IXMLDOMElement_get_childNodes(root, &list);
	IXMLDOMNodeList_get_length(list, &len);
	for (i = 0; i < len; i++)
	{
		IXMLDOMNodeList_get_item(list, i, &item);
		display_nodename(item);
		display_content(item);

		// 只有元素节点才有属性节点, 所以获取属性前要进行类型转换, 把node变为element node
		IXMLDOMNode_QueryInterface(item, &IID_IXMLDOMElement, &elem);
		display_attribute(elem, L"name", 0);
		IXMLDOMElement_Release(elem);

		IXMLDOMNode_Release(item);
	}
	IXMLDOMNodeList_Release(list);

	IXMLDOMElement_Release(root_node);
	IXMLDOMNodeList_Release(root);
}

// 打开XML文件
void open_xml(const wchar_t *name)
{
	HRESULT hr;
	IXMLDOMDocument *xmldoc;
	VARIANT filename;
	VARIANT_BOOL flag;

	hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
	if (SUCCEEDED(hr))
	{
		filename.bstrVal = SysAllocString(name); // 将wchar_t *转换为BSTR
		filename.vt = VT_BSTR;
		IXMLDOMDocument_load(xmldoc, filename, &flag);
		// 注: IXMLDOMDocument_load是读XML文件
		//     IXMLDOMDocument_loadXML是读XML字符串
		SysFreeString(filename.bstrVal);
		if (flag == VARIANT_TRUE)
		{
			printf("读取XML文件成功\n");
			read_xml(xmldoc);
		}
		else
			printf("读取XML文件失败\n");

		IXMLDOMDocument_Release(xmldoc);
	}
}

int main()
{
	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型

	open_xml(L"data.xml");

	CoUninitialize();
	return 0;
}

 

第二节 读取XML文件(使用char[]字符数组)

/* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */
#define COBJMACROS
#include <stdio.h>
#include <MsXml6.h>

#pragma comment(lib, "msxml6.lib")

// char *转BSTR
// 用完后调用SysFreeString释放
BSTR convert_string_to_bstr(const char *s)
{
	int n;
	wchar_t *ws;
	BSTR bstr = NULL;

	n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);
	ws = malloc(n * sizeof(wchar_t));
	if (ws != NULL)
	{
		MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);
		bstr = SysAllocString(ws);
		free(ws);
	}
	return bstr;
}

// BSTR转char *
// 用完后调用free释放
char *convert_bstr_to_string(BSTR bstr)
{
	char *s;
	int n;

	n = WideCharToMultiByte(CP_ACP, 0, bstr, -1, NULL, 0, NULL, NULL);
	s = malloc(n);
	if (s != NULL)
		WideCharToMultiByte(CP_ACP, 0, bstr, -1, s, n, NULL, NULL);
	return s;
}

// 显示元素节点的属性值 (元素节点是一种节点)
void display_attribute(IXMLDOMElement *elem, const char *name, int intval)
{
	char *value_str;
	int num;
	BSTR bstr; // 表示一个字符串
	VARIANT variant; // 表示一个弱类型的变量

	bstr = convert_string_to_bstr(name);
	IXMLDOMElement_getAttribute(elem, bstr, &variant);
	SysFreeString(bstr); // 使用完字符串后必须释放

	value_str = convert_bstr_to_string(variant.bstrVal);
	printf("[Attribute] name=%s, value=%s", name, value_str);
	if (intval)
	{
		num = atoi(value_str); // 转换为整形
		printf(", 2*value=%d", 2 * num);
	}
	printf("\n");
	
	free(value_str);
	SysFreeString(variant.bstrVal); // 保存在Variant中的BSTR也必须释放掉
}

// 显示节点名称
void display_nodename(IXMLDOMNode *node)
{
	char *s;
	BSTR bstr;

	IXMLDOMNode_get_nodeName(node, &bstr);
	s = convert_bstr_to_string(bstr);
	printf("[Node] name=%s\n", s);
	free(s);
	SysFreeString(bstr); // 使用完字符串后必须释放
}

// 显示节点中的文本内容 (方法一)
void display_content(IXMLDOMNode *node)
{
	char *text_str;
	BSTR text;

	IXMLDOMNode_get_text(node, &text);
	text_str = convert_bstr_to_string(text);
	printf("[Text] %s\n", text_str);
	free(text_str);
	SysFreeString(text);
}

// 显示节点中的文本内容 (方法二)
// 文本内容是文本节点, 是node节点下的子节点
/*
void display_content(IXMLDOMNode *node)
{
	char *value_str;
	IXMLDOMNode *child;
	VARIANT value;

	IXMLDOMNode_get_firstChild(node, &child); // 获取文本节点
	IXMLDOMNode_get_nodeValue(child, &value); // 得到文本节点的内容
	value_str = convert_bstr_to_string(value.bstrVal);
	printf("[Text] %s\n", value_str);
	
	free(value_str);
	SysFreeString(value.bstrVal);
	IXMLDOMNode_Release(child);
}
*/

// 获取节点中的XML文本内容
void display_inner_xml(IXMLDOMNode *node)
{
	char *s;
	BSTR bstr;

	IXMLDOMNode_get_xml(node, &bstr);
	s = convert_bstr_to_string(bstr);
	printf("[InnerXML] %s\n", s);
	free(s);
	SysFreeString(bstr);
}

void read_xml(IXMLDOMDocument *xmldoc)
{
	long i, len;
	IXMLDOMElement *root;
	IXMLDOMElement *elem;
	IXMLDOMNode *root_node;
	IXMLDOMNode *item;
	IXMLDOMNodeList *list;

	// 根节点
	IXMLDOMDocument_get_documentElement(xmldoc, &root);
	IXMLDOMNode_QueryInterface(root, &IID_IXMLDOMNode, &root_node); // 把element node变为node
	display_nodename(root_node);
	display_attribute(root, "id", 1);
	display_inner_xml(root_node);
	printf("\n");

	// 根节点下的子节点
	IXMLDOMElement_get_childNodes(root, &list);
	IXMLDOMNodeList_get_length(list, &len);
	for (i = 0; i < len; i++)
	{
		IXMLDOMNodeList_get_item(list, i, &item);
		display_nodename(item);
		display_content(item);

		// 只有元素节点才有属性节点, 所以获取属性前要进行类型转换, 把node变为element node
		IXMLDOMNode_QueryInterface(item, &IID_IXMLDOMElement, &elem);
		display_attribute(elem, "name", 0);
		IXMLDOMElement_Release(elem);

		IXMLDOMNode_Release(item);
	}
	IXMLDOMNodeList_Release(list);

	IXMLDOMElement_Release(root_node);
	IXMLDOMNodeList_Release(root);
}

// 打开XML文件
void open_xml(const char *name)
{
	HRESULT hr;
	IXMLDOMDocument *xmldoc;
	VARIANT filename;
	VARIANT_BOOL flag;

	hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
	if (SUCCEEDED(hr))
	{
		filename.bstrVal = convert_string_to_bstr(name); // 将char *转换为BSTR
		filename.vt = VT_BSTR;
		IXMLDOMDocument_load(xmldoc, filename, &flag);
		// 注: IXMLDOMDocument_load是读XML文件
		//     IXMLDOMDocument_loadXML是读XML字符串
		SysFreeString(filename.bstrVal);
		if (flag == VARIANT_TRUE)
		{
			printf("读取XML文件成功\n");
			read_xml(xmldoc);
		}
		else
			printf("读取XML文件失败\n");

		IXMLDOMDocument_Release(xmldoc);
	}
}

int main()
{
	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型

	open_xml("data.xml");

	CoUninitialize();
	return 0;
}

第三节 读取XML字符串(使用char[]字符数组)

读取XML字符串可使用IXMLDOMDocument_loadXML函数。

// 读取XML字符串
void load_xml_str()
{
	char *str = "<?xml version=\"1.0\" encoding=\"utf-8\"?><data id=\"20240117\"><post name=\"2023年人口统计数据发布了,大家怎么看?\">建档788W是主体,压哨建档出生的一部分,双胞胎三胞胎占一小部分,不建档生孩子的占一小部分,但是还是难凑出902,还是得预支一下。</post><post name=\"有什么是去了成都才知道的?\">不要跟团,也不要自驾游,成都每天都人满为患,人多车堵,还不好停车,景点大部分集中在1、2、3号线,坐地铁出行就能玩转成都。2、去鹤鸣茶社品一品茶,享受宁静时光感受成都的慢生活与惬意。3、成都火锅很辣,不是很能吃辣的朋友一定不要轻易尝试,不然会被辣的怀疑人生。</post><post name=\"能否找到一个f(x),使得当x取到全体正整数的时候,f(x)可以取到全体有理数?\">题主好像没有要求是单射啊……如果只要求满射的话,其实有个简单粗暴的做法,就是把正整数劈开……</post><post name=\"C++ 有什么好用的线程池?\">题主说的那两个thread pool其实都不错,但是2024年肯定是更推荐这一个,这个就是为进入标准的senders所实现的,学了也不会白学,当然它的使用非常简单。</post><post name=\"有什么适合大学生喝的性价比高的茶?\">虽然现代社会,买东西都在追求性价比,但茶叶这东西,我觉得还真不好说!</post></data>";
	BSTR bstr;
	HRESULT hr;
	IXMLDOMDocument *xmldoc;
	VARIANT_BOOL succeeded;

	hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
	if (SUCCEEDED(hr))
	{
		bstr = convert_string_to_bstr(str);
		hr = IXMLDOMDocument_loadXML(xmldoc, bstr, &succeeded);
		// 注: IXMLDOMDocument_load是读XML文件
		//     IXMLDOMDocument_loadXML是读XML字符串
		SysFreeString(bstr);

		if (SUCCEEDED(hr))
		{
			if (hr == S_OK)
			{
				printf("读取XML字符串成功\n");
				read_xml(xmldoc);
			}
			else if (hr == S_FALSE)
				printf("读取XML字符串失败\n");
		}

		// 另一种判断是否成功的方式
		if (succeeded == VARIANT_TRUE)
			printf("succeeded=true\n");
		else if (succeeded == VARIANT_FALSE)
			printf("succeeded=false\n");

		IXMLDOMDocument_Release(xmldoc);
	}
}

int main()
{
	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型

	load_xml_str();

	CoUninitialize();
	return 0;
}

第四节 创建XML文件(使用char[]字符数组)

创建XML文件可使用IXMLDOMDocument_save函数。

/* 这个程序只能在C编译器下编译成功, 请确保源文件的扩展名为c */
#define COBJMACROS
#include <stdio.h>
#include <time.h>
#include <MsXml6.h>

#pragma comment(lib, "msxml6.lib")

// char *转BSTR
// 用完后调用SysFreeString释放
BSTR convert_string_to_bstr(const char *s)
{
	int n;
	wchar_t *ws;
	BSTR bstr = NULL;

	n = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);
	ws = malloc(n * sizeof(wchar_t));
	if (ws != NULL)
	{
		MultiByteToWideChar(CP_ACP, 0, s, -1, ws, n);
		bstr = SysAllocString(ws);
		free(ws);
	}
	return bstr;
}

// BSTR转char *
// 用完后调用free释放
char *convert_bstr_to_string(BSTR bstr)
{
	char *s;
	int n;

	n = WideCharToMultiByte(CP_ACP, 0, bstr, -1, NULL, 0, NULL, NULL);
	s = malloc(n);
	if (s != NULL)
		WideCharToMultiByte(CP_ACP, 0, bstr, -1, s, n, NULL, NULL);
	return s;
}

void create_header(IXMLDOMDocument *xmldoc)
{
	BSTR target, data;
	IXMLDOMNode *header_node;
	IXMLDOMProcessingInstruction *header;
	
	// 创建XML文档头节点
	target = convert_string_to_bstr("xml");
	data = convert_string_to_bstr("version=\"1.0\" encoding=\"utf-8\"");
	IXMLDOMDocument_createProcessingInstruction(xmldoc, target, data, &header);
	SysFreeString(target);
	SysFreeString(data);

	// header转换成node, 然后添加进文档
	// 注意旧的header对象和新产生的node对象都要释放
	IXMLDOMProcessingInstruction_QueryInterface(header, &IID_IXMLDOMNode, &header_node);
	IXMLDOMProcessingInstruction_Release(header);
	IXMLDOMDocument_appendChild(xmldoc, header_node, NULL); // 最后一个参数是输出参数, 没有用
	IXMLDOMNode_Release(header_node);
}

IXMLDOMElement *create_root(IXMLDOMDocument *xmldoc)
{
	char timestr[30];
	struct tm tm;
	time_t t;
	BSTR name;
	IXMLDOMElement *root;
	IXMLDOMNode *root_node;
	VARIANT value;

	// 创建根节点
	name = convert_string_to_bstr("data");
	IXMLDOMDocument_createElement(xmldoc, name, &root);
	SysFreeString(name);
	
	// 将创建的根节点添加到文档中
	IXMLDOMElement_QueryInterface(root, &IID_IXMLDOMNode, &root_node);
	IXMLDOMDocument_appendChild(xmldoc, root_node, NULL);
	IXMLDOMNode_Release(root_node);

	// 获取当前时间
	time(&t);
	localtime_s(&tm, &t);
	strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", &tm);

	// 在根节点上添加一个属性
	name = convert_string_to_bstr("timestr");
	value.bstrVal = convert_string_to_bstr(timestr);
	value.vt = VT_BSTR;
	IXMLDOMElement_setAttribute(root, name, value);
	SysFreeString(name);
	SysFreeString(value.bstrVal);
	return root;
}

void add_post(IXMLDOMDocument *xmldoc, IXMLDOMElement *root, const char *name, const char *text)
{
	BSTR attrname_bstr;
	BSTR nodename_bstr;
	BSTR text_bstr;
	IXMLDOMElement *elem;
	IXMLDOMNode *node;
	VARIANT value;

	nodename_bstr = convert_string_to_bstr("post");
	IXMLDOMDocument_createElement(xmldoc, nodename_bstr, &elem); // 创建element
	SysFreeString(nodename_bstr);
	IXMLDOMElement_QueryInterface(elem, &IID_IXMLDOMNode, &node); // element转node
	IXMLDOMElement_appendChild(root, node, NULL);

	// 添加name属性
	attrname_bstr = convert_string_to_bstr("name");
	value.bstrVal = convert_string_to_bstr(name);
	value.vt = VT_BSTR;
	IXMLDOMElement_setAttribute(elem, attrname_bstr, value);
	SysFreeString(attrname_bstr);
	SysFreeString(value.bstrVal);

	// 添加节点内文本
	text_bstr = convert_string_to_bstr(text);
	IXMLDOMElement_put_text(elem, text_bstr);
	SysFreeString(text_bstr);

	// 释放element和node对象
	// 请注意element是node的子类, 换句话说element是一种node
	IXMLDOMElement_Release(elem);
	IXMLDOMNode_Release(node);
}

void write_xml(IXMLDOMDocument *xmldoc)
{
	IXMLDOMElement *root;

	create_header(xmldoc);
	root = create_root(xmldoc);
	add_post(xmldoc, root, "RPG Maker XP", "RPG Maker XP(RPGXP)是一款可让玩家自行制作在计算机游戏中相当受欢迎的角色扮演游戏,也就是 Role-Playing Game(RPG)的软件。制作完成的游戏,即使在没有安装 RPG Maker XP 的电脑上也能运行。");
	add_post(xmldoc, root, "高精度的画面", "支持 640×480 像素分辨率 32 色真彩色图像。完全对应 Alpha Channel(每个像素的透明度),光滑的透过处理,柔和的半透明表现。画面的合成方法也增加了通常的半透明合成、加算合成和减算合成。而且字型自动映射,粗体不再引人注目。");
	add_post(xmldoc, root, "使用软件的音乐播放", "作为 BGM,主要使用 DirectMusic Synthesizer 的 MIDI 播放。因为以软件播放,所以不能用其它格式代替,音乐素材的制造人没必需考虑对各种 MIDI 音乐的对应。在制作过程中能被正常播放的 MIDI 文件,同样可以在其它环境中正常播放。");
	add_post(xmldoc, root, "灵活且强大的脚本系统", "用 RPGXP 制作完成的游戏,脚本是以 Ruby 语言编写的。预先编写的脚本可以制作十分独特和有趣的游戏,它是为了更高级的游戏制作需求准备的,使用它可以进行画面设计、编写战斗系统,以及一切的游戏要素。运用脚本制作 RPG 以外类型的游戏也是可能的。");
	add_post(xmldoc, root, "魔塔样板", "魔塔样板是一个用RPG Maker XP、RPG Maker VX、RPG Maker Vx Ace或者VB制作的一个用来制作魔塔的样板,里面有魔塔里一些基本的地图和怪物和各种宝物。制作者只需改动怪物属性和地图,其他的都可以利用复制进行制作,制作起来方便。并且制作者还可以在魔塔样板中进行更高级的操作,做出一些特别的动画或者机关,总之,魔塔样版可以制作一切类型的魔塔。但是,有些质量较差的魔塔样板会有脚本错误。最常用版本是7630。");
	add_post(xmldoc, root, "房贷利息", "房贷利息是指每个月支付给银行的利息款,计算公式为:剩余未还房款×贷款利率%÷12。若贷款100万元,利率为5.88%,则第一个月要付的利息钱是100万×5.88%÷12=4900元。也就是说“钱价”为4900元/(百万元·月)。");

	IXMLDOMElement_Release(root);
}

// 创建XML文件
void create_xml(const char *name)
{
	HRESULT hr;
	IXMLDOMDocument *xmldoc;
	VARIANT filename;

	hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
	if (SUCCEEDED(hr))
	{
		write_xml(xmldoc);
		
		filename.bstrVal = convert_string_to_bstr(name); // 将char *转换为BSTR
		filename.vt = VT_BSTR;
		hr = IXMLDOMDocument_save(xmldoc, filename);
		SysFreeString(filename.bstrVal);
		if (SUCCEEDED(hr))
			printf("写入XML文件成功\n");
		else
			printf("写入XML文件失败\n");

		IXMLDOMDocument_Release(xmldoc);
	}
}

int main()
{
	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型

	create_xml("myxmlfile.xml");

	CoUninitialize();
	return 0;
}

第五节 创建XML字符串(使用char[]字符数组) 

使用IXMLDOMNode_get_xml函数获取的是单个节点的XML字符串(不包括XML头部声明)。使用IXMLDOMDocument_get_xml函数可获取整个XML文档的字符串(包括XML头部声明)。
两个函数获取到的都是UTF-16编码的BSTR字符串,可使用convert_bstr_to_string函数转换成GB2312编码的char *字符串。

void write_xml(IXMLDOMDocument *xmldoc)
{
	IXMLDOMElement *root;

	create_header(xmldoc);
	root = create_root(xmldoc);
	add_post(xmldoc, root, "配置“Debug|Win32”", "将平台工具集更改为“v143”(之前为“v100”)。");
	add_post(xmldoc, root, "配置“Debug|x64”", "将平台工具集更改为“v143”(之前为“v100”)。");
	add_post(xmldoc, root, "配置“Release|Win32”", "将平台工具集更改为“v143”(之前为“v100”)。");
	add_post(xmldoc, root, "配置“Release|x64”", "将平台工具集更改为“v143”(之前为“v100”)。");
	add_post(xmldoc, root, "正在升级项目“xmltest”...", "重定目标结束: 1 个已完成,0 个未通过,0 个已跳过");

	IXMLDOMElement_Release(root);
}

// 创建XML字符串
void create_xml_str()
{
	char *xmlstr;
	BSTR bstr;
	HRESULT hr;
	IXMLDOMDocument *xmldoc;

	hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, &xmldoc);
	if (SUCCEEDED(hr))
	{
		write_xml(xmldoc);
		
		IXMLDOMDocument_get_xml(xmldoc, &bstr);
		xmlstr = convert_bstr_to_string(bstr);
		SysFreeString(bstr);
		printf("%s\n", xmlstr);
		free(xmlstr);

		IXMLDOMDocument_Release(xmldoc);
	}
}

int main()
{
	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); // 初始化COM组件对象模型

	create_xml_str();

	CoUninitialize();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/338148.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AWTK 开源串口屏开发(7) - 屏幕保护

现代屏幕其实并不需要屏幕保护&#xff0c;不过屏幕保护程序会衍生一些其它用途。比如&#xff1a; 保护隐私。长时间不操作&#xff0c;通过动画或者其它方式隐藏屏幕内容。数据安全。长时间不操作&#xff0c;需要输入密码才能恢复。美观/广告。长时间不操作&#xff0c;显示…

Linux的IO文件操作和文件系统

前要&#xff1a;本次我想给您带来关于 IO 和文件的知识&#xff0c;而文件在本系列中分为内存上的文件和磁盘上的文件。 1.文件概念 1.1.文件读写 在谈及系统接口之前&#xff0c;我们先来从 C 语言的角度来谈及一些前要知识&#xff0c;以辅助我们后续来理解系统 IO。 我们…

如何优化SQL查询性能?解开你的数据库瓶颈之谜!

目录 1、前言 2、创建索引 2.1 确保表的主键和外键都有索引 2.2 根据查询条件创建适当的索引 2.3 避免在索引列上进行类型转换或函数操作 3、合理设计数据库架构 3.1 表的拆分和归并&#xff0c;避免不必要的数据冗余 3.2 使用适当的数据类型和字段长度&#xff0…

JMeter实操入门之登录

JMeter实操入门之登录 前言初级-无变量的登录线程组取样器-HTTP请求 进阶-定义变量的登录用户定义的变量获取JSON返回的数据-tokentoken设置全局变量 前言 安装及环境配置教程可移步&#xff1a;JMeter安装与配置环境 本篇文章针对小白进一步的认识及运用JMeter&#xff0c;围绕…

Microsoft Visual C++ RunTime怎么下载?

64位下载链接 下载好程序后双击&#xff0c;勾选“我同意许可条款和条件”&#xff0c;然后点击“安装” 安装完成后点击“关闭”即可 感谢您的阅读与关注&#xff0c;服务器大本营助您成为更专业的服务器管理员&#xff01;

一文了解【完全合作关系】下的【多智能体强化学习】

处于完全合作关系的多智能体的利益一致&#xff0c;获得的奖励相同&#xff0c;有共同的目标。比如多个工业机器人协同装配汽车&#xff0c;他们的目标是相同的&#xff0c;都希望把汽车装好。 在多智能体系统中&#xff0c;一个智能体未必能观测到全局状态 S。设第 i 号智能体…

文心一言 VS 讯飞星火 VS chatgpt (183)-- 算法导论13.4 7题

七、用go语言&#xff0c;假设用 RB-INSERT 将一个结点 x 插入一棵红黑树&#xff0c;紧接着又用 RB-DELETE 将它从树中删除。结果的红黑树与初始的红黑树是否一样&#xff1f;证明你的答案。 文心一言&#xff1a; 首先&#xff0c;我们需要明确RB-INSERT和RB-DELETE的基本操…

阿里云国外服务器多少钱一个月?24元/月

阿里云国外服务器优惠活动「全球云服务器精选特惠」&#xff0c;国外服务器租用价格24元一个月起&#xff0c;免备案适合搭建网站&#xff0c;部署独立站等业务场景&#xff0c;阿里云服务器网aliyunfuwuqi.com分享阿里云国外服务器优惠活动&#xff1a; 全球云服务器精选特惠…

云服务器定价_云服务器价格_云主机计费模式_腾讯云

腾讯云服务器租用价格表&#xff1a;轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器446元一年、646元15个月&#xff0c;云服务器CVM S5实例2核2G配置280.8元一年…

UE5 - Polycam扫描文件导入插件

Polycam是利用Gaussian Splatting进行3D重建的3D扫描相关软件&#xff0c;其对应有UE引擎的插件&#xff08;Plugin_XV3dGS&#xff09;可以把相关格式的文件导入到引擎&#xff1b; 首先Polycam的官网为&#xff1a;My Captures | Polycam 可以下载各种用户扫描文件&#xff…

java数据结构与算法刷题-----LeetCode485. 最大连续 1 的个数

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 法一&#xff0c;双指针2. 法二&#xff1a;变量计数 1. 法一…

【开源】基于JAVA语言的CRM客户管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统设计3.1 用例设计3.2 E-R 图设计3.3 数据库设计3.3.1 客户表3.3.2 商品表3.3.3 客户跟踪表3.3.4 客户消费表3.3.5 系统角色表 四、系统展示五、核心代码5.1 查询客户5.2 新增客户跟踪记录5.3 新增客户消费订单5.4 查…

JVM(上)

目录 一、JVM概述 一、JVM作用 二、JVM整体组成部分 二、JVM结构-类加载 一、类加载子系统概述 二、类加载过程 1.加载 2.链接 3.初始化&#xff08;类加载过程中的初始化&#xff09; 三、类加载器分类 大致分两类&#xff1a; 细致分类&#xff1a; 四、双亲委派机制 五、打…

【记录一下】【年底清洗抽油烟机---被套路了540块钱!!!】年底了,注意各种套路【警惕,不然钱没没!!!】

■事情结果 被骗&#xff08;啊&#xff0c;不是被骗&#xff0c;是被套路&#xff09;了360块钱 13050558273&#xff08;诈骗者&#xff0c;啊不能算是诈骗&#xff0c;是下套的清洗油烟机的吴某的电话&#xff09; 4008731099&#xff08;这个电话不是方太的客服电话&…

数据操作——Column 对象

Column 对象 1. 什么是Column对象 Column 表示了 Dataset 中的一个列, 并且可以持有一个表达式, 这个表达式作用于每一条数据, 对每条数据都生成一个值 2.Column对象如何创建 ’ 单引号 ’ 在 Scala 中是一个特殊的符号, 通过 ’ 会生成一个 Symbol 对象, Symbol 对象可以理…

优先级队列(堆) PriorityQueue

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f4d5;格言&#xff1a;那些在暗处执拗生长的花&#xff0c;终有一日会馥郁传香欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1.优先级队列 2.优先级队列的模拟实现 2.1 堆的概念 2.2 堆的创建 2.3 堆的插入和删除 2.…

数据结构——二叉树

目录 一、前言 1.1 树 1.2 树的相关概念 二、二叉树 2.1 定义 2.2 特殊类型 2.3 二叉树的性质 2.4 二叉树的存储结构 &#xff08;1&#xff09;顺序存储 &#xff08;2&#xff09;链式存储 三、二叉树相关操作 3.1 创建一颗二叉树 3.2 二叉树的遍历 &#xff…

构建STM32MP133的Buildroot环境

意法半导体ST在坚持用 Yocto构建他们的OpenSTLinux MP1系列MCU&#xff0c;编译费劲&#xff0c;而且我们的应用不需要Yocto的环境&#xff0c;所以基于Buildroot的最小Linux系统更适合我们。 STM32MP133微处理器基于单Arm Cortex-A7内核&#xff0c;运行频率可达1 GHz&#x…

JVM对象创建与内存回收机制

对象的创建过程有如下步骤&#xff1a; 1.类加载检查&#xff1a; 虚拟机遇到一个new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否已被加载、解析和初始化过&#xff0c;如果没…

带POE网络变压器与2.5G/5G/10G网络变压器产品特点介绍

Hqst华轩盛(石门盈盛)电子导读&#xff1a;一起来了解带POE网络变压器与2.5G/5G/10G网络变压器产品特点&#xff1f; 一﹑带POE网络变压器与2.5G/5G/10G网络变压器产品特点介绍 首先、POE网络变压器产品与常规不带POE产品的区别&#xff1a; 带POE网络变压器主要要求是耐电流等…
最新文章