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

一种清除windows通知区域“僵尸”图标的方案——XP系统解决方案

XP下“僵尸”图标的解决方案

从《一种清除windows通知区域“僵尸”图标的方案——问题分析》(以后简称《问题分析》)一文中分析的通知区域结构可以看出,XP的通知区域结构是相对简单的。如果我们解决了XP下的问题,那么Win7上的问题至少解决了一半——只有那个隐藏系统通知区域需要研究下。所以,我们先选择XP作为研究对象。(转载请指明出于breaksoftware的csdn博客)

从SPY++抓到的结构可以看出来,通知区域是一个ToolbarWindow32窗口类对象。ToolbarWindow32是windows一个标准的窗口类,虽然我们不能直接调用该类的一些方法来操作元素,但是该类对象响应Toolbar的标准消息。这也是我最开始的解决思路。

获取图标信息

在尝试去掉“僵尸”图标之前,有几个问题摆在我们面前

  1. 如何获取图标的总数
  2. 如何枚举到每个图标
  3. 如何获取图标的信息
  4. 如何找到我们创建的图标

查阅MSDN后,我发现Toolbar消息中TB_BUTTONCOUNT可以获取到通知区域图标总数。如此,我们便可以一个For循环枚举到所有图标。问题1和2便迎刃而解。那如何找到我们创建的图标呢?在《问题分析》一文中,我们在介绍初始化图标时,特别提出,我给图标Tip取了一个晦涩的名字——“中A英1文”。如此设计,也是因为我试图通过这个特征来识别图标(虽然这种方案存在不严谨性,但是图标的识别不是本文的主要的探讨课题)。

那么我们如何去获取图标的文字呢?查阅MSDN,发现TB_GETBUTTONTEXT这个消息可以获取图标的文字。其参数说明是

wParam
Command identifier of the button whose text is to be retrieved.
lParam
Pointer to a buffer that receives the button text.
这儿要注意几个问题:
  1. 什么是Command identfier?它和我们For循环传递的递增参数是一致的么?
  2. lParam指向的地址到底在哪个进程中?因为通知区域的进程载体是Explorer,而Explorer自然不可以访问到我们进程中的空间。如果这段空间在Explorer进程中,我们进程又如何才能读取到Explorer进程中的空间呢?
第一个问题,我查阅了MSDN,没有说明Command identifier具体的含义。只说了该字段被用在图标被选中而产生的WM_COMMAND消息结构体中。后来查阅了一下MSDN,关于传递的参数分为两种:
1 Command identifier of the button whose text is to be retrieved.
2 Zero-based index of the button for which to retrieve information.
可以见得两者是不可以混用的。这两者存在一个推导关系,即可以通过2推导出1。实现这个过程的是TB_GETBUTTON消息。
wParam
Zero-based index of the button for which to retrieve information.
lParam
Pointer to the TBBUTTON structure that receives the button information.
lParam指向一个获取的结构体TBBUTTON信息
typedef struct {int       iBitmap;int       idCommand;BYTE      fsState;BYTE      fsStyle;
#ifdef _WIN64BYTE      bReserved[6];
#else 
#if defined(_WIN32)BYTE      bReserved[2];
#endif 
#endif DWORD_PTR dwData;INT_PTR   iString;
} TBBUTTON, *PTBBUTTON, *LPTBBUTTON;
其中idCommand就是我们之前提到的Command identifier。
        问题1我们解决了,那么问题2呢?我们不仅在发送TB_GETBUTTONTEXT消息时遇到这个问题,在发送TB_GETBUTTON消息时也会遇到。这个问题说到底就是跨进程的数据通信,那使用内存映射?No!管道?No!Socket?No!……
