<p>Windows file system is case insensitive. How, given a file/folder name (e.g. "somefile"), I get the <em>actual</em> name of that file/folder (e.g. it should return "SomeFile" if Explorer displays it so)?</p>

<p>Some ways I know, all of which seem quite backwards:</p>

<ol>
<li>Given the full path, search for each folder on the path (via FindFirstFile). This gives proper cased results of each folder. At the last step, search for the file itself.</li>
<li>Get filename from handle (as in <a href="http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx" rel="noreferrer">MSDN example</a>). This requires opening a file, creating file mapping, getting it's name, parsing device names etc. Pretty convoluted. And it does not work for folders or zero-size files.</li>
</ol>

<p>Am I missing some obvious WinAPI call? The simplest ones, like GetActualPathName() or GetFullPathName() return the name using casing that was passed in (e.g. returns "program files" if that was passed in, even if it should be "Program Files").</p>

<p>I'm looking for a native solution (not .NET one).</p>

[{'answer_id': 74563, 'author': 'bugmagnet', 'author_id': 426, 'author_profile': 'https://Stackoverflow.com/users/426', 'pm_score': 2, 'selected': False, 'text': '<p>Okay, this is VBScript, but even so I\'d suggest using the Scripting.FileSystemObject object</p>\n\n<pre><code>Dim fso\nSet fso = CreateObject("Scripting.FileSystemObject")\nDim f\nSet f = fso.GetFile("C:\\testfile.dat") \'actually named "testFILE.dAt"\nwscript.echo f.Name\n</code></pre>\n\n<p>The response I get is from this snippet is </p>\n\n<pre><code>testFILE.dAt\n</code></pre>\n\n<p>Hope that at least points you in the right direction.</p>\n'}, {'answer_id': 74588, 'author': 'cspirz', 'author_id': 8352, 'author_profile': 'https://Stackoverflow.com/users/8352', 'pm_score': 2, 'selected': False, 'text': '<p>Have you tried using <a href="http://msdn.microsoft.com/en-us/library/bb762179(VS.85).aspx" rel="nofollow noreferrer">SHGetFileInfo?</a></p>\n'}, {'answer_id': 74589, 'author': 'Adam Rosenfield', 'author_id': 9530, 'author_profile': 'https://Stackoverflow.com/users/9530', 'pm_score': -1, 'selected': False, 'text': '<p>After a quick test, <a href="http://msdn.microsoft.com/en-us/library/aa364980(VS.85).aspx" rel="nofollow noreferrer">GetLongPathName()</a> does what you want.</p>\n'}, {'answer_id': 81493, 'author': 'NeARAZ', 'author_id': 6799, 'author_profile': 'https://Stackoverflow.com/users/6799', 'pm_score': 4, 'selected': True, 'text': '<p>And hereby I answer my own question, based on <a href="https://stackoverflow.com/questions/74451/getting-actual-file-name-with-proper-casing-on-windows#74588">original answer from <em>cspirz</em></a>.</p>\n\n<p>Here\'s a function that given absolute, relative or network path, will return the path with upper/lower case as it would be displayed on Windows. If some component of the path does not exist, it will return the passed in path from that point.</p>\n\n<p>It is quite involved because it tries to handle network paths and other edge cases. It operates on wide character strings and uses std::wstring. Yes, in theory Unicode TCHAR could be not the same as wchar_t; that is an exercise for the reader :)</p>\n\n<pre><code>std::wstring GetActualPathName( const wchar_t* path )\n{\n    // This is quite involved, but the meat is SHGetFileInfo\n\n    const wchar_t kSeparator = L\'\\\\\';\n\n    // copy input string because we\'ll be temporary modifying it in place\n    size_t length = wcslen(path);\n    wchar_t buffer[MAX_PATH];\n    memcpy( buffer, path, (length+1) * sizeof(path[0]) );\n\n    size_t i = 0;\n\n    std::wstring result;\n\n    // for network paths (\\\\server\\share\\RestOfPath), getting the display\n    // name mangles it into unusable form (e.g. "\\\\server\\share" turns\n    // into "share on server (server)"). So detect this case and just skip\n    // up to two path components\n    if( length &gt;= 2 &amp;&amp; buffer[0] == kSeparator &amp;&amp; buffer[1] == kSeparator )\n    {\n        int skippedCount = 0;\n        i = 2; // start after \'\\\\\'\n        while( i &lt; length &amp;&amp; skippedCount &lt; 2 )\n        {\n            if( buffer[i] == kSeparator )\n                ++skippedCount;\n            ++i;\n        }\n\n        result.append( buffer, i );\n    }\n    // for drive names, just add it uppercased\n    else if( length &gt;= 2 &amp;&amp; buffer[1] == L\':\' )\n    {\n        result += towupper(buffer[0]);\n        result += L\':\';\n        if( length &gt;= 3 &amp;&amp; buffer[2] == kSeparator )\n        {\n            result += kSeparator;\n            i = 3; // start after drive, colon and separator\n        }\n        else\n        {\n            i = 2; // start after drive and colon\n        }\n    }\n\n    size_t lastComponentStart = i;\n    bool addSeparator = false;\n\n    while( i &lt; length )\n    {\n        // skip until path separator\n        while( i &lt; length &amp;&amp; buffer[i] != kSeparator )\n            ++i;\n\n        if( addSeparator )\n            result += kSeparator;\n\n        // if we found path separator, get real filename of this\n        // last path name component\n        bool foundSeparator = (i &lt; length);\n        buffer[i] = 0;\n        SHFILEINFOW info;\n\n        // nuke the path separator so that we get real name of current path component\n        info.szDisplayName[0] = 0;\n        if( SHGetFileInfoW( buffer, 0, &amp;info, sizeof(info), SHGFI_DISPLAYNAME ) )\n        {\n            result += info.szDisplayName;\n        }\n        else\n        {\n            // most likely file does not exist.\n            // So just append original path name component.\n            result.append( buffer + lastComponentStart, i - lastComponentStart );\n        }\n\n        // restore path separator that we might have nuked before\n        if( foundSeparator )\n            buffer[i] = kSeparator;\n\n        ++i;\n        lastComponentStart = i;\n        addSeparator = true;\n    }\n\n    return result;\n}\n</code></pre>\n\n<p>Again, thanks to cspirz for pointing me to SHGetFileInfo.</p>\n'}, {'answer_id': 5452625, 'author': 'sergioko', 'author_id': 679366, 'author_profile': 'https://Stackoverflow.com/users/679366', 'pm_score': 2, 'selected': False, 'text': '<p>There is another solution. First call GetShortPathName() and then GetLongPathName(). Guess what character case will be used then? ;-)</p>\n'}, {'answer_id': 32892823, 'author': 'Doub', 'author_id': 67432, 'author_profile': 'https://Stackoverflow.com/users/67432', 'pm_score': 0, 'selected': False, 'text': '<p><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa364421(v=vs.85).aspx" rel="nofollow">FindFirstFileNameW</a> will work with a few drawbacks:</p>\n\n<ul>\n<li>it doesn\'t work on UNC paths</li>\n<li>it strips the drive letter so you need to add it back</li>\n<li>if there are more than one hard link to your file you need to identify the right one</li>\n</ul>\n'}, {'answer_id': 47353320, 'author': 'raymai97', 'author_id': 1261956, 'author_profile': 'https://Stackoverflow.com/users/1261956', 'pm_score': 1, 'selected': False, 'text': '<p>Just found that the <code>Scripting.FileSystemObject</code> suggested by @bugmagnet 10 years ago is a treasure. Unlike my old method, it works on Absolute Path, Relative Path, UNC Path and Very Long Path (path longer than <code>MAX_PATH</code>). Shame on me for not testing his method earlier.</p>\n\n<p>For future reference, I would like to present this code which can be compiled in both C and C++ mode. In C++ mode, the code will use STL and ATL. In C mode, you can clearly see how everything is working behind the scene.</p>\n\n<pre><code>#include &lt;Windows.h&gt;\n#include &lt;objbase.h&gt;\n#include &lt;conio.h&gt; // for _getch()\n\n#ifndef __cplusplus\n#   include &lt;stdio.h&gt;\n\n#define SafeFree(p, fn) \\\n    if (p) { fn(p); (p) = NULL; }\n\n#define SafeFreeCOM(p) \\\n    if (p) { (p)-&gt;lpVtbl-&gt;Release(p); (p) = NULL; }\n\n\nstatic HRESULT CorrectPathCasing2(\n    LPCWSTR const pszSrc, LPWSTR *ppszDst)\n{\n    DWORD const clsCtx = CLSCTX_INPROC_SERVER;\n    LCID const lcid = LOCALE_USER_DEFAULT;\n    LPCWSTR const pszProgId = L"Scripting.FileSystemObject";\n    LPCWSTR const pszMethod = L"GetAbsolutePathName";\n    HRESULT hr = 0;\n    CLSID clsid = { 0 };\n    IDispatch *pDisp = NULL;\n    DISPID dispid = 0;\n    VARIANT vtSrc = { VT_BSTR };\n    VARIANT vtDst = { VT_BSTR };\n    DISPPARAMS params = { 0 };\n    SIZE_T cbDst = 0;\n    LPWSTR pszDst = NULL;\n\n    // CoCreateInstance&lt;IDispatch&gt;(pszProgId, &amp;pDisp)\n\n    hr = CLSIDFromProgID(pszProgId, &amp;clsid);\n    if (FAILED(hr)) goto eof;\n\n    hr = CoCreateInstance(&amp;clsid, NULL, clsCtx,\n        &amp;IID_IDispatch, (void**)&amp;pDisp);\n    if (FAILED(hr)) goto eof;\n    if (!pDisp) {\n        hr = E_UNEXPECTED; goto eof;\n    }\n\n    // Variant&lt;BSTR&gt; vtSrc(pszSrc), vtDst;\n    // vtDst = pDisp-&gt;InvokeMethod( pDisp-&gt;GetIDOfName(pszMethod), vtSrc );\n\n    hr = pDisp-&gt;lpVtbl-&gt;GetIDsOfNames(pDisp, NULL,\n        (LPOLESTR*)&amp;pszMethod, 1, lcid, &amp;dispid);\n    if (FAILED(hr)) goto eof;\n\n    vtSrc.bstrVal = SysAllocString(pszSrc);\n    if (!vtSrc.bstrVal) {\n        hr = E_OUTOFMEMORY; goto eof;\n    }\n    params.rgvarg = &amp;vtSrc;\n    params.cArgs = 1;\n    hr = pDisp-&gt;lpVtbl-&gt;Invoke(pDisp, dispid, NULL, lcid,\n        DISPATCH_METHOD, &amp;params, &amp;vtDst, NULL, NULL);\n    if (FAILED(hr)) goto eof;\n    if (!vtDst.bstrVal) {\n        hr = E_UNEXPECTED; goto eof;\n    }\n\n    // *ppszDst = AllocWStrCopyBStrFrom(vtDst.bstrVal);\n\n    cbDst = SysStringByteLen(vtDst.bstrVal);\n    pszDst = HeapAlloc(GetProcessHeap(),\n        HEAP_ZERO_MEMORY, cbDst + sizeof(WCHAR));\n    if (!pszDst) {\n        hr = E_OUTOFMEMORY; goto eof;\n    }\n    CopyMemory(pszDst, vtDst.bstrVal, cbDst);\n    *ppszDst = pszDst;\n\neof:\n    SafeFree(vtDst.bstrVal, SysFreeString);\n    SafeFree(vtSrc.bstrVal, SysFreeString);\n    SafeFreeCOM(pDisp);\n    return hr;\n}\n\nstatic void Cout(char const *psz)\n{\n    printf("%s", psz);\n}\n\nstatic void CoutErr(HRESULT hr)\n{\n    printf("Error HRESULT 0x%.8X!\\n", hr);\n}\n\nstatic void Test(LPCWSTR pszPath)\n{\n    LPWSTR pszRet = NULL;\n    HRESULT hr = CorrectPathCasing2(pszPath, &amp;pszRet);\n    if (FAILED(hr)) {\n        wprintf(L"Input: &lt;%s&gt;\\n", pszPath);\n        CoutErr(hr);\n    }\n    else {\n        wprintf(L"Was: &lt;%s&gt;\\nNow: &lt;%s&gt;\\n", pszPath, pszRet);\n        HeapFree(GetProcessHeap(), 0, pszRet);\n    }\n}\n\n\n#else // Use C++ STL and ATL\n#   include &lt;iostream&gt;\n#   include &lt;iomanip&gt;\n#   include &lt;string&gt;\n#   include &lt;atlbase.h&gt;\n\nstatic HRESULT CorrectPathCasing2(\n    std::wstring const &amp;srcPath,\n    std::wstring &amp;dstPath)\n{\n    HRESULT hr = 0;\n    CComPtr&lt;IDispatch&gt; disp;\n    hr = disp.CoCreateInstance(L"Scripting.FileSystemObject");\n    if (FAILED(hr)) return hr;\n\n    CComVariant src(srcPath.c_str()), dst;\n    hr = disp.Invoke1(L"GetAbsolutePathName", &amp;src, &amp;dst);\n    if (FAILED(hr)) return hr;\n\n    SIZE_T cch = SysStringLen(dst.bstrVal);\n    dstPath = std::wstring(dst.bstrVal, cch);\n    return hr;\n}\n\nstatic void Cout(char const *psz)\n{\n    std::cout &lt;&lt; psz;\n}\n\nstatic void CoutErr(HRESULT hr)\n{\n    std::wcout\n        &lt;&lt; std::hex &lt;&lt; std::setfill(L\'0\') &lt;&lt; std::setw(8)\n        &lt;&lt; "Error HRESULT 0x" &lt;&lt; hr &lt;&lt; "\\n";\n}\n\nstatic void Test(std::wstring const &amp;path)\n{\n    std::wstring output;\n    HRESULT hr = CorrectPathCasing2(path, output);\n    if (FAILED(hr)) {\n        std::wcout &lt;&lt; L"Input: &lt;" &lt;&lt; path &lt;&lt; "&gt;\\n";\n        CoutErr(hr);\n    }\n    else {\n        std::wcout &lt;&lt; L"Was: &lt;" &lt;&lt; path &lt;&lt; "&gt;\\n"\n            &lt;&lt; "Now: &lt;" &lt;&lt; output &lt;&lt; "&gt;\\n";\n    }\n}\n\n#endif\n\n\nstatic void TestRoutine(void)\n{\n    HRESULT hr = CoInitialize(NULL);\n\n    if (FAILED(hr)) {\n        Cout("CoInitialize failed!\\n");\n        CoutErr(hr);\n        return;\n    }\n\n    Cout("\\n[ Absolute Path ]\\n");\n    Test(L"c:\\\\uSers\\\\RayMai\\\\docuMENTs");\n    Test(L"C:\\\\WINDOWS\\\\SYSTEM32");\n\n    Cout("\\n[ Relative Path ]\\n");\n    Test(L".");\n    Test(L"..");\n    Test(L"\\\\");\n\n    Cout("\\n[ UNC Path ]\\n");\n    Test(L"\\\\\\\\VMWARE-HOST\\\\SHARED FOLDERS\\\\D\\\\PROGRAMS INSTALLER");\n\n    Cout("\\n[ Very Long Path ]\\n");\n    Test(L"\\\\\\\\?\\\\C:\\\\VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\\\"\n        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME");\n\n    Cout("\\n!! Worth Nothing Behavior !!\\n");\n    Test(L"");\n    Test(L"1234notexist");\n    Test(L"C:\\\\bad\\\\PATH");\n\n    CoUninitialize();\n}\n\nint main(void)\n{\n    TestRoutine();\n    _getch();\n    return 0;\n}\n</code></pre>\n\n<p>Screenshot:</p>\n\n<p><a href="https://i.stack.imgur.com/goDre.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/goDre.png" alt="screenshot2"></a></p>\n\n<hr>\n\n<p>Old Answer:</p>\n\n<p>I found that <code>FindFirstFile()</code> will return the proper casing file name (last part of path) in <code>fd.cFileName</code>.  If we pass <code>c:\\winDOWs\\exPLORER.exe</code> as first parameter to <code>FindFirstFile()</code>, the <code>fd.cFileName</code> would be <code>explorer.exe</code> like this:</p>\n\n<p><a href="https://i.stack.imgur.com/3ihE5.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/3ihE5.png" alt="prove"></a></p>\n\n<p>If we replace the last part of path with <code>fd.cFileName</code>, we will get the last part right; the path would become <code>c:\\winDOWs\\explorer.exe</code>.</p>\n\n<p>Assuming the path is always absolute path (no change in text length), we can just apply this \'algorithm\' to every part of path (except the drive letter part). </p>\n\n<p>Talk is cheap, here is the code:</p>\n\n<pre><code>#include &lt;windows.h&gt;\n#include &lt;stdio.h&gt;\n\n/*\n    c:\\windows\\windowsupdate.log --&gt; c:\\windows\\WindowsUpdate.log\n*/\nstatic HRESULT MyProcessLastPart(LPTSTR szPath)\n{\n    HRESULT hr = 0;\n    HANDLE hFind = NULL;\n    WIN32_FIND_DATA fd = {0};\n    TCHAR *p = NULL, *q = NULL;\n    /* thePart = GetCorrectCasingFileName(thePath); */\n    hFind = FindFirstFile(szPath, &amp;fd);\n    if (hFind == INVALID_HANDLE_VALUE) {\n        hr = HRESULT_FROM_WIN32(GetLastError());\n        hFind = NULL; goto eof;\n    }\n    /* thePath = thePath.ReplaceLast(thePart); */\n    for (p = szPath; *p; ++p);\n    for (q = fd.cFileName; *q; ++q, --p);\n    for (q = fd.cFileName; *p = *q; ++p, ++q);\neof:\n    if (hFind) { FindClose(hFind); }\n    return hr;\n}\n\n/*\n    Important! \'szPath\' should be absolute path only.\n    MUST NOT SPECIFY relative path or UNC or short file name.\n*/\nEXTERN_C\nHRESULT __stdcall\nCorrectPathCasing(\n    LPTSTR szPath)\n{\n    HRESULT hr = 0;\n    TCHAR *p = NULL;\n    if (GetFileAttributes(szPath) == -1) {\n        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;\n    }\n    for (p = szPath; *p; ++p)\n    {\n        if (*p == \'\\\\\' || *p == \'/\')\n        {\n            TCHAR slashChar = *p;\n            if (p[-1] == \':\') /* p[-2] is drive letter */\n            {\n                p[-2] = toupper(p[-2]);\n                continue;\n            }\n            *p = \'\\0\';\n            hr = MyProcessLastPart(szPath);\n            *p = slashChar;\n            if (FAILED(hr)) goto eof;\n        }\n    }\n    hr = MyProcessLastPart(szPath);\neof:\n    return hr;\n}\n\nint main()\n{\n    TCHAR szPath[] = TEXT("c:\\\\windows\\\\EXPLORER.exe");\n    HRESULT hr = CorrectPathCasing(szPath);\n    if (SUCCEEDED(hr))\n    {\n        MessageBox(NULL, szPath, TEXT("Test"), MB_ICONINFORMATION);\n    }\n    return 0;\n}\n</code></pre>\n\n<p><a href="https://i.stack.imgur.com/vYAcH.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/vYAcH.png" alt="prove 2"></a></p>\n\n<p>Advantages:</p>\n\n<ul>\n<li>The code works on every version of Windows since Windows 95.</li>\n<li>Basic error-handling.</li>\n<li>Highest performance possible. <code>FindFirstFile()</code> is very fast, direct buffer manipulation makes it even faster.</li>\n<li>Just C and pure WinAPI. Small executable size.</li>\n</ul>\n\n<p>Disadvantages:</p>\n\n<ul>\n<li>Only absolute path is supported, other are undefined behavior.</li>\n<li>Not sure if it is relying on undocumented behavior.</li>\n<li>The code might be too raw too much DIY for some people. Might get you flamed.</li>\n</ul>\n\n<p>Reason behind the code style:</p>\n\n<p>I use <code>goto</code> for error-handling because I was used to it (<code>goto</code> is very handy for error-handling in C). I use <code>for</code> loop to perform functions like <code>strcpy</code> and <code>strchr</code> on-the-fly because I want to be certain what was actually executed.</p>\n'}]
['https://Stackoverflow.com/questions/74451', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/6799/']