عنوان:

‫چطور میتونم در فایل odbcad32 یک اتصال جدید اضافه کنم؟


نویسنده: هادی مزارعی
تاریخ: ۱۴۰۵/۰۵/۱۶ ۱۷:۴۲
آدرس: www.dntips.ir
چطور میتونم با استفاده از C# در فایل odbcad32.exe که در مسیر C:\Windows\System32 قرار دارد یک اتصال اضافه کنم (برای SQL) و چنانچه اتصالی از قبل وجود داشت (برحسب نام تعریف شده) آن را اصلاح کنم. یک برنامه کوچک ساختم که وقتی کاربر آن را اجرا می‌کند، برنامه فایل MS-Access (در نقش Front-End) موجود در شبکه را (آخرین نسخه) را از شبکه دریافت و در کامپیوتر کاربر کپی می‌کند و سپس اجرا خواهد کرد. فایل MS-Access از طریق Connection تعریف شده در odbcad32.exe با بانک اطلاعاتی ارتباط برقرار خواهد کرد. در حال حاضر Connection بصورت دستی در سیستم کاربر تعریف می‌شود و سپس برنامه به کاربر تحویل می‌شود اما چالش آنجاست که رمز اتصال به SQL را هر چند وقت یکبار تغییر میدم و قصد ندارم مجددا دستی در تمام سیستم‌ها Connection را اصلاح کنم بلکه می‌خوام توسط همان برنامه‌ای که فایل MS-Access را اجرا می‌کند، ابتدا اتصال را ایجاد/اصلاح کنم و سپس فایل را به سیستم کاربر انتقال بدم.

