Some implementation of odbccp32

Bill Medland billmedland at mercuryspeed.com
Tue Oct 4 19:27:15 CDT 2005


Bill Medland (billmedland at mercuryspeed.com)
Start some implementation of odbccp32.
This is enough for ACCPAC Advantage Series to run against UnixODBC

Index: include/odbcinst.h
===================================================================
RCS file: /home/wine/wine/include/odbcinst.h,v
retrieving revision 1.6
diff -u -r1.6 odbcinst.h
--- include/odbcinst.h	10 Aug 2005 09:51:40 -0000	1.6
+++ include/odbcinst.h	5 Oct 2005 00:18:23 -0000
@@ -58,6 +58,12 @@
 #define ODBC_ERROR_OUT_OF_MEM			21
 #define ODBC_ERROR_OUTPUT_STRING_TRUNCATED	22
 
+/* Values for SQLGet/SetConfigMode */
+#if (ODBCVER >= 0x300)
+#define ODBC_BOTH_DSN 0
+#define ODBC_USER_DSN 1
+#define ODBC_SYSTEM_DSN 2
+#endif
 
 BOOL WINAPI ODBCCPlApplet(LONG,LONG,LONG*,LONG*);
 BOOL WINAPI SQLConfigDataSource(HWND,WORD,LPCSTR,LPCSTR);
Index: dlls/odbccp32/odbccp32.c
===================================================================
RCS file: /home/wine/wine/dlls/odbccp32/odbccp32.c,v
retrieving revision 1.3
diff -u -r1.3 odbccp32.c
--- dlls/odbccp32/odbccp32.c	17 Jun 2005 21:26:31 -0000	1.3
+++ dlls/odbccp32/odbccp32.c	5 Oct 2005 00:18:23 -0000
@@ -2,6 +2,7 @@
  * Implementation of the ODBC driver installer
  *
  * Copyright 2005 Mike McCormack for CodeWeavers
+ * Copyright 2005 Bill Medland
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -33,6 +34,94 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(odbc);
 
