home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Internet Business Development Kit / PRODUCT_CD.iso / sqlsvr / odbcsdk / samples / smpldrvr / setup.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-07  |  20.7 KB  |  647 lines

  1. /*
  2. ** SETUP.C - This is the ODBC sample driver code for
  3. ** setup.
  4. **
  5. **    This code is furnished on an as-is basis as part of the ODBC SDK and is
  6. **    intended for example purposes only.
  7. **
  8. */
  9. /*--------------------------------------------------------------------------
  10.   setup.c -- Sample ODBC setup
  11.  
  12.   This code demonstrates how to interact with the ODBC Installer.  These
  13.   functions may be part of your ODBC driver or in a separate DLL.
  14.  
  15.   The ODBC Installer allows a driver to control the management of
  16.   data sources by calling the ConfigDSN entry point in the appropriate
  17.   DLL.  When called, ConfigDSN receives four parameters:
  18.  
  19.     hwndParent ---- Handle of the parent window for any dialogs which
  20.                     may need to be created.  If this handle is NULL,
  21.                     then no dialogs should be displayed (that is, the
  22.                     request should be processed silently).
  23.  
  24.     fRequest ------ Flag indicating the type of request (add, configure
  25.                     (edit), or remove).
  26.  
  27.     lpszDriver ---- Far pointer to a null-terminated string containing
  28.                     the name of your driver.  This is the same string you
  29.                     supply in the ODBC.INF file as your section header
  30.                     and which ODBC Setup displays to the user in lieu
  31.                     of the actual driver filename.  This string needs to
  32.                     be passed back to the ODBC Installer when adding a
  33.                     new data source name.
  34.  
  35.     lpszAttributes- Far pointer to a list of null-terminated attribute
  36.                     keywords.  This list is similar to the list passed
  37.                     to SQLDriverConnect, except that each key-value
  38.                     pair is separated by a null-byte rather than a
  39.                     semicolon.  The entire list is then terminated with
  40.                     a null-byte (that is, two consecutive null-bytes
  41.                     mark the end of the list).  The keywords accepted
  42.                     should be those for SQLDriverConnect which are
  43.                     applicable, any new keywords you define for ODBC.INI,
  44.                     and any additional keywords you decide to document.
  45.  
  46.   ConfigDSN should return TRUE if the requested operation succeeds and
  47.   FALSE otherwise.  The complete prototype for ConfigDSN is:
  48.  
  49.   BOOL FAR PASCAL ConfigDSN(HWND    hwndParent,
  50.                             WORD    fRequest,
  51.                             LPSTR   lpszDriver,
  52.                             LPCSTR  lpszAttributes)
  53.  
  54.   Your setup code should not write to ODBC.INI directly to add or remove
  55.   data source names.  Instead, link with ODBCINST.LIB (the ODBC Installer
  56.   library) and call SQLWriteDSNToIni and SQLRemoveDSNFromIni.
  57.   Use SQLWriteDSNToIni to add data source names.  If the data source name
  58.   already exists, SQLWriteDSNToIni will delete it (removing all of its
  59.   associated keys) and rewrite it.  SQLRemoveDSNToIni removes a data
  60.   source name and all of its associated keys.
  61.  
  62.   For NT compatibility, the driver code should not use the
  63.   Get/WritePrivateProfileString windows functions for ODBC.INI, but instead,
  64.   use SQLGet/SQLWritePrivateProfileString functions that are macros (16 bit) or
  65.   calls to the odbcinst.dll (32 bit).
  66.  
  67. --------------------------------------------------------------------------*/
  68.  
  69.  
  70. // Includes ----------------------------------------------------------------
  71. #include  "sample.h"                    // Local include files
  72. #include  "ctl3d.h"
  73. #include  "odbcinst.h"                    // ODBC installer prototypes
  74. #include  <string.h>                    // C include files
  75. #include  <stdlib.h>
  76.  
  77.  
  78. // Constants ---------------------------------------------------------------
  79. #define MIN(x,y)      ((x) < (y) ? (x) : (y))
  80.  
  81. #define MAXPATHLEN      (255+1)           // Max path length
  82. #define MAXKEYLEN       (15+1)            // Max keyword length
  83. #define MAXDESC         (255+1)           // Max description length
  84. #define MAXDSNAME       (32+1)            // Max data source name length
  85.  
  86. const char EMPTYSTR  []= "";
  87. const char OPTIONON  []= "Yes";
  88. const char OPTIONOFF []= "No";
  89.  
  90. // ODBC.INI keywords
  91. const char ODBC_INI    []="ODBC.INI";     // ODBC initialization file
  92. const char INI_KDESC   []="Description";  // Data source description
  93. const char INI_KOPT1   []="Option1";      // First option
  94. const char INI_KOPT2   []="Option2";      // Second option
  95. const char INI_SDEFAULT[] = "Default";    // Default data source name
  96. const char szTranslateName[] = "TranslationName";
  97. const char szTranslateDLL[] = "TranslationDLL";
  98. const char szTranslateOption[] = "TranslationOption";
  99. const char szUnkTrans[] = "Unknown Translator";
  100.  
  101. // Attribute key indexes (into an array of Attr structs, see below)
  102. #define KEY_DSN         0
  103. #define KEY_DESC        1
  104. #define KEY_OPT1        2
  105. #define KEY_OPT2        3
  106. #define KEY_TRANSNAME    4
  107. #define KEY_TRANSOPTION 5
  108. #define KEY_TRANSDLL    6
  109. #define NUMOFKEYS        7                // Number of keys supported
  110.  
  111. // Attribute string look-up table (maps keys to associated indexes)
  112. static struct {
  113.   char  szKey[MAXKEYLEN];
  114.   int    iKey;
  115. } s_aLookup[] = { "DSN",            KEY_DSN,
  116.                   "DESC",            KEY_DESC,
  117.                   "Description",    KEY_DESC,
  118.                   "Option1",        KEY_OPT1,
  119.                   "Option2",        KEY_OPT2,
  120.                   "TranslateName",    KEY_TRANSNAME,
  121.                   "TranslateDLL",    KEY_TRANSDLL,
  122.                   "TranslateOption",KEY_TRANSOPTION,
  123.                   "",                0
  124.                 };
  125.  
  126.  
  127. // Types -------------------------------------------------------------------
  128. typedef struct tagAttr {
  129.     BOOL  fSupplied;
  130.     char  szAttr[MAXPATHLEN];
  131. } Attr, FAR * LPAttr;
  132.  
  133.  
  134. // Globals -----------------------------------------------------------------
  135. // NOTE:  All these are used by the dialog procedures
  136. typedef struct tagSETUPDLG {
  137.     HWND    hwndParent;                     // Parent window handle
  138.     LPCSTR    lpszDrvr;                        // Driver description
  139.     Attr    aAttr[NUMOFKEYS];                // Attribute array
  140.     char    szDSN[MAXDSNAME];                // Original data source name
  141.     BOOL    fNewDSN;                        // New data source flag
  142.     BOOL    fDefault;                        // Default data source flag
  143.  
  144. } SETUPDLG, FAR *LPSETUPDLG;
  145.  
  146.  
  147.  
  148. // Prototypes --------------------------------------------------------------
  149. void INTFUNC CenterDialog      (HWND    hdlg);
  150. int  CALLBACK ConfigDlgProc    (HWND    hdlg,
  151.                        WORD    wMsg,
  152.                        WPARAM  wParam,
  153.                        LPARAM  lParam);
  154. void INTFUNC ParseAttributes (LPCSTR    lpszAttributes, LPSETUPDLG lpsetupdlg);
  155. BOOL CALLBACK SetDSNAttributes(HWND    hwnd, LPSETUPDLG lpsetupdlg);
  156.  
  157. /* ConfigDSN ---------------------------------------------------------------
  158.   Description:  ODBC Setup entry point
  159.                 This entry point is called by the ODBC Installer
  160.                 (see file header for more details)
  161.   Input      :  hwnd ----------- Parent window handle
  162.                 fRequest ------- Request type (i.e., add, config, or remove)
  163.                 lpszDriver ----- Driver name
  164.                 lpszAttributes - data source attribute string
  165.   Output     :  TRUE success, FALSE otherwise
  166. --------------------------------------------------------------------------*/
  167. BOOL CALLBACK ConfigDSN
  168.                         (HWND     hwnd,
  169.                          WORD     fRequest,
  170.                          LPCSTR  lpszDriver,
  171.                          LPCSTR  lpszAttributes)
  172. {
  173.     BOOL  fSuccess;                           // Success/fail flag
  174.     GLOBALHANDLE hglbAttr;
  175.     LPSETUPDLG lpsetupdlg;
  176.  
  177.     // Allocate attribute array
  178.     hglbAttr = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(SETUPDLG));
  179.     if (!hglbAttr)
  180.         return FALSE;
  181.     lpsetupdlg = (LPSETUPDLG)GlobalLock(hglbAttr);
  182.  
  183.     // Parse attribute string
  184.     if (lpszAttributes)
  185.         ParseAttributes(lpszAttributes, lpsetupdlg);
  186.  
  187.     // Save original data source name
  188.     if (lpsetupdlg->aAttr[KEY_DSN].fSupplied)
  189.         lstrcpy(lpsetupdlg->szDSN, lpsetupdlg->aAttr[KEY_DSN].szAttr);
  190.     else
  191.         lpsetupdlg->szDSN[0] = '\0';
  192.  
  193.     // Remove data source
  194.     if (ODBC_REMOVE_DSN == fRequest) {
  195.         // Fail if no data source name was supplied
  196.         if (!lpsetupdlg->aAttr[KEY_DSN].fSupplied)
  197.             fSuccess = FALSE;
  198.  
  199.         // Otherwise remove data source from ODBC.INI
  200.         else
  201.             fSuccess = SQLRemoveDSNFromIni(lpsetupdlg->aAttr[KEY_DSN].szAttr);
  202.     }
  203.  
  204.     // Add or Configure data source
  205.     else {
  206.         // Save passed variables for global access (e.g., dialog access)
  207.         lpsetupdlg->hwndParent = hwnd;
  208.         lpsetupdlg->lpszDrvr     = lpszDriver;
  209.         lpsetupdlg->fNewDSN     = (ODBC_ADD_DSN == fRequest);
  210.         lpsetupdlg->fDefault     =
  211.             !lstrcmpi(lpsetupdlg->aAttr[KEY_DSN].szAttr, INI_SDEFAULT);
  212.  
  213.         // Display the appropriate dialog (if parent window handle supplied)
  214.         if (hwnd) {
  215.             // Display dialog(s)
  216.               fSuccess = (IDOK == DialogBoxParam(s_hModule,
  217.                                           MAKEINTRESOURCE(CONFIGDSN),
  218.                                           hwnd,
  219.                                           ConfigDlgProc,
  220.                                           (LONG)(LPSTR)lpsetupdlg));
  221.         }
  222.  
  223.         else if (lpsetupdlg->aAttr[KEY_DSN].fSupplied)
  224.             fSuccess = SetDSNAttributes(hwnd, lpsetupdlg);
  225.         else
  226.             fSuccess = FALSE;
  227.     }
  228.  
  229.     GlobalUnlock(hglbAttr);
  230.     GlobalFree(hglbAttr);
  231.     return fSuccess;
  232. }
  233.  
  234.  
  235. /* CenterDialog ------------------------------------------------------------
  236.     Description:  Center the dialog over the frame window
  237.     Input       :  hdlg -- Dialog window handle
  238.     Output       :  None
  239. --------------------------------------------------------------------------*/
  240. void INTFUNC CenterDialog(HWND hdlg)
  241. {
  242.     HWND    hwndFrame;
  243.     RECT    rcDlg, rcScr, rcFrame;
  244.     int        cx, cy;
  245.  
  246.     hwndFrame = GetParent(hdlg);
  247.  
  248.     GetWindowRect(hdlg, &rcDlg);
  249.     cx = rcDlg.right  - rcDlg.left;
  250.     cy = rcDlg.bottom - rcDlg.top;
  251.  
  252.     GetClientRect(hwndFrame, &rcFrame);
  253.     ClientToScreen(hwndFrame, (LPPOINT)(&rcFrame.left));
  254.     ClientToScreen(hwndFrame, (LPPOINT)(&rcFrame.right));
  255.     rcDlg.top    = rcFrame.top  + (((rcFrame.bottom - rcFrame.top) - cy) >> 1);
  256.     rcDlg.left   = rcFrame.left + (((rcFrame.right - rcFrame.left) - cx) >> 1);
  257.     rcDlg.bottom = rcDlg.top  + cy;
  258.     rcDlg.right  = rcDlg.left + cx;
  259.  
  260.     GetWindowRect(GetDesktopWindow(), &rcScr);
  261.     if (rcDlg.bottom > rcScr.bottom)
  262.     {
  263.         rcDlg.bottom = rcScr.bottom;
  264.         rcDlg.top    = rcDlg.bottom - cy;
  265.     }
  266.     if (rcDlg.right  > rcScr.right)
  267.     {
  268.         rcDlg.right = rcScr.right;
  269.         rcDlg.left  = rcDlg.right - cx;
  270.     }
  271.  
  272.     if (rcDlg.left < 0) rcDlg.left = 0;
  273.     if (rcDlg.top  < 0) rcDlg.top  = 0;
  274.  
  275.     MoveWindow(hdlg, rcDlg.left, rcDlg.top, cx, cy, TRUE);
  276.     return;
  277. }
  278.  
  279. /* ConfigDlgProc -----------------------------------------------------------
  280.   Description:  Manage add data source name dialog
  281.   Input      :  hdlg --- Dialog window handle
  282.                 wMsg --- Message
  283.                 wParam - Message parameter
  284.                 lParam - Message parameter
  285.   Output     :  TRUE if message processed, FALSE otherwise
  286. --------------------------------------------------------------------------*/
  287. int CALLBACK ConfigDlgProc
  288.                         (HWND    hdlg,
  289.                          WORD    wMsg,
  290.                          WPARAM wParam,
  291.                          LPARAM lParam)
  292. {
  293.     switch (wMsg) {
  294.     // Initialize the dialog
  295.     case WM_INITDIALOG:
  296.     {
  297.         LPSETUPDLG lpsetupdlg;
  298.         LPCSTR       lpszDSN;
  299.  
  300.         Ctl3dRegister (s_hModule);
  301.         SetWindowLong(hdlg, DWL_USER, lParam);
  302. #ifdef WIN32
  303.         Ctl3dSubclassDlg(hdlg, CTL3D_ALL);
  304. #else
  305.         Ctl3dSubclassDlgEx(hdlg, CTL3D_ALL);
  306. #endif
  307.         CenterDialog(hdlg);                 // Center dialog
  308.  
  309.         lpsetupdlg = (LPSETUPDLG) lParam;
  310.         lpszDSN    = lpsetupdlg->aAttr[KEY_DSN].szAttr;
  311.         // Initialize dialog fields
  312.         // NOTE: Values supplied in the attribute string will always
  313.         //         override settings in ODBC.INI
  314.         SetDlgItemText(hdlg, IDC_DSNAME, lpszDSN);
  315.  
  316.         if (!lpsetupdlg->aAttr[KEY_DESC].fSupplied)
  317.         SQLGetPrivateProfileString(lpszDSN, INI_KDESC,
  318.             EMPTYSTR,
  319.             lpsetupdlg->aAttr[KEY_DESC].szAttr,
  320.             sizeof(lpsetupdlg->aAttr[KEY_DESC].szAttr),
  321.             ODBC_INI);
  322.         SetDlgItemText(hdlg, IDC_DESC, lpsetupdlg->aAttr[KEY_DESC].szAttr);
  323.  
  324.         if (!lpsetupdlg->aAttr[KEY_OPT1].fSupplied)
  325.         SQLGetPrivateProfileString(lpszDSN, INI_KOPT1,
  326.             EMPTYSTR,
  327.             lpsetupdlg->aAttr[KEY_OPT1].szAttr,
  328.             sizeof(lpsetupdlg->aAttr[KEY_OPT1].szAttr),
  329.             ODBC_INI);
  330.         CheckDlgButton(hdlg, IDC_OPTION1,
  331.             !lstrcmpi(lpsetupdlg->aAttr[KEY_OPT1].szAttr, OPTIONON));
  332.  
  333.         if (!lpsetupdlg->aAttr[KEY_OPT2].fSupplied)
  334.         SQLGetPrivateProfileString(lpszDSN, INI_KOPT2,
  335.             EMPTYSTR,
  336.             lpsetupdlg->aAttr[KEY_OPT2].szAttr,
  337.             sizeof(lpsetupdlg->aAttr[KEY_OPT2].szAttr),
  338.             ODBC_INI);
  339.         CheckDlgButton(hdlg, IDC_OPTION2,
  340.             !lstrcmpi(lpsetupdlg->aAttr[KEY_OPT2].szAttr, OPTIONON));
  341.  
  342.         // Set Translation fields
  343.         if (!lpsetupdlg->aAttr[KEY_TRANSDLL].fSupplied)
  344.         {
  345.             SQLGetPrivateProfileString(lpsetupdlg->aAttr[KEY_DSN].szAttr,
  346.                 szTranslateDLL,
  347.                 EMPTYSTR,
  348.                 lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr,
  349.                 sizeof(lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr),
  350.                 ODBC_INI);
  351.         }
  352.         if (!lpsetupdlg->aAttr[KEY_TRANSOPTION].fSupplied)
  353.         {
  354.             SQLGetPrivateProfileString(lpsetupdlg->aAttr[KEY_DSN].szAttr,
  355.                 szTranslateOption,
  356.                 EMPTYSTR,
  357.                 lpsetupdlg->aAttr[KEY_TRANSOPTION].szAttr,
  358.                 sizeof(lpsetupdlg->aAttr[KEY_TRANSOPTION].szAttr),
  359.                 ODBC_INI);
  360.         }
  361.         if (!lpsetupdlg->aAttr[KEY_TRANSNAME].fSupplied)
  362.         {
  363.             if (!SQLGetPrivateProfileString(lpsetupdlg->aAttr[KEY_DSN].szAttr,
  364.                 szTranslateName,
  365.                 EMPTYSTR,
  366.                 lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr,
  367.                 sizeof(lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr),
  368.                 ODBC_INI))
  369.             {
  370.                 if (*lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr)
  371.                 {
  372.                     lstrcpy (lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr,
  373.                         szUnkTrans);
  374.                 }
  375.             }
  376.         }
  377.         SetDlgItemText(hdlg, IDC_TRANS_NAME,
  378.           lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr);
  379.  
  380.         if (lpsetupdlg->fDefault)
  381.         {
  382.             EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
  383.             EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
  384.         }
  385.         else
  386.             SendDlgItemMessage(hdlg, IDC_DSNAME,
  387.                  EM_LIMITTEXT, (WPARAM)(MAXDSNAME-1), 0L);
  388.         SendDlgItemMessage(hdlg, IDC_DESC,
  389.             EM_LIMITTEXT, (WPARAM)(MAXDESC-1), 0L);
  390.         return TRUE;                        // Focus was not set
  391.     }
  392.  
  393. #ifdef WIN32
  394.     case WM_CTLCOLORBTN:
  395.     case WM_CTLCOLORDLG:
  396.     case WM_CTLCOLOREDIT:
  397.     case WM_CTLCOLORLISTBOX:
  398.     case WM_CTLCOLORMSGBOX:
  399.     case WM_CTLCOLORSCROLLBAR:    
  400.     case WM_CTLCOLORSTATIC:
  401.         return (BOOL)Ctl3dCtlColorEx(wMsg, wParam, lParam);
  402.  
  403.     case WM_SETTEXT:
  404.     case WM_NCPAINT:
  405.     case WM_NCACTIVATE:
  406.         SetWindowLong(hdlg, DWL_MSGRESULT,
  407.             Ctl3dDlgFramePaint(hdlg, wMsg, wParam, lParam));
  408.         return TRUE;
  409. #endif
  410.  
  411.     case WM_SYSCOLORCHANGE:
  412.         return Ctl3dColorChange();
  413.  
  414.     // Process buttons
  415.     case WM_COMMAND:
  416.         switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  417.         // Ensure the OK button is enabled only when a data source name
  418.         // is entered
  419.         case IDC_DSNAME:
  420.             if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_CHANGE)
  421.             {
  422.                 char    szItem[MAXDSNAME];        // Edit control text
  423.  
  424.                 // Enable/disable the OK button
  425.                 EnableWindow(GetDlgItem(hdlg, IDOK),
  426.                     GetDlgItemText(hdlg, IDC_DSNAME,
  427.                         szItem, sizeof(szItem)));
  428.                 return TRUE;
  429.             }
  430.             break;
  431.  
  432.         //    Translator selection
  433.         case IDC_SELECT:
  434.         {
  435.             WORD cbNameOut, cbPathOut;
  436.             SDWORD vOption;
  437.             LPSETUPDLG lpsetupdlg;
  438.  
  439.             lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);
  440.             GetDlgItemText (hdlg, IDC_TRANS_NAME,
  441.                 lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr,
  442.                 sizeof (lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr));
  443.             vOption = atol (lpsetupdlg->aAttr[KEY_TRANSOPTION].szAttr);
  444.             if (SQLGetTranslator (hdlg,
  445.                 lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr,
  446.                 sizeof (lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr)-1,
  447.                 &cbNameOut,
  448.                 lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr,
  449.                 sizeof (lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr)-1,
  450.                 &cbPathOut, &vOption))
  451.             {
  452.                 if (cbNameOut == 0)
  453.                 {    //    No translator selected
  454.                     *lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr = 0;
  455.                     *lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr = 0;
  456.                     vOption = 0;
  457.                 }
  458.                 SetDlgItemText (hdlg, IDC_TRANS_NAME,
  459.                     lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr);
  460.                 _ltoa (vOption, lpsetupdlg->aAttr[KEY_TRANSOPTION].szAttr,
  461.                     10);
  462.             }
  463.             return TRUE;
  464.         }
  465.  
  466.         // Accept results
  467.         case IDOK:
  468.         {
  469.             LPSETUPDLG lpsetupdlg;
  470.  
  471.             lpsetupdlg = (LPSETUPDLG)GetWindowLong(hdlg, DWL_USER);
  472.             // Retrieve dialog values
  473.             if (!lpsetupdlg->fDefault)
  474.                 GetDlgItemText(hdlg, IDC_DSNAME,
  475.                     lpsetupdlg->aAttr[KEY_DSN].szAttr,
  476.                     sizeof(lpsetupdlg->aAttr[KEY_DSN].szAttr));
  477.             GetDlgItemText(hdlg, IDC_DESC,
  478.                 lpsetupdlg->aAttr[KEY_DESC].szAttr,
  479.                 sizeof(lpsetupdlg->aAttr[KEY_DESC].szAttr));
  480.             lstrcpy(lpsetupdlg->aAttr[KEY_OPT1].szAttr,
  481.                 (IsDlgButtonChecked(hdlg, IDC_OPTION1) ?
  482.                     OPTIONON : OPTIONOFF));
  483.             lstrcpy(lpsetupdlg->aAttr[KEY_OPT2].szAttr,
  484.                 (IsDlgButtonChecked(hdlg, IDC_OPTION2) ?
  485.                     OPTIONON : OPTIONOFF));
  486.  
  487.             // Update ODBC.INI
  488.             SetDSNAttributes(hdlg, lpsetupdlg);
  489.         }
  490.  
  491.         // Return to caller
  492.         case IDCANCEL:
  493.             Ctl3dUnregister (s_hModule);
  494.             EndDialog(hdlg, wParam);
  495.             return TRUE;
  496.         }
  497.         break;
  498.     }
  499.  
  500.     // Message not processed
  501.     return FALSE;
  502. }
  503.  
  504.  
  505. /* ParseAttributes ---------------------------------------------------------
  506.   Description:  Parse attribute string moving values into the aAttr array
  507.   Input      :  lpszAttributes - Pointer to attribute string
  508.   Output     :  None (global aAttr normally updated)
  509. --------------------------------------------------------------------------*/
  510. void INTFUNC ParseAttributes(LPCSTR lpszAttributes, LPSETUPDLG lpsetupdlg)
  511. {
  512.     LPCSTR    lpsz;
  513.     LPCSTR    lpszStart;
  514.     char    aszKey[MAXKEYLEN];
  515.     int        iElement;
  516.     int        cbKey;
  517.  
  518.     for (lpsz=lpszAttributes; *lpsz; lpsz++)
  519.     {  //  Extract key name (e.g., DSN), it must be terminated by an equals
  520.         lpszStart = lpsz;
  521.         for (;; lpsz++)
  522.         {
  523.             if (!*lpsz)
  524.                 return;        // No key was found
  525.             else if (*lpsz == '=')
  526.                 break;        // Valid key found
  527.         }
  528.         // Determine the key's index in the key table (-1 if not found)
  529.         iElement = -1;
  530.         cbKey     = lpsz - lpszStart;
  531.         if (cbKey < sizeof(aszKey))
  532.         {
  533.             register int j;
  534.  
  535.             _fmemcpy(aszKey, lpszStart, cbKey);
  536.             aszKey[cbKey] = '\0';
  537.             for (j = 0; *s_aLookup[j].szKey; j++)
  538.             {
  539.                 if (!lstrcmpi(s_aLookup[j].szKey, aszKey))
  540.                 {
  541.                     iElement = s_aLookup[j].iKey;
  542.                     break;
  543.                 }
  544.             }
  545.         }
  546.  
  547.         // Locate end of key value
  548.         lpszStart = ++lpsz;
  549.         for (; *lpsz; lpsz++);
  550.  
  551.         // Save value if key is known
  552.         // NOTE: This code assumes the szAttr buffers in aAttr have been
  553.         //       zero initialized
  554.         if (iElement >= 0)
  555.         {
  556.             lpsetupdlg->aAttr[iElement].fSupplied = TRUE;
  557.             _fmemcpy(lpsetupdlg->aAttr[iElement].szAttr,
  558.                 lpszStart,
  559.                 MIN(lpsz-lpszStart+1, sizeof(lpsetupdlg->aAttr[0].szAttr)-1));
  560.         }
  561.     }
  562.     return;
  563. }
  564.  
  565.  
  566. /* SetDSNAttributes --------------------------------------------------------
  567.   Description:  Write data source attributes to ODBC.INI
  568.   Input      :  hwnd - Parent window handle (plus globals)
  569.   Output     :  TRUE if successful, FALSE otherwise
  570. --------------------------------------------------------------------------*/
  571. BOOL INTFUNC SetDSNAttributes(HWND hwndParent, LPSETUPDLG lpsetupdlg)
  572. {
  573.     LPCSTR    lpszDSN;                        // Pointer to data source name
  574.  
  575.     lpszDSN = lpsetupdlg->aAttr[KEY_DSN].szAttr;
  576.  
  577.     // Validate arguments
  578.     if (lpsetupdlg->fNewDSN && !*lpsetupdlg->aAttr[KEY_DSN].szAttr)
  579.         return FALSE;
  580.  
  581.     // Write the data source name
  582.     if (!SQLWriteDSNToIni(lpszDSN, lpsetupdlg->lpszDrvr))
  583.     {
  584.         if (hwndParent)
  585.         {
  586.             char  szBuf[MAXPATHLEN];
  587.             char  szMsg[MAXPATHLEN];
  588.  
  589.             LoadString(s_hModule, IDS_BADDSN, szBuf, sizeof(szBuf));
  590.             wsprintf(szMsg, szBuf, lpszDSN);
  591.             LoadString(s_hModule, IDS_MSGTITLE, szBuf, sizeof(szBuf));
  592.             MessageBox(hwndParent, szMsg, szBuf, MB_ICONEXCLAMATION | MB_OK);
  593.         }
  594.         return FALSE;
  595.     }
  596.  
  597.     // Update ODBC.INI
  598.     // Save the value if the data source is new, if it was edited, or if
  599.     // it was explicitly supplied
  600.     if (hwndParent || lpsetupdlg->aAttr[KEY_DESC].fSupplied )
  601.         SQLWritePrivateProfileString(lpszDSN,
  602.             INI_KDESC,
  603.             lpsetupdlg->aAttr[KEY_DESC].szAttr,
  604.             ODBC_INI);
  605.  
  606.     if (hwndParent || lpsetupdlg->aAttr[KEY_OPT1].fSupplied )
  607.         SQLWritePrivateProfileString(lpszDSN,
  608.             INI_KOPT1,
  609.             (lstrcmpi(lpsetupdlg->aAttr[KEY_OPT1].szAttr, OPTIONON) ?
  610.             OPTIONOFF : OPTIONON),
  611.             ODBC_INI);
  612.  
  613.     if (hwndParent || lpsetupdlg->aAttr[KEY_OPT2].fSupplied )
  614.         SQLWritePrivateProfileString(lpszDSN,
  615.             INI_KOPT2,
  616.             (lstrcmpi(lpsetupdlg->aAttr[KEY_OPT2].szAttr, OPTIONON)    ?
  617.             OPTIONOFF : OPTIONON),
  618.             ODBC_INI);
  619.  
  620.     if (hwndParent || lpsetupdlg->aAttr[KEY_TRANSNAME].fSupplied )
  621.     {
  622.         SQLWritePrivateProfileString(lpszDSN,
  623.             szTranslateName,
  624.             *lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr ?
  625.             lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr: NULL,
  626.             ODBC_INI);
  627.         SQLWritePrivateProfileString(lpszDSN,
  628.             szTranslateDLL,
  629.             *lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr ?
  630.             lpsetupdlg->aAttr[KEY_TRANSDLL].szAttr: NULL,
  631.             ODBC_INI);
  632.         SQLWritePrivateProfileString(lpszDSN,
  633.             szTranslateOption,
  634.             *lpsetupdlg->aAttr[KEY_TRANSNAME].szAttr ?
  635.             lpsetupdlg->aAttr[KEY_TRANSOPTION].szAttr: NULL,
  636.             ODBC_INI);
  637.     }
  638.  
  639.     // If the data source name has changed, remove the old name
  640.     if (lpsetupdlg->aAttr[KEY_DSN].fSupplied &&
  641.         lstrcmpi(lpsetupdlg->szDSN, lpsetupdlg->aAttr[KEY_DSN].szAttr))
  642.     {
  643.         SQLRemoveDSNFromIni(lpsetupdlg->szDSN);
  644.     }
  645.     return TRUE;
  646. }
  647.