Bootstrap

逐级创建目录(WIndowsAPI:SHCreateDirectoryEx函数和SHCreateDirectory函数)

一、SHCreateDirectoryEx 函数(支持安全属性)

SHCreateDirectoryEx 函数是 Windows API 中用于创建目录的函数。它允许开发者创建一个指定路径的目录,如果该目录已经存在,则不会进行任何操作。

函数原型

HRESULT SHCreateDirectoryEx(
  HWND hwnd,
  PCWSTR pszPath,
  LPSECURITY_ATTRIBUTES psa
);

参数说明

  • hwnd:

    • 类型:HWND
    • 描述:一个窗口句柄,通常用于显示任何可能的错误消息。如果不需要显示错误消息,可以传递 NULL
  • pszPath:

    • 类型:PCWSTR
    • 描述:指向要创建的目录的路径的指针。该路径可以是相对路径或绝对路径。
  • psa:

    • 类型:LPSECURITY_ATTRIBUTES
    • 描述:指向 SECURITY_ATTRIBUTES 结构的指针,该结构定义了新目录的安全描述符。如果不需要自定义安全性,可以传递 NULL

返回值

  • 如果函数成功,返回 S_OK
  • 如果函数失败,返回相应的 HRESULT 错误代码。

使用示例

以下是一个简单的示例,展示如何使用 SHCreateDirectoryEx 逐级创建目录:

#include <windows.h>
#include <shlobj.h>
#include <iostream>

int main() 
{
    // 要创建的目录路径
    PCWSTR path = L"C:\\Example\\1\\2\\3";

    // 调用 SHCreateDirectoryEx
    HRESULT result = SHCreateDirectoryEx(NULL, path, NULL);
    
    if (SUCCEEDED(result)) 
    {
        std::wcout << L"目录创建成功: " << path << std::endl;
    } 
    else 
    {
        std::wcerr << L"目录创建失败,错误代码: " << result << std::endl;
    }

    return 0;
}

注意事项

  1. 错误处理: 在使用时务必检查返回值,以确保目录创建成功。
  2. Unicode 支持: SHCreateDirectoryEx 是支持 Unicode 的,因此建议使用宽字符字符串(如示例中所示)。
  3. 权限问题: 创建目录可能会受到用户权限的限制。确保程序有足够的权限以创建指定位置的目录。
  4. 包含头文件和库: 使用该函数时,需要包含 <windows.h><shlobj.h> 头文件,并在编译时链接到 Shell32.lib

二、SHCreateDirectory 函数(不支持安全属性)

SHCreateDirectory 函数是 Windows API 用于创建一个新目录的函数。与 SHCreateDirectoryEx 相似,但 SHCreateDirectory 不支持安全属性参数,适合用于简单的目录创建任务。

函数原型

HRESULT SHCreateDirectory(
  HWND hwnd,
  PCWSTR pszPath
);

参数说明

  • hwnd:

    • 类型:HWND
    • 描述:一个窗口句柄,用于显示可能的错误消息。如果不需要显示错误消息,可以传递 NULL
  • pszPath:

    • 类型:PCWSTR
    • 描述:指向要创建的目录路径的指针。该路径可以是相对路径或绝对路径。

返回值

  • 如果函数成功,返回 S_OK
  • 如果函数失败,返回相应的 HRESULT 错误代码,可以使用 HRESULT_FROM_WIN32(GetLastError()) 来获取更多的错误信息。

使用示例

以下是一个简单的示例,展示如何使用 SHCreateDirectory 逐级创建目录:

#include <windows.h>
#include <shlobj.h>
#include <iostream>

int main() 
{
    // 要创建的目录路径
    PCWSTR path = L"C:\\Example\\1\\2\\3";

    // 调用 SHCreateDirectory
    HRESULT result = SHCreateDirectory(NULL, path);
    
    if (SUCCEEDED(result))
    {
        std::wcout << L"目录创建成功: " << path << std::endl;
    } 
    else 
    {
        std::wcerr << L"目录创建失败,错误代码: " << HRESULT_CODE(result) << std::endl;
    }

    return 0;
}

注意事项

  1. 错误处理: 确保检查返回值,以确保目录创建成功,并可以根据返回的 HRESULT 错误代码执行适当的错误处理。
  2. Unicode 支持: SHCreateDirectory 是支持 Unicode 的,因此建议使用宽字符字符串(如示例中所示)。
  3. 权限问题: 创建目录可能会受到用户权限限制,因此确保程序有足够的权限来创建指定位置的目录。
  4. 包含头文件和库: 使用该函数时,需要包含 <windows.h><shlobj.h> 头文件,并在编译时链接到 Shell32.lib

三、仿照上述API实现 CreateDirectoryRecursively 函数


#include <windows.h>
#include <iostream>
#include <string>


bool DirectoryExists(const std::string& path)
{
    DWORD attributes = GetFileAttributes(path.c_str());
    return (attributes != INVALID_FILE_ATTRIBUTES && (attributes & FILE_ATTRIBUTE_DIRECTORY));
}


bool CreateDirectoryRecursively(const std::string& path, DWORD attributes = 0)
{
    // 检查目录是否已经存在
    //if (DirectoryExists(path))
    //{
    //    return true; // 目录已存在
    //}

    DWORD attr = GetFileAttributes(path.c_str());
    bool result = (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY));
    if (result)
    {
        return true; // 目录已存在
    }

    // 分割路径以获取逐级目录
    std::string currentPath;
    size_t pos = 0;

    while ((pos = path.find_first_of("\\/", pos)) != std::string::npos)
    {
        currentPath = path.substr(0, pos);

        // 创建当前逐级目录
        if (!currentPath.empty())
        {
            if (CreateDirectory(currentPath.c_str(), NULL) ||
                GetLastError() == ERROR_ALREADY_EXISTS)
            {
                // 如果成功或目录已存在,则继续
            }
            else
            {
                // 如果创建失败,输出错误信息并返回 false
                std::cerr << "创建目录失败: " << currentPath << ", 错误代码: " << GetLastError() << std::endl;
                return false;
            }
        }

        pos++; // 移动到下一个分隔符
    }

    // 最后创建目标目录,并应用属性
    if (!CreateDirectory(path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
    {
        std::cerr << "创建目录失败: " << path << ", 错误代码: " << GetLastError() << std::endl;
        return false;
    }

    // 应用目录属性(如果需要)
    if (attributes != 0)
    {
        SetFileAttributes(path.c_str(), attributes);
    }

    return true;
}

int main()
{
    std::string path = "C:\\Users\\Admin\\Desktop\\Example\\SubDirectory1\\SubDirectory2";
    DWORD attributes = FILE_ATTRIBUTE_NORMAL; // 设定想要的属性

    if (CreateDirectoryRecursively(path, attributes))
    {
        std::cout << "目录创建成功: " << path << std::endl;
    }
    else
    {
        std::cerr << "目录创建失败: " << path << std::endl;
    }

    std::cin.get();
    return 0;
}

;