如何计算 GetModuleFileName 的完整缓冲区大小?

发布于 2024-07-18 11:24:15 字数 399 浏览 10 评论 0原文

GetModuleFileName() 需要一个缓冲区和缓冲区大小作为输入; 但是它的返回值只能告诉我们已经复制了多少个字符,以及大小是否不够(ERROR_INSUFFICIENT_BUFFER)。

如何确定保存 GetModuleFileName() 的整个文件名所需的实际缓冲区大小?

大多数人使用 MAX_PATH ,但我记得路径可以超过该值(默认定义为 260)...

(使用零作为缓冲区大小的技巧对此 API 不起作用 - 我已经尝试过前)

The GetModuleFileName() takes a buffer and size of buffer as input; however its return value can only tell us how many characters is has copied, and if the size is not enough (ERROR_INSUFFICIENT_BUFFER).

How do I determine the real required buffer size to hold entire file name for GetModuleFileName()?

Most people use MAX_PATH but I remember the path can exceed that (260 by default definition)...

(The trick of using zero as size of buffer does not work for this API - I've already tried before)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

蓝色星空 2024-07-25 11:24:15

通常的方法是调用它将大小设置为零,并且保证失败并提供分配足够缓冲区所需的大小。 分配一个缓冲区(不要忘记空终止的空间)并再次调用它。

在很多情况下,MAX_PATH 就足够了,因为许多文件系统限制路径名的总长度。 但是,可以构造超出 MAX_PATH 的合法且有用的文件名,因此查询所需的缓冲区可能是个好建议。

不要忘记最终从提供缓冲区的分配器返回缓冲区。

编辑:弗朗西斯在评论中指出,通常的方法不适用于GetModuleFileName()。 不幸的是,弗朗西斯在这一点上是绝对正确的,我唯一的借口是我在提供“通常”的解决方案之前没有去查证。

我不知道该 API 的作者在想什么,只是有可能在引入它时,MAX_PATH 确实是最大的可能路径,使正确的配方变得容易。 只需在长度不小于 MAX_PATH 字符的缓冲区中执行所有文件名操作即可。

哦,是的,不要忘记自 1995 年左右开始路径名就允许使用 Unicode 字符。 由于 Unicode 占用更多空间,因此任何路径名都可以在 \\?\ 前面,以显式请求删除对该名称的字节长度的 MAX_PATH 限制。 这使问题变得复杂。

MSDN 在标题为 文件名、路径的文章中对路径长度有这样的说法和命名空间

最大路径长度

在 Windows API 中(有一些
下面讨论的例外情况
段落),最大长度
路径为MAX_PATH,定义为
260 个字符。 本地路径是
按以下顺序构造:
驱动器号、冒号、反斜杠、
用反斜杠分隔的组件,
和一个终止空字符。 为了
例如,驱动器 D 上的最大路径
是“D:\<一些 256 个字符的路径
string>
”,其中“”表示
不可见的终止 null
当前系统的字符
代码页。 (使用字符 < >
此处是为了视觉清晰度,不能
有效路径字符串的一部分。)

注意文件I/O函数
Windows API 将“/”转换为“\”作为一部分
将名称转换为 NT 样式
名称,除非使用“\\?\
前缀如下详述
部分。

Windows API有很多功能
也有 Unicode 版本
允许延长路径
最大总路径长度为 32,767
人物。 这种类型的路径是
由分隔的组件组成
反斜杠,每个反斜杠直到值
返回于
lpMaximumComponentLength 参数
GetVolumeInformation 函数。 到
指定扩展长度路径,使用
\\?\”前缀。 例如,
\\?\D:\<非常长的路径>”。 (这
字符 < > 在这里用于
视觉清晰度,不能成为
有效的路径字符串。)

注意最大路径32767
字符是近似的,因为
\\?\”前缀可以扩展为
系统在运行时生成更长的字符串
时间,并且这种扩展适用于
总长度。

也可以使用“\\?\”前缀
根据构建的路径
通用命名约定 (UNC)。
要使用 UNC 指定此类路径,请使用
\\?\UNC\”前缀。 例如,
\\?\UNC\server\share”,其中“服务器”
是机器的名称和“共享”
是共享文件夹的名称。
这些前缀不用作
路径本身。 他们表明
该路径应该传递给
系统只需进行最小程度的修改,
这意味着你不能使用
正斜杠代表路径
分隔符或句点来表示
当前目录。 还有,你
不能使用“\\?\”前缀
相对路径,因此相对
路径限制为 MAX_PATH
如前所述的字符
不使用“\\?\”前缀的路径。

当使用 API 创建
目录,指定路径不能
太长以至于你无法附加
8.3 文件名(即目录名不能超过MAX_PATH负12)。

shell 和文件系统有
不同的要求。 有可能的
使用 Windows API 创建路径
shell 用户界面可能
无法处理。

因此,一个简单的答案是分配一个大小为 MAX_PATH 的缓冲区,检索名称并检查错误。 如果合适,就完成了。 否则,如果它以“\\?\”开头,则获得大小为 64KB 左右的缓冲区(上面的短语“32,767 个字符的最大路径是近似值”在这里有点麻烦,所以我留下一些细节以供进一步研究)并重试。

溢出 MAX_PATH 但不以“\\?\”开头似乎是“不可能发生”的情况。 同样,接下来要做什么是您必须处理的细节。

对于以“\\Server\Share\”开头的网络名称的路径长度限制也可能存在一些混淆,更不用说以“<”开头的内核对象名称空间中的名称了。代码>\\.\”。 上面的文章没有说,我也不确定这个API是否可以返回这样的路径。

The usual recipe is to call it setting the size to zero and it is guaranteed to fail and provide the size needed to allocate sufficient buffer. Allocate a buffer (don't forget room for nul-termination) and call it a second time.

In a lot of cases MAX_PATH is sufficient because many of the file systems restrict the total length of a path name. However, it is possible to construct legal and useful file names that exceed MAX_PATH, so it is probably good advice to query for the required buffer.

Don't forget to eventually return the buffer from the allocator that provided it.

Edit: Francis points out in a comment that the usual recipe doesn't work for GetModuleFileName(). Unfortunately, Francis is absolutely right on that point, and my only excuse is that I didn't go look it up to verify before providing a "usual" solution.

I don't know what the author of that API was thinking, except that it is possible that when it was introduced, MAX_PATH really was the largest possible path, making the correct recipe easy. Simply do all file name manipulation in a buffer of length no less than MAX_PATH characters.

Oh, yeah, don't forget that path names since 1995 or so allow Unicode characters. Because Unicode takes more room, any path name can be preceeded by \\?\ to explicitly request that the MAX_PATH restriction on its byte length be dropped for that name. This complicates the question.

MSDN has this to say about path length in the article titled File Names, Paths, and Namespaces:

Maximum Path Length

In the Windows API (with some
exceptions discussed in the following
paragraphs), the maximum length for a
path is MAX_PATH, which is defined as
260 characters. A local path is
structured in the following order:
drive letter, colon, backslash,
components separated by backslashes,
and a terminating null character. For
example, the maximum path on drive D
is "D:\<some 256 character path
string><NUL>
" where "<NUL>" represents
the invisible terminating null
character for the current system
codepage. (The characters < > are used
here for visual clarity and cannot be
part of a valid path string.)

Note File I/O functions in the
Windows API convert "/" to "\" as part
of converting the name to an NT-style
name, except when using the "\\?\"
prefix as detailed in the following
sections.

The Windows API has many functions
that also have Unicode versions to
permit an extended-length path for a
maximum total path length of 32,767
characters. This type of path is
composed of components separated by
backslashes, each up to the value
returned in the
lpMaximumComponentLength parameter of
the GetVolumeInformation function. To
specify an extended-length path, use
the "\\?\" prefix. For example,
"\\?\D:\<very long path>". (The
characters < > are used here for
visual clarity and cannot be part of a
valid path string.)

Note The maximum path of 32,767
characters is approximate, because the
"\\?\" prefix may be expanded to a
longer string by the system at run
time, and this expansion applies to
the total length.

The "\\?\" prefix can also be used
with paths constructed according to
the universal naming convention (UNC).
To specify such a path using UNC, use
the "\\?\UNC\" prefix. For example,
"\\?\UNC\server\share", where "server"
is the name of the machine and "share"
is the name of the shared folder.
These prefixes are not used as part of
the path itself. They indicate that
the path should be passed to the
system with minimal modification,
which means that you cannot use
forward slashes to represent path
separators, or a period to represent
the current directory. Also, you
cannot use the "\\?\" prefix with a
relative path, therefore relative
paths are limited to MAX_PATH
characters as previously stated for
paths not using the "\\?\" prefix.

When using an API to create a
directory, the specified path cannot
be so long that you cannot append an
8.3 file name (that is, the directory name cannot exceed MAX_PATH minus 12).

The shell and the file system have
different requirements. It is possible
to create a path with the Windows API
that the shell user interface might
not be able to handle.

So an easy answer would be to allocate a buffer of size MAX_PATH, retrieve the name and check for errors. If it fit, you are done. Otherwise, if it begins with "\\?\", get a buffer of size 64KB or so (the phrase "maximum path of 32,767 characters is approximate" above is a tad troubling here so I'm leaving some details for further study) and try again.

Overflowing MAX_PATH but not beginning with "\\?\" appears to be a "can't happen" case. Again, what to do then is a detail you'll have to deal with.

There may also be some confusion over what the path length limit is for a network name which begins "\\Server\Share\", not to mention names from the kernel object name space which begin with "\\.\". The above article does not say, and I'm not certain about whether this API could return such a path.

樱娆 2024-07-25 11:24:15

实施一些合理的策略来增加缓冲区,例如从 MAX_PATH 开始,然后使每个连续大小比前一个大 1.5 倍(或迭代次数较少时为 2 倍)。 迭代直到函数成功。

Implement some reasonable strategy for growing the buffer like start with MAX_PATH, then make each successive size 1,5 times (or 2 times for less iterations) bigger then the previous one. Iterate until the function succeeds.

等你爱我 2024-07-25 11:24:15

使用

extern char* _pgmptr

可能会起作用。

从 GetModuleFileName 的文档中:

全局变量_pgmptr自动初始化为可执行文件的完整路径,可用于检索可执行文件的完整路径名。

但如果我读到有关 _pgmptr 的内容:

当程序不是从命令行运行时,_pgmptr 可能会初始化为程序名称(不带文件扩展名的文件的基本名称)或文件名、相对路径或完整路径。

有人知道 _pgmptr 是如何初始化的吗? 如果 SO 支持后续问题,我会将此问题作为后续问题发布。

Using

extern char* _pgmptr

might work.

From the documentation of GetModuleFileName:

The global variable _pgmptr is automatically initialized to the full path of the executable file, and can be used to retrieve the full path name of an executable file.

But if I read about _pgmptr:

When a program is not run from the command line, _pgmptr might be initialized to the program name (the file's base name without the file name extension) or to a file name, relative path, or full path.

Anyone who knows how _pgmptr is initialized? If SO had support for follow-up questions I would posted this question as a follow up.

不顾 2024-07-25 11:24:15

虽然 API 是糟糕设计的证据,但解决方案实际上非常简单。 简单但悲伤的是它必须是这种方式,因为它有点消耗性能,因为它可能需要多次内存分配。 以下是解决方案的一些关键点:

  • 您不能真正依赖不同 Windows 版本之间的返回值,因为它在不同 Windows 版本(例如 XP)上可能具有不同的语义。

  • 如果提供的缓冲区太小而无法容纳字符串,则返回值是包含 0 终止符的字符数。

    如果提供的缓冲区

  • 如果提供的缓冲区足够大以容纳字符串,则返回值是不包括 0 终止符的字符数。

这意味着如果返回的值恰好等于缓冲区大小,您仍然不知道它是否成功。 可能还有更多数据。 或不。 最后,只有缓冲区长度实际上大于所需的长度,您才能确定成功。 可悲的是......

所以,解决方案是从一个小的缓冲区开始。 然后,我们调用 GetModuleFileName 传递确切的缓冲区长度(以 TCHAR 为单位)并将返回结果与其进行比较。 如果返回结果小于我们的缓冲区长度,则成功。 如果返回结果大于或等于我们的缓冲区长度,我们必须使用更大的缓冲区重试。 冲洗并重复直至完成。 完成后,我们制作缓冲区的字符串副本(strdup/wcsdup/tcsdup)、清理并返回字符串副本。 该字符串将具有正确的分配大小,而不是临时缓冲区中可能的开销。 请注意,调用者负责释放返回的字符串(strdup/wcsdup/tcsdup malloc 内存)。

请参阅下面的实现和使用代码示例。 我已经使用这个代码十多年了,包括在企业文档管理软件中,其中可能有很多很长的路径。 当然可以通过各种方式优化代码,例如首先将返回的字符串加载到本地缓冲区(TCHAR buf[256])中。 如果该缓冲区太小,您可以启动动态分配循环。 其他优化也是可能的,但这超出了本文的范围。

实现和使用示例:

/* Ensure Win32 API Unicode setting is in sync with CRT Unicode setting */
#if defined(_UNICODE) && !defined(UNICODE)
#   define UNICODE
#elif defined(UNICODE) && !defined(_UNICODE)
#   define _UNICODE
#endif

#include <stdio.h> /* not needed for our function, just for printf */
#include <tchar.h>
#include <windows.h>

LPCTSTR GetMainModulePath(void)
{
    TCHAR* buf    = NULL;
    DWORD  bufLen = 256;
    DWORD  retLen;

    while (32768 >= bufLen)
    {
        if (!(buf = (TCHAR*)malloc(sizeof(TCHAR) * (size_t)bufLen))
        {
            /* Insufficient memory */
            return NULL;
        }

        if (!(retLen = GetModuleFileName(NULL, buf, bufLen)))
        {
            /* GetModuleFileName failed */
            free(buf);
            return NULL;
        }
        else if (bufLen > retLen)
        {
            /* Success */
            LPCTSTR result = _tcsdup(buf); /* Caller should free returned pointer */
            free(buf);
            return result;
        }

        free(buf);
        bufLen <<= 1;
    }

    /* Path too long */
    return NULL;
}

int main(int argc, char* argv[])
{
    LPCTSTR path;

    if (!(path = GetMainModulePath()))
    {
        /* Insufficient memory or path too long */
        return 0;
    }

    _tprintf("%s\n", path);

    free(path); /* GetMainModulePath malloced memory using _tcsdup */ 

    return 0;
}

说了这么多,我想指出您需要非常了解 GetModuleFileName(Ex) 的各种其他注意事项。 32/64 位/WOW64 之间存在不同的问题。 此外,输出不一定是完整的长路径,但很可能是短文件名或受路径别名的影响。 我希望当您使用这样的函数时,目标是为调用者提供可用的、可靠的完整的、长的路径,因此我建议确实确保返回可用的、可靠的、完整的、长的绝对路径,以这样的方式它可以在各种 Windows 版本和体系结构之间移植(同样是 32/64 位/WOW64)。 如何有效地做到这一点超出了本文的范围。

虽然这是现有最糟糕的 Win32 API 之一,但我还是希望您能享受编码带来的乐趣。

While the API is proof of bad design, the solution is actually very simple. Simple, yet sad it has to be this way, for it's somewhat of a performance hog as it might require multiple memory allocations. Here is some keypoints to the solution:

  • You can't really rely on the return value between different Windows-versions as it can have different semantics on different Windows-versions (XP for example).

  • If the supplied buffer is too small to hold the string, the return value is the amount of characters including the 0-terminator.

  • If the supplied buffer is large enough to hold the string, the return value is the amount of characters excluding the 0-terminator.

This means that if the returned value exactly equals the buffer size, you still don't know whether it succeeded or not. There might be more data. Or not. In the end you can only be certain of success if the buffer length is actually greater than required. Sadly...

So, the solution is to start off with a small buffer. We then call GetModuleFileName passing the exact buffer length (in TCHARs) and comparing the return result with it. If the return result is less than our buffer length, it succeeded. If the return result is greater than or equal to our buffer length, we have to try again with a larger buffer. Rinse and repeat until done. When done we make a string copy (strdup/wcsdup/tcsdup) of the buffer, clean up, and return the string copy. This string will have the right allocation size rather than the likely overhead from our temporary buffer. Note that the caller is responsible for freeing the returned string (strdup/wcsdup/tcsdup mallocs memory).

See below for an implementation and usage code example. I have been using this code for over a decade now, including in enterprise document management software where there can be a lot of quite long paths. The code can ofcourse be optimized in various ways, for example by first loading the returned string into a local buffer (TCHAR buf[256]). If that buffer is too small you can then start the dynamic allocation loop. Other optimizations are possible but that's beyond the scope here.

Implementation and usage example:

/* Ensure Win32 API Unicode setting is in sync with CRT Unicode setting */
#if defined(_UNICODE) && !defined(UNICODE)
#   define UNICODE
#elif defined(UNICODE) && !defined(_UNICODE)
#   define _UNICODE
#endif

#include <stdio.h> /* not needed for our function, just for printf */
#include <tchar.h>
#include <windows.h>

LPCTSTR GetMainModulePath(void)
{
    TCHAR* buf    = NULL;
    DWORD  bufLen = 256;
    DWORD  retLen;

    while (32768 >= bufLen)
    {
        if (!(buf = (TCHAR*)malloc(sizeof(TCHAR) * (size_t)bufLen))
        {
            /* Insufficient memory */
            return NULL;
        }

        if (!(retLen = GetModuleFileName(NULL, buf, bufLen)))
        {
            /* GetModuleFileName failed */
            free(buf);
            return NULL;
        }
        else if (bufLen > retLen)
        {
            /* Success */
            LPCTSTR result = _tcsdup(buf); /* Caller should free returned pointer */
            free(buf);
            return result;
        }

        free(buf);
        bufLen <<= 1;
    }

    /* Path too long */
    return NULL;
}

int main(int argc, char* argv[])
{
    LPCTSTR path;

    if (!(path = GetMainModulePath()))
    {
        /* Insufficient memory or path too long */
        return 0;
    }

    _tprintf("%s\n", path);

    free(path); /* GetMainModulePath malloced memory using _tcsdup */ 

    return 0;
}

Having said all that, I like to point out you need to be very aware of various other caveats with GetModuleFileName(Ex). There are varying issues between 32/64-bit/WOW64. Also the output is not necessarily a full, long path, but could very well be a short-filename or be subject to path aliasing. I expect when you use such a function that the goal is to provide the caller with a useable, reliable full, long path, therefor I suggest to indeed ensure to return a useable, reliable, full, long absolute path, in such a way that it is portable between various Windows-versions and architectures (again 32/64-bit/WOW64). How to do that efficiently is beyond the scope here.

While this is one of the worst Win32 APIs in existance, I wish you alot of coding joy nonetheless.

酒废 2024-07-25 11:24:15

我的示例是“如果一开始不成功,则将缓冲区的长度加倍”方法的具体实现。 它使用字符串(实际上是 wstring,因为我希望能够处理 Unicode)作为缓冲区来检索正在运行的可执行文件的路径。 为了确定何时成功检索完整路径,它会根据 wstring::length() 返回的值检查从 GetModuleFileNameW 返回的值,然后使用该值调整大小最终字符串以去除多余的空字符。 如果失败,则返回一个空字符串。

inline std::wstring getPathToExecutableW() 
{
    static const size_t INITIAL_BUFFER_SIZE = MAX_PATH;
    static const size_t MAX_ITERATIONS = 7;
    std::wstring ret;
    DWORD bufferSize = INITIAL_BUFFER_SIZE;
    for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
    {
        ret.resize(bufferSize);
        DWORD charsReturned = GetModuleFileNameW(NULL, &ret[0], bufferSize);
        if (charsReturned < ret.length())
        {
            ret.resize(charsReturned);
            return ret;
        }
        else
        {
            bufferSize *= 2;
        }
    }
    return L"";
}

My example is a concrete implementation of the "if at first you don't succeed, double the length of the buffer" approach. It retrieves the path of the executable that is running, using a string (actually a wstring, since I want to be able to handle Unicode) as the buffer. To determine when it has successfully retrieved the full path, it checks the value returned from GetModuleFileNameW against the value returned by wstring::length(), then uses that value to resize the final string in order to strip the extra null characters. If it fails, it returns an empty string.

inline std::wstring getPathToExecutableW() 
{
    static const size_t INITIAL_BUFFER_SIZE = MAX_PATH;
    static const size_t MAX_ITERATIONS = 7;
    std::wstring ret;
    DWORD bufferSize = INITIAL_BUFFER_SIZE;
    for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
    {
        ret.resize(bufferSize);
        DWORD charsReturned = GetModuleFileNameW(NULL, &ret[0], bufferSize);
        if (charsReturned < ret.length())
        {
            ret.resize(charsReturned);
            return ret;
        }
        else
        {
            bufferSize *= 2;
        }
    }
    return L"";
}
千仐 2024-07-25 11:24:15

这是 std::wstring 的另一个解决方案:

DWORD getCurrentProcessBinaryFile(std::wstring& outPath)
{
    // @see https://msdn.microsoft.com/en-us/magazine/mt238407.aspx
    DWORD dwError  = 0;
    DWORD dwResult = 0;
    DWORD dwSize   = MAX_PATH;

    SetLastError(0);
    while (dwSize <= 32768) {
        outPath.resize(dwSize);

        dwResult = GetModuleFileName(0, &outPath[0], dwSize);
        dwError  = GetLastError();

        /* if function has failed there is nothing we can do */
        if (0 == dwResult) {
            return dwError;
        }

        /* check if buffer was too small and string was truncated */
        if (ERROR_INSUFFICIENT_BUFFER == dwError) {
            dwSize *= 2;
            dwError = 0;

            continue;
        }

        /* finally we received the result string */
        outPath.resize(dwResult);

        return 0;
    }

    return ERROR_BUFFER_OVERFLOW;
}

Here is a another solution with std::wstring:

DWORD getCurrentProcessBinaryFile(std::wstring& outPath)
{
    // @see https://msdn.microsoft.com/en-us/magazine/mt238407.aspx
    DWORD dwError  = 0;
    DWORD dwResult = 0;
    DWORD dwSize   = MAX_PATH;

    SetLastError(0);
    while (dwSize <= 32768) {
        outPath.resize(dwSize);

        dwResult = GetModuleFileName(0, &outPath[0], dwSize);
        dwError  = GetLastError();

        /* if function has failed there is nothing we can do */
        if (0 == dwResult) {
            return dwError;
        }

        /* check if buffer was too small and string was truncated */
        if (ERROR_INSUFFICIENT_BUFFER == dwError) {
            dwSize *= 2;
            dwError = 0;

            continue;
        }

        /* finally we received the result string */
        outPath.resize(dwResult);

        return 0;
    }

    return ERROR_BUFFER_OVERFLOW;
}
长发绾君心 2024-07-25 11:24:15

这是 Free Pascal (FPC)/Delphi 中的一个实现,以防有人需要它:

function GetExecutablePath(): UnicodeString;
const
  MAX_CHARS = 65536;
var
  NumChars, BufSize, CharsCopied: DWORD;
  pName: PWideChar;
begin
  // Poorly designed API...
  result := '';
  NumChars := 256;
  repeat
    BufSize := (NumChars * SizeOf(WideChar)) + SizeOf(WideChar);
    GetMem(pName, BufSize);
    CharsCopied := GetModuleFileNameW(0,  // HMODULE hModule
      pName,                              // LPWSTR  lpFilename
      NumChars);                          // DWORD   nSize
    if (CharsCopied < NumChars) and (CharsCopied <= MAX_CHARS) then
      result := UnicodeString(pName)
    else
      NumChars := NumChars * 2;
    FreeMem(pName, BufSize);
  until (CharsCopied >= MAX_CHARS) or (result <> '');
end;

Here's an implementation in Free Pascal (FPC)/Delphi in case anyone needs it:

function GetExecutablePath(): UnicodeString;
const
  MAX_CHARS = 65536;
var
  NumChars, BufSize, CharsCopied: DWORD;
  pName: PWideChar;
begin
  // Poorly designed API...
  result := '';
  NumChars := 256;
  repeat
    BufSize := (NumChars * SizeOf(WideChar)) + SizeOf(WideChar);
    GetMem(pName, BufSize);
    CharsCopied := GetModuleFileNameW(0,  // HMODULE hModule
      pName,                              // LPWSTR  lpFilename
      NumChars);                          // DWORD   nSize
    if (CharsCopied < NumChars) and (CharsCopied <= MAX_CHARS) then
      result := UnicodeString(pName)
    else
      NumChars := NumChars * 2;
    FreeMem(pName, BufSize);
  until (CharsCopied >= MAX_CHARS) or (result <> '');
end;
苯莒 2024-07-25 11:24:15

Windows 无法正确处理超过 260 个字符的路径,因此只需使用 MAX_PATH。
您无法运行路径长于 MAX_PATH 的程序。

Windows cannot handle properly paths longer than 260 characters, so just use MAX_PATH.
You cannot run a program having path longer than MAX_PATH.

千寻… 2024-07-25 11:24:15

我的方法是使用 argv,假设您只想获取正在运行的程序的文件名。 当您尝试从不同的模块获取文件名时,已经描述了无需任何其他技巧即可实现此目的的唯一安全方法,可以在此处找到实现。

// assume argv is there and a char** array

int        nAllocCharCount = 1024;
int        nBufSize = argv[0][0] ? strlen((char *) argv[0]) : nAllocCharCount;
TCHAR *    pszCompleteFilePath = new TCHAR[nBufSize+1];

nBufSize = GetModuleFileName(NULL, (TCHAR*)pszCompleteFilePath, nBufSize);
if (!argv[0][0])
{
    // resize memory until enough is available
    while (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        delete[] pszCompleteFilePath;
        nBufSize += nAllocCharCount;
        pszCompleteFilePath = new TCHAR[nBufSize+1];
        nBufSize = GetModuleFileName(NULL, (TCHAR*)pszCompleteFilePath, nBufSize);
    }

    TCHAR * pTmp = pszCompleteFilePath;
    pszCompleteFilePath = new TCHAR[nBufSize+1];
    memcpy_s((void*)pszCompleteFilePath, nBufSize*sizeof(TCHAR), pTmp, nBufSize*sizeof(TCHAR));

    delete[] pTmp;
    pTmp = NULL;
}
pszCompleteFilePath[nBufSize] = '\0';

// do work here
// variable 'pszCompleteFilePath' contains always the complete path now

// cleanup
delete[] pszCompleteFilePath;
pszCompleteFilePath = NULL;

我还没有遇到 argv 不包含文件路径(Win32 和 Win32 控制台应用程序)的情况。 但以防万一出现上述解决方案的后备方案。 对我来说似乎有点难看,但仍然完成了工作。

My approach to this is to use argv, assuming you only want to get the filename of the running program. When you try to get the filename from a different module, the only secure way to do this without any other tricks is described already, an implementation can be found here.

// assume argv is there and a char** array

int        nAllocCharCount = 1024;
int        nBufSize = argv[0][0] ? strlen((char *) argv[0]) : nAllocCharCount;
TCHAR *    pszCompleteFilePath = new TCHAR[nBufSize+1];

nBufSize = GetModuleFileName(NULL, (TCHAR*)pszCompleteFilePath, nBufSize);
if (!argv[0][0])
{
    // resize memory until enough is available
    while (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
    {
        delete[] pszCompleteFilePath;
        nBufSize += nAllocCharCount;
        pszCompleteFilePath = new TCHAR[nBufSize+1];
        nBufSize = GetModuleFileName(NULL, (TCHAR*)pszCompleteFilePath, nBufSize);
    }

    TCHAR * pTmp = pszCompleteFilePath;
    pszCompleteFilePath = new TCHAR[nBufSize+1];
    memcpy_s((void*)pszCompleteFilePath, nBufSize*sizeof(TCHAR), pTmp, nBufSize*sizeof(TCHAR));

    delete[] pTmp;
    pTmp = NULL;
}
pszCompleteFilePath[nBufSize] = '\0';

// do work here
// variable 'pszCompleteFilePath' contains always the complete path now

// cleanup
delete[] pszCompleteFilePath;
pszCompleteFilePath = NULL;

I had no case where argv didn't contain the file path (Win32 and Win32-console application), yet. But just in case there is a fallback to a solution that has been described above. Seems a bit ugly to me, but still gets the job done.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文