当前位置: 首页 > 编程日记 > 正文

如何定制一款12306抢票浏览器——处理预订页面和验证码自动识别功能

判断是否进入预订页面

我们先看一下预订页面的结构(转载请指明出于breaksoftware的csdn博客)


可以见得,这个页面也是嵌入了两个IFrame。关于IFrame的跨域问题,我已经在前一篇文章中讲述了解决办法。

我判断是否是预订页面是通过两个依据:

1 URL是否是http://www.12306.cn/mormhweb/kyfw/

2 是否可以在最里层IFrame中找到class是“table_qr”的元素该元素对应于

具体的查找过程我这儿就不再赘述,我们通过代码来解读

BOOL CDeal12306WebPage::IsBookingPage( CComPtr<IHTMLDocument2> & spDoc, CComBSTR & bstrUrl )
{HRESULT hr = E_FAIL;do  {CString cstrUrl = CString((LPWSTR)bstrUrl);if ( 0 == cstrUrl.CompareNoCase(LOGIN12306URL) ) {CComPtr<IHTMLElement> spTableQrTbody;hr = GetTableQrTbody( spDoc, spTableQrTbody);CHECKHRPOINTER(hr, spTableQrTbody);}} while (0);return FAILED(hr) ? FALSE : TRUE;
}
HRESULT CDeal12306WebPage::GetTableQrTbody( CComPtr<IHTMLDocument2> & spDoc,CComPtr<IHTMLElement> & spElem )
{HRESULT hr = E_FAIL;do  {CComPtr<IHTMLDocument2> spMainDoc;hr = GetMainDoc( spDoc, spMainDoc);CHECKHRPOINTER(hr, spMainDoc);CComPtr<IHTMLElement> spEnter_wElem;hr = GetEnter_wElement(spMainDoc, spEnter_wElem );CHECKHRPOINTER(hr, spEnter_wElem);CComPtr<IHTMLElement> spForm;hr = GetElementByID( spEnter_wElem, L"confirmPassenger", spForm);CHECKHRPOINTER(hr, spForm);CComPtr<IHTMLElement> spTable;hr = GetElementByClassName( spForm, L"table_qr", spTable);CHECKHRPOINTER(hr, spTable);hr = GetElementByIndex( spTable, 0, spElem);CHECKHRPOINTER(hr, spElem);} while (0);return hr;
}

插入用户信息,并设置相应的选项

我们看下用户填写信息的位置的HTML代码结构


        我们可以看到5个passenger可填写区域。目前只有第一个显示出来,而其他四个还没有显示。在上图的最下面是个超链接,其对应于“添加1位乘车人”按钮。可以想象,该按钮的一个操作就是将不能显示的tr显示出来。我们“人”线程填写用户信息的过程和人的行为是一致的:填写一个人信息后 ,点击“添加1位乘车人”,再填写一个……我们用代码说明这个过程。

