cef js返回c++的代码_CEF3开发者系列之外篇——IE中JS与C++交互

使用IE内核开发客户端产品,系统和前端页面之间的交互,通常给开发和维护带来很大的便利性。但操作系统和前端之间的交互却是比较复杂的。具体来说就是脚本语言和编译语言的交互。在IE内核中html和css虽然不兼容,但是IE编程接口是完全一样的,这得益于微软的COM组件的结构化设计和实现。所以与IE交互,必须得先说一下COM,COM全称组件对象模型(Component Object Model)。

COM的基本思想很简单,所有的组件模块都提供一个最根本的接口, IUnkown,它有三个方法,AddRef和Release实现了引用计数,QueryInterface实现了根据接口id查询另外的接口,所有的接口都从IUnkown派生。基于IE内核做开发,有一个接口是最关键的,IDispatch(欲知详情移步IDispatch接口 - GetIDsOfNames和Invoke)。 IDispatch接口是COM自动化的核心。其实,IDispatch这个接口本身也很简单,只有4个方法:最关键的两个方法Invoke和 GetIDsOfNames。脚本语言和编译型语言之间进行通信是通过IDispatch接口来行的。下面看一下这个关键的方法的原型:

IDispatch:public IUnkown

{

//...

HRESULT Invoke( DISPID dispIdMember,REFIID riid, LCID lcid,WORD wFlags,DISPPARAMS FAR* pDispParams,VARIANT FAR* pVarResult, EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr );

//...

}

这个方法每个参数的意义msdn上有详细的阐述,就我的理解而看,它作为一个组件, 向外提供了一个万能接口,据此可以实现两个很有用的功能:

1. 获取和设置组件的属性变量. 对应wFlags的DISPATCH_PROPERTYGET和DISPATCH_PROPERTYPUT

2.以任意参数调用任意一个被支持的方法. 对应wFlags的DISPATCH_METHOD

用面向对象的观点来看,有了这两个功能,任意一个实现该接口的组件就抽象成同一个接口实现的万能对象,可以通过指定的字符串名字获取设置属性和调用方 法。如果有过脚本编程经验开发人员一定会发现,脚本恰恰就是如此。c++书写代码的时候也是通过名字访问对象,但编译好后变成二进制代码后就没有名字的概念了,只有偏移和地址。而脚本里头书写代码的时候是通过名字,解释执行的时候也是通过名字。脚本和native语言的最大区别是脚本对象的所有属性和方法 是动态的,在执行的时候还可以修改。看到这里,很容易联想到实现了IDispatch的组件对象具有了脚本的特性,c++对象被脚本化了!这就意味着你可 以把原来用 c++写的类的所有属性和方法都通过Invoke来执行,在脚本里头可以直接访问!相当于给脚本增加了native的扩展。IDispatch接口很重要 的一个功能就是如此,微软通常所说的双接口就是这个意思。了解了这些,接下来要和JS脚本交互就比较容易了。

既然是IE内核里的JS与C++互相调用,我们先来简单的了解下IE内核编程需要的几个常用接口。说多了不好理解,先来看图。

IWebBrowser2,

IHTMLWindow2,IHTMLDocument2 这三个常用接口都是从IDispatch

派生的。IWebBrowser2接口里主要提供浏览器常规功能如打开URL、前进、后退等功能。IHTMLWindow2主要是提供接口操作浏览器中打

开的window对象,IHTMLDocument2获取文档相关信息,以及审查和修改HTML元素和文档中文本,包括获取JS对象。

IHTMLWindow2

对应于一个window的视图,IHTMLDocument2是IHTMLWindow2渲染文档,对应着dom树结构。在js中有两个全局对象

window和document,分别对应着IHTMlWindow2和 IHTMLDocument2。

想要详细了解这些,参看如下资料:

要完成c++和js交互,可以分解成两个任务,一是c++调用js代码;二是js调用c++代码,这其实也所有脚本和natvie交互的两个基本任务。本文主要根据自己的理解从设计开发的角度去阐述为什么要这么做。

C++调用JS

每段js执行代码都有它自己的执行环境,在IE里面可以看做是IHTMLWindow2。

据上边所讲,我们用先获取全局对象document,从document变成的IDispatch接口中得到

