<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7056200683605340623</id><updated>2012-01-05T18:25:02.754-08:00</updated><category term='C++'/><category term='Data Provider'/><category term='DataPump'/><category term='SQL'/><category term='Oracle'/><category term='WinForms'/><category term='.NET'/><category term='GUI'/><title type='text'>On the way to AlderPump</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-3480383423419872225</id><published>2009-08-24T16:08:00.000-07:00</published><updated>2009-08-24T18:25:49.210-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataPump'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>AlderPump 2.2 released</title><content type='html'>&lt;div style="text-align: justify;"&gt;First update to publicly available release 2.1 is shipping now. While its official number is 2.2, the release has a major new feature: file management. Upgrade from 2.1 is free, existing licenses continue to work.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;Oracle DataPump is a server component; hence it can't handle files on user machines or other servers. File Manager closes the gap by allowing files transfer between user workstation and database server machine. Other basic capabilities such as file deletion, renaming, etc are also present.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;The biggest problem was directory listing. None of Oracle releases to date support directory browsing. One can read, write, delete, or rename files - but only whose names were known from elsewhere. There is no rational explanation to that. Security reasons - one might say? Nonsense. Limit listings to folders exposed via Directory objects, add BROWSE privilege to already existing READ and WRITE - and let us be as secure as we want.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;Until this is available, there are few workarounds:&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;b&gt;Listing server-side directories&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li style="text-align: justify;"&gt;First is Java stored procedure. Apparently Java code can browse files via java.io.File interfaces. We must ensure Java is installed on the database, compile a piece of Java code, create a PL/SQL wrapper around it, and finally grant JAVAUSERPRIV to the interested users. This is doable, but not so straightforward for developers of shrinkwrap software considering number of points of possible failure.&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;div style="text-align: justify;"&gt;Another method is undocumented, but simpler. Oracle 10g+'s package DBMS_BACKUP_RESTORE has a procedure to list contents of directory. The package is not accessible by public, EXECUTE privilege must be explicitly granted to use it. The procedure populates an in-memory table which we can read. Interestingly, it not only lists contents of requested directory, but recursively dives into subdirectories and lists them too. This is better explained at &lt;a href="http://www.chrispoole.co.uk/tips/plsqltip2.htm"&gt;Christopher Poole's page&lt;/a&gt;.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;/li&gt;&lt;li style="text-align: justify;"&gt;One may also consider capability of DBMS_SCHEDULER to execute OS commands. We could run &lt;i&gt;dir&lt;/i&gt; and redirect its output to temp file, then parse it for file names. Again, from shrinkwrap software point of view, this is hell. Think about points of failure starting with scheduler jobs stuck (say, because job_queue_processes is 1 and job it is currently running got stuck - a real situation witnessed), recall all the OS-es out there and their variations of &lt;i&gt;ls&lt;/i&gt; or &lt;i&gt;dir&lt;/i&gt;, then mediate on where to write the temp file, finally think about formats of output. The method may work on a particular database with particular OS, but supporting any platform? Forget it.&lt;/li&gt;&lt;/ul&gt;&lt;div style="text-align: justify;"&gt;AlderPump equally supports the first two methods i.e. Java and PL/SQL. Java has little advantage in terms of operations as it returns files sizes along with names; PL/SQL is simpler to configure, manage, and remove. It also runs on databases where Java is not present. Both methods with their advantages and disadvantages explained in greater detail on &lt;a href="http://alderprogs.com/configuring_ssdb.html"&gt;AlderProgs site&lt;/a&gt;.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;But again, the entire idea of installing something on server side sucks. We live with it, but we are less than happy about it.&lt;/div&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;b&gt;Reading and writing files&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;Another challenge was file copying. We couldn't read the entire file into big BLOB and transfer it to the client; DataPump files can easily span to gigabytes and reading them all to memory is not a good idea. So, slash them to chunks. But to read chunk, one must have file handle, and how to preserve the handle between calls? Pass it as a parameter you've said? Well, in 10g+ handle is not a single number it used to be in 9i, it is 3-field structure (see utl_file package). Worse yet, it is PL/SQL type, not Oracle type. Passing PL/SQL structures is not easy, especially with limitations of Microsoft provider for Oracle.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;The only robust method is to pass file name and open/close file every time next chunk is read or written. Chunk size is limited to less than 32K. Reading 2G file would result in 64000 open/close operations not considering reads/writes themselves. Not very efficient.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;AlderPump is using a hack: we pass PL/SQL in string unpacking it prior to any file operation. Should Oracle change the structure we are doomed, but the risk is measured.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;As a side note, it is sad Oracle file handls are not atomic any more. There could be pretty complex structures behind them (and they are), but values exposed to users should be as simple as possible. Making it structure, especially one which can't be easily passed to client, effectively kills either performance or compatibility.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;b&gt;Working with remote databases&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;One of the features considered for implementation was working via database links. Indeed, if we can access a database, and that database has links to other databases - why can't we run AlderPump jobs there or at least manage files? As it tuned out, we can't.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;What killed it was Oracle policy about types. Simply put, the fact that two types on different databases have the same name does not guarantee they are same type. Sounds logical, right? Yes, but implementation lacks forethought. Even though both types belong to SYS schema and database versions are the same up to patch level - stubborn type system still considers them different. DataPump uses types for job status, dumpfile info, and other purposes, unfortunately this effectively kills remote capabilities.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;This type compatibility problem is very common, Oracle should really do something about it. Simplest coming to mind is hash, or checksum, or other sort of signature. "Sign" type with a key, then compare keys to ensure types are the same.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;b&gt;Installer&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;There was major rework on the installer. It can now install fresh version, upgrade 1.x and 2.1 to 2.2, or repair existing 2.2 installation. Uninstaller allso got smarter. Most of unsinstallers out there only clean out files they've created. AlderPump uninstaller also wipes out temporary ones such as sqlnet.log created by SqlNet on connection failure. It hunts down and removes saved job templates too - although this may be too obsessive. Finally, there is option to remove license, say to transfer it to another machine. Preserved licenses are picked up automatically on next install, they remain valid for all 2.x versions free of charge. Owners of 1.x versions can upgrade their licenses for free.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;b&gt;Next release&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;We are mostly done planning features for next release. It is scheduled to ship in 4 to 6 months, the rate at which AlderPump version are normally shipped. Like with this release, all 2.x licenses will continue to work and owners of 1.x (should any remain) may contact sales for free license upgrade. More details will be posted closer to release date.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: justify;"&gt;AlderPump Lite will remain free for everybody although with limited features.&lt;/div&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-3480383423419872225?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/3480383423419872225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=3480383423419872225' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/3480383423419872225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/3480383423419872225'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2009/08/alderpump-22-released.html' title='AlderPump 2.2 released'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-342860626008307505</id><published>2009-04-22T12:31:00.000-07:00</published><updated>2009-04-22T12:31:00.302-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Data Provider'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Sample program to run an arbitrary SQL</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: arial;"&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The goal&lt;/span&gt;&lt;br /&gt;In the &lt;a href="http://alderprogs.blogspot.com/2009/04/deploying-odpnet-with-oracle-instant.html"&gt;previous post&lt;/a&gt; we've discussed how to package .NET application with self-contained Oracle client so it can be copied to any machine and still be able to access Oracle.&lt;br /&gt;&lt;br /&gt;An example provided there wasn't really bright: it was merely demonstrated proof of concept. In this post we'll develop a bit more sophisticated application to execute arbitrary SQL statements. The utility is not SqlPlus (which is good, world doesn't need another SqlPlus - and Oracle is already shipping it bundled with Instant Client), but has somewhat similar functionality:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;First parameter is user credentials and connect string: &lt;i&gt;username/password@connect_string&lt;/i&gt;. Connect string can be in any form supported by Oracle Instant Client: ezConnect, full descriptor, TNS, or other.&lt;br /&gt;&lt;li&gt;Second and other parameters are concatenated to a SQL statement.&lt;br /&gt;&lt;li&gt;Only one statement is allowed (but you can pass PL/SQL block). If the statement is SELECT, returned rows are printed.&lt;br /&gt;&lt;li&gt;Talking about PL/SQL blocks, DBMS_OUTPUT is not supported.&lt;br /&gt;&lt;li&gt;We fetch all output to memory first to find proper column width. This makes the program unsuitable for large result sets.&lt;br /&gt;&lt;li&gt;Return code on syntax error is 2; on runtime error 1, on success it is 0.&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The implementation&lt;/span&gt;&lt;br /&gt;It starts with standard header, parameters checking, and usage printing:&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Text;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using Oracle.DataAccess.Client;&lt;br /&gt;&lt;br /&gt;namespace OraCmd {&lt;br /&gt;  class Program {&lt;br /&gt;&lt;br /&gt;    static int Main(string[] args) {&lt;br /&gt;&lt;br /&gt;      /* Uncomment to ignore client machine's settings&lt;br /&gt;     Environment.SetEnvironmentVariable("ORA_TZFILE", null);&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_LANG", "AMERICAN_AMERICA.AL32UTF8");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_DATE_FORMAT", "DD-MON-RR");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_TIME_FORMAT", "HH.MI.SSXFF AM");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_TIMESTAMP_FORMAT", "DD-MON-RR HH.MI.SSXFF AM");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_TIMESTAMP_TZ_FORMAT", "DD-MON-RR HH.MI.SSXFF AM TZR");&lt;br /&gt;      */&lt;br /&gt;&lt;br /&gt;      if( args.Length &lt; 2 ) {&lt;br /&gt;        Console.WriteLine("Parameters: username/password[@connect_string] sql_command");&lt;br /&gt;        Console.WriteLine("* For connect string format please see Oracle® 11.1 Call Interface Programmer's Guide, OCI: Introduction and Upgrading,");&lt;br /&gt;        Console.WriteLine("  Instant Client Light (English) at http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci01int.htm#CHDCFHAC");&lt;br /&gt;        Console.WriteLine("* All remaining parameters are concatentated to form sql command to execute");&lt;br /&gt;        Console.WriteLine("  alternatively the command can be enclosed in double quotes. Only one command is allowed.");&lt;br /&gt;        return 2;&lt;br /&gt;      }&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;&lt;br /&gt;Then we parse parameters, build Sql statement to execute, and print them. Function parse_args() is defined later.&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;      string usr, pwd, ds_str;&lt;br /&gt;      parse_args(args[0], out usr, out pwd, out ds_str);&lt;br /&gt;      string conn_str = string.Format("User Id={0};Password={1};Data Source={2}", usr, pwd, ds_str);&lt;br /&gt;&lt;br /&gt;      StringBuilder sb = new StringBuilder(args[1]);&lt;br /&gt;      for( int i = 2; i &lt; args.Length; i++ )&lt;br /&gt;        sb.Append(' ').Append(args[i]);&lt;br /&gt;      string sql = sb.ToString();&lt;br /&gt;&lt;br /&gt;      Console.WriteLine("Connect: {0}", conn_str);&lt;br /&gt;      Console.WriteLine("SQL:     {0}", sql);&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;Finally, we open connection and execute statement as SELECT getting back reader. If number of fields in the result set is zero, we assume the command wasn't SELECT and cleanup. If number of fields is positive, we print the result set.&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;      try {&lt;br /&gt;        OracleConnection conn = new OracleConnection(conn_str);&lt;br /&gt;        conn.Open();&lt;br /&gt;&lt;br /&gt;        OracleCommand cmd = new OracleCommand(sql, conn);&lt;br /&gt;        OracleDataReader rdr = cmd.ExecuteReader();&lt;br /&gt;        if( /*rdr.HasRows &amp;&amp;*/ rdr.FieldCount &gt; 0 )&lt;br /&gt;          print_rows(rdr);&lt;br /&gt;&lt;br /&gt;        rdr.Dispose();&lt;br /&gt;        cmd.Dispose();&lt;br /&gt;        conn.Dispose();&lt;br /&gt;      } catch( Exception x ) {&lt;br /&gt;        Console.WriteLine();&lt;br /&gt;        Console.WriteLine(x.Message);&lt;br /&gt;        if( x.InnerException != null )&lt;br /&gt;          Console.WriteLine(x.InnerException.Message);&lt;br /&gt;        Console.WriteLine(x.StackTrace);&lt;br /&gt;        return 1;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // end of Main: exit success&lt;br /&gt;      return 0;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;Function parse_args breaks credentials string into user name, password, and connect string. This could be more elegantly written in RegExp, but we don't bother:&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;    static void parse_args(string str, out string usr, out string pwd, out string ds_str) {&lt;br /&gt;      string usr_pwd = usr = pwd = ds_str = string.Empty;&lt;br /&gt;      int k = str.IndexOf('@');&lt;br /&gt;      usr_pwd = str.Substring(0, k == -1 ? str.Length : k  );&lt;br /&gt;      ds_str  = str.Substring(   k == -1 ? str.Length : k+1);&lt;br /&gt;      if( usr_pwd.Length &gt; 0 ) {&lt;br /&gt;        k = usr_pwd.IndexOf('/');&lt;br /&gt;        usr = usr_pwd.Substring(0, k == -1 ? str.Length : k  );&lt;br /&gt;        pwd = usr_pwd.Substring(   k == -1 ? str.Length : k+1);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;To work with result sets, we first define a helper struct for field definitions. We print strings left-aligned and all other values aligned to the right, hence the &lt;i&gt;LeftAligned&lt;/i&gt; member:&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;    struct FieldDesc {&lt;br /&gt;      public string Name;&lt;br /&gt;      public int    MaxLength;&lt;br /&gt;      public bool   LeftAligned;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;For result set printing we first determine field names and then store all values in a list. While fetching, we find maximum value length. Initial length is field name's to ensure names fit. Storing all values in a list is not too practical for real-world use, but sufficient for sample program like this.&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;    static void print_rows(OracleDataReader rdr) {&lt;br /&gt;&lt;br /&gt;      FieldDesc[] Desc = new FieldDesc[rdr.FieldCount];&lt;br /&gt;      for( int k = 0; k &lt; rdr.FieldCount; k++ ) {&lt;br /&gt;        Desc[k].Name = rdr.GetName(k);&lt;br /&gt;        Desc[k].MaxLength = Desc[k].Name.Length;&lt;br /&gt;        Desc[k].LeftAligned = rdr.GetFieldType(k) == typeof(string);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      string[] vals;&lt;br /&gt;      List&lt;string[]&gt; Vals = new List&lt;string[]&gt;();&lt;br /&gt;      while( rdr.Read() ) {&lt;br /&gt;        vals = new string[rdr.FieldCount];&lt;br /&gt;        for( int k = 0; k &lt; rdr.FieldCount; k++ ) {&lt;br /&gt;          vals[k] = rdr.GetValue(k).ToString();&lt;br /&gt;          if( vals[k].Length &gt; Desc[k].MaxLength )&lt;br /&gt;            Desc[k].MaxLength = vals[k].Length;&lt;br /&gt;        }&lt;br /&gt;        Vals.Add(vals);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      const string titleSep = "-";&lt;br /&gt;      const string colSep   = " ";&lt;br /&gt;&lt;br /&gt;      // print field names&lt;br /&gt;      Console.WriteLine();&lt;br /&gt;      for(int k=0; k &lt; Desc.Length; k++) {&lt;br /&gt;        if( k &gt; 0 )&lt;br /&gt;          Console.Write(colSep);&lt;br /&gt;        Console.Write(Desc[k].LeftAligned ? Desc[k].Name.PadRight(Desc[k].MaxLength) : Desc[k].Name.PadLeft(Desc[k].MaxLength));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // print title separator&lt;br /&gt;      Console.WriteLine();&lt;br /&gt;      for(int k=0; k &lt; Desc.Length; k++) {&lt;br /&gt;        if( k &gt; 0 )&lt;br /&gt;          Console.Write(colSep);&lt;br /&gt;        Console.Write(titleSep.PadRight(Desc[k].MaxLength, titleSep[0]));&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      // print values&lt;br /&gt;      foreach(string[] vls in Vals) {&lt;br /&gt;        Console.WriteLine();&lt;br /&gt;        for(int k=0; k &lt; Desc.Length; k++) {&lt;br /&gt;          if( k &gt; 0 )&lt;br /&gt;            Console.Write(colSep);&lt;br /&gt;          Console.Write(Desc[k].LeftAligned ? vls[k].PadRight(Desc[k].MaxLength) : vls[k].PadLeft(Desc[k].MaxLength));&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;In nutshell, this is it. Package the application with Instant client, share over network, and gain access to any Oracle database in sight. Again, the program won't work from network share directly, it must be copied to local drive in order to find DLLs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Test run&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;C:\Tmp&gt; OraCmd scott/tiger@orahost10g/ora10 select rownum,rowid,level from dual connect by level ^&lt;= 5&lt;br /&gt;Connect: User Id=scott;Password=tiger;Data Source= orahost10g/ora10&lt;br /&gt;SQL:     select rownum,rowid,level from dual connect by level &lt;= 5&lt;br /&gt;&lt;br /&gt;ROWNUM ROWID              LEVEL&lt;br /&gt;------ ------------------ -----&lt;br /&gt;     1 AAAADeAABAAAAZqAAA     1&lt;br /&gt;     2 AAAADeAABAAAAZqAAA     2&lt;br /&gt;     3 AAAADeAABAAAAZqAAA     3&lt;br /&gt;     4 AAAADeAABAAAAZqAAA     4&lt;br /&gt;     5 AAAADeAABAAAAZqAAA     5&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;br /&gt;Note how we had to mask the &amp;lt; character because it is Windows interpreter's special symbol. Alternatively, the entire SQL could be enclosed in double quotes.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-342860626008307505?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/342860626008307505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=342860626008307505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/342860626008307505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/342860626008307505'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2009/04/sample-program-to-run-arbitrary-sql.html' title='Sample program to run an arbitrary SQL'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-7179404671664991945</id><published>2009-04-17T09:22:00.000-07:00</published><updated>2009-04-17T09:22:00.439-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Data Provider'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Deploying ODP.NET with Oracle Instant Client</title><content type='html'>&lt;div style="text-align: justify;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;While ago I wrote about &lt;a href="http://alderprogs.blogspot.com/2007/07/in-search-for-data-provider.html"&gt;choosing right Data Provider&lt;/a&gt; to access Oracle. That time's choice was OleDB data provider from Microsoft.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A bit of history&lt;/span&gt;&lt;br /&gt;Its main advantage was zero installation (the provider comes with .NET runtime) plus moderate functionality. On the downside it doesn't support SYSDBA connections, unable to configure fetch buffer size, and has no adequate method to retrieve Oracle error number from Exception. The provider still needs Oracle Client software to be installed and configured on the user's machine.&lt;br /&gt;&lt;br /&gt;Oracle's .NET Provider came in 174M package and contained ODP.NET dlls along with full-blown client. At that time Oracle also shipped Instant Client which was "only" 80M in size, but required no installation. And the major pitfall was that ODP.NET didn't work with the Instant Client.&lt;br /&gt;&lt;br /&gt;Finally in 2008 Oracle released production version of ODP.NET provider for NET 2.0 which could work over Instant Client. To developers, this means we can now enjoy all the luxuries of genuine ODP.NET and relax prerequisite requirements. We can deploy all components needed to access databases with XCOPY method without messing with client machine's registry or other settings.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;What's discussed&lt;/span&gt;&lt;br /&gt;In this post we will create a small Visual Studio project for program fetching sysdate from Oracle database. Connection settings will be hardcoded for simplicity. In the coming post we will write a complete program connecting to any Oracle database and running arbitrary SQL statement passed as a parameter - with no need for any Oracle software installed on the client.&lt;br /&gt;&lt;br /&gt;Only 32-bit clients are discussed, 64-bit components for 11.x are not yet available at the moment of writing. Also we only bother about supporting English language.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ETL stands for Extract, Trash, and depLoy&lt;/span&gt;&lt;br /&gt;Deployment files come from ODP.NET and Instant Client, so create a directory for extracted DLLs. They will be shipped to client as part of our XCOPY deployment.&lt;br /&gt;&lt;br /&gt;First, we'll need to download ODP.NET provider. It is coming bundled along with other Oracle Data Access Components (ODAC) and available on Oracle &lt;a href="http://www.oracle.com/technology/software/tech/windows/odpnet/index.html"&gt;OTN site&lt;/a&gt;. Registration is required to download software, but it is free and if you work with Oracle, registering at OTN is a good idea anyway. Get "With XCOPY deployment" version. At the moment the available version is 11.1.0.6.21, size 43M.&lt;br /&gt;&lt;br /&gt;It comes in a ZIP archive, unpack it to temp directory. There is readme file there instructing to run install.bat but there is no need to do that. In subdirectories of ODP.NET20 locate and copy OraOps11w.dll and Oracle.DataAccess.dll to your deployment directory. You can erase downloaded file and unpacked directory now. Yes, we only need 2 files from the entire installation. They are ODP.NET provider, size ~1.3M.&lt;br /&gt;&lt;br /&gt;Second, get Instant Client. Our provider is 11.1.0.6, but client can be of any version newer than 11.6. Right now &lt;a href="http://www.oracle.com/technology/software/tech/oci/instantclient/htdocs/winsoft.html"&gt;version 1.1.0.7.0&lt;/a&gt; is available. There are 2 versions listed at the top: Instant Client Package - Basic and Basic Lite. It is up to you what version to pick; ODP.NET works happily with both. Lite version is significantly smaller, but supports limited national languages and client charsets:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;It supports US7ASCII, WE8DEC, WE8ISO8859P1, WE8MSWIN1252, UTF8, AL16UTF16, and AL32UTF8 character sets.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It can connect to databases with charsets US7ASCII, WE8DEC, WE8ISO8859P1, WE8MSWIN1252, WE8EBCDIC37C, WE8EBCDIC1047, UTF8, and AL32UTF8. All client-side messages are in English.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Basic is going to add ~105M to your distribution; Basic Lite is ~32M.&lt;br /&gt;&lt;br /&gt;Upon downloading the client, copy 3 DLLs from it to our deployment directory. For Basic version they are: oci.dll, orannzsbb11.dll, and oraociei11.dll (111M). For Lite version they are oci.dll, orannzsbb11.dll, and oraociicus11.dll (~31M). Even though first 2 names are the same, file sizes differ - so don't mix up DLLs from different distributions.&lt;br /&gt;&lt;br /&gt;In your deployment directory there should be 5 files now: 2 Data Provider DLLs and 3 Instant Client ones. This is all what's needed to access Oracle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Writing test app&lt;/span&gt;&lt;br /&gt;Create Visual Studio Console project and in Solution Explorer "Add Reference" from References subtree or Project context menu. Navigate to your deployment directory and select Oracle.DataAccess.dll. Make sure its "Copy Local" property is true (so file will be copied to output directory).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_F6O77syiOA4/SeilcBKR-NI/AAAAAAAAAIQ/6VwlT61Dn5k/s1600-h/SolutionExplorer.GIF"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 311px; height: 274px;" src="http://4.bp.blogspot.com/_F6O77syiOA4/SeilcBKR-NI/AAAAAAAAAIQ/6VwlT61Dn5k/s400/SolutionExplorer.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5325688460132088018" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now add remaining DLLs: right-click on project and "Add existing item". They also should be deployed to client, so set property "Copy to Output Directory" to "Copy if newer" for each item.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_F6O77syiOA4/SeilLyqvBsI/AAAAAAAAAII/JPjQOYkJ4QA/s1600-h/CopyIfNewer.gif"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 318px; height: 194px;" src="http://1.bp.blogspot.com/_F6O77syiOA4/SeilLyqvBsI/AAAAAAAAAII/JPjQOYkJ4QA/s400/CopyIfNewer.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5325688181363771074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We are now ready to write simple test code:&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;using System;&lt;br /&gt;using Oracle.DataAccess.Client;&lt;br /&gt;&lt;br /&gt;namespace InstantClientApp {&lt;br /&gt; class Program {&lt;br /&gt;   static void Main(string[] args) {&lt;br /&gt;     OracleConnection conn = new OracleConnection("User Id=scott;Password=tiger;Data Source=machine_name/service_name");&lt;br /&gt;     conn.Open();&lt;br /&gt;     OracleCommand cmd = new OracleCommand("select sysdate from dual", conn);&lt;br /&gt;     DateTime dtm = (DateTime)cmd.ExecuteScalar();&lt;br /&gt;     Console.WriteLine("Database time is {0}", dtm.ToString("F"));&lt;br /&gt;     cmd.Dispose();&lt;br /&gt;     conn.Dispose();&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;Replace connection string parameters with valid values.&lt;br /&gt;&lt;br /&gt;Build the project. Your Debug or Release directory should now contain 5 DLLs, the executable, and a couple of other now unneeded files generated by Visual Studio. This is it, a complete application packaged along with all what's needed to access Oracle, and ready to be deployed to the client. Try XCOPY deployment now: copy the files to another machine with no Oracle client installed, only .NET 2.0 runtime is needed. Run it, and if connection string is right, you should get database server time. Warning: it won't work from network share, you should really copy files to client's local drive.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;C:\Tmp&gt;dir&lt;br /&gt; Volume in drive C has no label.&lt;br /&gt; Volume Serial Number is 30A5-4F0E&lt;br /&gt;&lt;br /&gt; Directory of C:\Tmp&lt;br /&gt;&lt;br /&gt;04/17/2009  10:00 AM            16,384 InstantClientApp.exe&lt;br /&gt;10/01/2008  04:22 AM           520,192 oci.dll&lt;br /&gt;12/20/2007  03:53 AM           917,504 Oracle.DataAccess.dll&lt;br /&gt;09/18/2008  10:47 PM         1,130,496 orannzsbb11.dll&lt;br /&gt;10/01/2008  04:59 AM        29,802,496 oraociicus11.dll&lt;br /&gt;12/20/2007  04:02 AM           385,024 OraOps11w.dll&lt;br /&gt;               6 File(s)     32,772,096 bytes&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;font color="darkblue"&gt;C:\Tmp&gt;InstantClientApp.exe&lt;br /&gt;Database time is Friday, April 17, 2009 9:09:09 AM&lt;/font&gt;&lt;/b&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Connection string variations&lt;/span&gt;&lt;br /&gt;You've probably noted we used EZCONNECT method to specify database: machine_name/service_name. This form was a short version of:&lt;br /&gt;&lt;pre&gt;  [//]host[:port][/service_name]&lt;/pre&gt;&lt;br /&gt;Instant client also recognizes full descriptor format:&lt;br /&gt;&lt;pre&gt;  "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=hostname)(PORT=port#))(CONNECT_DATA=(SERVICE_NAME=service)))"&lt;/pre&gt;&lt;br /&gt;If Oracle Client software is already installed on the machine, Instant Client can employ TNSNAMES.ORA too. There are two methods to do that: either set environment variable TNS_ADMIN to path to network\admin directory, or set variable ORACLE_HOME to point to installation home and tnsnames.ora will be looked up in %ORACLE_HOME%\network\admin.&lt;br /&gt;&lt;br /&gt;More exotic methods such as LDAP or LOCAL variable are also supported, but I haven't tried them.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Environment variables&lt;/span&gt;&lt;br /&gt;There are several environment variables which may affect your application. To be completely isolated from machine's configuration, override them manually before opening connection:&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre&gt;&lt;br /&gt;     Environment.SetEnvironmentVariable("ORA_TZFILE", null);&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_LANG", "AMERICAN_AMERICA.AL32UTF8");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_DATE_FORMAT", "DD-MON-RR");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_TIME_FORMAT", "HH.MI.SSXFF AM");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_TIMESTAMP_FORMAT", "DD-MON-RR HH.MI.SSXFF AM");&lt;br /&gt;     Environment.SetEnvironmentVariable("NLS_TIMESTAMP_TZ_FORMAT", "DD-MON-RR HH.MI.SSXFF AM TZR");&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;span class="Apple-style-span"  style="font-family:arial;"&gt;&lt;br /&gt;Not sue if this is a complete set though.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Applicability&lt;/span&gt;&lt;br /&gt;Will &lt;a href="http://alderprogs.com/"&gt;AlderPump&lt;/a&gt; switch to Instant Client? Right now it doesn't seem very likely. There are several reasons:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The product's audience is DBAs or Oracle professionals who definitely have Oracle Client installed on their machines. Demand to have at least client software configured is not an obstacle for them.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Switching from MS provider would involve code rewrite. Not major, but visible. And the only real feature missing is ability to connect AS SYSDBA which is not a good idea anyways.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://alderprogs.com/Downloads.html"&gt;AlderPump 2.1 installation&lt;/a&gt; is 410K. Instant Client Lite is 32M. The numbers don't look quite right together.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The technology may not be mature enough yet, there were too few versions released so far. It probably needs more polishing.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Having said that, I'll definitely will keep eye on it and who knows? One day the gains may outweigh the cons. I kind of hope they will, because feature-wise Oracle's ODP.NET is really good.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Referencs&lt;/span&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://forums.oracle.com/forums/thread.jspa?messageID=3351596&amp;amp;#3351596"&gt;OTN forum&lt;/a&gt; discussing deployment.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Section &lt;a href="http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28395/oci01int.htm#i423362"&gt;OCI Instant Client&lt;/a&gt; of Oracle® Call Interface Programmer's Guide.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;OTN article &lt;a href="http://www.oracle.com/technology/oramag/oracle/08-nov/o68odpnet.html"&gt;Instant ODP.NET Deployment&lt;/a&gt; by Mark A. Williams.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-7179404671664991945?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/7179404671664991945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=7179404671664991945' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/7179404671664991945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/7179404671664991945'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2009/04/deploying-odpnet-with-oracle-instant.html' title='Deploying ODP.NET with Oracle Instant Client'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_F6O77syiOA4/SeilcBKR-NI/AAAAAAAAAIQ/6VwlT61Dn5k/s72-c/SolutionExplorer.GIF' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-4740982151288068209</id><published>2009-04-09T14:01:00.000-07:00</published><updated>2009-04-16T19:28:47.716-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataPump'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>AlderPump is shipping</title><content type='html'>&lt;div style="text-align: justify;"&gt;&lt;span class="Apple-style-span" style="font-family: arial; "&gt;After 7 months of development, a year of beta testing, and 3 more months of building around infrastructure, AlderPump is shipping. Its official site is &lt;a href="http://alderprogs.com/"&gt;http://alderprogs.com&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Publicly available version comes in two flavors: Professional and Lite. In Professional mode with all features are enabled. The mode is available for first 30 days for evaluation or after buying a license. Lite mode with limited functionality is free. In this mode only current user's jobs can be monitored and managed. For job creation, four single-page wizards are enabled. They are to create table and schema mode export and import jobs. Command line generation for expdp/impdp is also there.&lt;br /&gt;&lt;br /&gt;Looking at this in retrospective I must say choosing DataPump for automation wasn't very bright idea. DataPump is a new product and has ahead long way to evolve. Its interface changed quite a bit from 10.1 to 10.2 - this is why AlderPump is not really supporting 10.1 beyond checking for some quirks. Some promised functionality &lt;a href="http://alderprogs.com/Forum/viewtopic.php?f=4&amp;amp;t=25&amp;amp;sid=2f55629d62c1674a5e87b4f25d503aba"&gt;didn't work&lt;/a&gt; till later patches. Oracle 11.1 brought in new changes although not too revolutionary.&lt;br /&gt;&lt;br /&gt;DataPump interface is quite obscure. Say, from developer's perspective division into modes is purely artificial. What is the difference between export in SCHEMA and FULL mode with schema filter? Why prefer one to another? Can one perform FULL mode import from dump taken in TABLE mode? (the answer is btw yes). Why there is a parameter to replace tables but not other objects?&lt;br /&gt;&lt;br /&gt;Fortunately for AlderPump, expdp and impdp also suffer from artificial limitations - such as inability to mix INCLUDE and EXCLUDE filters (perfectly allowed by the API), specifying more than one expression filter (again, no limit), or applying metadata remaps basing on object types.&lt;br /&gt;&lt;br /&gt;Needless to say, these restrictions are no subject for AlderPump which allows anything the API has exposed. Very fortunate for AlderProgs :)&lt;br /&gt;&lt;br /&gt;Anyways, AlderPump has sailed. It is surprising how much work shipping takes, but the work was [almost] always fun so far. And ahead lies the best part: drafting plans for the next release. The time to throw in wild ideas with no real obligations, time to try new things without real need to make them working, time to travel away and claim this boosts creativity.&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-4740982151288068209?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/4740982151288068209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=4740982151288068209' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/4740982151288068209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/4740982151288068209'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2009/04/alderpump-has-sailed.html' title='AlderPump is shipping'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-8996161328948852943</id><published>2007-09-08T16:58:00.000-07:00</published><updated>2008-12-09T23:05:02.928-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='WinForms'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>Transparent ListBox</title><content type='html'>&lt;p style="font-family: Arial; text-align: justify;"&gt;&lt;br /&gt;Like most other transparent  ListBox controls found in on the Internet, mine is not the true one. I cheated,  making underlying control to expose its background as a bitmap - which is then  used to paint ListBox background. This doesn't work for any container, just for  the one I'm using. You will not find complete source here, just the essential bits.&lt;br /&gt;&lt;br /&gt;What I  wanted:&lt;br /&gt;On a form, I have a gradient-filled panel hosting various controls, one of them is a data-bound ListBox showing progress messages. Because the ListBox occupies the majority of the panel's real estate, I wanted it to be transparent. Here is the final result (colors were adjusted to better show gradient fill):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_F6O77syiOA4/RuOXF-FiSMI/AAAAAAAAABA/A12oPDB3cxs/s1600-h/transparent_listbox_output.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_F6O77syiOA4/RuOXF-FiSMI/AAAAAAAAABA/A12oPDB3cxs/s400/transparent_listbox_output.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5108092531189631170" /&gt;&lt;/a&gt;&lt;br /&gt;The suggestions found on the Internet were only partially working: whatever method I tried had a little glitch here or there - but that little problem was enough to ruin the entire idea.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OnDrawItem  approach:&lt;/b&gt;&lt;br /&gt;First was "transparent background" approach: we set  background color to transparent and voilà - everything is magically working. Not quite. In .NET's ListBox, setting background color to transparent is illegal; an exception is thrown when trying to assign a color with &lt;i&gt;alpha&lt;/i&gt; less than 255.&lt;br /&gt;&lt;br /&gt;There are at least two ways to make .NET bypass transparent backgrounds: either enable transparency in control's constructor, or override CreateParams method. Both ways require deriving our class from WinForm's ListBox:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;protected override&lt;/span&gt; CreateParams CreateParams{&lt;br /&gt;  &lt;span style="color:blue;"&gt;get&lt;/span&gt; {&lt;br /&gt;    CreateParams cp = &lt;span style="color:blue;"&gt;base&lt;/span&gt;.CreateParams;&lt;br /&gt;    cp.ExStyle |= 0x20; &lt;span style="color:green;"&gt;// WS_EX_TRANSPARENT&lt;/span&gt;&lt;br /&gt;    &lt;span style="color:blue;"&gt;return&lt;/span&gt; cp;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;Or, use SetStyle method in control's constructor:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;public&lt;/span&gt; TransparentListBox(): &lt;span style="color:blue;"&gt;base&lt;/span&gt;() {&lt;br /&gt;  SetStyle(ControlStyles.SupportsTransparentBackColor, &lt;span style="color:blue;"&gt;true&lt;/span&gt;);&lt;br /&gt;  &lt;span style="color:blue;"&gt;this&lt;/span&gt;.BackColor = &lt;span style="color:darkcyan;"&gt;Color&lt;/span&gt;.FromArgb(0, &lt;span style="color:darkcyan;"&gt;Color&lt;/span&gt;.Transparent);&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;This is where another gotcha is awaiting. In WinForms, transparency is not real. It is simulated by asking the control's &lt;b&gt;form&lt;/b&gt; to paint its background in the rectangle where our control resides.  Any intermediate controls are not painted. If we'd placed our transparent  control on a Panel hosted within a TabPage, not the Panel nor TabPage is painted, only the underlying form.&lt;br /&gt;&lt;br /&gt;Obviously, this is not too useful, in my case the ListBox is sitting on a gradient Panel.&lt;br /&gt;&lt;br /&gt;The workaround is to bypass WinForms and paint item's background manually. This is achieved by setting DrawStyle to OwnerDrawFixed and  implementing custom OnDrawItem method. Details aside, the method's skeleton  would look similarly to:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;protected override void&lt;/span&gt; OnDrawItem(&lt;span style="color:darkcyan;"&gt;DrawItemEventArgs&lt;/span&gt; e) {&lt;br /&gt;  &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt; brush = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt;(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.ForeColor);&lt;br /&gt;  &lt;span style="color:blue;"&gt;string&lt;/span&gt; val = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.GetItemText(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.Items[e.Index]);&lt;br /&gt;  e.Graphics.DrawString(val, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Font, brush, e.Bounds.X, e.Bounds.Y);&lt;br /&gt;  brush.Dispose();&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;Here we do not paint background thus letting our gradient panel to shine through.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Note the use of GetItemText rather than Items[e.Index].ToString(), that is because the control is data-bound.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;This mostly works except for two things. Firstly, the text flickers when items are added. That is because ListBox painting routine first erases everything with BackColor before calling our OnDrawItem(). And secondly, when the number of items in ListBox is too small to fill it up entirely, the remaining bottom space is still filled up with BackColor. Did you think of making background transparent as described above? Won't help: the background has been filled up with BackColor already.&lt;br /&gt;&lt;br /&gt;The first problem could be partially alleviated by setting ListBox BackColor to a color close to the gradient's background, so the flicker is not that noticeable.&lt;br /&gt;&lt;br /&gt;The second one could be approached by setting DrawMode to OwnerDrawVariable and making the last item span to the bottom of the control. This would be handled in OnMeasureItem() method (which we must provide in Variable mode). Still, this won't work when ListBox is empty - and a possible solution is to never have it empty: always add an empty line and replace it with real text when it is first added.&lt;br /&gt;&lt;br /&gt;I didn't try these ideas - my ListBox is data bound and the logic seems to get overcomplicated.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OnPaint approach:&lt;/b&gt;&lt;br /&gt;Giving up on OnDrawItem() idea, we're left with the last resort: custom painting. This requires to override OnPaint() and OnPaintBackground() and draw the entire control's contents (at least its client area). To tell .NET we're paitning on our own, we set a few styles in the control's constructor:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;public&lt;/span&gt; TransparentListBox(): &lt;span style="color:blue;"&gt;base&lt;/span&gt;() {&lt;br /&gt;  SetStyle(&lt;span style="color:darkcyan;"&gt;ControlStyles&lt;/span&gt;.OptimizedDoubleBuffer, &lt;span style="color:blue;"&gt;true&lt;/span&gt;);&lt;br /&gt;  SetStyle(&lt;span style="color:darkcyan;"&gt;ControlStyles&lt;/span&gt;.UserPaint, &lt;span style="color:blue;"&gt;true&lt;/span&gt;);&lt;br /&gt;  SetStyle(&lt;span style="color:darkcyan;"&gt;ControlStyles&lt;/span&gt;.AllPaintingInWmPaint, &lt;span style="color:blue;"&gt;true&lt;/span&gt;);&lt;br /&gt;  SetStyle(&lt;span style="color:darkcyan;"&gt;ControlStyles&lt;/span&gt;.ResizeRedraw, &lt;span style="color:blue;"&gt;true&lt;/span&gt;);&lt;br /&gt;  SetStyle(&lt;span style="color:darkcyan;"&gt;ControlStyles&lt;/span&gt;.Opaque, &lt;span style="color:blue;"&gt;false&lt;/span&gt;);&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;UserPaint instructs .NET to call our OnPaint method and AllPaintingInWmPaint tells it to omit calls to OnPaintBackground(): we draw all ListBox items in OnPaint() and let non-covered underlying background to remain untouched. The method loops through all visible items (first visible item is determined by ListBox.TopIndex property).&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;protected override void&lt;/span&gt; OnPaint(&lt;span style="color:darkcyan;"&gt;PaintEventArgs&lt;/span&gt; e) {&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;float&lt;/span&gt; x = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.X;&lt;br /&gt;  &lt;span style="color:blue;"&gt;float&lt;/span&gt; y = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.Y;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt; sel_bg_brush = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt;(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.SelBackColor);&lt;br /&gt;  &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt; fore_brush   = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt;(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.ForeColor);&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;int&lt;/span&gt; cnt = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Height / &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ItemHeight;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;for&lt;/span&gt;(&lt;span style="color:blue;"&gt;int&lt;/span&gt; k = 0; k &amp;lt; cnt; k++ ) {&lt;br /&gt;    &lt;span style="color:blue;"&gt;int&lt;/span&gt; idx = k + &lt;span style="color:blue;"&gt;this&lt;/span&gt;.TopIndex;&lt;br /&gt;    &lt;span style="color:blue;"&gt;if&lt;/span&gt;( idx &gt;= &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Items.Count )&lt;br /&gt;      &lt;span style="color:blue;"&gt;break&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:blue;"&gt;bool&lt;/span&gt; selected = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.SelectedIndices.Contains(idx);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color:blue;"&gt;if&lt;/span&gt;( selected )&lt;br /&gt;      e.Graphics.FillRectangle(sel_bg_brush, x, y, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.Width, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ItemHeight);&lt;br /&gt;&lt;br /&gt;    fore_brush.Color = selected ? &lt;span style="color:blue;"&gt;this&lt;/span&gt;.SelForeColor : &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ForeColor;&lt;br /&gt;    &lt;span style="color:blue;"&gt;string&lt;/span&gt; val = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.GetItemText(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.Items[idx]);&lt;br /&gt;    e.Graphics.DrawString(val, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Font, fore_brush, x, y);&lt;br /&gt;    y += &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ItemHeight;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  sel_bg_brush.Dispose();&lt;br /&gt;  fore_brush.Dispose();&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;Custom properties SelForeColor and SelBackColor are exposed to Designer - to customize color of selected items. Since our mode is OwnerDrawFixed, we still override OnDrawItem(), but this time it just invalidates the control, making Windows to invoke our OnPaint(). One more important method to override is OnSelectedIndexChanged(), we call Refresh() there to immediately redraw the control (unlike Invalidate, where redraw is postponed till next Update). Without this, our selection bar does not get redrawn until selection changes the next time: it is always behind, showing the previously selected item&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;protected override void&lt;/span&gt; OnDrawItem(&lt;span style="color:darkcyan;"&gt;DrawItemEventArgs&lt;/span&gt; e)  { &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Invalidate(); }&lt;br /&gt;&lt;span style="color:blue;"&gt;protected override void&lt;/span&gt; OnSelectedIndexChanged(&lt;span style="color:darkcyan;"&gt;EventArgs&lt;/span&gt; e) { &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Refresh(); }&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;The code worked quite well, failing in only one point: the vertical scrollbar. The scrollbar gets painted by Windows at intervals which do not to seem to be related to control redrawns - and I couldn't find any way to force its redraw. Moreover, sometimes the bar is only partially painted: only slider is shown without the up/down buttons. While this is unacceptable, the behavour is only observed for data-bound controls. Finally, I ended up catching BindingSource's ListChanged event and adding items to the list manually. Since the ListBox is append-only, that was simple.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;This is not the complete truth though. My ListBox's binding source is a subordinate to another BS. I had to catch changes to that parent BS as well and reload entire ListBox contents. Another improvement was to make ListBox auto-scroll when selected item is the last one. This way the list would auto-scroll unless user moved out of the last line. This is achieved by setting SelectedIndex to Items.Count-1, although this would add the item to selection rather than replace it for multi-selected ListBoxes. The code is irrelevant, I might provide it some other time.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;BackgroundBitmap:&lt;/b&gt;&lt;br /&gt;With the scrollbar workaround the solution worked, but I wanted more. While ListBox is placed immediately on gradient panel, everything works fine. But a couple of days later I ran out of screen space and decided to move the ListBox into a TabControl page. This changed immediate parent to TabControl and the transparency effect was lost.&lt;br /&gt;&lt;br /&gt;The next idea was to give up on transparency and paint ListBox background from an image, saved by its any parent. The "parent" may not be immediate nor even host ListBox: we just tell the ListBox where to get its underlying image from.&lt;br /&gt;&lt;br /&gt;For this to work, parent must expose its background image somehow: an interface was extracted to indicate so:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;public interface&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;IExposingBackground&lt;/span&gt; { &lt;span style="color:darkcyan;"&gt;Bitmap&lt;/span&gt; BackgroundBitmap(); }&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;Implementation is simple enough: since parent is drawing itself with OnPaint(), we modify it a bit to save image in a bitmap prior to drawing:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;public class&lt;/span&gt; FancyPanel : ..., &lt;span style="color:darkcyan;"&gt;&lt;b&gt;IExposingBackground&lt;/b&gt;&lt;/span&gt; {&lt;br /&gt;  &lt;span style="color:blue;"&gt;private&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Bitmap&lt;/span&gt; bgr_bmp;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;public&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Bitmap&lt;/span&gt; BackgroundBitmap() { &lt;span style="color:blue;"&gt;return&lt;/span&gt; bgr_bmp; }&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;private void&lt;/span&gt; init_bitmap(&lt;span style="color:darkcyan;"&gt;Graphics&lt;/span&gt; g) {&lt;br /&gt;    &lt;span style="color:blue;"&gt;if&lt;/span&gt;(    bgr_bmp == &lt;span style="color:blue;"&gt;null&lt;/span&gt;&lt;br /&gt;        || bgr_bmp.Width  != &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.Width&lt;br /&gt;        || bgr_bmp.Height != &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.Height&lt;br /&gt;    ) {&lt;br /&gt;      &lt;span style="color:blue;"&gt;if&lt;/span&gt;( bgr_bmp != &lt;span style="color:blue;"&gt;null&lt;/span&gt; )&lt;br /&gt;        bgr_bmp.Dispose();&lt;br /&gt;      bgr_bmp = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Bitmap&lt;/span&gt;(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.Width, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle.Height, g);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  &lt;span style="color:blue;"&gt;private void&lt;/span&gt; OnPaint(&lt;span style="color:darkcyan;"&gt;PaintEventArgs&lt;/span&gt; e) {&lt;br /&gt;    init_bitmap(e.Graphics);&lt;br /&gt;    &lt;span style="color:darkcyan;"&gt;Graphics&lt;/span&gt; g = &lt;span style="color:darkcyan;"&gt;Graphics&lt;/span&gt;.FromImage(bgr_bmp);&lt;br /&gt;    &lt;i&gt;&lt;span style="color:darkmagenta;"&gt;... draw background on g ...&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;    e.Graphics.DrawImageUnscaledAndClipped(bgr_bmp, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ClientRectangle);&lt;br /&gt;    g.Dispose();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;&lt;i&gt;The background bitmap works as a double buffer, therefore  buffering provided by .NET can be disabled to conserve memory. This is achieved by calling SetStyle(ControlStyles.OptimizedDoubleBuffer, false) in the control's  constructor.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Transparent ListBox can now use parent's bitmap to draw any part of its background:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;private&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Control&lt;/span&gt; parentBgr;&lt;br /&gt;&lt;span style="color:blue;"&gt;private&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Point&lt;/span&gt;   offset_in_parent = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Point&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;private void&lt;/span&gt; paint_background(&lt;span style="color:darkcyan;"&gt;Graphics&lt;/span&gt; g, &lt;span style="color:darkcyan;"&gt;Rectangle&lt;/span&gt; rect) {&lt;br /&gt;  &lt;span style="color:darkcyan;"&gt;Bitmap&lt;/span&gt; bmp;&lt;br /&gt;  &lt;span style="color:blue;"&gt;if&lt;/span&gt;( ParentBackground != &lt;span style="color:blue;"&gt;null&lt;/span&gt; &amp;&amp;amp;&lt;br /&gt;    (bmp = ((&lt;span style="color:darkcyan;"&gt;IExposingBackground&lt;/span&gt;)ParentBackground).BackgroundBitmap()) != &lt;span style="color:blue;"&gt;null&lt;/span&gt;&lt;br /&gt;  ) {&lt;br /&gt;    &lt;b&gt;g.DrawImage(bmp, rect,&lt;br /&gt;      offset_in_parent.X + rect.X, offset_in_parent.Y + rect.Y,&lt;br /&gt;      rect.Width, rect.Height,&lt;br /&gt;      &lt;span style="color:darkcyan;"&gt;GraphicsUnit&lt;/span&gt;.Pixel&lt;br /&gt;    );&lt;/b&gt;&lt;br /&gt;  } &lt;span style="color:blue;"&gt;else&lt;/span&gt; {&lt;br /&gt;    &lt;span style="color:darkcyan;"&gt;Brush&lt;/span&gt; brush = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;SolidBrush&lt;/span&gt;(&lt;span style="color:blue;"&gt;this&lt;/span&gt;.BackColor);&lt;br /&gt;    g.FillRectangle(brush, rect);&lt;br /&gt;    brush.Dispose();&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;Variable parentBgr (exposed via property ParentBackground) is set at design time.&lt;br /&gt;&lt;br /&gt;There is one more thing though. Since our "background-exposing" parent control may not be the immediate one, it takes a bit of coding to properly compute our offset in it. Should the parent be immediate, our offset would be the one available in our &lt;i&gt;Location&lt;/i&gt; property. But since there could be any number of controls between us and the parent, all these Locations must be added up to compute the proper offset. Two helper methods are used for this:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;private void&lt;/span&gt; compute_offset() {&lt;br /&gt;  &lt;span style="color:darkcyan;"&gt;Point&lt;/span&gt; loc = &lt;span style="color:blue;"&gt;new&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;Point&lt;/span&gt;(&lt;span style="color:blue;"&gt;&gt;this&lt;/span&gt;.Location.X, &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Location.Y);&lt;br /&gt;  &lt;span style="color:blue;"&gt;if&lt;/span&gt;( &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ParentBackground != &lt;span style="color:blue;"&gt;null&lt;/span&gt; ) {&lt;br /&gt;    &lt;span style="color:darkcyan;"&gt;Control&lt;/span&gt; p = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Parent;&lt;br /&gt;    &lt;span style="color:blue;"&gt;while&lt;/span&gt;( p != &lt;span style="color:blue;"&gt;null&lt;/span&gt; &amp;&amp;amp; p != &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ParentBackground ) {&lt;br /&gt;      loc.Offset(p.Location);&lt;br /&gt;      p = p.Parent;&lt;br /&gt;    }&lt;br /&gt;    &lt;span style="color:blue;"&gt;if&lt;/span&gt;( p == &lt;span style="color:blue;"&gt;null&lt;/span&gt; )&lt;br /&gt;      &lt;span style="color:blue;"&gt;return&lt;/span&gt;;&lt;br /&gt;  }&lt;br /&gt;  this.offset_in_parent = loc;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;private void&lt;/span&gt; find_suitable_parent() {&lt;br /&gt;  &lt;span style="color:blue;"&gt;if&lt;/span&gt;( DesignMode &amp;&amp;amp; &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ParentBackground == &lt;span style="color:blue;"&gt;null&lt;/span&gt; )&lt;br /&gt;    &lt;span style="color:blue;"&gt;for&lt;/span&gt;(&lt;span style="color:darkcyan;"&gt;Control&lt;/span&gt; p = &lt;span style="color:blue;"&gt;this&lt;/span&gt;.Parent; p != &lt;span style="color:blue;"&gt;null&lt;/span&gt;; p = p.Parent)&lt;br /&gt;      &lt;span style="color:blue;"&gt;if&lt;/span&gt;( p &lt;span style="color:blue;"&gt;is&lt;/span&gt; &lt;span style="color:darkcyan;"&gt;IExposingBackground&lt;/span&gt; ) {&lt;br /&gt;        &lt;span style="color:blue;"&gt;this&lt;/span&gt;.ParentBackground = p;&lt;br /&gt;        &lt;span style="color:blue;"&gt;break&lt;/span&gt;;&lt;br /&gt;      }&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;These methods are called whenever our &lt;i&gt;Location&lt;/i&gt; changes, parent is changed, or our control is resized. All these handlers look similar, they call the base handler and then recompute the offset. Here is, for example, OnParentChanged:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:blue;"&gt;protected override void&lt;/span&gt; OnParentChanged(&lt;span style="color:darkcyan;"&gt;EventArgs&lt;/span&gt; e) {&lt;br /&gt;  &lt;span style="color:blue;"&gt;base&lt;/span&gt;.OnParentChanged(e);&lt;br /&gt;  find_suitable_parent();&lt;br /&gt;  compute_offset();&lt;br /&gt;}&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;The first procedure, compute_offset(), calculates our position within the parent. The second, find_suitable_parent, is only working in Designer mode. Whenever our ListBox is placed on a form, it re-computes its "background parent" - if not already set. The procedure is merely for programmer's convenience.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Other uses of  BackgroundBitmap:&lt;/b&gt;&lt;br /&gt;Having fancy panels expose their background turned out to be quite handy. Later on I wanted to add a transparent DataGridView. Working with DGV is much easier than with LisBox, which is "all or nothing" sort of thing: we can only override OnPaint thus taking full responsibility for painting the entire control.&lt;br /&gt;&lt;br /&gt;Unlike ListBox, DGV allows us to handle every aspect of cell painting, we can override just a little bit and let the control do the rest. All I had to do is to override PaintBackground and OnRowPrepaint methods. Both methods looked very similar to paint_background() above, except that they called their base handlers in the "else" part.&lt;br /&gt;&lt;br /&gt;I've also overrode OnScroll() (another method missing in ListBox, one must override WndProc to handle scrolls) to call the base handler  and then Refresh() the DGV. That is because when scrolling, Windows copies unchanged portion of the screen up or down, and then asks our Paint routine to draw just the first or last line. This doesn't work for gradient backgrounds  and Refresh() is taking care of it.&lt;br /&gt;&lt;br /&gt;As a final touch, double buffering was enabled to eliminate flicker.&lt;br /&gt;&lt;br /&gt;Even later, I added a transparent TabControl with very little pain. There, the main challenge was to make page headers sit on the right side of the control, but this is another story.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_F6O77syiOA4/RuRXn-FiSNI/AAAAAAAAABI/_uD_UBxXzUo/s1600-h/Transparent_controls.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_F6O77syiOA4/RuRXn-FiSNI/AAAAAAAAABI/_uD_UBxXzUo/s400/Transparent_controls.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5108304221537716434" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;hr&gt;&lt;p style="font-family: Arial; text-align: justify;"&gt;&lt;i&gt;This post was written a while ago, but it took me ages to fight Blogger's editor. The silly thing won't preserve code formatting: whenever text is reopened, some "improvements" sneak through. For example, Blogger removes one space character in pre-formatted blocks whenever entry is closed/opened.&lt;br /&gt;&lt;br /&gt;Preview looks ok, hopefully publishing won't garble formatting.&lt;/i&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-8996161328948852943?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/8996161328948852943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=8996161328948852943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/8996161328948852943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/8996161328948852943'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2007/09/transparent-listbox.html' title='Transparent ListBox'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_F6O77syiOA4/RuOXF-FiSMI/AAAAAAAAABA/A12oPDB3cxs/s72-c/transparent_listbox_output.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-6614273174824033358</id><published>2007-07-30T09:39:00.000-07:00</published><updated>2007-08-11T13:08:37.779-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataPump'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>Obscurities in DataPump API: OPEN procedure</title><content type='html'>&lt;div style="text-align: justify;"&gt;&lt;span style="font-size:100%;"&gt;A couple of weeks ago, I ranted on Oracle DataPump API. The post wasn't published because the wording needed polishing; I wanted emotions to calm down before posting it. Today I'm glad it wasn't published at that time: Oracle released version 11g and I'm happy to see some problems were addressed there. I'll publish the original post followed by 11g comments.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The original 10g post:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Don't know who projected Oracle DataPump API, but obviously these people didn't invest much brain in their work. Feels like they started with a robust vision, but as project's deadline approached, something has changed. Maybe their chief architect got replaced with a summer intern. Or perhaps they strengthened their team with a bunch of unexperienced new hires. Or maybe a desperate manager decided to keep team's spirit high by stuffing their fridges with beer.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div style="text-align: justify;"&gt;&lt;span style="font-size:100%;"&gt;Whatever the reason was, the results were demolishing. I'm trying to summarize today's findings, updating the series as new "discoveries" come up.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;OPEN function&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;The definition is as follows:&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;pre space="preserve" class="oac_no_warn"&gt;DBMS_DATAPUMP.OPEN (&lt;br /&gt;operation    IN VARCHAR2,&lt;br /&gt;mode         IN VARCHAR2,&lt;br /&gt;remote_link  IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;job_name     IN VARCHAR2 DEFAULT NULL,&lt;br /&gt;version      IN VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;compression  IN NUMBER DEFAULT KU$_COMPRESS_METADATA&lt;br /&gt;) RETURN NUMBER;&lt;br /&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;"If you have a procedure with more than 5 parameters, you're probably missing some". Indeed. Parameter "compression" is so important, that programmers shall think of it every time they create a job. The choices are so broad, that we can't make up our mind. Yes, we want compression! No, we don't! Yes, we do! Developers spend hours on meetings and management schedules a golf session to decide whenever they want to use such an important option.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Get real. In 99.(9)% of the cases the fraction of metadata is so small, that nobody gives a dime. Everybody wants compression. Just turn it on and put it to a dusty corner - such as SET_PARAMETER() procedure.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Oh, wait - is it here because 11g will offer new compression mode - data compression? Still, everybody loves compression. Turn it on and move it away.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;I will not rant on "mode" much. Perhaps implementation difficulties make it necessary to decide early in the game what kind of 5 exports we want. Perhaps the paradigm was inherited from old exp/imp. I don't know. All I know is that choosing mode imposes limitations on other API calls. More on this in metadata_filter section.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Comments after 11g release:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The comments are based on documentation published on OTN. Maybe the real package is different (this was the case with some API calls in the past) - 11g installation is still being downloaded, but specification of OPEN has changed: parameter COMPRESSION now belongs to SET_PARAMETER procedure.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&amp;lt;paranoid mode&amp;gt;Oracle is reading my mind !&amp;lt;/paranoid mode&amp;gt;&lt;/div&gt;&lt;br /&gt;Not quite. The default is still to compress metadata only.&lt;br /&gt;&lt;br /&gt;Hope Oracle left old version of OPEN in the package to preserve compatibility. Removing it would break existing code (my code will be broken, good thing it is not yet released).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Later, after installing 11.1.0.6/Linux:&lt;br /&gt;&lt;/span&gt;Parameter "Compression" is still in OPEN, it just gone undocumented. &lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-6614273174824033358?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/6614273174824033358/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=6614273174824033358' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/6614273174824033358'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/6614273174824033358'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2007/07/obscurities-in-datapump-api-open.html' title='Obscurities in DataPump API: OPEN procedure'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-6849603931400233869</id><published>2007-07-18T18:53:00.000-07:00</published><updated>2007-07-30T11:15:53.703-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><title type='text'>Two C++ snippets</title><content type='html'>&lt;div style="text-align: justify;"&gt;These code fragments do not belong to AlderPump. I'm posting them here because they came handy few times in the past (about every 4 years), and every time I spent hours searching for them in the piles of files in my source code directories. Let the Internet store them now, hopefully they will be easier to find.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;The first snippet parses SQL statement for bind variables. In Oracle, bind variables begin with colon and followed by variable name: &lt;span style="color: rgb(0, 0, 102);font-family:courier new;" &gt;INSERT INTO tbl(col1, col2, col3) VALUES(:val1, :val2, :val3)&lt;/span&gt;. Here, val1, val2, and val3 are bind variables.&lt;br /&gt;&lt;br /&gt;Function sqlNextBind() is called with an argument, pointing at the beginning of the statement. It returns pointer to the next bind variable or NULL at the end. To find next variable, call the function with the pointer returned by the previous call, plus one.&lt;br /&gt;&lt;br /&gt;Literals (strings, enclosed in single quotes) are recognized and skipped, even for strings contain embedded quotes (by convention, such quotes are doubled).&lt;br /&gt;&lt;br /&gt;SQL comments are recognized and skipped too: both &lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(0, 0, 102);font-family:courier new;" &gt;/* multi-line */&lt;/span&gt;&lt;/span&gt; and  &lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;-- single line&lt;/span&gt; As with SQL, nested multi-line comments are not supported.&lt;br /&gt;&lt;br /&gt;Function sqlCountBinds() demonstrates how to use sqlNextBind().&lt;br /&gt;&lt;pre&gt;&lt;span style="font-size:85%;"&gt;&lt;pre&gt;&lt;strong&gt;const&lt;/strong&gt; &lt;strong&gt;char&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;sqlNextBind&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;strong&gt;const&lt;/strong&gt; &lt;strong&gt;char&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;int&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;lit&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;, &lt;span style="color: rgb(32, 64, 160);"&gt;cmt&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;for&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;cmt&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'&lt;span style="color: rgb(119, 221, 119);"&gt;\'&lt;/span&gt;'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;lit&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;lit&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;lit&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;  &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;':'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;break&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'-'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'-'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;strchr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;2&lt;/span&gt;, &lt;span style="color: rgb(0, 128, 0);"&gt;'&lt;span style="color: rgb(119, 221, 119);"&gt;\n&lt;/span&gt;'&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;break&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt; &lt;strong&gt;else&lt;/strong&gt; &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'/'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'*'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: rgb(32, 64, 160);"&gt;cmt&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt; &lt;strong&gt;else&lt;/strong&gt; &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'*'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'/'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;cmt&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;return&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;ptr&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;int&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;sqlCountBinds&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;strong&gt;const&lt;/strong&gt; &lt;strong&gt;char&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;stmt&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;  int&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;cnt&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;  const&lt;/strong&gt; &lt;strong&gt;char&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;p&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;stmt&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;-&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;  for&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;cnt&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;p&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;sqlNextBind&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;p&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;p&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;p&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;cnt&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 68);"&gt;/* do nothing */&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;  return&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;cnt&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div style="text-align: justify;"&gt;&lt;span style="font-family:Georgia,serif;"&gt;&lt;/span&gt;Another snippet comes from unknown author. I found it 20 years ago in a program whose name is forgotten. Nevertheless the code had not rusted, it still works perfectly. The program matches string against a wildcard pattern, just like OS does when looking for files matching the mask. Recognized wildcards are '*' and '?', the comparison is case-insensitive. To make it case-sensitive, remove calls to tolower().&lt;br /&gt;&lt;/div&gt;&lt;pre&gt;&lt;span style="font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;&lt;strong&gt;static&lt;/strong&gt; &lt;strong&gt;bool&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;matches&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;strong&gt;const&lt;/strong&gt; &lt;strong&gt;char&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt;, &lt;strong&gt;const&lt;/strong&gt; &lt;strong&gt;char&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;register&lt;/strong&gt; &lt;strong&gt;int&lt;/strong&gt;  &lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;register&lt;/strong&gt; &lt;strong&gt;bool&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;star&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;new_segment&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;star&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;strong&gt;false&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;while&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;*&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'*'&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;star&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;strong&gt;true&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;test_match&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;for&lt;/strong&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'*'&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;  &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;tolower&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;tolower&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;      &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;return&lt;/strong&gt; &lt;strong&gt;false&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt;  &lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'?'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;continue&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;star&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;return&lt;/strong&gt; &lt;strong&gt;false&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;strong&gt;goto&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;test_match&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'*'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;{&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;strong&gt;goto&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;new_segment&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;return&lt;/strong&gt; &lt;strong&gt;true&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&amp;&lt;/span&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;pWild&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;[&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;i&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;-&lt;/span&gt;&lt;span style="color: rgb(255, 0, 0);"&gt;1&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;]&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 128, 0);"&gt;'*'&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;return&lt;/strong&gt; &lt;strong&gt;true&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;if&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;(&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;!&lt;/span&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;star&lt;/span&gt; &lt;span style="color: rgb(68, 68, 255);"&gt;)&lt;/span&gt; &lt;strong&gt;return&lt;/strong&gt; &lt;strong&gt;false&lt;/strong&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(32, 64, 160);"&gt;pString&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;+&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;goto&lt;/strong&gt; &lt;span style="color: rgb(32, 64, 160);"&gt;test_match&lt;/span&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(68, 68, 255);"&gt;&lt;strong&gt;}&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;PS: The indentation got partially lost during formatting. This blog editor is not well suited for code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-6849603931400233869?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/6849603931400233869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=6849603931400233869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/6849603931400233869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/6849603931400233869'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2007/07/two-c-snippets.html' title='Two C++ snippets'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-6644955001584005899</id><published>2007-07-16T21:16:00.000-07:00</published><updated>2008-12-09T23:05:03.080-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='GUI'/><title type='text'>GUI: A wizard layout</title><content type='html'>&lt;div style="text-align: justify;"&gt;The back end part is somewhat complete. There are still glitches especially with job control, but it is mostly working. I'm concentrating on the GUI now. The program consists of two major forms: one to show running jobs and their progress, and another is job creation wizard.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;To my surprise, .NET does not have facility to create wizards. Searching the Internet revealed several commercial and non commercial components, but I didn't like either of them much: some enforce the "standard" layouts which waste too much of valuable space. In others, every page is a separate form making them hard to cross-reference variables.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;After all, the approach doesn't seem to work well in my case: working your way through wizards is like observing a big painting though a keyhole. We're guided through myriad of pages (some  having a single check box on the entire page) and on page ten nobody remembers where it all started. Navigating back and forth is a joke: we unmark a checkbox on page two to find that this action has invalidated page five and all the data must be re-typed again.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;In DataPump, there are several dozen parameters for user to set, most of them depend on each  other. Because of these dependencies, a Wizard approach kind of makes sense, although putting every page on a separate form is out of question.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;TabControl looks like a good starting point; but it needs polishing:&lt;br /&gt;&lt;ul style="text-align: justify;"&gt;&lt;li&gt;Navigation by clicking on tabs doesn't work well. There will be quite a few tabs and they are unlikely to fit on a single line.  Multi-line tabs could alleviate the problem but not cure it: I'm planning to support job templates with every template located on a separate tab. With multi-line tabs I'll loose control on the program's appearance.&lt;/li&gt;&lt;/ul&gt;&lt;ul style="text-align: justify;"&gt;&lt;li&gt;I'm planning to set tab labels style according to their state: valid/invalid, required/optional, and enabled/disabled. This computes to 8 different styles. But TabControl does not support style changes. Not only we can not color individual tabs, but we can not change the color at all!&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:85%;"&gt;&lt;rant&gt;&lt;br /&gt;&lt;/rant&gt;&lt;/span&gt;&lt;div style="text-align: justify;"&gt;&lt;span style="font-size:85%;"&gt;Tab Control is extremely useful, but it has several serious limitations. Color of tabs band is not changeable; the tabs band can not be hidden; there is no page visibility property: to show/hide a tab one must add/remove the page from the container control; when tabs are located on the left or right, the text is always vertical. These things are so little, but .NET 3.0 is shipping, and they are still not there.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Hiding tabs band is simple enough, thanks to &lt;a href="http://www.dotnetrix.co.uk/tabcontrols.html"&gt;Mick Dohertys&lt;/a&gt; who published "Add a HideTabs property to turn on/off the Tabs" snippet. The tabs are visible at design mode and turned off at runtime. This causes all the text to jump up a bit, but most of my pages have a docked control which resizes itself to fill the extra space.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;For navigation, I've put a tree view on left side of the form. Tree view adds ability to collapse branches thus making more space for items currently in use. This allows to extend program's functionality. Job templates will be grouped under top level "Templates" node.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://www.dotnetrix.co.uk/tabcontrols.html"&gt;&lt;/a&gt;Here is how it looks:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_F6O77syiOA4/RpxJFKDfY_I/AAAAAAAAAAw/KFkS91Yo24k/s1600-h/job_creation_files.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_F6O77syiOA4/RpxJFKDfY_I/AAAAAAAAAAw/KFkS91Yo24k/s400/job_creation_files.png" alt="" id="BLOGGER_PHOTO_ID_5088022031968986098" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;The tree view on the left shows status of every job component. Invalid items are red, required are bold, disabled are gray.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;The ability to color TreeView items helps a lot - items requiring attention are easily spotted. I've also added "Consistency problems" page: it summarizes all inter-parameter issues. Setting a parameter to a conflicting value highlights the problem pages and adds a record to the consistency tab. This way users can spot the effect of their changes right away.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Btw, Joel has criticized such design in his "&lt;a href="http://www.joelonsoftware.com/uibook/chapters/fog0000000060.html"&gt;User Interface Design for Programmers&lt;/a&gt;" book. He says the tab control and tree view look disconnected: users have troubles linking them together. I have two excuses: firstly I hope users learned a bit or two since 1992; and secondly, the program is designed for Oracle DBAs who are familiar with computers and were able to learn much more complex concepts than a disjoined control.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;Talking about usability, there is one thing I don't like about Files page: the order of controls. It is natural to have directory to come before file. But directory is limited to 30 characters, while file can be up to is 512.  Current layout leaves more horizontal space to file name. Moving file below directory would bring Max Size field before File, making the order unnatural again.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;The good thing typically missing in other GUI programs is immediate response. Problem items are highligted with pink background. And the text of "Add file" button is changing depending on the intended action. The text can read "Add log" or "Modify dump" or "Type mismatch" or other message.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div style="text-align: justify;"&gt;The Directories dialog is also customized. I wanted to present not only directory name, but also its status (readable and/or writable) and the server path. This requires 3 columns, but WinForms ComboBox control only supports one. Fortunately, &lt;span id="_ctl0_MainContent_Userinfo1" name="Userinfo1"&gt;RemcoJVG posted a &lt;a href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=140260&amp;amp;SiteID=1"&gt;good example&lt;/a&gt; on how to extend the control. I rewrote the code in C# and tweaked it a bit exposing gradient colors as properties and computing text area more precisely. Now, the problem is that the control with its gradient brush and mouse navigation looks and behaves so good, that other built-in ComboBoxes do not look right any more. I'm ought to either replace all such controls to &lt;/span&gt;&lt;span id="_ctl0_MainContent_Userinfo1" name="Userinfo1"&gt;RemcoJVG's or let users suffer from the inconsistency.&lt;/span&gt;&lt;br /&gt;&lt;span id="_ctl0_MainContent_Userinfo1" name="Userinfo1"&gt;&lt;/span&gt;&lt;/div&gt;&lt;span id="_ctl0_MainContent_Userinfo1" name="Userinfo1"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-6644955001584005899?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/6644955001584005899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=6644955001584005899' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/6644955001584005899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/6644955001584005899'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2007/07/gui-wizard-layout.html' title='GUI: A wizard layout'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_F6O77syiOA4/RpxJFKDfY_I/AAAAAAAAAAw/KFkS91Yo24k/s72-c/job_creation_files.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7056200683605340623.post-7802687829461080811</id><published>2007-07-04T21:21:00.000-07:00</published><updated>2007-07-19T18:59:52.311-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Data Provider'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>In search for Data Provider</title><content type='html'>In .NET, access to Oracle in accomplished via so called Data Providers. To a developer - they are merely assemblies  exposing objects and their methods to connect, run a query, traverse result set, etc. A few providers come with .NET; Oracle supplies their own; and a number of third party ones exist.&lt;br /&gt;&lt;br /&gt;Picking a right data provider is essential for applications deployed to end users, there are several options to consider:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Third party providers do not quite fit for this project.&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;The product is a simple (at least from user perspective) self-contained utility. There should be as little external dependencies as possible, ideally zero. Shipping with a third party assembly may require extra configuration. And any bugs in their code will be seen as flaws of the application. Depending on other company's will to fix bugs may ruin product's creditability - I'm not taking this chance.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;For reference, not all third party providers introduce extra dependencies. As an example, &lt;/span&gt;&lt;a style="font-style: italic;" href="http://www.crlab.com/oranet"&gt;OraDirect for .NET&lt;/a&gt;&lt;span style="font-style: italic;"&gt; &lt;span style="font-weight: bold;"&gt;reduces&lt;/span&gt; dependency to Oracle client software by offering a direct TCP &lt;/span&gt;&lt;span style="font-style: italic;"&gt;database&lt;/span&gt;&lt;span style="font-style: italic;"&gt; access (similarly to Java type-4 thin driver).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Oracle supplied provider does not fit for the same reason&lt;/u&gt;&lt;br /&gt;To use the application, users must download a 450M Oracle client CD and install support for .NET. A bit of overkill for a little program like this. Think I'll be having hard time to convince people to install &lt;span style="font-weight: bold;"&gt;my&lt;/span&gt; program - not talking about registering on OTN, downloading the multi-meg file, unzipping it, running installer, navigating to .NET assembly and installing it. They'll forget the installer's option to pick by the time they get there.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;I really regret this because Oracle provider is such a luxury. In addition to the basic features every provider has, it adds tons of useful functionality including a few critical options. Oracle Corporation &lt;/span&gt;&lt;span style="font-style: italic;"&gt;really &lt;/span&gt;&lt;span style="font-style: italic;"&gt;should stop thinking big projects and enterprise clients. [Added after July 11, 2007: Oracle just announced "Oracle 11" which supports instant client for .NET". This should address all Oracle Provider issues below - after it is released officially].&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;We are left with providers coming along with .NET framework. All of them work out of the box and need no extra configuration.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Microsoft Data Provider for Oracle&lt;/u&gt;&lt;br /&gt;This was my choice previously, but this time it didn't work. Here is why:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;SYSDBA connections are not supported.&lt;br /&gt;Connect in SYSDBA is accomplished by adding "DBA Privilege=SYSDBA" to the connection string. To a surprise, attempts to do so yields "keyword is not supported" exception. Searched Google and yes, Microsoft provider does not support AS SYSDBA.&lt;br /&gt;&lt;br /&gt;I'm working on a system utility to be used by DBAs. There are big chances the DBAs would want to connect as SYS or other SYSDBA user - and what do I tell them? "Sorry, because of limitation of Microsoft Data Provider, SYSDBA connections are not supported"? People do not care who's fault it is, they'll just march away.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;Can't cancel running statements&lt;br /&gt;Not widely used feature, but crucial in my  case. The application calls DataPump procedure get_status() waiting indefinitely for an event of interest to occur. Now, when user wants to stop or pause the job, we're ought to tell Oracle to do so by calling stop_job(). But we can't - we're in the middle of get_status call.&lt;br /&gt;&lt;br /&gt;One workaround is to invoke get_status() in non-blocking way. The procedure takes timeout parameter; we could poll database in a loop checking cancellation flag between the calls. The question is the value of the timeout interval. Make it too short, and program will trash the database (which is already busy pumping data). Make it too long, and user will loose patience waiting for response. Saved the idea for last resort.&lt;br /&gt;&lt;br /&gt;Another method is to abort the running query. OracleCommand class has method Cancel() which is a bit strange: it is not guaranteed to work - and doesn't. As if this wasn't enough, there is no indication of whenever cancellation succeeded. MSDN reference reads: &lt;blockquote&gt;"If there is nothing to cancel, nothing happens. However, if there is a  command in process, and the attempt to cancel fails, no exception is  generated."&lt;/blockquote&gt;Way to go.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;There is no way to control fetch buffer size.&lt;br /&gt;When query is returning resultset, there is a number of exchanges between server and the client. Server fills a buffer and ships it to the client, indicating whenever more data is available. The client consumes the buffer and asks server for another chunk. This is called a roundtrip. The number of roundtrips depends on amount of data to retrieve (which we can not control) and buffer size. Obviously, the smaller the buffer, the more roundtrips are needed.&lt;br /&gt;&lt;br /&gt;How bad the roundtrips are? In one of the companies I worked for, a test fetch of 100,000 rows one row at a time, took 30 minutes. Changing buffer size to 5000 rows reduced the time to 2 seconds. There is no typo in the figures: the same query ran 900 times faster.&lt;br /&gt;&lt;br /&gt;Who controls the buffer size? The client program, by calling OCI function OCIAttrSet with either OCI_ATTR_PREFETCH_ROWS or OCI_ATTR_PREFETCH_MEMORY.&lt;br /&gt;&lt;br /&gt;How this function is exposed to us, end users and developers - depends on the client program or high-level library we're using. SqlPlus has parameter ARRAYSIZE. Oracle Export and Import utilities are using BUFFER. Informatica is using DatabaseArrayOperatorSize setting. Oracle provider for .NET offers property FetchSize in OracleCommand and OracleDataReader.&lt;br /&gt;&lt;br /&gt;Microsoft Data Provider for Oracle supplies nothing.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;This is really strange shortcoming, and I don't believe there is a technical reason for this.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;AlderPump does not fetch lots of data. Think the biggest result set comes from DATABASE_EXPORT_OBJECTS and similar tables - when creating a job.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;u&gt;Considering the above, the decision was to stick with OleDb provider.&lt;/u&gt;&lt;br /&gt;The provider comes with .NET just as Microsoft Data Provider, and does not require configuration nor brings extra dependencies. Connecting AS SYSDBA works. Queries are cancelled as expected. The only limitation remaining is fetch buffer size.&lt;br /&gt;&lt;br /&gt;So far I only bumped into one strange problem, but it was easy to fix:&lt;br /&gt;&lt;br /&gt;When running certain anonymous PL/SQL blocks, the provider fails to bind output variables and input variables are replaced with literal values.&lt;br /&gt;&lt;br /&gt;Here is sample program:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;using System;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;using System.Data;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;using System.Data.OleDb;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;public class test {&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; public static void Main() {&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;   try {&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     OleDbConnection conn = new OleDbConnection();&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     conn.ConnectionString = "Provider=MSDAORA;User ID=scott;Password=tiger;Data Source=ora10g";&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     conn.Open();&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     OleDbCommand cmd = new OleDbCommand("", conn);&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     cmd.CommandText =&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; "declare&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; n number;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; begin &lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;? := 123;&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;end;"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     cmd.Parameters.Add("?", OleDbType.Numeric).Direction = ParameterDirection.Output;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     //cmd.Parameters.Add("?", OleDbType.Numeric).Value = 321;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     Console.WriteLine("parameter added");&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     cmd.ExecuteNonQuery();&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     decimal d = (decimal)cmd.Parameters[0].Value;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     Console.WriteLine("result: {0}", d);&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;   } catch( Exception x ) {&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     Console.WriteLine(x.Message);&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;   }&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; }&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;}&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;The code is throwing Oracle exception "Not all variables bound".&lt;br /&gt;&lt;br /&gt;After several hours trying to find a workaround, I almost accidentally enclosed the block into another begin/end pair - and it worked:&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;     cmd.CommandText =&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; &lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;&lt;span style="font-weight: bold; color: rgb(204, 0, 0);"&gt;begin&lt;/span&gt; declare&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; n number;&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt; begin &lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;? := 123;&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;end; &lt;span style="font-weight: bold; color: rgb(204, 0, 0);"&gt;end;&lt;/span&gt;"&lt;/span&gt;&lt;span style="color: rgb(0, 0, 102);font-size:85%;" &gt;;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;More problems may be lurking around, but so far OleDb provider is working.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;And yes, the ODBC Data Provider.&lt;/u&gt;&lt;br /&gt;I didn't test this one. ODBC requires an Oracle Driver to be installed (from Oracle, Microsoft or other party), and I don't think the driver is installed on every machine. Installing it requires either MDAC (Microsoft) or Oracle Client CD (Oracle). Nah.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7056200683605340623-7802687829461080811?l=alderprogs.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://alderprogs.blogspot.com/feeds/7802687829461080811/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=7056200683605340623&amp;postID=7802687829461080811' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/7802687829461080811'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7056200683605340623/posts/default/7802687829461080811'/><link rel='alternate' type='text/html' href='http://alderprogs.blogspot.com/2007/07/in-search-for-data-provider.html' title='In search for Data Provider'/><author><name>Alder</name><uri>http://www.blogger.com/profile/07308119679241522149</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
