Dell の個人向け商品で領収書をもらう方法 (または,まともなタッチ対応ディスプレイを経費で買う方法)

まとめ

Dell の 10 点タッチ外付け 23 インチディスプレイ S2340T は,個人向けサイトにしか載っていないけど,経費購入に必要な領収書も用意してもらえるので必要な人は迷わず買うと良いよ.

S2340T

Windows 8 に起因するタッチ対応案件は第一弾が一段落したころでしょうか.「Windows 7 のときに済ませた」という人から,「まだ対応していない」,「テストすらしていない」,「積み残しタスクがある」,という方まで,色々いらっしゃることかと思います.
そんななか,まだタッチ対応アプリケーションの開発体制を整えていなくて機材を探しているという場合におすすめなのが Dell の 23 インチディスプレイ S2340T.この製品,Full HD (1,920 x 1,080),10 点タッチに対応しつつ,59,980 円という Windows 8 前のタッチ対応外付けディスクプレイからは考えられない低価格で.半年前に発表されるやいなや注文が積み上がり,一時期は通常納期 + 14 週間というすさまじいバックオーダーが生じていました.それが最近は需給が釣り合ってきたらしく,ついに通常納期まで改善したようなのでおすすめしてみる次第であります.


(参考) 製品紹介記事:
screenshot screenshot screenshot
さて,この製品を経費で買おう思ったあなたに一点アドバイス.S2340T は Dell の個人向けサイト にのみ掲載されており,法人向けサイトには掲載されていません.これで困るのが領収書.通常の注文フローだと会社名・事業者名宛ての領収書が作れないっぽく見えるため,経費で買おう企んでいた人はそこで諦めてしまいかねません.私の場合,諦めきれなかったので Dell のサポートの人に聞いてみたのですが,実は個人向け製品でも領収書は作れるとのこと.

Q 領収書を発行可能ですか?
A 領収書は、現金払いでのご注文の場合原則”お振込票の控え”等をもって代えさせて頂いております。現金以外のお支払方法ご利用の場合、お客様よりご希望のあった場合のみ発行しております。ご希望の場合はこちらのフォームよりご連絡いただけますようお願い申し上げます。
※個人のお客様で現金前払いのお客様には弊社にて入金確認後「入金確認書兼領収書」を郵送させていただいておりますのでそちらを領収書としてご利用いただけます。
※領収日は現金前振込の場合はご入金の確認がとれた日・クレジットカードの場合はカード会社より引き落としを承認された日になります。(領収日の変更は承ることが出来ません。)
※領収書のお届けは納品後となり、お時間がかかることもございます。また商品には同梱できませんので、予めご了承ください。

よくあるご質問

流れとしては,以下のようになるようです.
1. 個人向けページから購入する.
2. 領収書発行フォーム から領収書を請求する.

というわけで,S2340T の納期も改善してきたタイミングでの,タッチ対応 Windows アプリケーション / Web アプリ開発者向け情報でしたー

SSD なら動作を変えるアプリケーションを作る

Windows 7 以降の OS では,SSD 上のボリュームに対してデフラグがスケジューリングされません.これと同様に,ドライブ特性に応じた動作戦略の変更をアプリケーションでも行いたいこともあるでしょう.そのような場合にアプリケーションに組み込めるように,簡単なサンプルコードを書いてみます.
元ネタは,
Windows 7 Disk Defragmenter User Interface Overview - The Storage Team at Microsoft - File Cabinet Blog - Site Home - TechNet Blogs
で解説されているアルゴリズムです.

以下,物理ドライブ "\\.\PhysicalDrive0" について,"no seek penalty" かどうかと,"nominal media rotation rate" かどうかの取得を行います.なお,前者はディスクポートドライバの動作に依存するため,Windows 7 以降でなければ使用できませんが,管理者権限なしで動作するというメリットがあります.後者は ATA8-ACS に対応したデバイスであれば,Windows 7 より前の OS でも動作しますが,動作には管理者権限が必要です.
上記記事に解説されているように,これらの関数を組み合わせて使ってもよいですし,どちらか一方のみを使いある程度の false negative は諦めるという手もあります.

#include <Windows.h>
#include <WinIoCtl.h>
#include <Ntddscsi.h>
#include <Setupapi.h>

#include <iostream>
#include <string>

using std::wstring;
using std::wcout;
using std::endl;

