From e88fde08ea10267ce4083bd6b3dc7a3c652ee09d Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Fri, 20 Jun 2014 20:18:43 +0300
Subject: [PATCH 1/1] Handle Unicode correctly when reading/writing DSN
 properties

There was some confusion on how the DSN name and other settings were
encoded. When a DSN name was written in the dialog, it was passed in native
encoding to the functiosn that write to the registry, SQLDsnToIni() and
SQLWritePrivateProfileString(). The first problem with that is that it only
works if the system codepage can encode the DSN name, even if you're using
the Unicode driver which is supposed to handle any character. But even then,
the settings were not read back correctly when the DSN was used. Our
SQLDriverConnectW() function converts the DSN to UTF-8, and incorrectly
passed the UTF-8 encoded DSN name to SQLGetPrivateProfileString() function,
which expects the arguments to be encoded with the system codepage.

To fix both of those issues, always use the Unicode functions when
registering or modifying ODBC settings, when built as a Unicode driver. Also
implement ConfigDSNW function, and use the Unicode *W functions when working
with the dialog boxes, so that non-ASCII characters are not lost when moving
strings to/from the GUI.
---
 connection.h   |   6 +
 dlg_specific.c | 369 ++++++++++++++++++++++++++++-----------------------------
 dlg_specific.h |   2 +-
 dlg_wingui.c   | 193 +++++++++++++++++++++---------
 drvconn.c      |  12 +-
 psqlodbc.def   |   4 +-
 setup.c        | 154 +++++++++++++++++++-----
 win_setup.h    |   4 -
 8 files changed, 461 insertions(+), 283 deletions(-)

diff --git a/connection.h b/connection.h
index e92ff61..c160054 100644
--- a/connection.h
+++ b/connection.h
@@ -280,6 +280,12 @@ typedef struct CancelRequestPacket
 */
 typedef struct
 {
+	/*
+	 * In ANSI driver, all the attributes are stored using the system codepage.
+	 *
+	 * In Unicode driver, they are stored in UTF-8, because that's what we
+	 * use when communicating with the server.
+	 */
 	char		dsn[MEDIUM_REGISTRY_LEN];
 	char		desc[MEDIUM_REGISTRY_LEN];
 	char		drivername[MEDIUM_REGISTRY_LEN];
diff --git a/dlg_specific.c b/dlg_specific.c
index 8e051af..5d30f66 100644
--- a/dlg_specific.c
+++ b/dlg_specific.c
@@ -28,6 +28,8 @@
 
 extern GLOBAL_VALUES globals;
 
+static int getPrivateProfileString(const char *section, const char *key, const char *def, char *dst, int maxlen, const char *filename);
+
 static void encode(const pgNAME, char *out, int outlen);
 static pgNAME decode(const char *in);
 static pgNAME decode_or_remove_braces(const char *in);
@@ -808,7 +810,7 @@ getDSNdefaults(ConnInfo *ci)
 int
 getDriverNameFromDSN(const char *dsn, char *driver_name, int namelen)
 {
-	return SQLGetPrivateProfileString(ODBC_DATASOURCES, dsn, "", driver_name, namelen, ODBC_INI);
+	return getPrivateProfileString(dsn, ODBC_DATASOURCES, "", driver_name, namelen, ODBC_INI);
 }
 
 int
@@ -823,6 +825,75 @@ setLogDir(const char *dir)
 	return SQLWritePrivateProfileString(DBMS_NAME, INI_LOGDIR, dir, ODBCINST_INI);
 }
 
