VC++如何获取所有运行中的Word实例的COM对象

news2024/11/18 22:29:25

目录

  • 一 问题的提出
  • 二 工程创建
    • 2.1 创建一个基于对话框的MFC工程
    • 2.2 导入word相关的自动化包装类
  • 三 代码实例
    • 3.1 初始化COM库
    • 3.2 对话框类头文件修改
    • 3.3 对话框类实现文件
      • 1.根据进程名称获取进程ID
      • 2. 获取一个进程下所有的窗口
      • 3. 判断某个窗口是否为主窗口
      • 4. 判断word进程下面哪个窗口是word客户区所对应的窗口
      • 5. 获取所有word文档的信息
      • 6.对话框其他接口
  • 参考文章

一 问题的提出

  在自动化编程中,一个常见的问题是:如何获取运行中的Word/Excel实例的COM对象,一般来说,可以采取以下代码:

	CLSID IDExcel;
	::CLSIDFromProgID(L"Excel.Application", &IDExcel);
	LPUNKNOWN pUnkEx = NULL;
	::GetActiveObject(IDExcel, NULL, &pUnkEx);

  上述代码可以获取ROT表(Running Object Table运行实例表)中第一个对应的实例对象,但是很遗憾,可能并不是你想要的。比如当一个WPS word对象和MicroSoft office word对象同时打开时,上述代码返回的竟然是WPS对象的接口指针
  本文利用另外一种办法,可以获取所有运行中的word实例COM对象。最后的运行效果如下。
在这里插入图片描述
本文编译环境为VS2017+Office2016 ,涉及的项目源码链接为:
https://download.csdn.net/download/mary288267/87719124

二 工程创建

2.1 创建一个基于对话框的MFC工程

  按照下图修改对话框模板,ClistCtrl为报表形式,CEdit为多行模式,并在附加依赖项中增加一个导入库Oleacc.lib。
在这里插入图片描述

2.2 导入word相关的自动化包装类

  在VS中进入类向导->添加类->类型库中的MFC类
在这里插入图片描述
  在可用的类型库列表中找到word的类型库,单击,即可显示该类型库中所有的接口。

4.
  选出其中的_Application、_Document、Documents、Paragraph、Paragraphs、Window、Selection、range等接口,可以根据自己偏好修改上述类的名称。
在这里插入图片描述
  最终导出的包装类为
在这里插入图片描述
  请注意,包装类导出后,需要删掉每个包装类起始处的#Import指令行,即类似下面的指令语句
在这里插入图片描述

三 代码实例

3.1 初始化COM库

  首先,应该在app类的InitInstance函数中加入AfxOleInit函数,用于初始化COM库。

BOOL CGetAllWordInstancesApp::InitInstance()
{
	//首先,必须初始化COM库
	if (!AfxOleInit())
	{
		AfxMessageBox(_T("Can't initilize COM!"));
		return TRUE;
	}
	
	//........省略
}

3.2 对话框类头文件修改

  对话框类的头文件为:


// GetAllWordInstancesDlg.h: 头文件
//

#pragma once
#include <map>

#define MAXTITLELEN 256
#define MAXCLASSLEN 256

//窗口信息结构体
struct SWinInfo
{
public:
	HWND hWnd;
	HWND hParent;
	HWND hOwner;
	LONG lStyle;
	DWORD idProcess;  // process id
	DWORD idThread;  // creator thread id
	TCHAR pszTitle[MAXTITLELEN];	//Window title
	TCHAR pszWinClass[MAXCLASSLEN];// window class name.

	void Reset() {
		hWnd = hParent = hOwner = NULL;
		idProcess = idThread = NULL;
		lStyle = 0;
		memset(pszTitle, 0, sizeof(pszTitle));
		memset(pszWinClass, 0, sizeof(pszWinClass));
	}
};

