我有一个用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++容器。我想。。