• 关于 sqlite3.dll 动态加载使用问题


    写pbidea库时,有考虑把 sqlite3 作为一个功能封装进去,静态编译又觉得太大,所以决定把sqlite3.dll 当作插件动态加载使用。

    官网下载的DLL,2个文件,sqlite3.dll 和sqlite3.def。通常是使用lib生成 sqlite3.lib,然后引入使用。这个方法不能动态加载作为插件使用,当sqlite3.dll不存在时,整个pbidea都会报错。所以,必须考虑LoadLibrary后作为插件使用。这就有个难题 :它有好几百个函数,需要自己一个一个去声明,太费劲了。而且不同版本,它函数也有变化。

    下载了sqlite3的源码,研究了一下,发现其实它有个接口

    1. struct sqlite3_api_routines {
    2.   void * (*aggregate_context)(sqlite3_context*,int nBytes);
    3.   int  (*aggregate_count)(sqlite3_context*);
    4.   int  (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
    5.   int  (*bind_double)(sqlite3_stmt*,int,double);
    6.   int  (*bind_int)(sqlite3_stmt*,int,int);
    7.   int  (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
    8.   int  (*bind_null)(sqlite3_stmt*,int);
    9. …………..
    10. static const sqlite3_api_routines sqlite3Apis = {
    11.   sqlite3_aggregate_context,
    12. #ifndef SQLITE_OMIT_DEPRECATED
    13.   sqlite3_aggregate_count,
    14. …………..

    sqlite3Apis是可以直接导出所有函数指针的。那这个接口怎么取得呢?进一步研究,它这个接口是给它的扩展库使用的,具体的sqlite3_load_extension函数里面。sqlite3_load_extension函数的第一个参数是sqlite3 *db。那么问题来了:我还没加载它呢,哪来的sqlite3 *db,这就成了先有鸡还是先有蛋的问题了。看来,走扩展库这条路也行不通。而我又不想几百个函数去折腾声明一遍。

    于是,编译源码,在源码里添加了一个导出函数:

    1. SQLITE_API const sqlite3_api_routines* get_sqlite3_interface()
    2. {
    3. return &sqlite3Apis;
    4. }

    然后然后这个函数,果然可以取得一个接口,需要时LoadLibrary(“sqlite3.dll”),用完后FreeLibrary。挺爽的。

    可是,又遇到一个新问题,不小心用官网下载的DLL覆盖后我编译的DLL后,整个接口消失了。所以,自己编译的DLL不能再命名为sqlite3.dll,需要改名。作为有点小洁癖的人来说,还是不够爽。

    貌似到此处,已经没有办法了!

    看到

    static const sqlite3_api_routines sqlite3Apis = {}

    忽然灵光一现,好象还能想点办法。能不能从DLL里查找到它呢?如果直接查找到它,就能直接取得这个接口,不必使用函数导出。

    说干就干!(这个方法要求你对PE格式有一定了解)

    1. //加载DLL
    2. HMODULE m_dll_sqlite3 = LoadLibraryW(L"sqlite3.dll");
    3. //取PE头信息
    4. IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)m_dll_sqlite3;
    5. IMAGE_NT_HEADERS* pNtHeader = (IMAGE_NT_HEADERS*)((unsigned long)pDosHeader + pDosHeader->e_lfanew);
    6. IMAGE_OPTIONAL_HEADER* pOption = (IMAGE_OPTIONAL_HEADER*)&pNtHeader->OptionalHeader;
    7. DWORD dwImageBase = pOption->ImageBase;
    8. PIMAGE_EXPORT_DIRECTORY pImageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)m_dll_sqlite3 + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
    9. PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)m_dll_sqlite3 + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
    10. DWORD* pAddressOfFunction = (DWORD*)(pImageExportDirectory->AddressOfFunctions + (DWORD)m_dll_sqlite3);
    11. DWORD* pAddressOfNames = (DWORD*)(pImageExportDirectory->AddressOfNames + (DWORD)m_dll_sqlite3);
    12. DWORD dwNumberOfNames = (DWORD)(pImageExportDirectory->NumberOfNames);
    13. WORD* pAddressOfNameOrdinals = (WORD*)(pImageExportDirectory->AddressOfNameOrdinals + (DWORD)m_dll_sqlite3);
    14. //获取所有导出函数
    15. std::mapchar*> funcs;
    16. for (int i = 0; i < (int)dwNumberOfNames; i++)
    17. {
    18. char* strFunction = (char*)(pAddressOfNames[i] + (DWORD)m_dll_sqlite3);
    19. funcs[pAddressOfFunction[i] + dwImageBase] = strFunction;
    20. }
    21. unsigned long dwSectionNumber = pNtHeader->FileHeader.NumberOfSections;
    22. IMAGE_SECTION_HEADER* pSectionHeader = IMAGE_FIRST_SECTION(pNtHeader);
    23. IMAGE_DATA_DIRECTORY* pDataDirectory = pOption->DataDirectory;
    24. for (DWORD i = 0; i < dwSectionNumber; i++)
    25. {
    26. DWORD dwBeginVA = pSectionHeader[i].VirtualAddress + dwImageBase;
    27. DWORD dwEndVA = pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData + dwImageBase;
    28. //static const sqlite3_api_routines* sqlite3_api; 所在 section,想知道为什么它在这里?经验很重要
    29. if (memcmp(pSectionHeader[i].Name, ".rdata", 6) == 0)
    30. {
    31. DWORD* p = (DWORD*)dwBeginVA;
    32. while (p < (DWORD*)dwEndVA)
    33. {
    34. //根据sqlite3_api_routines结构,成员是指向函数的指针,判断是不是 sqlite3_api
    35. bool find = true;
    36. for (int i = 0; i < 32; i++)
    37. {
    38. if (HIWORD(p[i]) && funcs.find(p[i]) == funcs.end())
    39. {
    40. find = false;
    41. break;
    42. }
    43. }
    44. if (find)
    45. {
    46. sqlite3_api = (const sqlite3_api_routines*)p;
    47. break;
    48. }
    49. p++;
    50. }
    51. }
    52. if (sqlite3_api)
    53. break;
    54. }

    代码写好,编译完运行,果然取得了接口指针,完全实现了sqlite3.dll的动态加载使用了。

    爽!爽!爽!

                                  大自在,QQ:781770213

                                      2023/9/19

  • 相关阅读:
    网络系统管理 - GWServer虚拟机配置
    Win11安装最新Android studio时闪退
    科普|一文看懂虚拟人技术原理
    flutter多版本管理fvm使用
    【类、抽象与继承】
    【Python基础】多值参数 || 计算多个数字的和 || 元组和字典的拆包 || 面向过程开发 || 面向对象基本概念:类和对象的关系、大驼峰命名法
    如何通过日志的方式理解Redis主从复制
    数据结构——排序【仅用于考试】
    二叉树的经典OJ题
    CSS鼠标指针表
  • 原文地址:https://blog.csdn.net/lxbin2003/article/details/133033209