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