home *** CD-ROM | disk | FTP | other *** search
/ CICA 1993 August / CICA.cdr / win_nt / odbc_sdk / odbc.exe / SAMPLESP.C / SAMPLESP.C
Encoding:
C/C++ Source or Header  |  1992-09-14  |  19.1 KB  |  513 lines

  1. /*--------------------------------------------------------------------------
  2.   Samplesp.c -- Sample ODBC setup
  3.  
  4.   This code demonstrates how to interact with the ODBC Installer.  These
  5.   functions may be part of your ODBC driver or in a separate DLL.
  6.  
  7.   The ODBC Installer allows a driver to control the management of
  8.   data sources by calling the ConfigDSN entry point in the appropriate
  9.   DLL.  When called, ConfigDSN receives four parameters:
  10.  
  11.     hwndParent ---- Handle of the parent window for any dialogs which
  12.                     may need to be created.  If this handle is NULL,
  13.                     then no dialogs should be displayed (that is, the
  14.                     request should be processed silently).
  15.  
  16.     fRequest ------ Flag indicating the type of request (add, configure
  17.                     (edit), or remove).
  18.  
  19.     lpszDriver ---- Far pointer to a null-terminated string containing
  20.                     the name of your driver.  This is the same string you
  21.                     supply in the ODBC.INF file as your section header
  22.                     and which ODBC Setup displays to the user in lieu
  23.                     of the actual driver filename.  This string needs to
  24.                     be passed back to the ODBC Installer when adding a
  25.                     new data source name.
  26.  
  27.     lpszAttributes- Far pointer to a list of null-terminated attribute
  28.                     keywords.  This list is similar to the list passed
  29.                     to SQLDriverConnect, except that each key-value
  30.                     pair is separated by a null-byte rather than a
  31.                     semicolon.  The entire list is then terminated with
  32.                     a null-byte (that is, two consecutive null-bytes
  33.                     mark the end of the list).  The keywords accepted
  34.                     should be those for SQLDriverConnect which are
  35.                     applicable, any new keywords you define for ODBC.INI,
  36.                     and any additional keywords you decide to document.
  37.  
  38.   ConfigDSN should return TRUE if the requested operation succeeds and
  39.   FALSE otherwise.  The complete prototype for ConfigDSN is:
  40.  
  41.   BOOL FAR PASCAL ConfigDSN(HWND    hwndParent,
  42.                             UINT    fRequest,
  43.                             LPSTR   lpszDriver,
  44.                             LPCSTR  lpszAttributes)
  45.  
  46.   Your setup code should not write to ODBC.INI directly to add or remove
  47.   data source names.  Instead, link with ODBCINST.LIB (the ODBC Installer
  48.   library) and call SQLWriteDSNToIni and SQLRemoveDSNFromIni.
  49.   Use SQLWriteDSNToIni to add data source names.  If the data source name
  50.   already exists, SQLWriteDSNToIni will delete it (removing all of its
  51.   associated keys) and rewrite it.  SQLRemoveDSNToIni removes a data
  52.   source name and all of its associated keys.
  53.  
  54.   (c) Microsoft Corp., 1990-1992
  55. --------------------------------------------------------------------------*/
  56.  
  57.  
  58. // Includes ----------------------------------------------------------------
  59. #include  <windows.h>                     // Windows include file
  60. #include  <odbcinst.h>                    // ODBC installer prototypes
  61. #include  <string.h>                      // C include files
  62. #include  "sampstp.h"                     // Local include files
  63.  
  64.  
  65. // Constants ---------------------------------------------------------------
  66. #define MIN(x,y)      ((x) < (y) ? (x) : (y))
  67.  
  68. #define MAXPATHLEN      (255+1)           // Max path length
  69. #define MAXKEYLEN       (15+1)            // Max keyword length
  70. #define MAXDESC         (255+1)           // Max description length
  71. #define MAXDSNAME       (32+1)            // Max data source name length
  72.  
  73. const char EMPTYSTR  []= "";
  74. const char OPTIONON  []= "Yes";
  75. const char OPTIONOFF []= "No";
  76.  
  77. // ODBC.INI keywords
  78. const char ODBC_INI    []="ODBC.INI";     // ODBC initialization file
  79. const char INI_KDESC   []="Description";  // Data source description
  80. const char INI_KOPT1   []="Option1";      // First option
  81. const char INI_KOPT2   []="Option2";      // Second option
  82. const char INI_SDEFAULT[] = "Default";    // Default data source name
  83.  
  84. // Attribute key indexes (into an array of Attr structs, see below)
  85. #define KEY_DSN       0
  86. #define KEY_DESC      1
  87. #define KEY_OPT1      2
  88. #define KEY_OPT2      3
  89. #define NUMOFKEYS     4                   // Number of keys supported
  90.  
  91. // Attribute string look-up table (maps keys to associated indexes)
  92. static struct {
  93.   char  szKey[MAXKEYLEN];
  94.   int    iKey;
  95. } s_aLookup[] = { "DSN",         KEY_DSN,
  96.                   "DESC",        KEY_DESC,
  97.                   "Description", KEY_DESC,
  98.                   "Option1",     KEY_OPT1,
  99.                   "Option2",     KEY_OPT2,
  100.                   "",            0
  101.                 };
  102.  
  103.  
  104. // Types -------------------------------------------------------------------
  105. typedef struct tagAttr {
  106.   BOOL  fSupplied;
  107.   char  szAttr[MAXPATHLEN];
  108. } Attr, FAR * LPAttr;
  109.  
  110.  
  111. // Globals -----------------------------------------------------------------
  112. // NOTE:  All these are used by the dialog procedures
  113. HGLOBAL hinst;                            // Dialog instance handle
  114. HWND    hwndParent;                       // Parent window handle
  115. LPCSTR  lpszDrvr;                         // Driver description
  116. HGLOBAL hglbAttr;                         // Attribute array handle
  117. LPAttr  aAttr;                            // Attribute array pointer
  118. BOOL    fNewDSN;                          // New data source flag
  119. BOOL    fDefault;                         // Default data source flag
  120. char    szDSN[MAXDSNAME];                 // Original data source name
  121.  
  122.  
  123. // Prototypes --------------------------------------------------------------
  124. void     PASCAL CenterDialog    (HWND    hdlg);
  125. int  FAR PASCAL ConfigDlgProc   (HWND    hdlg,
  126.                                  WORD    wMsg,
  127.                                  WORD    wParam,
  128.                                  DWORD   lParam);
  129. BOOL FAR PASCAL ConfigDSN       (HWND    hwnd,
  130.                                  UINT    fRequest,
  131.                                  LPCSTR  lpszRsv,
  132.                                  LPCSTR  lpszAttributes);
  133. void     PASCAL ParseAttributes (LPCSTR  lpszAttributes);
  134. BOOL     PASCAL SetDSNAttributes(HWND    hwnd);
  135.  
  136. int FAR PASCAL  LibMain         (HANDLE  hInstance,
  137.                                  WORD    wDSize,
  138.                                  WORD    wHSize,
  139.                                  LPSTR   lpsz);
  140. int FAR PASCAL  WEP             (int     nParam);
  141.  
  142.  
  143. /* LibMain -----------------------------------------------------------------
  144.   Description:  DLL init routine (called by LIBENTRY.ASM)
  145.   Input      :  hinst -- Instance handle
  146.                 wDSize - Size of data segment (in words)
  147.                 wHSize - Size of heap (in bytes)
  148.                 lpsz --- Pointer to associated application command line
  149.   Output     :  TRUE (always)
  150. --------------------------------------------------------------------------*/
  151. int FAR PASCAL LibMain(HANDLE hInstance,
  152.                        WORD   wDSize,
  153.                        WORD   wHSize,
  154.                        LPSTR  lpsz)
  155. {
  156.   if (wHSize > 0) 
  157.     UnlockData(0);
  158.  
  159.   hinst = hInstance;
  160.   return TRUE;
  161. }
  162.  
  163.  
  164. /* WEP ---------------------------------------------------------------------
  165.   Description:  DLL exit routine
  166.   Input      :  nParam -- System exit code
  167.   Output     :  TRUE (always)
  168. --------------------------------------------------------------------------*/
  169. int FAR PASCAL WEP (int nParam)
  170. {
  171.   return TRUE;
  172. }
  173.  
  174.  
  175. /* ConfigDSN ---------------------------------------------------------------
  176.   Description:  ODBC Setup entry point
  177.                 This entry point is called by the ODBC Installer
  178.                 (see file header for more details)
  179.   Input      :  hwnd ----------- Parent window handle
  180.                 fRequest ------- Request type (i.e., add, config, or remove)
  181.                 lpszDriver ----- Driver name
  182.                 lpszAttributes - data source attribute string
  183.   Output     :  TRUE success, FALSE otherwise
  184. --------------------------------------------------------------------------*/
  185. BOOL FAR PASCAL ConfigDSN(HWND    hwnd,
  186.                           UINT    fRequest,
  187.                           LPCSTR  lpszDriver,
  188.                           LPCSTR  lpszAttributes)
  189. {
  190.   BOOL  fSuccess;                         // Success/fail flag
  191.  
  192.   // Allocate attribute array
  193.   hglbAttr = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,
  194.                           sizeof(Attr) * NUMOFKEYS);
  195.   if (!hglbAttr)
  196.     return FALSE;
  197.   aAttr = (LPAttr)GlobalLock(hglbAttr);
  198.  
  199.   // Parse attribute string
  200.   if (lpszAttributes) ParseAttributes(lpszAttributes);
  201.  
  202.   // Save original data source name
  203.   if (aAttr[KEY_DSN].fSupplied)
  204.     lstrcpy(szDSN, aAttr[KEY_DSN].szAttr);
  205.   else
  206.     szDSN[0] = '\0';
  207.  
  208.   // Remove data source
  209.   if (ODBC_REMOVE_DSN == fRequest) {
  210.     // Fail if no data source name was supplied
  211.     if (!aAttr[KEY_DSN].fSupplied)
  212.       fSuccess = FALSE;
  213.  
  214.     // Otherwise remove data source from ODBC.INI
  215.     else 
  216.       fSuccess = SQLRemoveDSNFromIni(aAttr[KEY_DSN].szAttr);
  217.   }
  218.  
  219.   // Add or Configure data source
  220.   else {
  221.     // Save passed variables for global access (e.g., dialog access)
  222.     hwndParent = hwnd;
  223.     lpszDrvr   = lpszDriver;
  224.     fNewDSN    = (ODBC_ADD_DSN == fRequest);
  225.     fDefault   = !lstrcmpi(aAttr[KEY_DSN].szAttr, INI_SDEFAULT);
  226.  
  227.     // Display the appropriate dialog (if parent window handle supplied)
  228.     if (hwnd) {
  229.       // Display dialog(s)
  230.       fSuccess = (IDOK == DialogBox(hinst,
  231.                                     MAKEINTRESOURCE(CONFIGDSN),
  232.                                     hwnd,
  233.                                     ConfigDlgProc));
  234.     }
  235.     
  236.     else if (aAttr[KEY_DSN].fSupplied)
  237.       fSuccess = SetDSNAttributes(hwnd);
  238.     else
  239.       fSuccess = FALSE;
  240.   }
  241.  
  242.   GlobalUnlock(hglbAttr);
  243.   GlobalFree(hglbAttr);
  244.   return fSuccess;
  245. }
  246.  
  247.  
  248. /* CenterDialog ------------------------------------------------------------
  249.   Description:  Center the dialog over the parent window
  250.   Input      :  hdlg -- Dialog window handle
  251.   Output     :  None
  252. --------------------------------------------------------------------------*/
  253. void PASCAL CenterDialog(HWND hdlg)
  254. {
  255.   RECT  rcParent;                         // Parent window client rect
  256.   RECT  rcDlg;                            // Dialog window rect
  257.   int   nLeft, nTop;                      // Top-left coordinates
  258.   int   cWidth, cHeight;                  // Width and height
  259.  
  260.   // Get frame window client rect in screen coordinates
  261.   GetWindowRect(GetParent(hdlg), &rcParent);
  262.  
  263.   // Determine the top-left point for the dialog to be centered
  264.   GetWindowRect(hdlg, &rcDlg);
  265.   cWidth  = rcDlg.right  - rcDlg.left;
  266.   cHeight = rcDlg.bottom - rcDlg.top;
  267.   nLeft   = rcParent.left + 
  268.             (((rcParent.right  - rcParent.left) - cWidth ) / 2);
  269.   nTop    = rcParent.top  +
  270.             (((rcParent.bottom - rcParent.top ) - cHeight) / 2);
  271.   if (nLeft < 0) nLeft = 0;
  272.   if (nTop  < 0) nTop  = 0;
  273.  
  274.   // Place the dialog
  275.   MoveWindow(hdlg, nLeft, nTop, cWidth, cHeight, TRUE);
  276.   return;
  277. }
  278.  
  279.  
  280. /* ConfigDlgProc -----------------------------------------------------------
  281.   Description:  Manage add data source name dialog
  282.   Input      :  hdlg --- Dialog window handle
  283.                 wMsg --- Message
  284.                 wParam - Message parameter
  285.                 lParam - Message parameter
  286.   Output     :  TRUE if message processed, FALSE otherwise
  287. --------------------------------------------------------------------------*/
  288. int  FAR PASCAL ConfigDlgProc(HWND  hdlg,
  289.                               WORD  wMsg,
  290.                               WORD  wParam,
  291.                               DWORD lParam)
  292. {
  293.   switch (wMsg) {
  294.     // Initialize the dialog
  295.     case WM_INITDIALOG: {
  296.       LPCSTR  lpszDSN;
  297.  
  298.       CenterDialog(hdlg);                 // Center dialog
  299.       lpszDSN = aAttr[KEY_DSN].szAttr;
  300.  
  301.       // Initialize dialog fields
  302.       // NOTE: Values supplied in the attribute string will always
  303.       //       override settings in ODBC.INI
  304.       SetDlgItemText(hdlg, IDC_DSNAME, lpszDSN);
  305.  
  306.       if (!aAttr[KEY_DESC].fSupplied)
  307.         GetPrivateProfileString(lpszDSN, INI_KDESC,
  308.                                 EMPTYSTR,
  309.                                 aAttr[KEY_DESC].szAttr,
  310.                                 sizeof(aAttr[KEY_DESC].szAttr),
  311.                                 ODBC_INI);
  312.       SetDlgItemText(hdlg, IDC_DESC, aAttr[KEY_DESC].szAttr);
  313.  
  314.       if (!aAttr[KEY_OPT1].fSupplied)
  315.         GetPrivateProfileString(lpszDSN, INI_KOPT1,
  316.                                 EMPTYSTR,
  317.                                 aAttr[KEY_OPT1].szAttr,
  318.                                 sizeof(aAttr[KEY_OPT1].szAttr),
  319.                                 ODBC_INI);
  320.       CheckDlgButton(hdlg, IDC_OPTION1,
  321.                      !lstrcmpi(aAttr[KEY_OPT1].szAttr, OPTIONON));
  322.  
  323.       if (!aAttr[KEY_OPT2].fSupplied)
  324.         GetPrivateProfileString(lpszDSN, INI_KOPT2,
  325.                                 EMPTYSTR,
  326.                                 aAttr[KEY_OPT2].szAttr,
  327.                                 sizeof(aAttr[KEY_OPT2].szAttr),
  328.                                 ODBC_INI);
  329.       CheckDlgButton(hdlg, IDC_OPTION2,
  330.                      !lstrcmpi(aAttr[KEY_OPT2].szAttr, OPTIONON));
  331.  
  332.       if (fDefault) {
  333.         EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
  334.         EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
  335.       }
  336.       else
  337.         SendDlgItemMessage(hdlg, IDC_DSNAME,
  338.                            EM_LIMITTEXT, (WORD)(MAXDSNAME-1), 0L);
  339.       SendDlgItemMessage(hdlg, IDC_DESC,
  340.                          EM_LIMITTEXT, (WORD)(MAXDESC-1), 0L);
  341.       return TRUE;                        // Focus was not set
  342.     }
  343.  
  344.     // Process buttons
  345.     case WM_COMMAND:
  346.       switch (wParam) {
  347.         // Ensure the OK button is enabled only when a data source name
  348.         // is entered
  349.         case IDC_DSNAME:
  350.           if (HIWORD(lParam) == EN_CHANGE) {
  351.             char  szItem[MAXDSNAME];      // Edit control text
  352.  
  353.             // Enable/disable the OK button
  354.             EnableWindow(GetDlgItem(hdlg, IDOK),
  355.                          GetDlgItemText(hdlg, IDC_DSNAME,
  356.                                         szItem, sizeof(szItem)));
  357.             return TRUE;
  358.           }
  359.           break;
  360.  
  361.         // Accept results
  362.         case IDOK: {
  363.           // Retrieve dialog values
  364.           if (!fDefault)
  365.             GetDlgItemText(hdlg, IDC_DSNAME,
  366.                            aAttr[KEY_DSN].szAttr,
  367.                            sizeof(aAttr[KEY_DSN].szAttr));
  368.           GetDlgItemText(hdlg, IDC_DESC,
  369.                          aAttr[KEY_DESC].szAttr,
  370.                          sizeof(aAttr[KEY_DESC].szAttr));
  371.           lstrcpy(aAttr[KEY_OPT1].szAttr,
  372.                   (IsDlgButtonChecked(hdlg, IDC_OPTION1)
  373.                         ? OPTIONON
  374.                         : OPTIONOFF));
  375.           lstrcpy(aAttr[KEY_OPT2].szAttr,
  376.                   (IsDlgButtonChecked(hdlg, IDC_OPTION2)
  377.                         ? OPTIONON
  378.                         : OPTIONOFF));
  379.  
  380.           // Update ODBC.INI
  381.           SetDSNAttributes(hdlg);
  382.         }
  383.  
  384.         // Return to caller
  385.         case IDCANCEL:
  386.           EndDialog(hdlg, wParam);
  387.           return TRUE;
  388.       }
  389.       break;
  390.   }
  391.  
  392.   // Message not processed
  393.   return FALSE;
  394. }
  395.  
  396.  
  397. /* ParseAttributes ---------------------------------------------------------
  398.   Description:  Parse attribute string moving values into the aAttr array
  399.   Input      :  lpszAttributes - Pointer to attribute string
  400.   Output     :  None (global aAttr normally updated)
  401. --------------------------------------------------------------------------*/
  402. void PASCAL ParseAttributes(LPCSTR lpszAttributes)
  403. {
  404.   LPCSTR  lpsz;
  405.   LPCSTR  lpszStart;
  406.   char    aszKey[MAXKEYLEN];
  407.   short   iElement;
  408.   short   cbKey;
  409.  
  410.   for (lpsz=lpszAttributes; *lpsz; lpsz++) {
  411.     //  Extract key name (e.g., DSN), it must be terminated by an equals
  412.     lpszStart = lpsz;
  413.     for (;; lpsz++)
  414.       if (!*lpsz)            return;      // No key was found
  415.       else if (*lpsz == '=') break;       // Valid key found
  416.  
  417.     // Determine the key's index in the key table (-1 if not found)
  418.     iElement = -1;
  419.     cbKey    = lpsz - lpszStart;
  420.     if (cbKey < sizeof(aszKey)) {
  421.       register short  j;
  422.  
  423.       _fmemcpy(aszKey, lpszStart, cbKey);
  424.       aszKey[cbKey] = '\0';
  425.       for (j = 0; *s_aLookup[j].szKey; j++)
  426.         if (!lstrcmpi(s_aLookup[j].szKey, aszKey)) {
  427.           iElement = s_aLookup[j].iKey;
  428.           break;
  429.         }
  430.     }
  431.  
  432.     // Locate end of key value
  433.     lpszStart = ++lpsz;
  434.     for (; *lpsz; lpsz++);
  435.  
  436.     // Save value if key is known
  437.     // NOTE: This code assumes the szAttr buffers in aAttr have been
  438.     //       zero initialized
  439.     if (iElement >= 0) {
  440.       aAttr[iElement].fSupplied = TRUE;
  441.       _fmemcpy(aAttr[iElement].szAttr,
  442.                lpszStart,
  443.                MIN(lpsz-lpszStart+1, sizeof(aAttr[0].szAttr)-1));
  444.     }
  445.   }
  446.   return;
  447. }
  448.  
  449.  
  450. /* SetDSNAttributes --------------------------------------------------------
  451.   Description:  Write data source attributes to ODBC.INI
  452.   Input      :  hwnd - Parent window handle (plus globals)
  453.   Output     :  TRUE if successful, FALSE otherwise
  454. --------------------------------------------------------------------------*/
  455. BOOL PASCAL SetDSNAttributes(HWND hwnd)
  456. {
  457.   LPCSTR  lpszDSN;                        // Pointer to data source name
  458.  
  459.   lpszDSN = aAttr[KEY_DSN].szAttr;
  460.  
  461.   // Validate arguments
  462.   if (fNewDSN && !*aAttr[KEY_DSN].szAttr)
  463.     return FALSE;
  464.  
  465.   // Write the data source name
  466.   if (!SQLWriteDSNToIni(lpszDSN, lpszDrvr)) {
  467.     if (hwnd) {
  468.       char  szBuf[MAXPATHLEN];
  469.       char  szMsg[MAXPATHLEN];
  470.  
  471.       LoadString(hinst, IDS_BADDSN, szBuf, sizeof(szBuf));
  472.       wsprintf(szMsg, szBuf, lpszDSN);
  473.       LoadString(hinst, IDS_MSGTITLE, szBuf, sizeof(szBuf));
  474.       MessageBox(hwnd, szMsg, szBuf, MB_ICONEXCLAMATION | MB_OK);
  475.     }
  476.     return FALSE;
  477.   }
  478.  
  479.   // Update ODBC.INI
  480.   // Save the value if the data source is new, if it was edited, or if
  481.   // it was explicitly supplied
  482.   if (hwndParent               ||
  483.       aAttr[KEY_DESC].fSupplied )
  484.     WritePrivateProfileString(lpszDSN,
  485.                               INI_KDESC,
  486.                               aAttr[KEY_DESC].szAttr,
  487.                               ODBC_INI);
  488.  
  489.   if (hwndParent               ||
  490.       aAttr[KEY_OPT1].fSupplied )
  491.     WritePrivateProfileString(lpszDSN,
  492.                               INI_KOPT1,
  493.                               (lstrcmpi(aAttr[KEY_OPT1].szAttr, OPTIONON)
  494.                                 ? OPTIONOFF
  495.                                 : OPTIONON),
  496.                               ODBC_INI);
  497.  
  498.   if (hwndParent               ||
  499.       aAttr[KEY_OPT2].fSupplied )
  500.     WritePrivateProfileString(lpszDSN,
  501.                               INI_KOPT2,
  502.                               (lstrcmpi(aAttr[KEY_OPT2].szAttr, OPTIONON)
  503.                                 ? OPTIONOFF
  504.                                 : OPTIONON),
  505.                               ODBC_INI);
  506.  
  507.   // If the data source name has changed, remove the old name
  508.   if (aAttr[KEY_DSN].fSupplied && lstrcmpi(szDSN, aAttr[KEY_DSN].szAttr)) {
  509.     SQLRemoveDSNFromIni(szDSN);
  510.   }
  511.   return TRUE;
  512. }
  513.