IHTMLDocument2,IHTMLDocument2接口的get_Script方法获取到了HTML文档中JS代码的IDispatch接口,

我们用IDispatch接口,把HTML文档中的JS代码当作一个COM对象,对他进行操作。

CComPtr GetScript()

{

CComPtr spWebBrowser;

HRESULT hResult = QueryControl(IID_IWebBrowser2, (void**)&spWebBrowser);

if (SUCCEEDED(hResult))

{

CComPtr spDocDisp;

hResult = spWebBrowser->get_Document(&spDocDisp);

if (SUCCEEDED(hResult))

{

CComPtr spDocDisp2;

hResult = spDocDisp->QueryInterface(IID_IHTMLDocument2, (void**)&spDocDisp2);

if (SUCCEEDED(hResult))

{

CComPtr spScript;

hResult = spDocDisp2->get_Script(&spScript);

if (SUCCEEDED(hResult))

{

return spScript;

}

}

}

}

}

有两种方案可以执行JS,一种是直接调用IHTMLWindow2的execScript方法.

HRESULT execScript( BSTR code,    BSTR language, VARIANT *pvarRet);

代码示例:

wstring strJavaScript;

CComVariant pvarRet;

CComBSTR bstrJavaScript(strJavaScript.c_str());

CComBSTR bstrScriptType(_T("javascript"));

CComQIPtr spWindow2(spScriptDisp);

spWindow2->execScript(bstrJavaScript, bstrScriptType, &pvarRet);

要看懂这段代码不难,我们先来了解下CComQIPtr,用IDispatch接口调用COM对象的各种方法、设置与获取COM对象的属性、让COM对象 回调我们,都是用IDispatch的Invoke方法来实现。一个Invoke就要实现那么多功能,用起来当然很麻烦。不过好在ATL智能指针类中的 CComDispatchDriver(即CComQIPtr)封装了IDispatch接口,使用起来非常方便!先拿到IHTMLWindow2接口的智能指针,直接把js代码环境IDispatch指针的赋值给它。不过注意这里是BSTR的字符串,可以用 SysAllocString来分配。

第二种方案同样是使用IHTMLDocument的get_Script()方法。它能得到一个IDispatch指针,这个IDispatch就是IHTMLDocument里的JS。按照前面介绍的IDispatch的使用,你通过它就可以调用任意js函数了。例如要执行一个 js中的函数 function。

CComPtr spDocDisp2;

spDocDisp2->get_Script(&spScript);

OLECHAR * Names= L"function" ;

DISPID dispID=0;

//先获取接受调度标示符DISPID,需要调用GetIDsOfNames来获取

spScript->GetIDsOfNames(IID_NULL,&Names,1,LOCALE_SYSTEM_DEFAULT, &dispID);

//通过Invoke(援引)方法调用JS方法

spScript->Invoke(dispID,,IID_NULL ,LOCALE_SYSTEM_DEFAULT,DISPATCH_METHOD,NULL,NULL,NULL,NULL);

这里function是js里面的一个全局函数。这里可以看到 Invoke并没有直接把字符串名字拿过来用,而是通过另一个方法GetDispofNames做了一个映射,获取接受调度标示符DISPID。通过 IHTMLDocument得到的script接口对应着该页面的JS全局环境,从中可以通过多次invoke得到任意一个全局变量,函数,从而能够得到对象的成员变量或成员方法。

第二种方案就是通过Invoke调用来实现在c++中存取js变量和调用函数。这和第一种方案的区别很明显,一个是在用c++写js代码,有点类似自己在解析执行js了,而前者更简单,再复杂的js调用序列,一个字符串全部搞定。

要做到c++和脚本交互有一个基本的问题要做好,就是脚本中的数据类型和c++中的数据类型如何对应起来。众所周知,js中有很多类型,Boolean, Number, String, Object, Array , Function等。写到这里,插一句,基本所有的语言里头都有字符串和数字这两种基本的数据类型(c/c++中仅为以\0结尾的字符数组),面向对象的 语言中还会有Object这样的复合数据类型。在Invoke调用参数中, VARAINT就代表了c中的基本数据类型,js中的数字会转换成VT_I4或者VT_R4或VT_R8。字符串会转换成VT_BSTR类型的 bstr(这是微软com标准里使用的字符串类型),其他所有的复合类型包括对象数组函数在c中都对应着VT_DISPATCh的一个IDispatch 指针。有了IDispatch指针,你就可以按照前面的方法任意存取对象的属性,也可以发起函数调用并获得返回值。了解了这些,就可以进行c与js的交互 了,它们都通过IDispatch的invoke调用来完成。CComDispatchDriver对GetIDsOfNames和Invoke进一步进 行了封装,只需更少的参数即方便可调用。

Invoke0    //调用0个参数的方法

Invoke1    //调用1个参数的方法

Invoke2    //调用2个参数的方法

InvokeN    //调用多个参数的方法

说了这么多,估计有些人看得云里雾里的。下边直接给出例子:

我们动手写一个HTML,其中包含这样一段JS代码:

function Add(value1, value2) {

return value1 + value2;

}

然后我们用WebBrowser加载这个HTML后,在VC中这样来调用这个函数名为Add的JS函数:

//别忘了#include

//m_WebBrowser是一个WebBrowser的Activex控件对象。

CComQIPtr spDoc = m_WebBrowser.get_Document();

CComDispatchDriver spScript;

spDoc->get_Script(&spScript);

CComVariant var1 = 10, var2 = 20, varRet;

spScript.Invoke2(L"Add", &var1, &var2, &varRet);

spScript.Invoke2的作用是调用JS函数中名为Add的函数,传入两个参数,用varRet接收返回值。Invoke2调用成功后,varRet得到了返回值30。

但这样的话一次只能接受一个返回值。如果要一次接受多个返回值的话,怎么办呢?

我们可以让JS返回一个JS中的Array数组或Object对象。

当 JS函数return一个Array或一个Object对象时,VC这边的 varRet将接受到一个代表该对象的IDispatch接口。我们仍然用CComDispatchDriver来管理这个IDispatch。 CComDispatchDriver有四个方法:

GetProperty

GetPropertyByName

PutProperty

PutPropertyByName

来从这个Array或Object对象中取出我们要的数据。

实践是检验真理的唯一标准,让我们再来写一个JS函数:

function Add(value1, value2) {

var array = new Array();

array[0] = value1;

array[1] = value2;

array[2] = value1 + value2;

return array;

}

然后在VC中这样写:

CComQIPtr spDoc = m_WebBrowser.get_Document();

CComDispatchDriver spScript;

spDoc->get_Script(&spScript);

CComVariant var1 = 10, var2 = 20, varRet;

spScript.Invoke2(L"Add", &var1, &var2, &varRet);

CComDispatchDriver spArray = varRet.pdispVal;

//获取数组中元素个数,这个length在JS中是Array对象的属性

CComVariant varArrayLen;

spArray.GetPropertyByName(L"length", &varArrayLen);

//获取数组中第0,1,2个元素的值:

CComVariant varValue[3];

spArray.GetPropertyByName(L"0", &varValue[0]);

spArray.GetPropertyByName(L"1", &varValue[1]);

spArray.GetPropertyByName(L"2", &varValue[2]);

可以看到,10,20,30,这三个JS函数返回的值已经躺在我们的varValue[3]里了。

当然,如果不知道JS返回的Array对象里面有几个元素,我们可以在VC这边获取它的length属性,然后在一个循环中取出数组中的每个值。

如果我们的JS函数返回一个包含有多个属性值的Object对象,VC这边该如何接收呢?

让我们再来写一个JS函数:

function Add(value1, value2) {

var data = new Object();

data.result = value1 + value2;

data.str = "Hello,World!";

return data;

}

然后在VC中我们这样接收:

CComQIPtr spDoc = m_WebBrowser.get_Document();

CComDispatchDriver spScript;

spDoc->get_Script(&spScript);

CComVariant var1 = 10, var2 = 20, varRet;

spScript.Invoke2(L"Add", &var1, &var2, &varRet);

CComDispatchDriver spData = varRet.pdispVal;

CComVariant varValue1, varValue2;

spData.GetPropertyByName(L"result", &varValue1);

spData.GetPropertyByName(L"str", &varValue2);

