Windows文件系统不区分大小写。 如果给定一个文件/文件夹名称(例如“somefile”),我得到该文件/文件夹的实际名称(例如,如果Explorer显示它,它应该返回“SomeFile”)?

我知道的一些方法,所有这些看起来都很倒退:

  1. 给定完整路径,搜索路径上的每个文件夹(通过FindFirstFile)。 这给出了每个文件夹的正确结果。 在最后一步,搜索文件本身。
  2. 从句柄获取文件名(如MSDN示例中所示 )。 这需要打开文件,创建文件映射,获取它的名称,解析设备名称等。相当复杂。 它不适用于文件夹或零大小的文件。

我错过了一些明显的WinAPI电话吗? 最简单的一个,比如GetActualPathName()或GetFullPathName()使用传入的大小写返回名称(例如,如果传入的话,返回“程序文件”,即使它应该是“Program Files”)。

我正在寻找原生解决方案(不是.NET)。

===============>>#1 票数:5 已采纳

在此我根据cspirz的原始答案回答我自己的问题。

这是一个给定绝对路径,相对路径或网络路径的函数,它将返回大小写的路径,就像在Windows上显示的那样。 如果路径的某个组件不存在,它将从该点返回传入的路径。

它非常复杂,因为它试图处理网络路径和其他边缘情况。 它在宽字符串上运行,并使用std :: wstring。 是的,理论上Unicode TCHAR可能与wchar_t不同; 这是读者的练习:)

std::wstring GetActualPathName( const wchar_t* path )
{
    // This is quite involved, but the meat is SHGetFileInfo

    const wchar_t kSeparator = L'\\';

    // copy input string because we'll be temporary modifying it in place
    size_t length = wcslen(path);
    wchar_t buffer[MAX_PATH];
    memcpy( buffer, path, (length+1) * sizeof(path[0]) );

    size_t i = 0;

    std::wstring result;

    // for network paths (\\server\share\RestOfPath), getting the display
    // name mangles it into unusable form (e.g. "\\server\share" turns
    // into "share on server (server)"). So detect this case and just skip
    // up to two path components
    if( length >= 2 && buffer[0] == kSeparator && buffer[1] == kSeparator )
    {
        int skippedCount = 0;
        i = 2; // start after '\\'
        while( i < length && skippedCount < 2 )
        {
            if( buffer[i] == kSeparator )
                ++skippedCount;
            ++i;
        }

        result.append( buffer, i );
    }
    // for drive names, just add it uppercased
    else if( length >= 2 && buffer[1] == L':' )
    {
        result += towupper(buffer[0]);
        result += L':';
        if( length >= 3 && buffer[2] == kSeparator )
        {
            result += kSeparator;
            i = 3; // start after drive, colon and separator
        }
        else
        {
            i = 2; // start after drive and colon
        }
    }

    size_t lastComponentStart = i;
    bool addSeparator = false;

    while( i < length )
    {
        // skip until path separator
        while( i < length && buffer[i] != kSeparator )
            ++i;

        if( addSeparator )
            result += kSeparator;

        // if we found path separator, get real filename of this
        // last path name component
        bool foundSeparator = (i < length);
        buffer[i] = 0;
        SHFILEINFOW info;

        // nuke the path separator so that we get real name of current path component
        info.szDisplayName[0] = 0;
        if( SHGetFileInfoW( buffer, 0, &info, sizeof(info), SHGFI_DISPLAYNAME ) )
        {
            result += info.szDisplayName;
        }
        else
        {
            // most likely file does not exist.
            // So just append original path name component.
            result.append( buffer + lastComponentStart, i - lastComponentStart );
        }

        // restore path separator that we might have nuked before
        if( foundSeparator )
            buffer[i] = kSeparator;

        ++i;
        lastComponentStart = i;
        addSeparator = true;
    }

    return result;
}

再次,感谢cspirz指向SHGetFileInfo。

===============>>#2 票数:4

你尝试过使用SHGetFileInfo吗?

===============>>#3 票数:3

还有另一种解决方案。 首先调用GetShortPathName()然后调用GetLongPathName()。 猜猜那将使用什么字符案例? ;-)

===============>>#4 票数:2

好的,这是VBScript,但即便如此,我建议使用Scripting.FileSystemObject对象

Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")
Dim f
Set f = fso.GetFile("C:\testfile.dat") 'actually named "testFILE.dAt"
wscript.echo f.Name

我得到的答案来自这个片段

testFILE.dAt

希望至少能指出你正确的方向。

===============>>#5 票数:1

刚刚发现10年前@bugmagnet建议的Scripting.FileSystemObject是一个宝藏。 与我的旧方法不同,它适用于绝对路径,相对路径,UNC路径和超长路径(路径长于MAX_PATH )。 因为我之前没有测试他的方法而感到羞耻。

为了将来参考,我想提出这个可以在C和C ++模式下编译的代码。 在C ++模式下,代码将使用STL和ATL。 在C模式下,您可以清楚地看到一切在幕后工作的情况。

#include <Windows.h>
#include <objbase.h>
#include <conio.h> // for _getch()

#ifndef __cplusplus
#   include <stdio.h>

#define SafeFree(p, fn) \
    if (p) { fn(p); (p) = NULL; }

#define SafeFreeCOM(p) \
    if (p) { (p)->lpVtbl->Release(p); (p) = NULL; }


static HRESULT CorrectPathCasing2(
    LPCWSTR const pszSrc, LPWSTR *ppszDst)
{
    DWORD const clsCtx = CLSCTX_INPROC_SERVER;
    LCID const lcid = LOCALE_USER_DEFAULT;
    LPCWSTR const pszProgId = L"Scripting.FileSystemObject";
    LPCWSTR const pszMethod = L"GetAbsolutePathName";
    HRESULT hr = 0;
    CLSID clsid = { 0 };
    IDispatch *pDisp = NULL;
    DISPID dispid = 0;
    VARIANT vtSrc = { VT_BSTR };
    VARIANT vtDst = { VT_BSTR };
    DISPPARAMS params = { 0 };
    SIZE_T cbDst = 0;
    LPWSTR pszDst = NULL;

    // CoCreateInstance<IDispatch>(pszProgId, &pDisp)

    hr = CLSIDFromProgID(pszProgId, &clsid);
    if (FAILED(hr)) goto eof;

    hr = CoCreateInstance(&clsid, NULL, clsCtx,
        &IID_IDispatch, (void**)&pDisp);
    if (FAILED(hr)) goto eof;
    if (!pDisp) {
        hr = E_UNEXPECTED; goto eof;
    }

    // Variant<BSTR> vtSrc(pszSrc), vtDst;
    // vtDst = pDisp->InvokeMethod( pDisp->GetIDOfName(pszMethod), vtSrc );

    hr = pDisp->lpVtbl->GetIDsOfNames(pDisp, NULL,
        (LPOLESTR*)&pszMethod, 1, lcid, &dispid);
    if (FAILED(hr)) goto eof;

    vtSrc.bstrVal = SysAllocString(pszSrc);
    if (!vtSrc.bstrVal) {
        hr = E_OUTOFMEMORY; goto eof;
    }
    params.rgvarg = &vtSrc;
    params.cArgs = 1;
    hr = pDisp->lpVtbl->Invoke(pDisp, dispid, NULL, lcid,
        DISPATCH_METHOD, &params, &vtDst, NULL, NULL);
    if (FAILED(hr)) goto eof;
    if (!vtDst.bstrVal) {
        hr = E_UNEXPECTED; goto eof;
    }

    // *ppszDst = AllocWStrCopyBStrFrom(vtDst.bstrVal);

    cbDst = SysStringByteLen(vtDst.bstrVal);
    pszDst = HeapAlloc(GetProcessHeap(),
        HEAP_ZERO_MEMORY, cbDst + sizeof(WCHAR));
    if (!pszDst) {
        hr = E_OUTOFMEMORY; goto eof;
    }
    CopyMemory(pszDst, vtDst.bstrVal, cbDst);
    *ppszDst = pszDst;

eof:
    SafeFree(vtDst.bstrVal, SysFreeString);
    SafeFree(vtSrc.bstrVal, SysFreeString);
    SafeFreeCOM(pDisp);
    return hr;
}

static void Cout(char const *psz)
{
    printf("%s", psz);
}

static void CoutErr(HRESULT hr)
{
    printf("Error HRESULT 0x%.8X!\n", hr);
}

static void Test(LPCWSTR pszPath)
{
    LPWSTR pszRet = NULL;
    HRESULT hr = CorrectPathCasing2(pszPath, &pszRet);
    if (FAILED(hr)) {
        wprintf(L"Input: <%s>\n", pszPath);
        CoutErr(hr);
    }
    else {
        wprintf(L"Was: <%s>\nNow: <%s>\n", pszPath, pszRet);
        HeapFree(GetProcessHeap(), 0, pszRet);
    }
}


#else // Use C++ STL and ATL
#   include <iostream>
#   include <iomanip>
#   include <string>
#   include <atlbase.h>

static HRESULT CorrectPathCasing2(
    std::wstring const &srcPath,
    std::wstring &dstPath)
{
    HRESULT hr = 0;
    CComPtr<IDispatch> disp;
    hr = disp.CoCreateInstance(L"Scripting.FileSystemObject");
    if (FAILED(hr)) return hr;

    CComVariant src(srcPath.c_str()), dst;
    hr = disp.Invoke1(L"GetAbsolutePathName", &src, &dst);
    if (FAILED(hr)) return hr;

    SIZE_T cch = SysStringLen(dst.bstrVal);
    dstPath = std::wstring(dst.bstrVal, cch);
    return hr;
}

