fixed Windows build errors
[public/netxms.git] / src / db / dbdrv / db2 / db2.cpp
1 /*
2 ** DB2 Database Driver
3 ** Copyright (C) 2010-2016 Raden Solutinos
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 **/
20
21 #define _CRT_SECURE_NO_WARNINGS
22
23 #include "db2drv.h"
24
25 DECLARE_DRIVER_HEADER("DB2")
26
27 /**
28 * Convert DB2 state to NetXMS database error code and get error text
29 */
30 static DWORD GetSQLErrorInfo(SQLSMALLINT nHandleType, SQLHANDLE hHandle, NETXMS_WCHAR *errorText)
31 {
32 SQLRETURN nRet;
33 SQLSMALLINT nChars;
34 DWORD dwError;
35 SQLWCHAR buffer[DBDRV_MAX_ERROR_TEXT];
36
37 // Get state information and convert it to NetXMS database error code
38 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_SQLSTATE, buffer, 16, &nChars);
39 if (nRet == SQL_SUCCESS)
40 {
41 #if UNICODE_UCS2
42 if (
43 (!wcscmp((WCHAR *)buffer, L"08003")) || // Connection does not exist
44 (!wcscmp((WCHAR *)buffer, L"08S01")) || // Communication link failure
45 (!wcscmp((WCHAR *)buffer, L"HYT00")) || // Timeout expired
46 (!wcscmp((WCHAR *)buffer, L"HYT01")) || // Connection timeout expired
47 (!wcscmp((WCHAR *)buffer, L"08506"))) // SQL30108N: A connection failed but has been re-established.
48 #else
49 char state[16];
50 ucs2_to_mb(buffer, -1, state, 16);
51 if (
52 (!strcmp(state, "08003")) || // Connection does not exist
53 (!strcmp(state, "08S01")) || // Communication link failure
54 (!strcmp(state, "HYT00")) || // Timeout expired
55 (!strcmp(state, "HYT01")) || // Connection timeout expired
56 (!strcmp(state, "08506"))) // SQL30108N: A connection failed but has been re-established.
57 #endif
58 {
59 dwError = DBERR_CONNECTION_LOST;
60 }
61 else
62 {
63 dwError = DBERR_OTHER_ERROR;
64 }
65 }
66 else
67 {
68 dwError = DBERR_OTHER_ERROR;
69 }
70
71 // Get error message
72 if (errorText != NULL)
73 {
74 #if UNICODE_UCS2
75 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, errorText, DBDRV_MAX_ERROR_TEXT, &nChars);
76 #else
77 nRet = SQLGetDiagFieldW(nHandleType, hHandle, 1, SQL_DIAG_MESSAGE_TEXT, buffer, DBDRV_MAX_ERROR_TEXT, &nChars);
78 #endif
79 if (nRet == SQL_SUCCESS)
80 {
81 #if UNICODE_UCS4
82 buffer[DBDRV_MAX_ERROR_TEXT - 1] = 0;
83 ucs2_to_ucs4(buffer, -1, errorText, DBDRV_MAX_ERROR_TEXT);
84 #endif
85 RemoveTrailingCRLFW(errorText);
86 }
87 else
88 {
89 wcscpy(errorText, L"Unable to obtain description for this error");
90 }
91 }
92
93 return dwError;
94 }
95
96 /**
97 * Clear any pending result sets on given statement
98 */
99 static void ClearPendingResults(SQLHSTMT statement)
100 {
101 while(1)
102 {
103 SQLRETURN rc = SQLMoreResults(statement);
104 if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO))
105 {
106 break;
107 }
108 }
109 }
110
111 /**
112 * Prepare string for using in SQL query - enclose in quotes and escape as needed
113 */
114 extern "C" WCHAR EXPORT *DrvPrepareStringW(const WCHAR *str)
115 {
116 int len = (int)wcslen(str) + 3; // + two quotes and \0 at the end
117 int bufferSize = len + 128;
118 WCHAR *out = (WCHAR *)malloc(bufferSize * sizeof(WCHAR));
119 out[0] = L'\'';
120
121 const WCHAR *src = str;
122 int outPos;
123 for(outPos = 1; *src != 0; src++)
124 {
125 if (*src == L'\'')
126 {
127 len++;
128 if (len >= bufferSize)
129 {
130 bufferSize += 128;
131 out = (WCHAR *)realloc(out, bufferSize * sizeof(WCHAR));
132 }
133 out[outPos++] = L'\'';
134 out[outPos++] = L'\'';
135 }
136 else
137 {
138 out[outPos++] = *src;
139 }
140 }
141 out[outPos++] = L'\'';
142 out[outPos++] = 0;
143
144 return out;
145 }
146
147 extern "C" char EXPORT *DrvPrepareStringA(const char *str)
148 {
149 int len = (int)strlen(str) + 3; // + two quotes and \0 at the end
150 int bufferSize = len + 128;
151 char *out = (char *)malloc(bufferSize);
152 out[0] = '\'';
153
154 const char *src = str;
155 int outPos;
156 for(outPos = 1; *src != 0; src++)
157 {
158 if (*src == '\'')
159 {
160 len++;
161 if (len >= bufferSize)
162 {
163 bufferSize += 128;
164 out = (char *)realloc(out, bufferSize);
165 }
166 out[outPos++] = '\'';
167 out[outPos++] = '\'';
168 }
169 else
170 {
171 out[outPos++] = *src;
172 }
173 }
174 out[outPos++] = '\'';
175 out[outPos++] = 0;
176
177 return out;
178 }
179
180 /**
181 * Initialize driver
182 */
183 extern "C" bool EXPORT DrvInit(const char *cmdLine)
184 {
185 return true;
186 }
187
188 /**
189 * Unload handler
190 */
191 extern "C" void EXPORT DrvUnload()
192 {
193 }
194
195 /**
196 * Connect to database
197 * pszHost should be set to DB2 source name, and pszDatabase is ignored
198 */
199 extern "C" DBDRV_CONNECTION EXPORT DrvConnect(char *pszHost, char *pszLogin,
200 char *pszPassword, char *pszDatabase, const char *schema, NETXMS_WCHAR *errorText)
201 {
202 long iResult;
203 DB2DRV_CONN *pConn;
204 SQLHSTMT sqlStatement = NULL;
205
206 // Allocate our connection structure
207 pConn = (DB2DRV_CONN *)malloc(sizeof(DB2DRV_CONN));
208
209 // Allocate environment
210 iResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &pConn->sqlEnv);
211 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
212 {
213 wcscpy(errorText, L"Cannot allocate environment handle");
214 goto connect_failure_0;
215 }
216
217 // Set required DB2 version
218 iResult = SQLSetEnvAttr(pConn->sqlEnv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
219 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
220 {
221 wcscpy(errorText, L"Call to SQLSetEnvAttr failed");
222 goto connect_failure_1;
223 }
224
225 // Allocate connection handle, set timeout
226 iResult = SQLAllocHandle(SQL_HANDLE_DBC, pConn->sqlEnv, &pConn->sqlConn);
227 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
228 {
229 wcscpy(errorText, L"Cannot allocate connection handle");
230 goto connect_failure_1;
231 }
232 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_LOGIN_TIMEOUT, (SQLPOINTER *)15, 0);
233 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_CONNECTION_TIMEOUT, (SQLPOINTER *)30, 0);
234
235 // Connect to the datasource
236 iResult = SQLConnect(pConn->sqlConn, (SQLCHAR *)pszHost, SQL_NTS, (SQLCHAR *)pszLogin, SQL_NTS, (SQLCHAR *)pszPassword, SQL_NTS);
237 if ((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO))
238 {
239 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
240 goto connect_failure_2;
241 }
242
243 // Set current schema
244 if ((schema != NULL) && (schema[0] != 0))
245 {
246 char query[256];
247 snprintf(query, 256, "SET CURRENT SCHEMA = '%s'", schema);
248
249 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
250 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
251 {
252 // Execute statement
253 SQLWCHAR *temp = (SQLWCHAR *)UCS2StringFromMBString(query);
254 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
255 free(temp);
256 if ((iResult != SQL_SUCCESS) &&
257 (iResult != SQL_SUCCESS_WITH_INFO) &&
258 (iResult != SQL_NO_DATA))
259 {
260 GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
261 goto connect_failure_3;
262 }
263 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
264 }
265 else
266 {
267 GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
268 goto connect_failure_2;
269 }
270 }
271
272 // Create mutex
273 pConn->mutexQuery = MutexCreate();
274
275 // Success
276 return (DBDRV_CONNECTION)pConn;
277
278 // Handle failures
279 connect_failure_3:
280 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
281
282 connect_failure_2:
283 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
284
285 connect_failure_1:
286 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
287
288 connect_failure_0:
289 free(pConn);
290 return NULL;
291 }
292
293 /**
294 * Disconnect from database
295 */
296 extern "C" void EXPORT DrvDisconnect(DB2DRV_CONN *pConn)
297 {
298 MutexLock(pConn->mutexQuery);
299 MutexUnlock(pConn->mutexQuery);
300 SQLDisconnect(pConn->sqlConn);
301 SQLFreeHandle(SQL_HANDLE_DBC, pConn->sqlConn);
302 SQLFreeHandle(SQL_HANDLE_ENV, pConn->sqlEnv);
303 MutexDestroy(pConn->mutexQuery);
304 free(pConn);
305 }
306
307 /**
308 * Prepare statement
309 */
310 extern "C" DBDRV_STATEMENT EXPORT DrvPrepare(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
311 {
312 long iResult;
313 SQLHSTMT statement;
314 DB2DRV_STATEMENT *result;
315
316 MutexLock(pConn->mutexQuery);
317
318 // Allocate statement handle
319 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &statement);
320 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
321 {
322 // Prepare statement
323 #if defined(_WIN32) || defined(UNICODE_UCS2)
324 iResult = SQLPrepareW(statement, (SQLWCHAR *)pwszQuery, SQL_NTS);
325 #else
326 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
327 iResult = SQLPrepareW(statement, temp, SQL_NTS);
328 free(temp);
329 #endif
330 if ((iResult == SQL_SUCCESS) ||
331 (iResult == SQL_SUCCESS_WITH_INFO))
332 {
333 result = (DB2DRV_STATEMENT *)malloc(sizeof(DB2DRV_STATEMENT));
334 result->handle = statement;
335 result->buffers = new Array(0, 16, true);
336 result->connection = pConn;
337 *pdwError = DBERR_SUCCESS;
338 }
339 else
340 {
341 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement, errorText);
342 SQLFreeHandle(SQL_HANDLE_STMT, statement);
343 result = NULL;
344 }
345 }
346 else
347 {
348 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
349 result = NULL;
350 }
351
352 MutexUnlock(pConn->mutexQuery);
353 return result;
354 }
355
356
357 //
358 // Bind parameter to statement
359 //
360
361 extern "C" void EXPORT DrvBind(DB2DRV_STATEMENT *statement, int pos, int sqlType, int cType, void *buffer, int allocType)
362 {
363 static SQLSMALLINT odbcSqlType[] = { SQL_VARCHAR, SQL_INTEGER, SQL_BIGINT, SQL_DOUBLE, SQL_LONGVARCHAR };
364 static SQLSMALLINT odbcCType[] = { SQL_C_WCHAR, SQL_C_SLONG, SQL_C_ULONG, SQL_C_SBIGINT, SQL_C_UBIGINT, SQL_C_DOUBLE, SQL_C_WCHAR };
365 static DWORD bufferSize[] = { 0, sizeof(LONG), sizeof(DWORD), sizeof(INT64), sizeof(QWORD), sizeof(double), 0 };
366
367 int length = (cType == DB_CTYPE_STRING) ? (int)wcslen((WCHAR *)buffer) + 1 : 0;
368
369 SQLPOINTER sqlBuffer;
370 switch(allocType)
371 {
372 case DB_BIND_STATIC:
373 #if defined(_WIN32) || defined(UNICODE_UCS2)
374 if (cType == DB_CTYPE_UTF8_STRING)
375 {
376 sqlBuffer = WideStringFromUTF8String((char *)buffer);
377 statement->buffers->add(sqlBuffer);
378 length = (int)strlen((char *)sqlBuffer) + 1;
379 }
380 else
381 {
382 sqlBuffer = buffer;
383 }
384 #else
385 if (cType == DB_CTYPE_STRING)
386 {
387 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
388 statement->buffers->add(sqlBuffer);
389 }
390 else if (cType == DB_CTYPE_UTF8_STRING)
391 {
392 sqlBuffer = UCS2StringFromUTF8String((char *)buffer);
393 statement->buffers->add(sqlBuffer);
394 length = (int)strlen((char *)sqlBuffer) + 1;
395 }
396 else
397 {
398 sqlBuffer = buffer;
399 }
400 #endif
401 break;
402 case DB_BIND_DYNAMIC:
403 #if defined(_WIN32) || defined(UNICODE_UCS2)
404 if (cType == DB_CTYPE_UTF8_STRING)
405 {
406 sqlBuffer = WideStringFromUTF8String((char *)buffer);
407 free(buffer);
408 length = (int)strlen((char *)sqlBuffer) + 1;
409 }
410 else
411 {
412 sqlBuffer = buffer;
413 }
414 #else
415 if (cType == DB_CTYPE_STRING)
416 {
417 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
418 free(buffer);
419 }
420 else if (cType == DB_CTYPE_UTF8_STRING)
421 {
422 sqlBuffer = UCS2StringFromUTF8String((char *)buffer);
423 free(buffer);
424 length = (int)strlen((char *)sqlBuffer) + 1;
425 }
426 else
427 {
428 sqlBuffer = buffer;
429 }
430 #endif
431 statement->buffers->add(sqlBuffer);
432 break;
433 case DB_BIND_TRANSIENT:
434 #if defined(_WIN32) || defined(UNICODE_UCS2)
435 if (cType == DB_CTYPE_UTF8_STRING)
436 {
437 sqlBuffer = WideStringFromUTF8String((char *)buffer);
438 length = (int)strlen((char *)sqlBuffer) + 1;
439 }
440 else
441 {
442 sqlBuffer = nx_memdup(buffer, (cType == DB_CTYPE_STRING) ? (DWORD)(length * sizeof(WCHAR)) : bufferSize[cType]);
443 }
444 #else
445 if (cType == DB_CTYPE_STRING)
446 {
447 sqlBuffer = UCS2StringFromUCS4String((WCHAR *)buffer);
448 }
449 else if (cType == DB_CTYPE_UTF8_STRING)
450 {
451 sqlBuffer = UCS2StringFromUTF8String((char *)buffer);
452 length = (int)strlen((char *)sqlBuffer) + 1;
453 }
454 else
455 {
456 sqlBuffer = nx_memdup(buffer, bufferSize[cType]);
457 }
458 #endif
459 statement->buffers->add(sqlBuffer);
460 break;
461 default:
462 return; // Invalid call
463 }
464 SQLBindParameter(statement->handle, pos, SQL_PARAM_INPUT, odbcCType[cType], odbcSqlType[sqlType],
465 ((cType == DB_CTYPE_STRING) || (cType == DB_CTYPE_UTF8_STRING)) ? length : 0, 0, sqlBuffer, 0, NULL);
466 }
467
468 /**
469 * Execute prepared statement
470 */
471 extern "C" DWORD EXPORT DrvExecute(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *statement, NETXMS_WCHAR *errorText)
472 {
473 DWORD dwResult;
474
475 MutexLock(pConn->mutexQuery);
476 long rc = SQLExecute(statement->handle);
477 if (
478 (rc == SQL_SUCCESS) ||
479 (rc == SQL_SUCCESS_WITH_INFO) ||
480 (rc == SQL_NO_DATA))
481 {
482 ClearPendingResults(statement->handle);
483 dwResult = DBERR_SUCCESS;
484 }
485 else
486 {
487 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
488 }
489 MutexUnlock(pConn->mutexQuery);
490 return dwResult;
491 }
492
493
494 //
495 // Destroy prepared statement
496 //
497
498 extern "C" void EXPORT DrvFreeStatement(DB2DRV_STATEMENT *statement)
499 {
500 if (statement == NULL)
501 {
502 return;
503 }
504
505 MutexLock(statement->connection->mutexQuery);
506 SQLFreeHandle(SQL_HANDLE_STMT, statement->handle);
507 MutexUnlock(statement->connection->mutexQuery);
508 delete statement->buffers;
509 free(statement);
510 }
511
512 /**
513 * Perform non-SELECT query
514 */
515 extern "C" DWORD EXPORT DrvQuery(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, NETXMS_WCHAR *errorText)
516 {
517 long iResult;
518 DWORD dwResult;
519
520 MutexLock(pConn->mutexQuery);
521
522 // Allocate statement handle
523 SQLHSTMT sqlStatement;
524 iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
525 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
526 {
527 // Execute statement
528 #if defined(_WIN32) || defined(UNICODE_UCS2)
529 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
530 #else
531 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
532 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
533 free(temp);
534 #endif
535 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO) || (iResult == SQL_NO_DATA))
536 {
537 dwResult = DBERR_SUCCESS;
538 }
539 else
540 {
541 dwResult = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
542 }
543 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
544 }
545 else
546 {
547 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
548 }
549
550 MutexUnlock(pConn->mutexQuery);
551 return dwResult;
552 }
553
554 /**
555 * Get complete field data
556 */
557 static NETXMS_WCHAR *GetFieldData(SQLHSTMT sqlStatement, short column)
558 {
559 NETXMS_WCHAR *result = NULL;
560 SQLINTEGER dataSize;
561 #if defined(_WIN32) || defined(UNICODE_UCS2)
562 WCHAR buffer[256];
563 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, buffer, sizeof(buffer), &dataSize);
564 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - sizeof(WCHAR))))) && (dataSize != SQL_NULL_DATA))
565 {
566 result = wcsdup(buffer);
567 }
568 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
569 {
570 if (dataSize > (SQLLEN)(sizeof(buffer) - sizeof(WCHAR)))
571 {
572 WCHAR *temp = (WCHAR *)malloc(dataSize + sizeof(WCHAR));
573 memcpy(temp, buffer, sizeof(buffer));
574 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, &temp[255], dataSize - 254 * sizeof(WCHAR), &dataSize);
575 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
576 {
577 result = temp;
578 }
579 else
580 {
581 free(temp);
582 }
583 }
584 else if (dataSize == SQL_NO_TOTAL)
585 {
586 size_t tempSize = sizeof(buffer) * 4; // temporary buffer size in bytes
587 WCHAR *temp = (WCHAR *)malloc(tempSize);
588 memcpy(temp, buffer, sizeof(buffer));
589 size_t offset = sizeof(buffer) - sizeof(WCHAR); // offset in buffer in bytes
590 while(true)
591 {
592 SQLINTEGER readSize = (SQLINTEGER)(tempSize - offset);
593 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, (char *)temp + offset, readSize, &dataSize);
594 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
595 break;
596 if (dataSize == SQL_NO_TOTAL)
597 {
598 tempSize += sizeof(buffer) * 4;
599 }
600 else
601 {
602 tempSize += dataSize - readSize;
603 }
604 temp = (WCHAR *)realloc(temp, tempSize);
605 offset += readSize - sizeof(WCHAR);
606 }
607 result = temp;
608 }
609 }
610 #else
611 UCS2CHAR buffer[256];
612 SQLRETURN rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, buffer, sizeof(buffer), &dataSize);
613 if (((rc == SQL_SUCCESS) || ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize >= 0) && (dataSize <= (SQLLEN)(sizeof(buffer) - sizeof(UCS2CHAR))))) && (dataSize != SQL_NULL_DATA))
614 {
615 int len = ucs2_strlen(buffer);
616 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
617 ucs2_to_ucs4(buffer, -1, pResult->values[i], len + 1);
618 }
619 else if ((rc == SQL_SUCCESS_WITH_INFO) && (dataSize != SQL_NULL_DATA))
620 {
621 if (dataSize > (SQLLEN)(sizeof(buffer) - sizeof(UCS2CHAR)))
622 {
623 UCS2CHAR *temp = (UCS2CHAR *)malloc(dataSize + sizeof(UCS2CHAR));
624 memcpy(temp, buffer, sizeof(buffer));
625 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, &temp[255], dataSize - 254 * sizeof(UCS2CHAR), &dataSize);
626 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
627 {
628 int len = ucs2_strlen(temp);
629 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
630 ucs2_to_ucs4(temp, -1, result, len + 1);
631 }
632 free(temp);
633 }
634 else if (dataSize == SQL_NO_TOTAL)
635 {
636 size_t tempSize = sizeof(buffer) * 4;
637 UCS2CHAR *temp = (UCS2CHAR *)malloc(tempSize);
638 memcpy(temp, buffer, sizeof(buffer));
639 size_t offset = sizeof(buffer) - sizeof(UCS2CHAR);
640 while(true)
641 {
642 SQLLEN readSize = tempSize - offset;
643 rc = SQLGetData(sqlStatement, column, SQL_C_WCHAR, (char *)temp + offset, readSize, &dataSize);
644 if ((rc == SQL_SUCCESS) || (rc == SQL_NO_DATA))
645 break;
646 if (dataSize == SQL_NO_TOTAL)
647 {
648 tempSize += sizeof(buffer) * 4;
649 }
650 else
651 {
652 tempSize += dataSize - readSize;
653 }
654 temp = (UCS2CHAR *)realloc(temp, tempSize);
655 offset += readSize - sizeof(UCS2CHAR);
656 }
657 int len = ucs2_strlen(temp);
658 result = (NETXMS_WCHAR *)malloc((len + 1) * sizeof(NETXMS_WCHAR));
659 ucs2_to_ucs4(temp, -1, result, len + 1);
660 free(temp);
661 }
662 }
663 #endif
664 return (result != NULL) ? result : wcsdup(L"");
665 }
666
667 /**
668 * Process results of SELECT query
669 */
670 static DB2DRV_QUERY_RESULT *ProcessSelectResults(SQLHSTMT statement)
671 {
672 // Allocate result buffer and determine column info
673 DB2DRV_QUERY_RESULT *pResult = (DB2DRV_QUERY_RESULT *)malloc(sizeof(DB2DRV_QUERY_RESULT));
674 short wNumCols;
675 SQLNumResultCols(statement, &wNumCols);
676 pResult->numColumns = wNumCols;
677 pResult->numRows = 0;
678 pResult->values = NULL;
679
680 // Get column names
681 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
682 for(int i = 0; i < (int)pResult->numColumns; i++)
683 {
684 UCS2CHAR name[256];
685 SQLSMALLINT len;
686
687 SQLRETURN iResult = SQLColAttributeW(statement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, (SQLPOINTER)name, 256, &len, NULL);
688 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
689 {
690 name[len] = 0;
691 pResult->columnNames[i] = MBStringFromUCS2String(name);
692 }
693 else
694 {
695 pResult->columnNames[i] = strdup("");
696 }
697 }
698
699 // Fetch all data
700 long iCurrValue = 0;
701 SQLRETURN iResult;
702 while(iResult = SQLFetch(statement), (iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
703 {
704 pResult->numRows++;
705 pResult->values = (NETXMS_WCHAR **)realloc(pResult->values, sizeof(NETXMS_WCHAR *) * (pResult->numRows * pResult->numColumns));
706 for(int i = 1; i <= (int)pResult->numColumns; i++)
707 {
708 pResult->values[iCurrValue++] = GetFieldData(statement, (short)i);
709 }
710 }
711
712 return pResult;
713 }
714
715 /**
716 * Perform SELECT query
717 */
718 extern "C" DBDRV_RESULT EXPORT DrvSelect(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
719 {
720 DB2DRV_QUERY_RESULT *pResult = NULL;
721
722 MutexLock(pConn->mutexQuery);
723
724 // Allocate statement handle
725 SQLHSTMT sqlStatement;
726 long iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
727 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
728 {
729 // Execute statement
730 #if defined(_WIN32) || defined(UNICODE_UCS2)
731 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
732 #else
733 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
734 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
735 free(temp);
736 #endif
737 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
738 {
739 pResult = ProcessSelectResults(sqlStatement);
740 *pdwError = DBERR_SUCCESS;
741 }
742 else
743 {
744 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
745 }
746 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
747 }
748 else
749 {
750 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
751 }
752
753 MutexUnlock(pConn->mutexQuery);
754 return pResult;
755 }
756
757 /**
758 * Perform SELECT query using prepared statement
759 */
760 extern "C" DBDRV_RESULT EXPORT DrvSelectPrepared(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *statement, DWORD *pdwError, NETXMS_WCHAR *errorText)
761 {
762 DB2DRV_QUERY_RESULT *pResult = NULL;
763
764 MutexLock(pConn->mutexQuery);
765 long rc = SQLExecute(statement->handle);
766 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
767 {
768 pResult = ProcessSelectResults(statement->handle);
769 *pdwError = DBERR_SUCCESS;
770 }
771 else
772 {
773 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, statement->handle, errorText);
774 }
775 MutexUnlock(pConn->mutexQuery);
776 return pResult;
777 }
778
779 /**
780 * Get field length from result
781 */
782 extern "C" LONG EXPORT DrvGetFieldLength(DB2DRV_QUERY_RESULT *pResult, int iRow, int iColumn)
783 {
784 LONG nLen = -1;
785
786 if (pResult != NULL)
787 {
788 if ((iRow < pResult->numRows) && (iRow >= 0) &&
789 (iColumn < pResult->numColumns) && (iColumn >= 0))
790 {
791 nLen = (LONG)wcslen(pResult->values[iRow * pResult->numColumns + iColumn]);
792 }
793 }
794 return nLen;
795 }
796
797 /**
798 * Get field value from result
799 */
800 extern "C" NETXMS_WCHAR EXPORT *DrvGetField(DB2DRV_QUERY_RESULT *pResult, int iRow, int iColumn, NETXMS_WCHAR *pBuffer, int nBufSize)
801 {
802 NETXMS_WCHAR *pValue = NULL;
803
804 if (pResult != NULL)
805 {
806 if ((iRow < pResult->numRows) && (iRow >= 0) &&
807 (iColumn < pResult->numColumns) && (iColumn >= 0))
808 {
809 #ifdef _WIN32
810 wcsncpy_s(pBuffer, nBufSize, pResult->values[iRow * pResult->numColumns + iColumn], _TRUNCATE);
811 #else
812 wcsncpy(pBuffer, pResult->values[iRow * pResult->numColumns + iColumn], nBufSize);
813 pBuffer[nBufSize - 1] = 0;
814 #endif
815 pValue = pBuffer;
816 }
817 }
818 return pValue;
819 }
820
821 /**
822 * Get number of rows in result
823 */
824 extern "C" int EXPORT DrvGetNumRows(DB2DRV_QUERY_RESULT *pResult)
825 {
826 return (pResult != NULL) ? pResult->numRows : 0;
827 }
828
829 /**
830 * Get column count in query result
831 */
832 extern "C" int EXPORT DrvGetColumnCount(DB2DRV_QUERY_RESULT *pResult)
833 {
834 return (pResult != NULL) ? pResult->numColumns : 0;
835 }
836
837 /**
838 * Get column name in query result
839 */
840 extern "C" const char EXPORT *DrvGetColumnName(DB2DRV_QUERY_RESULT *pResult, int column)
841 {
842 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
843 }
844
845 /**
846 * Free SELECT results
847 */
848 extern "C" void EXPORT DrvFreeResult(DB2DRV_QUERY_RESULT *pResult)
849 {
850 if (pResult == NULL)
851 return;
852
853 int i, iNumValues;
854
855 iNumValues = pResult->numColumns * pResult->numRows;
856 for(i = 0; i < iNumValues; i++)
857 {
858 free(pResult->values[i]);
859 }
860 safe_free(pResult->values);
861
862 for(i = 0; i < pResult->numColumns; i++)
863 {
864 free(pResult->columnNames[i]);
865 }
866 free(pResult->columnNames);
867 free(pResult);
868 }
869
870 /**
871 * Perform unbuffered SELECT query
872 */
873 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectUnbuffered(DB2DRV_CONN *pConn, NETXMS_WCHAR *pwszQuery, DWORD *pdwError, NETXMS_WCHAR *errorText)
874 {
875 DB2DRV_UNBUFFERED_QUERY_RESULT *pResult = NULL;
876
877 MutexLock(pConn->mutexQuery);
878
879 // Allocate statement handle
880 SQLHSTMT sqlStatement;
881 SQLRETURN iResult = SQLAllocHandle(SQL_HANDLE_STMT, pConn->sqlConn, &sqlStatement);
882 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
883 {
884 // Execute statement
885 #if defined(_WIN32) || defined(UNICODE_UCS2)
886 iResult = SQLExecDirectW(sqlStatement, (SQLWCHAR *)pwszQuery, SQL_NTS);
887 #else
888 SQLWCHAR *temp = UCS2StringFromUCS4String(pwszQuery);
889 iResult = SQLExecDirectW(sqlStatement, temp, SQL_NTS);
890 free(temp);
891 #endif
892 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
893 {
894 // Allocate result buffer and determine column info
895 pResult = (DB2DRV_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(DB2DRV_UNBUFFERED_QUERY_RESULT));
896 pResult->sqlStatement = sqlStatement;
897 pResult->isPrepared = false;
898
899 short wNumCols;
900 SQLNumResultCols(sqlStatement, &wNumCols);
901 pResult->numColumns = wNumCols;
902 pResult->pConn = pConn;
903 pResult->noMoreRows = false;
904
905 // Get column names
906 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
907 for(int i = 0; i < pResult->numColumns; i++)
908 {
909 SQLWCHAR name[256];
910 SQLSMALLINT len;
911
912 iResult = SQLColAttributeW(sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
913 if ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO))
914 {
915 name[len] = 0;
916 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)name);
917 }
918 else
919 {
920 pResult->columnNames[i] = strdup("");
921 }
922 }
923
924 *pdwError = DBERR_SUCCESS;
925 }
926 else
927 {
928 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, sqlStatement, errorText);
929 // Free statement handle if query failed
930 SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement);
931 }
932 }
933 else
934 {
935 *pdwError = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, errorText);
936 }
937
938 if (pResult == NULL) // Unlock mutex if query has failed
939 {
940 MutexUnlock(pConn->mutexQuery);
941 }
942 return pResult;
943 }
944
945 /**
946 * Perform unbuffered SELECT query using prepared statement
947 */
948 extern "C" DBDRV_UNBUFFERED_RESULT EXPORT DrvSelectPreparedUnbuffered(DB2DRV_CONN *pConn, DB2DRV_STATEMENT *stmt, DWORD *pdwError, WCHAR *errorText)
949 {
950 DB2DRV_UNBUFFERED_QUERY_RESULT *pResult = NULL;
951
952 MutexLock(pConn->mutexQuery);
953 SQLRETURN rc = SQLExecute(stmt->handle);
954 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
955 {
956 // Allocate result buffer and determine column info
957 pResult = (DB2DRV_UNBUFFERED_QUERY_RESULT *)malloc(sizeof(DB2DRV_UNBUFFERED_QUERY_RESULT));
958 pResult->sqlStatement = stmt->handle;
959 pResult->isPrepared = true;
960
961 short wNumCols;
962 SQLNumResultCols(pResult->sqlStatement, &wNumCols);
963 pResult->numColumns = wNumCols;
964 pResult->pConn = pConn;
965 pResult->noMoreRows = false;
966
967 // Get column names
968 pResult->columnNames = (char **)malloc(sizeof(char *) * pResult->numColumns);
969 for(int i = 0; i < pResult->numColumns; i++)
970 {
971 SQLWCHAR name[256];
972 SQLSMALLINT len;
973
974 rc = SQLColAttributeW(pResult->sqlStatement, (SQLSMALLINT)(i + 1), SQL_DESC_NAME, name, 256, &len, NULL);
975 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
976 {
977 name[len] = 0;
978 pResult->columnNames[i] = MBStringFromUCS2String((UCS2CHAR *)name);
979 }
980 else
981 {
982 pResult->columnNames[i] = strdup("");
983 }
984 }
985
986 *pdwError = DBERR_SUCCESS;
987 }
988 else
989 {
990 *pdwError = GetSQLErrorInfo(SQL_HANDLE_STMT, stmt->handle, errorText);
991 }
992
993 if (pResult == NULL) // Unlock mutex if query has failed
994 MutexUnlock(pConn->mutexQuery);
995 return pResult;
996 }
997
998 /**
999 * Fetch next result line from asynchronous SELECT results
1000 */
1001 extern "C" bool EXPORT DrvFetch(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult)
1002 {
1003 bool bResult = false;
1004
1005 if (pResult != NULL)
1006 {
1007 long iResult;
1008
1009 iResult = SQLFetch(pResult->sqlStatement);
1010 bResult = ((iResult == SQL_SUCCESS) || (iResult == SQL_SUCCESS_WITH_INFO));
1011 if (!bResult)
1012 {
1013 pResult->noMoreRows = true;
1014 }
1015 }
1016 return bResult;
1017 }
1018
1019 /**
1020 * Get field length from unbuffered query result
1021 */
1022 extern "C" LONG EXPORT DrvGetFieldLengthUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult, int iColumn)
1023 {
1024 LONG nLen = -1;
1025
1026 if (pResult != NULL)
1027 {
1028 if ((iColumn < pResult->numColumns) && (iColumn >= 0))
1029 {
1030 SQLLEN dataSize;
1031 char temp[1];
1032 long rc = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_CHAR, temp, 0, &dataSize);
1033 if ((rc == SQL_SUCCESS) || (rc == SQL_SUCCESS_WITH_INFO))
1034 {
1035 nLen = (LONG)dataSize;
1036 }
1037 }
1038 }
1039 return nLen;
1040 }
1041
1042 /**
1043 * Get field from current row in unbuffered query result
1044 */
1045 extern "C" NETXMS_WCHAR EXPORT *DrvGetFieldUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult, int iColumn, NETXMS_WCHAR *pBuffer, int iBufSize)
1046 {
1047 SQLLEN iDataSize;
1048 long iResult;
1049
1050 // Check if we have valid result handle
1051 if (pResult == NULL)
1052 {
1053 return NULL;
1054 }
1055
1056 // Check if there are valid fetched row
1057 if (pResult->noMoreRows)
1058 {
1059 return NULL;
1060 }
1061
1062 if ((iColumn >= 0) && (iColumn < pResult->numColumns))
1063 {
1064 #if defined(_WIN32) || defined(UNICODE_UCS2)
1065 iResult = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR,
1066 pBuffer, iBufSize * sizeof(WCHAR), &iDataSize);
1067 #else
1068 SQLWCHAR *tempBuff = (SQLWCHAR *)malloc(iBufSize * sizeof(SQLWCHAR));
1069 iResult = SQLGetData(pResult->sqlStatement, (short)iColumn + 1, SQL_C_WCHAR,
1070 tempBuff, iBufSize * sizeof(SQLWCHAR), &iDataSize);
1071 ucs2_to_ucs4(tempBuff, -1, pBuffer, iBufSize);
1072 pBuffer[iBufSize - 1] = 0;
1073 free(tempBuff);
1074 #endif
1075 if (((iResult != SQL_SUCCESS) && (iResult != SQL_SUCCESS_WITH_INFO)) || (iDataSize == SQL_NULL_DATA))
1076 {
1077 pBuffer[0] = 0;
1078 }
1079 }
1080 else
1081 {
1082 pBuffer[0] = 0;
1083 }
1084 return pBuffer;
1085 }
1086
1087 /**
1088 * Get column count in async query result
1089 */
1090 extern "C" int EXPORT DrvGetColumnCountUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult)
1091 {
1092 return (pResult != NULL) ? pResult->numColumns : 0;
1093 }
1094
1095 /**
1096 * Get column name in async query result
1097 */
1098 extern "C" const char EXPORT *DrvGetColumnNameUnbuffered(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult, int column)
1099 {
1100 return ((pResult != NULL) && (column >= 0) && (column < pResult->numColumns)) ? pResult->columnNames[column] : NULL;
1101 }
1102
1103 /**
1104 * Destroy result of unbuffered query
1105 */
1106 extern "C" void EXPORT DrvFreeUnbufferedResult(DB2DRV_UNBUFFERED_QUERY_RESULT *pResult)
1107 {
1108 if (pResult == NULL)
1109 return;
1110
1111 if (pResult->isPrepared)
1112 SQLCloseCursor(pResult->sqlStatement);
1113 else
1114 SQLFreeHandle(SQL_HANDLE_STMT, pResult->sqlStatement);
1115 MutexUnlock(pResult->pConn->mutexQuery);
1116
1117 for(int i = 0; i < pResult->numColumns; i++)
1118 {
1119 free(pResult->columnNames[i]);
1120 }
1121 free(pResult->columnNames);
1122
1123 free(pResult);
1124 }
1125
1126 /**
1127 * Begin transaction
1128 */
1129 extern "C" DWORD EXPORT DrvBegin(DB2DRV_CONN *pConn)
1130 {
1131 SQLRETURN nRet;
1132 DWORD dwResult;
1133
1134 if (pConn == NULL)
1135 {
1136 return DBERR_INVALID_HANDLE;
1137 }
1138
1139 MutexLock(pConn->mutexQuery);
1140 nRet = SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0);
1141 if ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO))
1142 {
1143 dwResult = DBERR_SUCCESS;
1144 }
1145 else
1146 {
1147 dwResult = GetSQLErrorInfo(SQL_HANDLE_DBC, pConn->sqlConn, NULL);
1148 }
1149 MutexUnlock(pConn->mutexQuery);
1150 return dwResult;
1151 }
1152
1153 /**
1154 * Commit transaction
1155 */
1156 extern "C" DWORD EXPORT DrvCommit(DB2DRV_CONN *pConn)
1157 {
1158 SQLRETURN nRet;
1159
1160 if (pConn == NULL)
1161 {
1162 return DBERR_INVALID_HANDLE;
1163 }
1164
1165 MutexLock(pConn->mutexQuery);
1166 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_COMMIT);
1167 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1168 MutexUnlock(pConn->mutexQuery);
1169 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
1170 }
1171
1172 /**
1173 * Rollback transaction
1174 */
1175 extern "C" DWORD EXPORT DrvRollback(DB2DRV_CONN *pConn)
1176 {
1177 SQLRETURN nRet;
1178
1179 if (pConn == NULL)
1180 {
1181 return DBERR_INVALID_HANDLE;
1182 }
1183
1184 MutexLock(pConn->mutexQuery);
1185 nRet = SQLEndTran(SQL_HANDLE_DBC, pConn->sqlConn, SQL_ROLLBACK);
1186 SQLSetConnectAttr(pConn->sqlConn, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0);
1187 MutexUnlock(pConn->mutexQuery);
1188 return ((nRet == SQL_SUCCESS) || (nRet == SQL_SUCCESS_WITH_INFO)) ? DBERR_SUCCESS : DBERR_OTHER_ERROR;
1189 }
1190
1191
1192 /**
1193 * Check if table exist
1194 */
1195 extern "C" int EXPORT DrvIsTableExist(DB2DRV_CONN *pConn, const NETXMS_WCHAR *name)
1196 {
1197 WCHAR query[256];
1198 swprintf(query, 256, L"SELECT count(*) FROM sysibm.systables WHERE type='T' AND upper(name)=upper('%ls')", name);
1199 DWORD error;
1200 WCHAR errorText[DBDRV_MAX_ERROR_TEXT];
1201 int rc = DBIsTableExist_Failure;
1202 DB2DRV_QUERY_RESULT *hResult = (DB2DRV_QUERY_RESULT *)DrvSelect(pConn, query, &error, errorText);
1203 if (hResult != NULL)
1204 {
1205 WCHAR buffer[64] = L"";
1206 DrvGetField(hResult, 0, 0, buffer, 64);
1207 rc = (wcstol(buffer, NULL, 10) > 0) ? DBIsTableExist_Found : DBIsTableExist_NotFound;
1208 DrvFreeResult(hResult);
1209 }
1210 return rc;
1211 }
1212
1213 #ifdef _WIN32
1214
1215 /**
1216 * DLL Entry point
1217 */
1218 BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
1219 {
1220 if (dwReason == DLL_PROCESS_ATTACH)
1221 {
1222 DisableThreadLibraryCalls(hInstance);
1223 }
1224 return TRUE;
1225 }
1226
1227 #endif