Windows 7 x64上的tmpfile()

在Windows 7 x64上运行以下代码

#include  #include  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次通话中给出了错误13(权限被拒绝),它在XP上运行良好(没试过7 x86)。 我错过了什么或这是一个错误吗?

tmpfile()的联机帮助页上复习一下,返回一个FILE*

文件关闭或程序终止时将自动删除。

我对此问题的判断:在Windows上删除文件很奇怪。

当您在Windows上删除文件时,只要有东西持有句柄,就不能在具有相同绝对路径的东西上调用CreateFile ,否则它将失败并STATUS_DELETE_PENDING NT错误代码STATUS_DELETE_PENDING ,它将映射到Win32代码ERROR_ACCESS_DENIED 。 这可能是errno EPERM来自的地方。 您可以使用Sysinternals Process Monitor等工具进行确认。

我的猜测是CRT以某种方式创建了一个与之前使用的名称相同的文件。 我有时会目睹在Windows上删除文件可能看起来是异步的,因为其他一些进程(有时甚至是防病毒产品,因为你刚刚关闭了一个关闭删除句柄的事实……)会留下一个句柄打开文件,因此对于某个计时窗口,您将看到一个可见的文件,如果没有达到删除暂挂/访问被拒绝,您无法获得处理。 或者,可能是tmpfile只是选择了其他进程正在处理的文件名。

为了避免这种情况,您可能需要考虑临时文件的另一种机制…例如,像Win32 GetTempFileName这样的函数允许您创建自己的前缀,这可能使碰撞不太可能发生。 如果创建失败并且“已存在”,则该函数似乎通过重试来解决竞争条件,因此请小心删除该事物生成的临时文件名 – 删除该文件会取消您与其他进程/线程同时使用它的权限。

我在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  // 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); // Eg 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失败吗?