+/*
+ * A wrapper for SQLGetPrivateProfileString() function. In a Unicode driver,
+ * calls the SQLGetPrivateProfileStringW(), and converts the arguments and
+ * the result from/to UTF8. In an ANSI function, passes through to
+ * SQLGetPrivateProfileStringA().
+ */
+static int
+getPrivateProfileString(const char *section, const char *key, const char *def, char *dst, int maxlen, const char *filename)
+{
+#ifdef UNICODE_SUPPORT
+	SQLWCHAR	wsection[MEDIUM_REGISTRY_LEN];
+	SQLWCHAR	wkey[MEDIUM_REGISTRY_LEN];
+	SQLWCHAR	wdefault[SMALL_REGISTRY_LEN];
+	SQLWCHAR	wdst[LARGE_REGISTRY_LEN];
+	SQLWCHAR	wfilename[SMALL_REGISTRY_LEN];
+	char	   *dst_copy;
+	int			result;
+
+	(void) utf8_to_ucs2(section, SQL_NTS, wsection, MEDIUM_REGISTRY_LEN);
+	(void) utf8_to_ucs2(key, SQL_NTS, wkey, MEDIUM_REGISTRY_LEN);
+	(void) utf8_to_ucs2(def, SQL_NTS, wdefault, SMALL_REGISTRY_LEN);
+	(void) utf8_to_ucs2(filename, SQL_NTS, wfilename, SMALL_REGISTRY_LEN);
+
+	SQLGetPrivateProfileStringW(wsection, wkey, wdefault, wdst, LARGE_REGISTRY_LEN, wfilename);
+	dst_copy = ucs2_to_utf8(wdst, SQL_NTS, NULL, FALSE);
+	if (dst_copy)
+	{
+		strncpy_null(dst, dst_copy, maxlen);
+		free(dst_copy);
+		result = (int) strlen(dst);
+	}
+	else
+	{
+		dst[0] = '\0';
+		result = 0;
+	}
+	return result;
+#else
+	return SQLGetPrivateProfileString(section, key, def, dst, maxlen, filename);
+#endif
+}
+
+int
+writePrivateProfileString(const char *section, const char *key, const char *value, const char *filename)
+{
+#ifdef UNICODE_SUPPORT
+	SQLWCHAR	wfilename[SMALL_REGISTRY_LEN];
+	SQLWCHAR	wsection[MEDIUM_REGISTRY_LEN];
+	SQLWCHAR	wkey[MEDIUM_REGISTRY_LEN];
+	SQLWCHAR	wvalue[LARGE_REGISTRY_LEN];
+
+	(void) utf8_to_ucs2(filename, SQL_NTS, wfilename, SMALL_REGISTRY_LEN);
+	(void) utf8_to_ucs2(section, SQL_NTS, wsection, MEDIUM_REGISTRY_LEN);
+	(void) utf8_to_ucs2(key, SQL_NTS, wkey, MEDIUM_REGISTRY_LEN);
+	if (value)
+		(void) utf8_to_ucs2(value, SQL_NTS, wvalue, LARGE_REGISTRY_LEN);
+
+	return SQLWritePrivateProfileStringW(wsection, wkey, value ? wvalue : NULL, wfilename);
+#else
+	return SQLWritePrivateProfileString(section, key, value, filename);
+#endif
+}
+
+int
+writeDSNOptionString(const char *dsn, const char *key, const char *value)
+{
+	return writePrivateProfileString(dsn, key, value, ODBC_INI);
+}
+
 void
 getDSNinfo(ConnInfo *ci, char overwrite)
 {
@@ -858,45 +929,45 @@ getDSNinfo(ConnInfo *ci, char overwrite)
 	/* Proceed with getting info for the given DSN. */
 
 	if (ci->desc[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_KDESC, "", ci->desc, sizeof(ci->desc), ODBC_INI);
+		getPrivateProfileString(DSN, INI_KDESC, "", ci->desc, sizeof(ci->desc), ODBC_INI);
 
 	if (ci->server[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_SERVER, "", ci->server, sizeof(ci->server), ODBC_INI);
+		getPrivateProfileString(DSN, INI_SERVER, "", ci->server, sizeof(ci->server), ODBC_INI);
 
 	if (ci->database[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_DATABASE, "", ci->database, sizeof(ci->database), ODBC_INI);
+		getPrivateProfileString(DSN, INI_DATABASE, "", ci->database, sizeof(ci->database), ODBC_INI);
 
 	if (ci->username[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_USERNAME, "", ci->username, sizeof(ci->username), ODBC_INI);
+		getPrivateProfileString(DSN, INI_USERNAME, "", ci->username, sizeof(ci->username), ODBC_INI);
 
 	if (NAME_IS_NULL(ci->password) || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_PASSWORD, "", encoded_item, sizeof(encoded_item), ODBC_INI);
+		getPrivateProfileString(DSN, INI_PASSWORD, "", encoded_item, sizeof(encoded_item), ODBC_INI);
 		ci->password = decode(encoded_item);
 	}
 
 	if (ci->port[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI);
+		getPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI);
 
 	if (ci->onlyread[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_READONLY, "", ci->onlyread, sizeof(ci->onlyread), ODBC_INI);
+		getPrivateProfileString(DSN, INI_READONLY, "", ci->onlyread, sizeof(ci->onlyread), ODBC_INI);
 
 	if (ci->show_oid_column[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_SHOWOIDCOLUMN, "", ci->show_oid_column, sizeof(ci->show_oid_column), ODBC_INI);
+		getPrivateProfileString(DSN, INI_SHOWOIDCOLUMN, "", ci->show_oid_column, sizeof(ci->show_oid_column), ODBC_INI);
 
 	if (ci->fake_oid_index[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_FAKEOIDINDEX, "", ci->fake_oid_index, sizeof(ci->fake_oid_index), ODBC_INI);
+		getPrivateProfileString(DSN, INI_FAKEOIDINDEX, "", ci->fake_oid_index, sizeof(ci->fake_oid_index), ODBC_INI);
 
 	if (ci->row_versioning[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_ROWVERSIONING, "", ci->row_versioning, sizeof(ci->row_versioning), ODBC_INI);
+		getPrivateProfileString(DSN, INI_ROWVERSIONING, "", ci->row_versioning, sizeof(ci->row_versioning), ODBC_INI);
 
 	if (ci->show_system_tables[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, "", ci->show_system_tables, sizeof(ci->show_system_tables), ODBC_INI);
+		getPrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, "", ci->show_system_tables, sizeof(ci->show_system_tables), ODBC_INI);
 
 	if (ci->protocol[0] == '\0' || overwrite)
 	{
 		char	*ptr;
-		SQLGetPrivateProfileString(DSN, INI_PROTOCOL, "", ci->protocol, sizeof(ci->protocol), ODBC_INI);
+		getPrivateProfileString(DSN, INI_PROTOCOL, "", ci->protocol, sizeof(ci->protocol), ODBC_INI);
 		if (ptr = strchr(ci->protocol, '-'), NULL != ptr)
 		{
 			*ptr = '\0';
@@ -910,94 +981,93 @@ getDSNinfo(ConnInfo *ci, char overwrite)
 
 	if (NAME_IS_NULL(ci->conn_settings) || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_item, sizeof(encoded_item), ODBC_INI);
+		getPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_item, sizeof(encoded_item), ODBC_INI);
 		ci->conn_settings = decode(encoded_item);
 	}
 
 	if (ci->translation_dll[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_TRANSLATIONDLL, "", ci->translation_dll, sizeof(ci->translation_dll), ODBC_INI);
+		getPrivateProfileString(DSN, INI_TRANSLATIONDLL, "", ci->translation_dll, sizeof(ci->translation_dll), ODBC_INI);
 
 	if (ci->translation_option[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_TRANSLATIONOPTION, "", ci->translation_option, sizeof(ci->translation_option), ODBC_INI);
+		getPrivateProfileString(DSN, INI_TRANSLATIONOPTION, "", ci->translation_option, sizeof(ci->translation_option), ODBC_INI);
 
 	if (ci->disallow_premature < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_DISALLOWPREMATURE, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_DISALLOWPREMATURE, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->disallow_premature = atoi(temp);
 	}
 
 	if (ci->allow_keyset < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_UPDATABLECURSORS, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_UPDATABLECURSORS, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->allow_keyset = atoi(temp);
 	}
 
 	if (ci->lf_conversion < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_LFCONVERSION, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_LFCONVERSION, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->lf_conversion = atoi(temp);
 	}
 
 	if (ci->true_is_minus1 < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_TRUEISMINUS1, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_TRUEISMINUS1, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->true_is_minus1 = atoi(temp);
 	}
 
 	if (ci->int8_as < -100 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_INT8AS, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_INT8AS, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->int8_as = atoi(temp);
 	}
 
 	if (ci->bytea_as_longvarbinary < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_BYTEAASLONGVARBINARY, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_BYTEAASLONGVARBINARY, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->bytea_as_longvarbinary = atoi(temp);
 	}
 
 	if (ci->use_server_side_prepare < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_USESERVERSIDEPREPARE, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_USESERVERSIDEPREPARE, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->use_server_side_prepare = atoi(temp);
 	}
 
 	if (ci->lower_case_identifier < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_LOWERCASEIDENTIFIER, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_LOWERCASEIDENTIFIER, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->lower_case_identifier = atoi(temp);
 	}
 
 	if (ci->gssauth_use_gssapi < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_GSSAUTHUSEGSSAPI, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_GSSAUTHUSEGSSAPI, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->gssauth_use_gssapi = atoi(temp);
 	}
 
 	if (ci->sslmode[0] == '\0' || overwrite)
-		SQLGetPrivateProfileString(DSN, INI_SSLMODE, "", ci->sslmode, sizeof(ci->sslmode), ODBC_INI);
+		getPrivateProfileString(DSN, INI_SSLMODE, "", ci->sslmode, sizeof(ci->sslmode), ODBC_INI);
 
 #ifdef	_HANDLE_ENLIST_IN_DTC_
 	if (ci->xa_opt < 0 || overwrite)
 	{
-		SQLGetPrivateProfileString(DSN, INI_XAOPT, "", temp, sizeof(temp), ODBC_INI);
+		getPrivateProfileString(DSN, INI_XAOPT, "", temp, sizeof(temp), ODBC_INI);
 		if (temp[0])
 			ci->xa_opt = atoi(temp);
 	}
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
 
 	/* Force abbrev connstr or bde */
-	SQLGetPrivateProfileString(DSN, INI_EXTRAOPTIONS, "",
-					temp, sizeof(temp), ODBC_INI);
+	getPrivateProfileString(DSN, INI_EXTRAOPTIONS, "", temp, sizeof(temp), ODBC_INI);
 	if (temp[0])
 	{
 		UInt4	val = 0;
@@ -1052,34 +1122,34 @@ writeDriverCommoninfo(const char *fileName, const char *sectionName,
 		sectionName = DBMS_NAME;
 
 	sprintf(tmp, "%d", comval->commlog);
-	if (!SQLWritePrivateProfileString(sectionName, INI_COMMLOG, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_COMMLOG, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->debug);
-	if (!SQLWritePrivateProfileString(sectionName, INI_DEBUG, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_DEBUG, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->fetch_max);
-	if (!SQLWritePrivateProfileString(sectionName, INI_FETCH, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_FETCH, tmp, fileName))
 		errc--;
 
 	if (stricmp(ODBCINST_INI, fileName) == 0)
 		return errc;
 
 	sprintf(tmp, "%d", comval->fetch_max);
-	if (!SQLWritePrivateProfileString(sectionName, INI_FETCH, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_FETCH, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->disable_optimizer);
-	if (!SQLWritePrivateProfileString(sectionName, INI_OPTIMIZER, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_OPTIMIZER, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->ksqo);
-	if (!SQLWritePrivateProfileString(sectionName, INI_KSQO, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_KSQO, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->unique_index);
-	if (!SQLWritePrivateProfileString(sectionName, INI_UNIQUEINDEX, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_UNIQUEINDEX, tmp, fileName))
 		errc--;
 	/*
 	 * Never update the onlyread from this module.
@@ -1087,47 +1157,46 @@ writeDriverCommoninfo(const char *fileName, const char *sectionName,
 	if (stricmp(ODBCINST_INI, fileName) == 0)
 	{
 		sprintf(tmp, "%d", comval->onlyread);
-		SQLWritePrivateProfileString(sectionName, INI_READONLY, tmp,
-									 fileName);
+		writePrivateProfileString(sectionName, INI_READONLY, tmp, fileName);
 	}
 
 	sprintf(tmp, "%d", comval->use_declarefetch);
-	if (!SQLWritePrivateProfileString(sectionName, INI_USEDECLAREFETCH, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_USEDECLAREFETCH, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->unknown_sizes);
-	if (!SQLWritePrivateProfileString(sectionName, INI_UNKNOWNSIZES, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_UNKNOWNSIZES, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->text_as_longvarchar);
-	if (!SQLWritePrivateProfileString(sectionName, INI_TEXTASLONGVARCHAR, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_TEXTASLONGVARCHAR, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->unknowns_as_longvarchar);
-	if (!SQLWritePrivateProfileString(sectionName, INI_UNKNOWNSASLONGVARCHAR, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_UNKNOWNSASLONGVARCHAR, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->bools_as_char);
-	if (!SQLWritePrivateProfileString(sectionName, INI_BOOLSASCHAR, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_BOOLSASCHAR, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->parse);
-	if (!SQLWritePrivateProfileString(sectionName, INI_PARSE, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_PARSE, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->cancel_as_freestmt);
-	if (!SQLWritePrivateProfileString(sectionName, INI_CANCELASFREESTMT, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_CANCELASFREESTMT, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->max_varchar_size);
-	if (!SQLWritePrivateProfileString(sectionName, INI_MAXVARCHARSIZE, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_MAXVARCHARSIZE, tmp, fileName))
 		errc--;
 
 	sprintf(tmp, "%d", comval->max_longvarchar_size);
-	if (!SQLWritePrivateProfileString(sectionName, INI_MAXLONGVARCHARSIZE, tmp, fileName))
+	if (!writePrivateProfileString(sectionName, INI_MAXLONGVARCHARSIZE, tmp, fileName))
 		errc--;
 
-	if (!SQLWritePrivateProfileString(sectionName, INI_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes, fileName))
+	if (!writePrivateProfileString(sectionName, INI_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes, fileName))
 		errc--;
 
 	/*
@@ -1147,136 +1216,54 @@ writeDSNinfo(const ConnInfo *ci)
 	char		encoded_item[LARGE_REGISTRY_LEN],
 				temp[SMALL_REGISTRY_LEN];
 
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_KDESC,
-								 ci->desc,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_DATABASE,
-								 ci->database,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_SERVER,
-								 ci->server,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_PORT,
-								 ci->port,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_USERNAME,
-								 ci->username,
-								 ODBC_INI);
-	SQLWritePrivateProfileString(DSN, INI_UID, ci->username, ODBC_INI);
-
+	writeDSNOptionString(DSN, INI_KDESC, ci->desc);
+	writeDSNOptionString(DSN, INI_DATABASE, ci->database);
+	writeDSNOptionString(DSN, INI_SERVER, ci->server);
+	writeDSNOptionString(DSN, INI_PORT, ci->port);
+	writeDSNOptionString(DSN, INI_USERNAME, ci->username);
+	writeDSNOptionString(DSN, INI_UID, ci->username);
 	encode(ci->password, encoded_item, sizeof(encoded_item));
-	SQLWritePrivateProfileString(DSN,
-								 INI_PASSWORD,
-								 encoded_item,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_READONLY,
-								 ci->onlyread,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_SHOWOIDCOLUMN,
-								 ci->show_oid_column,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_FAKEOIDINDEX,
-								 ci->fake_oid_index,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_ROWVERSIONING,
-								 ci->row_versioning,
-								 ODBC_INI);
-
-	SQLWritePrivateProfileString(DSN,
-								 INI_SHOWSYSTEMTABLES,
-								 ci->show_system_tables,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_PASSWORD, encoded_item);
+	writeDSNOptionString(DSN, INI_READONLY, ci->onlyread);
+	writeDSNOptionString(DSN, INI_SHOWOIDCOLUMN, ci->show_oid_column);
+	writeDSNOptionString(DSN, INI_FAKEOIDINDEX, ci->fake_oid_index);
+	writeDSNOptionString(DSN, INI_ROWVERSIONING, ci->row_versioning);
+	writeDSNOptionString(DSN, INI_SHOWSYSTEMTABLES, ci->show_system_tables);
 
 	if (ci->rollback_on_error >= 0)
 		sprintf(temp, "%s-%d", ci->protocol, ci->rollback_on_error);
 	else
 		strncpy_null(temp, ci->protocol, sizeof(temp));
-	SQLWritePrivateProfileString(DSN,
-								 INI_PROTOCOL,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_PROTOCOL, temp);
 
 	encode(ci->conn_settings, encoded_item, sizeof(encoded_item));
-	SQLWritePrivateProfileString(DSN,
-								 INI_CONNSETTINGS,
-								 encoded_item,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_CONNSETTINGS, encoded_item);
 
 	sprintf(temp, "%d", ci->disallow_premature);
-	SQLWritePrivateProfileString(DSN,
-								 INI_DISALLOWPREMATURE,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_DISALLOWPREMATURE, temp);
 	sprintf(temp, "%d", ci->allow_keyset);
-	SQLWritePrivateProfileString(DSN,
-								 INI_UPDATABLECURSORS,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_UPDATABLECURSORS, temp);
 	sprintf(temp, "%d", ci->lf_conversion);
-	SQLWritePrivateProfileString(DSN,
-								 INI_LFCONVERSION,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_LFCONVERSION, temp);
 	sprintf(temp, "%d", ci->true_is_minus1);
-	SQLWritePrivateProfileString(DSN,
-								 INI_TRUEISMINUS1,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_TRUEISMINUS1, temp);
 	sprintf(temp, "%d", ci->int8_as);
-	SQLWritePrivateProfileString(DSN,
-								 INI_INT8AS,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_INT8AS, temp);
 	sprintf(temp, "%x", getExtraOptions(ci));
-	SQLWritePrivateProfileString(DSN,
-							INI_EXTRAOPTIONS,
-							 temp,
-							 ODBC_INI);
+	writeDSNOptionString(DSN, INI_EXTRAOPTIONS, temp);
 	sprintf(temp, "%d", ci->bytea_as_longvarbinary);
-	SQLWritePrivateProfileString(DSN,
-								 INI_BYTEAASLONGVARBINARY,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_BYTEAASLONGVARBINARY, temp);
 	sprintf(temp, "%d", ci->use_server_side_prepare);
-	SQLWritePrivateProfileString(DSN,
-								 INI_USESERVERSIDEPREPARE,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_USESERVERSIDEPREPARE, temp);
 	sprintf(temp, "%d", ci->lower_case_identifier);
-	SQLWritePrivateProfileString(DSN,
-								 INI_LOWERCASEIDENTIFIER,
-								 temp,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_LOWERCASEIDENTIFIER, temp);
 	sprintf(temp, "%d", ci->gssauth_use_gssapi);
-	SQLWritePrivateProfileString(DSN,
-								 INI_GSSAUTHUSEGSSAPI,
-								 temp,
-								 ODBC_INI);
-	SQLWritePrivateProfileString(DSN,
-								 INI_SSLMODE,
-								 ci->sslmode,
-								 ODBC_INI);
+	writeDSNOptionString(DSN, INI_GSSAUTHUSEGSSAPI, temp);
+	writeDSNOptionString(DSN, INI_SSLMODE, ci->sslmode);
+
 #ifdef	_HANDLE_ENLIST_IN_DTC_
 	sprintf(temp, "%d", ci->xa_opt);
-	SQLWritePrivateProfileString(DSN, INI_XAOPT, temp, ODBC_INI);
+	writeDSNOptionString(DSN, INI_XAOPT, temp);
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
 }
 
@@ -1300,8 +1287,8 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 	else
 		comval = &globals;
 	/* Fetch Count is stored in driver section */
-	SQLGetPrivateProfileString(section, INI_FETCH, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_FETCH, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 	{
 		comval->fetch_max = atoi(temp);
@@ -1313,24 +1300,24 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->fetch_max = FETCH_MAX;
 
 	/* Socket Buffersize is stored in driver section */
-	SQLGetPrivateProfileString(section, INI_SOCKET, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_SOCKET, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->socket_buffersize = atoi(temp);
 	else if (inst_position)
 		comval->socket_buffersize = SOCK_BUFFER_SIZE;
 
 	/* Debug is stored in the driver section */
-	SQLGetPrivateProfileString(section, INI_DEBUG, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_DEBUG, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->debug = atoi(temp);
 	else if (inst_position)
 		comval->debug = DEFAULT_DEBUG;
 
 	/* CommLog is stored in the driver section */
-	SQLGetPrivateProfileString(section, INI_COMMLOG, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_COMMLOG, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->commlog = atoi(temp);
 	else if (inst_position)
@@ -1339,24 +1326,24 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 	if (!ci)
 		logs_on_off(0, 0, 0);
 	/* Optimizer is stored in the driver section only */
-	SQLGetPrivateProfileString(section, INI_OPTIMIZER, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_OPTIMIZER, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->disable_optimizer = atoi(temp);
 	else if (inst_position)
 		comval->disable_optimizer = DEFAULT_OPTIMIZER;
 
 	/* KSQO is stored in the driver section only */
-	SQLGetPrivateProfileString(section, INI_KSQO, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_KSQO, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->ksqo = atoi(temp);
 	else if (inst_position)
 		comval->ksqo = DEFAULT_KSQO;
 
 	/* Recognize Unique Index is stored in the driver section only */
-	SQLGetPrivateProfileString(section, INI_UNIQUEINDEX, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_UNIQUEINDEX, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->unique_index = atoi(temp);
 	else if (inst_position)
@@ -1364,8 +1351,8 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 
 
 	/* Unknown Sizes is stored in the driver section only */
-	SQLGetPrivateProfileString(section, INI_UNKNOWNSIZES, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_UNKNOWNSIZES, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->unknown_sizes = atoi(temp);
 	else if (inst_position)
@@ -1373,23 +1360,23 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 
 
 	/* Lie about supported functions? */
-	SQLGetPrivateProfileString(section, INI_LIE, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_LIE, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->lie = atoi(temp);
 	else if (inst_position)
 		comval->lie = DEFAULT_LIE;
 
 	/* Parse statements */
-	SQLGetPrivateProfileString(section, INI_PARSE, "",
-							   temp, sizeof(temp), filename);
+	getPrivateProfileString(section, INI_PARSE, "",
+							temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->parse = atoi(temp);
 	else if (inst_position)
 		comval->parse = DEFAULT_PARSE;
 
 	/* SQLCancel calls SQLFreeStmt in Driver Manager */
-	SQLGetPrivateProfileString(section, INI_CANCELASFREESTMT, "",
+	getPrivateProfileString(section, INI_CANCELASFREESTMT, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->cancel_as_freestmt = atoi(temp);
@@ -1397,7 +1384,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
 
 	/* UseDeclareFetch is stored in the driver section only */
-	SQLGetPrivateProfileString(section, INI_USEDECLAREFETCH, "",
+	getPrivateProfileString(section, INI_USEDECLAREFETCH, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->use_declarefetch = atoi(temp);
@@ -1405,7 +1392,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->use_declarefetch = DEFAULT_USEDECLAREFETCH;
 
 	/* Max Varchar Size */
-	SQLGetPrivateProfileString(section, INI_MAXVARCHARSIZE, "",
+	getPrivateProfileString(section, INI_MAXVARCHARSIZE, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->max_varchar_size = atoi(temp);
@@ -1413,7 +1400,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->max_varchar_size = MAX_VARCHAR_SIZE;
 
 	/* Max TextField Size */
-	SQLGetPrivateProfileString(section, INI_MAXLONGVARCHARSIZE, "",
+	getPrivateProfileString(section, INI_MAXLONGVARCHARSIZE, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->max_longvarchar_size = atoi(temp);
@@ -1421,7 +1408,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->max_longvarchar_size = TEXT_FIELD_SIZE;
 
 	/* Text As LongVarchar	*/
-	SQLGetPrivateProfileString(section, INI_TEXTASLONGVARCHAR, "",
+	getPrivateProfileString(section, INI_TEXTASLONGVARCHAR, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->text_as_longvarchar = atoi(temp);
@@ -1429,7 +1416,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->text_as_longvarchar = DEFAULT_TEXTASLONGVARCHAR;
 
 	/* Unknowns As LongVarchar	*/
-	SQLGetPrivateProfileString(section, INI_UNKNOWNSASLONGVARCHAR, "",
+	getPrivateProfileString(section, INI_UNKNOWNSASLONGVARCHAR, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->unknowns_as_longvarchar = atoi(temp);
@@ -1437,7 +1424,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		comval->unknowns_as_longvarchar = DEFAULT_UNKNOWNSASLONGVARCHAR;
 
 	/* Bools As Char */
-	SQLGetPrivateProfileString(section, INI_BOOLSASCHAR, "",
+	getPrivateProfileString(section, INI_BOOLSASCHAR, "",
 							   temp, sizeof(temp), filename);
 	if (temp[0])
 		comval->bools_as_char = atoi(temp);
@@ -1450,7 +1437,7 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 	 * Use @@@ to distinguish between blank extra prefixes and no key
 	 * entry
 	 */
-	SQLGetPrivateProfileString(section, INI_EXTRASYSTABLEPREFIXES, "@@@",
+	getPrivateProfileString(section, INI_EXTRASYSTABLEPREFIXES, "@@@",
 							   temp, sizeof(temp), filename);
 	if (strcmp(temp, "@@@"))
 		strcpy(comval->extra_systable_prefixes, temp);
@@ -1469,14 +1456,14 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		 * ConnSettings is stored in the driver section and per datasource
 		 * for override
 		 */
-		SQLGetPrivateProfileString(section, INI_CONNSETTINGS, "",
-			conn_settings, sizeof(conn_settings), filename);
+		getPrivateProfileString(section, INI_CONNSETTINGS, "",
+								conn_settings, sizeof(conn_settings), filename);
 		if ('\0' != conn_settings[0])
 			STRX_TO_NAME(comval->conn_settings, conn_settings);
 
 		/* Default state for future DSN's Readonly attribute */
-		SQLGetPrivateProfileString(section, INI_READONLY, "",
-								   temp, sizeof(temp), filename);
+		getPrivateProfileString(section, INI_READONLY, "",
+								temp, sizeof(temp), filename);
 		if (temp[0])
 			comval->onlyread = atoi(temp);
 		else
@@ -1487,8 +1474,8 @@ getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 		 * real driver option YET.	This is more intended for
 		 * customization from the install.
 		 */
-		SQLGetPrivateProfileString(section, INI_PROTOCOL, "@@@",
-								   temp, sizeof(temp), filename);
+		getPrivateProfileString(section, INI_PROTOCOL, "@@@",
+								temp, sizeof(temp), filename);
 		if (strcmp(temp, "@@@"))
 			strncpy_null(comval->protocol, temp, sizeof(comval->protocol));
 		else
diff --git a/dlg_specific.h b/dlg_specific.h
index c73bb64..bc91857 100644
--- a/dlg_specific.h
+++ b/dlg_specific.h
@@ -257,7 +257,7 @@ void		getCommonDefaults(const char *section, const char *filename, ConnInfo *ci)
 
 #ifdef WIN32
 void		SetDlgStuff(HWND hdlg, const ConnInfo *ci);
-void		GetDlgStuff(HWND hdlg, ConnInfo *ci);
+void		GetDlgStuff(HWND hdlg, ConnInfo *ci, BOOL getDsn);
 
 LRESULT CALLBACK driver_optionsProc(HWND hdlg,
 				   UINT wMsg,
diff --git a/dlg_wingui.c b/dlg_wingui.c
index 072f542..f60a7f8 100644
--- a/dlg_wingui.c
+++ b/dlg_wingui.c
@@ -34,6 +34,8 @@ extern HINSTANCE NEAR s_hModule;
 static int	driver_optionsDraw(HWND, const ConnInfo *, int src, BOOL enable);
 static int	driver_options_update(HWND hdlg, ConnInfo *ci, const char *);
 
+static void SetTextFromResource(HWND hdlg, UINT uID, char *str);
+
 static struct {
 	int	ids;
 	const char * const	modestr;
@@ -47,6 +49,52 @@ static struct {
 	};
 static int	dspcount_bylevel[] = {1, 4, 6};
 
+
+/*
+ * Read a text field using GetDlgItemText[W].
+ *
+ * In Unicode driver, calls GetDlgItemTextW and converts the string to UTF-8.
+ * In ANSI driver, calls GetDlgItemText.
+ */
+static int
+GetDlgItemTextNative(HWND hdlg, int nIDDlgItem, LPTSTR lpString, int nMaxCount)
+{
+#ifdef UNICODE_SUPPORT
+	SQLWCHAR	buf_ucs2[LARGE_REGISTRY_LEN];
+	char	   *buf_utf8;
+
+	GetDlgItemTextW(hdlg, nIDDlgItem, buf_ucs2, LARGE_REGISTRY_LEN);
+	buf_utf8 = ucs2_to_utf8(buf_ucs2, SQL_NTS, NULL, FALSE);
+	if (buf_utf8)
+	{
+		strncpy_null(lpString, buf_utf8, nMaxCount);
+		free(buf_utf8);
+		return (int) strlen(lpString);
+	}
+	else
+		return 0;
+#else
+	return GetDlgItemText(hdlg, nIDDlgItem, lpString, nMaxCount);
+#endif
+}
+
+static int
+SetDlgItemTextNative(HWND hdlg, int nIDDlgItem, const char *lpString)
+{
+#ifdef UNICODE_SUPPORT
+	SQLWCHAR	buf_ucs2[LARGE_REGISTRY_LEN];
+
+	(void) utf8_to_ucs2(lpString, SQL_NTS, buf_ucs2, LARGE_REGISTRY_LEN);
+
+	return SetDlgItemTextW(hdlg, nIDDlgItem, buf_ucs2);
+#else
+	return SetDlgItemText(hdlg, nIDDlgItem, lpString);
+#endif
+}
+
+/*
+ * Fill in all dialog edit boxes using information from ConnInfo.
+ */
 void
 SetDlgStuff(HWND hdlg, const ConnInfo *ci)
 {
@@ -58,14 +106,14 @@ SetDlgStuff(HWND hdlg, const ConnInfo *ci)
 	 * If driver attribute NOT present, then set the datasource name and
 	 * description
 	 */
-	SetDlgItemText(hdlg, IDC_DSNAME, ci->dsn);
-	SetDlgItemText(hdlg, IDC_DESC, ci->desc);
+	SetDlgItemTextNative(hdlg, IDC_DSNAME, ci->dsn);
+	SetDlgItemTextNative(hdlg, IDC_DESC, ci->desc);
 
-	SetDlgItemText(hdlg, IDC_DATABASE, ci->database);
-	SetDlgItemText(hdlg, IDC_SERVER, ci->server);
-	SetDlgItemText(hdlg, IDC_USER, ci->username);
-	SetDlgItemText(hdlg, IDC_PASSWORD, SAFE_NAME(ci->password));
-	SetDlgItemText(hdlg, IDC_PORT, ci->port);
+	SetDlgItemTextNative(hdlg, IDC_DATABASE, ci->database);
+	SetDlgItemTextNative(hdlg, IDC_SERVER, ci->server);
+	SetDlgItemTextNative(hdlg, IDC_USER, ci->username);
+	SetDlgItemTextNative(hdlg, IDC_PASSWORD, SAFE_NAME(ci->password));
+	SetDlgItemTextNative(hdlg, IDC_PORT, ci->port);
 
 	dsplevel = 0;
 #ifdef USE_LIBPQ
@@ -114,20 +162,28 @@ mylog("SendMessage CTL_COLOR\n");
 }
 
 
+/*
+ * Set fields fields in ConnInfo, using the curreny values in the edit boxes
+ * in the dialog. If 'getDSN' is TRUE, also the DSN field is copied, otherwise
+ * it is not.
+ */
 void
-GetDlgStuff(HWND hdlg, ConnInfo *ci)
+GetDlgStuff(HWND hdlg, ConnInfo *ci, BOOL getDsn)
 {
-	int	sslposition;
-	char	medium_buf[MEDIUM_REGISTRY_LEN];
+	int			sslposition;
+	char		medium_buf[MEDIUM_REGISTRY_LEN];
 
-	GetDlgItemText(hdlg, IDC_DESC, ci->desc, sizeof(ci->desc));
+	if (getDsn)
+		GetDlgItemTextNative(hdlg, IDC_DSNAME, ci->dsn, sizeof(ci->dsn));
 
-	GetDlgItemText(hdlg, IDC_DATABASE, ci->database, sizeof(ci->database));
-	GetDlgItemText(hdlg, IDC_SERVER, ci->server, sizeof(ci->server));
-	GetDlgItemText(hdlg, IDC_USER, ci->username, sizeof(ci->username));
-	GetDlgItemText(hdlg, IDC_PASSWORD, medium_buf, sizeof(medium_buf));
+	GetDlgItemTextNative(hdlg, IDC_DESC, ci->desc, sizeof(ci->desc));
+
+	GetDlgItemTextNative(hdlg, IDC_DATABASE, ci->database, sizeof(ci->database));
+	GetDlgItemTextNative(hdlg, IDC_SERVER, ci->server, sizeof(ci->server));
+	GetDlgItemTextNative(hdlg, IDC_USER, ci->username, sizeof(ci->username));
+	GetDlgItemTextNative(hdlg, IDC_PASSWORD, medium_buf, sizeof(medium_buf));
 	STR_TO_NAME(ci->password, medium_buf);
-	GetDlgItemText(hdlg, IDC_PORT, ci->port, sizeof(ci->port));
+	GetDlgItemTextNative(hdlg, IDC_PORT, ci->port, sizeof(ci->port));
 	sslposition = (int)(DWORD)SendMessage(GetDlgItem(hdlg, IDC_SSLMODE), CB_GETCURSEL, 0L, 0L);
 	strncpy_null(ci->sslmode, modetab[sslposition].modestr, sizeof(ci->sslmode));
 }
@@ -217,10 +273,10 @@ driver_optionsDraw(HWND hdlg, const ConnInfo *ci, int src, BOOL enable)
 	SetDlgItemInt(hdlg, DRV_CACHE_SIZE, comval->fetch_max, FALSE);
 	SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, comval->max_varchar_size, FALSE);
 	SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, comval->max_longvarchar_size, TRUE);
-	SetDlgItemText(hdlg, DRV_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes);
+	SetDlgItemTextNative(hdlg, DRV_EXTRASYSTABLEPREFIXES, comval->extra_systable_prefixes);
 
 	/* Driver Connection Settings */
-	SetDlgItemText(hdlg, DRV_CONNSETTINGS, SAFE_NAME(comval->conn_settings));
+	SetDlgItemTextNative(hdlg, DRV_CONNSETTINGS, SAFE_NAME(comval->conn_settings));
 	EnableWindow(GetDlgItem(hdlg, DRV_CONNSETTINGS), enable);
 	ShowWindow(GetDlgItem(hdlg, IDPREVPAGE), enable ? SW_HIDE : SW_SHOW);
 	ShowWindow(GetDlgItem(hdlg, IDNEXTPAGE), enable ? SW_HIDE : SW_SHOW);
@@ -300,17 +356,15 @@ driver_optionsProc(HWND hdlg,
 				   LPARAM lParam)
 {
 	ConnInfo   *ci;
-	char	strbuf[128];
 
 	switch (wMsg)
 	{
 		case WM_INITDIALOG:
 			SetWindowLongPtr(hdlg, DWLP_USER, lParam);		/* save for OK etc */
 			ci = (ConnInfo *) lParam;
-			LoadString(s_hModule, IDS_ADVANCE_OPTION_DEF, strbuf, sizeof(strbuf));
-			SetWindowText(hdlg, strbuf);
-			LoadString(s_hModule, IDS_ADVANCE_SAVE, strbuf, sizeof(strbuf));
-			SetWindowText(GetDlgItem(hdlg, IDOK), strbuf);
+
+			SetTextFromResource(hdlg, IDS_ADVANCE_OPTION_DEF, NULL);
+			SetTextFromResource(GetDlgItem(hdlg, IDOK), IDS_ADVANCE_SAVE, NULL);
 			ShowWindow(GetDlgItem(hdlg, IDAPPLY), SW_HIDE);
 			driver_optionsDraw(hdlg, ci, 0, TRUE);
 			break;
@@ -421,6 +475,60 @@ global_optionsProc(HWND hdlg,
 	return FALSE;
 }
 
+/*
+ * Set window text 'hdlg' (e.g. window title), using resource uID. If 'str'
+ * is not NULL, the resource string is assumed to be a snprintf format string
+ * containing a single %s, which will be replaced with 'str'. In a Unicode
+ * driver, 'str' is assumed to be UTF-8 encoded.
+ */
+static void
+SetTextFromResource(HWND hdlg, UINT uID, char *str)
+{
+	DWORD	cmd;
+#ifdef UNICODE_SUPPORT
+	WCHAR		strbuf1[128] = L"";
+	WCHAR		strbuf2[256];
+	WCHAR	   *strbuf;
+
+	cmd = LoadStringW(s_hModule, uID, strbuf1, 128);
+
+	if (str && cmd > 0)
+	{
+		SQLWCHAR	buf_ucs2[MEDIUM_REGISTRY_LEN];
+
+		(void) utf8_to_ucs2(str, SQL_NTS, buf_ucs2, MEDIUM_REGISTRY_LEN);
+		MessageBoxW(NULL, buf_ucs2, NULL, MB_OK);
+
+		_snwprintf(strbuf2, sizeof(strbuf2), strbuf1, buf_ucs2);
+		MessageBoxW(NULL, strbuf2, NULL, MB_OK);
+		strbuf = strbuf2;
+	}
+	else
+		strbuf = strbuf1;
+
+	SetWindowTextW(hdlg, strbuf);
+	return;
+
+#else
+	char		strbuf1[128] = "";
+	char		strbuf2[256];
+	char	   *strbuf;
+
+	cmd = LoadString(s_hModule, uID, strbuf1, sizeof(strbuf1));
+
+	if (str && cmd > 0)
+	{
+		_snprintf(strbuf2, sizeof(strbuf2), strbuf1, str);
+		strbuf = strbuf2;
+	}
+	else
+		strbuf = strbuf1;
+
+	SetWindowText(hdlg, strbuf);
+	return;
+#endif
+}
+
 LRESULT			CALLBACK
 ds_options1Proc(HWND hdlg,
 				   UINT wMsg,
@@ -428,7 +536,6 @@ ds_options1Proc(HWND hdlg,
 				   LPARAM lParam)
 {
 	ConnInfo   *ci;
-	char	strbuf[128];
 
 	switch (wMsg)
 	{
@@ -436,23 +543,10 @@ ds_options1Proc(HWND hdlg,
 			SetWindowLongPtr(hdlg, DWLP_USER, lParam);		/* save for OK etc */
 			ci = (ConnInfo *) lParam;
 			if (ci && ci->dsn && ci->dsn[0])
-			{
-				DWORD	cmd;
-				char	fbuf[64];
-
-				cmd = LoadString(s_hModule,
-						IDS_ADVANCE_OPTION_DSN1,
-						fbuf,
-						sizeof(fbuf));
-				if (cmd <= 0)
-					strcpy(fbuf, "Advanced Options (%s) 1/2");
-				sprintf(strbuf, fbuf, ci->dsn);
-				SetWindowText(hdlg, strbuf);
-			}
+				SetTextFromResource(hdlg, IDS_ADVANCE_OPTION_DSN1, ci->dsn);
 			else
 			{
-				LoadString(s_hModule, IDS_ADVANCE_OPTION_CON1, strbuf, sizeof(strbuf));
-				SetWindowText(hdlg, strbuf);
+				SetTextFromResource(hdlg, IDS_ADVANCE_OPTION_CON1, NULL);
 				ShowWindow(GetDlgItem(hdlg, IDAPPLY), SW_HIDE);
 			}
 			driver_optionsDraw(hdlg, ci, 1, FALSE);
@@ -512,23 +606,12 @@ ds_options2Proc(HWND hdlg,
 
 			/* Change window caption */
 			if (ci && ci->dsn && ci->dsn[0])
-			{
-				char	fbuf[64];
-
-				cmd = LoadString(s_hModule,
-						IDS_ADVANCE_OPTION_DSN2,
-						fbuf,
-						sizeof(fbuf));
-				if (cmd <= 0)
-					strcpy(fbuf, "Advanced Options (%s) 2/2");
-				sprintf(buf, fbuf, ci->dsn);
-				SetWindowText(hdlg, buf);
-			}
+				SetTextFromResource(hdlg, IDS_ADVANCE_OPTION_DSN2, ci->dsn);
 			else
 			{
-				LoadString(s_hModule, IDS_ADVANCE_OPTION_CON2, buf, sizeof(buf));
-				SetWindowText(hdlg, buf);
-				ShowWindow(GetDlgItem(hdlg, IDAPPLY), SW_HIDE);				}
+				SetTextFromResource(hdlg, IDS_ADVANCE_OPTION_CON2, NULL);
+				ShowWindow(GetDlgItem(hdlg, IDAPPLY), SW_HIDE);
+			}
 
 			/* Readonly */
 			CheckDlgButton(hdlg, DS_READONLY, atoi(ci->onlyread));
@@ -606,7 +689,7 @@ ds_options2Proc(HWND hdlg,
 			EnableWindow(GetDlgItem(hdlg, DS_FAKEOIDINDEX), atoi(ci->show_oid_column));
 
 			/* Datasource Connection Settings */
-			SetDlgItemText(hdlg, DS_CONNSETTINGS, SAFE_NAME(ci->conn_settings));
+			SetDlgItemTextNative(hdlg, DS_CONNSETTINGS, SAFE_NAME(ci->conn_settings));
 			break;
 
 		case WM_COMMAND:
diff --git a/drvconn.c b/drvconn.c
index dcbe9b0..84b4996 100644
--- a/drvconn.c
+++ b/drvconn.c
@@ -394,10 +394,12 @@ dconn_FDriverConnectProc(
 				case IDOK:
 					ci = (ConnInfo *) GetWindowLongPtr(hdlg, DWLP_USER);
 
-					GetDlgStuff(hdlg, ci);
+					GetDlgStuff(hdlg, ci, FALSE);
+					EndDialog(hdlg, TRUE);
+					return TRUE;
 
 				case IDCANCEL:
-					EndDialog(hdlg, GET_WM_COMMAND_ID(wParam, lParam) == IDOK);
+					EndDialog(hdlg, FALSE);
 					return TRUE;
 
 				case IDC_DATASOURCE:
@@ -408,8 +410,14 @@ dconn_FDriverConnectProc(
 
 				case IDC_DRIVER:
 					ci = (ConnInfo *) GetWindowLongPtr(hdlg, DWLP_USER);
+#ifdef UNICODE_SUPPORT
+					DialogBoxParamW(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
+									hdlg, driver_optionsProc, (LPARAM) ci);
+#else
 					DialogBoxParam(s_hModule, MAKEINTRESOURCE(DLG_OPTIONS_DRV),
 								   hdlg, driver_optionsProc, (LPARAM) ci);
+#endif
+
 					break;
 			}
 	}
diff --git a/psqlodbc.def b/psqlodbc.def
index 4f4a327..062c040 100644
--- a/psqlodbc.def
+++ b/psqlodbc.def
@@ -76,7 +76,7 @@ SQLBulkOperations @100
 SQLDummyOrdinal @199
 dconn_FDriverConnectProc @200
 DllMain @201
-ConfigDSN @202
+;ConfigDSN @202
 ConfigDriver @203
 
 SQLColAttributeW	@101
@@ -107,3 +107,5 @@ SQLSetStmtAttrW		@126
 SQLSetDescFieldW	@127
 SQLGetTypeInfoW		@128
 SQLGetDiagFieldW	@129
+
+ConfigDSNW		@302
diff --git a/setup.c b/setup.c
index 0956bd2..9df597c 100644
--- a/setup.c
+++ b/setup.c
@@ -7,7 +7,7 @@
  *
  * Classes:			n/a
  *
- * API functions:	ConfigDSN, ConfigDriver
+ * API functions:	ConfigDSN, ConfigDSNW, ConfigDriver
  *
  * Comments:		See "readme.txt" for copyright and license information.
  *-------
@@ -27,6 +27,14 @@
 #include  "win_setup.h"
 
 
+static PGAPI_ConfigDSN(HWND hwnd, WORD fRequest,
+					   const char *lpszDriver, const char **attrs, int nattrs);
+
+static void ParseAttributes(const char **attrs, int nattrs, LPSETUPDLG lpsetupdlg);
+static BOOL SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg, DWORD *errcode);
+
+static LRESULT CALLBACK ConfigDlgProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
+
 #define INTFUNC  __stdcall
 
 extern HINSTANCE NEAR s_hModule;	/* Saved module handle. */
@@ -39,6 +47,7 @@ extern GLOBAL_VALUES	globals;
 #define MAXDESC			(255+1) /* Max description length */
 #define MAXDSNAME		(32+1)	/* Max data source name length */
 
+#define MAX_CONFIG_ATTRIBUTES 100
 
 /*--------
  *	ConfigDSN
@@ -49,16 +58,96 @@ extern GLOBAL_VALUES	globals;
  *	Input	 :	hwnd ----------- Parent window handle
  *				fRequest ------- Request type (i.e., add, config, or remove)
  *				lpszDriver ----- Driver name
- *				lpszAttributes - data source attribute string
+ *				lpszAttributes - data source attribute string, as a doubly
+ *                               NULL-terminated list of strings
  *	Output	 :	TRUE success, FALSE otherwise
  *--------
  */
-BOOL		CALLBACK
+BOOL CALLBACK
 ConfigDSN(HWND hwnd,
 		  WORD fRequest,
 		  LPCSTR lpszDriver,
 		  LPCSTR lpszAttributes)
 {
+	const char	   *attrs[MAX_CONFIG_ATTRIBUTES];
+	int			nattrs;
+	size_t		len;
+
+	for (nattrs = 0; nattrs < MAX_CONFIG_ATTRIBUTES && *lpszAttributes != 0;)
+	{
+		char buf[200];
+		len = strlen(lpszAttributes);
+		attrs[nattrs] = lpszAttributes;
+		lpszAttributes += len + 1;
+		sprintf(buf, "val: %s len: %d next: %s",
+				attrs[nattrs], len, lpszAttributes);
+		nattrs++;
+	}
+
+	return PGAPI_ConfigDSN(hwnd, fRequest, lpszDriver, attrs, nattrs);
+}
+
+#ifdef UNICODE_SUPPORT
+BOOL CALLBACK
+ConfigDSNW(HWND hwnd,
+		   WORD fRequest,
+		   LPCWSTR lpszDriver,
+		   LPCWSTR lpszAttributes)
+{
+	char	   *szDriverIn;
+	char	   *attrs[MAX_CONFIG_ATTRIBUTES];
+	int			nattrs;
+	SQLULEN		len;
+	BOOL		result;
+	int			i;
+
+	szDriverIn = ucs2_to_utf8(lpszDriver, SQL_NTS, NULL, FALSE);
+	if (!szDriverIn)
+		return FALSE;
+
+	for (nattrs = 0; nattrs < MAX_CONFIG_ATTRIBUTES && *lpszAttributes != 0;)
+	{
+		len = ucs2strlen(lpszAttributes);
+		attrs[nattrs] = ucs2_to_utf8(lpszAttributes, len, NULL, FALSE);
+		lpszAttributes += len + 1;
+		if (attrs[nattrs])
+			nattrs++;
+	}
+
+	result = PGAPI_ConfigDSN(hwnd, fRequest, szDriverIn, attrs, nattrs);
+
+	for (i = 0; i < nattrs; i++)
+		free(attrs[i]);
+	free(szDriverIn);
+
+	return result;
+}
+#endif
+
+/*--------
+ *	PGAPI_ConfigDSN
+ *
+ * Internal function implementing SQLConfigDSN API function. The API
+ * differs in that the caller passes the attributes as an array of strings.
+ *
+ *	Description:	ODBC Setup entry point
+ *				This entry point is called by the ODBC Installer
+ *				(see file header for more details)
+ *	Input	 :	hwnd ----------- Parent window handle
+ *				fRequest ------- Request type (i.e., add, config, or remove)
+ *				lpszDriver ----- Driver name
+ *				attrs ---------- data source attribute strings
+ *				nattrs --------- number of attributes in 'attrs' array
+ *	Output	 :	TRUE success, FALSE otherwise
+ *--------
+ */
+static BOOL
+PGAPI_ConfigDSN(HWND hwnd,
+				WORD fRequest,
+				const char *lpszDriver,
+				const char **attrs,
+				int nattrs)
+{
 	BOOL		fSuccess;		/* Success/fail flag */
 	GLOBALHANDLE hglbAttr;
 	LPSETUPDLG	lpsetupdlg;
@@ -70,8 +159,8 @@ ConfigDSN(HWND hwnd,
 		return FALSE;
 	lpsetupdlg = (LPSETUPDLG) GlobalLock(hglbAttr);
 	/* Parse attribute string */
-	if (lpszAttributes)
-		ParseAttributes(lpszAttributes, lpsetupdlg);
+	if (nattrs > 0)
+		ParseAttributes(attrs, nattrs, lpsetupdlg);
 
 	/* Save original data source name */
 	if (lpsetupdlg->ci.dsn[0])
@@ -190,7 +279,7 @@ ConfigDriver(HWND hwnd,
  *		Output	   :  None
  *-------
  */
-void		INTFUNC
+static void
 CenterDialog(HWND hdlg)
 {
 	HWND		hwndFrame;
@@ -245,7 +334,7 @@ CenterDialog(HWND hdlg)
  *	Output	 :	TRUE if message processed, FALSE otherwise
  *-------
  */
-LRESULT			CALLBACK
+static LRESULT			CALLBACK
 ConfigDlgProc(HWND hdlg,
 			  UINT wMsg,
 			  WPARAM wParam,
@@ -313,12 +402,13 @@ ConfigDlgProc(HWND hdlg,
 				case IDC_DSNAME:
 					if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
 					{
-						char		szItem[MAXDSNAME];	/* Edit control text */
+						SQLWCHAR	szItem[MAXDSNAME];	/* Edit control text */
+						BOOL		enabled;
 
 						/* Enable/disable the OK button */
-						EnableWindow(GetDlgItem(hdlg, IDOK),
-									 GetDlgItemText(hdlg, IDC_DSNAME,
-												szItem, sizeof(szItem)));
+						enabled = (GetDlgItemTextW(hdlg, IDC_DSNAME,
+												   szItem, MAXDSNAME) != 0);
+						EnableWindow(GetDlgItem(hdlg, IDOK), enabled);
 						return TRUE;
 					}
 					break;
@@ -328,12 +418,7 @@ ConfigDlgProc(HWND hdlg,
 				case IDAPPLY:
 					lpsetupdlg = (LPSETUPDLG) GetWindowLongPtr(hdlg, DWLP_USER);
 					/* Retrieve dialog values */
-					if (!lpsetupdlg->fDefault)
-						GetDlgItemText(hdlg, IDC_DSNAME,
-									   lpsetupdlg->ci.dsn,
-									   sizeof(lpsetupdlg->ci.dsn));
-					/* Get Dialog Values */
-					GetDlgStuff(hdlg, &lpsetupdlg->ci);
+					GetDlgStuff(hdlg, &lpsetupdlg->ci, !lpsetupdlg->fDefault);
 
 					/* Update ODBC.INI */
 					SetDSNAttributes(hdlg, lpsetupdlg, NULL);
@@ -354,7 +439,7 @@ ConfigDlgProc(HWND hdlg,
 						char    szMsg[SQL_MAX_MESSAGE_LENGTH];
 
 						/* Get Dialog Values */
-						GetDlgStuff(hdlg, &lpsetupdlg->ci);
+						GetDlgStuff(hdlg, &lpsetupdlg->ci, FALSE);
 						if (env)
 							conn = CC_Constructor();
 						if (conn)
@@ -461,24 +546,25 @@ ConfigDlgProc(HWND hdlg,
  *	Output	 :	None (global aAttr normally updated)
  *-------
  */
-void		INTFUNC
-ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
+static void
+ParseAttributes(const char **attrs, int nattrs, LPSETUPDLG lpsetupdlg)
 {
 	LPCSTR		lpsz;
 	LPCSTR		lpszStart;
 	char		aszKey[MAXKEYLEN];
 	int			cbKey;
 	char		value[MAXPGPATH];
+	int			i;
 
 	CC_conninfo_init(&(lpsetupdlg->ci), COPY_GLOBALS);
 
-	for (lpsz = lpszAttributes; *lpsz; lpsz++)
+	for (i = 0; i < nattrs; i++)
 	{
 		/*
 		 * Extract key name (e.g., DSN), it must be terminated by an
 		 * equals
 		 */
-		lpszStart = lpsz;
+		lpsz = lpszStart = attrs[i];
 		for (;; lpsz++)
 		{
 			if (!*lpsz)
@@ -511,6 +597,21 @@ ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
 	return;
 }
 
+static BOOL
+writeDSNToIni(const char *dsn, const char *driver)
+{
+#ifdef UNICODE_SUPPORT
+	SQLWCHAR	wdsn[MEDIUM_REGISTRY_LEN];
+	SQLWCHAR	wdriver[MEDIUM_REGISTRY_LEN];
+
+	(void) utf8_to_ucs2(dsn, SQL_NTS, wdsn, SMALL_REGISTRY_LEN);
+	(void) utf8_to_ucs2(driver, SQL_NTS, wdriver, MEDIUM_REGISTRY_LEN);
+
+	return SQLWriteDSNToIniW(wdsn, wdriver);
+#else
+	return SQLWriteDSNToIni(dsn, driver);
+#endif
+}
 
 /*--------
  * SetDSNAttributes
@@ -520,7 +621,7 @@ ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
  *	Output	 :	TRUE if successful, FALSE otherwise
  *--------
  */
-BOOL		INTFUNC
+static BOOL
 SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg, DWORD *errcode)
 {
 	LPCSTR		lpszDSN;		/* Pointer to data source name */
@@ -534,7 +635,7 @@ SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg, DWORD *errcode)
 		return FALSE;
 
 	/* Write the data source name */
-	if (!SQLWriteDSNToIni(lpszDSN, lpsetupdlg->lpszDrvr))
+	if (!writeDSNToIni(lpszDSN, lpsetupdlg->lpszDrvr))
 	{
 		RETCODE	ret = SQL_ERROR;
 		DWORD	err = SQL_ERROR;
@@ -620,9 +721,6 @@ cleanup:
 	return ret;
 }
 
-
-#ifdef	WIN32
-
 BOOL	INTFUNC
 ChangeDriverName(HWND hwndParent, LPSETUPDLG lpsetupdlg, LPCSTR driver_name)
 {
@@ -651,5 +749,3 @@ ChangeDriverName(HWND hwndParent, LPSETUPDLG lpsetupdlg, LPCSTR driver_name)
 	}
 	return (err == 0);
 }
-
-#endif /* WIN32 */
diff --git a/win_setup.h b/win_setup.h
index 8638a69..703b81e 100644
--- a/win_setup.h
+++ b/win_setup.h
@@ -19,10 +19,6 @@ typedef struct tagSETUPDLG
 }	SETUPDLG, FAR * LPSETUPDLG;
 
 /* Prototypes */
-void INTFUNC CenterDialog(HWND hdlg);
-LRESULT CALLBACK ConfigDlgProc(HWND hdlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
-void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg);
-BOOL INTFUNC SetDSNAttributes(HWND hwnd, LPSETUPDLG lpsetupdlg, DWORD *);
 BOOL INTFUNC ChangeDriverName(HWND hwnd, LPSETUPDLG lpsetupdlg, LPCSTR driver_name);
 
 #endif /* _WIN_SETUP_H__ */
-- 
2.0.0