// CGetAllWordInstancesDlg 对话框
class CGetAllWordInstancesDlg : public CDialog
{
public:
	typedef std::map<CString, CString> MapDocTitle2Cont;
	typedef MapDocTitle2Cont::iterator mapIter;
	CGetAllWordInstancesDlg(CWnd* pParent = nullptr);
	enum { IDD = IDD_GETALLWORDINSTANCES_DIALOG };

protected:
	HICON m_hIcon;
	virtual void DoDataExchange(CDataExchange* pDX);
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnBnClickedButton1();
	afx_msg void OnNMClickList1(NMHDR *pNMHDR, LRESULT *pResult);
	DECLARE_MESSAGE_MAP()

protected:
	void GetDocsCont(MapDocTitle2Cont& mapDocTitle2String);

private:
	CListCtrl m_wndLstProcess;
	MapDocTitle2Cont m_mapDocTitle2String;
	CEdit m_wndEdt;
};

  在头文件中,我们加入了一个结构体SWinInfo,这个结构体主要用来保存窗口的信息,包括窗口句柄、父窗口句柄、窗口所在进程和线程ID、窗口标题以及窗口类名称。

3.3 对话框类实现文件

  对话框类的实现文件中需要加入以下关键函数。

1.根据进程名称获取进程ID

//根据进程名拿到进程id
DWORD GetProcessIDByName(CString strName, std::vector<DWORD> &vtcUid)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe))
	{
		CString strTemp = pe.szExeFile;
		if (strTemp == strName)
			vtcUid.push_back(pe.th32ProcessID);
	}
	CloseHandle(hSnapshot);
	return 0;
}

  GetProcessIDByName的第一个参数为进程的名称,例如word就是“WINWORD.EXE”;第二个参数就是该进程ID的数组。这个函数里面调用了CreateToolhelp32Snapshot,用于拍摄指定进程以及这些进程使用的堆、模块和线程的快照;其中第一个标志变量TH32CS_SNAPPROCES指示拍摄系统中所有进程的快照。

2. 获取一个进程下所有的窗口

  在Windows API中,EnumWindows函数可以枚举屏幕上的所有顶级窗口,并把窗口的句柄传递给一个回调函数。它的原型是:

BOOL EnumWindows(
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);

  该函数的第一个参数就是枚举窗口时所调用的回调函数。根据上述函数,获取一个进程下所有窗口的例程如下:

//该结构体用做回调函数的参数
typedef struct EnumHWndsArg
{
	std::vector<HWND> *vecHWnds;
	DWORD dwProcessId;
}EnumHWndsArg, *LPEnumHWndsArg;

//回调函数
BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam)
{
	EnumHWndsArg *pArg = (LPEnumHWndsArg)lParam;
	if (pArg)
	{
		DWORD  idPprocess = 0;
		//注意这个函数,引用参数返回的是创建窗口的进程ID,而函数本身返回的是线程ID
		::GetWindowThreadProcessId(hwnd, &idPprocess);
		if (idPprocess == pArg->dwProcessId)
			pArg->vecHWnds->push_back(hwnd);
	}

	return TRUE;
}

//获取一个进程下所有的窗口
void GetHWndsByProcessID(DWORD processID, std::vector<HWND> &vecHWnds)
{
	EnumHWndsArg infoWin;
	infoWin.dwProcessId = processID;
	infoWin.vecHWnds = &vecHWnds;
	::EnumWindows(lpEnumFunc, (LPARAM)&infoWin);
}

3. 判断某个窗口是否为主窗口

  在一个Word进程中,一般有一个或者多个主窗口,主窗口的特点是具有标题、可见、没有父窗口,因此判断一个窗口是否为主窗口的函数为:

//判断一个窗口是否为主窗口
bool IsMainWindow(HWND hWnd)
{
	if (!::IsWindow(hWnd))
		return false;

	SWinInfo cWndInfo;
	GetWindowInfo(hWnd, cWndInfo);
	DWORD dwVisibleStyle = WS_VISIBLE;
	bool bRet = _tcslen(cWndInfo.pszTitle)
		&& (cWndInfo.lStyle & dwVisibleStyle)
		&& !cWndInfo.hOwner;
	return bRet;
}

4. 判断word进程下面哪个窗口是word客户区所对应的窗口

  上述标题起的有点绕口,其实也表明这个问题有点复杂。首先,我们可以获取一个word进程下的所有顶级窗口,也可以判断这些窗口中哪些是主窗口。但现在的问题是:我们如何根据主窗口得到word文档对应的COM对象?
  经过反复查阅资料,得出了一个基本思路:首先,找到word进程下的主窗口(可能有多个,例如你同时打开几个word文档,在任务管理器上可以看到只有一个word进程);然后,依次迭代主窗口下的子窗口,并找到其中一个名字为"_WwG"的窗口,这个窗口实际上就是word的客户区(就是我们编辑文本的那个窗口),至于为什么窗口类名称为"_WwG",下文会有解释;最后,利用COM提供的AccessibleObjectFromWindow函数返回客户区的接口指针。
  以下为实现例程。

