Like I alluded to in the post Using win32 GUI from posix code using mixedmode. , there are more than one way to write mixed mode programs.
There are atleast two distinct approaches that we can take when we write code that transgresses both posix and win32 worlds.
1. SUA code with inline calls to win32 functions
2. SUA Application that links to a custom Win32 dll which exposes functions and data whose datatypes are consistent with that of SUA datatypes.
From a technical standpoint, both these approaches are the same. Even in the second approach we still make calls to win32 functions within posix code; but the difference lies in the fact that in the second approach we make calls to custom written functions that act as wrappers to the win32 function/ code that we want to access from posix code.
I’ll discuss the first approach in detail in this post. The second approach will have to wait for the next post J.
SUA Code with inline calls to win32 functions:
‘inline’ here doesn’t refer to inline functions , rather it refers to the practice of calling win32 functions from within posix/sua code. In this paradigm, calls to Win32 functions are interspersed in the SUA Applications code. In other words calls to Win32 functions will be in the same source file as that of SUA code. This is an intuitive and straight forward approach and increases readability and enables easier debugging as developers need not develop additional libraries as is the case with the other approach. Also not e that, this is the only approach possible when only static libraries of the Win32 library is available.
If I were to break down this approach into simple steps, this is how it will look.
1. Identify the function in the Win32 library that you want to call in SUA/POSIX code.
2. Identify headers that need to be included to call the win32 call identified in step 1.
3. Include the header in the SUA/POSIX code and make calls to the WIN32 function .
Note: When some Windows hearders are included in a SUA/POSIX application, the application may not compile because of conflict in data type definitions. This happens because particular datatype/function prototypes/macros may be defined in different fashion in the Windows header from that of SUA headers. I’ll discuss at the end of this post how to identify and resolve header conflicts.
4. If you have to use the data that is returned by the Win32 call in SUA/POSIX, make sure care is taken to covert data from the type that the Win32 call returned to the type that SUA application is expecting.
Lest look a sample code that was written following the above approach. Below is a small SUA application that connects to SQL server, creates a table and fills the table with data.
Function calls that start with SQL* are win32 calls that reside in the odbc32.dll which is a win32 dll.
#include <stdio.h>
#include "CommonHeader.h"
#define ROWS 3
#define NEWBALANCEVALUE 2000
#define TIMESTOSQLFETCHSCROLL 5
#define SQLTEXTLEN 1024
/* buffers for inserting values into the table */
#define INPUTCOUNT 9
int accno_array[]={1,2,3,4,5,6,7,8,9};
float bal_array[]={1000,2000,3000,4000,5000,6001,7000,800,9000};
SQLRETURN CreateAndFillTables(SQLHENV henv,SQLHDBC hdbc)
{
SQLRETURN retcode;
SQLINTEGER ACCOUNT_IDArray[ROWS];
SQLINTEGER ACCOUNT_IDIndArray[ROWS];
SQLINTEGER BalanceArray[ROWS];
SQLINTEGER BalanceIndArray[ROWS];
SQLUSMALLINT RowStatusArray[ROWS], RowNum;
SQLHSTMT hstmt;
SQLCHAR cursorName[NAME_LEN];
SQLINTEGER cursorLen;
SQLINTEGER NewBalance=NEWBALANCEVALUE;
SQLINTEGER NewBalanceInd;
char SQL_Text[SQLTEXTLEN];
int CursorTypeValue = 0;
int i=0,k=0;
if(SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt)!=0)
printf("Error In Setting The Connection Handler\n");
return MYFAIL;
}
retcode=SQLSetStmtAttr(hstmt, (SQLINTEGER)SQL_ATTR_CONCURRENCY, (SQLPOINTER)SQL_CONCUR_VALUES,0);
if(retcode== SQL_ERROR)
else if(retcode==SQL_SUCCESS_WITH_INFO)
printf("Required cursor not set, Cursor type set is %d, but wanted %d \n\n",CursorTypeValue,SQL_CONCUR_VALUES);
retcode=SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE,(SQLPOINTER)SQL_CURSOR_KEYSET_DRIVEN,0);// , 0);
printf("Required cursor not set, Cursor type set is %d, but wanted %d\n\n",CursorTypeValue,SQL_CURSOR_KEYSET_DRIVEN);
if(SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE, SQL_BIND_BY_COLUMN, 0)!=0)
if(SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)ROWS, 0)!=0)
if(SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray, 0)!=0)
if(SQLSetCursorName(hstmt, "MY_CURSOR", SQL_NTS))
// Bind arrays to the ACCOUNT_IDArray
if(SQLBindCol(hstmt, 1, SQL_C_SLONG, ACCOUNT_IDArray, 0, ACCOUNT_IDIndArray)!=0)
if(SQLBindCol(hstmt, 2, SQL_C_SLONG, BalanceArray, 0, BalanceIndArray)!=0)
// delete the existing table
SQLExecDirect(hstmt, "drop table accounts", SQL_NTS);// ignore errors if any
/* create the table accounts */
if(SQLExecDirect(hstmt, "create table accounts(account_id int not null unique,bal decimal(11,2))", SQL_NTS)!=0)
/* insert the values in the table*/
for(i=0;i<INPUTCOUNT;i++)
sprintf(SQL_Text,"insert into accounts values (%d,%f)",accno_array[i],bal_array[i]);
if(SQLExecDirect(hstmt, SQL_Text, SQL_NTS)!=0)
// Execute a statement to retrieve rows from the Customers table.
if(SQLExecDirect(hstmt, "select * from ACCOUNTS", SQL_NTS)!=0)
// Fetch and display the rows, from the table
while(1)
if(SQLFetchScroll(hstmt, SQL_FETCH_NEXT, 0))
break;
for(k=0;k<ROWS;k++)
printf("ACCOUNT_ID: %d Balance: %d \n",ACCOUNT_IDArray[k],BalanceArray[k]);
SQLCloseCursor(hstmt);
return MYPASS;
FAILED:
int main(int argc, char **argv)
char dsn[256] = "ODBC-TEST";
char user[256] = "sa";
char passwd[256] = "111_aaa";
SQLHENV henv;
SQLHDBC hdbc;
if (argc > 1)
strcpy(dsn, argv[1]);
if(argc > 2)
strcpy(user, argv[2]);
if(argc > 3)
strcpy(passwd, argv[3]);
if(MYFAIL == ConnectToDB(dsn, user, passwd, &henv, &hdbc))
printf("Could not establish connection\n");
return 1;
CreateAndFillTables(henv, hdbc);
return 0;
You would see lots of dataypes alien to posix used here. These are types that are defined in sql headers like sql.h, sqltypes.h etc.
Below is the code in commonheader.h. I’ve included this so that if you want to try building this application, you have all the sources.
#include <interix/mixedmode.h>
#include <odbc/sql.h>
#include <odbc/sqlext.h>
#include <odbc/sqltypes.h>
#include <odbc/odbcss.h>
#include <stdlib.h>
#include <odbc/odbcinst.h>
#include <errno.h>
#define NAME_LEN 50
#define MYFAIL -1
#define MYPASS 0
/* displays the last error message */
void ShowError(SQLHSTMT hstmt,SQLSMALLINT HandleType)
SQLCHAR SqlState[6], SQLStmt[100], Msg[SQL_MAX_MESSAGE_LENGTH];
SQLINTEGER NativeError;
SQLSMALLINT i, MsgLen;
SQLRETURN rc1, rc2;
i = 1;
while ((rc2 = SQLGetDiagRec(HandleType, hstmt, i, SqlState, &NativeError,Msg, sizeof(Msg), &MsgLen)) != SQL_NO_DATA)
printf("SqlState %s,NativeError %d ,Msg %s \n",SqlState,NativeError,Msg);
i++;
/* establishes the connection with the DB */
int ConnectToDB(char *DSN,char * UserId ,char *passwd,SQLHENV *henv,SQLHDBC *hdbc)
if(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, henv))
printf("SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) failed\n");
if(SQLSetEnvAttr(*henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,0))
printf("SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3,0) failed\n");
if(SQLAllocHandle(SQL_HANDLE_DBC, *henv, hdbc))
printf("SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) failed\n");
if((retcode =SQLSetConnectAttr(*hdbc, (SQLINTEGER)SQL_ATTR_ODBC_CURSORS,(SQLPOINTER)SQL_CUR_USE_IF_NEEDED,0))!=SQL_SUCCESS)//SQL_CUR_USE_ODBC
printf("Error In Using Cursor Library\n");
//Setting Connection Attribute
if((retcode=SQLSetConnectAttr(*hdbc,(SQLINTEGER)SQL_LOGIN_TIMEOUT, (SQLPOINTER )5, 0))!=SQL_SUCCESS)
printf( "Error In Setting The Connection Handler\n");
//Connecting to SQL-SERVER
if((retcode =SQLConnect(*hdbc,(SQLCHAR *)DSN,SQL_NTS, (SQLCHAR *) UserId,SQL_NTS,(SQLCHAR *) passwd,SQL_NTS ))!=SQL_SUCCESS_WITH_INFO)
printf( "Error In Connecting To The SQLSERVER \n");
void CloseConnection(SQLHENV henv,SQLHDBC hdbc)
//Free the Connection Handle
if( hdbc)
SQLFreeHandle( SQL_HANDLE_DBC, hdbc );
//Free the Environment Handle
if( henv)
SQLFreeHandle( SQL_HANDLE_ENV, henv );
The help files that come with ‘Utilities and SDK for SUA’ has detailed help on how to build applications that access SQL and Oracle databases. You can find that in the section ‘Developing database connectivity applications’. I strongly recommend you to read this if you want to develop database applications that talk to either SQL server or Oracle.
Header Conflicts:
A hurdle that one will have to overcome when following the above approach is header conflicts. ‘Header conflicts’ are a result of conflicting type definitions in SUA headers and Windows headers. For example, ULONG is defined in mixedmode.h a SUA/POSIX header. When mixedmode.h and a windows header that defines ULONG is included in a SUA/POSIX application, then a header conflict results. Header conflicts can be identified by looking at the compile error.
Below are sample header conflict errors
With CC/C89
c:\winnt\sua\usr\include\odbc\sqltypes.h(122) : warning C4142: benign redefinition of type
With GCC
In file included from /usr/include/odbc/sql.h:25,
from CommonHeader.h:4,
from sample.c:2:
/usr/include/odbc/sqltypes.h:122: error: conflicting types for `ULONG'
/usr/include/interix/mixedmode.h:33: error: previous declaration of `ULONG'
How to resolve header conflicts?
1. Construct a new header file with the types required by the Win32 function and include that instead of the standard windows header. Often, a type that is not used results in header conflicts.
2. Change the Win32 header to resolve header conflicts. While changing the header it is advisable to make a copy if the headers and change the copy. This method of resolving header conflicts is advisable when the win32 header that resulted in type re declaration error is the first level header included in SUA/POSIX code and the number of re declaration error is limited to few type conflicts.
I strongly recommend this if you are developing sql applications – the help file also suggests this.
3. Use a different programming approach – by segregating the scope of data types of SUA and Windows, thereby removing any possibility of conflict.
This post should have given you a brief idea of one of the approaches in mixed-mode programming, In the next post, I’ll discuss the second approach.