static void Cout(char const *psz)
{
    std::cout << psz;
}

static void CoutErr(HRESULT hr)
{
    std::wcout
        << std::hex << std::setfill(L'0') << std::setw(8)
        << "Error HRESULT 0x" << hr << "\n";
}

static void Test(std::wstring const &path)
{
    std::wstring output;
    HRESULT hr = CorrectPathCasing2(path, output);
    if (FAILED(hr)) {
        std::wcout << L"Input: <" << path << ">\n";
        CoutErr(hr);
    }
    else {
        std::wcout << L"Was: <" << path << ">\n"
            << "Now: <" << output << ">\n";
    }
}

#endif


static void TestRoutine(void)
{
    HRESULT hr = CoInitialize(NULL);

    if (FAILED(hr)) {
        Cout("CoInitialize failed!\n");
        CoutErr(hr);
        return;
    }

    Cout("\n[ Absolute Path ]\n");
    Test(L"c:\\uSers\\RayMai\\docuMENTs");
    Test(L"C:\\WINDOWS\\SYSTEM32");

    Cout("\n[ Relative Path ]\n");
    Test(L".");
    Test(L"..");
    Test(L"\\");

    Cout("\n[ UNC Path ]\n");
    Test(L"\\\\VMWARE-HOST\\SHARED FOLDERS\\D\\PROGRAMS INSTALLER");

    Cout("\n[ Very Long Path ]\n");
    Test(L"\\\\?\\C:\\VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME\\"
        L"VERYVERYVERYLOOOOOOOONGFOLDERNAME");

    Cout("\n!! Worth Nothing Behavior !!\n");
    Test(L"");
    Test(L"1234notexist");
    Test(L"C:\\bad\\PATH");

    CoUninitialize();
}

int main(void)
{
    TestRoutine();
    _getch();
    return 0;
}

截图:

screenshot2


旧答案:

我发现FindFirstFile()将在fd.cFileName返回正确的大小写文件名(路径的最后一部分)。 如果我们将c:\\winDOWs\\exPLORER.exe作为第一个参数传递给FindFirstFile() ,则fd.cFileName将是explorer.exe如下所示:

证明

如果我们用fd.cFileName替换路径的最后一部分,我们将得到最后一部分; 路径将变为c:\\winDOWs\\explorer.exe

假设路径始终是绝对路径(文本长度没有变化),我们可以将此“算法”应用于路径的每个部分(驱动器号部分除外)。

谈话很便宜,这里是代码:

#include <windows.h>
#include <stdio.h>

/*
    c:\windows\windowsupdate.log --> c:\windows\WindowsUpdate.log
*/
static HRESULT MyProcessLastPart(LPTSTR szPath)
{
    HRESULT hr = 0;
    HANDLE hFind = NULL;
    WIN32_FIND_DATA fd = {0};
    TCHAR *p = NULL, *q = NULL;
    /* thePart = GetCorrectCasingFileName(thePath); */
    hFind = FindFirstFile(szPath, &fd);
    if (hFind == INVALID_HANDLE_VALUE) {
        hr = HRESULT_FROM_WIN32(GetLastError());
        hFind = NULL; goto eof;
    }
    /* thePath = thePath.ReplaceLast(thePart); */
    for (p = szPath; *p; ++p);
    for (q = fd.cFileName; *q; ++q, --p);
    for (q = fd.cFileName; *p = *q; ++p, ++q);
eof:
    if (hFind) { FindClose(hFind); }
    return hr;
}

/*
    Important! 'szPath' should be absolute path only.
    MUST NOT SPECIFY relative path or UNC or short file name.
*/
EXTERN_C
HRESULT __stdcall
CorrectPathCasing(
    LPTSTR szPath)
{
    HRESULT hr = 0;
    TCHAR *p = NULL;
    if (GetFileAttributes(szPath) == -1) {
        hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
    }
    for (p = szPath; *p; ++p)
    {
        if (*p == '\\' || *p == '/')
        {
            TCHAR slashChar = *p;
            if (p[-1] == ':') /* p[-2] is drive letter */
            {
                p[-2] = toupper(p[-2]);
                continue;
            }
            *p = '\0';
            hr = MyProcessLastPart(szPath);
            *p = slashChar;
            if (FAILED(hr)) goto eof;
        }
    }
    hr = MyProcessLastPart(szPath);
eof:
    return hr;
}

