There are 2 ways to add registry string values. One with a null terminated string, and the other with a buffer and length.
The Oracle Instant Client ODBC "odbc_install.exe" must be using the second method, and providing the full length of the buffer.
I know this because I am reading the ODBC registry using the C# language which doesn't use null terminators for its strings, and when I get the "Driver" value for the "Oracle in instantclient_11_2" ODBC driver and then attempt to use the path, I get an exception stating the path contains illegal values.
The value looked fine in RegEdit, but when I viewed it in Visual Studio it was ~512 characters long, and viewing it showed it full of lots of escaped control characters.
I wrote the little windows console app below in C# to dump all the "BAD" string values in the ODBC registry.
It finds the problem on another system with the newest 12_1 Instant Client as well.
Note, it finds nothing wrong on a system with the full Oracle Client installed with the Universal Installer.
Console App Source Code:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32;
namespace CheckODBCRegistry
{
class Program
{
static void Main( string[] args )
{
Console.WriteLine( "Check ODBC Registry for invalid values" );
var odbcBases = new List<string>();
odbcBases.Add( @"Software\ODBC\ODBCINST.INI\" );
if( Environment.Is64BitOperatingSystem ) // Check ODBC registry for both bitness's on x64 OS
odbcBases.Add( @"Software\Wow6432Node\ODBC\ODBCINST.INI\" );
foreach( var odbcBase in odbcBases )
{
// Get the ODBC Driver names
string[] odbcDrivers = null;
using( var drivers = Registry.LocalMachine.OpenSubKey( odbcBase + "ODBC Drivers" ) )
odbcDrivers = drivers.GetValueNames();
if( odbcDrivers != null )
{
foreach( var odbcDriver in odbcDrivers )
{
if( string.IsNullOrWhiteSpace( odbcDriver ) )
Console.WriteLine( "\tInvalid 'empty' ODBC Driver name" );
else
{
try
{
using( var odbcDriverKey = Registry.LocalMachine.OpenSubKey( odbcBase + odbcDriver ) )
{
try
{
var valueNames = odbcDriverKey.GetValueNames();
if( valueNames == null || valueNames.Length < 1 )
Console.WriteLine( $"\tDriver '{odbcDriver}' has no registry values" );
else
{
bool firstBadValueForDriver = true;
ValidateString( odbcDriver, "Name", odbcDriver, ref firstBadValueForDriver );
foreach( var valueName in valueNames )
{
var kind = odbcDriverKey.GetValueKind( valueName );
if( kind != RegistryValueKind.String && kind != RegistryValueKind.ExpandString )
continue;
var value = odbcDriverKey.GetValue( valueName ).ToString();
ValidateString( odbcDriver, valueName, value, ref firstBadValueForDriver );
}
}
}
catch( Exception ex )
{
Console.WriteLine( $"\tDriver '{odbcDriver}' access caused exception '{ex.Message}'" );
}
}
}
catch( System.Security.SecurityException ex )
{
Console.WriteLine( $"\tDriver '{odbcDriver}' has an ACL blocking the current user from reading it" );
}
}
}
}
}
Console.WriteLine();
Console.WriteLine( "Press any key to exit" );
Console.ReadKey();
}
private static bool BadString( string text )
{ return ( text != null && text.IndexOf( '\0' ) >= 0 ); }
private static string ClipString( string text )
{
var nullPos = text.IndexOf( '\0' );
if( nullPos == 0 )
return ( string.Empty );
if( nullPos > 0 )
return ( text.Substring( 0, nullPos ) );
return ( text );
}
private static string EscapeString( string text )
{
var sb = new StringBuilder();
foreach( char c in text )
{
if( Char.IsControl( c ) )
{
sb.Append( @"\x" );
sb.Append( ( (int)c ).ToString( "x" ) );
}
else
sb.Append( c );
}
return ( sb.ToString() );
}
private static void ValidateString( string odbcDriver, string name,
string value, ref bool firstBadValueForDriver )
{
if( BadString( value ) )
{
if( firstBadValueForDriver )
{
Console.Write( "\tDriver '" );
Console.Write( ClipString( odbcDriver ) );
Console.WriteLine( "'" );
firstBadValueForDriver = false;
}
Console.Write( $"\t\tHas a BAD {name}: " );
Console.WriteLine( EscapeString( value ) );
}
}
}
}
Output from my system:
Check ODBC Registry for invalid values
Driver 'Oracle in instantclient_11_2'
Has a BAD APILevel: 1\x0\x0\x0\x0\x0\x0\x0\x9-
Has a BAD ConnectionFunctions: YYY\x0¢\x0£\x0 \x0¥\x0▌\x0 \x0"\x0c\x0ª\x0«\x0¬\x0-\x0r\x0_\x0°\x0±\x0²\x03\x0'\x0µ\x0 \x0·\x0,\x01\x0º\x0»\x0¼\x0½\x0_\x0¿\x0A\x0A\x0A\x0A\x0Ä\x0Å\x0Æ\x0Ç\x0E\x0É\x0E\x0E\x0I\x0I\x0I\x0I\x0D\x0Ñ\x0O\x0O\x0O\x0O\x0Ö\x0x\x0O\x0U\x0U\x0U\x0Ü\x0Y\x0_\x0ß\x0A\x0A\x0A\x0A\x0Ä\x0Å\x0Æ\x0Ç\x0E\x0É\x0E\x0E\x0I\x0I\x0I\x0I\x0D\x0Ñ\x0O\x0O\x0O\x0O\x0Ö\x0÷\x0O\x0U\x0U\x0U\x0Ü\x0Y\x0_\x0x\x1II\x0\x0\x0\x0\x0\x0\x0\x0A\x0\x0\x0\x0\x0 \x0\x1\x0\x2\x0\x3\x0\x4\x0\x5\x0\x6\x0\x7\x0\x8\x0\x9\x0\xa\x0\xb\x0\xc\x0\xd\x0\xe\x0\xf\x0\x10\x0\x11\x0\x12\x0\x13\x0\x14\x0\x15\x0\x16\x0\x17
Has a BAD CPTimeout: 60\x0\x0,O\x0@\x1
Has a BAD Driver: C:\Oracle\SQORA32.dll\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x8\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x3\x0\x0\x0\x0\x0\x0\x0_\r_y\x7f\x0\x0\x0\x0E\x0\x0\x0\x0\x0\x2\x0(\x0\x0\x0\x0\x0 \x0\x0\x0\x0\x0\x0\x00\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0ÅKq_y\x7f\x0\x0\x1\x0\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0\x0\x0\x0\x0è[\x3\x0\x0\x0\x0\x0pä\x14\x0\x0\x0\x0\x0îôZ»pD\x0\x0\x12\x0\x0\x0\x0\x0\x0\x0PHE\x0\x0\x0\x0\x0@rE\x0\x0\x0\x0\x0PFE\x0\x0\x0\x0\x0(é\x14\x0\x0\x0\x0\x0\x17\x0\x0\x0\x0\x0\x0\x0\xfKq_y\x7f\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0d-E\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x00GE\x0\x0\x0\x0\x0 qE\x0\x0\x0\x0\x0\x90ö\x14\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x1\x0_\x0\x2\x0\x0\x1\x1\x1\x0\x0\x0\x0\x0\x0\x2E\x0\x0\x0\x0\x0F\x0\x0\x0y\x7f\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0Ä_5\x20æ\x14\x0\x0\x0\x0\x01\x0ÿÿ\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0@rE\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x16\x0\x0\x0\x0\x0\x0\x0PFE\x0\x0\x0\x0\x0\x1c\x0\x1c\x0\x0\x0\x0\x0OaTYë6\x0\x0*\x0\x0\x0\x0\x0\x0\x0Pê\x14\x0\x0\x0\x0\x0\x15-+\y\x7f\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0\x0\x0\x0\x0Aä\x14\x0\x0\x0\x0\x0DÉ+\y\x7f\x0\x0\x14\x2\x0\x0\x0\x0\x0\x0^ä\x14\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0Pê\x14\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0\x0\x0
Has a BAD DriverODBCVer: 03.51\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x1dq_y\x7f\x0\x0 \x1\x2\x3\x4\x5\x6\x7\x8\x9\xa\xb\xc\xd\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x0!"#$%&'\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0234567O\x2\x0\x0\x0\x0\x0\x00_E\x0\x0\x0\x0\x0\x1\x0\x0\x0LMN\x1PQRSTUVW\x90\x12\x0\x0\x0\x0\x0\x0`abcde\x1g\x0\x0\x0\x0lmno _E\x0\x0\x0\x0\x0s\x2\x0\x0|}~\x7f _E\x0\x0\x0\x0\x0\x1\x0\x0\x0O\x8dZ\x8f?\x4\x0\x0\x0\x0\x0\x0H\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0 ¥▌ O\x2\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0?\x0\x0\x0\x0\x0\x0\x0ÿ\x3\x0\x0\x0\x0\x0\x0\x0\x0A\x0\x0\x0\x0\x0\x0\x0A\x0\x0\x0\x0\x0T\x0\x0T\x0\x0\x0
Has a BAD Setup: C:\Oracle\SQORAS32.DLL\x0\x0\x2\x0,\x0\x0\x0\x0\x0p\x4\x0\x0\x0\x0\x0\x0?\x4\x0\x0\x0\x0\x0\x0H\x5E\x0\x0\x0\x0\x0@ü\x14\x0\x0\x0\x0\x0\x0\x0\x0@\x1\x0\x0\x0?\x0\x0\x0\x0\x0\x0\x1\x0`\x1\x0\x0\x0\x0\x0 Éu_y\x7f\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x2\x0\x0\x0\x0\x0\x0\x0\x2\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x6\x0\x0\x0\x0\x0\x0\x0z\x0\x0\x0\x0.gí:r%\y\x7f\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0-3v=\x0\x0\x0\x0\x8d\x0\x0\x8d\x1\x3\x1\x3ú\x2\x0\x0\x0\xeEí\x0\x0\x0@\x1\x0\x0\x0\x0`\x1\x0\x0\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0\x0^\x0`\x0\x0\x0\x0\x0^ü\x14\x0\x0\x0\x0\x0\\x0D\x0e\x0v\x0i\x0c\x0e\x0\\x0H\x0a\x0r\x0d
Has a BAD SQLLevel: 1\x0\x0\x0\x0\x0\x0\x0,O
Press any key to exit