Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Alpha testers

Есть самец, есть альфа-самец. Есть тестеры, есть альфа-тестеры. Разница понятна?

Casablanca refresh

А тем временем тихонько обновили Casablanca. Предыдущий refresh не работал с Visual Studion 2012 RTM.

DOTA 2 gifts

I have 2 DOTA 2 gifts. Probably you can ask me by email, probably someone will definitely get it. Go, gentlemen.

Realtime ETW consumer howto

Short howto for real time Event Tracing for Windows.

#pragma once

struct ITraceConsumer;

class TraceSession
{

public:
   TraceSession(LPCTSTR szSessionName);
   ~TraceSession();

public:
   bool Start();
   bool EnableProvider(const GUID& providerId, UCHAR level, ULONGLONG anyKeyword = 0, ULONGLONG allKeyword = 0);
   bool OpenTrace(ITraceConsumer *pConsumer);
   bool Process();
   bool CloseTrace();
   bool DisableProvider(const GUID& providerId);
   bool Stop();

   ULONG Status() const;
   LONGLONG PerfFreq() const;

private:
   LPTSTR _szSessionName;
   ULONG _status;
   EVENT_TRACE_PROPERTIES* _pSessionProperties;
   TRACEHANDLE hSession;
   EVENT_TRACE_LOGFILE _logFile;
   TRACEHANDLE _hTrace;

};

Initialization and cleanup:

TraceSession::TraceSession(LPCTSTR szSessionName) : _szSessionName(_tcsdup(szSessionName))
{
}

TraceSession::~TraceSession(void)
{
   delete []_szSessionName;
   delete _pSessionProperties;
}

First step - lets start trace session:

bool TraceSession::Start()
{
   if (!_pSessionProperties) {
      const size_t buffSize = sizeof(EVENT_TRACE_PROPERTIES) + (_tcslen(_szSessionName) + 1) * sizeof(TCHAR);
      _pSessionProperties = reinterpret_cast<EVENT_TRACE_PROPERTIES *>(malloc(buffSize));
      ZeroMemory(_pSessionProperties, buffSize);
      _pSessionProperties->Wnode.BufferSize = buffSize;
      _pSessionProperties->Wnode.ClientContext = 1;
      _pSessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
      _pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
   }

   // Create the trace session.
   _status = StartTrace(&hSession, _szSessionName, _pSessionProperties);

   return (_status == ERROR_SUCCESS);
}

Then lets enable a provider with some filters:

bool TraceSession::EnableProvider(const GUID& providerId, UCHAR level, ULONGLONG anyKeyword, ULONGLONG allKeyword)
{
   _status = EnableTraceEx2(hSession, &providerId, EVENT_CONTROL_CODE_ENABLE_PROVIDER, level, anyKeyword, allKeyword, 0, NULL);
   return (_status == ERROR_SUCCESS);
}

BTW, you can have useful ETW metadata generated by mc.exe utility, just find your specific man manifest file for your ETW provider.

Now we open the trace:

bool TraceSession::OpenTrace(ITraceConsumer *pConsumer)
{   
   if (!pConsumer)
      return false;

   ZeroMemory(&_logFile, sizeof(EVENT_TRACE_LOGFILE));
   _logFile.LoggerName = _szSessionName;
   _logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD;
   _logFile.EventRecordCallback = &EventRecordCallback;
   _logFile.Context = pConsumer;

   _hTrace = ::OpenTrace(&_logFile);
   return (_hTrace != 0);
}

EventRecordCallback looks like this:

namespace
{

VOID WINAPI EventRecordCallback(_In_ PEVENT_RECORD pEventRecord)
{
   reinterpret_cast<ITraceConsumer *>(pEventRecord->UserContext)->OnEventRecord(pEventRecord);
}

}

But this is just for convenience and OOP, you can have just static function. Up to you.

Finally, call the following method:

bool TraceSession::Process()
{
   _status = ProcessTrace(&_hTrace, 1, NULL, NULL);
   return (_status == ERROR_SUCCESS);
}

Ensure you are running under elevated privileges.

When you decide its over - close the trace, disable any providers and stop the session:

bool TraceSession::CloseTrace()
{
   _status = ::CloseTrace(_hTrace);
   return (_status == ERROR_SUCCESS);
}

bool TraceSession::DisableProvider(const GUID& providerId)
{
   _status = EnableTraceEx2(hSession, &providerId, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, NULL);
   return (_status == ERROR_SUCCESS);
}

bool TraceSession::Stop()
{
   _status = ControlTrace(hSession, _szSessionName, _pSessionProperties, EVENT_TRACE_CONTROL_STOP);
   delete _pSessionProperties;
   _pSessionProperties = NULL;
   
   return (_status == ERROR_SUCCESS);
}

Some useful stuff:

ULONG TraceSession::Status() const
{
   return _status;
}

LONGLONG TraceSession::PerfFreq() const
{
   return _logFile.LogfileHeader.PerfFreq.QuadPart;
}

Remember: sessions are global, not tied to your executable. So be sure to write something like this:

if (!traceSession.Start()) {
      if (traceSession.Status() == ERROR_ALREADY_EXISTS) {
         if (!traceSession.Stop() || !traceSession.Start()) {
            // Handle an error
         }
      }
   }

