ODBC 驱动程序字符串转换取决于控件 Panel/Regional 设置
OBDC driver string translation depends on Control Panel/Regional Settings
我有一个 C++ 程序,它使用“SQL 服务器”驱动程序连接到 MSSQL 数据库。
它 inserts/reads 一个 UTF-8 编码的字符串 to/from 服务器上的一个 varchar
字段。此操作完全有效
在特定区域设置的情况下。否则 UTF-8 连续字节将被 ODBC 驱动程序剥离。
我想实现一个解决方案,它不依赖于 Windows OS.
的区域设置
插入是这样的:
const char chineseStr[] = "\xE5\x8A\xA0\xE6\xB2\xB9\xE7\xAB\x99";
... // some logic
... // pass chineseStr gdal111.dll (Geospatial Data Abstraction Library)
... // it generates "INSERT INTO ..." query string and after that executes it:
SQLExecDirect(...) // at this point \xE5 is preserved, it was not stripped to \x61
在此调用“SQL 服务器”驱动程序后,将数据发送到服务器。原本错误的结果出现在
数据库:\x61\x8A\xA0\x61\x32\x31\xE7\xAB\x99
。但我知道完全相同的程序效果很好
在其他客户端环境中。在调试 运行ning 进程的注册表访问之后,我猜到了
加载的代码页有问题,因此我在控制面板中更改了此设置:
区域和语言/管理/非 Unicode 程序的语言。它是“英语(美国)”
我已将其更改为“Hungarian(匈牙利)”并按要求重新启动计算机。归根结底,
一切正常,所以我的程序可以正确读写 UTF-8 字段,并且数据库
表述也正确。
其他可能的解决方案均无效。我试图将“AutoTranslate=no”添加到连接字符串,
并像这样调试:
SQLAllocConnect(...); // we are in gdal111.dll
...
SQLDriverConnect(...) // parameters after return:
//InConnectionString: DRIVER=SQL Server;Server=MyServerName;Database=MY_DATABASE;UID=username;PWD=passw;AutoTranslate=no;
//OutConnectionString: DRIVER=SQL Server;SERVER=MyServerName;UID=username;PWD=passw;WSID=win_username;DATABASE=MY_DATABASE;AutoTranslate=no
基于OutConnectionString
,我假设给定的连接选项已到达驱动程序,但没有任何效果。
与语言环境相关的标准函数(例如:std::setlocale
)没有任何效果。之后我尝试了
GetLocaleInfo 使用此代码调用:
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCOUNTRY, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << std::endl;
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTLANGUAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCOUNTRY, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << std::endl;
在为上面提到的“非 Unicode 程序的语言”设置不同的语言后,我运行它:
English (United States) // doesn't work
040e,36,852,1250,10029
0409,1,437,1252,10000
Hungarian (Hungary) // <-- insert/read WORKS (latin2)
040e,36,852,1250,10029
040e,36,852,1250,10029
German (Germany) // doesn't work
040e,36,852,1250,10029
0407,49,850,1252,10000
Greek (Greece) // doesn't work
040e,36,852,1250,10029
0408,30,737,1253,10006
Turkish (Turkey) // doesn't work
040e,36,852,1250,10029
041f,90,857,1254,10081
German (Germany) // +all regional setting set to German in Control Panel --> doesn't work
0407,49,850,1252,10000
0407,49,850,1252,10000
Croatian (Croatia) // <-- insert/read WORKS (latin2)
040e,36,852,1250,10029
041a,385,852,1250,10082
此输出意味着,codepage 852 或 1250 禁用客户端上的字符串转换。我不
理解为什么其他人不会这样工作,以及什么样的代码页差异会导致字符串
翻译。服务器上设置了“English (United States)”,但我认为它不相关。
也许 SetLocaleInfo 可以解决问题,但是当我已经返回 ERROR_INVALID_FLAGS
试图设置系统语言环境。我没有找到这方面的示例代码,无论如何它可能是不允许的。
我如何实施不依赖于控制面板/区域设置的解决方案,以及
不需要重启?或者是否有一个 SQL 驱动程序选项可以完全禁用客户端
字符串翻译?
其他详情:
- OS: Windows 7 SP1
- "SQL 服务器" 驱动程序: 6.01.7601.17514
您必须使用各种 ODBC API 函数的 unicode 版本。
KB294169: DOC: Explanation of Length Arguments for Unicode ODBC Functions 尝试记录一些 Unicode 在 ODBC 中的工作方式的行为 API
The ODBC Driver Manager version 3.5 or later supports both ANSI and Unicode versions of all functions that accept pointers to character strings or SQLPOINTER in their arguments. The Unicode functions are implemented as functions with a suffix of "W", such as SQLExecDirectW and SQLGetInfoW.
例如:
| Ansi | Unicode (Wide) |
|---------------|----------------|
| SQLExecDirect | SQLExecDirectW |
| SQLGetInfo | SQLGetInfoW |
| SQLConnect | SQLConnectW |
您传递每个字符串的 UTF-16 版本的函数,并以 个字符 (不是字节)传递字符串的长度。
您可以在 sqlucode.h
中找到函数
This is the the unicode include for ODBC Core functions
如果您定义 UNICODE
它应该自动重新定义 SQLExecDirect 以使用 SQLExecDirectW:
//---------------------------------------------
// Mapping macros for Unicode
//---------------------------------------------
#ifndef SQL_NOUNICODEMAP // define this to disable the mapping
#ifdef UNICODE
#define SQLExecDirect SQLExecDirectW
我有一个 C++ 程序,它使用“SQL 服务器”驱动程序连接到 MSSQL 数据库。
它 inserts/reads 一个 UTF-8 编码的字符串 to/from 服务器上的一个 varchar
字段。此操作完全有效
在特定区域设置的情况下。否则 UTF-8 连续字节将被 ODBC 驱动程序剥离。
我想实现一个解决方案,它不依赖于 Windows OS.
插入是这样的:
const char chineseStr[] = "\xE5\x8A\xA0\xE6\xB2\xB9\xE7\xAB\x99";
... // some logic
... // pass chineseStr gdal111.dll (Geospatial Data Abstraction Library)
... // it generates "INSERT INTO ..." query string and after that executes it:
SQLExecDirect(...) // at this point \xE5 is preserved, it was not stripped to \x61
在此调用“SQL 服务器”驱动程序后,将数据发送到服务器。原本错误的结果出现在
数据库:\x61\x8A\xA0\x61\x32\x31\xE7\xAB\x99
。但我知道完全相同的程序效果很好
在其他客户端环境中。在调试 运行ning 进程的注册表访问之后,我猜到了
加载的代码页有问题,因此我在控制面板中更改了此设置:
区域和语言/管理/非 Unicode 程序的语言。它是“英语(美国)”
我已将其更改为“Hungarian(匈牙利)”并按要求重新启动计算机。归根结底,
一切正常,所以我的程序可以正确读写 UTF-8 字段,并且数据库
表述也正确。
其他可能的解决方案均无效。我试图将“AutoTranslate=no”添加到连接字符串, 并像这样调试:
SQLAllocConnect(...); // we are in gdal111.dll
...
SQLDriverConnect(...) // parameters after return:
//InConnectionString: DRIVER=SQL Server;Server=MyServerName;Database=MY_DATABASE;UID=username;PWD=passw;AutoTranslate=no;
//OutConnectionString: DRIVER=SQL Server;SERVER=MyServerName;UID=username;PWD=passw;WSID=win_username;DATABASE=MY_DATABASE;AutoTranslate=no
基于OutConnectionString
,我假设给定的连接选项已到达驱动程序,但没有任何效果。
与语言环境相关的标准函数(例如:std::setlocale
)没有任何效果。之后我尝试了
GetLocaleInfo 使用此代码调用:
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTLANGUAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCOUNTRY, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << std::endl;
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTLANGUAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCOUNTRY, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << ",";
GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_IDEFAULTMACCODEPAGE, (LPTSTR)&buf, bufsize); std::wcout << buf << std::endl;
在为上面提到的“非 Unicode 程序的语言”设置不同的语言后,我运行它:
English (United States) // doesn't work
040e,36,852,1250,10029
0409,1,437,1252,10000
Hungarian (Hungary) // <-- insert/read WORKS (latin2)
040e,36,852,1250,10029
040e,36,852,1250,10029
German (Germany) // doesn't work
040e,36,852,1250,10029
0407,49,850,1252,10000
Greek (Greece) // doesn't work
040e,36,852,1250,10029
0408,30,737,1253,10006
Turkish (Turkey) // doesn't work
040e,36,852,1250,10029
041f,90,857,1254,10081
German (Germany) // +all regional setting set to German in Control Panel --> doesn't work
0407,49,850,1252,10000
0407,49,850,1252,10000
Croatian (Croatia) // <-- insert/read WORKS (latin2)
040e,36,852,1250,10029
041a,385,852,1250,10082
此输出意味着,codepage 852 或 1250 禁用客户端上的字符串转换。我不
理解为什么其他人不会这样工作,以及什么样的代码页差异会导致字符串
翻译。服务器上设置了“English (United States)”,但我认为它不相关。
也许 SetLocaleInfo 可以解决问题,但是当我已经返回 ERROR_INVALID_FLAGS
试图设置系统语言环境。我没有找到这方面的示例代码,无论如何它可能是不允许的。
我如何实施不依赖于控制面板/区域设置的解决方案,以及 不需要重启?或者是否有一个 SQL 驱动程序选项可以完全禁用客户端 字符串翻译?
其他详情:
- OS: Windows 7 SP1
- "SQL 服务器" 驱动程序: 6.01.7601.17514
您必须使用各种 ODBC API 函数的 unicode 版本。
KB294169: DOC: Explanation of Length Arguments for Unicode ODBC Functions 尝试记录一些 Unicode 在 ODBC 中的工作方式的行为 API
The ODBC Driver Manager version 3.5 or later supports both ANSI and Unicode versions of all functions that accept pointers to character strings or SQLPOINTER in their arguments. The Unicode functions are implemented as functions with a suffix of "W", such as SQLExecDirectW and SQLGetInfoW.
例如:
| Ansi | Unicode (Wide) |
|---------------|----------------|
| SQLExecDirect | SQLExecDirectW |
| SQLGetInfo | SQLGetInfoW |
| SQLConnect | SQLConnectW |
您传递每个字符串的 UTF-16 版本的函数,并以 个字符 (不是字节)传递字符串的长度。
您可以在 sqlucode.h
This is the the unicode include for ODBC Core functions
如果您定义 UNICODE
它应该自动重新定义 SQLExecDirect 以使用 SQLExecDirectW:
//---------------------------------------------
// Mapping macros for Unicode
//---------------------------------------------
#ifndef SQL_NOUNICODEMAP // define this to disable the mapping
#ifdef UNICODE
#define SQLExecDirect SQLExecDirectW