我 们从JS返回的Object对象里取出了它的两个属性,result和str,分别是一个整形数据和一个字符串。这里JS代码是我们自己写的,在VC这边 当然事先知道这个JS函数返回的对象有result和str这两个属性。如果JS代码不是我们写的,或者它的属性是事先不能确定的,该怎么办呢?答案是使用IDispatchEx接口来枚举这个对象的相关信息(方法名、属性名)。

C++调用JS的实例演示到此为止。

js调用c++代码

按照前面所说的IDispatch的用途,就可以推断出如何做到这一点了,自定义一个c++类,实现一个IDispatch的接口,把它的指针通过某次 js调用作为返回值返回给js,那么js代码中就持有该对象了,就可以像使用普通js对象一样的使用它。问题是,一开始js啥都没有,怎么直接调到c++ 里头从而返回c++对象呢?IE已经考虑好了这个问题,它对于每个IWebbrowser2实例(顶层)有一个内置的IDispatch对象,该对象可以 在创建浏览器控件实例之后在c++中自己制定,而在js中则使用window.external来访问。也就是说每个js环境都已经内置了一个全局对象 external,并且它对应的c++中的IDispatch可以由程序员自己指定。下面谈一下如何来设置这个对象实例。

在windows中要自己host一个active控件,如果用sdk自己写。其中有一个接口叫IDocHostUIHandler ,它有一个方法GetExternalDisp用以向宿主查询一个IDispatch对象,就直接对应着js中的external脚本对象。 IDocHostUIHandler 还有一个有用的方法ShowContextMenu,当要show菜单的时候这个方法会被回调,应用程序就可以自定义菜单了。MFC也可以很方便的 host一个IE控件,但它的类库太庞大了,幸亏微软又出了ATL,提供了一个轻量级的方法让你可以达到同样的效果。下面直接贴代码片段.

class CWebBrowser : public CAxHostWindow

{

private:

CComPtr m_pWebBrowser; //保存创建出来的浏览器控件实例

BEGIN_MSG_MAP(CWebBrowser)

MESSAGE_HANDLER(WM_CREATE,OnCreate)

CHAIN_MSG_MAP(CAxHostWindow)

END_MSG_MAP()

LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)

{

// Create WebBrowser object

LPOLESTR pName=NULL;

StringFromCLSID(CLSID_WebBrowser,&pName);

CComPtrdisp;

CComPtr p;

_InternalQueryInterface(IID_IDispatch,(void**)&disp);

// 创建 WebBrowser

CreateControlEx(pName,m_hWnd,NULL,&p,DIID_DWebBrowserEvents2,disp);

CoTaskMemFree(pName);

// 查询IWebBrowser2 接口,用于控制

HRESULT hRet = QueryControl(IID_IWebBrowser2, (void**)&this->m_pWebBrowser);

return m_pWebBrowser?S_OK:-1;

}

}

CWebBrowser 是用户自己的宿主窗口,在它的OnCreate里头创建com对象,一个浏览器窗口就出来了,这个代码是不是很简洁?CAxHostWindow为我们做 了很多事情,包括IDocHostUIHandler也被实现,所以我们从它派生就天然的拥有了很多控制IE控件的能力,当然都是通过com接口来完成 的。以后如果有定制需求,大可重写父类的虚函数来达到目的。CAxHostWindow还封装了一个方法SetExternalDispatch,到这里 一切都可以暂时告一段落了,你可以在CWebBrowser中实现IDispatch也可以单独用一个类来实现,然后把IDispatch接口设进去就可 以了。有兴趣研究这个寄宿控件过程的童鞋们可以看CAxHostWindow的代码实现,全在一个头文件中。

假设你的external提供了一个函数创建对象   function newMyObject,在js中

var newObject=window.external.newMyObject(); //通过external构建一个c++对象交给js持有

alert(newObject.name);       //访问该对象的属性

alert(newObject.GetValue())  //调用该对象的方法

那么你需要做的事情其实还是关注Invoke就可以了.在external的IDispatch的Invoke实现中

STDMETHODIMP CWebBrowserDisp::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, unsigned int* puArgErr)