Well, thats it.

Marshall function call to another thread

Short tutorial on how to make cross apartment (cross thread) COM call without interfaces, proxies, stubs and tlbs.

Each apartment (thread) has its own accesible context object. It can be obtained via CoGetObjectContext API function:

CComPtr<IContext> pContext;   
CComPtr<IContextCallback> pContextCallback;
hr = CoGetObjectContext(IID_PPV_ARGS(&pContext));
hr = CoGetObjectContext(IID_PPV_ARGS(&pContextCallback));

One of interfaces you can get is IContextCallback with only one method - ContextCallback. As you might already guessed, we just need to get IContextCallback in target execution context and invoke ContextCallback method in any other context:

pContextCallback.p->AddRef();
_beginthread(&myThread, 65536, pContextCallback.p);
HANDLE h[] = { CreateEvent(NULL, TRUE, FALSE, NULL) };
DWORD dwIndex;
CoWaitForMultipleHandles(0, INFINITE, _countof(h), h, &dwIndex);
[...]
HRESULT _stdcall contextCall(ComCallData *pParam)
{
   OutputDebugString(_T("contextCall\n"));
   return S_OK;
}

void __cdecl myThread(LPVOID p)
{
   CoInitialize(NULL);

   CComPtr<IContextCallback> pContextCallback;
   pContextCallback.Attach(reinterpret_cast<IContextCallback *>(p));
   
   ComCallData cd = { 0, 0, NULL };
   pContextCallback->ContextCallback(&contextCall, &cd, IID_NULL, 0, NULL);

   CoUninitialize();
}

Here I'm starting a separate thread to make cross context call and using CoWaitForMultipleHandles OLE32 function to trigger message loop to accept cross thread COM calls (regular message loop is surely enough, I'm just reluctant to write one). Here are resulting call stacks:

Main thread:
> Win32ConsoleApplication.exe!contextCall(tagComCallData * pParam) Line 227 C++
  ole32.dll!CRemoteUnknown::DoCallback(struct tagXAptCallback *) Unknown
  rpcrt4.dll!_Invoke@12 () Unknown
  rpcrt4.dll!_NdrStubCall2@16 () Unknown
  ole32.dll!_CStdStubBuffer_Invoke@12 () Unknown
  ole32.dll!SyncStubInvoke(struct tagRPCOLEMESSAGE *,struct _GUID const &,class CIDObject *,void *,struct IRpcChannelBuffer *,struct IRpcStubBuffer *,unsigned long *) Unknown
  ole32.dll!StubInvoke(struct tagRPCOLEMESSAGE *,class CStdIdentity *,struct IRpcStubBuffer *,struct IRpcChannelBuffer *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!CCtxComChnl::ContextInvoke(struct tagRPCOLEMESSAGE *,struct IRpcStubBuffer *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!MTAInvoke(struct tagRPCOLEMESSAGE *,unsigned long,struct IRpcStubBuffer *,class IInternalChannelBuffer *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!STAInvoke(struct tagRPCOLEMESSAGE *,unsigned long,struct IRpcStubBuffer *,class IInternalChannelBuffer *,void *,struct tagIPIDEntry *,unsigned long *) Unknown
  ole32.dll!AppInvoke(class CMessageCall *,class CRpcChannelBuffer *,struct IRpcStubBuffer *,void *,void *,struct tagIPIDEntry *,struct LocalThis *) Unknown
  ole32.dll!ComInvokeWithLockAndIPID(class CMessageCall *,struct tagIPIDEntry *) Unknown
  ole32.dll!ComInvoke(class CMessageCall *) Unknown
  ole32.dll!ThreadDispatch(void *) Unknown
  ole32.dll!ThreadWndProc(struct HWND__ *,unsigned int,unsigned int,long) Unknown
  user32.dll!_InternalCallWinProc@20 () Unknown
  user32.dll!_UserCallWinProcCheckWow@32 () Unknown
  user32.dll!_DispatchMessageWorker@8 () Unknown
  user32.dll!_DispatchMessageW@4 () Unknown
  ole32.dll!CCliModalLoop::PeekRPCAndDDEMessage(void) Unknown
  ole32.dll!CCliModalLoop::FindMessage(unsigned long) Unknown
  ole32.dll!CCliModalLoop::HandleWakeForMsg(void) Unknown
  ole32.dll!CCliModalLoop::BlockFn(void * *,unsigned long,unsigned long *) Unknown
  ole32.dll!_CoWaitForMultipleHandles@20 () Unknown
  Win32ConsoleApplication.exe!wmain(int argc, wchar_t * * argv) Line 284 C++
  Win32ConsoleApplication.exe!__tmainCRTStartup() Line 533 C
  Win32ConsoleApplication.exe!wmainCRTStartup() Line 377 C
  kernel32.dll!@BaseThreadInitThunk@12 () Unknown
  ntdll.dll!___RtlUserThreadStart@8 () Unknown
  ntdll.dll!__RtlUserThreadStart@8 () Unknown
Worker thread:
  ntdll.dll!_NtWaitForMultipleObjects@20 () Unknown
  ntdll.dll!_NtWaitForMultipleObjects@20 () Unknown
  KernelBase.dll!_WaitForMultipleObjectsEx@20 () Unknown
  kernel32.dll!_WaitForMultipleObjectsExImplementation@20 () Unknown
  user32.dll!_RealMsgWaitForMultipleObjectsEx@20 () Unknown
  ole32.dll!CCliModalLoop::BlockFn(void * *,unsigned long,unsigned long *) Unknown
  ole32.dll!ModalLoop(class CMessageCall *) Unknown
  ole32.dll!SwitchSTA(class OXIDEntry *,class CMessageCall * *) Unknown
  ole32.dll!CRpcChannelBuffer::SwitchAptAndDispatchCall(class CMessageCall * *) Unknown
  ole32.dll!CRpcChannelBuffer::SendReceive2(struct tagRPCOLEMESSAGE *,unsigned long *) Unknown
  ole32.dll!CCliModalLoop::SendReceive(struct tagRPCOLEMESSAGE *,unsigned long *,class IInternalChannelBuffer *) Unknown
  ole32.dll!CAptRpcChnl::SendReceive(struct tagRPCOLEMESSAGE *,unsigned long *) Unknown
  ole32.dll!CCtxComChnl::SendReceive(struct tagRPCOLEMESSAGE *,unsigned long *) Unknown
  ole32.dll!NdrExtpProxySendReceive(void *,struct _MIDL_STUB_MESSAGE *) Unknown
  rpcrt4.dll!@NdrpProxySendReceive@4 () Unknown
  rpcrt4.dll!_NdrClientCall2 () Unknown
  ole32.dll!_ObjectStublessClient@8 () Unknown
  ole32.dll!_ObjectStubless@0 () Unknown
  ole32.dll!CObjectContext::InternalContextCallback(long (*)(void *),void *,struct _GUID const &,int,struct IUnknown *) Unknown
  ole32.dll!CObjectContext::ContextCallback(long (*)(struct tagComCallData *),struct tagComCallData *,struct _GUID const &,int,struct IUnknown *) Unknown
> Win32ConsoleApplication.exe!myThread(void * p) Line 239 C++
  msvcr110d.dll!_callthreadstart() Line 255 C
  msvcr110d.dll!_threadstart(void * ptd) Line 239 C
  kernel32.dll!@BaseThreadInitThunk@12 () Unknown
  ntdll.dll!___RtlUserThreadStart@8 () Unknown
 ntdll.dll!__RtlUserThreadStart@8 () Unknown

Windows OS has many hidden features, used by Microsoft products. Are we any worse?

DLGTEMPLATE

Бывает нужно прочитать шаблон диалога из ресурса. Для этого в библиотеке ATL есть набор полезных рюшечек: файл atlwin.h, класс _DialogSplitHelper. Например там есть объявление структур DLGTEMPLATEEX, DLGITEMTEMPLATEEX, реализации функций IsDialogEx, DlgTemplateItemCount, FindFirstDlgItem и FindNextDlgItem. Пользуйтесь на здоровье.

MacBook russian keyboard layout

Только что узнал, что на официальных Макбуках (сертифицированных для продажи в РФ и Украине) НАСТОЛЬКО уебищная раскладка клавиатуры. Снова этот кастрированный шифт и припизженная клавиша Ввод. Хорошо, что у меня америкос.

How to call GC.WaitForPendingFinalizers via CLR interop interfaces

HRESULT WaitForPendingFinalizers()
{
   const ATL::CComSafeArray<IUnknown *> assemblies = DefaultDomain()->GetAssemblies();
   const auto pAssemblyThunk = assemblies.GetAt(assemblies.GetLowerBound());
   const _AssemblyPtr pMsCorLibAssembly = pAssemblyThunk.p;
   const auto pGCType = pMsCorLibAssembly->GetType_2(L"System.GC");
   const BindingFlags bFlags = static_cast(BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public);
   ATL::CComVariant vtTarget;
   ATL::CComSafeArray<VARIANT> saMethodArgs;
   const HRESULT hr = pGCType->InvokeMember_3(L"WaitForPendingFinalizers", bFlags, NULL, vtTarget, saMethodArgs);
   return hr;
}

WinDBG support dropped for immediate window?

Похоже, в Visual Studio 2012 отладчик больше не поддерживает команды WinDBG. Содержимое Immediate Window:

.load sos
expected an expression

Они, однако, сделали поддержку WinDBG путем выбора нужного отладчика в выпадающем списке:

Но необходимо установить WDK. SDK не канает.

Медленный он, конечно. И убогий в сравнении с привычным отладчиком Visual Studio, но что поделать?

Extra pdb information?

Update: code center premium. Ах, как хочется.

A comparison of C++11 language support in VS2012, g++ 4.7 and Clang 3.1

Apple снова первые! A comparison of C++11 language support in VS2012, g++ 4.7 and Clang 3.1

Change system drive letter in Windows

Недавно пришлось устанавливать Windows 7 x64 из работающей Windows 7 x32 (болванки не было нормальной для загрузки с нее). В результате буква системного диска стала G. Я такого не люблю и попытался всяческими способами сменить ее на C. В результате система перестала грузиться или оказалась в состоянии что-то в духе человека-овоща, подключенного к аппарату поддержки жизнедеяльности. А все потому, что реестр захламлен путями к файлам драйверов, COM библиотек и прочей важной ерунды. А пути там, да, абсолютные, включают букву системного диска. Ура, товарищи! Букву системного диска сменить НЕЛЬЗЯ, устанавливайте с оптических или других носителей.

How to set up Cisco VPN on MacOS X x64

How to set up Cisco VPN on MacOS X x64

MacOS X internals

Захотелось разобраться как работает оконная система MacOS X изнутри. В Windows все понятно и документировано - цикл сообщений, API для создания окон и манипуляции их состоянием. В замечательной OS X все спрятано за красивым фреймворком Cocoa. Годы хардфакинга в MFC/WinAPI подсказывают, что принципы построения графической оболочки везде одинаковы. Потратил время, докопался до места откуда растут ноги.

Механизм взаимодействия графической подсистемы с пользовательским кодом

Если создать в XCode простейшее AppKit приложение и запустить его - получим пустое окно на экране. Чудес, как обычно, не бывает - окно должно быть создано неким API. За композицию окон в OS X отвечает подсистема с замысловатым названием Quartz. В списке процессов можно обнаружить WindowServer, это собственно ядро Quartz. Именно там происходит формирование картинки для устройства отображения (монитора). Гугление на тему создания окон в OS X привело меня на страничку Header file for undocumented CoreGraphics SPI. Изучив контент я сделал вывод, что существует библиотека Core Graphics с низкоуровневым API для взаимодействия с WindowServer. Дабы убедиться, что Cocoa использует этот API, я поставил точку останова на функцию _CGSCreateWindowInline и запустил приложение. В результате получил такой стек вызовов:

#1 0x00007fff8fc8d64b in _CGSCreateWindowInline ()
#2 0x00007fff8fc8cd0b in CGSNewWindowWithOpaqueShape ()
#3 0x00007fff8be708e8 in _NSCreateWindowWithOpaqueShape2 ()
#4 0x00007fff8be6ed70 in -[NSWindow _commonAwake] ()
#5 0x00007fff8bf271c9 in -[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] ()
#6 0x00007fff8bf26a18 in -[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] ()
#7 0x00007fff8bf265ff in -[NSWindow orderWindow:relativeTo:] ()
#8 0x00007fff8be27c96 in -[NSIBObjectData nibInstantiateWithOwner:topLevelObjects:] ()
#9 0x00007fff8be06b7d in loadNib ()
#10 0x00007fff8be060a9 in +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] ()
#11 0x00007fff8be05ede in -[NSBundle(NSNibLoading) loadNibNamed:owner:topLevelObjects:] ()
#12 0x00007fff8be05cbe in +[NSBundle(NSNibLoading) loadNibNamed:owner:] ()
#13 0x00007fff8be0247f in NSApplicationMain ()
#14 0x00000001000011b2 in main at /Users/admin/Development/CocoaInspector/CocoaInspector/main.m:13
#15 0x00007fff932127e1 in start ()
  

OK, это то, что нам нужно. Осталось выяснить как Core Graphics взаимодействует с процессом WindowServer. Ассемблерный листинг функции _CGSCreateWindowInline содержит инструкцию вызова системной процедуры mach_msg, которая является основным способом межпроцессного взаимодействия в операционной системе OS X.

Выводы: Core Graphics является закрытой недокументированной библиотекой для взаимодействия с графическим сервером средствами ядра путем отправки и получения сообщений.

Visual Studio 2012 RTM leaked

Уже можно найти на обменниках. Дядко ЦЯ своровал.

MFC fails again

MFC, I love you. Really. Your source code is a fantastic example of how not to do it. Don't do it ever.

The problem

OK, so here's the problem - given an MFC dialog (or dialog control), sometimes we have no focus cues when tabbing through UI elements. You press Tab, use cursor keys and yet focus rectangle is not there. Forever. What might the problem be? Let's find out how this works internally.

Focus rect internals

First of all open any system dialog (for example taskbar Properties) using mouse only and ensure there is no focus rectangle on currently focused UI element. Press Tab and watch for it to immediately appear. Whatever you do from now on (excluding cases where the whole window hierarchy is hidden or recreated), focus rect is always on the screen. Now try to open this dialog using keyboard operation (you can open taskbar context menu and select Properties menu item using keyboard keys). Focus rect is shown immediately. What's happening here?

The answer begins it's path in dlgbegin.c, function InternalCreateDialog:

HWND InternalCreateDialog(
    HANDLE hmod,
    LPDLGTEMPLATE lpdt,
    DWORD cb,
    HWND hwndOwner,
    DLGPROC lpfnDialog,
    LPARAM lParam,
    UINT fSCDLGFlags)
{
...
}

The most interesting part is the following:

 /*
  * UISTATE: if keyboard indicators are on and this is a topmost dialog
  * set the internal bit.
  */
 if (TEST_KbdCuesPUSIF) {
     /*
      * If property page, UISTATE bits were copied from parent when I was created
      * Top level dialogs act as containers and initialize their state based on
      * the type of the last input event, after sending UIS_INITIALIZE
      */
     if (!TestwndChild(pwnd)) {
         SendMessageWorker(pwnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, 0), 0, FALSE);
     }
 }

TEST_KbdCuesPUSIF tests current execution environment for keyboard cues enablement (field check in some global structure), TestwndChild ensures top level dialog. Then the window gets WM_CHANGEUISTATE message with two arguments - UIS_INITIALIZE and zero. Documentation says Top level dialogs act as containers and initialize their state based on the type of the last input event. What does this mean? The answer is in the default window procedure (dwp.c):

/***************************************************************************\
* xxxDefWindowProc (API)
*
* History:
* 10-23-90 MikeHar Ported from WaWaWaWindows.
* 12-07-90 IanJa   CTLCOLOR handling round right way
\***************************************************************************/

LRESULT xxxDefWindowProc(
    PWND pwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
...
}

Take a look at WM_CHANGEUISTATE handler:

case WM_CHANGEUISTATE:
    {
        WORD wAction = LOWORD(wParam);
        WORD wFlags = HIWORD(wParam);
        BOOL bRealChange = FALSE;

        if (wFlags & ~UISF_VALID || wAction > UIS_LASTVALID ||
            lParam || !TEST_KbdCuesPUSIF) {
                return 0;
        }

        if (wAction == UIS_INITIALIZE) {
            if (gpsi->bLastRITWasKeyboard) {
                wAction = UIS_CLEAR;
            } else {
                wAction = UIS_SET;
            }
            wFlags = UISF_HIDEFOCUS | UISF_HIDEACCEL;
            wParam = MAKEWPARAM(wAction, wFlags);
        }

        UserAssert(wAction == UIS_SET || wAction == UIS_CLEAR);
        /*
         * If the state is not going to change, there's nothing to do here
         */
        if (wFlags & UISF_HIDEFOCUS) {
            bRealChange = (!!TestWF(pwnd, WEFPUIFOCUSHIDDEN)) ^ (wAction == UIS_SET);
        }
        if (wFlags & UISF_HIDEACCEL) {
            bRealChange |= (!!TestWF(pwnd, WEFPUIACCELHIDDEN)) ^ (wAction == UIS_SET);
        }

        if (!bRealChange) {
            break;
        }

        /*
         * Children pass this message up
         * Top level windows update their children's state and
         * send down to their imediate children WM_UPDATEUISTATE.
         */
        if (TestwndChild(pwnd)) {
            ThreadLockAlways(pwnd->spwndParent, &tlpwndParent);
            lt = xxxSendMessage(pwnd->spwndParent, WM_CHANGEUISTATE, wParam, lParam);
            ThreadUnlock(&tlpwndParent);
            return lt;
        } else {
            return xxxSendMessage(pwnd, WM_UPDATEUISTATE, wParam, lParam);
        }

        }
    break;

case WM_QUERYUISTATE:
    return (TestWF(pwnd, WEFPUIFOCUSHIDDEN) ? UISF_HIDEFOCUS : 0) |
           (TestWF(pwnd, WEFPUIACCELHIDDEN) ? UISF_HIDEACCEL : 0);
    break;

case WM_UPDATEUISTATE:
    {
        WORD wAction = LOWORD(wParam);
        WORD wFlags = HIWORD(wParam);

        if (wFlags & ~UISF_VALID || wAction > UIS_LASTVALID ||
            lParam || !TEST_KbdCuesPUSIF) {
                return 0;
        }

        switch (wAction) {
            case UIS_INITIALIZE:
                /*
                 * UISTATE: UIS_INITIALIZE sets the UIState bits
                 * based on the last input type
                 */
                if (!gpsi->bLastRITWasKeyboard) {
                    SetWF(pwnd, WEFPUIFOCUSHIDDEN);
                    SetWF(pwnd, WEFPUIACCELHIDDEN);
                    wParam = MAKEWPARAM(UIS_SET, UISF_HIDEACCEL | UISF_HIDEFOCUS);
                } else {
                    ClrWF(pwnd, WEFPUIFOCUSHIDDEN);
                    ClrWF(pwnd, WEFPUIACCELHIDDEN);
                    wParam = MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS);
                }
                break;

            case UIS_SET:
                if (wFlags & UISF_HIDEACCEL) {
                    SetWF(pwnd, WEFPUIACCELHIDDEN);
                }
                if (wFlags & UISF_HIDEFOCUS) {
                    SetWF(pwnd, WEFPUIFOCUSHIDDEN);
                }
                break;

            case UIS_CLEAR:
                if (wFlags & UISF_HIDEACCEL) {
                    ClrWF(pwnd, WEFPUIACCELHIDDEN);
                }
                if (wFlags & UISF_HIDEFOCUS) {
                    ClrWF(pwnd, WEFPUIFOCUSHIDDEN);
                }
                break;

            default:
                break;
         }

        /*
         * Send it down to its immediate children if any
         */
         if (pwnd->spwndChild) {

            PBWL pbwl;
            HWND *phwnd;
            TL tlpwnd;

            pbwl = BuildHwndList(pwnd->spwndChild, BWL_ENUMLIST, NULL);
            if (pbwl == NULL)
                return 0;

            for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
                /*
                 * Make sure this hwnd is still around.
                 */
                if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
                    continue;

                ThreadLockAlways(pwnd, &tlpwnd);
                xxxSendMessage(pwnd, message, wParam, lParam);
                ThreadUnlock(&tlpwnd);
            }
            FreeHwndList(pbwl);
        }
    }
    break;