我们采用《VC下提前注入进程的一些方法2——远线程带参数》一文中的方法——在其他进程空间中申请可读写内存。具体的代码如下。
#define SENDMSGTIME 100CSendMessageToProcess::CSendMessageToProcess(void)
{
}CSendMessageToProcess::CSendMessageToProcess( HWND hwnd, ENUMWINOWSENDPROC lpFunc )
{m_hWnd = hwnd;m_lpFun = lpFunc;m_stRemoteBuffer.lpBuffer = NULL;m_stRemoteBuffer.dwBufferSize = BUFFERSIZE;m_hProcess = NULL;InitializeRemoteBuffer();
}CSendMessageToProcess::~CSendMessageToProcess(void)
{UnitializeRemoteBuffer();
}BOOL CSendMessageToProcess::InitializeRemoteBuffer()
{if ( NULL != m_stRemoteBuffer.lpBuffer ) {return TRUE;}BOOL bSuc = FALSE;do {if ( 0 == m_stRemoteBuffer.dwBufferSize ) {::SetLastError(ERROR_INVALID_PARAMETER);break;}DWORD dwProcessID = 0;GetWindowThreadProcessId(m_hWnd, &dwProcessID);m_hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ|PROCESS_VM_WRITE , FALSE, dwProcessID);if ( NULL == m_hProcess ) {break;;}m_stRemoteBuffer.lpBuffer = (LPBYTE)VirtualAllocEx(m_hProcess, NULL, m_stRemoteBuffer.dwBufferSize,  MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);if ( NULL == m_stRemoteBuffer.lpBuffer ) {break;}bSuc = TRUE;}while (0);return bSuc;
}VOID CSendMessageToProcess::UnitializeRemoteBuffer()
{if ( NULL != m_hProcess ) {VirtualFreeEx(m_hProcess, m_stRemoteBuffer.lpBuffer, 0, MEM_RELEASE);m_stRemoteBuffer.lpBuffer = NULL;m_stRemoteBuffer.dwBufferSize = 0;CloseHandle(m_hProcess);m_hProcess = NULL;}
}BOOL CSendMessageToProcess::SendRemoteMessage( DWORD dwMsgID, DWORD dwIndexOrCmdId, PStBufferInfo lpstLocalBuffer, DWORD& dwRet )
{BOOL bSuc = FALSE;do {if ( NULL == lpstLocalBuffer ) {if ( 0 == ::SendMessageTimeout( m_hWnd, dwMsgID, dwIndexOrCmdId, 0, SMTO_ABORTIFHUNG, SENDMSGTIME, &dwRet ) ) {break;}bSuc = TRUE;break;}if ( NULL == lpstLocalBuffer || NULL == m_stRemoteBuffer.lpBuffer ) {::SetLastError(ERROR_INVALID_PARAMETER);break;}if ( NULL == m_stRemoteBuffer.lpBuffer || 0 == m_stRemoteBuffer.dwBufferSize ) {::SetLastError(ERROR_INVALID_PARAMETER);break;}if ( lpstLocalBuffer->dwBufferSize > m_stRemoteBuffer.dwBufferSize ) {::SetLastError(ERROR_MORE_DATA);break;}DWORD dwWrite = 0;if ( NULL != lpstLocalBuffer->lpBuffer && 0 != lpstLocalBuffer->dwBufferSize ) {// 使用传入的数据初始化数据空间if ( FALSE == WriteProcessMemory(m_hProcess, m_stRemoteBuffer.lpBuffer, lpstLocalBuffer->lpBuffer,  lpstLocalBuffer->dwBufferSize , &dwWrite) ) {break;}}if ( 0 == ::SendMessageTimeout( m_hWnd, dwMsgID, dwIndexOrCmdId, (LPARAM)m_stRemoteBuffer.lpBuffer, SMTO_ABORTIFHUNG, SENDMSGTIME, &dwRet ) ) {break;}DWORD dwRead = 0;if ( NULL != lpstLocalBuffer->lpBuffer && 0 != lpstLocalBuffer->dwBufferSize ) {if ( FALSE == ReadProcessMemory( m_hProcess, m_stRemoteBuffer.lpBuffer, lpstLocalBuffer->lpBuffer, lpstLocalBuffer->dwBufferSize, &dwRead))  {break;}}bSuc = TRUE;} while (0);return bSuc;
}
这儿有个特别需要说明的:我使用的是SendMessageTimeout发送消息,而不是SendMessage。因为我们对其他进程发送消息时,我们无法保证其他进程在处理消息时是否会非常费时,或者压根就不返回,从而导致我们发起消息的线程被堵塞。一般来说,比较好的方式是采用PostMessage向其他进程发送消息。但是由于我们的流程需要同步执行,所以在保证同步的情况下,同时兼顾SendMessage执行超时,采用了SendMessageTimeout方式。
打通一切问题后,我们把枚举方法也给出
BOOL CSendMessageToProcess::EnumChild()
{BOOL bSuc = FALSE;do {DWORD dwChildCount = 0;if ( 0 == ::SendMessageTimeout(m_hWnd, TB_BUTTONCOUNT, 0, 0, SMTO_ABORTIFHUNG, SENDMSGTIME, &dwChildCount) ) {break;}BOOL bContinue = TRUE;for ( DWORD dwIndex = 0; dwIndex < dwChildCount && bContinue; dwIndex++ ) {bContinue = m_lpFun(this, dwIndex);}bSuc = TRUE;} while (0);return bSuc;
}
这儿m_lpFun是一个回调函数,其用来判定和执行清除“僵尸”图标的目的。其回调函数是
static BOOL CALLBACK DealChildComp(CSendMessageToProcess* lpThis, DWORD dwIndex)
{BOOL bContinue = TRUE;do  {BYTE byLocalBuffer[BUFFERSIZE] = {0};WCHAR wchBuffer[BUFFERSIZE] = {0};memset(byLocalBuffer, 0, sizeof(byLocalBuffer));TBBUTTON TBButton;memset(&TBButton, 0, sizeof(TBButton));StBufferInfo stLocalBuffer;stLocalBuffer.lpBuffer = &TBButton;stLocalBuffer.dwBufferSize = sizeof(TBBUTTON);DWORD dwSucGetButton = 0;if ( FALSE == lpThis->SendRemoteMessage(TB_GETBUTTON, dwIndex, &stLocalBuffer, dwSucGetButton ) ) {continue;}memset(byLocalBuffer, 0, sizeof(byLocalBuffer));stLocalBuffer.lpBuffer = byLocalBuffer;stLocalBuffer.dwBufferSize = sizeof(byLocalBuffer);DWORD dwRetLenth = 0xFFFFFFFF;if ( FALSE == lpThis->SendRemoteMessage(TB_GETBUTTONTEXTW, TBButton.idCommand, &stLocalBuffer, dwRetLenth ) ) {continue;}if ( 0xFFFFFFFF == dwRetLenth  ) {continue;}memset(wchBuffer, 0, sizeof(wchBuffer));memcpy_s( wchBuffer, sizeof(wchBuffer), byLocalBuffer, dwRetLenth * sizeof(WCHAR));std::cout<<wchBuffer<<std::endl;if ( 0 != lstrcmp( L"中A英1文", wchBuffer)) {continue;}g_bFind = TRUE;//bContinue = lpThis->ClearIcon(TBButton.idCommand, FALSE);bContinue = lpThis->ClearIcon(dwIndex, TRUE);} while (0);return bContinue;
}
下一步,我们将要将重心放在如何清除“僵尸”图标上。注意一下ClearIcon这个函数,从我上面列出的代码可以看出,貌似是存在两种方法。是的,的确是两种。之后我将详细介绍这两种方法。

直接删除“僵尸”图标

MSDN上给出了Toolbar消息的所有名称,其中最开始吸引我的是TB_DELETEBUTTON这个消息。在经过上面一系列努力后,我们只要发送这个消息给通知区域便可以干净利索优雅的清除“僵尸”图标。其参数说明是

wParam
Zero-based index of the button to delete.
lParam
Must be zero.
我们的代码是

BOOL CSendMessageToProcess::ClearIcon(DWORD dwIndexOrCmdID, BOOL bByDelete/* = FALSE*/)
{BOOL bContinue = FALSE;do {if ( bByDelete ) {DWORD dwNULL = 0;SendRemoteMessage(TB_DELETEBUTTON, dwIndexOrCmdID, NULL, dwNULL);bContinue = TRUE;break;}
这样看似没什么问题了,但是我们看下执行的结果


         看下红色框住的区域(非水印内容),“僵尸”图标的确是被删除了,但是任务栏的长度却没有变化!这是这种最优雅的方法的最失败的地方,也正是这个缺陷促使我再次寻找能彻底解决的方法。但是其实这个技术缺陷可以通过产品设计的方法来规避:我们进程启动时,清除“僵尸”图标,然后创建一个可用的图标。这样会促使通知区域重新计算区域大小,从而触发一次自动调整。

模拟鼠标方式去除“僵尸”图标

模拟鼠标方式是最符合“常规”的一种方法。因为正常情况下,鼠标划过“僵尸”图标会导致通知区域删除之。那么我们在程序中模拟鼠标滑动,不也是可以解决这个问题么?现在的问题就集中在以下问题上
  1. 如何计算出“僵尸”图标的位置
  2. 发送哪些消息
第一个问题我们可以通过发送TB_GETRECT消息来获得,其参数说明是
wParam
Command identifier of the button.
lParam
Pointer to a RECT structure that will receive the bounding rectangle information.
第二个问题我们可以在计算好滑动区域的情况下,发送WM_MOUSEMOVE,对应的代码是
  else {RECT rc;StBufferInfo stLocalBuffer;stLocalBuffer.lpBuffer = &rc;stLocalBuffer.dwBufferSize = sizeof(rc);DWORD dwGetRectRet = 0;if ( FALSE == SendRemoteMessage(TB_GETRECT, dwIndexOrCmdID, &stLocalBuffer, dwGetRectRet ) ) {break;;}if ( 0 == dwGetRectRet ) {break;}MouseMoveOnWindow(m_hWnd, &rc);}} while (0);return bContinue;
}
VOID MouseMoveOnWindow( HWND hWnd, LPRECT lpRect )
{if ( FALSE == ::IsWindow(hWnd) || NULL == lpRect) {return;}RECT rc;memset(&rc, 0, sizeof(rc));if ( FALSE == ::GetWindowRect(hWnd, &rc) ) {return;}// 滑动的点POINT pt;DWORD dwStart = 0;DWORD dwEnd = 0;dwStart = lpRect->left;dwEnd = lpRect->right;pt.y = (lpRect->top + lpRect->bottom)/2;for ( DWORD loffset = dwStart; loffset < dwEnd; loffset = loffset + STOPLENGTH) {pt.x =  loffset; ::PostMessage(hWnd, WM_MOUSEMOVE, 0, MAKELONG(pt.x, pt.y));Sleep(10);}
}

使用IAccessible接口枚举并删除“僵尸”图标

之前的方案看似已经可以满足我们的需求了,但是其中存在一个问题:如果我们进程权限很低,将无法打开Explorer进程,进而之后的发送消息也将无法谈起。当我在MSDN查阅通知区域相关文档时,无意中看到我们可以使用IAccessible接口查询到通知区域的信息。
我并不打算在此详细介绍IAccessible接口的枚举方法,只是要提出一点:因为我们无法使用IAccessible接口删除图标,所以我们在找到“僵尸”图标后,将使用上面的模拟鼠标的方法。对应的代码是
#include "Accessible.h"BOOL EnumAccessible( HWND hwnd, XENUMACCESSIBLEPROC lpEnumAccessibleProc )
{BOOL bRet = FALSE;_ASSERTE (::IsWindow(hwnd));_ASSERTE(lpEnumAccessibleProc);if (::IsWindow(hwnd) && lpEnumAccessibleProc){CComPtr<IAccessible> pIAcc;HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**)&pIAcc);if (SUCCEEDED(hr) && pIAcc){CComVariant varChild;CComPtr<IAccessible> pIAccChild;FindChild(pIAcc, pIAccChild, varChild, lpEnumAccessibleProc);bRet = TRUE;}}return bRet;
}static BOOL FindChild(CComPtr<IAccessible>& pIAccParent, CComPtr<IAccessible>& pIAccChild,CComVariant& varChild, XENUMACCESSIBLEPROC lpEnumAccessibleProc)
{BOOL bSuc = FALSE;BOOL bContinue = TRUE;do {if ( NULL == pIAccParent || NULL ==lpEnumAccessibleProc) {break;}BOOL bContinue = TRUE;CComPtr<IEnumVARIANT> pEnum;HRESULT hr = pIAccParent->QueryInterface(IID_IEnumVARIANT, (PVOID*) &pEnum);if ( SUCCEEDED(hr) && pEnum) {pEnum->Reset();}// get child countlong nChildren = 0;unsigned long nFetched = 0;pIAccParent->get_accChildCount(&nChildren);for (long index = 1; (index <= nChildren) && bContinue; index++) {varChild.Clear();if ( pEnum ) {hr = pEnum->Next(1, &varChild, &nFetched );if ( FAILED(hr)) {bContinue = FALSE;break;}}else {varChild.vt = VT_I4;varChild.lVal = index;}// get IDispatch interface for the childCComPtr<IDispatch> pDisp;if ( VT_I4 == varChild.vt ) {hr = pIAccParent->get_accChild(varChild, &pDisp);}else if ( VT_DISPATCH == varChild.vt ) {pDisp = varChild.pdispVal;}// get IAccessible interface for the childCComPtr<IAccessible> pCAcc;if ( NULL != pDisp ) {hr = pDisp->QueryInterface(IID_IAccessible, (void**)&pCAcc);if ( FAILED(hr) ) {continue;}}// get information about the childif ( NULL != pCAcc) {varChild.Clear();varChild.vt = VT_I4;varChild.lVal = CHILDID_SELF;pIAccChild = pCAcc;}else {pIAccChild = pIAccParent;}DWORD dwState = 0;if ( FALSE == GetObjectState(pIAccChild, varChild, dwState) ) {continue;}// check if object is availableif (dwState & STATE_SYSTEM_INVISIBLE ) {continue;}HWND hwndChild = 0;WindowFromAccessibleObject(pIAccChild, &hwndChild);// call enum callbackbContinue = lpEnumAccessibleProc(pIAccChild, varChild, hwndChild);if (bContinue && pCAcc) {bContinue = FindChild(pCAcc, pIAccChild, varChild, lpEnumAccessibleProc);}}bSuc = TRUE;} while (0);return bContinue;
}BOOL GetObjectState( CComPtr<IAccessible>& pAcc, CComVariant& varChild, DWORD& dwState )
{BOOL bRet = FALSE;dwState = 0;do {if ( NULL == pAcc ) {break;}CComVariant varState;HRESULT hr = pAcc->get_accState(varChild, &varState);if ( FAILED(hr) || VT_I4 != varState.vt ) {break;}dwState = varState.lVal;bRet = TRUE;} while (0);return bRet;
}BOOL GetObjectRole( CComPtr<IAccessible>& pAcc, CComVariant& varChild, DWORD& dwRole )
{BOOL bRet = FALSE;dwRole = 0;do {if ( NULL == pAcc ) {break;}CComVariant varRole;HRESULT hr = pAcc->get_accRole(varChild, &varRole);if ( FAILED(hr) || VT_I4 != varRole.vt ) {break;}dwRole = varRole.lVal;bRet = TRUE;} while (0);return bRet;
}BOOL GetObjectName( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpName, DWORD dwBufferSize, DWORD& dwWrite )
{BOOL bRet = FALSE;do {if ( NULL == pAcc || NULL == lpName ) {break;}CComBSTR bstrName;HRESULT hr = pAcc->get_accName(varChild, &bstrName);if ( FAILED(hr) ) {break;}if ( dwBufferSize < bstrName.ByteLength() ) {break;}if ( 0 != memcpy_s(lpName, dwBufferSize, (LPWSTR)bstrName, bstrName.ByteLength()) ) {break;}dwWrite = bstrName.ByteLength();bRet = TRUE;} while (0);return bRet;
}BOOL GetObjectRoleString( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpBuffer, DWORD dwBufferLength, DWORD& dwWrite )
{BOOL bRet = FALSE;DWORD dwRole = 0;do {if ( NULL == pAcc || NULL == lpBuffer ) {break;}CComVariant varRole;HRESULT hr = pAcc->get_accRole(varChild, &varRole);if ( FAILED(hr) ) {break;}if ( VT_I4 == varRole.vt ) {dwRole = varRole.lVal;dwWrite = ::GetRoleText(dwRole, (LPWSTR)lpBuffer, dwBufferLength / sizeof(WCHAR));if ( 0 == dwWrite ) {break;}dwWrite *= sizeof(WCHAR);}else if ( VT_BSTR == varRole.vt ) {CComBSTR bstrRoletext(varRole.bstrVal);if ( dwBufferLength < bstrRoletext.ByteLength() ) {break;}if ( 0 != memcpy_s(lpBuffer, dwBufferLength, (LPWSTR)bstrRoletext, bstrRoletext.ByteLength())) {break;}dwWrite = bstrRoletext.ByteLength();}bRet = TRUE;} while (0);return bRet;
}BOOL GetObjectDescription( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpBuffer, DWORD dwBufferLength, DWORD& dwWrite )
{BOOL bRet = FALSE;do {if ( NULL == pAcc || NULL == lpBuffer ) {break;}CComBSTR bstrDescription;HRESULT hr = pAcc->get_accDescription(varChild, &bstrDescription);if ( FAILED(hr) ) {break;}if ( dwBufferLength < bstrDescription.ByteLength() ) {break;}if ( 0 != memcpy_s(lpBuffer, dwBufferLength, (LPWSTR)bstrDescription, bstrDescription.ByteLength()) ) {break;}dwWrite = bstrDescription.ByteLength();bRet = TRUE;} while (0);return bRet;
}BOOL GetObjectValue( CComPtr<IAccessible>& pAcc, CComVariant& varChild, LPVOID lpBuffer, DWORD dwBufferLength, DWORD& dwWrite )
{BOOL bRet = FALSE;do {if ( NULL == pAcc || NULL == lpBuffer ) {break;}CComBSTR bstrValue;HRESULT hr = pAcc->get_accValue(varChild, &bstrValue);if ( FAILED(hr) ) {break;}if ( dwBufferLength < bstrValue.ByteLength() ) {break;}if ( 0 != memcpy_s(lpBuffer, dwBufferLength, (LPWSTR)bstrValue, bstrValue.ByteLength() ) ) {break;}dwWrite = bstrValue.ByteLength();bRet = TRUE;} while (0);return bRet;
}BOOL GetObjectLocation( CComPtr<IAccessible>& pAcc, CComVariant& varChild, RECT& rect )
{BOOL bRet = FALSE;do {if ( NULL == pAcc) {break;}HRESULT hr = pAcc->accLocation(&rect.left, &rect.top, &rect.right, &rect.bottom, varChild);if ( FAILED(hr) ) {break;}// accLocation returns width and heightrect.right += rect.left;rect.bottom += rect.top;bRet = TRUE;} while (0);return bRet;
}

相关文章:

《评人工智能如何走向新阶段》后记(再续12)

由AI科技大本营下载自视觉中国151. 新一代人工智能研究方向: &#xff08;1&#xff09;研究新一代人工智能基础理论&#xff08;机理、模型和算法&#xff09;&#xff1b;&#xff08;2&#xff09;研发面向需求的共性技术&#xff08;以神经网络和算法为核心、数据和硬件为基…

正则表达式测试工具 Regex Tester 的使用方法

2019独角兽企业重金招聘Python工程师标准>>> 正则表达式测试工具“RegexTester”&#xff0c;下载地址&#xff1a;http://www.oschina.net/p/regextester 一、关于本文 今天的工作中遇到了一些正则表达式&#xff0c;我需要检验它们是否正确&#xff0c;不过我对自…

一种清除windows通知区域“僵尸”图标的方案——Windows7系统解决方案

Windows7下“僵尸”图标的解决方案 从《一种清除windows通知区域“僵尸”图标的方案——问题分析》&#xff08;以后简称《问题分析》&#xff09;一文中分析的通知区域结构可以看出&#xff0c;Windows7的通知区域比XP通知区域多出了一个“临时”系统通知区域&#xff08;转载…

《评人工智能如何走向新阶段》后记(再续13)

由AI科技大本营下载自视觉中国161. 引自美国科技媒体TNW记者对美欧企业主管与AI专家的访谈录摘要&#xff0c;谈到2020年AI的八大趋势&#xff1a; ①人工智能将使医疗保健更准确、成本更低&#xff1b; ②可解释性和信托及AI伦理将受到更多关注&#xff1b; ③在人工智能领…

在特定情况下的简单SSO实现方案

最近需要实现类似单点登录的功能。情况是这样的&#xff0c;最初在做网站A&#xff0c;做着做着&#xff0c;要做网站B了&#xff0c;要求与网站A完全分开作为两个应用&#xff0c;但用户数据要求与网站A保持一致&#xff0c;也要求用户在网站A登录后&#xff0c;转到网站B时不…

为创业者保驾护航 “无安全 不创业” 安全狗全国路演北京站

2019独角兽企业重金招聘Python工程师标准>>> 2015年上半年&#xff0c;网络安全问题毫无疑问已经成为了互联网行业关注的重点。在短短一年多的时间里&#xff0c;网络安全问题就从隐患转而呈现出爆发之势&#xff0c;即使是网易、支付宝、携程这样的互联网行业巨头也…

一种将快捷方式从开始菜单“常用应用”的中去除的方法

当我们安装一款软件的时候&#xff0c;这款软件的一些快捷方式可能被设置到开始菜单的“常用应用”区域。但是&#xff0c;如果是“卸载”快捷方式被“钉”到该区域&#xff0c;就会造成非常不好的体验。毕竟把“卸载”接口暴露得如此醒目&#xff0c;如同把该款软件的地狱大门…

ISA---不能访问网址或是多次刷新才能访问的解决方法一则

当你安装ISA2006在WINDOWS 2003 SERVER上&#xff0c;并打上SP2补订时。遇SNAT客户端不能访问WEB&#xff0c;但能PING通&#xff0c;能TELNET通&#xff0c;也能访问QQ或是MSN的问题时可以利用以下方法解决。同时&#xff0c;如果你遇到在此环境下&#xff0c;客户端访问外部网…

《评人工智能如何走向新阶段》后记(深谈人工智能发展前沿)

由AI科技大本营下载自视觉中国来自国内外的跟贴留言 深谈人工智能发展前沿 自从我们发表《评人工智能如何走向新阶段》一文以来&#xff0c;至今约5个月&#xff0c;引来了中外专家、草根们的大量跟贴留言&#xff08;也有人转录他人的公开言论作为跟贴来发表的&#xff09;。…

URAL 2027 URCAPL, Episode 1 (模拟)

题意&#xff1a;给你一个HxW的矩阵&#xff0c;每个点是一个指令&#xff0c;根据指令进行一系列操作。 题解&#xff1a;模拟 #include<cstdio> #include<algorithm> using namespace std;const int maxn 101; char G[maxn][maxn];int dx[] {-1,0,1, 0}; int d…

使用WinHttp接口实现HTTP协议Get、Post和文件上传功能

我实现了一个最新版本的接口&#xff0c;详见《实现HTTP协议Get、Post和文件上传功能——使用WinHttp接口实现》。还有基于libcurl实现的版本《实现HTTP协议Get、Post和文件上传功能——使用libcurl接口实现》。以下是原博文&#xff1a; 我们在做项目开发时&#xff0c;往往会…

收藏 | 一文带你总览知识蒸馏,详解经典论文

「免费学习 60 节公开课&#xff1a;投票页面&#xff0c;点击讲师头像」作者&#xff1a;凉爽的安迪来源 | 深度传送门&#xff08;ID&#xff1a;deep_deliver&#xff09;【导读】这是一篇关于【知识蒸馏】简述的文章&#xff0c;目的是想对自己对于知识蒸馏学习的内容和问题…

[工具推荐]用了TrueCrypt 再无难掩之隐

缘起&#xff1a;混在网络n多年了&#xff0c;手头总有些东西不想被别人看到的东西&#xff0c;由于小弟人品好&#xff0c;相貌佳&#xff0c;总有很多朋友喜欢用我的电脑玩啊玩啊……。 近日&#xff0c;冠希、柏芝等前辈以身示法&#xff0c;为我等上了很好一堂关于隐私保护…

利用phpmailer类邮件发送

<?phprequire("class.phpmailer.php"); //下载的文件必须放在该文件所在目录$mail new PHPMailer(); //建立邮件发送类$address "接收方邮箱"; //接收方地址$mail->IsSMTP(); //使用SMTP方式发送$…

据说这是大多数人【减肥】的真实写照

有句诗说得好 “冬天不减肥&#xff0c;夏天徒伤悲” 在这个人人储存脂肪的季节绝对是你甩掉脂肪的好时机&#xff08;毕竟这是一个拼颜值的时代颜值是天生的&#xff0c;可是身材绝不能输&#xff09;但是 据说大多数人的减肥经历其实是这样的减肥第一步管住嘴&#xff0c;迈开…

PE文件和COFF文件格式分析——导出表的应用——一种摘掉Inline钩子(Unhook)的方法

在日常应用中&#xff0c;某些程序往往会被第三方程序下钩子(hook)。如果被下钩子的进程是我们的进程&#xff0c;并且第三方钩子严重影响了我们的逻辑和流程&#xff0c;我们就需要把这些钩子摘掉(Unhook)。本件讲述一种在32位系统上&#xff0c;如何摘掉API钩子的思路和方法。…

设置列表字段为主键

转贴:Sample event handler to set a field as a pr imary key (enforce no duplicates) Got this as a request from a reader- how to prevent users from adding items with same titles as ones that already exist in the list. Codeusing System;using System.Collectio…

谁登录了你的linux

最近有一台数据库服务器自动重启。查了一下相关登录信息&#xff1a;查看linux下的用户登录日志&#xff0c;包括用户登录时所用的主机的ip&#xff1a;more /var/log/secure who /var/log/wtmp干了些什么&#xff1f; root账户下输入su - username 切换到username下输入 histo…

一种使用GDI+对图片尺寸和质量的压缩方法

今天同事向我询问图片压缩的算法&#xff0c;我想起大概两三年前做过的一个项目。其中包含了尺寸和质量两种压缩算法&#xff0c;并且支持JPEG、bmp、PNG等格式。今天把这段逻辑贴出来&#xff0c;供大家参考。&#xff08;转载请指明来源于breaksoftware的CSDN博客&#xff09…

.NET企业级应用架构设计系列之应用服务器

本文属spanzhang&#xff08;张友邦&#xff09;原创&#xff0c;发布地址为&#xff1a;http://blog.csdn.net/spanzhang。转载或引用请注明原文之出处&#xff0c;谢谢&#xff01; .NET企业级应用架构设计系列之开场白 .NET企业级应用架构设计系列之技术选型 这里要说到的…

编程语言发展70年,用50种不同语言输出「Hello World」

「免费学习 60 节公开课&#xff0c;投票页面&#xff0c;点击讲师头像」作者 | Sylvain Saurel译者 | 风车云马责编 | 屠敏【导读】历经 70 年&#xff0c;不断出现的编程语言为开发者解决了哪些难题&#xff1f;其存在又有怎样的特性&#xff1f;本文将以「Hello World」为例…

函数循环的理解

2019独角兽企业重金招聘Python工程师标准>>> var ulObjdocument.getElementById("box"); var lisObjulObj.getElementsTagname("li"); for(var i0;i<lisObj.length;i) { lisObj[i].οnclickfunction()//循环时对应节点绑定事件&#xff0c;事…

从LeNet到GoogLeNet:逐层详解,看卷积神经网络的进化

「免费学习 60 节公开课&#xff1a;投票页面&#xff0c;点击讲师头像」作者 | MrCharles来源 | CSDN原力计划获奖作品&#xff08;*点击阅读原文&#xff0c;查看作者更多文章&#xff09;前言深度学习的兴起使卷积神经网络在计算机视觉方面大放异彩&#xff0c;本文将按时间…

Windows客户端C/C++编程规范“建议”——前言

前言 工作中接触了很多编程规范。其中最有意思的是&#xff0c;公司最近发布了一版C/C编程规范&#xff0c;然后我看到该规范的最后一段时&#xff0c;有这么一句&#xff1a;“该规范不适用于Windows平台开发”。看来这份规范是由做其他平台开发的同学制定的。那么做Windows开…

storm入门教程 第一章 前言[转]

1.1 实时流计算 互联网从诞生的第一时间起&#xff0c;对世界的最大的改变就是让信息能够实时交互&#xff0c;从而大大加速了各个环节的效率。正因为大家对信息实时响应、实时交互的需求&#xff0c;软件行业除了个人操作系统之外&#xff0c;数据库&#xff08;更精确的说是…

Windows客户端C/C++编程规范“建议”——函数

1 函数 1.1 代码行数控制在80行及以内 等级&#xff1a;【要求】 说明&#xff1a;每个函数的代码行数控制应该控制在80行以内。如果超过这个限制函数内部逻辑一般可以拆分。如果试图超过这个标准&#xff0c;请列出理由。但理由不包含如下&#xff1a; 无法拆分。流程内部…

把自己朝九晚五的工作自动化了,有错吗?

作者 | Brian Merchant译者 | 谭开朗编辑 | 屠敏来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;【导读】用代码让自己工作开启自动化之际&#xff0c;是否意味着自己将面临被解雇的风险&#xff1f;2016年&#xff0c;Reddit上出现了一个匿名的帖子&#xff0c;内…

COM组件转换成.NET组件[转]

利用类型库导入器(Type Library Importer)可以将其包装成一个.NET组件&#xff0c;然后就可以像使用.NET组件一样使用它了。 .NET框架只是提供了一个包装&#xff0c;并没有真正改变原有的对象 1.找到要转换的.dll文件 2.在命令提示符窗口中输入.dll文件的文件路径&#xff0c;…

干货:Android 源码使用心得分享

我相信很多初学者会和我一样经常在网上去找Android开发源码&#xff0c;但是往往因为运行不起来非常的懊恼&#xff01;在做爱开发网站的时候&#xff0c;收集App代码时就遇到了这种困难&#xff0c;我相信网络上面的源码大部分在发布前都会人工测试&#xff0c;能够正常运行才…