mirror of
https://github.com/HackPlan/winsparkle.git
synced 2026-01-12 17:02:51 +08:00
Fix downloading updates if the user directory is non-ASCII.
Use Unicode APIs everywhere related to filenames, including the temporary directory. Fixes #26.
This commit is contained in:
@@ -102,10 +102,11 @@ bool GetHttpHeader(HINTERNET handle, DWORD whatToGet, DWORD& output)
|
||||
) == TRUE;
|
||||
}
|
||||
|
||||
std::string GetURLFileName(const URL_COMPONENTSA& urlc)
|
||||
std::wstring GetURLFileName(const URL_COMPONENTSA& urlc)
|
||||
{
|
||||
const char *lastSlash = strrchr(urlc.lpszUrlPath, '/');
|
||||
return std::string(lastSlash ? lastSlash + 1 : urlc.lpszUrlPath);
|
||||
const std::string fn(lastSlash ? lastSlash + 1 : urlc.lpszUrlPath);
|
||||
return AnsiToWide(fn);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
@@ -201,8 +202,7 @@ void DownloadFile(const std::string& url, IDownloadSink *sink, int flags)
|
||||
else
|
||||
*ptr2 = 0;
|
||||
|
||||
std::string filename( c_filename );
|
||||
sink->SetFilename(filename);
|
||||
sink->SetFilename(AnsiToWide(c_filename));
|
||||
filename_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ struct IDownloadSink
|
||||
/**
|
||||
Inform the sink of detected filename
|
||||
*/
|
||||
virtual void SetFilename(const std::string& filename) = 0;
|
||||
virtual void SetFilename(const std::wstring& filename) = 0;
|
||||
|
||||
/// Add chunk of downloaded data
|
||||
virtual void Add(const void *data, size_t len) = 0;
|
||||
@@ -59,7 +59,7 @@ struct StringDownloadSink : public IDownloadSink
|
||||
{
|
||||
virtual void SetLength(size_t) {}
|
||||
|
||||
virtual void SetFilename(const std::string& filename) {}
|
||||
virtual void SetFilename(const std::wstring&) {}
|
||||
|
||||
virtual void Add(const void *data, size_t len)
|
||||
{
|
||||
|
||||
@@ -178,7 +178,7 @@ std::string Settings::GetDefaultRegistryPath()
|
||||
namespace
|
||||
{
|
||||
|
||||
void RegistryWrite(const char *name, const char *value)
|
||||
void RegistryWrite(const char *name, const wchar_t *value)
|
||||
{
|
||||
const std::string subkey = Settings::GetRegistryPath();
|
||||
|
||||
@@ -198,14 +198,14 @@ void RegistryWrite(const char *name, const char *value)
|
||||
if ( result != ERROR_SUCCESS )
|
||||
throw Win32Exception("Cannot write settings to registry");
|
||||
|
||||
result = RegSetValueExA
|
||||
result = RegSetValueEx
|
||||
(
|
||||
key,
|
||||
name,
|
||||
AnsiToWide(name).c_str(),
|
||||
0,
|
||||
REG_SZ,
|
||||
(const BYTE*)value,
|
||||
strlen(value) + 1
|
||||
(wcslen(value) + 1) * sizeof(wchar_t)
|
||||
);
|
||||
|
||||
RegCloseKey(key);
|
||||
@@ -240,7 +240,7 @@ void RegistryDelete(const char *name)
|
||||
}
|
||||
|
||||
|
||||
int DoRegistryRead(HKEY root, const char *name, char *buf, size_t len)
|
||||
int DoRegistryRead(HKEY root, const char *name, wchar_t *buf, size_t len)
|
||||
{
|
||||
const std::string subkey = Settings::GetRegistryPath();
|
||||
|
||||
@@ -262,10 +262,10 @@ int DoRegistryRead(HKEY root, const char *name, char *buf, size_t len)
|
||||
|
||||
DWORD buflen = len;
|
||||
DWORD type;
|
||||
result = RegQueryValueExA
|
||||
result = RegQueryValueEx
|
||||
(
|
||||
key,
|
||||
name,
|
||||
AnsiToWide(name).c_str(),
|
||||
0,
|
||||
&type,
|
||||
(BYTE*)buf,
|
||||
@@ -292,7 +292,7 @@ int DoRegistryRead(HKEY root, const char *name, char *buf, size_t len)
|
||||
}
|
||||
|
||||
|
||||
int RegistryRead(const char *name, char *buf, size_t len)
|
||||
int RegistryRead(const char *name, wchar_t *buf, size_t len)
|
||||
{
|
||||
// Try reading from HKCU first. If that fails, look at HKLM too, in case
|
||||
// some settings have globally set values (either by the installer or the
|
||||
@@ -313,7 +313,7 @@ CriticalSection g_csConfigValues;
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
void Settings::DoWriteConfigValue(const char *name, const char *value)
|
||||
void Settings::DoWriteConfigValue(const char *name, const wchar_t *value)
|
||||
{
|
||||
CriticalSectionLocker lock(g_csConfigValues);
|
||||
|
||||
@@ -321,15 +321,15 @@ void Settings::DoWriteConfigValue(const char *name, const char *value)
|
||||
}
|
||||
|
||||
|
||||
std::string Settings::DoReadConfigValue(const char *name)
|
||||
std::wstring Settings::DoReadConfigValue(const char *name)
|
||||
{
|
||||
CriticalSectionLocker lock(g_csConfigValues);
|
||||
|
||||
char buf[512];
|
||||
wchar_t buf[512];
|
||||
if ( RegistryRead(name, buf, sizeof(buf)) )
|
||||
return buf;
|
||||
else
|
||||
return std::string();
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
void Settings::DeleteConfigValue(const char *name)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define _settings_h_
|
||||
|
||||
#include "threads.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
@@ -176,24 +177,49 @@ public:
|
||||
template<typename T>
|
||||
static void WriteConfigValue(const char *name, const T& value)
|
||||
{
|
||||
std::ostringstream s;
|
||||
std::wostringstream s;
|
||||
s << value;
|
||||
DoWriteConfigValue(name, s.str().c_str());
|
||||
}
|
||||
|
||||
static void WriteConfigValue(const char *name, const std::string& value)
|
||||
{
|
||||
DoWriteConfigValue(name, AnsiToWide(value).c_str());
|
||||
}
|
||||
|
||||
static void WriteConfigValue(const char *name, const std::wstring& value)
|
||||
{
|
||||
DoWriteConfigValue(name, value.c_str());
|
||||
}
|
||||
|
||||
// Reads a value from registry. Returns true if it was present,
|
||||
// false otherwise.
|
||||
template<typename T>
|
||||
static bool ReadConfigValue(const char *name, T& value)
|
||||
{
|
||||
const std::string v = DoReadConfigValue(name);
|
||||
const std::wstring v = DoReadConfigValue(name);
|
||||
if ( v.empty() )
|
||||
return false;
|
||||
std::istringstream s(v);
|
||||
std::wistringstream s(v);
|
||||
s >> value;
|
||||
return !s.fail();
|
||||
}
|
||||
|
||||
static bool ReadConfigValue(const char *name, std::string& value)
|
||||
{
|
||||
const std::wstring v = DoReadConfigValue(name);
|
||||
if (v.empty())
|
||||
return false;
|
||||
value = WideToAnsi(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ReadConfigValue(const char *name, std::wstring& value)
|
||||
{
|
||||
value = DoReadConfigValue(name);
|
||||
return !value.empty();
|
||||
}
|
||||
|
||||
// Reads a value from registry, substituting default value if not present.
|
||||
template<typename T>
|
||||
static bool ReadConfigValue(const char *name, T& value, const T& defval)
|
||||
@@ -225,8 +251,8 @@ private:
|
||||
|
||||
static std::string GetDefaultRegistryPath();
|
||||
|
||||
static void DoWriteConfigValue(const char *name, const char *value);
|
||||
static std::string DoReadConfigValue(const char *name);
|
||||
static void DoWriteConfigValue(const char *name, const wchar_t *value);
|
||||
static std::wstring DoReadConfigValue(const char *name);
|
||||
|
||||
private:
|
||||
// guards the variables below:
|
||||
|
||||
12
src/ui.cpp
12
src/ui.cpp
@@ -143,9 +143,9 @@ wxIcon GetApplicationIcon()
|
||||
|
||||
struct EventPayload
|
||||
{
|
||||
Appcast appcast;
|
||||
size_t sizeDownloaded, sizeTotal;
|
||||
std::string updateFile;
|
||||
Appcast appcast;
|
||||
size_t sizeDownloaded, sizeTotal;
|
||||
std::wstring updateFile;
|
||||
};
|
||||
|
||||
|
||||
@@ -283,7 +283,7 @@ public:
|
||||
// update download progress
|
||||
void DownloadProgress(size_t downloaded, size_t total);
|
||||
// change state into "update downloaded"
|
||||
void StateUpdateDownloaded(const std::string& updateFile);
|
||||
void StateUpdateDownloaded(const std::wstring& updateFile);
|
||||
|
||||
private:
|
||||
void EnablePulsing(bool enable);
|
||||
@@ -713,7 +713,7 @@ void UpdateDialog::DownloadProgress(size_t downloaded, size_t total)
|
||||
}
|
||||
|
||||
|
||||
void UpdateDialog::StateUpdateDownloaded(const std::string& updateFile)
|
||||
void UpdateDialog::StateUpdateDownloaded(const std::wstring& updateFile)
|
||||
{
|
||||
m_downloader->Join();
|
||||
delete m_downloader;
|
||||
@@ -1221,7 +1221,7 @@ void UI::NotifyDownloadProgress(size_t downloaded, size_t total)
|
||||
|
||||
|
||||
/*static*/
|
||||
void UI::NotifyUpdateDownloaded(const std::string& updateFile)
|
||||
void UI::NotifyUpdateDownloaded(const std::wstring& updateFile)
|
||||
{
|
||||
UIThreadAccess uit;
|
||||
EventPayload payload;
|
||||
|
||||
2
src/ui.h
2
src/ui.h
@@ -86,7 +86,7 @@ public:
|
||||
/**
|
||||
Notifies the UI that an update was downloaded.
|
||||
*/
|
||||
static void NotifyUpdateDownloaded(const std::string& updateFile);
|
||||
static void NotifyUpdateDownloaded(const std::wstring& updateFile);
|
||||
|
||||
/**
|
||||
Shows the WinSparkle window in "checking for updates..." state.
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace winsparkle
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string CreateUniqueTempDirectory()
|
||||
std::wstring CreateUniqueTempDirectory()
|
||||
{
|
||||
// We need to put downloaded updates into a directory of their own, because
|
||||
// if we put it in $TMP, some DLLs could be there and interfere with the
|
||||
@@ -53,25 +53,23 @@ std::string CreateUniqueTempDirectory()
|
||||
//
|
||||
// This code creates a new randomized directory name and tries to create it;
|
||||
// this process is repeated if the directory already exists.
|
||||
char tmpdir[MAX_PATH+1];
|
||||
if ( GetTempPathA(sizeof(tmpdir), tmpdir) == 0 )
|
||||
wchar_t tmpdir[MAX_PATH+1];
|
||||
if ( GetTempPath(MAX_PATH+1, tmpdir) == 0 )
|
||||
throw Win32Exception("Cannot create temporary directory");
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
std::ostringstream sdir;
|
||||
sdir << tmpdir << "Update-";
|
||||
std::wstring dir(tmpdir);
|
||||
dir += L"Update-";
|
||||
|
||||
UUID uuid;
|
||||
UuidCreate(&uuid);
|
||||
RPC_CSTR uuidStr;
|
||||
UuidToStringA(&uuid, &uuidStr);
|
||||
sdir << uuidStr;
|
||||
RpcStringFreeA(&uuidStr);
|
||||
RPC_WSTR uuidStr;
|
||||
RPC_STATUS status = UuidToString(&uuid, &uuidStr);
|
||||
dir += reinterpret_cast<wchar_t*>(uuidStr);
|
||||
RpcStringFree(&uuidStr);
|
||||
|
||||
const std::string dir(sdir.str());
|
||||
|
||||
if ( CreateDirectoryA(dir.c_str(), NULL) )
|
||||
if ( CreateDirectory(dir.c_str(), NULL) )
|
||||
return dir;
|
||||
else if ( GetLastError() != ERROR_ALREADY_EXISTS )
|
||||
throw Win32Exception("Cannot create temporary directory");
|
||||
@@ -80,9 +78,9 @@ std::string CreateUniqueTempDirectory()
|
||||
|
||||
struct UpdateDownloadSink : public IDownloadSink
|
||||
{
|
||||
UpdateDownloadSink(Thread& thread, const std::string& dir)
|
||||
UpdateDownloadSink(Thread& thread, const std::wstring& dir)
|
||||
: m_thread(thread),
|
||||
m_dir(dir), m_path(""), m_file(NULL),
|
||||
m_dir(dir), m_file(NULL),
|
||||
m_downloaded(0), m_total(0), m_lastUpdate(-1)
|
||||
{}
|
||||
|
||||
@@ -97,17 +95,17 @@ struct UpdateDownloadSink : public IDownloadSink
|
||||
}
|
||||
}
|
||||
|
||||
std::string GetFilePath(void) { return m_path; }
|
||||
std::wstring GetFilePath(void) { return m_path; }
|
||||
|
||||
virtual void SetLength(size_t l) { m_total = l; }
|
||||
|
||||
virtual void SetFilename(const std::string& filename)
|
||||
virtual void SetFilename(const std::wstring& filename)
|
||||
{
|
||||
if ( m_file )
|
||||
throw std::runtime_error("Update file already set");
|
||||
|
||||
m_path = m_dir + "\\" + filename;
|
||||
m_file = fopen(m_path.c_str(), "wb");
|
||||
m_path = m_dir + L"\\" + filename;
|
||||
m_file = _wfopen(m_path.c_str(), L"wb");
|
||||
if ( !m_file )
|
||||
throw std::runtime_error("Cannot save update file");
|
||||
}
|
||||
@@ -136,8 +134,8 @@ struct UpdateDownloadSink : public IDownloadSink
|
||||
Thread& m_thread;
|
||||
size_t m_downloaded, m_total;
|
||||
FILE *m_file;
|
||||
std::string m_dir;
|
||||
std::string m_path;
|
||||
std::wstring m_dir;
|
||||
std::wstring m_path;
|
||||
clock_t m_lastUpdate;
|
||||
};
|
||||
|
||||
@@ -166,7 +164,7 @@ void UpdateDownloader::Run()
|
||||
|
||||
try
|
||||
{
|
||||
const std::string tmpdir = CreateUniqueTempDirectory();
|
||||
const std::wstring tmpdir = CreateUniqueTempDirectory();
|
||||
Settings::WriteConfigValue("UpdateTempDir", tmpdir);
|
||||
|
||||
UpdateDownloadSink sink(*this, tmpdir);
|
||||
|
||||
Reference in New Issue
Block a user