Source Reader

mfreadwrite.hをインクルードしたときに、

1>c:\program files (x86)\***\mfreadwrite.h(273): error C2065: 'IMFMediaSource': 定義されていない識別子です。
1>c:\program files (x86)\***\mfreadwrite.h(273): error C2065: 'pMediaSource': 定義されていない識別子です。
1>c:\program files (x86)\***\mfreadwrite.h(274): error C2065: 'pAttributes': 定義されていない識別子です。
...

のようにC2065でエラーとなるときには、先にmfidl.hをインクルードします。c++ - Error in windows file mfreadwrite.h - Stack Overflow

Source Readerの作成

MFCreateSourceReaderFromURL()

URLからSource Readerを作成できます。

HRESULT MFCreateSourceReaderFromURL(
  LPCWSTR         pwszURL,         // 開くMedia FileのURL
  IMFAttributes   *pAttributes,    // Source Readerを設定するための、IMFAttributesへのポインタ
  IMFSourceReader **ppSourceReader // 結果を受け取るための、IMFSourceReaderへのポインタ
);
MFCreateSourceReaderFromURL function | Microsoft Learn

無効なファイルを指定すると、次のようなエラーが返されます。

  • 0x80070002 (ERROR_FILE_NOT_FOUND) … 指定されたファイルが見つかりません。
  • 0xC00D36C4 (MF_E_UNSUPPORTED_BYTESTREAM_TYPE) … 指定された URL のバイト ストリーム タイプはサポートされていません。

これで開いたファイルは、このSource Readerを解放するまでロックされます。

MFCreateSourceReaderFromMediaSource()

Media SourceからSource Readerを作成できます。

HRESULT MFCreateSourceReaderFromMediaSource(
  IMFMediaSource  *pMediaSource,
  IMFAttributes   *pAttributes,
  IMFSourceReader **ppSourceReader
);
MFCreateSourceReaderFromMediaSource function (mfreadwrite.h) | Microsoft Learn

Media Sampleの読み込み

ReadSample()

Media Sourceから、次のMedia Sampleを読み込めます。

読み込まれたMedia Sampleに含まれる音声サンプルはMedia Dataの一部であり、すべてを読み込むにはMedia SourceからすべてのMedia Sampleを読み込む必要があります。

HRESULT ReadSample(
  DWORD     dwStreamIndex,         // データを入力するストリーム
  DWORD     dwControlFlags,        // MF_SOURCE_READER_CONTROL_FLAG列挙型または0
  DWORD     *pdwActualStreamIndex, // ストリームのインデックスを受け取るためのポインタ
  DWORD     *pdwStreamFlags,       // 結果の状態を受け取るためのポインタ。状態はMF_SOURCE_READER_FLAG列挙型の値
  LONGLONG  *pllTimestamp,         // サンプルのタイムスタンプ、またはpdwStreamFlagsで示されるストリームイベントの時間 [100nsec単位]
  IMFSample **ppSample             // サンプルを受け取るためのポインタ
);
IMFSourceReader::ReadSample | Microsoft Learn

読み込んだ結果は、引数のpdwStreamFlagsから確認できます。

MF_SOURCE_READER_FLAG列挙型
識別子 意味
MF_SOURCE_READERF_ERROR An error occurred. If you receive this flag, do not make any further calls to IMFSourceReader methods.
MF_SOURCE_READERF_ENDOFSTREAM source readerはストリームの最後に到達した。
MF_SOURCE_READERF_NEWSTREAM One or more new streams were created. Respond to this flag by doing at least one of the following:
  • Set the output types on the new streams.
  • Update the stream selection by selecting or deselecting streams.
MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED The native format has changed for one or more streams. The native format is the format delivered by the media source before any decoders are inserted.
MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED The current media has type changed for one or more streams. To get the current media type, call the IMFSourceReader::GetCurrentMediaType method.
MF_SOURCE_READERF_STREAMTICK There is a gap in the stream. This flag corresponds to an MEStreamTick event from the media source.
MF_SOURCE_READERF_ALLEFFECTSREMOVED All transforms inserted by the application have been removed for a particular stream. This could be due to a dynamic format change from a source or decoder that prevents custom transforms from being used because they cannot handle the new media type.
MF_SOURCE_READER_FLAG | Microsoft Learn

同期モード (Synchronous Mode)

同期モードでは、次のMedia Sampleが得られるまでスレッドがブロックされます。Synchronous Mode - IMFSourceReader::ReadSample (mfreadwrite.h) - Win32 apps | Microsoft Learn

HRESULT hr = S_OK;

// COMライブラリを初期化する
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

// Media Foundationを初期化する
hr = MFStartup(MF_VERSION);


// ファイルを開く
LPCWSTR pwszURL = L"C:\\sample.wav";

IMFSourceReader* pSourceReader = NULL;
hr = MFCreateSourceReaderFromURL(pwszURL, NULL, &pSourceReader);


// 出力フォーマットを指定する
UINT32 sampleRate = 2000;
UINT32 bitsPerSample = 8;
UINT32 channels = 1;

UINT32 blockAlign = channels * (bitsPerSample / 8);
UINT32 bytesPerSecond = blockAlign * sampleRate;

IMFMediaType* pNewMediaType = NULL;
hr = MFCreateMediaType(&pNewMediaType);

hr = pNewMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
hr = pNewMediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
hr = pNewMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, channels);
hr = pNewMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, sampleRate);
hr = pNewMediaType->SetUINT32(MF_MT_AUDIO_BLOCK_ALIGNMENT, blockAlign);
hr = pNewMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, bytesPerSecond);
hr = pNewMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample);
hr = pNewMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);

hr = pSourceReader->SetCurrentMediaType(
    (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
    NULL,
    pNewMediaType
);

// ストリームが確実に選択されるようにする
hr = pSourceReader->SetStreamSelection(
    (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
    TRUE
);

// データを読み込む
BYTE* pBuffer = NULL;
IMFSample* pSample = NULL;
IMFMediaBuffer* pMediaBuffer = NULL;

while (true)
{
    DWORD dwStreamFlags = 0;
    hr = pSourceReader->ReadSample(
        (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
        0,
        NULL,
        &dwStreamFlags,
        NULL,
        &pSample
    );

    if (dwStreamFlags) break;
    if (pSample == NULL) continue;

    // サンプルからバッファへのポインタを取得する
    hr = pSample->ConvertToContiguousBuffer(&pMediaBuffer);

    DWORD cbCurrentLength = 0;
    hr = pMediaBuffer->Lock(&pBuffer, NULL, &cbCurrentLength);

    // データが格納されている pBufferを処理する

    hr = pMediaBuffer->Unlock();
    pMediaBuffer->Release();
    pBuffer = NULL;

    pSample->Release();
    pSample = NULL;
}

pSourceReader->Release();
pNewMediaType->Release();

if (pBuffer) pMediaBuffer->Unlock();
pMediaBuffer->Release();

if (pSample) pSample->Release();


// 終了処理
MFShutdown();
CoUninitialize();
Audio Clip Sample - Win32 apps | Microsoft Learn

非同期モード (Asynchronous Mode)

非同期モードでは即座に制御が戻り、操作が完了したときにIMFSourceReaderCallback::OnReadSample()が呼ばれます。Asynchronous Mode - IMFSourceReader::ReadSample (mfreadwrite.h) - Win32 apps | Microsoft Learn

まず、IMFSourceReaderCallbackを実装したクラスを定義します。

class SourceReaderCallback : public IMFSourceReaderCallback
{
public:
    long m_nRefCount;           // 参照カウント
    CRITICAL_SECTION m_critsec; // クリティカル セクション
    HANDLE m_hEvent;            // 同期オブジェクトへのハンドル

    HRESULT m_hrStatus;
    DWORD m_dwStreamFlags;
    IMFSample *m_pSample;

public:
    SourceReaderCallback(HANDLE hEvent)
        : m_nRefCount(1), m_hEvent(hEvent), m_hrStatus(S_OK), m_dwStreamFlags(0), m_pSample(NULL)
    {
        InitializeCriticalSection(&m_critsec);
    }

    ~SourceReaderCallback()
    {
        CloseHandle(m_hEvent);
        DeleteCriticalSection(&m_critsec);
    }

    // IMFSourceReaderCallbackはIUnknownを継承するため、それを実装する
    HRESULT QueryInterface(REFIID iid, void** ppv)
    {
        static const QITAB qit[] = { QITABENT(SourceReaderCallback, IMFSourceReaderCallback),{ 0 } };
        return QISearch(this, qit, iid, ppv);
    }

    ULONG AddRef()
    {
        return InterlockedIncrement(&m_nRefCount);
    }

    ULONG Release()
    {
        ULONG uCount = InterlockedDecrement(&m_nRefCount);
        if (uCount == 0) delete this;
        return uCount;
    }


    // 操作が完了したときに呼ばれる関数
    HRESULT OnReadSample(
        HRESULT hrStatus,
        DWORD /* dwStreamIndex */,
        DWORD dwStreamFlags,
        LONGLONG /* llTimestamp */,
        IMFSample *pSample)
    {
        EnterCriticalSection(&m_critsec); // クリティカル セクションの所有権を獲得


        // 結果をクラス フィールドで保持
        m_hrStatus = hrStatus;
        m_dwStreamFlags = dwStreamFlags;

        m_pSample = pSample;
        if (m_pSample != NULL) m_pSample->AddRef();


        LeaveCriticalSection(&m_critsec);
        SetEvent(m_hEvent); // 同期オブジェクトをシグナル状態にして、操作の完了を通知

        return S_OK;
    }

    HRESULT OnEvent(DWORD, IMFMediaEvent *)
    {
        return S_OK;
    }

    HRESULT OnFlush(DWORD)
    {
        return S_OK;
    }

    HRESULT Wait(DWORD dwMilliseconds, DWORD *dwStreamFlags, IMFSample **pSample)
    {
        // 同期オブジェクトがシグナル状態になるか、タイムアウトするまで待機
        DWORD dwResult = WaitForSingleObject(m_hEvent, dwMilliseconds);

        if (dwResult == WAIT_TIMEOUT) // タイムアウト
        {
            return E_PENDING;
        }


        EnterCriticalSection(&m_critsec);

        // OnReadSample()で取得した値を返す
        *dwStreamFlags = m_dwStreamFlags;

        *pSample = m_pSample;
        if (*pSample != NULL) (*pSample)->AddRef();

        SafeRelease(&_pSample);

        HRESULT hr = m_hrStatus;
        LeaveCriticalSection(&m_critsec);

        return hr;
    }
};

次にIMFSourceReaderを作成するときに渡すIMFAttributesに、このコールバックのポインタを関連付けます。

// 同期オブジェクトへのハンドルを作成
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

// コールバックを作成
SourceReaderCallback *pCallback = new SourceReaderCallback(hEvent);

hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, pCallback);

そしてReadSample()を呼ぶと、コールバックを関連付けているため即座に制御が戻されます。このとき最後の4つの引数pdwActualStreamIndexpdwStreamFlagspllTimestampppSampleはNULLとします。

hr = pSourceReader->ReadSample(
    (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
    0,
    NULL,
    NULL,
    NULL,
    NULL);

DWORD dwTimeout = 1000;
DWORD dwStreamFlags = 0;
IMFSample *pSampleTmp = NULL;

// OnReadSample()が呼ばれて、結果が返されるまで待機
hr = pCallback->Wait(dwTimeout, &dwStreamFlags, &pSampleTmp);
if (hr == E_PENDING) // timed out

非同期とすることをより意義あるものとするには、この例のように結果が返されるまで待機せず、OnReadSample()で処理するようにします。Using the Source Reader in Asynchronous Mode - Win32 apps | Microsoft Learn

非同期のReadSample()の呼び出しは、Flush()で放棄できます。IMFSourceReader::Flush (mfreadwrite.h) - Win32 apps | Microsoft Learn

Media Typeの設定と取得

SetCurrentMediaType()

指定のストリームにMedia Typeを設定できます。元とは異なる形式を指定した場合には、ReadSample()で読み込むときにその形式に変換されます。

HRESULT SetCurrentMediaType(
  DWORD        dwStreamIndex, // 設定するストリーム
  DWORD        *pdwReserved,  // 予約されている引数。NULLを指定する
  IMFMediaType *pMediaType
);
IMFSourceReader::SetCurrentMediaType | Microsoft Learn

下表の値が返されます。

識別子 意味
0x0 S_OK The method succeeded.
0xC00D36B4 MF_E_INVALIDMEDIATYPE At least one decoder was found for the native stream type, but the type specified by pMediaType was rejected.

(メディアの種類に指定されたデータが無効か、矛盾するか、またはこのオブジェクトではサポートされていません。)

0xC00D36B2 MF_E_INVALIDREQUEST One or more sample requests are still pending.
0xC00D36B3 MF_E_INVALIDSTREAMNUMBER The dwStreamIndex parameter is invalid.
0xC00D5212 MF_E_TOPO_CODEC_NOT_FOUND Could not find a decoder for the native stream type.
Return Value - IMFSourceReader::SetCurrentMediaType | Microsoft Learn Mferror.h

GetCurrentMediaType()

指定のストリームの、現在のMedia Typeを取得できます。SetCurrentMediaType()で設定したストリームからは、その値が返されます。

HRESULT GetCurrentMediaType(
  DWORD        dwStreamIndex, // 要求するストリーム
  IMFMediaType **ppMediaType
);
IMFSourceReader::GetCurrentMediaType | Microsoft Learn

dwStreamIndexで取得するストリームの種類を指定します。

種類
MF_SOURCE_READER_FIRST_VIDEO_STREAM ストリームの最初の映像
MF_SOURCE_READER_FIRST_AUDIO_STREAM ストリームの最初の音声

結果は、Media Type Attributesで定義される属性を含む型で返されます。

GetNativeMediaType()

Media SourceのもともとのMedia Typeを取得できます。SetCurrentMediaType()で新しい値を設定していなければ、GetCurrentMediaType()と同じ結果となります。IMFSourceReader::GetNativeMediaType (mfreadwrite.h) | Microsoft Learn

Media Sourceでの位置

SetCurrentPosition()

Media Sourceの新しい位置まで移動させられます。

HRESULT SetCurrentPosition(
  REFGUID        guidTimeFormat, // 時間の書式を指定するGUID。GUID_NULLとすることで、100nsec単位となる
  REFPROPVARIANT varPosition     // 再生が開始される位置
);
IMFSourceReader::SetCurrentPosition | Microsoft Learn

実際に移動する位置はメディアの内容に依存するため、正確性は保証されません。メディアに映像ストリームが含まれる場合には、キーフレーム (key frame) に最も近い位置に移動します。

PROPVARIANT varPosition;
PropVariantInit(&varPosition);

varPosition.vt = VT_I8; // guidTimeFormatをGUID_NULLとするならば、VT_I8とする
varPosition.hVal.QuadPart = 10000000; // 1秒 (10000000[100nsec])

HRESULT hr = pSourceReader->SetCurrentPosition(GUID_NULL, varPosition);
PropVariantClear(&varPosition);

指定の位置へ移動できないときには「現在のオフセットでの操作は許可されていません。(The operation on the current offset is not permitted.)」として、MF_E_INVALID_POSITIONが返されます。

Presentation Attribute

GetPresentationAttribute()

基礎となるMedia Sourceから属性を取得できます。

HRESULT GetPresentationAttribute(
  DWORD       dwStreamIndex, // 要求するストリーム
  REFGUID     guidAttribute, // 検索する属性のGUID
  PROPVARIANT *pvarAttribute
);
IMFSourceReader::GetPresentationAttribute | Microsoft Learn

guidAttributeは、要求するdwStreamIndexによって

にある値を用います。

たとえばメディアの時間は、次のように取得できます。

HRESULT hr = S_OK;
PROPVARIANT varAttribute;
LONGLONG duration;

hr = pSourceReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &varAttribute);
hr = PropVariantToInt64(varAttribute, &duration);

PropVariantClear(&varAttribute);

サムネイル (Thumbnail)

IMFSourceReader::ReadSample()の呼び出し前に、

IMFMediaType* pNewMediaType = NULL;
hr = MFCreateMediaType(&pNewMediaType);

hr = pNewMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
hr = pNewMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
VideoThumbnail Sample - Win32 apps | Microsoft Learn

のようにサブタイプを指定することで、指定の画像形式で取得できるようになります。また取得する画像の位置は、IMFSourceReader::SetCurrentPosition()で指定します。ただしSetCurrentPosition()で指定できる位置は不正確なため、ReadSample()の引数pllTimestampで実際に取得された位置を確認しながら、指定の位置に近づくようにReadSample()の呼び出しをくり返します。

RGB-32への変換が必要なファイル形式に対しては、Source Reader作成時にMF_SOURCE_READER_ENABLE_VIDEO_PROCESSING属性を指定します。これを怠るとSetCurrentMediaType()呼び出し時にMF_E_INVALIDMEDIATYPE (0xC00D36B4) が返され、Media Typeの設定に失敗します。

IMFAttributes* pAttributes = NULL;
hr = MFCreateAttributes(&pAttributes, 1);
hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE);

IMFSourceReader* pSourceReader = NULL;
hr = MFCreateSourceReaderFromURL(
    pwszURL,
    pAttributes,
    &pSourceReader
);

サンプルコード

HRESULT hr = S_OK;

hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
hr = MFStartup(MF_VERSION);

// ファイルを開く
LPCWSTR pwszURL = L"sample.mp4";

IMFAttributes* pAttributes = NULL;
hr = MFCreateAttributes(&pAttributes, 1);
hr = pAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, TRUE);

IMFSourceReader* pSourceReader = NULL;
hr = MFCreateSourceReaderFromURL(pwszURL, pAttributes, &pSourceReader);


// ビデオ ストリームを選択する
IMFMediaType* pNewMediaType = NULL;
hr = MFCreateMediaType(&pNewMediaType);

hr = pNewMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
hr = pNewMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);

hr = pSourceReader->SetCurrentMediaType(
    (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
    NULL,
    pNewMediaType
);
SafeRelease(&pNewMediaType);

hr = pSourceReader->SetStreamSelection(
    (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
    TRUE
);

// 動画フォーマットを取得する
IMFMediaType* pMediaType = NULL;
hr = pSourceReader->GetCurrentMediaType(
    (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
    &pMediaType
);

GUID subtype = { 0 };
hr = pMediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (subtype != MFVideoFormat_RGB32)
{
    hr = E_UNEXPECTED;
}

UINT32 width = 0;
UINT32 height = 0;
hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);

// メディアの長さを取得する
PROPVARIANT var;
PropVariantInit(&var);
hr = pSourceReader->GetPresentationAttribute(
    (DWORD)MF_SOURCE_READER_MEDIASOURCE,
    MF_PD_DURATION,
    &var
);

LONGLONG hnsDuration = 0;
if (SUCCEEDED(hr))
{
    hnsDuration = var.hVal.QuadPart;
}
PropVariantClear(&var);


// GDI+を初期化する
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusInput;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusInput, NULL);


const int COUNT = 10;
LONGLONG hnsIncrement = hnsDuration / COUNT;

for (int i = 0; i < COUNT; i++)
{
    // メディアの位置を指定する
    PROPVARIANT var;
    PropVariantInit(&var);
    var.vt = VT_I8;
    var.hVal.QuadPart = i * hnsIncrement;

    hr = pSourceReader->SetCurrentPosition(GUID_NULL, var);
    PropVariantClear(&var);

    // ソースリーダーからビデオフレームを取得する
    IMFSample* pSample = NULL;
    while (TRUE)
    {
        DWORD dwStreamFlags = 0;
        LONGLONG pllTimestamp = 0;
        IMFSample* pSampleTmp = NULL;

        hr = pSourceReader->ReadSample(
            (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
            0,
            NULL,
            &dwStreamFlags,
            &pllTimestamp,
            &pSampleTmp
        );

        if (dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM) break;
        if (dwStreamFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
        {
            // 現在のMedia Typeが変更されたため、動画フォーマットを再取得する
            hr = pSourceReader->GetCurrentMediaType(
                (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
                &pMediaType
            );

            hr = pMediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
            if (subtype != MFVideoFormat_RGB32)
            {
                hr = E_UNEXPECTED;
            }

            hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
        }

        if (pSampleTmp == NULL) continue;

        SafeRelease(&pSample);
        pSample = pSampleTmp;
        pSample->AddRef();
        SafeRelease(&pSampleTmp);

        break;
    }

    if (pSample)
    {
        // サンプルからバッファへのポインタを取得する
        IMFMediaBuffer* pMediaBuffer = NULL;
        hr = pSample->ConvertToContiguousBuffer(&pMediaBuffer);

        BYTE* pBuffer = NULL;
        DWORD cbCurrentLength = 0;
        hr = pMediaBuffer->Lock(&pBuffer, NULL, &cbCurrentLength);

        // Bitmapを作成する
        const UINT BYTES_PER_PIXEL = 32 / 8; // PixelFormat32bppRGB
        Gdiplus::Bitmap* bitmap = new Gdiplus::Bitmap(
            width,
            height,
            width * BYTES_PER_PIXEL,
            PixelFormat32bppRGB,
            pBuffer
        );

        wchar_t filename[100];
        swprintf(filename, 100, L"image_%d.bmp", i);

        CLSID clsidEncoder;
        CLSIDFromString(L"{557CF400-1A04-11D3-9A73-0000F81EF32E}", &clsidEncoder);
        Gdiplus::Status status = bitmap->Save(filename, &clsidEncoder);


        pMediaBuffer->Unlock();
        SafeRelease(&pMediaBuffer);
    }
    else
    {
        hr = MF_E_END_OF_STREAM;
    }
    SafeRelease(&pSample);
}
Gdiplus::GdiplusShutdown(gdiplusToken);
SafeRelease(&pSourceReader);

MFShutdown();
CoUninitialize();
Microsoft Learnから検索