ActiveX در برنامه های net.

سه شنبه 26 آبان 1394

تکنولوژی ActiveX بیشتر در شرکتهای 3rd برا ی توزیع API خود استفاده می شود. معمولا به صورت یک کنترل Windows form ارائه می شود. ویژوال استودیو ادغام چنین کنترلی را به برنامه های Windows form بسیار آسان کرده است.

ActiveX در برنامه های  net.

تکنولوژی ActiveX بیشتر در شرکتهای 3rd برا ی توزیع API خود استفاده می شود. معمولا به صورت یک کنترل Windows form ارائه می شود. ویژوال استودیو ادغام چنین کنترلی را به برنامه های Windows form   بسیار آسان کرده است. اما چطور می توان از آن در Console Application  یا Class Library  استفاده کرد؟

در این مقاله نحوه استفاده از ActiveX در این برنامه ها را نشان خواهیم داد. همچنین درباره بعضی مشکلاتی که ممکن است توسط ActiveX اتفاق بیافتد و راه حل آنها را توضیح خواهیم داد.

ثبت / حذف ActiveX

اول از همه ابزار ActiveX  باید روی کامپیوتر ثبت شود ، در غیر اینصورت با خطای زیر مواجه می شوید.

System.Runtime.InteropServices.COMException (0x80040154): Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))

معمولا ابزار  ActiveX به صورت یک فایل  ".ocx" و چندین فایل  ".dll" نشان داده می شود. برای نصب آن از کد زیر در CMD است استفاده میکنیم:

regsvr32 "path_to_activex\activex_name.ocx"

برای حذف آن از کد زیر استفاده میکنیم:

regsvr32 /u "path_to_activex\activex_name.ocx"

اگر Administrator نباشید، خطاهای زیر را مشاهده خواهید کرد:

The module "C:\[file being registered]" was loaded but the call to DllRegisterServer failed with error code 0x80040200.

The module "C:\[file being registered]" was loaded but the call to DllUnregisterServer failed with error code 0x80040200.?

این پیام زمان ثبت یا حذف یک فایل روی windows 7  هنگامی که کنترل حساب کاربری فعال باشد دیده می شود. کنترل حساب کاربری زمانی که یک کاربر مجوز نداشته باشد برای بعضی وظایف محدودیت قائل می شود یا از آنها جلوگیری میکند .

اگر تنظیمات امنیتی اجازه ثبت یا حذف یک فایل را از Windows run box ندهد لازم است تا ثبت و حذف آن از windows command prompt انجام شود. قدم های زیر را برای ثبت و حذف فایلها از Elevated Command Promp دنبال کنید.

    1. همه برنامه ها را ببندید.

    2. بر روی start کلیک کنید ، سپس  All program  و بعد Accessories 

    3. روی Command Prompt کلیک راست کرده و run as administrator را انتخاب کنید.

    4. فرمان های regsvr32  مورد نیاز را برای ثبت یا حذف DLL و OCX های متناظر اجرا کنید.

استفاده از ActiveX در کتابخانه

ActiveX برای استفاده در برنامه های GUI طراحی شد. به همین دلیل است که بیشتر آنها از رشته های STA استفاده میکنند و  خیلی چیزهای دیگر که در Windows form project وجود دارند اما در کتابخانه های کلاس غایب می باشند. در ادامه نشان خواهیم داد که چگونه می توان از این درد سرها با ایجاد یک پوشش حول شیء ActiveX اجتناب کرد. 

در یک پروژه ویندوز فرم اگر ActiveX  وارد شده 32 بیتی باشد، مطمئن شوید که پروژه شما با تنظیمات x86 گرد آوری می شود. قدم بعدی اضافه کردن Windows form با نام SdkWrapperForm  به پروژه است . هدف از ایجاد این فرم ، میزبانی کنترل ActivX  ما است . پس لازم است آن را به فرم اضافه کنید. از  Toolbox و  قسمت Choose Items گزینه COM Component  را انتخاب وسپس ActiveX را انتخاب کنید. کنترل جدید در Toolbox\General نمایش داده خواهد شد. با استفاده از Drag&Drop آن را در فرم قرار دهید . به صورت خودکار ویژوال استودیو دو فایل  dllبه پروزه شما اضافه میکند:  AxInterop.activex_name و Interop.activex_name   . این دو dll همراه با dll خروجی پروژه شما توزیع می شوند.

اکنون به SdkWrapperForm.cs رفته و کدهای زیر را وارد کنید.

public partial class SdkWrapperForm : Form
    {
        public SdkWrapperForm()
        {
            InitializeComponent();
        }

        public activex_type SDK
        {
            get
            {
                return activex_obj;
            }
        }
    }

این خاصیت به ما اجازه می دهد تا به شیء ActiveX  که در فرم قرار داده ایم از کلاسهای دیگر دسترسی داشته باشیم.

اکنون آماده نوشتن بخش مهم wrapper  هستید. کلاس جدیدی با نام SdkWrapper را به پروژه اضافه کرده و کدهای زیر را در آن قرار دهید.

public class SdkWrapper : IDisposable
    {
        #region Private members

        private Thread _uiThread;
        private SdkWrapperForm _form;

        #endregion

        #region Constructor

        public SdkWrapper()
        {
            ManualResetEvent initFinished = new ManualResetEvent(false);

            _uiThread = new Thread(() =>
            {
                _form = new SdkWrapperForm();
                //TODO: implement specific initialization here
                //example:
                //_form.SDK.some_method();
                //_form.SDK.some_event += SDK_some_event;

                initFinished.Set();

                while (true)
                {
                    try
                    {
                        Application.Run();
                    }
                    catch (ThreadAbortException)
                    {
                        //exit loop
                        return;
                    }
                    catch (Exception ex)
                    {
                        //log exception
                    }
                }
            }
            );
            _uiThread.SetApartmentState(ApartmentState.STA);
            _uiThread.IsBackground = true;
            _uiThread.Start();

            initFinished.WaitOne(1000);
        }

        public void Dispose()
        {
            _uiThread.Abort();
            _uiThread = null;
            Application.Exit();
        }

        #endregion
    }