+/* I am sure that these are not hard coded but it will do for now */
+static const char TXT_COMPONENT_NOT_FOUND [] = "Component not found in registry";
+static const char TXT_INVALID_BUFF_LEN [] = "Invalid buffer length";
+static const char TXT_INVALID_PARAM_SEQUENCE [] = "Invalid parameter sequence";
+
+/* Most variables are per-process so there is presumably a critical section
+ * in here too.  Actually there might be several.
+ */
+
+static CRITICAL_SECTION crit;
+
+/* There must be some sort of error stack.  The documentation of
+ * SQLInstallerError seems to suggest it might be a static 8-element array.
+ * That would prevent problems allocating new entries.
+ * Question.  Is SQL_MAX_MESSAGE_LENGTH the length with or without the \0?
+ */
+WORD num_errors = 0;
+typedef struct {
+    DWORD code;
+    CHAR text [SQL_MAX_MESSAGE_LENGTH + 1 /* ? See above */];
+} sqli_error;
+sqli_error error_stack [8];
+
+/* The mode is maintained by SQLGetConfigMode/SQLSetConfigMode and affects
+ * which part of the registry most of the functions access
+ */
+DWORD config_mode = ODBC_BOTH_DSN;
+
+/* End of per-process variables */
+
+/***********************************************************************
+ * DllMain [Internal] Initializes the internal 'ODBCCP32.DLL'.
+ *
+ * PARAMS
+ *     hinstDLL    [I] handle to the DLL's instance
+ *     fdwReason   [I]
+ *     lpvReserved [I] reserved, must be NULL
+ *
+ * RETURNS
+ *     Success: TRUE
+ *     Failure: FALSE
+ */
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    TRACE("Initializing or Finalizing odbccp32: %p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
+
+    if (fdwReason == DLL_PROCESS_ATTACH)
+    {
+        /* FIXME - Should we also ReplicateToRegistry in here as we do in 
+         * the proxyodbc of odbc32.dll?
+         */
+        InitializeCriticalSection (&crit);
+    }
+    return TRUE;
+}
+
+/* Utility functions */
+
+/* The error stack is cleared by many but not all functions */
+static void clear_errors (void)
+{
+    EnterCriticalSection (&crit);
+    num_errors = 0;
+    LeaveCriticalSection (&crit);
+}
+
+/* I am not sure when the translation occurs for standard errors */
+static void push_error (DWORD code, PCSTR text)
+{
+    size_t len;
+    len = text ? strlen (text) : 0;
+    if (len >= sizeof (error_stack[0].text))
+        len = sizeof (error_stack[0].text) - 1;
+    EnterCriticalSection (&crit);
+    if (num_errors < sizeof(error_stack)/sizeof(error_stack[0]))
+    {
+        sqli_error *p = error_stack + num_errors;
+        p->code = code;
+        if (len)
+            memcpy (p->text, text, len);
+        *(p->text + len) = '\0';
+        num_errors++;
+    }
+    /* else merely drop it */
+    LeaveCriticalSection (&crit);
+}
+
 BOOL WINAPI ODBCCPlApplet( LONG i, LONG j, LONG * p1, LONG * p2)
 {
     FIXME( "( %ld %ld %p %p) : stub!\n", i, j, p1, p2);
@@ -85,7 +174,7 @@
 BOOL WINAPI SQLConfigDataSource(HWND hwndParent, WORD fRequest,
                LPCSTR lpszDriver, LPCSTR lpszAttributes)
 {
-    FIXME("\n");
+    FIXME("%p %d %s %s\n", hwndParent, fRequest, lpszDriver, lpszAttributes);
     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
     return FALSE;
 }
@@ -138,9 +227,21 @@
 
 BOOL WINAPI SQLGetConfigMode(UWORD *pwConfigMode)
 {
-    FIXME("\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    /* MSDN states a potential error case to do with memory.  I suggest that 
+     * it is a thorough documentation of the fact that EnterCriticalSection 
+     * can itself throw an exception.  I am not going to implement it here
+     * since the same potential error is not mentioned for other functions
+     * that might do the same.
+     */
+    clear_errors();
+    if (pwConfigMode)
+    {
+        EnterCriticalSection (&crit);
+        *pwConfigMode = config_mode;
+        LeaveCriticalSection (&crit);
+    }
+    /* Yes, there is nothing wrong with passing a null pointer */
+    return TRUE;
 }
 
 BOOL WINAPI SQLGetInstalledDriversW(LPWSTR lpszBuf, WORD cbBufMax,
@@ -151,12 +252,59 @@
     return FALSE;
 }
 
+/* MSDN suggests that this can fail under various circumstances but actually
+ * it seems to return true under almost all circumstances, even missing key 
+ * etc.  We will go with the obvious.
+ */
 BOOL WINAPI SQLGetInstalledDrivers(LPSTR lpszBuf, WORD cbBufMax,
                WORD *pcbBufOut)
 {
-    FIXME("\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    HKEY hDrivers;
+    LONG reg_ret;
+    BOOL we_ret = FALSE;
+    clear_errors();
+    if (!lpszBuf || cbBufMax < 2)
+    {
+        push_error (ODBC_ERROR_INVALID_BUFF_LEN, TXT_INVALID_BUFF_LEN);
+    }
+    else if ((reg_ret = RegCreateKeyExA (HKEY_LOCAL_MACHINE,
+                    "Software\\ODBC\\ODBCINST.INI\\ODBC Drivers", 0, NULL, 
+                    REG_OPTION_NON_VOLATILE, 
+                    KEY_QUERY_VALUE, NULL, &hDrivers, NULL))
+            == ERROR_SUCCESS)
+    {
+        DWORD index;
+        DWORD remaining = cbBufMax - 1;
+        DWORD size;
+        index = 0;
+        while (size = remaining, (reg_ret = RegEnumValueA (hDrivers, index, lpszBuf, &size, 0, 0, 0, 0)) != ERROR_NO_MORE_ITEMS)
+        {
+            /* Currently this works.  However I am not sure how well it fits
+             * with what Windows actually does with the registry.
+             * In fact the native version of odbccp32 gets even more 
+             * confused by what the registry returns and returns twice the
+             * correct value if there is not enough room.
+             */
+            if (reg_ret == ERROR_SUCCESS)
+                size++;
+            lpszBuf += size;
+            remaining -= size;
+            index++;
+        }
+        *(lpszBuf++) = '\0';
+        (void) RegCloseKey (hDrivers);
+        if (pcbBufOut)
+        {
+            *pcbBufOut = (cbBufMax - remaining);
+        }
+        we_ret = TRUE;
+    }
+    else
+    {
+        push_error (ODBC_ERROR_COMPONENT_NOT_FOUND, TXT_COMPONENT_NOT_FOUND);
+    }
+
+    return we_ret;
 }
 
 int WINAPI SQLGetPrivateProfileStringW(LPCWSTR lpszSection, LPCWSTR lpszEntry,
@@ -298,9 +446,38 @@
 SQLRETURN WINAPI SQLInstallerError(WORD iError, DWORD *pfErrorCode,
                LPSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
 {
-    FIXME("\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    RETCODE we_ret;
+    if (iError == 0)
+        return SQL_ERROR;
+
+    EnterCriticalSection (&crit);
+    if (iError > num_errors)
+        we_ret = SQL_NO_DATA;
+    else
+    {
+        size_t len;
+        sqli_error *p = error_stack + iError - 1;
+        if (pfErrorCode)
+            *pfErrorCode = p->code;
+        len = strlen (p->text);
+        we_ret = (len >= cbErrorMsgMax) ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
+        /* The Microsoft (Version 3.525.1022.0, at least) version has the 
+         * obvious off-by-one error in the above test.
+         */
+        /* Return the size before possibly reducing it */
+        if (pcbErrorMsg)
+            *pcbErrorMsg = len;
+        if (lpszErrorMsg && cbErrorMsgMax)
+        {
+            if (len >= cbErrorMsgMax)
+                len = cbErrorMsgMax - 1;
+            memcpy (lpszErrorMsg, p->text, len);
+            *(lpszErrorMsg + len) = '\0';
+        }
+    }
+    LeaveCriticalSection (&crit);
+
+    return we_ret;
 }
 
 BOOL WINAPI SQLInstallTranslatorExW(LPCWSTR lpszTranslator, LPCWSTR lpszPathIn,
@@ -438,9 +615,21 @@
 
 BOOL WINAPI SQLSetConfigMode(UWORD wConfigMode)
 {
-    FIXME("\n");
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return FALSE;
+    clear_errors();
+    if (wConfigMode != ODBC_USER_DSN && wConfigMode != ODBC_SYSTEM_DSN &&
+            wConfigMode != ODBC_BOTH_DSN)
+    {
+        push_error (ODBC_ERROR_INVALID_PARAM_SEQUENCE, TXT_INVALID_PARAM_SEQUENCE);
+        return FALSE;
+
+    }
+    else
+    {
+        EnterCriticalSection (&crit);
+        config_mode = wConfigMode;
+        LeaveCriticalSection (&crit);
+        return TRUE;
+    }
 }
 
 BOOL WINAPI SQLValidDSNW(LPCWSTR lpszDSN)




More information about the wine-patches mailing list