【问题标题】:Thread synchronisation on files WIN32 API文件 WIN32 API 上的线程同步
【发布时间】:2017-12-21 23:00:19
【问题描述】:

我正在使用 Win32 API 开发一个客户端-服务器应用程序,并且我正在尝试从 login.txt 文件中删除一条记录,该文件存储了我所有登录的用户。问题是我正在使用的文件没有被删除,临时文件没有被重命名(他们做了 2-3 次,但在几次注销后它失败了)。

我的代码是:

EnterCriticalSection(&CriticalSection2);

    TCHAR tmpString[256];
    FILE *tmp = NULL;

    tmp = fopen("C:\\Users\\Liviu\\Desktop\\temporaryFile.txt", "w");

    if (!tmp)
    {
        printf("Temporary file could not open");
        system("pause");
        return;
    }

    TCHAR searchString[20];

    // Removes the possibility of having the ThreadId = password
    sprintf(searchString, "%s%d", ",", GetCurrentThreadId());

    // Paste all the lines execept the specified record to the tmp file
    while (fgets(tmpString, 255, loginFile))
    {
        if (!strstr(tmpString, searchString))
        {
            fputs(tmpString, tmp);
        }
    }

    fclose(tmp);
    fclose(loginFile);

    // I have put "while" here just to make sure this actually works, but it doesn't
    while (remove("C:\\Users\\Liviu\\Desktop\\login.txt") != 0)
        ;

    while (rename("C:\\Users\\Liviu\\Desktop\\temporaryFile.txt", "C:\\Users\\Liviu\\Desktop\\login.txt") != 0)
        ;

    LeaveCriticalSection(&CriticalSection2);

我怎样才能做到这一点?我认为是关于线程同步的,但我不知道如何修复它。

【问题讨论】:

  • "C:\..\..\in.txt" 是驱动器 c: 根目录上方的两个目录,这看起来不对。
  • 什么是GetLastError()
  • 想知道这是否很简单,例如使用 unicode 文本构建项目,但对 CreateFile() 使用 ansi 文本调用。
  • 您也一直使用不同的选项 - OPEN_EXISTINGCREATE_NEWCREATE_ALWAYS。可能是 \??\c:\in.txt 根本不存在
  • 为什么不改用CopyFile()

标签: windows winapi file-io


【解决方案1】:

您没有显示您传递给CreateFile() 的实际路径,或者GetLastError() 报告的错误代码,因此没有人可以帮助您了解它失败的原因。

但是,即使CreateFile() 工作正常,您将无效参数传递给ReadFile()WriteFile(),因此您的代码仍然无法将数据从一个文件复制到另一个文件。事实上,代码很可能完全崩溃并终止。

试试类似的方法:

int _tmain(int argc, TCHAR *argv[])
{
    int exitCode = 1;

    if (argc < 3)
    {
        _tprintf(_T("not enough arguments!"));
        goto done;
    }

    _tprintf(_T("%s\n%s\n"), argv[1], argv[2]);

    HANDLE hFile1 = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hFile1 == INVALID_HANDLE_VALUE)
    {
        _tprintf(_T("Cant open File 1, Error: %d\n"), GetLastError());
        goto done;
    }

    HANDLE hFile2 = CreateFile(argv[2], GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
    if (hFile2 == INVALID_HANDLE_VALUE)
    {
        _tprintf(_T("Cant open File 2, Error: %d\n"), GetLastError());
        goto cleanup1;
    }

    BYTE a[100];
    BYTE *ptr;
    DWORD bytesRead;
    DWORD bytesWritten;

    do
    {
        if (!ReadFile(hFile1, a, sizeof(a), &bytesRead, NULL))
        {
            _tprintf(_T("Cant read from File 1, Error: %d\n"), GetLastError());
            goto cleanup;
        }

        if (bytesRead == 0)
            break;

        ptr = a;
        do
        {
            if (!WriteFile(hFile2, ptr, bytesRead, &bytesWritten, NULL))
            {
                _tprintf(_T("Cant write to File 2, Error: %d\n"), GetLastError());
                goto cleanup;
            }
            ptr += bytesWritten;
            bytesRead -= bytesWritten;
        }
        while (bytesRead != 0);
    }
    while (true);

    _tprintf(_T("Finished\n"));
    exitCode = 0;

cleanup:
    CloseHandle(hFile2);
cleanup1:
    CloseHandle(hFile1);

done:
    system("pause");
    return exitCode;
}

话虽如此,您的代码基本上只是在复制 CopyFile() 已经做的事情,所以您可以考虑改用它:

int _tmain(int argc, TCHAR *argv[])
{
    int exitCode = 1;

    if (argc < 3)
    {
        _tprintf(_T("not enough arguments!"));
        goto done;
    }

    _tprintf(_T("%s\n%s\n"), argv[1], argv[2]);

    if (!CopyFile(argv[1], argv[2], FALSE))
    {
        _tprintf(_T("Cant copy File, Error: %d\n"), GetLastError());
        goto done;
    }

    _tprintf(_T("Finished\n"));
    exitCode = 0;

done:
    system("pause");
    return exitCode;
}

【讨论】:

  • 感谢您向我展示了一个很好的例子。我已将路径放在 Visual Studio -> 项目属性 -> 命令参数中,如下所示: "C:\Users\Liviu\Desktop\in.txt" "C:\Users\Liviu\Desktop\out.txt" 有没有问题 ? GetLastError() 显示代码 0。
  • @Liviu 这些路径看起来不错。而GetLastError() 仅在这种情况下发生故障时才有意义,并且在这种情况下它不能返回 0,除非你做一些事情来重置它。在我的示例中,情况并非如此。那么你的代码输出到底是什么,从哪里输出呢?
  • 当我运行它时,在屏幕上打印 hFile1 is not ok 0. 创建了一个包含不可读字符的文件并且其中没有内容。
  • @Liviu: GetLastError() 正在返回 0,因为您在其前面调用了 printf(),这可能会将其重置为 0,从而丢失来自 CreateFile() 的错误代码。使用GetLastError() 时,您需要在失败的函数之后立即调用它,然后再调用任何可能重置它的东西。
  • @Liviu:在您最初显示的代码中,如果CreateFile() 失败,您不会立即退出程序。所以你最终试图从一个未打开的输入文件中读取,并且没有任何东西可以写入输出文件。就像我之前说的那样,无论如何您都将无效的参数值传递给ReadFile()WriteFile()(而且您根本没有对它们进行任何错误处理),因此您甚至可能最终将垃圾写入文件 2。假设您的代码不会直接崩溃。我的答案中的代码解决了所有这些问题。你试过我的代码了吗?
【解决方案2】:

如果您的错误是 char * 无法转换为 wchar_t const * 对于 argv 情况,请使用 CreateFileA 而不是 CreateFile,或者切换到使用 int wmain(int, wchar_t * argv[]) 而不是 int main(int, char *[])

【讨论】:

  • OP 出现运行时错误,而不是编译时错误。如果数据类型不匹配,代码甚至无法编译
  • 你不应该这么快就投反对票。我只是尝试将 main 更改为 wmain,嘿,它工作了!在那之前,它只处理传递给主 argv 的第一个字符
猜你喜欢
  • 1970-01-01
  • 2012-11-19
  • 2011-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-21
  • 1970-01-01
相关资源
最近更新 更多