Q:

Win32 detect if window is maximized/docked to half screen (Win-key + Left/Right)

I have a classic Win32-API (C++) application and need to detect if the window is docked to the left/right half of the screen. 
Background of the question is that the window only sizes in grid steps, let's say 32 pixel.   In full screen the program detects that state, allow size to match the full screen and pad the excess space.   With Windows 8 and later I would like to do the same instead of currently leaving borders (because the size snaps to a multiple of 32 pixel). 

A:

With the function GetWindowPlacement() you can retrieve the normal window rectangle, using the member rcNormalPosition of WINDOWPLACEMENT.  Then compare the normal rectangle to the actual window rectangle.  If they don't match the window is most likely in a docked state. 
Example:
bool IsDockedToMonitor(HWND hWnd)
{
    WINDOWPLACEMENT placement = {sizeof(WINDOWPLACEMENT)};
    GetWindowPlacement(hWnd, &placement);
    RECT rc;
    GetWindowRect(hWnd, &rc);

    return placement.showCmd == SW_SHOWNORMAL
        && (rc.left ! = placement.rcNormalPosition.left ||
            rc.top ! = placement.rcNormalPosition.top ||
            rc.right ! = placement.rcNormalPosition.right ||
            rc.bottom ! = placement.rcNormalPosition.bottom);
}

Note that this solution is not reliable 100% of the time.   There is a slim chance that the normal rectangle and current window rectangle could match even when the window is docked to the side of the monitor. 

A:

In addition to what IInspectable has already mentioned, there is another way to determine this information and act accordingly. 

Wait for a WM_WINDOWPOSCHANGED message and read its x, y, cx, and cy values from the WINDOWPOS pointer stored in lParam. 
Get a handle to the current monitor on which the window is placed by calling MonitorFromWindow. 
Create a MONITORINFO variable and set its cbSize field to sizeof(MONITORINFO). 
Use the monitor handle and the address of your MONITORINFO variable to call GetMonitorInfo.  
Read the rcWork value from your MONITORINFO variable. 

rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcWork.left == WINDOWPOS.x - the window is "docked" to the left
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx)  && rcwork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the right
rcWork.top == WINDOWPOS.y && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the top
rcWork.top == (WINDOWPOS.y + WINDOWPOS.cy) && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the bottom

You say you already have logic to determine if the window is fullscreen (do you mean fullscreen or maximized?),  but effective maximization can be determined if left == x && top == y && right == x + cx && bottom == y + cy. 
Here is an MSDN example of something similar. 
Note that it may be more desirable to cache the MONITORINFO values so you don't need to call it every time the window is repositioned. 

If you only want this to apply when a user does NOT manually resize the window, here is a contrived example of a possible way to do so:
LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
  static bool userSizing = false;

  switch (msg)
  {
  // could also catch WM_ENTERSIZEMOVE here, but this will trigger on 
  // moves as well as sizes
  case WM_SIZING:
    userSizing = true;
    break;

  case WM_EXITSIZEMOVE:
    userSizing = false;
    break;

  case WM_WINDOWPOSCHANGED:
    if (userSizing)
    {
      break;
    }

    // do logic to check to see if the window is sized in a "docked"
    // manner here
    break;

  // handle other window messages ...

  }
}

