// Experimental Viber header OCR; never called on the keyboard hook thread. #include #include namespace ks { namespace { std::mutex viberMutex; HWND viberWindow{}; std::wstring viberKey; ULONGLONG viberTime{}; std::wstring elementClass(IUIAutomationElement *e) { BSTR s{}; e->get_CurrentClassName(&s); std::wstring out=s?s:L""; SysFreeString(s); return out; } bool findViberHeader(IUIAutomationTreeWalker *walker,IUIAutomationElement *e,RECT &pane,RECT &search,int depth,int &budget) { if(depth>5 || --budget<0) return false; bool stack=elementClass(e).starts_with(L"StackView_"); Com child;walker->GetFirstChildElement(e,child.out()); bool found=false,info=false; while(child.p && --budget>0) { if(stack) { auto name=elementClass(child.p); if(name.starts_with(L"SearchButton_")){child->get_CurrentBoundingRectangle(&search);found=true;} if(name.starts_with(L"InfoButton_")) info=true; } else if(findViberHeader(walker,child.p,pane,search,depth+1,budget)) return true; Com next;walker->GetNextSiblingElement(child.p,next.out()); child.p->Release();child.p=next.p;next.p=nullptr; } if(found && info){e->get_CurrentBoundingRectangle(&pane);return true;} return false; } Pixels viberHeader(HWND window) { if(!IsWindow(window)||IsIconic(window)) return {}; Com automation; check(CoCreateInstance(CLSID_CUIAutomation8,nullptr,CLSCTX_INPROC_SERVER,IID_IUIAutomation2,(void**)automation.out())); automation->put_ConnectionTimeout(200);automation->put_TransactionTimeout(200); Com root;check(automation->ElementFromHandle(window,root.out())); Com walker;check(automation->get_RawViewWalker(walker.out())); RECT pane{},search{},rect{};int budget=160; if(!findViberHeader(walker.p,root.p,pane,search,0,budget)||!GetWindowRect(window,&rect)) return {}; double scale=GetDpiForWindow(window)/96.0; int x=pane.left+int(76*scale)-rect.left,y=search.top-int(12*scale)-rect.top; int width=search.left-int(12*scale)-rect.left-x,height=int(28*scale); int ww=rect.right-rect.left,wh=rect.bottom-rect.top; if(width<30||height<10||x<0||y<0||x+width>ww||y+height>wh||uint64_t(ww)*wh>16000000) return {}; HDC dc=CreateCompatibleDC(nullptr);BITMAPINFO bi{};bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth=ww;bi.bmiHeader.biHeight=-wh;bi.bmiHeader.biPlanes=1;bi.bmiHeader.biBitCount=32; void *bits{};HBITMAP bitmap=CreateDIBSection(dc,&bi,DIB_RGB_COLORS,&bits,nullptr,0); if(!bitmap){DeleteDC(dc);return {};} auto old=SelectObject(dc,bitmap);Pixels p; if(PrintWindow(window,dc,2)) { p.width=width;p.height=height;p.bgra.resize(size_t(width)*height*4); for(int row=0;row