//根据窗口句柄获取窗口信息
void GetWindowInfo(HWND hWnd, SWinInfo& cWndInfo)
{
	cWndInfo.hWnd = hWnd;
	cWndInfo.hParent = GetParent(hWnd);
	cWndInfo.hOwner = GetWindow(hWnd, GW_OWNER);
	cWndInfo.lStyle = GetWindowLong(hWnd, GWL_STYLE);
	::GetWindowText(hWnd, cWndInfo.pszTitle, MAXTITLELEN);
	::GetClassName(hWnd, cWndInfo.pszWinClass, MAXCLASSLEN);
	cWndInfo.idThread = ::GetWindowThreadProcessId(hWnd, &cWndInfo.idProcess);
}

BOOL CALLBACK NextExcelChildWindow(HWND hWnd, LPARAM lParam)
{
	SWinInfo* pWinInfo = (SWinInfo*)lParam;
	TCHAR psz[MAXCLASSLEN] = { 0 };
	::GetClassName(hWnd, psz, MAXCLASSLEN);
	if (_tcscmp(psz, _T("EXCEL7")) == 0)
	{
		GetWindowInfo(hWnd, *pWinInfo);
		return FALSE;
	}
	return TRUE;
}

//根据主窗口的句柄得到EXCEL的COM对象
LPDISPATCH ExcelComFromMainWindowHandle(HWND hMainWin)
{
	SWinInfo cWinInfo;
	::EnumChildWindows(hMainWin, NextExcelChildWindow, (LPARAM)&cWinInfo);
	if (_tcscmp(cWinInfo.pszWinClass, _T("EXCEL7")) == 0)
	{
		void* pVoid = NULL;
		if (S_OK == AccessibleObjectFromWindow(cWinInfo.hWnd, OBJID_NATIVEOM, IID_IDispatch, &pVoid))
			return (LPDISPATCH)pVoid;
	}
	return NULL;
}

//获取Word的窗口COM对象
BOOL CALLBACK NextWordChildWindow(HWND hWnd, LPARAM lParam)
{
	SWinInfo* pWinInfo = (SWinInfo*)lParam;
	TCHAR psz[MAXCLASSLEN] = { 0 };
	::GetClassName(hWnd, psz, MAXCLASSLEN);
	if (_tcscmp(psz, _T("_WwG")) == 0) //word文档窗口的窗口类名称为"_WwG"
	{
		GetWindowInfo(hWnd, *pWinInfo);
		return FALSE;
	}
	return TRUE;
}

//根据主窗口的句柄得到Word的窗口COM对象
LPDISPATCH WordComFromMainWindowHandle(HWND hMainWin, SWinInfo& cWinInfo)
{
	cWinInfo.Reset();
	::EnumChildWindows(hMainWin, NextWordChildWindow, (LPARAM)&cWinInfo);
	if (_tcscmp(cWinInfo.pszWinClass, _T("_WwG")) == 0)
	{
		void* pVoid = NULL;
		if (S_OK == AccessibleObjectFromWindow(cWinInfo.hWnd, OBJID_NATIVEOM, IID_IDispatch, &pVoid))
			return (LPDISPATCH)pVoid;
	}
	return NULL;
}

  上述例程中WordComFromMainWindowHandle接口可以获取某个主窗口下对应的word客户区对象接口指针。
  我们重点解释下下面几个函数

BOOL EnumChildWindows( HWND hWndParent,
WNDENUMPROC lpEnumFunc,
LPARAM lParam
);

  EnumChildWindows会枚举父窗口的所有子窗口,并且将子窗口的句柄传给回调函数lpEnumFunc。因此在函数WordComFromMainWindowHandle中我们枚举主窗口所有的子窗口,并且在回调函数NextWordChildWindow中判断这个窗口的窗口类名称是不是"_WwG",如果是,我们把这个子窗口句柄保存下来。