int main()
{
    TCHAR szPath[] = TEXT("c:\\windows\\EXPLORER.exe");
    HRESULT hr = CorrectPathCasing(szPath);
    if (SUCCEEDED(hr))
    {
        MessageBox(NULL, szPath, TEXT("Test"), MB_ICONINFORMATION);
    }
    return 0;
}

证明2

好处:

  • 从Windows 95开始,该代码适用于Windows的每个版本。
  • 基本的错误处理。
  • 最高性能。 FindFirstFile()非常快,直接缓冲操作使其更快。
  • 只是C和纯WinAPI。 可执行文件小。

缺点:

  • 仅支持绝对路径,其他是未定义的行为。
  • 不确定它是否依赖于未记录的行为。
  • 对于某些人来说,代码可能太过于原始DIY。 可能会让你受到伤害。

代码风格背后的原因:

我使用goto进行错误处理,因为我习惯了它( goto非常方便C中的错误处理)。 我使用for循环来执行strcpystrchr等函数,因为我想确定实际执行的是什么。

===============>>#6 票数:0

FindFirstFileNameW将有一些缺点:

  • 它不适用于UNC路径
  • 它会删除驱动器号,因此您需要将其添加回来
  • 如果您的文件有多个硬链接,则需要确定正确的链接

===============>>#7 票数:-3

经过快速测试后, GetLongPathName()可以满足您的需求。

  ask by NeARAZ translate from so

未解决问题?本站智能推荐:

1回复

Windows文件名 - 如何检查文件名是否有效?

我想要一个函数,可能在路径函数中 ,它将检查文件名是否有效。 如果有效,我的意思是如果字符串中存在的字符都是有效的(例如,没有? , >等)。 但遗憾的是,没有任何功能。 通过网络浏览,我发现了一些技术,我不喜欢或者发现它们都很稳固。 使用正则表达式检查文件名的内容。
1回复

通过文件名映射到内存中获取文件名

假设有以下代码- 给定要映射的地址,是否可以使用c ++提取“ some_dll”? 尝试使用windbg调试此可执行文件时,似乎也无法很好地提取模块
1回复

是否有WinAPI可以从命令行获取带有可选空格和其他参数的文件名?

我的目标是从命令行调用/字符串获取文件名。 例如,如果我在输入中包含以下字符串: API应该分别返回以下内容: 因此,我尝试使用splitpath函数,尽管它适用于非常简单的文件路径,但是在上面的两个示例中它完全失败了。 我知道我可以编写自己的解析器(因此请不要提供该解析
2回复

Windows FindFirstFile / FindNextFile API是否有不搜索短文件名的替代方法?

我正在使用Windows API调用FindFirstFile和FindNextFile来搜索与目录中某个通配符字符串匹配的文件。 例如,我可能想在目录中找到所有XML文件,因此我使用模式“ * .xml”进行搜索。 我遇到的问题是,如果我正在搜索一个三字母扩展名,并且文件的扩展名以该三
4回复

获取Windows上的分区列表

目标 我正在将文件系统移植到Windows,并且正在为mounter可执行文件编写更像Windows的界面。 此过程的一部分是让用户找到分区并选择驱动器号。 最终,分区的选择必须导致我可以使用CreateFile() , open() , fopen()或类似的东西打开。 信息
4回复

如何使用C ++获取有关Windows可执行文件(.exe)的信息

我必须创建一个软件,它将扫描几个目录并提取有关所找到的可执行文件的信息。 我需要做两件事: 确定给定文件是否是可执行文件( .exe , .dll等) - 检查扩展名可能不够好。 获取有关此可执行文件的信息( 公司名称 , 产品名称等)。 我之前从未这样做过,因此
1回复

Windows API是否在路径包含符号链接的情况下获取文件属性?

我用了_stat64(const char *path, struct __stat64 *buffer); 用于获取文件/目录统计信息的API,但是如果输入路径包含符号链接,则此API将失败。 如果文件路径包含符号链接,则GetFileAttributes()和GetFileAttri
1回复

如何在Windows 7上获取gethostid行为?

我正在将使用<unistd.h> (特别是gethostid函数)的Linux代码移植到Windows 7。 我需要在应用程序中使用的唯一32位数字。 如何以与在Linux上相同的方式使用Windows 7中的API获取唯一编号? 谢谢。
2回复

如何将文本附加到Windows中的文件?

每次调用此函数时,旧文本数据都会丢失? 告诉我如何维护以前的数据并附加新数据。 此函数被调用10次:
1回复

如何使用Windows API确定注册表值数据的大小?

RegQueryValueEx()基本上要求您对保存注册表值所需的缓冲区大小进行大胆的猜测。 如果您将其弄乱了,则麻烦会得到ERROR_MORE_DATA 。 很明显,您可以循环调用该函数并每次都增加缓冲区大小,直到您幸运为止,但是这种“解决方案”让我感到恶心。 我是否错过了正确执行操作的