{

HRESULT nRet = S_OK;

if(wFlags&DISPATCH_METHOD) //属于方法调用

{

//给newMyObject分配的id,字符串名字映射

if(dispIdmember== DISPID_newMyObject)

{

IDispatch* pMyObject=NULL;

//创建c++对象并获取其IDispatch接口

CreateMyObject(&pMyObject);

pVarResult->vt=VT_DISPATCH;

pVarResult->pdispVal=pMyObject; //作为返回值传递给js

}

}

return 0;

}

这个代码也很简洁。据此可以看出,要把c++对象导出到js中,那么该对象必须要实现IDispatch接口,只需要把这个接口作为Invoke的返回值 传给js即可。它有引用计数,不必担心内存的释放问题,在js的垃圾回收被触发的某个时刻自然会被销毁。接下来,MyObject有哪些属性和方法可以被 js调用,那就又归它自己的IDispatch的Invoke实现来关心了。

另外一种就是在webbrowser控件中,JS调用C++方法。如果你对webbrowser控件熟悉的话,这里使用起来就更简单了。Invoke接口实现基本上和上边的类似,唯一不同的是如何让JS调用到本地的C++ 代码。在JS代码中创建了函数window.external.newMyObject()。页面渲染时,会触发浏览器的GETEXTERNAL事件,在浏览器中,通过消息过滤,当消息为WN_GETEXTERNAL时,通过IDispatch接口,获取JS代码需要调用的类。

IDispatch **ppDispatch = (IDispatch**)wParam;

*ppDispatch = &m_superCall;

综上所述,在IE中和c++与js交互,IDispatch扮演了很重要的角色,理解好了它你就可以随心所欲的c++和js的混合编程了。COM接口很不容易理 解,知道怎么用,却难以了解其内部机制。其实,在前面所讲的过程中,IDispatch是自己的代码创建的,和系统完全无关。从c++的语法看,它就是继 承了一个虚基类,实现其全部方法而已,还有就是引用计数。所以,我们完全可以用很简单的c++代码来写自己的IDispatch,不必去理会那么多的COM特性。js执行环境总是在主线程,所以你要知道一点你的对象的方法也总是在主线程被调用。下边给出简单的实现代码:

#include "StdAfx.h"

#include "SQSuperCall.h"

CJSCallC::CJSCallC(void)

{

m_mapFunction[TEXT("FuncTest")] = DISPID_FuncTest;

}

CJSCallC::~CJSCallC(void)

{

}