STDAPI AccessibleObjectFromWindow(
HWND hwnd,
DWORD dwObjectID,
REFIID riid,
void** ppvObject);

  AccessibleObjectFromWindow函数可以获取指定窗口关联的COM对象接口,这里面第二个参数是对象ID,是标准对象标识符常量值之一;或者是自定义的对象ID,比如OBJID_NATIVEOM,就是Microsoft Office本机对象模型的ID。
  若要获取指向本机对象模型支持的类的 IDispatch 接口指针,请在 dwObjectID 中指定OBJID_NATIVEOM。使用此对象标识符时,hwnd 参数必须与以下窗口类类型匹配。从下表可以看出,word对应的窗口类名称为"_WwG"。第三个参数是IID_IAccessible 或者 IID_IDispatch,这里取IID_IDispatch。
在这里插入图片描述

5. 获取所有word文档的信息

  做完了上述工作,基本就大功告成了,接下来,我们来获取所有正在运行的word文档的内容,在对话框类中添加以下成员函数。在GetDocsCont函数中,我们取出所有名称为"WINWORD.EXE"的进程ID,然后依次取出每个进程ID下面所有的窗口,并找到其中的主窗口,然后根据主窗口的句柄,得到word客户区窗口的COM对象,进而读取对应的文档文字内容。

void CGetAllWordInstancesDlg::GetDocsCont(MapDocTitle2Cont& mapDocTitle2String)
{
	mapDocTitle2String.clear();
	std::vector<DWORD> aridProcess;
	GetProcessIDByName(_T("WINWORD.EXE"), aridProcess);	//获取WORD进程
	for (int i = 0; i < aridProcess.size(); i++)
	{
		DWORD idProcess = aridProcess[i];
		std::vector<HWND> vtHWnds;
		GetHWndsByProcessID(idProcess, vtHWnds);	//取出该进程中所有对话框
		HWND hMainWin;
		for (int i = 0; i < vtHWnds.size(); i++)
		{
			hMainWin = vtHWnds[i];
			if (IsMainWindow(hMainWin))
			{
				LPDISPATCH pDispatch = NULL;
				SWinInfo cWinInfo;
				pDispatch = (LPDISPATCH)WordComFromMainWindowHandle(hMainWin, cWinInfo);
				if (pDispatch)
				{
					CString sContent;
					VARIANT vt;
					vt.vt = VT_I4;
					vt.lVal = i;
					CWordWindow wordDocWindow;
					wordDocWindow.AttachDispatch(pDispatch);
					CWordDocument doc = wordDocWindow.get_Document();
					CString sTitle = doc.get_FullName();
					CWordParagraphs paragraphs = doc.get_Paragraphs();
					for (int i = 1; i < paragraphs.get_Count() + 1; i++)
					{
						CWordParagraph paragraph = paragraphs.Item(i);
						CWordRange range = paragraph.get_Range();
						sContent += range.get_Text();
					}
					sContent.Replace(_T("\r"), _T("\r\n"));
					mapDocTitle2String[sTitle] = sContent;
				}
			}
		}
	}
}

void CGetAllWordInstancesDlg::OnBnClickedButton1()
{
	GetDocsCont(m_mapDocTitle2String);
	m_wndLstProcess.DeleteAllItems();
	mapIter it;
	CString str;
	int iItem = 0;
	for (it = m_mapDocTitle2String.begin(); it != m_mapDocTitle2String.end(); it++)
	{
		str.Format(_T("%d"), iItem +1);
		m_wndLstProcess.InsertItem(iItem, str);
		m_wndLstProcess.SetItemText(iItem, 1, it->first);
		iItem++;
	}
}

  在OnBnClickedButton1方法中,我们调用了GetDocsCont,后者找到了不同文档名称对应的文档内容。

6.对话框其他接口

  对话框还需要补充一个接口,根据用户点击ClistCtrl中的不同项目,读取对应文档的文字内容(不含图片、表格等,仅是文字)。

void CGetAllWordInstancesDlg::OnNMClickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);

	//进行单击检测,这个结构已经被扩展为能够适应子项的单击检测。
	int iCurRow;
	LVHITTESTINFO cHitTest;
	cHitTest.pt = pNMItemActivate->ptAction;
	if (-1 !=m_wndLstProcess.SubItemHitTest(&cHitTest))	//检测给定坐标位于哪个单元格上
	{
		if (cHitTest.flags & LVHT_ONITEMLABEL)
		{
			iCurRow = cHitTest.iItem;
			CString sWinHandle = m_wndLstProcess.GetItemText(iCurRow, 1);
			if (m_mapDocTitle2String.end() != m_mapDocTitle2String.find(sWinHandle))
			{
				CString sCont = m_mapDocTitle2String[sWinHandle];
				m_wndEdt.SetWindowText(sCont);
			}
		}
	}

	*pResult = 0;
}