We are interested in the following code:

if (wAction == UIS_INITIALIZE) {
    if (gpsi->bLastRITWasKeyboard) {
        wAction = UIS_CLEAR;
    } else {
        wAction = UIS_SET;
    }
    wFlags = UISF_HIDEFOCUS | UISF_HIDEACCEL;
    wParam = MAKEWPARAM(wAction, wFlags);
}

It checks whether last input event was the one from keyboard (bLastRITWasKeyboard, RIT - raw input thread) and prepares appropriate action - set UISF_HIDEFOCUS and UISF_HIDEACCEL flags or clear them. The system tries to understand user experience type (mouse or keyboard) and initializes window state for better interaction. That's why you don't see focus or accelerator cues having opened a dialog using mouse whereas they are both on if you used your keyboard.

There is a little trick to have the system on: move the mouse right after you triggered a dialog using keyboard, or press any key after you triggered a dialog using mouse. The effect speaks for itself.

The rest of the handler simply decides where to send WM_UPDATEUISTATE message. WM_UPDATEUISTATE's handler just saves the state to internal OS structure for particular window. As you have already guessed, WM_QUERYUISTATE reads the state back.

What does this all mean? There is a window state for focus and accelerator cues and a set of messages for it's manipulation. This state is kept in sync by all windows of the same hierarchy. OS initializes the state for best user experience.

