2

Windows 7 x64 で次のコードを実行する

#include <stdio.h>
#include <errno.h>

int main() {
    int i;
    FILE *tmp;
    for (i = 0; i < 10000; i++) {
        errno = 0;
        if(!(tmp = tmpfile())) printf("Fail %d, err %d\n", i, errno);
        fclose(tmp);
    }
    return 0;
}

637 番目と 1004 番目の呼び出しで errno 13 (許可が拒否されました) が発生し、XP で正常に動作します (7 x86 は試していません)。何か不足していますか、それともバグですか?

4

2 に答える 2

4

Windows 8でも同様の問題が発生しました-tmpfile()がwin32 ERROR_ACCESS_DENIEDエラーコードを引き起こしていました-はい、管理者権限でアプリケーションを実行すると、正常に動作します.

ここで問題が言及されていると思います: https://lists.gnu.org/archive/html/bug-gnulib/2007-02/msg00162.html

Windows では、tmpfile 関数は常にルート ディレクトリに一時ファイルを作成するように定義されています。ほとんどのユーザーはそれを行う権限を持っていないため、失敗することがよくあります。

これは不完全な Windows ポートの問題であると思われます。したがって、これは Microsoft に報告されたエラーであるはずです。(役に立たないのに tmpfile 関数をコーディングするのはなぜですか?)

しかし、マイクロソフトの風車と戦う時間があるのは誰ですか?! :-)

GetTempPathW / GetModuleFileNameW / _wfopen を使用して同様の実装をコーディングしました。この問題に遭遇したコードは libjpeg からのものです。ソース コード全体をここに添付しますが、jpeg_open_backing_store からコードを取得できます。

jmemwin.cpp:

//
//  Windows port for jpeg lib functions.
//
#define JPEG_INTERNALS
#include <Windows.h>        // GetTempFileName
#undef FAR                  // Will be redefined - disable warning
#include "jinclude.h"
#include "jpeglib.h"

extern "C" {
#include "jmemsys.h"        // jpeg_ api interface.

//
// Memory allocation and freeing are controlled by the regular library routines malloc() and free().
//

GLOBAL(void *) jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
{
    return (void *) malloc(sizeofobject);
}

GLOBAL(void) jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
{
    free(object);
}

/*
 * "Large" objects are treated the same as "small" ones.
 * NB: although we include FAR keywords in the routine declarations,
 * this file won't actually work in 80x86 small/medium model; at least,
 * you probably won't be able to process useful-size images in only 64KB.
 */

GLOBAL(void FAR *) jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
{
    return (void FAR *) malloc(sizeofobject);
}

GLOBAL(void) jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
{
    free(object);
}

//
//  Used only by command line applications, not by static library compilation
//
#ifndef DEFAULT_MAX_MEM     /* so can override from makefile */
#define DEFAULT_MAX_MEM     1000000L /* default: one megabyte */
#endif

GLOBAL(long) jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, long max_bytes_needed, long already_allocated)
{
    // jmemansi.c's jpeg_mem_available implementation was insufficient for some of .jpg loads.
    MEMORYSTATUSEX status = { 0 };
    status.dwLength = sizeof(status);
    GlobalMemoryStatusEx(&status);

    if( status.ullAvailPhys > LONG_MAX )
        // Normally goes here since new PC's have more than 4 Gb of ram.
        return LONG_MAX;

    return (long) status.ullAvailPhys;
}


/*
    Backing store (temporary file) management.
    Backing store objects are only used when the value returned by
    jpeg_mem_available is less than the total space needed.  You can dispense
    with these routines if you have plenty of virtual memory; see jmemnobs.c.
*/

METHODDEF(void) read_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
    if (fseek(info->temp_file, file_offset, SEEK_SET))
        ERREXIT(cinfo, JERR_TFILE_SEEK);

    size_t readed = fread( buffer_address, 1, byte_count, info->temp_file);

    if (readed != (size_t) byte_count)
        ERREXIT(cinfo, JERR_TFILE_READ);
}


METHODDEF(void)
write_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
    if (fseek(info->temp_file, file_offset, SEEK_SET))
        ERREXIT(cinfo, JERR_TFILE_SEEK);

    if (JFWRITE(info->temp_file, buffer_address, byte_count) != (size_t) byte_count)
        ERREXIT(cinfo, JERR_TFILE_WRITE);

    // E.g. if you need to debug writes.
    //if( fflush(info->temp_file) != 0 )
    //    ERREXIT(cinfo, JERR_TFILE_WRITE);
}