HRESULT CDeal12306WebPage::AddPassengerInfo( CComPtr<IHTMLElement>& spTableQrTbody,const VecStSinglePassengerInfo& vecStSingleinfo )
{HRESULT hr = E_FAIL;do {// 下标没有从0开始!int i = 1;for ( VecStSinglePassengerInfoCIter it = vecStSingleinfo.begin(); it != vecStSingleinfo.end();i++ ) {CString cstrPassengerId;cstrPassengerId.Format(PASSENGERID, i);hr = BookSinglePassenger( spTableQrTbody, cstrPassengerId, it);CHECKHR(hr);it++;if ( it != vecStSingleinfo.end() ) {AddPassenger(spTableQrTbody);}}} while (0);return hr;
}

上面代码我们将枚举用户设置的乘客信息。第12行,我们将在table中填写一个乘客信息。第16行,我们将判断最新加入的用户是否是最后一个,如果不是最后一个,则点击“添加1位乘车人”。

HRESULT CDeal12306WebPage::AddPassenger( CComPtr<IHTMLElement> & spTableQrTbody )
{HRESULT hr = E_FAIL;do {CComPtr<IHTMLElement> spTr;hr = GetElementByIndex(spTableQrTbody, 6, spTr);CHECKHRPOINTER(hr, spTr);CComPtr<IHTMLElement> spTd;hr = GetElementByIndex(spTr, 1, spTd);CHECKHRPOINTER(hr, spTd);CComPtr<IHTMLElement> spA;hr = GetElementByIndex(spTd, 0, spA);CHECKHRPOINTER(hr, spA);hr = spA->click();} while (0);return hr;
}

填写每个乘客信息的代码是

HRESULT CDeal12306WebPage::BookSinglePassenger( CComPtr<IHTMLElement> & spElem, const CString& cstrPassengerID, VecStSinglePassengerInfoCIter iter )
{HRESULT hr = E_FAIL;do  {CComPtr<IHTMLElement> spTr;hr = GetElementByID( spElem, cstrPassengerID, spTr );CHECKHRPOINTER(hr, spTr);hr = SetName(spTr, iter->cstrName);CHECKHR(hr);hr = SetCardNo(spTr, iter->cstrCardNo);CHECKHR(hr);hr = SetMobileNo(spTr, iter->cstrMobileNo);CHECKHR(hr);hr = SetTicket(spTr, iter->cstrTicket);CHECKHR(hr);hr = SetCardtype(spTr, iter->cstrCardtype);CHECKHR(hr);hr = SetSeat(spTr, iter->ListSeat);} while (0);return hr;
}

其中填写姓名的操作很简单,只要找到相应控件,并向该控件中插入文字即可

HRESULT CDeal12306WebPage::SetName( CComPtr<IHTMLElement> & spElem, const CString& cstrName )
{return SetInputHelper(spElem, cstrName, 4);
}
HRESULT CDeal12306WebPage::SetInputHelper( CComPtr<IHTMLElement> & spElem, const CString& cstrValue, long lIndex )
{HRESULT hr = E_FAIL;do  {CComPtr<IHTMLElement> spTd;hr = GetElementByIndex( spElem, lIndex, spTd );CHECKHRPOINTER(hr, spTd);CComPtr<IHTMLElement> spInputElem;hr = GetElementByIndex(spTd, 0, spInputElem);CHECKHRPOINTER(hr, spInputElem);CComPtr<IHTMLInputElement> spInput;hr = spInputElem->QueryInterface(IID_IHTMLInputElement, (LPVOID*)&spInput);CHECKHRPOINTER(hr, spInput);hr = spInput->put_value( CComBSTR(cstrValue.GetString()) );CHECKHR(hr);} while (0);return hr;
}

设置席别这类Select选项则稍微复杂点,其实原理是一致的

HRESULT CDeal12306WebPage::SetSeat( CComPtr<IHTMLElement> & spElem, const CString& cstrSeat )
{return SetOptionHelper( spElem, cstrSeat, 2);
}
HRESULT CDeal12306WebPage::SetOptionHelper( CComPtr<IHTMLElement> & spElem, const CString& cstrValue, long lIndex )
{HRESULT hr = E_FAIL;do  {CComPtr<IHTMLElement> spTd;hr = GetElementByIndex( spElem, lIndex, spTd );CHECKHRPOINTER(hr, spTd);CComPtr<IHTMLElement> spSelectElem;hr = GetElementByIndex(spTd, 0, spSelectElem);CHECKHRPOINTER(hr, spSelectElem);hr = SetOptionSelect( spSelectElem, cstrValue);CHECKHR(hr);} while (0);return hr;
}
HRESULT CDeal12306WebPage::SetOptionSelect( CComPtr<IHTMLElement> & spElem, const CString& cstrValue )
{HRESULT hRes = E_FAIL;HRESULT hr = E_FAIL;do {CComPtr<IHTMLElementCollection> spElemCollection;hr = GetElementCollection(spElem, spElemCollection );CHECKHRPOINTER(hr, spElemCollection);long lCount = 0;hr = spElemCollection->get_length(&lCount);CHECKHR(hr);for ( long lindex = 0; lindex < lCount; lindex++ ) {CComVariant VarIndex = lindex;CComPtr<IDispatch> spDispatchElem;hr = spElemCollection->item( VarIndex, VarIndex, &spDispatchElem );CHECKHRPOINTER(hr,spDispatchElem);CComPtr<IHTMLOptionElement> spOption;hr = spDispatchElem->QueryInterface(IID_IHTMLOptionElement, (LPVOID*)& spOption);if ( FAILED(hr) || NULL == spOption ) {continue;}CComBSTR bstrValue;hr = spOption->get_value(&bstrValue);if ( FAILED(hr) ) {continue;}CString cstrReadValue(bstrValue);if (  0 == cstrReadValue.Compare(cstrValue) ) {hRes = spOption->put_selected(VARIANT_TRUE);break;}}} while (0);return hRes;
}

如此自动填写乘客信息的操作就完成了。

验证码的自动识别

说来惭愧,这个模块本来是我这个软件的一个亮点。可是随着12306将验证码生成方法改变,导致我原来的逻辑产生了很大的误差。其实图像识别这块,我使用的是第三方库tesseract-ocr。之前12306的验证码相对比较简单,但是仍然加入了噪点和干扰线,使得tesseract-ocr识别率非常不准。于是我写了一个bmp文件格式分析和图片转换类去处理原始验证码图片,使得验证码变得清晰,同时提高了tesseract-ocr的识别准确率。我列一些以前的处理结果对比图



网上有使用2012编译tesseract-ocr的介绍。我做了点改动:在tesseract-ocr的init函数中,提供了一个指定相关目录的参数,但是代码底层却优先读取了系统环境变量TESSDATA_PREFIX的值作为相关目录。我修改了源代码中的这部分:即只使用我指明的程序路径,而不是使用系统环境变量TESSDATA_PREFIX的值。

我封装了一个文字识别的类COcr。其内容也很简单

BOOL COcr::Init(const CString& cstrSetupFloder)
{std::string sSetupFloder = CW2A(cstrSetupFloder.GetString());int nstatus = m_Tesseract.Init(sSetupFloder.c_str(), "eng", tesseract::OEM_TESSERACT_ONLY);if ( nstatus < 0 ) {return FALSE;}m_Tesseract.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);nstatus = m_Tesseract.SetVariable( "tessedit_char_whitelist", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwsyz" );return nstatus > 0 ? TRUE : FALSE;
}BOOL COcr::GetText( const CString& cstrImgPath, CString & cstrText )
{std::string sImgPath = CW2A(cstrImgPath.GetString());STRING text_out;if (!m_Tesseract.ProcessPages(sImgPath.c_str(), NULL, 0, &text_out)) {return FALSE;}std::string sText = text_out.string();cstrText = CA2W(sText.c_str());return TRUE;
}

简单说明下上述代码。代码第4行,我们设置了语言是eng,即英语体系。因为目前12306的验证码还只是数字和字母。代码第9行,告诉tesseract-ocr验证码中只是包含0~9A~Za~z字符。之前12306的验证码只有数字和大写字母,所以那个时候设置这个参数为0~9A~Z是非常必要的。

代码识别模块ok后,就是如何保存验证码图片的问题了。

如何保存验证码图片

仔细看过12306验证码区域的HTML代码的朋友,应该知道,该处的IMG的src不是指向的是一个图片,而是一个随机地址。

<img title="单击刷新验证码" id="img_rrand_code" style="vertical-align: text-bottom; cursor: hand;" οnclick="this.src=this.src+'&'+Math.random();" src="/otsweb/passCodeAction.do?rand=randp" border="0"/>

我之前想通过Src下载图片的方法明显是行不通的。那么就得使用截屏技术了。下面的代码,将验证码区域复制到剪贴板中,然后再将剪贴板中的图片保存为一个32位真彩色的bmp图片。

HRESULT CDeal12306WebPage::SaveImg( CComPtr<IHTMLElement> spElement, const CString& cstrFilePath )
{HRESULT hr = E_FAIL;do {CComPtr<IDispatch> spDispDoc;hr = spElement->get_document(&spDispDoc);CHECKHRPOINTER(hr, spDispDoc);CComPtr<IHTMLDocument2> spMainDoc;hr = spDispDoc->QueryInterface(IID_IHTMLDocument2, (LPVOID*)&spMainDoc);CHECKHRPOINTER(hr, spMainDoc);CComPtr<IHTMLElement> spBody;hr = spMainDoc->get_body(&spBody);CHECKHRPOINTER(hr, spBody);CComPtr<IHTMLElement2> spBody2;hr = spBody->QueryInterface(IID_IHTMLElement2, (LPVOID*)&spBody2);CHECKHRPOINTER(hr, spBody2);CComPtr<IDispatch> spDisp;hr = spBody2->createControlRange(&spDisp);CHECKHRPOINTER(hr, spDisp);CComPtr<IHTMLControlRange> spControlRange;hr = spDisp->QueryInterface(IID_IHTMLControlRange, (LPVOID*)&spControlRange);CHECKHRPOINTER(hr, spControlRange);CComPtr<IHTMLControlElement> spControlElem;hr = spElement->QueryInterface(IID_IHTMLControlElement, (LPVOID*)&spControlElem);CHECKHRPOINTER(hr, spControlElem);hr = spControlRange->add(spControlElem);CHECKHR(hr);VARIANT_BOOL vbReturn = VARIANT_FALSE;CComVariant vEmpty;CComBSTR bstrCmd(L"Copy");hr = spControlRange->execCommand(bstrCmd, VARIANT_FALSE, vEmpty, &vbReturn );CHECKHR(hr);if ( VARIANT_FALSE == vbReturn ) {hr = E_FAIL;break;}if(OpenClipboard(NULL)){//获得剪贴板数据HBITMAP handle = (HBITMAP)GetClipboardData(CF_BITMAP);if ( NULL != handle ) {CImage Img;Img.Attach(handle);hr = Img.Save(cstrFilePath);}else {hr = E_FAIL;}CloseClipboard();}} while (0);return hr;
}

截屏、识别、输入验证码的逻辑

HRESULT CDeal12306WebPage::SetCaptcha( CComPtr<IHTMLElement> & spTableQrTbody )
{HRESULT hr = E_FAIL;do {CComPtr<IHTMLElement> spImg;hr = GetCaptchaImgElem( spTableQrTbody, spImg);CHECKHRPOINTER(hr, spImg);CComPtr<IHTMLElement> spInput;hr = GetCaptchaInputElem( spTableQrTbody, spInput );CHECKHRPOINTER(hr, spInput);CString cstrImgPath;cstrImgPath.Format(L"%s%d.bmp", m_cstrFloder, GetTickCount());hr = SaveImg( spImg, cstrImgPath);CHECKHR(hr);CString cstrNewImgPath = cstrImgPath + ".bmp";CBmp bmp;bmp.SetFilePath( cstrImgPath, cstrNewImgPath );if ( FALSE == bmp.DealBmp() ) {hr = E_FAIL;break;}CString cstrTxet;if ( FALSE == m_ocr.GetText( cstrNewImgPath, cstrTxet) ) {hr = E_FAIL;break;}if ( CAPTCHACOUNT > cstrTxet.GetLength() ) {hr = E_FAIL;break;}cstrTxet = cstrTxet.Left(CAPTCHACOUNT);CComPtr<IHTMLInputElement> spInputElem;hr = spInput->QueryInterface(IID_IHTMLInputElement, (LPVOID*)&spInputElem);CHECKHRPOINTER(hr, spInputElem);hr = spInputElem->put_value( CComBSTR(cstrTxet.GetString()) );CHECKHR(hr);} while (0);return hr;
}

如果识别的字符数不对,则会认为失败,这样我们会刷新验证码,并重新识别。

HRESULT CDeal12306WebPage::SetCaptchaEx( CComPtr<IHTMLElement>& spTableQrTbody )
{HRESULT hr = E_FAIL;do {for ( int n = 0; n < CAPTCHARETRYCOUNT; n++ ) {hr = SetCaptcha( spTableQrTbody );if ( FAILED(hr) ) {// 如果失败刷新验证码再来一次CComPtr<IHTMLElement> spImg;hr = GetCaptchaImgElem( spTableQrTbody, spImg);CHECKHRPOINTER(hr, spImg);spImg->click();Sleep(CAPTCHAWAITTIME);}else {break;}}} while (0);return hr;
}

验证码输入完毕后,我们将点击“提交订单”按钮。现在有个问题冒出来了:如果我们验证码输入错误,那么网页会alert一下提示“验证码错误”,这个迫使我们得去点击这个按钮。如何去点击这个按钮呢?这个问题困扰了我一下,最后我决定还是绕过这个问题——彻底屏蔽Alert弹框,并记录Alert准备弹出的内容。在点击完按钮后,我将根据保存的Alert准备弹出的内容判断是否成功和失败。

屏蔽Alert

我们的窗口要继承IDocHostShowUI接口,并修改该接口的一个方法:
STDMETHODIMP CBrowserHost::ShowMessage( 
/* [in] */ HWND hwnd, 
/* [annotation][in] */ __in __nullterminated LPOLESTR lpstrText, 
/* [annotation][in] */ __in __nullterminated LPOLESTR lpstrCaption, 
/* [in] */ DWORD dwType, 
/* [annotation][in] */ __in __nullterminated LPOLESTR lpstrHelpFile, 
/* [in] */ DWORD dwHelpContext, 
/* [out] */ LRESULT *plResult )
{*plResult = 0;return S_OK;
}

从上面代码看,我并没有记录alert的内容。因为我发现了一个更为有效和简单的办法去判断是否成功了。我们看下提交没有成功时HTML网页结构


我们再看下提交成功的页面的网页结构


可以见得,提交成功的页面中新增了两个Div。其中最下面那个Div就是确认信息的HTML代码


于是完整的预订流程是

HRESULT CDeal12306WebPage::BookTickets( CComPtr<IHTMLDocument2> & spDoc )
{HRESULT hr = E_FAIL;do  {CComPtr<IHTMLElement> spTableQrTbody;hr = GetTableQrTbody( spDoc, spTableQrTbody);CHECKHRPOINTER(hr, spTableQrTbody);if ( m_stTrainNoPassenger.vecPassengerInfo.size() > MAXPASSENGERCOUNT) {ATLASSERT(FALSE);}hr = AddPassengerInfo( spTableQrTbody, m_stTrainNoPassenger.vecPassengerInfo );CHECKHR(hr);DWORD dwCount = 0;Sleep(6*1000);do {hr = SetCaptchaEx( spTableQrTbody );CHECKHR(hr);hr = ClickSubmitButton(spTableQrTbody);CHECKHR(hr);dwCount++;} while ( FAILED(ConfirmOrd(spDoc)));} while (0);return hr;
}
HRESULT CDeal12306WebPage::ConfirmOrd( CComPtr<IHTMLDocument2> & spDoc )
{HRESULT hr = E_FAIL;do {CComPtr<IHTMLElement> spDiv;hr = GetOrderConfirm( spDoc, spDiv);CHECKHRPOINTER(hr, spDiv);CComPtr<IHTMLElement> spOkButton;hr = GetConfirmOKElem(spDiv, spOkButton);CHECKHRPOINTER(hr, spOkButton);hr = spOkButton->click();CHECKHR(hr);} while (0);return hr;
}

相关文章:

利用MTCNN和FaceNet实现人脸检测和人脸识别 | CSDN博文精选

作者 | pan_jinquan来源 | CSDN博文精选&#xff08;*点击阅读原文&#xff0c;查看作者更多文章&#xff09;人脸检测和人脸识别技术算是目前人工智能方面应用最成熟的技术了。本博客将利用MTCNN和FaceNet搭建一个实现人脸检测和人脸识别的系统。基本思路也很简单&#xff0c;…

[导入]郁闷`````[原]

阅读全文 类别&#xff1a;职场生涯 查看评论文章来源:http://hi.baidu.com/huqing7002/blog/item/b69a27082063fbd263d98619.html转载于:https://www.cnblogs.com/huqing7002/archive/2007/12/14/1007049.html

code标签的妙用

code标签的秒用是&#xff1a; 当你写了一大版的css或者html或者其他代码&#xff0c;想要去掉代码中多余的空格和换行从而达到压缩的目的的时候&#xff0c;可以新建一个html文件&#xff0c;然后把想要压缩的代码段放到code标签里&#xff0c;保存&#xff0c;浏览器浏览&…

如何定制一款12306抢票浏览器——完结篇

差不多花了一个星期的业余时间去完成了相关的编码。最后也只是使用了5篇文章将整个大题流程和使用的关键技术介绍了一下。其中有很多酸甜苦辣,其中记忆最为深刻的就是我对图像做了处理后,tesseract-ocr对验证码识别的准确度提升非常大。当我还沉浸子在这片喜悦之中时,12306将…

所有各数据库或文件的连接串定义CHM文件.

从www.connectionstrings.com 中制作生成/Files/margiex/DotnetConn.rar

钢铁侠“变身”AI布道师?小罗伯特·唐尼这次推出一部AI科普纪录片

作者 | 若名出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;穿着一身运动便服&#xff0c;脱去了铠甲的“钢铁侠”托尼斯塔克最近出现在荧屏上&#xff0c;他正与身后的AI助手互动&#xff0c;一本正经讲解着“机器学习”、“算法”这些词汇。不过&#xff0c;这个AI…

Wireshark数据抓包教程之Wireshark的基础知识

Wireshark数据抓包教程之Wireshark的基础知识 Wireshark的基础知识 在这个网络信息时代里&#xff0c;计算机安全始终是一个让人揪心的问题&#xff0c;网络安全则有过之而无不及。Wireshark作为国际知名的网络数据抓包和分析工具&#xff0c;可以广泛地应用各种领域&#xff0…

WMI技术介绍和应用——查询快捷方式、映射磁盘和虚拟内存信息

本文使用了《WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类》中代码做为基础。本节只是列出了WQL语句&#xff0c;具体使用参看前面的例子。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 因为将要介绍的三个模块都比较简单&#xff0c;故将这三…

不要痴迷蓝牙耳机了,出门选这个准没错,99W+人的选择

01有一种耳机啊它是真的不适合在长途火车上使用那就蓝牙耳机除非你带充电宝还有一种耳机啊它是真的不适合丢那就是带有充电仓的耳机因为充电仓丢了这个耳机基本上就废了02这不&#xff0c;我就经历了&#xff0c;活生生的例子出差的路上在火车上把耳机充电仓给弄丢了只剩孤零零…

结构化文件存取

纲要:AssignFile: 关联Rewrite: 创建并打开一个新文件, 如已存在则覆盖Reset: 打开已存在的文件; 追加也要用它先打开, 然后再移动指针; Append 是文本文件专用的CloseFile: 关闭FileSize: 记录数FilePos: 返回文件的当前位置Seek: 把文件指针移到指定位置(只用于结构化文件)Eo…

_00021 尼娜抹微笑伊拉克_谁的的最离奇的异常第二阶段 Jedis pool.returnResource(jedis)...

笔者博文&#xff1a;妳那伊抹微笑博客地址&#xff1a;http://blog.csdn.net/u012185296博文标题&#xff1a;_00021 妳那伊抹微笑_谁的异常最诡异第二期之 Jedis pool.returnResource(jedis)个性签名&#xff1a;世界上最遥远的距离不是天涯&#xff0c;也不是海角&#xff0…

WMI技术介绍和应用——查询系统信息

本文使用了《WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类》中代码做为基础。本节只是列出了WQL语句&#xff0c;具体使用参看前面的例子。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 本机主要知识点是Win32_ComputerSystem类。通过该类我们…

利用OpenCV实现抖音最强变脸术 | CSDN原力计划

作者 | 亓斌来源 | CSDN原力计划获奖作品&#xff08;*点击阅读原文&#xff0c;查看作者更多文章&#xff09;最近一个“最强变脸术”又火爆抖音啦&#xff0c;还不知道的朋友建议先打开抖音&#xff0c;搜索“最强变脸术”看个十来个视频再回来看这篇文章。视频看起来炫酷&am…

思科PIX防火墙的实际应用配置

PIX&#xff1a;一个合法IP完成inside、outside和dmz之间的访问 现有条件&#xff1a;100M宽带接入&#xff0c;分配一个合法的IP&#xff08;222.134.135.98&#xff09;&#xff08;只有1个静态IP是否够用&#xff1f;&#xff09;&#xff1b;PiX515e-r-DMZ-BUN1台&#xff…

CommonJS 的 AMD 规范

异步模块定义&#xff08;Asynchronous Module Definition&#xff0c;简称 AMD&#xff09;API 描述了一种定义模块的机制&#xff0c;模块及其依赖模块可以通过这种机制进行加载。该机制特别适用于浏览器。 本规范曾被称为 Modules Transport/C&#xff0c;但本规范主要不是用…

易观的大数据中台之路

作者 | 易观CTO郭炜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;本文为CSDN即将推出的《新战场&#xff1a;决胜中台》专刊的第 2 篇文章。什么是数据中台&#xff1f;中台的定义来自于投资银行&#xff0c;简单说来&#xff0c;投行当中前台是赚钱的、后台是做支…

WMI技术介绍和应用——查询系统信息和补丁包信息

本文使用了《 WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类》中代码做为基础。本节只是列出了WQL语句&#xff0c;具体使用参看前面的例子。&#xff08; 转载请指明出于breaksoftware的csdn博客&#xff09;本文主要知识点是Win32_OperatingSystem和Win32_Quick…

Winform与Webform中的对话框

参考MSDNDialogResult result MessageBox.Show("见过打劫的没?", "打、打、打劫", MessageBoxButtons.OKCancel, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);switch (result){case DialogResult.OK:MessageBox.Show("你按了确定…

启动Genymotion时报错Failed to initialize backend EGL display

在启动Genymotion的时候报错&#xff1a; video card说的是显卡&#xff0c;你的显卡可能不支持 OpenGL2.0&#xff0c;或者你装的驱动有问题。解决办法&#xff1a;将驱动重新安装一下。可直接下载一个如“驱动人生“一样的驱动软件&#xff0c;把你的显卡驱动更新一下。转载…

新年新方向-斩获新职位

^_^上个星期斩获公司内部竞聘职位&#xff1a;项目管理部总经理&#xff0c;高兴&#xff0c;发展方向变了&#xff0c;以后我会多写一些项目管理的文章和大家分享。WPF的学习和分享也不会停下来&#xff0c;我要全面发展&#xff0c;做社会主义的接班人!YE!项目管理部总经理职…

清华官宣:前百度总裁张亚勤正式加盟清华大学

整理 | Jane出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;12月31日&#xff0c;2019 年的最后一天&#xff0c;在大家沉浸在「总结过去」、「展望未来」之时&#xff0c;清华大学刚刚官宣了一则消息&#xff1a;前百度总裁张亚勤正式加盟清华大学&#…

WMI技术介绍和应用——查询时间信息

本文使用了《WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类》中代码做为基础。本节只是列出了WQL语句&#xff0c;具体使用参看前面的例子。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 本文主要介绍Win32_LocalTime和Win32_UTCTime。 如何使…

问题-[VMware Workstation]断电后,重启电脑,之后就提示“内部错误”

问题现象&#xff1a;突然断电后&#xff0c;重启电脑&#xff0c;再打开VMware Workstation&#xff0c;启动不了。之后就提示“内部错误”。问题原因&#xff1a;希望高人指点。问题处理&#xff1a;关闭VMware Workstation&#xff0c;在快捷方式上&#xff0c;右击&#xf…

FC SAN与IP SAN的比较

SAN&#xff08;Storage Area Network&#xff0c;存储区域网络&#xff09;是一个由存储设备和系统部件构成的网络。所有的通信都在一个与应用网络隔离的单独的网络上完成&#xff0c;可以被用来集中和共享存储资源。SAN不但提供了对数据设备的高性能连接&#xff0c;提高了数…

“数学不行,干啥也不行!”骨灰级程序员:其实你们都是瞎努力

编程圈一直都流传着一个段子&#xff1a;一流程序员靠数学&#xff0c;二流程序员靠算法&#xff0c;末端程序员靠百度&#xff0c;低端看高端就是黑魔法。懂的人其实都知道&#xff0c;这不是段子&#xff0c;其实就是程序员的真实写照。想一想&#xff0c;我们日常学习、求职…

WMI技术介绍和应用——查询系统服务

本文使用了《 WMI技术介绍和应用——使用VC编写一个半同步查询WMI服务的类》中代码做为基础。本节只是列出了WQL语句&#xff0c;具体使用参看前面的例子。&#xff08; 转载请指明出于breaksoftware的csdn博客&#xff09;本文主要介绍Win32_Service类。 如何使用WMI枚举系统中…

送书 | 2020年新一天,用这本书开启你的NLP学习之路!

本文选自电子工业出版社博文视点新书《大数据智能&#xff1a;数据驱动的自然语言处理技术》。本书作者&#xff1a;清华大学刘知远、薄言RSVP.ai崔安颀、腾讯张开旭、清华大学韩文弢、中国人民大学赵鑫、厦门大学苏劲松、罗格斯大学张永锋、北京大学严睿、哈尔滨工业大学&…

如何在父进程中读取子(外部)进程的标准输出和标准错误输出结果

最近接手一个小项目&#xff0c;要求使用谷歌的aapt.exe获取apk软件包中的信息。依稀记得去年年中时&#xff0c;有个同事也问过我如何获取被调用进程的输出结果&#xff0c;当时还研究了一番&#xff0c;只是没有做整理。今天花点时间&#xff0c;将该方法整理成文。&#xff…

Ruby环境的安装(In Ubuntu 7.10)

今天开始学习Ruby。准备的图书呢&#xff0c;就是《Programming Ruby &#xff0d; 2nd》。为了准备一个实验的环境&#xff0c;于是要给我的Ubuntu上安装Ruby的环境。1、安装解释器&#xff1a;sudo apt-get install ruby 2、安装一个即时执行工具irb:由于第一部安装的结果…

基于animation.css实现动画旋转特效

分享一款基于animation.css实现动画旋转特效。这是一款基于CSS3实现的酷炫的动画旋转特效代码。效果图如下&#xff1a; 在线预览 源码下载 实现的代码。 html代码&#xff1a; <div class"wrap"><div class"mod_bg"><div class"bg…