我有一个用C++编写的DLL,封装了FindFirstFile /AA> /AA>提供文件搜索功能:

std::vector<std::wstring> ELFindFilesInFolder(std::wstring folder, std::wstring fileMask = TEXT(""), bool fullPath = false);

此函数返回一个FindNextFile,其中包含给定文件夹中与给定文件掩码匹配的文件名列表。到目前为止还不错;功能按预期工作。
不过,我需要围绕这个库编写一个C包装器,因为FindClose。这会导致头痛不止。
我最初以为我会设置一个函数来接收二维std::vector数组,修改它以包含文件名列表,然后返回它:
bool ELFindFilesInFolder(const wchar_t* folderPath, const wchar_t* fileMask, const bool fullPath, wchar_t* filesBuffer[], size_t* filesBufferSize);

然而,这被证明是个坏主意,因为wchar_t。我想我可以强制调用方创建第二个维度MAX_PATH(这样函数将接收一个文件名缓冲区的可变长度列表,每个缓冲区都MAX_PATH长),但这对我来说似乎很混乱。
我考虑了Windows api风格的包装:
bool ELFindNextFileInFolder(const wchar_t* folderPath, const wchar_t* fileMask, const bool fullPath, wchar_t* fileBuffer, size_t* fileBufferSize, HANDLE* searchToken);

这将执行搜索,返回找到的第一个文件名,并保存FindFirstFile提供的搜索句柄。以后对ELFindNextFileInFolder的调用将提供此搜索句柄,这样就可以很容易地找到上一个关闭的调用:FindNextFile将只获得保存的搜索句柄。但是,这样的句柄需要通过FindClose关闭,C似乎没有智能指针的C++概念,所以我不能保证searchToken将被关闭。当FindNextFile表示没有更多结果时,我可以自己关闭一些句柄,但是如果调用方在该点之前放弃搜索,则会有一个浮动句柄保持打开状态。我非常希望我的图书馆行为端正,不要到处乱放垃圾桶,所以这件事已经结束了。我也不希望提供ELCloseSearchHandle函数,因为我不确定是否可以信任调用方正确使用它。
有没有一种很好的、最好是单一函数的方法来包装这些windowsapi,或者我只需要从一个不完美的解决方案列表中选择一个?

最佳答案

像这样的怎么样?
DLL module中:

#include <windows.h>
#include <vector>
#include <unordered_map>

unsigned int global_file_count; //just a counter..
std::unordered_map<unsigned int, std::vector<std::wstring>> global_file_holder; //holds vectors of strings for us.


/** Example file finder C++ code (not exported) **/
std::vector<std::wstring> Find_Files(std::wstring FileName)
{
    std::vector<std::wstring> Result;
    WIN32_FIND_DATAW hFound = {0};
    HANDLE hFile = FindFirstFileW(FileName.c_str(), &hFound);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        do
        {
            Result.emplace_back(hFound.cFileName);
        } while(FindNextFileW(hFile, &hFound));
    }
    FindClose(hFile);
    return Result;
}


/** C Export **/
extern "C" __declspec(dllexport) unsigned int GetFindFiles(const wchar_t* FileName)
{
    global_file_holder.insert(std::make_pair(++global_file_count, Find_Files(FileName)));
    return global_file_count;
}

/** C Export **/
extern "C" __declspec(dllexport) int RemoveFindFiles(unsigned int handle)
{
    auto it = global_file_holder.find(handle);
    if (it != global_file_holder.end())
    {
        global_file_holder.erase(it);
        return 1;
    }
    return 0;
}

/** C Export **/
extern "C" __declspec(dllexport) const wchar_t* File_Get(unsigned int handle, unsigned int index, unsigned int* len)
{
    auto& ref = global_file_holder.find(handle)->second;

    if (ref.size() > index)
    {
        *len = ref[index].size();
        return ref[index].c_str();
    }

    *len = 0;
    return nullptr;
}

/** C Export (really crappy lol.. maybe clear and reset is better) **/
extern "C" __declspec(dllexport) void File_ResetReferenceCount()
{
    global_file_count = 0;
    //global_file_holder.clear();
}

extern "C" __declspec(dllexport) bool __stdcall DllMain(HINSTANCE hinstDLL, DWORD fdwReason, void* lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hinstDLL);
            break;

        case DLL_PROCESS_DETACH:
            break;

        case DLL_THREAD_ATTACH:
            break;

        case DLL_THREAD_DETACH:
            break;
    }
    return true;
}

然后在C code中,您可以像这样使用它:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

int main()
{
    HMODULE module = LoadLibrary("CModule.dll");
    if (module)
    {
        unsigned int (__cdecl *GetFindFiles)(const wchar_t* FileName) = (void*)GetProcAddress(module, "GetFindFiles");
        int (__cdecl *RemoveFindFiles)(unsigned int handle) = (void*)GetProcAddress(module, "RemoveFindFiles");
        const wchar_t* (__cdecl *File_Get)(unsigned int handle, unsigned int index, unsigned int* len) = (void*)GetProcAddress(module, "File_Get");
        void (__cdecl *File_ResetReferenceCount)() = (void*)GetProcAddress(module, "File_ResetReferenceCount");


        unsigned int index = 0, len = 0;
        const wchar_t* file_name = NULL;
        unsigned int handle = GetFindFiles(L"C:/Modules/*.dll"); //not an actual handle!

        while((file_name = File_Get(handle, index++, &len)) != NULL)
        {
            if (len)
            {
                wprintf(L"%s\n", file_name);
            }
        }

        RemoveFindFiles(handle); //Optional..
        File_ResetReferenceCount(); //Optional..

        /** The above two functions marked optional only need to be called
            if you used FindFiles a LOT! Why? Because you'd be having a ton
            of vectors not in use. Not calling it has no "leaks" or "bad side-effects".
            Over time it may. (example is having 500+ (large) vectors of large strings) **/

        FreeLibrary(module);
    }

    return 0;
}

老实说,这看起来有点脏,但我真的不知道有什么“惊人”的方法来做这件事。我就是这样做的。。大部分工作是在C++侧完成的,你不必担心泄露。即使导出一个函数来清除map也很好。。
最好将C导出添加到模板类中,然后导出其中的每一个。。这将使它可以用于大多数C++容器。我想。。

09-25 22:19