OK, that is all!写作不易,如果大家觉得对自己有点帮助,麻烦点个赞吧!

参考文章

  1. 《Get list of all open word documents in all Word instances》 https://social.microsoft.com/Forums/zh-CN/fd0411cb-dba4-48a9-acf7-2575ade4e597/get-list-of-all-open-word-documents-in-all-word-instances
  2. 《Get a Collection of All Running Excel Instances》 https://www.codeproject.com/Tips/1080611/Get-a-Collection-of-All-Running-Excel-Instances
  3. 《C++通过COM操作EXCEL》 https://blog.csdn.net/litterCooker/article/details/81538461
  4. 《VBA关于Word Windows对象参考》 https://learn.microsoft.com/en-us/office/vba/api/word.window

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

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

相关文章

数字ic验证工程师经典笔试面试题(含答案)

数字ic验证工程师在找工作时&#xff0c;刷笔试面试题必不可少&#xff0c;在面试前做好充足的准备才能抓住更多的机会&#xff0c;今天小编为大家准备了数字ic验证工程师大厂面试常用笔试面试题。 下列关于代码覆盖率描述错误的是&#xff1a;CD A.代码覆盖率包括语句覆盖率…

用CD4051 实现八档位可变 / 可编程增益同相比例运放电路

CD4051 相当于一个单刀八掷的开关&#xff0c;一个公共端对应另一边八个档位&#xff0c;如下图。左边的Z 就是公共端的“单刀”&#xff0c;右边Y0 到Y7 是“八掷”&#xff0c;用A0、A1、A2 三位选择这八个档位。基于CD4051 的变增益电路实现的原理是一致的&#xff0c;只是细…

国际十大正规现货黄金交易平台排名(2023年优质版)

在现今这个时代&#xff0c;投资理财是在平常不过的了&#xff0c;但是在投资市场中黄金是最为热门的产品之一&#xff0c;而黄金投资市场中现货黄金因行情变化快、盈利机会多、多空双向交易机制而获得人们广泛喜爱和选择的主要理由&#xff0c;由于现货黄金的发展史起源于国外…

《PyTorch 深度学习实践》第12讲 循环神经网络(基础篇)

文章目录 1 什么是RNN&#xff1f;1.1 原理1.2 维度说明 2 一些琐碎代码2.1 RNNCell2.2 RNN2.3 RNN参数&#xff1a;batch_first 3 例子&#xff1a;序列变换把 "hello" --> "ohlol"3.1 使用RNNCell3.2 使用RNN3.3 使用embedding and linear layer嵌入&…

迅为iTOP4412精英版Visual Studio Code 插件安装

我们在此以 ubuntu 环境为例&#xff0c;讲解 Visual Studio Code 插件安装。 VSCode 支持多种语言&#xff0c;比如 C/C、Python、C#等等&#xff0c;对于嵌入式开发的我们主要用来编写 C/C程 序的&#xff0c;所以需要安装 C/C的扩展包&#xff0c;扩展包安装很简单&#x…

【 SpringBoot单元测试 和 Mybatis 增,删,改 操作 】

文章目录 一、Spring-Boot单元测试(了解)1.1 概念1.2 单元测试引用1.3 单元测试的实现1.4 简单的断言说明1.5 单元测试优点 二、Mybatis 增&#xff0c;删&#xff0c;改 操作2.1 增加⽤户操作2.2 修改⽤户操作2.3 删除⽤户操作 一、Spring-Boot单元测试(了解) 1.1 概念 单元测…

Java设计模式-day02

4&#xff0c;创建型模式 4.2 工厂模式 4.2.1 概述 需求&#xff1a;设计一个咖啡店点餐系统。 设计一个咖啡类&#xff08;Coffee&#xff09;&#xff0c;并定义其两个子类&#xff08;美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】&#xff09;&#xff1b;再设…

找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll

找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll 找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll 找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll 链接&#xff1a;https://pan.baidu.com/s/15O9cRwexHp4nzZj8eYVfnw 提取码&#xff1a;4iyr --来自百度…

FPGA基于XDMA实现PCIE X8的HDMI视频采集 提供工程源码和QT上位机程序和技术支持

目录 1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取 1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PC…

