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

news2024/11/26 14:44:43

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

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

/* 这个程序只能在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字符串
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[]字符数组)

/* 这个程序只能在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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

web蓝桥杯真题--8、和手机相处的时光

介绍 现在都提倡健康使用手机&#xff0c;那么统计一下在一周中每天使用手机的情况吧&#xff01;本题使用 ECharts 实现统计手机使用时长的折线图&#xff0c;但是代码中存在 Bug 需要你去修复。 准备 开始答题前&#xff0c;需要先打开本题的项目代码文件夹&#xff0c;目…

深度学习记录--梯度检验

数值逼近 为了对梯度进行检验&#xff0c;需要计算近似误差值来接近梯度 对于单边误差和双边误差公式&#xff0c;其中双边误差与真实梯度相差更小&#xff0c;故一般采用双边误差公式 双边误差 公式&#xff1a; 梯度检验(gradient checking) 对于成本函数&#xff0c;求出…

Python 邮件发送

首先需要有一个邮箱&#xff0c;没有就注册一个&#xff0c;最好是163邮箱。 本文将使用163邮箱讲解python发送邮件。 目录 开启邮箱smtp服务 打开SMTP设置 开启SMTP服务 记录授权密码和服务端配置 脚本发送邮件 引入所需的库 发送配置 邮箱登录发送邮件 查看收到邮件…

如何做标准化?| 京东云技术团队

在现代信息化的市场环境和社会中&#xff0c;标准化已经成为了各种行业的一个重要的标志。标准化不仅可以提升生产效率&#xff0c;减轻质量问题&#xff0c;还可以增加产品的可靠性和互通性。在这篇文章中&#xff0c;我们将探讨如何做标准化&#xff0c;为您提供详细的指导和…

一个小程序跳转到另一个小程序中如何实现

小程序 保证两个小程序是一样的主体才可以跳转。怎么知道是不是同样的主体呢&#xff1f; 小程序的后台管理-设置-基本设置-基本信息。查看主体信息。 跳转 <button clicktoOtherMini()>跳转到另一个小程序</button> function toOtherMini(){wx.navigateToMini…

chromedriver+Selenium+springboot+Java实现后端截图

chromedriver这种方法实现截图&#xff0c;依赖服务器端的谷歌浏览器软件&#xff0c;需要在服务器端下载谷歌浏览器。 Windows服务器说明 1.下载谷歌浏览器 2.根据第一步下载的谷歌浏览器版本&#xff0c;下载chromedriver&#xff0c;可以在这个页面找到和版本相近的版本去下…

开源进程/任务管理服务Meproc使用之HTTP API

本文讲述如何使用开源进程/任务管理服务Meproc的HTTP API管理整个服务。 Meproc所提供的全部 API 的 URL 都是相同的。 http://ip:port/proc例如 http://127.0.0.1:8606/proc在下面的小节中&#xff0c;我们使用curl命令向您展示 API 的方法、参数和请求正文。 启动任务 …

嵌入式-Stm32-江科大基于标准库的GPIO4个小实验

文章目录 一 、硬件介绍二 、实验&#xff1a;LED闪烁、LED流水灯、蜂鸣器提示2.1 需求1&#xff1a;面包板上的LED以1s为周期进行闪烁。亮0.5s,灭0.5s.....2.2 需求2: 8个LED实现流水灯 三、硬件介绍-按键开关、光敏电阻四、 实验 按键控制LED、光敏传感器控制蜂鸣器4.1 需求1…

CNAS中兴新支点——软件测试7条原则

软件测试报告 最需要注意的就是测试思考&#xff0c;而非测试执行。而对软件测试菜鸟来说&#xff0c;初入行&#xff0c;首先要知道软件测试的7条原则&#xff0c;了解这些可以让你事倍功半。 软件测试的7条原则 1&#xff09;测试的不可穷尽原则 是的&#xff01;任何产品…

【K8S 云原生】K8S的对外服务—ingress

目录 一、K8S的Service 1、Service的作用 2、Service类型&#xff1a; 二、ingress 1、ingress的组成&#xff1a; 2、ingress资源的定义项&#xff1a; 3、ingress暴露服务端的方式 3.1、DeploymentLoadBalancer模式&#xff1a; 1、工作流程图&#xff1a; 3.2、Dae…

Eclipse闪退 打开eclipse闪退 打开eclipse图标一闪而过 eclipse闪退 eclipse打不开

Eclipse闪退 打开eclipse闪退 打开eclipse图标一闪而过 eclipse闪退 eclipse打不开 问题描述切换为命令行启动 查看异常日志 问题描述 双击图标&#xff0c;窗口一闪而过&#xff0c;马上关闭了 切换为命令行启动 查看异常日志 进入Eclipse安装目录&#xff0c;运行终端启动…

Tektronix/泰克MSO5204B混合信号示波器

181/2461/8938产品概述&#xff1a; 带宽:2 GHz通道:4个模拟通道和16个数字通道采样速率:10 GS/s记录长度:50分钟FastAcq采集的最大波形捕获速率超过250&#xff0c;000 wfms/sFastFrame分段内存采集模式&#xff0c;每秒可采集多达290&#xff0c;000个分段和超过310&#xff…

Blender——将模型及其所有纹理与材质导入unity

前期准备 参考视频&#xff1a;7分钟教会你如何将Blender的模型材质导入unity_哔哩哔哩_bilibili 实验模型官网下载地址&#xff1a;Hoi An Ancient House Model free VR / AR / low-poly 3D model CSDN下载链接&#xff1a; 【免费】Blender三维模型-古代房屋模型&#xff…

逆向使用webpack打包的网站

webpack webpack 是 JavaScript 应用程序的模块打包器,可以把开发中的所有资源&#xff08;图片、js文件、css文件等&#xff09;都看成模块&#xff0c;通过loader&#xff08;加载器&#xff09;和 plugins &#xff08;插件&#xff09;对资源进行处理&#xff0c;打包成符…

【银行测试】银行项目,信贷/贷款业务测试+常问面试(二)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 银行测试-信贷&am…

1358. 素数环-深度优先搜索-DFS

代码&#xff1a; #include <bits/stdc.h> using namespace std; int n, a[19], c 0; bool f[19]; bool prime(int n){if(n 1)return false;for ( int i 2 ; i * i < n ; i )if(n % i 0)return false;return true; } void print(){c;cout << c << &q…

Kafka-消费者-KafkaConsumer分析-ConsumerNetworkClient

前面介绍过NetworkClient的实现&#xff0c;它依赖于KSelector、InFlightRequests、Metadata等组件&#xff0c;负责管理客户端与Kafka集群中各个Node节点之间的连接&#xff0c;通过KSelector法实现了发送请求的功能&#xff0c;并通过一系列handle*方法处理请求响应、超时请求…

el-table里面存在固定列获取video的ref的时候无法获取原始DOM

el-table里面存在固定列获取video的ref的时候无法获取原始DOM 问题复现 这是通过ref获取的dom实例&#xff0c;却变成了fixed固定出现了表格里面的video的实例 我现在的需求是修改里面的currentTime&#xff0c;但是获取的是固定列的video的ref&#xff0c;修改了&#xff0c…

Redis 笔记一

概览 1.Redis核心数据存储结构 2.Redis底层String编码int&embstr&raw 3.Redis底层压缩列表&跳表&哈希表 4.Redis底层Zset实现压缩列表和跳表如何选择 5.基于Redis实现微博&抢红包&12306核心业务 辅助学习&#xff1a;Redis 教程 | 菜鸟教程 1.Redis为什…

虚拟架桥:SD-WAN企业组网网络的智慧构筑

云桥通SD-WAN企业组网&#xff08;软件定义广域网&#xff09;代表着一项通过软件定义和虚拟化技术&#xff0c;将企业分支机构、数据中心和云服务等多种网络连接有机整合的创新解决方案。其核心框架涵盖了以下关键构成&#xff1a; 边缘设备&#xff1a; 在云桥通SD-WAN企业组…