Who uses the state

At least common controls, of course. The pattern is simple (listview.c for example):

case WM_UPDATEUISTATE:
{
    DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);

    // we care only about focus not accel, and redraw only if changed
    if (CCOnUIState(&(plv->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam))
    {
        if(plv->iFocus >= 0)
        {
            // an item has the focus, invalidate it
            ListView_InvalidateItem(plv, plv->iFocus, FALSE, RDW_INVALIDATE | RDW_ERASE);
        }
    }

    goto DoDefault;
}

It just listens to UI state updates and does appropriate actions (saves the state to local structure, invalidates the window etc.).

Who updates the state

As I've already mentioned, focus rect is shown immediately as you pressed focus navigation key (Tab for example). This is done by ::IsDialogMessage OS function:

case WM_SYSKEYDOWN:
    /*
     * If Alt is down, deal with keyboard cues
     */
    if ((HIWORD(lpMsg->lParam) & SYS_ALTERNATE) && TEST_KbdCuesPUSIF) {
        if (TestWF(pwnd, WEFPUIFOCUSHIDDEN) || (TestWF(pwnd, WEFPUIACCELHIDDEN))) {
                SendMessageWorker(pwndDlg, WM_CHANGEUISTATE,
                                  MAKEWPARAM(UIS_CLEAR, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0, FALSE);
            }
    }
    break;
case WM_KEYDOWN:
    code = (UINT)SendMessage(lpMsg->hwnd, WM_GETDLGCODE, lpMsg->wParam,
            (LPARAM)lpMsg);
    if (code & (DLGC_WANTALLKEYS | DLGC_WANTMESSAGE))
        break;

    switch (lpMsg->wParam) {
    case VK_TAB:
        if (code & DLGC_WANTTAB)
            break;
        pwnd2 = _GetNextDlgTabItem(pwndDlg, pwnd,
                (GetKeyState(VK_SHIFT) & 0x8000));

        if (TEST_KbdCuesPUSIF) {
            if (TestWF(pwnd, WEFPUIFOCUSHIDDEN)) {
                SendMessageWorker(pwndDlg, WM_CHANGEUISTATE,
                                      MAKEWPARAM(UIS_CLEAR, UISF_HIDEFOCUS), 0, FALSE);
            }
        }

WM_SYSKEYDOWN and WM_KEYDOWN analyze keyboard input and trigger UI state updates by sending WM_CHANGEUISTATE. All child windows then receive state change update, focused window shows up a shiny focus rectangle, labels begin to draw accelerator cues. Simple, huh?

So what the fuck is wrong might be in MFC

As you already know (I hope) MFC focus navigation is implemented via CWnd::PreTranslateMessage virtual function which eventually calls CWnd::IsDialogMessage method:

BOOL CWnd::IsDialogMessage(LPMSG lpMsg)
{
 ASSERT(::IsWindow(m_hWnd));

 if (m_nFlags & WF_OLECTLCONTAINER)
  return afxOccManager->IsDialogMessage(this, lpMsg);
 else
  return ::IsDialogMessage(m_hWnd, lpMsg);
}

In case of ActiveX controls in your dialog template (or any other shit involving OCC state initialization), WF_OLECTLCONTAINER flag is set and the control passes to COccManager::IsDialogMessage instead of API ::IsDialogMessage. Believe me or not, COccManager::IsDialogMessage analyzes keyboard input and moves the focus. The biggest problem with this method - it doesn't consider UI layout the way ::IsDialogMessage does. ZOrder enumeration is done using CWnd::GetNextDlgTabItem (see correct overload with COleControlSiteOrWnd * parameter), which moves through controls registered in COleControlContainer - m_pCtrlCont->m_listSitesOrWnds. As you might have already guessed, this container is populated by MFC during dialog creation (from template of course) and is not updated afterwards. So if you add your controls dynamically (WTL/MFC/whatever) - be ready for focus navigation problems. The solution is to update container with necessary items for each control you create/recreate etc. Plus you have to keep items order to reflect proper ZOrder. What a crap!

Finally, the answer

COccManager::IsDialogMessage doesn't fucking send WM_CHANGEUISTATE as ::IsDialogMessage does. It doesn't fucking do it. Fucking MFC, fuck you. FUCK YOU.

Stupid X5 driver

My hero

DOTA 2 reporting

Захожу в игру, а там (уже второй раз):

We’ve recently taken action against one or more players you’ve previously reported for bad conduct. Thanks for your help in making the Dota 2 community a friendlier place.

Поэтому, ублюдки и школота, ведите себя нормально.

Where are my 15?

Верните мне тот возраст, когда карамелька за щекою была просто карамелькой...

Windows UX guidelines

Постоянно трачу время на поиск этой странички: Windows User Experience Interaction Guidelines > Guidelines > Visuals > Layout.

Constraint vs Limitation

Доходчиво объясняют разницу между словами constraint и limitation.

DSTU is good

Покупая мороженное, проверяйте наличие аббревиатуры ДСТУ на упаковке. Оно почти всегда будет вкусным и без тонны ебанной химии. Например Ріжок «Дитяче бажання».

Woman driving in the opposite lane

Objective-C selectors

Как известно, селекторы в Objective-C - это уникальные адреса строк в памяти, которые используются objc_msgSend для диспетчеризации сообщений. Уникальность обеспечена неким механизмом интернирования. При этом он работает как на этапе компиляции (директива @selector), так и во время выполнения программы (функция sel_registerName или NSSelectorFromString). Пытался понять как это работает - получилось.

Все банально просто - компилятор создает отедельную секцию __objc_selrefs данных для всех селекторов, используемых ключевым словом @selector:

                                       ; 
                                       ; Section __objc_selrefs
                                       ; 
                                       ; Range 0x100002678 - 0x1000026a0 (40 bytes)
                                       ; File offset 9848 (40 bytes)
                                       ; Flags : 0x10000005
                                       ; 
                                            objc_sel_point:
0000000100002678                                 dq         0x00000001000019c4            ; XREF=0x100001485, 0x100001543
                                            objc_sel_init:
0000000100002680                                 dq         0x00000001000019de            ; XREF=0x100001607, 0x1000016b8, 0x10000170c, 0x1000017a7
                                            objc_sel_setHelloText_:
0000000100002688                                 dq         0x00000001000019e3            ; XREF=0x100001633, 0x1000016d3, 0x100001727
                                            objc_sel_stringWithFormat_:
0000000100002690                                 dq         0x00000001000019f1            ; XREF=0x10000167d
                                            objc_sel_description:
0000000100002698                                 dq         0x0000000100001a18            ; XREF=0x1000017d4, 0x1000018eb

Ну, а sel_registerName возваращает элемент из этого списка, если он найден, в противном случае - адрес строки из какого-то динамического списка времени выполнения.

Есть еще нюанс - работает ли @selector сквозь модули? Подозреваю, что нет.

Push the tempo & Mama Lyuba

Оборжака!

Где удалить зуб мудрости в г. Киев

Удаление зубов мудрости - это жесть, жопа и разрыв последней первым. Я удалял оба нижних, которые были огромными и росли в сторону своих соседей. Зуб при этом нужно распиливать прежде чем вынимать. После неделя отходняка. Если вы ищете хорошего врача-хирурга для этой операции, рекомендую +380 67 949-18-85, Гайструк Борис Васильевич. Это нихуя не реклама, просто он профессионал и такую опасную во всех смыслах операцию рекомендую доверить именно ему. Последний раз это было 800 грн.

MacBook Pro WiFi problem

Мой свежий MacBook Pro late 2011 хреновенько держал беспроводную сеть. Картина печальная:

64 bytes from 192.168.1.1: icmp_seq=1075 ttl=64 time=1451.455 ms
Request timeout for icmp_seq 1077
64 bytes from 192.168.1.1: icmp_seq=1077 ttl=64 time=1358.118 ms
64 bytes from 192.168.1.1: icmp_seq=1078 ttl=64 time=715.122 ms
64 bytes from 192.168.1.1: icmp_seq=1079 ttl=64 time=652.952 ms
64 bytes from 192.168.1.1: icmp_seq=1080 ttl=64 time=294.866 ms
64 bytes from 192.168.1.1: icmp_seq=1082 ttl=64 time=368.870 ms
64 bytes from 192.168.1.1: icmp_seq=1083 ttl=64 time=615.296 ms
Request timeout for icmp_seq 1084
64 bytes from 192.168.1.1: icmp_seq=1085 ttl=64 time=656.923 ms
Request timeout for icmp_seq 1086
64 bytes from 192.168.1.1: icmp_seq=1086 ttl=64 time=1128.858 ms
64 bytes from 192.168.1.1: icmp_seq=1088 ttl=64 time=542.205 ms
Request timeout for icmp_seq 1089
64 bytes from 192.168.1.1: icmp_seq=1090 ttl=64 time=649.374 ms
Request timeout for icmp_seq 1091
64 bytes from 192.168.1.1: icmp_seq=1092 ttl=64 time=882.039 ms
Request timeout for icmp_seq 1093
64 bytes from 192.168.1.1: icmp_seq=1094 ttl=64 time=667.309 ms
Request timeout for icmp_seq 1095
Request timeout for icmp_seq 1096
64 bytes from 192.168.1.1: icmp_seq=1097 ttl=64 time=241.912 ms
Request timeout for icmp_seq 1098

После сканирования других сетей и выбора уникального канала в настройках точки доступа все стало гораздо лучше (до этого канал стоял Auto и выбор падал на 1-й):

--- 192.168.1.1 ping statistics ---
183 packets transmitted, 173 packets received, 5.5% packet loss
round-trip min/avg/max/stddev = 1.061/3.985/17.368/1.837 ms

XCode 4.4 DP4

XCode 4.4 DP4 direct download links.

Visual Studio 11 update

Апрельское обновление Visual Studio 11 Beta.

Girls, stop this shit!

Девушки, прекратите это дерьмо!

Maldives

Выбираем тур на Мальдивы. Какой же это геморрой! Масса отелей, фотки невнятные, цены везде нужно искать, описания скудные. С перелетами - вообще отдельная история.

Dell Latitude E5520-A1

Куплен очередной Dell Latitude E5520-A1. Все работает, кроме встроенной камеры. Ее даже в устройствах нету.

Dell, как ты меня уже заебал. Абсолютно все ноутбуки, которые я покупал, все были изначально с дефектом.

Shit happens

Почти ДТП. Перед выездом на встречную я почему-то решил, что это все еще не встречная. Возможно из-за отсутствия автомобилей на ней или из-за того, что одну полосу справа отрезало снегом.

Снег, кстати, был мокрый, сразу же сработал АБС при торможении. Таксист вовремя вернулся назад.

На видео, конечно, кажется ерундой. А очечко сжалось!

Dell Vostro V131

Купил очередной Dell. В очередной раз сразу же после покупки отвез в сервисный центр - перестала работать подсветка экрана. Как уже это все задрало! Если бы не потребность в Windows - купил бы MacBook без задней мысли!

Real blonde

Полный пиздец.

Internet boorishness

Девушки, прекратите это дерьмо.

No life

Spartacus: Vengeance

Начался новый сезон Spartacus: Vengeance.

Был в IMAX-е на Underworld: Awakening. Отличный экшн с кучей кровищи, поломанных костей, расчлененки и прочих радостей кинематографа. И минимум соплей.

Why not Android

и не беспокоит потеря поддержки актуальности системы всего через полтора года с начала продаж?

Не, ну, если смотреть как быстро производители клепают свои поделки, то через полтора года нужно думать не об поддержке системы, а о покупке нового девайса.


Это вселенная андроида живет по таким законам. Там объявляют о выходе 4-й версии и в тот же день, 8-мью часами позже Моторола представляет "новейший" телефон-бритву на устаревшей версии. Очень показательный косяк всей системы. Причем никто и никак точно не может сказать, когда и кто и получит ли и какое это будет обновление, даже нативные нексусы.

В соседней вселенной можно взять телефон в 2009-м году и получить обновление операционки нового поколения в декабре 2011-го. День-в-день с современнейшими моделями.
Причем user-experience (нет такого термина в русском, извините) очень тщательно сохраняется между моделями, юзеры заметно довольней чем у конкурентов.

Ковровое бомбометание моделями по всем существующим и выдуманным рыночным нишам говорит только об неуверенности производителя. Об отсутствии видения.

Зоопарк планшетов с шагом экрана в 1 дюйм - ярчайший пример. Фееричное растрачивание усилий по всем направлениям, а в результате - вышел киндл файр и слил весь зоопарк с первого места из догоняющих.

http://forum.ixbt.com/topic.cgi?id=16:42549-125#3872 

Тупой писюн за рулем

What a Shame

Эталон колхозника за рулем

Эталон тупого колхозника на дороге. Целый букет - просачивание рядом для выезда за стоп линию, наезд на пешеходный переход и проезд перекрестка на красный. Чтобы... поехать на мойку.

За рулем, видимо, водила, депутата внутри не было.

Android developers

Diana Hackborn, единственная мать ОС Android. Теперь от этой ОС и телефонов на ее основе я буду держаться еще дальше. Гы.

DOTA 2 beta key

Получил от добрых людей DOTA 2 beta gift. Блеять, оторваться не могу.

DOTA 2

Интерфейс у игры конченный, сервера нестабильны. Бета - она и есть бета. Но кайфу от командной резни полные трусы.

Если у вас есть приглашения - с удовольствием передам их товарищам, которые любят и хотят играть в DOTA 2. Не хватает 3 человека в команду. Напишите, пожалуйста, если хотите поделиться.

Copyright 2007-2011 Chabster