From 0b9342604290bb4fd44f573d2f4d5e0eae64a40a Mon Sep 17 00:00:00 2001 From: Oliver Duis Date: Fri, 24 Dec 2010 09:33:12 +0100 Subject: [PATCH] Cleaned up old Windows file rename workaround by using Win API native functions; see issue #430 --- rtengine/imageio.cc | 21 ++++----------------- rtengine/safegtk.cc | 33 ++++++++++++++++++++++++++++++--- rtengine/safegtk.h | 1 + 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index 566fefb64..533363fc1 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -2,6 +2,7 @@ * This file is part of RawTherapee. * * Copyright (c) 2004-2010 Gabor Horvath + * Copyright (c) 2010 Oliver Duis * * RawTherapee is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -501,11 +502,7 @@ int ImageIO::loadPPMFromMemory(const char* buffer, int width, int height, bool s int ImageIO::savePNG (Glib::ustring fname, int compression, int bps) { - // create a temporary file name that is opened in parallel by e.g. image viewers whilte RT is still writing - Glib::ustring tmpFname=fname; - tmpFname.append(".tmp"); - - FILE *file = safe_g_fopen (tmpFname, "wb"); + FILE *file = safe_g_fopen_WriteBinLock (fname); if (!file) return IMIO_CANNOTREADFILE; @@ -572,9 +569,6 @@ int ImageIO::savePNG (Glib::ustring fname, int compression, int bps) { delete [] row; fclose (file); - // Rename temporary filename, practically atomic - safe_g_rename(tmpFname,fname); - if (pl) { pl->setProgressStr ("Ready."); pl->setProgress (1.0); @@ -592,11 +586,7 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality) { cinfo.err = jpeg_std_error (&jerr); jpeg_create_compress (&cinfo); - // create a temporary file name that is opened in parallel by e.g. image viewers whilte RT is still writing - Glib::ustring tmpFname=fname; - tmpFname.append(".tmp"); - - FILE *file = safe_g_fopen (tmpFname, "wb"); + FILE *file = safe_g_fopen_WriteBinLock (fname); if (!file) return IMIO_CANNOTREADFILE; @@ -686,9 +676,6 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality) { delete [] row; fclose (file); - // Rename temporary filename, practically atomic - safe_g_rename(tmpFname,fname); - if (pl) { pl->setProgressStr ("Ready."); pl->setProgress (1.0); @@ -709,7 +696,7 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) { unsigned char* linebuffer = new unsigned char[lineWidth]; // TODO the following needs to be looked into - do we really need two ways to write a Tiff file ? if (exifRoot && uncompressed) { - FILE *file = safe_g_fopen (fname, "wb"); + FILE *file = safe_g_fopen_WriteBinLock (fname); if (!file) return IMIO_CANNOTREADFILE; diff --git a/rtengine/safegtk.cc b/rtengine/safegtk.cc index a08ff346a..69305e8f3 100644 --- a/rtengine/safegtk.cc +++ b/rtengine/safegtk.cc @@ -22,6 +22,10 @@ #include #include #include +#ifdef WIN32 +#include +#include +#endif Glib::RefPtr safe_create_from_file(const std::string& filename) @@ -169,7 +173,7 @@ bool safe_spawn_command_line_async (const Glib::ustring& cmd_utf8) #ifdef GLIBMM_EXCEPTIONS_ENABLED try { cmd = Glib::filename_from_utf8(cmd_utf8); - printf ("command line: |%s|\n", cmd.c_str()); + printf ("command line: %s\n", cmd.c_str()); Glib::spawn_command_line_async (cmd.c_str()); success = true; } catch (Glib::Exception& ex) { @@ -179,7 +183,7 @@ bool safe_spawn_command_line_async (const Glib::ustring& cmd_utf8) std::auto_ptr error; cmd = Glib::filename_from_utf8(cmd_utf8, error); if (!error.get()) { - printf ("command line: |%s|\n", cmd.c_str()); + printf ("command line: %s\n", cmd.c_str()); Glib::spawn_command_line_async (cmd, error); } if (error.get()) @@ -201,7 +205,7 @@ bool safe_spawn_command_line_sync (const Glib::ustring& cmd_utf8) int exitStatus=-1; try { //cmd = Glib::filename_from_utf8(cmd_utf8); - printf ("command line: |%s|\n", cmd_utf8.c_str()); + printf ("command line: %s\n", cmd_utf8.c_str()); // if it crashes here on windows, make sure you have the GTK runtime files gspawn-win32-helper*.exe files in RT directory Glib::spawn_command_line_sync (cmd_utf8, NULL, NULL, &exitStatus); @@ -211,6 +215,29 @@ bool safe_spawn_command_line_sync (const Glib::ustring& cmd_utf8) return (exitStatus==0); } +// Opens a file for binary writing and request exclusive lock (cases were you need "wb" mode plus locking) +// (Important on Windows to prevent Explorer to crash RT when parallel scanning e.g. a currently written image file) +FILE * safe_g_fopen_WriteBinLock(const Glib::ustring& fname) { + FILE* f=NULL; + +#ifdef WIN32 + // g_fopen just uses _wfopen internally on Windows, does not lock access and has no options to set this + // so use a native function to work around this problem + wchar_t *wFname = (wchar_t*)g_utf8_to_utf16 (fname.c_str(), -1, NULL, NULL, NULL); + HANDLE hFile = CreateFileW(wFname, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + g_free(wFname); + + if (hFile==INVALID_HANDLE_VALUE) + f=NULL; + else + f=_fdopen( _open_osfhandle((intptr_t)hFile, 0) , "wb"); +#else + f = safe_g_fopen(fname, "wb"); +#endif + + return f; +} + FILE * safe_g_fopen(const Glib::ustring& src,const gchar *mode) { return g_fopen(src.c_str(),mode); diff --git a/rtengine/safegtk.h b/rtengine/safegtk.h index 73f8dfbad..ec8ddb2ee 100644 --- a/rtengine/safegtk.h +++ b/rtengine/safegtk.h @@ -31,6 +31,7 @@ std::string safe_locale_from_utf8 (const Glib::ustring& utf8_str); std::string safe_filename_from_utf8 (const Glib::ustring& utf8_str); FILE * safe_g_fopen(const Glib::ustring& src,const gchar *mode); +FILE * safe_g_fopen_WriteBinLock(const Glib::ustring& fname); bool safe_file_test (const Glib::ustring& filename, Glib::FileTest test); int safe_g_remove(const Glib::ustring& filename); int safe_g_rename(const Glib::ustring& oldFilename, const Glib::ustring& newFilename);