HRESULT STDMETHODCALLTYPE CJSCallC::GetIDsOfNames(

/* [in] */ __RPC__in REFIID riid,

/* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,

/* [range][in] */ UINT cNames,

/* [in] */ LCID lcid,

/* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)

{

HRESULT hr = NOERROR;

for (UINT nIndex = 0; nIndex < cNames; ++nIndex)

{

wstring strFuntion = rgszNames[nIndex];

map::iterator iter = m_mapFunction.find(strFuntion);

if (m_mapFunction.end() != iter)

{

rgDispId[nIndex] = iter->second;

}

else

{

hr = ResultFromScode(DISP_E_UNKNOWNNAME);

rgDispId[nIndex] = DISPID_UNKNOWN;

}

}

return hr;

}

/* [local] */ HRESULT STDMETHODCALLTYPE CJSCallC::Invoke(

/* [in] */ DISPID dispIdMember,

/* [in] */ REFIID riid,

/* [in] */ LCID lcid,

/* [in] */ WORD wFlags,

/* [out][in] */ DISPPARAMS *pDispParams,

/* [out] */ VARIANT *pVarResult,

/* [out] */ EXCEPINFO *pExcepInfo,

/* [out] */ UINT *puArgErr)

{

if (dispIdMember == DISPID_FuncTest)

{

int paramsCount = pDispParams->cArgs;

if (paramsCount < 2)

return S_FALSE;

VARIANTARG* cmdVar = (VARIANTARG*)(&pDispParams->rgvarg[paramsCount - 1]);

if (!(cmdVar->vt == VT_I4 || cmdVar->vt == VT_BSTR))

return S_FALSE;

int nCmdId = cmdVar->intVal;

cmdVar = (VARIANTARG*)(&pDispParams->rgvarg[paramsCount - 2]);

if( cmdVar->vt != VT_BSTR )

return S_FALSE;

CString csInfos = cmdVar->bstrVal;

wstring strInfos(csInfos);

}

return S_OK;

}

HRESULT STDMETHODCALLTYPE CJSCallC::QueryInterface(

/* [in] */ REFIID riid,

/* [iid_is][out] */

__RPC__deref_out void **ppvObject)

{

//*ppvObject = NULL;

if (riid == IID_IUnknown)

{

*ppvObject = static_cast(this);

}

else if (riid == IID_IDispatch)

{

*ppvObject = static_cast(this);

}

else

{

return E_NOINTERFACE;

}

return S_OK;

}

参考文档:

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

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

相关文章

多线程编程指南 part 2

多线程编程指南Sun Microsystems, Inc.4150 Network CircleSanta Clara, CA95054U.S.A.文件号码819–7051–102006 年10 月版权所有2005 Sun Microsystems, Inc. 4150 Network Circle, Santa Clara, CA95054 U.S.A. 保留所有权利。本文档及其相关产品的使用、复制、分发和反编译…

00030_ArrayList集合

1、数组可以保存多个元素&#xff0c;但在某些情况下无法确定到底要保存多少个元素&#xff0c;此时数组将不再适用&#xff0c;因为数组的长度不可变 2、JDK中提供了一系列特殊的类&#xff0c;这些类可以存储任意类型的元素&#xff0c;并且长度可变&#xff0c;统称为集合 3…

1.3tf的varible\labelencoder

1.tf的varible变量 import tensorflow as tf #定义变量--这里是计数的变量 statetf.Variable(0,namecounter) print (state.name) #输出变量值 onetf.constant(1) #常量new_valuetf.add(state,one) updatetf.assign(state,new_value)#初始化所有变量 inittf.initialize_all_var…

kafka 日志相关配置

日志目录 ${kafka.logs.dir}/server.log &#xff1a;服务器日志${kafka.logs.dir}/state-change.log&#xff1a;状态变化日志${kafka.logs.dir}/kafka-request.log&#xff1a;请求处理日志${kafka.logs.dir}/log-cleaner.log&#xff1a;日志清理${kafka.logs.dir}/control…

linux mc服务器 mod_如何在linux搭建MC服务器

摘要&#xff1a;前言Q&#xff1a;为什么要用linux搭建MC服务器&#xff1f;A&#xff1a;因为linux系统占用率低&#xff0c;跑服务器效果非常好Q&#xff1a;看不懂linux ssh&#xff0c;后期自己无力运维怎么办A&#xff1a;没关系&#xff0c;这个教程是通过安装BT...前言…

flask-SQLAlchemy 使用 session.commit() 处理异常回滚

最为原始的try/except办法&#xff0c;多次插入数据就要写多次&#xff0c;很麻烦&#xff0c;使用python原生的contextlib.contextmanager简化代码&#xff01; try:user_db User(emailself.email, nicknameself.nickname, passwordself.password)db.session.add(user_db)#所…

小米机器人清理主刷和轴承_扫地机主刷怎样更换

原标题&#xff1a;扫地机主刷怎样更换随着电动扫地机逐渐取代手动清洁&#xff0c;电动扫地机的工作量不断增加&#xff0c;从而导致主刷磨损更加严重&#xff0c;并增加了主刷更换的次数。因此&#xff0c;伟顿在这里告诉大家扫地机主刷怎样更换&#xff1a;1 .首先&#xff…

多线程编程指南

1. 多线程编程指南1--线程基础 线程编程指南1--线程基础 Wednesday, 29. March 2006, 11:48:45 多线程 本文出自:BBS水木清华站 作者:Mccartney (coolcat) (2002-01-29 20:25:25) multithreading可以被翻译成多线程控制。与传统的UNIX不同&#xff0c;一个传统 的UNIX进…

路由器和猫的区别

路由器和猫的区别 网络在我们现在生活中必不可少,我们链接互联网经常需要用到猫和路由器,但是依然有很多菜鸟根本不知道什么是猫什么是路由器,至于猫和路由器怎么使用就更不知道了,下面给大家详细的讲解下路由器和猫的区别。 路由器和猫的用途和链接位置不一样,如下图: 路由器:…

Docker 精通之 docker-compose

1、Docker-compose简介 1.1、ocker-compose简介 Docker-Compose项目是Docker官方的开源项目&#xff0c;负责实现对Docker容器集群的快速编排。   Docker-Compose将所管理的容器分为三层&#xff0c;分别是工程&#xff08;project&#xff09;&#xff0c;服务&#xff08…

json数据格式 穗康码_Json数据格式

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式&#xff0c;这些特性使JSON成为理想的数据交换语言。易于人阅读和编写&#xff0c;同时也易于机器解析和生成。JSON 比 XML 更小、更快&#xff0c;更易解析。JSON建构于两种结…

Linux 系统版本查询

显示Linux版本信息输入"cat /proc/version",说明正在运行的内核版本。输入"cat /etc/issue", 显示的是发行版本信息。输入"lsb_release -a ",可对多个linux版本适用。输入"uname -a ",可显示电脑以及操作系统的相关信息。转载于:https…

kafka 命令行命令大全

kafka 脚本 connect-distributed.sh connect-mirror-maker.sh connect-standalone.sh kafka-acls.sh kafka-broker-api-versions.sh kafka-configs.sh kafka-console-consumer.sh kafka-console-producer.sh kafka-consumer-groups.sh kafka-consumer-perf-test.sh kafka-dele…

kotlin将对象转换为map_Kotlin程序将哈希映射(HashMap)转换为列表(List)

Kotlin程序将哈希映射(HashMap)转换为列表(List)在此程序中&#xff0c;您将学习在Kotlin中将map转换为列表的不同方法。示例&#xff1a;将map转换为列表示例import java.util.ArrayListimport java.util.HashMapfun main(args: Array) {val map HashMap()map.put(1, "a…

零元学Expression Blend 4 - Chapter 4元件重复运用的观念

零元学Expression Blend 4 - Chapter 4元件重复运用的观念 原文:零元学Expression Blend 4 - Chapter 4元件重复运用的观念本章将教大家Blend元件重复运用的观念&#xff0c;这在Silverlight设计中是非常重要的&#xff0c;另外加码赠送渐层工具(Gradient Tool)。 ? 本章将教…

Python 内置模块之 ConfigParser - 解析 ini 文件

ini配置文件是被configParser直接解析然后再加载的&#xff0c;如果只是修改配置文件&#xff0c;并不会改变已经加载的配置 INI文件结构简单描述 INI文件就是扩展名为“ini”的文件。在Windows系统中&#xff0c;INI文件是很多&#xff0c;最重要的就是“System.ini”、“Sy…

电脑老是弹出vrvedp_m_出现三个可疑进程vrvedp_m.exe vrvrf_c.exe vrvsafec.exe

满意答案 你机器里装了北信源的DeviceRegist软件,这个软件不是杀毒软件或者防毒软件,而是一个远程桌面管理软件。这类软件其实和木马程序原理上一样,只不过是正规软件公司开发的,但是流氓程度不容小觑,即使在安全模式下也会加载vrvrf_c.exe,vrvedp_m.exe,vrvsafec.exe,wat…

音视频编解码 文件格式 协议内容详解

编解码学习笔记&#xff08;一&#xff09;&#xff1a;基本概念 媒体业务是网络的主要业务之间。尤其移动互联网业务的兴起&#xff0c;在运营商和应用开发商中&#xff0c;媒体业务份量极重&#xff0c;其中媒体的编解码服务涉及需求分析、应用开发、释放license收费等等。最…

git 拉取远程其他分支代码_【记录】git 拉取远程分支代码,同步到另一个git上...

最近有需求从某git 上拉取所有分支代码同步到另一git上&#xff0c;现记录操作步骤&#xff0c;以便日后使用&#xff1a;1&#xff1a;先克隆其中一个分支代码到本地环境git clone -b test http://账号:密码XXX.git2&#xff1a;查看本地分支git brach3&#xff1a;查看远程分…

WIN下的CMD下载命令

certutil -urlcache -split -f 远程地址 本地保存的文件跑径与文 件名 # 如里不写本地文 件名与路径名&#xff0c; 会自动跟远程文 件名相同&#xff0c; 并保存到当前目 录下另一个是&#xff1a; bitsadmin /rawreturn /transfer getfile http://download.sysinternals.com…