No.044<软考>《(高项)备考大全》【第27章】运筹学计算(典型考题思路讲解)

【第27章】运筹学计算&#xff08;典型考题思路讲解&#xff09; 1 章节概述1.1 运筹学计算涉及到的题型2 最优的函数值3 线性规划题1题2题3 4 动态规划 投资收益最大的问题5 最小生成树题1题2题3 6 匈牙利法题1题2 7 最短最长路径问题题1题2题3题4题5题6题7 8 最大流量问题9 决…

Java-String类

文章目录 写在前面1 String类的常用方法1.1 字符串的构造1.2 String对象的比较1. 利用 比较是否引用同一对象2. 利用equals() 方法比较3. 利用compareTo 方法比较两个字符串的4.利用compareToIgnoreCase方法比较(忽略大小写) 1.3字符串查找1.4转化1. 数值和字符串的转化2. 大小…

关于java.io的学习记录(读取文本)

可以通过字节流&#xff08;FileInputStream&#xff09;、字符流&#xff08;InputStreamReader&#xff09;、字符缓冲流&#xff08;BufferedReader&#xff09;读取文本中的数据。 1、FileInputStream读取文本 public void read(){String path "fileTest.txt";F…

浅测SpringBoot环境中使用WebSocket(多端实时通信)

目录 概述 测试&#xff1a;前端代码 后端代码&#xff08;SpringBoot环境&#xff09; 1.创建处理器类&#xff08;用于处理连接和消息&#xff09; 2.创建配置类&#xff08;用于注册处理器类&#xff0c;开启WebSocket&#xff09; 连接测试 概述 这篇博客主要是记录测试…

怎么将图片变成圆角矩形,2种方法可供选择

怎么将图片变成圆角矩形&#xff1f;我们在一些职场工作中&#xff0c;可能会遇到需要把图片变成圆角矩形的这样的要求&#xff0c;我们能够理解公司或领导&#xff0c;这样要求的用意是为了让我们的图片展示更加美观整齐&#xff0c;让我们的用户让我们的客户看起来更加美丽整…

android:手搓一个即时消息聊天框(包含消息记录)

先看一下效果 1.后端 要实现这个&#xff0c;先说一下后端要实现的接口 1.创建会话id 传入“发送id”和“接收id”给服务端&#xff0c;服务端去创建“会话id” 比如 get请求&#xff1a;http://xxxx:8110/picasso/createSession?fromUserId1&toUserId2 返回seesionId…

RabbitMQ防止消息丢失

生产者没有成功把消息发送到MQ 丢失的原因 &#xff1a;因为网络传输的不稳定性&#xff0c;当生产者在向MQ发送消息的过程中&#xff0c;MQ没有成功接收到消息&#xff0c;但是生产者却以为MQ成功接收到了消息&#xff0c;不会再次重复发送该消息&#xff0c;从而导致消息的丢…

直播授课在线课堂学习平台的设计与实现nodejs+vue

在各学校的教学过程中&#xff0c;直播授课管理是一项非常重要的事情。随着计算机多媒体技术的发展和网络的普及&#xff0c;“基于网络的学习模式”正悄无声息的改变着传统的直播学习模式&#xff0c;“基于网络的直播教学平台”的研究和设计也成为教育技术领域的热点课题。1、…

金融数据获取:获取网站交互式图表背后的数据,Headers模拟浏览器请求,防盗链破解及Cookie验证

有时候写爬虫难免会遇上只提供一张图表却没有背后结构化数据的情况&#xff0c;尤其是金融市场数据&#xff0c;例如股票K线&#xff0c;基金净值等。笔者看了市面上主流的行情网站&#xff0c;基本都是图一这样的交互式图表&#xff0c;但这样的图表是没有办法直接拿到原始数据…

前端技术学习第八讲:VUE基础语法---初识VUE

VUE基础语法—初识VUE 一、初识VUE Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是&#xff0c;Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层&#xff0c;不仅易于上手&#xff0c;还便于与第三方库…

如何快速白嫖一个SSL证书

首先呢&#xff0c;还是要非常感谢各大云厂商能够为白嫖党免费提供申请SSL证书的这个一个平台。 SSL证书作用不在描述&#xff0c;自行百度&#xff1f; 本篇主要从百度云、腾讯云、阿里云讨论下 阿里云 地址&#xff1a;阿里云-计算&#xff0c;为了无法计算的价值 首先需…