METHODDEF(void)
close_backing_store (j_common_ptr cinfo, backing_store_ptr info)
{
    fclose(info->temp_file);
    // File is deleted using 'D' flag on open.
}

static HMODULE DllHandle()
{
    MEMORY_BASIC_INFORMATION info;
    VirtualQuery(DllHandle, &info, sizeof(MEMORY_BASIC_INFORMATION));
    return (HMODULE)info.AllocationBase;
}

GLOBAL(void) jpeg_open_backing_store(j_common_ptr cinfo, backing_store_ptr info, long total_bytes_needed)
{
    // Generate unique filename.
    wchar_t path[ MAX_PATH ] = { 0 };
    wchar_t dllPath[ MAX_PATH ] = { 0 };
    GetTempPathW( MAX_PATH, path );

    // Based on .exe or .dll filename
    GetModuleFileNameW( DllHandle(), dllPath, MAX_PATH );

    wchar_t* p = wcsrchr( dllPath, L'\\');
    wchar_t* ext = wcsrchr( p + 1, L'.');

    if( ext ) *ext = 0;
    wchar_t* outFile = path + wcslen(path);

    static int iTempFileId = 1;
    // Based on process id (so processes would not fight with each other)
    // Based on some process global id.
    wsprintfW(outFile, L"%s_%d_%d.tmp",p + 1, GetCurrentProcessId(), iTempFileId++ );

    // 'D' - temporary file.
    if ((info->temp_file = _wfopen(path, L"w+bD") ) == NULL)
        ERREXITS(cinfo, JERR_TFILE_CREATE, "");

    info->read_backing_store = read_backing_store;
    info->write_backing_store = write_backing_store;
    info->close_backing_store = close_backing_store;
} //jpeg_open_backing_store


/*
 * These routines take care of any system-dependent initialization and
 * cleanup required.
 */

GLOBAL(long)
jpeg_mem_init (j_common_ptr cinfo)
{
     return DEFAULT_MAX_MEM;    /* default for max_memory_to_use */
}

GLOBAL(void)
jpeg_mem_term (j_common_ptr cinfo)
{
  /* no work */
}

}

一部の関数からのエラーを意図的に無視しています - GetTempPathW または GetModuleFileNameW が失敗するのを見たことがありますか?

于 2016-06-09T21:22:30.693 に答える
3

on のマンページから少し復習すると、次のtmpfile()ように返されますFILE*

ファイルを閉じるか、プログラムを終了すると、ファイルは自動的に削除されます。

この問題に対する私の判断: Windows でファイルを削除するのは奇妙です。

Windows でファイルを削除すると、何かがハンドルを保持している限り、同じ絶対パスで何かを呼び出すことはできません。そうしないと、Win32 コードにマップされるCreateFileNT エラー コードで失敗します。これはおそらくinがどこから来ているかです。これは、Sysinternals Process Monitor などのツールで確認できます。STATUS_DELETE_PENDINGERROR_ACCESS_DENIEDEPERMerrno

私の推測では、CRT が何らかの形で以前に使用されていたものと同じ名前のファイルを作成してしまったのでしょう。Windows でのファイルの削除が非同期のように見えることがあります。これは、他のプロセス (場合によっては、クローズ時に削除ハンドルを閉じたという事実に反応して、ウイルス対策製品でさえも) がハンドルを残すためです。そのため、一部のタイミング ウィンドウでは、削除保留/アクセス拒否を押さないとハンドルを取得できない可視ファイルが表示されます。tmpfileまたは、他のプロセスが作業しているファイル名を単に選択した可能性があります。

この種のことを回避するには、一時ファイルの別のメカニズムを検討することをお勧めします...たとえば、Win32 のような関数GetTempFileNameを使用すると、衝突の可能性を低くする独自のプレフィックスを作成できます。その関数は、作成が「既に存在する」で失敗した場合に再試行することで競合状態を解決するように見えるため、生成された一時ファイル名を削除する場合は注意してください。ファイルを削除すると、他のプロセス/スレッドと同時に使用する権利が取り消されます。

于 2011-06-06T02:09:00.193 に答える