نظرات

  • وحید نصیری در ۱۴۰۵/۰۵/۱۶ ۱۸:۳۷
    ویندوز، تابعی به نام SQLConfigDataSource در فایل ODBCCP32.dll دارد که دقیقاً همان کار odbcad32.exe را انجام می‌دهد. در #C به شکل زیر قابل استفاده‌است:
    internal static class OdbcInstaller
    {
        [DllImport("ODBCCP32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        private static extern bool SQLConfigDataSource(
            IntPtr hwndParent,
            int fRequest,
            string lpszDriver,
            string lpszAttributes);
    
        private const int ODBC_ADD_SYS_DSN = 4;
        private const int ODBC_CONFIG_SYS_DSN = 5;
    
        public static bool CreateOrUpdateSqlServerDsn(
            string dsn,
            string server,
            string database,
            string user,
            string password)
        {
            string attributes =
                $"DSN={dsn}\0" +
                $"SERVER={server}\0" +
                $"DATABASE={database}\0" +
                $"UID={user}\0" +
                $"PWD={password}\0" +
                "Trusted_Connection=No\0" +
                "\0";
    
            // ابتدا تلاش برای ویرایش
            if (SQLConfigDataSource(
                    IntPtr.Zero,
                    ODBC_CONFIG_SYS_DSN,
                    "ODBC Driver 18 for SQL Server",
                    attributes))
            {
                return true;
            }
    
            // اگر وجود نداشت ایجاد شود
            return SQLConfigDataSource(
                IntPtr.Zero,
                ODBC_ADD_SYS_DSN,
                "ODBC Driver 18 for SQL Server",
                attributes);
        }
    }
    روش استفاده:
    OdbcInstaller.CreateOrUpdateSqlServerDsn(
        "Accounting",
        "SQLSERVER01",
        "AccountingDB",
        "sa",
        "123456");

    البته نام Driver باید دقیقاً همان نامی باشد که روی سیستم نصب شده؛ مثلاً ممکن است یکی از این‌ها باشد:
    ODBC Driver 18 for SQL Server
    ODBC Driver 17 for SQL Server
    SQL Server
    SQL Server Native Client 11.0
    • هادی مزارعی در ۱۴۰۵/۰۵/۱۶ ۲۲:۳۷
      ضمن تشکر از توضیحات شما، با توجه به اینکه Connection ایجاد نشد از GPT کمک گرفتم و جهت دریافت خطای برنامه کد را بصورت زیر اصلاح کرد:
      using System;
      using System.Runtime.InteropServices;
      using System.Text;
      
      public static class OdbcInstaller
      {
          #region Win32 API
      
          [DllImport("ODBCCP32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
          private static extern bool SQLConfigDataSource(
              IntPtr hwndParent,
              int fRequest,
              string lpszDriver,
              string lpszAttributes);
      
          [DllImport("ODBCCP32.dll", CharSet = CharSet.Unicode)]
          private static extern uint SQLInstallerError(
              uint iError,
              out uint pfErrorCode,
              StringBuilder lpszErrorMsg,
              ushort cbErrorMsgMax,
              out ushort pcbErrorMsg);
      
          #endregion
      
          #region Constants
      
          private const int ODBC_ADD_DSN = 1;
          private const int ODBC_CONFIG_DSN = 2;
          private const int ODBC_REMOVE_DSN = 3;
      
          private const int ODBC_ADD_SYS_DSN = 4;
          private const int ODBC_CONFIG_SYS_DSN = 5;
          private const int ODBC_REMOVE_SYS_DSN = 6;
      
          #endregion
      
          /// <summary>
          /// Creates or updates a SQL Server DSN.
          /// </summary>
          /// <param name="dsn">DSN name.</param>
          /// <param name="server">SQL Server.</param>
          /// <param name="database">Database.</param>
          /// <param name="user">Login.</param>
          /// <param name="password">Password.</param>
          /// <param name="driver">
          /// Installed ODBC driver name.
          /// Example:
          /// "ODBC Driver 18 for SQL Server"
          /// "ODBC Driver 17 for SQL Server"
          /// "SQL Server"
          /// </param>
          /// <param name="systemDsn">True = System DSN.</param>
          public static void CreateOrUpdateSqlServerDsn(
              string dsn,
              string server,
              string database,
              string user,
              string password,
              string driver = "ODBC Driver 18 for SQL Server",
              bool systemDsn = false)
          {
              string attributes =
                  $"DSN={dsn}\0" +
                  $"SERVER={server}\0" +
                  $"DATABASE={database}\0" +
                  $"UID={user}\0" +
                  $"PWD={password}\0" +
                  $"Trusted_Connection=No\0" +
                  $"Encrypt=No\0" +
                  $"TrustServerCertificate=Yes\0\0";
      
              int config = systemDsn ? ODBC_CONFIG_SYS_DSN : ODBC_CONFIG_DSN;
              int add = systemDsn ? ODBC_ADD_SYS_DSN : ODBC_ADD_DSN;
      
              if (SQLConfigDataSource(IntPtr.Zero, config, driver, attributes))
                  return;
      
              if (SQLConfigDataSource(IntPtr.Zero, add, driver, attributes))
                  return;
      
              throw new Exception(GetInstallerErrors());
          }
      
          public static void RemoveDsn(
              string dsn,
              string driver = "ODBC Driver 18 for SQL Server",
              bool systemDsn = false)
          {
              int remove = systemDsn ? ODBC_REMOVE_SYS_DSN : ODBC_REMOVE_DSN;
      
              string attributes =
                  $"DSN={dsn}\0\0";
      
              if (!SQLConfigDataSource(IntPtr.Zero, remove, driver, attributes))
                  throw new Exception(GetInstallerErrors());
          }
      
          private static string GetInstallerErrors()
          {
              StringBuilder sb = new();
      
              uint i = 1;
      
              while (true)
              {
                  StringBuilder msg = new(1024);
      
                  uint errorCode;
                  ushort length;
      
                  uint result = SQLInstallerError(
                      i,
                      out errorCode,
                      msg,
                      (ushort)msg.Capacity,
                      out length);
      
                  if (result != 0)
                      break;
      
                  sb.AppendLine($"[{errorCode}] {msg}");
      
                  i++;
              }
      
              if (sb.Length == 0)
              {
                  int err = Marshal.GetLastWin32Error();
                  sb.AppendLine($"Win32 Error: {err}");
              }
      
              return sb.ToString();
          }
      }
      جهت استفاده:
      try
          {
              OdbcInstaller.CreateOrUpdateSqlServerDsn(
                  dsn: "Accounting",
                  server: @"DESKTOP-321584",
                  database: "MyDatabase",
                  user: "Newuser",
                  password: "123",
                  driver: "SQL Server", // Change if necessary
                  systemDsn: false);
      
              MessageBox.Show("DSN created successfully.");
          }
          catch (Exception ex)
          {
              MessageBox.Show(ex.Message);
          }
      در اجرای کد با خطای زیر مواجه شدم:

      [8] Invalid keyword-value pairs
      [11] Driver's ConfigDSN, ConfigDriver, or ConfigTranslator failed
      اشکال در دو پارامتر UID و PWD بود که بصورت زیر و جداگانه در رجیستری ثبت شدند:
      using Microsoft.Win32;
      
      using var key = Registry.CurrentUser.CreateSubKey(
          @"Software\ODBC\ODBC.INI\Accounting");
      
      key.SetValue("UID", "NewUser");
      key.SetValue("PWD", "123");

      اما مساله این هستش که وقتی در odbcca32.exe اتصال Accounting را باز میکنم، گزینه SQL Authentication غیرفعال و Windows Authentication فعال هستش و ورودی‌های username و password غیرفعال هستند. که ادامه بررسی بدین صورت شد:

      تنظیمات احراز هویت توسط خود Driver مدیریت میشه (msodbcsql17S.dll, msodbcsql18S.dll, etc.) و روشی برای انتخاب گزینه احراز هویت توسط SQL از طریق SQLConfigDataSource برای این Driver وجود ندارد.