// Returns S_OK if |physical_drive_path| has no seek penalty.
// Returns S_FALSE otherwise.
// Returns E_FAIL if fails to retrieve the status.
// |physical_drive_path| should be something like
// "\\\\.\\PhysicalDrive0".
HRESULT HasNoSeekPenalty(const wstring& physical_drive_path) {
    // We do not need write permission.
  const HANDLE handle = ::CreateFileW(
      physical_drive_path.c_str(), FILE_READ_ATTRIBUTES, 
      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (handle == INVALID_HANDLE_VALUE) {
    return E_FAIL;
  }

  STORAGE_PROPERTY_QUERY query_seek_penalty = {
    StorageDeviceSeekPenaltyProperty,  // PropertyId
    PropertyStandardQuery,             // QueryType,
  };
  DEVICE_SEEK_PENALTY_DESCRIPTOR query_seek_penalty_desc = {};
  DWORD returned_query_seek_penalty_size = 0;
  const BOOL query_seek_penalty_result = DeviceIoControl(
    handle, IOCTL_STORAGE_QUERY_PROPERTY,
    &query_seek_penalty, sizeof(query_seek_penalty),
    &query_seek_penalty_desc,
    sizeof(query_seek_penalty_desc),
    &returned_query_seek_penalty_size, NULL);
  CloseHandle(handle);
  if (!query_seek_penalty_result) {
    // failed to retrieve data.
    return E_FAIL;
  }

  return !query_seek_penalty_desc.IncursSeekPenalty ?
      S_OK : S_FALSE;
}

// Returns S_OK if |physical_drive_path| has nominal media
// rotation rate in terms of ATA8-ACS specification.
// http://www.t13.org/Documents/UploadedDocuments/docs2007/D1699r4-ATA8-ACS.pdf#Page=179
// Returns S_FALSE otherwise.
// Returns E_FAIL if fails to retrieve the status.
// |physical_drive_path| should be something like
// "\\\\.\\PhysicalDrive0".
HRESULT HasNominalMediaRotationRate(
    const wstring& physical_drive_path) {
  // In order to use IOCTL_ATA_PASS_THROUGH,
  // We *do* need read/write permission, which means
  // that the caller has admin privilege.
  const HANDLE handle = CreateFileW(
    physical_drive_path.c_str(),
    GENERIC_READ | GENERIC_WRITE, 
    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  if (handle == INVALID_HANDLE_VALUE) {
    return E_FAIL;
  }

  struct ATAIdentifyDeviceQuery {
    ATA_PASS_THROUGH_EX header;
    WORD data[256];
  };

  ATAIdentifyDeviceQuery id_query = {};
  id_query.header.Length = sizeof(id_query.header);
  id_query.header.AtaFlags = ATA_FLAGS_DATA_IN;
  id_query.header.DataTransferLength =
      sizeof(id_query.data);
  id_query.header.TimeOutValue = 3;  // sec
  id_query.header.DataBufferOffset =
      sizeof(id_query.header);
  id_query.header.CurrentTaskFile[6] =
      0xec;  // ATA IDENTIFY DEVICE command

  DWORD retval_size = 0;
  const BOOL result = DeviceIoControl( 
    handle, IOCTL_ATA_PASS_THROUGH,
    &id_query, id_query.header.DataTransferLength,
    &id_query, id_query.header.DataTransferLength,
    &retval_size, NULL);
  if (!result) {
    return E_FAIL;
  }
  const int kNominalMediaRotRateWordIndex = 217;
  // RPM == 1 means this is non-rotate device
  return id_query.data[kNominalMediaRotRateWordIndex] == 1 ?
      S_OK : S_FALSE;
}

int main() {
  const wstring kDrive = L"\\\\.\\PhysicalDrive0";

  switch (HasNoSeekPenalty(kDrive)) {
    case S_OK:
      wcout << kDrive << L" has no seek penalty." << endl;
      break;
    case S_FALSE:
      wcout << kDrive << L" has seek penalty." << endl;
      break;
    default:
      wcout << L"failed to retrieve the status." << endl;
      break;
  }
 
  switch (HasNominalMediaRotationRate(kDrive)) {
    case S_OK:
      wcout << kDrive << L" has no seek penalty." << endl;
      break;
    case S_FALSE:
      wcout << kDrive << L" has seek penalty." << endl;
      break;
    default:
      wcout << L"failed to retrieve the status." << endl;
      break;
  } 

  return 0;
}

さて,"C:\Windows\System32\calc.exe" のようなパス名から,そのファイルが存在する物理ドライブ名を取得するにはどうすればよいでしょうか?
これには,

  1. GetVolumePathName API を利用してパスからマウントポイントを取得する
  2. GetVolumeNameForVolumeMountPoint API を利用して,論理ボリューム名を取得する
  3. IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS IO Control を利用して,物理ドライブの番号を取得する

という手順を踏みます.後は,得られた番号を "\\.\PhysicalDrive" の末尾に付ければ,物理ボリューム名が判明します.
なお,ダイナミックボリュームでは,複数の物理ドライブにまたがった論理ボリュームが作成可能なことに注意してください.以下の関数は,指定されたパスに対応する物理ドライブ番号を返します.

vector<int> GetExtentsFromPath(const wstring& path) {
  vector<int> extents;

  wchar_t mount_point[1024];
  if (!GetVolumePathNameW(
        path.c_str(), mount_point, ARRAYSIZE(mount_point))) {
    return extents;
  }

  wchar_t volume_name[1024];
  if (!GetVolumeNameForVolumeMountPointW(
          mount_point, volume_name, ARRAYSIZE(volume_name))) {
    return extents;
  }

  wstring volume = volume_name;

  // remove trailing '\\'
  volume.resize(volume.size() - 1);

  // We do not need write permission (nor admin rights).
  const HANDLE volume_handle = CreateFileW(
      volume.c_str(), FILE_READ_ATTRIBUTES, 
      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
      FILE_ATTRIBUTE_NORMAL, NULL);

  VOLUME_DISK_EXTENTS initial_buffer = {};
  DWORD returned_size = 0;
  const BOOL get_volume_disk_result = DeviceIoControl(
      volume_handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
      NULL, 0, &initial_buffer, sizeof(initial_buffer),
      &returned_size, NULL);
  const DWORD query_size_error = GetLastError();
  if (get_volume_disk_result != FALSE &&
      initial_buffer.NumberOfDiskExtents == 1) {
    extents.push_back(initial_buffer.Extents[0].DiskNumber);
    return extents;
  }
  if (query_size_error != ERROR_MORE_DATA) {
    return extents;
  }

  const size_t buffer_size =
      sizeof(initial_buffer.NumberOfDiskExtents) + 
      sizeof(initial_buffer.Extents) *
      initial_buffer.NumberOfDiskExtents;
  char* underlaying_buffer = new char[buffer_size];
  VOLUME_DISK_EXTENTS* query_buffer =
      reinterpret_cast<VOLUME_DISK_EXTENTS *>(
          &underlaying_buffer[0]);
  const BOOL devide_ioc_result = DeviceIoControl(
      volume_handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
      NULL, 0, 
      query_buffer, buffer_size, &returned_size, NULL);
  const DWORD device_detail_result_error = ::GetLastError();
  if (!!devide_ioc_result) {
    for (DWORD i = 0;
         i < query_buffer->NumberOfDiskExtents; ++i) {
      extents.push_back(query_buffer->Extents[i].DiskNumber);
    }
  }
  delete[] underlaying_buffer;
  CloseHandle(volume_handle);
  return extents;
}

幸いなことに,上記処理は管理者権限なしでも動作します.Windows 7 以降では,管理者に昇格する必要なしに,SSD 上で動作しているらしいかどうかをアプリケーションが判別できるということになります.

IWordBreaker とファイル検索

「『プリキュア』で検索したら『ハートキャッチプリキュア』にマッチしない」という Windows Search の話.

Windows7に深刻なバグを発見したので、警鐘を鳴らすために晒してみます。
再現に使用したOSはWindows7 Home Premium x64です。

バグの再現手順

 
!!! 悪用厳禁 !!!
 
●1.適当にフォルダを作る 名前は何でもOK


 
●2.作ったフォルダーを開いて、
ハートキャッチプリキュア
ふたりはプリキュア
プリキュア
の3つのフォルダを新規作成する

 

●3.検索窓に「プリキュア」と入力してみる


 

●4.「ハートキャッチプリキュア」が無かったことにされる

ちくしょう!誰がこんなことを!メディーック!!メディーーーーック!!

対処方法

検索窓に「*プリキュア」と入れると全部ヒットするみたい。

でも、XPの頃は「プリキュア」で全部ヒットしてたのでなんか腑に落ちないアレが。

ちなみに検索インデックスの有無は関係ないみたいです。

#2010/10/30 11:05 追記
VistaやMacOSでも再現するとか。
Windowsの人は、「Everything」を使うと幸せになれるらしいです。

「従来何も考えずにファイル名の部分文字列で検索できていたのものを,どうしてアスタリスクが必要にしちゃったの?」という方向の話のような気もしますが,その辺は置いておいて久しぶりに IWordBreaker とか.
Windows 7 に標準で付いてくる日本語向け IWordBreaker 実装に「ハートキャッチプリキュア」等を食わせてみます.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.Win32;
using WordBreaker;

namespace WordBreakerTest
{
  using HRESULT = System.UInt32;
  public struct HResults
  {
    public const HRESULT S_OK = 0x00000000;
    public const HRESULT S_FALSE = 0x00000001;
    public const HRESULT E_FAIL = 0x80004005;
    public const HRESULT WBREAK_E_END_OF_TEXT = 0x80041780;
    public const HRESULT LANGUAGE_S_LARGE_WORD = 0x00041781;
    public const HRESULT WBREAK_E_QUERY_ONLY = 0x80041782;
    public const HRESULT WBREAK_E_BUFFER_TOO_SMALL = 0x80041783;
    public const HRESULT LANGUAGE_E_DATABASE_NOT_FOUND = 0x80041784;
    public const HRESULT WBREAK_E_INIT_FAILED = 0x80041785;
  }

  public enum WORDREP_BREAK_TYPE
  {
    WORDREP_BREAK_EOW = 0,
    WORDREP_BREAK_EOS = 1,
    WORDREP_BREAK_EOP = 2,
    WORDREP_BREAK_EOC = 3
  }

  [SuppressUnmanagedCodeSecurity]
  [ComImport, Guid("CC907054-C058-101A-B554-08002B33B0E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IWordSink
  {
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT PutWord(
        uint cwc,
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf,
        uint cwcSrcLen,
        uint cwcSrcPos);
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT PutAltWord(
        uint cwc,
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf,
        uint cwcSrcLen,
        uint cwcSrcPos);
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT StartAltPhrase();
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT EndAltPhrase();
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT PutBreak(WORDREP_BREAK_TYPE breakType);
  }

  [SuppressUnmanagedCodeSecurity]
  [ComImport, Guid("CC906FF0-C058-101A-B554-08002B33B0E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IPhraseSink
  {
    [Obsolete("Not supported.")]
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT PutSmallPhrase(
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcNoun,
        uint cwcNoun,
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3, ArraySubType = UnmanagedType.U2)] char[] pwcModifier,
        uint cwcModifier, uint ulAttachmentType);
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT PutPhrase(
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcPhrase,
        uint cwcPhrase);
  }

  public class WordSink : IWordSink
  {
    public Action<string, uint, uint> OnWord { get; set; }
    public Action<string, uint, uint> OnAltWord { get; set; }
    public Action<WORDREP_BREAK_TYPE> OnBreak { get; set; }
    #region CWordSink Members
    public HRESULT PutWord(
        uint cwc,
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf,
        uint cwcSrcLen,
        uint cwcSrcPos)
    {
      if (OnWord != null)
      {
        OnWord(new string(pwcInBuf), cwcSrcLen, cwcSrcPos);
      }
      return HResults.S_OK;
    }
    public HRESULT PutAltWord(
        uint cwc,
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.U2)] char[] pwcInBuf,
        uint cwcSrcLen,
        uint cwcSrcPos)
    {
      if (OnAltWord != null)
      {
        OnAltWord(new string(pwcInBuf), cwcSrcLen, cwcSrcPos);
      }
      return HResults.S_OK;
    }
    public HRESULT StartAltPhrase()
    {
      return HResults.S_OK;
    }
    public HRESULT EndAltPhrase()
    {
      return HResults.S_OK;
    }
    public HRESULT PutBreak(WORDREP_BREAK_TYPE breakType)
    {
      if (OnBreak != null)
      {
        OnBreak(breakType);
      }
      return HResults.S_OK;
    }
    #endregion
  }

  public class CPhraseSink : IPhraseSink
  {
    #region CPhraseSink Members
    public HRESULT PutSmallPhrase(
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcNoun,
        uint cwcNoun,
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3, ArraySubType = UnmanagedType.U2)] char[] pwcModifier,
        uint cwcModifier,
        uint ulAttachmentType)
    {
      return HResults.S_OK;
    }
    public HRESULT PutPhrase(
        [In][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1, ArraySubType = UnmanagedType.U2)] char[] pwcPhrase,
        uint cwcPhrase)
    {
      return HResults.S_OK;
    }
    #endregion
  }

  [UnmanagedFunctionPointer(CallingConvention.StdCall)]
  public delegate uint FillTextBufferDelegate(ref TEXT_SOURCE pTextSource);

  [StructLayout(LayoutKind.Sequential)]
  public struct TEXT_SOURCE
  {
    [MarshalAs(UnmanagedType.FunctionPtr)]
    public FillTextBufferDelegate pfnFillTextBuffer;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string awcBuffer;
    public uint iEnd;
    public uint iCur;
  }

  [SuppressUnmanagedCodeSecurity]
  [ComImport, Guid("D53552C8-77E3-101A-B552-08002B33B0E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface IWordBreaker
  {
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT Init(
        [MarshalAs(UnmanagedType.Bool)] bool fQuery,
        uint maxTokenSize, [MarshalAs(UnmanagedType.Bool)] out bool pfLicense);
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT BreakText(
        ref TEXT_SOURCE pTextSource, [MarshalAs(UnmanagedType.Interface)] IWordSink pWordSink,
        [MarshalAs(UnmanagedType.Interface)] IPhraseSink pPhraseSink);
    [PreserveSig, MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    HRESULT GetLicenseToUse([MarshalAs(UnmanagedType.LPWStr)] out string ppwcsLicense);
  }

  public static class Program
  {
    public static void BreakText(string text, bool forQuery)
    {
      const string kWordBreakerKey =
          @"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ContentIndex\Language\Japanese_Default";
      var guid = new Guid(Registry.GetValue(kWordBreakerKey, @"WBreakerClass", string.Empty) as string);
      var wordBreakerType = Type.GetTypeFromCLSID(guid);

      // A newer wordbreaker shipped with MS Office 2010.
      // wordBreakerType = Type.GetTypeFromProgID("NLG.Japanese Wordbreaker.4.1");

      var wordBreaker = default(IWordBreaker);
      try
      {
        wordBreaker = Activator.CreateInstance(wordBreakerType) as IWordBreaker;

        var license = true;
        wordBreaker.Init(forQuery, 4096, out license);

        var filler = (FillTextBufferDelegate)((ref TEXT_SOURCE _) => HResults.WBREAK_E_END_OF_TEXT);
        var pTextSource = new TEXT_SOURCE()
        {
          pfnFillTextBuffer = filler,
          awcBuffer = text,
          iCur = 0,
          iEnd = checked((uint)text.Length),
        };

        var dictionary = new Dictionary<WORDREP_BREAK_TYPE, string>
        {
          {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOC, "[EOC]"},
          {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOP, "[EOP]"},
          {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOS, "[EOS]"},
          {WORDREP_BREAK_TYPE.WORDREP_BREAK_EOW, "[EOW]"},
        };

        var words = new List<string>();
        var altWords = new List<string>();
        wordBreaker.BreakText(ref pTextSource, new WordSink
        {
          OnWord = (word, _, __) => words.Add(word),
          OnAltWord = (word, _, __) => altWords.Add(word),
          OnBreak = type => { words.Add(dictionary[type]); altWords.Add(dictionary[type]); },
        }, new CPhraseSink());
        GC.KeepAlive(filler);
        Console.WriteLine("Words: " + string.Join("/", words));
        Console.WriteLine("Alt Words: " + string.Join("/", altWords));
      }
      catch
      {
        if (wordBreaker != null)
        {
          Marshal.ReleaseComObject(wordBreaker);
          wordBreaker = null;
        }
      }
    }

    [MTAThread]
    static void Main(string[] args)
    {
      BreakText("プリキュア", false);
      BreakText("ふたりはプリキュア", false);
      BreakText("ハートキャッチプリキュア", false);
      BreakText("マイコンピューター", false);
      BreakText("情シス", false);
    }
  }
}
Words: プリキュア
Alt Words:
Words: ふたり/は/プリキュア
Alt Words:
Words: ハトキアッチプリキュア
Alt Words: ハートキャッチプリキュア
Words: マイコンピュタ
Alt Words: マイコンピューター
Words: 情/シス
Alt Words:

さすがに "プリキュア" で分割してくれたりはしないようですね.というかそもそも,「欧文地名以外の複合語をカタカナ表記するときは分かち書き」という Microsoft のスタイルガイド が遵守されているのが前提なのか,カタカナの連続は何も考えずにくっつけているだけのような挙動にも見えました.あんまりちゃんと実験してませんが.
ちなみに,SharePoint に付属する WordBreaker では,以下のようにユーザ辞書ファイルを使うことが出来るようです.

4. 以下に従い、ファイルを保存します。
場所 "C:\Program Files\Microsoft Office Servers\12.0\Bin"
(日本語ワードブレーカ nlsdata0011.dllが存在する場所)
ファイル名 "Custom0011.lex" (0011 は言語 ID)
文字コード "Unicode"

さらにこの nlsdata0011.dll というファイルですが,手元の Windows 7 Ja 環境では同名のファイルがシステムディレクトリに存在します.試しに %SystemRoot%\System32\Custom0011.lex (と %SystemRoot%\SysWOW64\Custom0011.lex) というファイルを作り,以下の内容を入力し,BOM 付き UTF-16 ファイルで保存してみます.

#CUSTOMER_WB
情シス
プリキュア

改めて最初のコードを実行すると,結果は以下のようになりました.

Words: プリキュア
Alt Words:
Words: ふたり/は/プリキュア
Alt Words:
Words: ハトキアッチプリキュア
Alt Words: ハートキャッチプリキュア
Words: マイコンピュタ
Alt Words: マイコンピューター
Words: 情シス
Alt Words:

少なくとも「情シス」の方は 1 word として認識されるようになりました.また,実行中に Custom0011.lex が読み込まれていることも,Process Monitor のログから確かめられました.
一方,ユーザ辞書に「プリキュア」を追加しても,"ハートキャッチ/プリキュア" と分割されませんでした.これは,以下の SharePoint での事例と同じもののようです.

ワードブレーキング (設定箇所 : サーバー定義ファイル)

こちらは、セミナーの資料では省略していましたが、懇親会でご質問がありましたので記載しておきます (懇親会場でご回答させて頂きました)。

例えば、「ペドロ&カプリシャス」のようなキーワードを検索したい場合、インデックス収集時に、間の記号(アンパサント &)によって、「ペドロ」と「カプリシャス」でキーワードが自動的に区切られます。こうした場合には、カスタムディクショナリー(Custom Dictionary) を設定することで、こうした自動ブレークを阻止し、「ペドロ&カプリシャス」で完全マッチの検索をおこなうことができます。

カスタムディクショナリの設定ファイルを配置する場所は、シソーラスファイルとは異なり、%programfiles%\Microsoft Office Servers\12\bin\CustomLANG.lex です。(日本語の場合は、Custom0011.lex です。) 設定を反映させるには、インデックスの再収集以外に、クエリー時のブレーク箇所も正しく認識させる必要があるため、ファイル編集後は、 Office SharePoint Server Search サービス (osearch) の再起動と、再クロールの双方をおこなってください。

カスタムディクショナリの作成方法については、以下の記事が参考になります。

TechNet : ユーザー辞書を作成する (Office SharePoint Server 2007)
http://technet.microsoft.com/ja-jp/library/cc263242.aspx

実は、懇親会では、「ワードブレークを阻止したい」 というご質問ではなく、逆に 「ワードを分割して認識させられないか」 というものでした。私は、この回答として、「カスタム辞書 (上記の CustomLANG.lex) を編集することで認識させられる可能性があるかもしれない」 とお答えしてしまいましたが、すみません、動作を確認してみたところ、本来分割されていないワードを分割して認識させることは不可能でした。(この予測は誤っておりました。申し訳ありません . . .)

発端の話も,「ワードを分割して認識させられないか」の一種だと思いますが,どうも現世代の Microsoft 製 IWordBreaker 実装ではユーザ辞書を使ってもこの問題を回避できなさそうな感じです. 次なる手段としては,自分で IWordBreaker を実装 して,HKLM\SYSTEM\CurrentControlSet\Control\ContentIndex\Language\Japanese_Default 以下の WBreakerClass を置き換えてしまう,あたりでしょうか.試してはいないので,うまくいくかは分かりませんが.

数式入力パネルとアプリケーションを連携させる 2 つの方法

Windows 7 では,タブレット PC 向け機能が強化され,数式の手書き入力がサポートされるようになりました.この機能とアプリケーションを連携させるための方法を 2 つほど紹介します.

数式入力パネルからのデータをクリップボード経由で受け取る

『数式入力パネル』は,単体アプリケーションとして動作する数式入力ツールです.このツールは,いわゆるソフトウェアキーボードや文字パレットのように動作し,入力フォーカスを持つアプリケーションに数式情報を送り込みます.
実際には,この機能はクリップボードを利用して実現されています.
『数式入力パネル』は,挿入ボタンが押されると,数式を UTF-8エンコードされた MathML 形式でクリップボードに格納し,Ctrl+V のキーボードイベントを発生させます.このとき,入力フォーカスを持つアプリケーションが,Ctrl+V で貼り付け動作を行い,かつクリップボードに格納された "MathML Presentation" 形式または "MathML" 形式のデータを解釈できることが,『数式入力パネル』と連携するための条件です.

参考

数式入力パネル (Math Input Panel、MIP) は、Tablet PC のタブレットとペンを使用するように設計されています。ただし、タッチスクリーン、外部デジタイザー、あるいはマウスなどの任意の入力デバイスでも使用可能です。MIP は、クリップボードを介して、標準化された数学的なマークアップ言語である MathML フォーマットで認識結果を出力します。MIP で手書きされて認識された数式は、完全に編集可能な形式でレプリケート先アプリケーションに出力されます。テキストを編集したいときは、出力に対して挿入したり編集したりできます。

数式入力パネルでは、数学用マークアップ言語 (MathML) をサポートするプログラムにのみ数式を挿入できます。

数式入力パネルの入力ウィンドウをインプロセスで利用する

COM のインプロセスサーバとして,数式入力パネルの入力ウィンドウを利用することも可能です."%CommonProgramFiles%\Microsoft Shared\ink\micaut.dll" 内に格納された TypeLib から、必要な情報を得ることが出来るでしょう.
参考までに,C# で COM Interop を行ってみたサンプルを置いておきます.

このサンプルは,実行すると数式入力パネルの入力ウィンドウとコンソールウィンドウが表示され,入力ウィンドウの「挿入」を押したときに表示されていた数式が MathML 形式でコンソールに表示されます.

余談: 近年の Microsoft 製品と MathML とクリップボード

近年の Microsoft 製品では MathML の利用が増えています.
Microsoft Word 2007 以降の Microsoft Word や,Microsoft PowerPoint 2010 は,MathML 形式で数式を貼り付けることが可能です.ただし,クリップボードデータ形式については注意が必要でした.手元の Microsoft Office 2010 Beta で試してみたところ,以下のような挙動の違いがありました.

  • Microsoft Word 2010
    • データ形式が "Text" または "Unicode Text" であっても,内容が MathML であれば数式として挿入される.
    • データ形式が "MathML Presentation","MathML" いずれの場合も貼り付け可能
  • Microsoft PowerPoint 2010
    • データ形式が "Text" または "Unicode Text" の場合,内容が MathML であってもただのテキストとして解釈される.
    • データ形式が "MathML" の場合,MathML として解釈する."MathML Presentation" はサポートしない.

実験用の PowerShell スクリプトを以下に示します.

# STA モードで PowerShell を起動する (Windows.Forms.Clipboard のため)
powershell.exe -sta
$null = [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

# E = mc^2
$mathml = '<mml:math xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mi>E</mml:mi><mml:mo>=</mml:mo><mml:mi>m</mml:mi><mml:msup><mml:mi>c</mml:mi><mml:mn>2</mml:mn></mml:msup></mml:math>'

# UTF-8 で MemoryStream に格納
$ms = New-Object System.IO.MemoryStream(,[System.Text.Encoding]::UTF8.GetBytes($mathml))

# "MathML" という形式名でクリップボードに格納
[Windows.Forms.Clipboard]::SetData("MathML", $ms)

このスクリプトを実行した後で,Microsoft Word 2010 または Microsoft PowerPoint 2010 に貼り付けを行ってみて下さい.数式が貼り付けられるはずです.
これを利用すると,MathML 形式で数式を出力できるソフトウェア,例えば Mathematica から出力した数式を,構造を保ったまま PowerPoint に貼り付ける,といったことが可能です.
また,Microsoft Word 2010 や Microsoft PowerPoint 2010 からクリップボードにコピーする際に,数式を MathML 形式でコピーするように設定することも可能です.この設定を有効にすれば,PowerPoint に書かれていた数式を Mathematica にコピーし,その場で計算を行う,といった使い道も可能です.

未来の標準は現在のブラウザ実装を制約するか

  • qnighy qnighy DirectXって…標準の意味わかってんのかこいつら 2009/11/19

Internet Explorer 6 以前の DirectX Filter みたいなのと誤解しているのならアレですが,(まだドラフト段階の) 標準と (現在初期実装段階にある製品の) 実装技術が切り離せるかという切り口で見ると結構奥深い話かもしれません.今後たとえば WebGL が標準として意味を持つのであれば,OpenGL をブラウザの実装技術の中核にしておいた方が都合が良いという議論はありえると思います.
WebGL の場合,それが OpenGL コマンドをそのまま流用したような命令形態であることがポイントとなります.仮にブラウザのレンダリングエンジンを Direct3D / Direct2D ベースで行っていた場合,WebGL サポートのためには 1)部分的に OpenGL を使用する 2) Direct3DWebGL の処理をエミュレートする,といった対応が必要となります.いずれの場合も,最初から OpenGL ベースでブラウザを実装するよりは複雑性が増すこととなるでしょう.

WebGLは、3Dグラフィックスを活用したWebアプリの実現を目指し制定された、OpenGL準拠のAPIセット。OpenGL関連であることからうかがえるように、各種グラフィックス技術の標準化を進める団体Khronosグループが仕様の策定を進めている。今回、Safariなど多数のWebブラウザに採用されているHTMLレンダリングエンジン「WebKit」が、WebGL実装を含む開発者向けテスト版の配布を開始したことを受け、どのように表示されるかなどエンドユーザからの目線でテストを実施した。

WebGL最大の特徴は、その3D描画をプラグインなしに実現できることだろう。これまでもWebブラウザ上に3Dグラフィックスを表示する試みは存在し、現在もGoogleが「O3D」の開発を進めるなど動きはあるが、Web標準ではない。プラグインの力を借りず、次世代のWeb標準たるHTML5でサポートされることが、WebGLが注目を集める大きな理由だ。

HTML5 の一部として WebGL がサポートされるかのように海上氏は書かれていますが,果たしてこれが本当かどうかはよく分かりません.少なくとも 『HTML 5 - W3C Working Draft 25 August 2009』にそれらしい記述はみつけられませんでした.とはいえ,今後 HTML5 の延長線上に WebGL が "標準" の地位を手に入れる可能性はあります.このシナリオは Microsoft にとってあまり好ましくないものかもしれません.そして,未来の標準「勧告」に合わせようとすると,現在の技術選定の幅が狭められかねないという構図はちとアレです.
もちろん,WebGLDirect3D/2D が絶対に共存不可能ということはなく,実際現場レベルではこんな話もあります.以下は,WebKit (WebCore) が現在実装中の CSS 3D Transforms Module Level 3 (W3C Working Draft 20 March 2009) (これもまだ Working Draft) を,Windows でどう行うかに関するものです.

現在コミットされているパッチを見る限り,WebKit は,CSS 3D Transforms に Direct3D を使う方向で動いているようです.この流れに対し Brent Fulgham 氏は将来的に WebGL 実装が必要となったときの懸念を示しましたが,Apple の Chris Marrin 氏は「Both OpenGL and Direct3D can handle multiple contexts without any trouble.」と述べ,(WebGL のために必要ならば) OpenGLDirect3D の同時に使用することも念頭に置いているようでした.とまあ外野としては「それが世界の選択か」とつぶやく前にとりあえずソースと関係者の議論を読むってのがいいのかもしれません.
ちなみに,WebKit での DirectWrite サポートも,既にトラック用のバグは登録されています.

同様に Mozilla でも DirectWrite に関する議論は開始されています.流れを見るに Mozilla の方が先に何か出てくるかもしれません.

追記

なんかこんな記事が出ていたのを発見.

追記2

Firefox の関係者この発言も興味深い.WebGL が,Direct3D のような他の Native 3D API でも実装できることを確認したいとのこと.

We still have some ways to go, as there are issues in shader security and portability, not to mention figuring out what to do on platforms where OpenGL is not available. (The latter is an interesting problem; we're trying to ensure that the API can be implementable on top of a non-GL native 3D API, such as Direct3D, so that might be one option.) But progress is being quickly made.

Windows における例外ハンドリングとか 64-bit プロセス固有のあれそれとか

すごくまとまってました.個人的に印象深かったのはこの辺.

実は、x64 版 Windows 用のバイナリにおいては、fs:[0] に例外ハンドリング構造体へのポインタを設定するなどという行為は一切行いません。というより、コード上には、例外をハンドルする如何なる追加コードも存在しません。これは、例外ハンドリングを行わない場合のプログラムには一切オーバーヘッドが存在しないということを意味します。

Windows の細かい部分まで見てみると,64-bit プロセスのみ影響を受ける仕様というのがいくつかあって,今回のケースもそのひとつですかね.64-bit アプリケーションの場合,「従来から存在するバイナリ」というのが存在しないので,互換性に悩まされることなく 32-bit 時代の仕様をリセットできる,と.
Windows の 64-bit 移行に伴うドライバの変更とかは有名でしょうから,アプリケーション制作者に関係しそうな部分で,64-bit アプリケーションにのみ適用される変更からいくつか挙げてみると,

  • UAC に伴う自動昇格 (install.exe とか setup.exe の自動昇格) は,64-bit プロセスには適用されない*1
  • UAC に伴うファイルとレジストリの仮想化は,(デフォルトでは?) 64-bit プロセスに適用されない.
  • 64-bit プロセス向けの関数呼び出し規約が統一された*2.64-bit をターゲットとした場合,__cdecl, __stdcall, __fastcall, __thiscall, __stdcall はいずれも無視される.
  • Windows 7 で導入された User Mode Scheduling (UMS) は,64-bit プロセスでのみ使用可能*3.
  • (ハードウェア) DEP は64-bit プロセスで常にオンである./NXCOMPAT:NO リンカオプションを使用してビルドしても効果がない*4
  • Vista (以降?) の 64-bit プロセスでは HeapEnableTerminationOnCorruption がデフォルトでオンになる*5
  • いくつかの Legacy なライブラリは 64-bit 版が存在しない.例えば DirectMusic performance layer, DirectPlay 4 以前, DirectDraw 6 以前, Direct3D 8 以前, and DirectInput 7 以前の 64-bit 版は存在しない*6

てな感じですかね.

WOW64 と異種バイナリの混在

ついでに思い出したのが,Windows でユーザ作成の 64-bit バイナリと 32-bit バイナリが単一プロセスで混在できないという WOW64 の問題を,メモリレイアウト由来としている Wikipedia のこの記述.さすがに問題を単純化しすぎてるんじゃないでしょーか.

問題点

Windowsの64ビットABIは、そのままWin32の32ビットABIを64ビットに拡張した物である。従って、64ビットABIのアプリケーションは8TBのアドレス空間を独占的に使える様になっている。ここに一つの問題点がある。32ビット ABIのコードを格納可能な仮想空間下位4GBが64ビットABIに独占されてしまった事である。この為、32ビットABIを格納する場所が無く、32ビットアプリケーションはもとより、DLLやOCXをロードして呼び出すことも不可能となってしまった。そのため、マイクロソフトは、32ビットABIのコードと64ビットABIのコードとの相互な呼び出しを禁止している(COMインターフェースを経由して相互乗り入れは可能であるが、x64アーキテクチャで本来可能であったシームレスな32ビットコードと64ビットコードの相互呼び出し機能は全く生かされていない)。この顕著な例として、Internet Explorerの振舞があげられる。32ビットのActiveXコンポーネントを検出すると、64ビット版Internet Explorerは処理を中断して32ビット版Internet Explorerに処理を引き継ぐ。32ビットアプリケーションと64ビットアプリケーションの間には、実行ファイル以外のコンポーネント群を互いに利用することができない深い溝がある。

「Windowsの64ビットABIは、そのままWin32の32ビットABIを64ビットに拡張した物である。」て時点でたぶん外していて,例外処理に関する ABI が違うじゃんと.

リーフ関数 [関数を呼び出したり、任意のスタック空間に自身を割り当てたりしない関数] 以外のすべての関数には、不揮発性レジスタを回復するために、それらを正しくアンワインドする方法をオペレーティング システムに対して記述したデータで注釈を付ける必要があります。このデータは xdata または ehdata と呼ばれ、pdata から指されます。Prolog と epilog は、xdata で正しく記述されるように大幅に制限されます。スタック ポインタは、リーフ関数を除いて、epilog または prolog の一部でない任意のコード領域で 16 バイトに配置される必要があります。prolog および epilog 関数の正しい構造の詳細については、「プロローグとエピローグ」を参照してください。例外処理および例外処理/アンワインディング pdata および xdata の詳細については、「例外処理 (x64)」を参照してください。

仮にメモリレイアウトを工夫して 64-bit コードから 32-bit コードを (またはその逆を) 呼び出せたとして,そこで例外起きたらどうすりゃいいんでしょうね? さらにその応用編として,64-bit コードから 32-bit コードを呼び出し,さらに 64-bit コードにコールバックされた状態で例外が発生した場合も.
もちろん上に挙げたような 32-bit プロセスと 64-bit プロセスの違いもあります.UMS を使う DLL が 32-bit プロセスにロードされた場合,果たして UMS をサポートすべきか否かとか.
他にも API をフックするコードに,プロセス環境ブロック (PEB) やスレッド環境ブロック (TEB) を見に行くコード,自分でコールスタックを遡っちゃうコード,プロセス内でひとつしか存在しないと仮定して書かれた DLL の 32-bit 版と 64-bit 版が同時にロードされてしまうケース,環境変数は 64-bit モジュールと 32-bit モジュールで分かれていた方が良いのか否か,等々.
そんなあれそれで,本気で異種バイナリの混在させたかったら,メモリレイアウト云々は所詮と氷山の一角と思うわけです.

*1:[http://msdn.microsoft.com/ja-jp/magazine/cc163486.aspx:title=アプリケーションで Windows Vista のユーザー アカウント制御を有効に活用する - MSDN Magazine 2007 年 1 月号]

*2:[http://msdn.microsoft.com/ja-jp/library/ms235286.aspx:title=x64 呼び出し規約の概要 - MSDN ライブラリ]

*3:[http://msdn.microsoft.com/en-us/library/dd627187.aspx:title=User-Mode Scheduling - MSDN ライブラリ]

*4:[http://d.hatena.ne.jp/egggarden/20091018/1255844431:title=実行可能属性のページとDEP - やや温め納豆]

*5:[http://blogs.msdn.com/michael_howard/archive/2008/02/18/faq-about-heapsetinformation-in-windows-vista-and-heap-based-buffer-overruns.aspx:title=FAQ about HeapSetInformation in Windows Vista and Heap Based Buffer Overruns - Michael Howard's Web Log]

*6:[http://msdn.microsoft.com/en-us/library/ee418798.aspx:title=64-bit programming for Game Developers - MSDN ライブラリ]

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

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>