این کد یک نمونه از SdkWrapperForm در thread   که با صفت STA مشخص شده است ایجاد می کند و حلقه پیام برنامه اجرا می شود. توجه داشته باشید که Dispose باید قبل از بسته شدن برنامه فراخوانی شود.

اکنون اگر بخواهیم از بعضی متدهای شیء ActiveX استفاده کنید ، لازم است Wrapper  برای آنها ایجاد شود.  در اینجا نمونه هایی را مشاهده میکنید.

public void AX_connect(string url, string username, string password)
        {
            if (_form.InvokeRequired)
            {
                _form.Invoke(new Action(() => { AX_connect(url, username, password); }));
            }
            else
            {
                _form.SDK.TS_connect(url, username, password);
            }
        }

و :

public string AX_get_version()
       {
           if (_form.InvokeRequired)
           {
               return (string)_form.Invoke(new Func<string>(() => { return AX_get_version(); }));
           }
           else
           {
               return _form.SDK.AX_get_version();
           }
       }

و دیگری :

public string AX_get_some_info(short id)

        {

            if (_form.InvokeRequired)

            {

                return (string)_form.Invoke(new Func<string>(() => { return AX_get_some_info(id); }));

            }

            else

            {

                return _form.SDK.AX_get_some_info(id);

            }

        }

همانطور که مشاهده میکنید ، متدها از شیء ActiveX باید از  همان thread که ActiveX در آن ایجاد شده بود فراخوانی شوند.  کلاس SdkWrapper در هر جایی از کد شما می تواند استفاده شود.

در هنگام یکپارچه سازی اجزاء ActiveX در برنامه با مشکلاتی مواجه خواهید شد.

اولی  مربوط به مرتب کردن نادرست است که خطای زیر را می دهد:

شیء COM   که از RCW جدا شده است نمی تواند استفاده شود.

at System.RuntimeType.InvokeDispMethod(String name, BindingFlags invokeAttr, Object target, Object[] args, Boolean[] byrefModifiers, Int32 culture, String[] namedParameters) at System.RuntimeType.InvokeMember(String name, BindingFlags bindingFlags, Binder binder, Object target, Object[] providedArgs, ParameterModifier[] modifiers, CultureInfo culture, String[] namedParams) at System.RuntimeType.ForwardCallToInvokeMember(String memberName, BindingFlags flags, Object target, Int32[] aWrapperTypes, MessageData& msgData)

مشکل دیگر مربوط به  تخصیص منابع مدیریت نشده است.

Application: my_app.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.AccessViolationException Stack: at System.Windows.Forms.UnsafeNativeMethods.PeekMessage(MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32, Int32) at System.Windows.Forms.Application+ ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.
FPushMessageLoop(IntPtr, Int32, Int32) at System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext) at System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext) at System.Windows.Forms.Application.Run() at my_app.SdkWrapper+<>c__DisplayClassf.<.ctor>b__6() at System.Threading.ThreadHelper.ThreadStart_Context(System.Object) at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Threading.ThreadHelper.ThreadStart()

یا شبیه به :

Application: my_app.exe Framework Version: v4.0.30319 Description: The process was terminated due to an unhandled exception. Exception Info: System.Reflection.TargetInvocationException Stack: at System.RuntimeMethodHandle.InvokeMethod(System.Object, System.Object[], System.Signature, Boolean) at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(System.Object, System.Object[], System.Object[]) at System.Reflection.RuntimeMethodInfo.UnsafeInvoke(System.Object, System.Reflection.BindingFlags, System.Reflection.Binder, System.Object[], System.Globalization.CultureInfo) at System.Delegate.DynamicInvokeImpl(System.Object[]) at System.Delegate.DynamicInvoke(System.Object[]) at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry) at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(System.Object) at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean) at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry) at System.Windows.Forms.Control.InvokeMarshaledCallbacks() at System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef) at System.Windows.Forms.ScrollableControl.WndProc(System.Windows.Forms.Message ByRef) at System.Windows.Forms.ContainerControl.WndProc(System.Windows.Forms.Message ByRef) at System.Windows.Forms.Form.WndProc(System.Windows.Forms.Message ByRef) at my_app.MainForm.WndProc(System.Windows.Forms.Message ByRef) at System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef) at System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef) at System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr) at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef) at System.Windows.Forms.Application+ ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.
FPushMessageLoop(IntPtr, Int32, Int32) at System.Windows.Forms.Application+ThreadContext.RunMessageLoopInner(Int32, System.Windows.Forms.ApplicationContext) at System.Windows.Forms.Application+ThreadContext.RunMessageLoop(Int32, System.Windows.Forms.ApplicationContext) at System.Windows.Forms.Application.Run(System.Windows.Forms.Form) at my_app.MainForm.Main(System.String[])

این خطاها به دلیل آنکه برنامه اشیاءActivX  را در حین کار ایجاد و حذف میکند رخ می دهد. به عنوان راه حل می توان ActiveX object pool ساخت . برنامه اول به اشیاء موجود در Pool نگاه میکند ، اگر خالی بود یک شیء جدید می سازد ، اگر خالی نبود از شیء موجود استفاده میکند .

آموزش سی شارپ

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 3k بازدید
  • 2 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید