GetFinalPathNameByHandleW が XP でも使いたい

```GetFinalPathNameByHandleW``` が使いたかったけど、XP には無かった…
…だけならわざわざ作りはしなかったけれども、c:\tmp の ディレクトリハンドルの GUID を取得した際に FindFirst/NextVolume で列挙したボリューム郡を指していない GUID を返してきたため、しぶしぶ作った。

``` c++
static HRESULT GetVolumeNameForVolumeSerialNumberW(
    const DWORD dwVolumeSerialNumber, 
    CStringW &result);
static HRESULT GetVolumePathNamesForVolumeNameW(
    LPCWSTR szVolumeName,
    std::vector<CStringW> &VolumePaths);

HRESULT GetFinalPathNameByHandleW2(HANDLE hFile, DWORD dwFlags, CStringW &result) {
	BYTE data[sizeof(DWORD) + sizeof(WCHAR) * 32767];	//	32767 is a NTFS maximum filename length
	const DWORD fileNameMask = FILE_NAME_NORMALIZED | FILE_NAME_OPENED;
	const DWORD volumeNameMask = VOLUME_NAME_DOS | VOLUME_NAME_GUID | VOLUME_NAME_NT | VOLUME_NAME_NONE;
	CStringW trail;
	CStringW foundVolume;

	BOOL rc;
	HRESULT hr;

	DWORD dwVolumeSerialNumber;

	rc = GetFileInformationByHandleEx(hFile, FileNameInfo, &data, sizeof(data));
	if (rc == FALSE) {
		return HRESULT_FROM_WIN32(GetLastError());
	}

	{
		FILE_NAME_INFO &fiName = (FILE_NAME_INFO &)data;
		trail = CStringW(fiName.FileName, fiName.FileNameLength / sizeof(WCHAR));
		if (trail[0] == L'\\') {
			trail.Delete(0, 1);
		}

		switch (dwFlags & fileNameMask) {
		case FILE_NAME_NORMALIZED:
			/// ?('_.)?	need change?
			break;
		case FILE_NAME_OPENED:
			/// ?('_.)?	need change?
			break;
		}
	}

	rc = GetVolumeInformationByHandleW(hFile,
	                                   NULL, 0,
	                                   &dwVolumeSerialNumber,
	                                   NULL,
	                                   NULL,
	                                   NULL, 0);
	if (rc == FALSE) {
		return HRESULT_FROM_WIN32(GetLastError());
	}

	hr = GetVolumeNameForVolumeSerialNumber(dwVolumeSerialNumber, foundVolume);

	if (hr == S_OK) {
		switch (dwFlags & volumeNameMask) {
		case VOLUME_NAME_DOS:
			//	\\?\c:\ type.
			{
				std::vector<CStringW> VolumePaths;
				GetVolumePathNamesForVolumeNameW(foundVolume, VolumePaths);

				if (VolumePaths.size() == 0) {
					// ?('_.)? no change? 
					break;
				}

				CStringW shortest;
				DWORD lenShortest = (DWORD)(-1);

				for (int i = 0; i < VolumePaths.size(); i++) {
					size_t len = VolumePaths[i].GetLength();

					if (len < lenShortest) {
						lenShortest = len;
						shortest = VolumePaths[i];
					}
				}

				foundVolume = CStringW(L"\\\\?\\") + shortest;
			}
			break;
		case VOLUME_NAME_GUID:
			//	\\?\Volume{guid}\ type.	no change.
			break;
		case VOLUME_NAME_NT:
			/// don't know format. 調べるのタルい.
			throw "not impremented.";
			break;
		case VOLUME_NAME_NONE:
			/// don't know format. 調べるのタルい.
			throw "not impremented.";
			break;
		}

		hr = S_OK;
	}
	else {
		//	maybe network file handle.
		switch (dwFlags & volumeNameMask) {
		case VOLUME_NAME_DOS:
			//	\\?\UNC\server\share\ type
			hr = S_OK;
			throw "not impremented.";
			break;
		case VOLUME_NAME_GUID:
			/// error.
			hr = E_FAIL;
			break;
		case VOLUME_NAME_NT:
			/// don't know format. 調べるのタルい.
			hr = S_OK;
			throw "not impremented.";
			break;
		case VOLUME_NAME_NONE:
			/// don't know format. 調べるのタルい.
			hr = S_OK;
			throw "not impremented.";
			break;
		}
	}

	if (SUCCEEDED(hr)) {
		result = foundVolume;

		if (result[result.GetLength() - 1] != L'\\') {
			result += L"\\";
		}

		result += trail;
	}
	return hr;
}


static HRESULT GetVolumeNameForVolumeSerialNumber(const DWORD dwVolumeSerialNumber, CStringW &result) {
	WCHAR VolumeNameBuffer[1024];

	HANDLE hFindVolume = FindFirstVolume(VolumeNameBuffer, _countof(VolumeNameBuffer));
	if (hFindVolume == NULL || hFindVolume == INVALID_HANDLE_VALUE) {
		return HRESULT_FROM_WIN32(GetLastError());
	}

	DWORD err;
	do {
		DWORD vsn;
		GetVolumeInformationW(VolumeNameBuffer,
		                      NULL, 0,
		                      &vsn,
		                      NULL,
		                      NULL,
		                      NULL, 0);

		if (vsn != dwVolumeSerialNumber) {
			continue;
		}

		assert(GetLastError() == ERROR_MORE_DATA);
		result = VolumeNameBuffer;
		break;
	}
	while (FindNextVolumeW(hFindVolume, VolumeNameBuffer, _countof(VolumeNameBuffer)));
	err = GetLastError();

	FindVolumeClose(hFindVolume);

	return
	    err == ERROR_NO_MORE_FILES ? HRESULT_FROM_WIN32(ERROR_NOT_FOUND) :
	    err == ERROR_MORE_DATA ? S_OK :
	    HRESULT_FROM_WIN32(err);
}


static HRESULT GetVolumePathNamesForVolumeNameW(
    LPCWSTR szVolumeName,
    std::vector<CStringW> &VolumePaths
) {
	CHeapPtr<WCHAR> heapNames;
	DWORD count;

	SetLastError(ERROR_SUCCESS);
	::GetVolumePathNamesForVolumeNameW(szVolumeName, NULL, 0, &count);
	DWORD lastError = GetLastError();

	if (lastError != ERROR_MORE_DATA) {
		return HRESULT_FROM_WIN32(lastError);
	}

	if (heapNames.Allocate(count) == false) {
		return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
	}

	SetLastError(ERROR_SUCCESS);
	BOOL
	rc = ::GetVolumePathNamesForVolumeNameW(szVolumeName, heapNames.m_pData, count, &count);

	if (rc == FALSE) {
		DWORD lastError = GetLastError();
		return HRESULT_FROM_WIN32(lastError);
	}

	for (WCHAR *NameIdx = heapNames.m_pData;
	        NameIdx[0] != L'\0';
	        NameIdx += wcslen(NameIdx) + 1) {
		VolumePaths.push_back(CStringW(NameIdx));
	}

	return S_OK;
}

```

###known issue.
```\\localpc\share\dir\subdir\tatoeba.bmp``` を開いた HANDLE を食わせてやると、本家では ```\\?\UNC\localpc\share\dir\subdir\tatoeba.bmp``` を返してくるけれど、この関数では ```\\C:\test\sharedir\localpc\share\dir\subdir\tatoeba.bmp``` を返してくる。

###follow link.
[C++ - GetFinalPathNameByHandleW が 2000 でも使いたい](http://blog.livedoor.jp/blackwingcat/archives/1917229.html)