読者です 読者をやめる 読者になる 読者になる

Windows 7 64-bit 環境で Application Manifest が無視されることがあるように見える

Windows 7

Windows 7 では,互換性に配慮した形で API の仕様を変更する仕組みが導入されました. <compatibility> という要素がマニフェストファイルのスキーマに追加され,Windows Vista 相当の動作と,Windows 7 で改良された動作のどちらを期待するかが選択できるようになっています.(ただし API ごとの動作選択はできず,あくまでどちらの OS でテストしたかという表明のような形になります)

たとえば,Windows 7 コンテキストを指定することで変わる API 動作のうち,DirectX に関係するものとしては,DirectDraw 経由での Front Buffer への描き込みが大きく制限されるというものがあります.

DirectDraw Lock

  • Windows 7: Applications manifested for Windows 7 cannot call Lock API in DDRAW to lock the primary Desktop video buffer. Doing so will result in error, and NULL pointer for the primary will be returned. This behavior is enforced even if Desktop Window Manager Composition is not turned on. Windows 7 compatible applications must not lock the primary video buffer to render.
  • Windows Vista (default): Applications will be able to acquire a lock on the primary video buffer as legacy applications depend on this behavior. Running the application turns off Desktop Window Manager.

DirectDraw Bit Block Transfer (Blt) to Primary without Clipping Window

  • Windows 7: Applications manifested for Windows 7 are prevented from performing Blt's to the primary Desktop video buffer without a clipping window. Doing so will result in error and the Blt area will not be rendered. Windows enforces this behavior even if you do not turn on Desktop Window Manager Composition. Windows 7 compatible applications must Blt to a clipping window.
  • Windows Vista (default): Applications must be able to Blt to the primary without a clipping window as legacy applications depend on this behavior. Running this application turns off the Desktop Window Manager.

この他,Remote Procedure Call (RPC) が OS のスレッドプールを使用するようになる,GetOverlappedResult の挙動の改善,Program Compatibility Assistant の仕様変更が行われています.



んが……どうもこのコンテキストの選択に不可解な挙動というかバグっぽい動作が.
Windows 7 x64 版 (RTM 版) で,Windows 7 互換と宣言したアプリケーションを,32-bit プロセスから起動すると,Windows Vista コンテキストでプロセスが起動してしまっている気がします.いくつかテストした範囲内では,

  • 子プロセスが 32-bit か 64-bit かは関係ない.親プロセスが 32-bit の場合に限り期待通り動作しない.
  • Windows 7 x86 版では発生しない.
  • DirectDraw を経由した FrontBuffer の Lock 動作の挙動と,リソースモニターに表示されるアプリケーションコンテキストは首尾一貫している.
    • Windows Vista コンテキストで起動したときは,FrontBuffer のロックが成功し,DWM がオフになる.

とりあえず本家の MSDN Forums にスレッドを立ててみました.

(2009年10月13日追記) 上記スレッドによると,Windows 7 RTM 版での既知のバグのようです.そのうち修正されるとのこと.急ぎの方は MSDN Subscription 等のインシデント経由で問い合わせてみると良いようです.(既知のバグということで,問い合わせ後のインシデントの消費は無いとのこと)



以下簡単にサンプルコードを用意しました.
http://www.dwahan.net/nyaruru/hatena/manifesttest.zip

#define STRICT
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <tchar.h>
#include <comdef.h>
#include <ddraw.h>

#pragma comment (lib, "ddraw.lib")
#pragma comment (lib, "dxguid.lib")

_COM_SMARTPTR_TYPEDEF(IDirectDraw7, IID_IDirectDraw7);
_COM_SMARTPTR_TYPEDEF(IDirectDrawSurface7, IID_IDirectDrawSurface7);

HWND InitWindow(HINSTANCE instance_handle);

int PASCAL _tWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int nShowCmd)
{
  if (SUCCEEDED(::CoInitialize(NULL))) {
    const HWND window_handle = InitWindow(hinst);

    if (window_handle) {
      ::ShowWindow(window_handle, nShowCmd);

      HRESULT hr = S_OK;

      IDirectDraw7Ptr dd;
      hr = ::DirectDrawCreateEx(NULL, reinterpret_cast<VOID**>(&dd), IID_IDirectDraw7, NULL);
      hr = dd->SetCooperativeLevel(window_handle, DDSCL_NORMAL);

      IDirectDrawSurface7Ptr front_buffer;
      DDSURFACEDESC2 surface_desc;
      ZeroMemory(&surface_desc, sizeof(surface_desc));
      surface_desc.dwSize = sizeof(surface_desc);
      surface_desc.dwFlags = DDSD_CAPS;
      surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
      hr = dd->CreateSurface(&surface_desc, &front_buffer, NULL);

      {
        DDSURFACEDESC2 lock_desc;
        ZeroMemory(&lock_desc, sizeof(lock_desc));
        lock_desc.dwSize = sizeof(lock_desc);

        // DirectDraw Lock
        // Windows 7: Applications manifested for Windows 7 cannot call Lock API in DDRAW to lock 
        //   the primary Desktop video buffer. Doing so will result in error, and NULL pointer for
        //   the primary will be returned. This behavior is enforced even if Desktop Window Manager
        //   Composition is not turned on. Windows 7 compatible applications must not lock the
        //   primary video buffer to render.
        // Windows Vista (default): Applications will be able to acquire a lock on the primary video
        //   buffer as legacy applications depend on this behavior. Running the application turns off
        //   Desktop Window Manager.
        hr = front_buffer->Lock(NULL, &lock_desc, DDLOCK_WAIT, NULL);
        if (SUCCEEDED(hr)) {
          front_buffer->Unlock(NULL);
          ::OutputDebugString(_T("Lock Succeeded\n"));
          if (lock_desc.lpSurface == NULL) {
            ::OutputDebugString(_T("Retrieved pointer is NULL\n"));
          }
        } else {
          ::OutputDebugString(_T("Lock failed\n"));
        }
      }

      MSG msg;
      while (::GetMessage(&msg, NULL, 0, 0)) {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
      }
    }
    ::CoUninitialize();
  }
  return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  switch (uMsg) {
    case WM_DESTROY:
      ::PostQuitMessage(0);
      break;
  }
  return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}

HWND InitWindow(HINSTANCE instance_handle) {
  const TCHAR *class_name = _T("Windows 7 Compatiblity Test");

  WNDCLASS wc      = {0};
  wc.style         = 0;
  wc.lpfnWndProc   = WndProc;
  wc.cbClsExtra    = 0;
  wc.cbWndExtra    = 0;
  wc.hInstance     = instance_handle;
  wc.hIcon         = NULL;
  wc.hCursor       = ::LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
  wc.lpszMenuName  = NULL;
  wc.lpszClassName = class_name;

  ::RegisterClass(&wc);

  return ::CreateWindowEx(
    0, class_name, TEXT("Test Window"), WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    NULL, NULL, instance_handle, NULL);
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!--Windows 7-->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
    </application>
  </compatibility>
</assembly>