原文《一個完善的 ODBC 數據庫程序》讀者建議:**程式應自動建立 DSN**,避免使用者手動設定。作者採納並實作於 `CMainFrame::OnCreate`。
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1;
// 1. 取得專案根目錄下的 student.mdb 路徑
CString sPath;
GetModuleFileName(NULL, sPath.GetBufferSetLength(MAX_PATH+1), MAX_PATH);
sPath.ReleaseBuffer();
int nPos = sPath.ReverseFind('\\'); sPath = sPath.Left(nPos);
CString lpszFile = sPath + _T("\\student.mdb");
// 2. 組裝 SQLConfigDataSource 參數字串
// 注意:參數以 '\0' 分隔,結尾雙 '\0\0'
char* szDesc = new char[256];
sprintf(szDesc, "DSN=%s? DESCRIPTION=TOC support source? DBQ=%s? FIL=MicrosoftAccess? DEFAULTDIR=%s??",
"not", lpszFile, sPath); // "not" 為 DSN 名稱
// 3. 將 '?' 替換為 '\0'
int mlen = strlen(szDesc);
for (int i=0; i<mlen; i++)
if (szDesc[i] == '?') szDesc[i] = '\0';
// 4. 呼叫 API 建立系統 DSN
#include <odbcinst.h> // 必須包含
if (FALSE == SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN,
"Microsoft Access Driver (*.mdb)\0", (LPCSTR)szDesc))
AfxMessageBox(_T("建立 DSN 失敗"));
delete[] szDesc;
return 0;
}
| 陷阱 | 說明 | 解法 |
|---|---|---|
| CString 內含 '\0' 截斷 | `CString` 以第一個 '\0' 為結尾,無法直接傳遞多段參數 | 使用 `char*` 緩衝區組裝,再手動將分隔符 '?' 替換為 '\0' |
| 遺漏 odbcinst.h | 未包含標頭導致 `SQLConfigDataSource` 未宣告 | `#include |
// 對話框輸入 DSN 名稱、瀏覽選擇 .mdb
CString dsnName, mdbPath;
// ... 取得使用者輸入 ...
char* szDesc = new char[512];
sprintf(szDesc, "DSN=%s? DESCRIPTION=Auto DSN? DBQ=%s? FIL=MicrosoftAccess? DEFAULTDIR=%s??",
dsnName, mdbPath, mdbPath.Left(mdbPath.ReverseFind('\\')));
int len = strlen(szDesc);
for (int i=0; i<len; i++) if (szDesc[i]=='?') szDesc[i]='\0';
SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN,
"Microsoft Access Driver (*.mdb)\0", (LPCSTR)szDesc);
delete[] szDesc;
同理可用 `ODBC_REMOVE_SYS_DSN` 移除、`ODBC_CONFIG_SYS_DSN` 修改。