删除包含Sqlite的目录时,64位Windows 7递归目录删除失败

删除包含Sqlite的目录时,64位Windows 7递归目录删除失败,sqlite,fileapi,Sqlite,Fileapi,我有一个测试用例(更大应用程序的剥离版本),当试图删除目录或文件时,它会在Windows7机器上偶尔失败。它使用的是本机Windows API。 测试执行以下步骤-- 创建目录 在步骤#1创建的目录中创建sqlite3 db 在数据库中创建一个表 关闭数据库 如果存在任何数据库日志文件,请将其删除 删除数据库文件 删除目录 若您跳过步骤#1,那个么测试将正常运行。 若您在步骤4和步骤5之间添加延迟,那个么测试将正常运行 #include <windows.h> #include &l

我有一个测试用例(更大应用程序的剥离版本),当试图删除目录或文件时,它会在Windows7机器上偶尔失败。它使用的是本机Windows API。 测试执行以下步骤--

  • 创建目录
  • 在步骤#1创建的目录中创建sqlite3 db
  • 在数据库中创建一个表
  • 关闭数据库
  • 如果存在任何数据库日志文件,请将其删除
  • 删除数据库文件
  • 删除目录
  • 若您跳过步骤#1,那个么测试将正常运行。 若您在步骤4和步骤5之间添加延迟,那个么测试将正常运行

    #include <windows.h>
    #include <string>
    #include <iostream>
    #include <stdio.h>
    #include <direct.h>
    #include <sqlite3.h>
    #include <io.h>
    using namespace std;
    
    bool sleep_for_sometime = false;
    bool create_table = false;
    bool runTest()
    {
        char cwdpath[1024] = {'\0'};
        if (_getcwd(cwdpath, 1023) == NULL) return false;
        string testpath(cwdpath);
        testpath += "/test_dir";
        _mkdir(testpath.c_str());
        string dbpath = testpath + "/test.db";
        sqlite3 *db = NULL;
        if (sqlite3_open(dbpath.c_str(), &db) != SQLITE_OK) return false;
        // Create table.
        if (create_table) {
            string sql = "CREATE TABLE COMPANY(" \
                      "ID INT PRIMARY KEY  NOT NULL," \
                      "NAME           TEXT NOT NULL," \
                      "AGE            INT  NOT NULL)";
            char *zErrMsg = 0;
            if (sqlite3_exec(db, sql.c_str(), NULL, 0, &zErrMsg) != SQLITE_OK) {
                sqlite3_close(db);
                cerr << "Could not create table: " << zErrMsg << endl;
                return false;
            }
        }
        sqlite3_close(db);
        if (sleep_for_sometime) {
            Sleep(100);
        }
        string journal_file = dbpath + "-journal";
        string journal_error = journal_file + " failed";
        if (_access(journal_file.c_str(), 06) == 0) {
            if (_unlink(journal_file.c_str()) != 0) {
                perror(journal_error.c_str());
                return false;
            }
            cout << "journal file --" << journal_file << endl;
        }
        string db_error = dbpath + " failed";
        if (_unlink(dbpath.c_str()) != 0) {
           perror(db_error.c_str());
           return false;
        }
        string dir_error = testpath + " failed";
        if (_rmdir(testpath.c_str()) != 0) {
           perror(dir_error.c_str());
           return false;
        }
        return true;
    }
    int main(int argc, char **argv)
    {
        cout << "Usage: ./projtest 1 1" << endl;
        cout << "------If you pass two parameter, then always create table and sleep for some time." << endl;
        cout << "Usage: ./projtest 1" << endl;
        cout << "------If you pass one parameter, then always create table, but don't sleep." << endl;
        cout << "Usage: ./projtest" << endl;
        cout << "------If you don't pass any parameter, then don't create table and don't sleep." << endl;
        if (argc == 3) {
          sleep_for_sometime = true;
          create_table = true;
        } else if (argc == 2) {
          create_table = true;
          sleep_for_sometime = false;
        } else {
          create_table = false;
          sleep_for_sometime = false;
        }
        for (int i = 0; i < 500 ; i++) {
            if (! runTest()) {
                cerr << "Err in runTest trial --" << i+1 << endl;
                return 1;
            }
        }
        return 0;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    使用名称空间std;
    bool sleep_for_time=假;
    bool create_table=false;
    bool运行测试()
    {
    char cwdpath[1024]={'\0'};
    if(_getcwd(cwdpath,1023)==NULL)返回false;
    字符串测试路径(cwdpath);
    testpath+=“/test_dir”;
    _mkdir(testpath.c_str());
    字符串dbpath=testpath+“/test.db”;
    sqlite3*db=NULL;
    if(sqlite3_open(dbpath.c_str(),&db)!=SQLITE_OK)返回false;
    //创建表。
    if(创建_表){
    string sql=“创建表公司(”\
    ID INT主键不为空\
    名称文本不为空\
    “年龄整数不为空)”;
    char*zErrMsg=0;
    if(sqlite3_exec(db,sql.c_str(),NULL,0和zErrMsg)!=SQLITE_OK){
    sqlite3_关闭(db);
    
    cerr可能是活动的filelock sqllite在第4步和第5步之间强加了未及时清除的权限。 记住,与硬盘相比,内存中的代码以闪存速度运行。 硬盘上的SQLLite进程指令可能仍在忙着将数据写入文件,在日志中输入条目,然后释放文件处理程序,而您的代码已经在忙着将其删除

    因此,在SQLLite完成并处理的同时,硬盘仍在记录数据和清除锁的工作列表

    也许您可以实现一个while循环,检查文件是否被锁定,然后耐心地等待释放锁,或者标记文件以备将来删除,并每50毫秒重新检查一次

    编辑 补充说明

    问题是这里有两个问题。硬盘速度和CPU速度。在正常程序代码中,当您将写入指令推送到硬盘时,它会被放入驱动程序/驱动器本身的队列中进行写入,并且驱动程序/硬盘会逐个关闭que(ish)

    程序将收到一条消息,表明操作已完成,以便继续执行。为什么程序必须等待一两毫秒才能继续正确运行?因为硬盘驱动器速度慢,并且有自己的读写优先级。它可以这样做,但不完全是现在导致它正在读一个文件或者什么的


    因此,虽然文件可以自由编辑的写入操作仍在要写入的队列中,但它已经发送了一个删除请求,但该文件尚未标记为可写,因为该操作仍在队列中。错误来自于此。

    此代码是单线程的,那么为什么要关闭sqlite3_()在完成所有操作之前返回。其次,。表示同步已满(2),SQLite数据库引擎将使用VFS的xSync方法,以确保在继续之前将所有内容安全地写入磁盘表面。因此,在所有内容都写入磁盘之前,控件不应返回。我补充了一点说明。可能是文件本身已完全写入,但原理相同不要应用于已应用的文件锁。我在以下情况中也观察到类似问题。
    DeleteFile('xyz');fopen('xyz','w'))
    。此处文件打开失败,权限被拒绝。它并不总是可复制的,但很多时候是可复制的。问题与此处基本相同。最好将硬盘驱动器指令和程序视为单独的进程。当程序向驱动器进程发出删除指令时,驱动器进程将添加到queue,完成作业,然后锁定文件,将文件标记为已删除的FS注册表,然后清除锁定。如果在应用删除锁定后过早尝试打开文件,则权限将被拒绝。这是一件好事。您应该无法写入标记为删除的文件。请使用时间戳名称来解决此问题