کار با اسکنر در برنامه های تحت وب (قسمت دوم و آخر)
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: ده دقیقه

در قسمت قبل « کار با اسکنر در برنامه‌های تحت وب (قسمت اول) » دیدی از کاری که قرار است انجام دهیم، رسیدیم. حالا سراغ یک پروژه‌ی عملی و پیاده سازی مطالب مطرح شده می‌رویم.

ابتدا پروژه‌ی   WCF را شروع می‌کنیم. ویژوال استودیو را باز کرده و از قسمت New Project > Visual C# > WCF یک پروژه‌ی WCF Service Application جدید را مثلا با نام "WcfServiceScanner" ایجاد نمایید. پس از ایجاد، دو فایل IService1.cs و Service1.scv موجود را به IScannerService و ScannerService تغییر نام دهید. سپس ابتدا محتویات کلاس اینترفیس IScannerService را به صورت زیر تعریف نمایید :

    [ServiceContract]
    public interface IScannerService
    {
        [OperationContract]
        [WebInvoke(Method = "GET",
           BodyStyle = WebMessageBodyStyle.Wrapped,
           RequestFormat = WebMessageFormat.Json,
           ResponseFormat = WebMessageFormat.Json,
           UriTemplate = "GetScan")]
        string GetScan();
    }
در اینجا ما فقط اعلان متدهای مورد نیاز خود را ایجاد کرده‌ایم. علت استفاده از Attribute ایی با نام WebInvoke ، مشخص نمودن نوع خروجی به صورت Json است و همچنین عنوان آدرس مناسبی برای صدا زدن متد. پس از آن کلاس ScannerService را مطابق کدهای زیر تغییر دهید:
    public class ScannerService : IScannerService
    {
        public string GetScan()
        {
            // TODO Add code here
        }
    }
تا اینجا فقط یک WCF Service معمولی ساخته‌ایم .در ادامه به سراغ کلاس WIA برای ارتباط با اسکنر می‌رویم.
بر روی پروژه‌ی خود راست کلیک کرده و Add Reference را انتخاب نموده و سپس در قسمت COM، گزینه‌ی Microsoft Windows Image Acquisition Library v2.0 را به پروژه‌ی خود اضافه نمایید.
با اضافه شدن این ارجاع به پروژه، دسترسی به فضای نام WIA برای ما امکان پذیر می‌شود که ارجاعی از آن را در کلاس ScannerService قرار می‌دهیم.
using WIA;
اکنون متد GetScan را مطابق زیر اصلاح می‌نماییم:
        public string GetScan()
        {
            var imgResult = String.Empty;
            var dialog = new CommonDialogClass();
            try
            {
                // نمایش فرم پیشفرض اسکنر
                var image = dialog.ShowAcquireImage(WiaDeviceType.ScannerDeviceType);
                
                // ذخیره تصویر در یک فایل موقت
                var filename = Path.GetTempFileName();
                image.SaveFile(filename);
                var img = Image.FromFile(filename);

                // img جهت ارسال سمت کاربر و نمایش در تگ Base64 تبدیل تصویر به 
                imgResult = ImageHelper.ImageToBase64(img, ImageFormat.Jpeg);
            }
            catch
            {
                // از آنجاییه که امکان نمایش خطا وجود ندارد در صورت بروز خطا رشته خالی 
                // بازگردانده می‌شود که به معنای نبود تصویر می‌باشد
            }

            return imgResult;
        }
دقت داشته باشید که کدها را در زمان توسعه بین Try..Catch قرار ندهید چون ممکن‌است در این زمان به خطاهایی برخورد کنید که نیاز باشد در مرورگر آنها را دیده و رفع خطا نمایید.
CommonDialogClass  کلاس اصلی در اینجا جهت نمایش فرم کار با اسکنر می‌باشد و متد‌های مختلفی را جهت ارتباط با اسکنر در اختیار ما قرار می‌دهد که بسته به نیاز خود می‌توانید از آنها استفاده کنید. برای نمونه در مثال ما نیز متد اصلی که مورد استفاده قرار گرفته، ShowAcquireImage می‌باشد که این متد، فرم پیش فرض دریافت اسکنر را به کاربر نمایش می‌دهد و کاربر از طریق آن می‌تواند قبل از شروع اسکن، یکسری تنظیمات را انجام دهد.
این متد ابتدا به صورت خودکار فرم تعیین دستگاه اسکنر ورودی را نمایش داده :


و سپس فرم پیش فرض اسکنر‌های TWAIN را جهت تعیین تنظیمات اسکن نمایش می‌دهد که این امکان نیز در این فرم فراهم است تا دستگاه‌های Feeder یا Flated انتخاب گردند.

خروجی این متد همان عکس اسکن شده است که از نوع WIA.ImageFile می‌باشد و ما پس از دریافتش، ابتدا آن را در یک فایل موقت ذخیره نموده و سپس با استفاده از یک متد کمکی آن را به فرمت Base64 برای درخواست کننده اسکن ارسال می‌نماییم.

کدهای کلاس کمکی ImageHelper:

        public static string ImageToBase64(Image image, System.Drawing.Imaging.ImageFormat format)
        {
            if (image != null)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    // Convert Image to byte[]
                    image.Save(ms, format);
                    byte[] imageBytes = ms.ToArray();

                    // Convert byte[] to Base64 String
                    string base64String = Convert.ToBase64String(imageBytes);
                    return base64String;
                }
            }
            return String.Empty;
        }
توجه داشته باشید که خروجی این متد قرار است توسط callBack یک متد جاوا اسکریپتی مورد استفاده قرار گرفته و احیانا عکس مورد نظر در صفحه نمایش داده شود. پس بهتر است که از قالب تصویر به شکل Base64 استفاده گردد. ضمن اینکه پلاگین‌های Jquery مرتبط با ویرایش تصویر هم از این قالب پشتیبانی می‌کنند. (اینجا )

این مثال به ساده‌ترین شکل نوشته شد. کلاس دیگری هم در اینجا وجود دارد و در صورتیکه از اسکنر نوع Feeder استفاده می‌کنید، می‌توانید از کدهای آن استفاده کنید.

کار ما تا اینجا در پروژه‌ی WCF Service تقریبا تمام است. اگر پروژه را یکبار Build نمایید برای اولین بار احتمالا پیغام خطاهای زیر ظاهر خواهند شد:


جهت رفع این خطا، در قسمت Reference‌های پروژه خود، WIA را انتخاب نموده و از Properties‌های آن خصوصیت Embed Interop Types را به False تغییر دهید؛ مشکل حل می‌شود.

به سراغ پروژه‌ی ویندوز فرم جهت هاست کردن این WCF سرویس می‌رویم. می‌توانید این سرویس را بر روی یک Console App یا Windows Service هم هاست کنید که در اینجا برای سادگی مثال، از WinForm استفاده می‌کنیم.
یک پروژه‌ی WinForm جدید را ایجاد کنید و سپس از قسمت Add Reference > Solution به مسیر پروژه‌ی قبلی رفته و dll‌‌های آن را به پروژه خود اضافه نمایید.
Form1.cs  را باز کرده و ابتدا دو متغیر زیر را در آن به صورت عمومی تعریف نمایید:
        private readonly Uri _baseAddress = new Uri("http://localhost:6019");
        private ServiceHost _host;
برای استفاده از کلاس ServiceHost لازم است تا ارجاعی به فضای نام System.ServiceModel داده شود. متغیر baseAddress_ نگه دارنده‌ی آدرس ثابت سرویس اسکنر در سمت کلاینت می‌باشد و به این ترتیب ما دقیقا می‌دانیم باید سرویس را با کدام آدرس در کدهای جاوا اسکریپتی خود فراخوانی نماییم.
حال در رویداد Form_Load برنامه، کدهای زیر را جهت هاست کردن سرویس اضافه می‌نماییم:
        private void Form1_Load(object sender, EventArgs e)
        {
            _host = new ServiceHost(typeof(WcfServiceScanner.ScannerService), _baseAddress);
            _host.Open();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            _host.Close();
        }
همین چند خط برای هاست کردن سرویس روی آدرس localhost و پورت 8010 کامپیوتر کلاینت کافی است. اما یکسری تنظیمات مربوط به خود سرویس هم وجود دارد که باید در زمان پیاده سازی سرویس، در خود پروژه‌ی سرویس، ایجاد می‌گردید. اما از آنجا که ما قرار است سرویس را در یک پروژه‌ی دیگر هاست کنیم، بنابراین این تنظیمات را باید در همین پروژه‌ی WinForm قرار دهیم.
فایل App.Config پروژه‌ی WinForm را باز کرده و کدهای آنرا مطابق زیر تغییر دهید:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="BehaviourMetaData">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="WcfServiceScanner.ScannerService"
               behaviorConfiguration="BehaviourMetaData">
        <endpoint address=""
                  binding="basicHttpBinding"
                  contract="WcfServiceScanner.IScannerService" />
      </service>
    </services>
  </system.serviceModel>

</configuration>
اکنون پروژه‌ی هاست آماده اجرا می‌باشد. اگر آنرا اجرا کنید و در مرورگر خود آدرس ذکر شده را وارد کنید، صفحه‌ی زیر را مشاهده خواهد کرد که به معنی صحت اجرای سرویس اسکنر می‌باشد.

اگر موفق به اجرا نشدید و احیانا با خطای زیر مواجه شدید، اطمینان حاصل کنید که ویژوال استودیو Run as Administrator باشد. مشکل حل خواهد شد.


به سراغ پروژه‌ی بعدی، یعنی وب سایت خود می‌رویم. یک پروژه‌ی MVC جدید ایجاد نمایید و در View مورد نظر خود، کدهای زیر را جهت صدا زدن متد GetScan اضافه می‌کنیم.
( از آنجا که کدها به صورت جاوا اسکریپت می‌باشد، پس مهم نیست که حتما پروژه MVC باشد؛ یک صفحه‌ی HTML ساده هم کافی است).
<a href="#" id="get-scan">Get Scan</a>
<img src="" id="img-scanned" />
<script>
    $("#get-scan").click(function () {
        var url = 'http://localhost:6019/';
        $.get(url, function (data) {
            $("#img-scanned").attr("src","data:image/Jpeg;base64,  "+ data.GetScanResult);
        });
    });
</script>
دقت کنید در هنگام دریافت اطلاعات از سرویس، نتیجه به شکل GetScanResult خواهد بود. الان اگر پروژه را اجرا نمایید و روی لینک کلیک کنید، اسکنر شروع به دریافت اسکن خواهد کرد اما نتیجه‌ای بازگشت داده نخواهد شد و علت هم مشکل امنیتی CORS می‌باشد که به دلیل دریافت اطلاعات از یک دامین دیگر رخ می‌دهد و اگر با Firebug درخواست را بررسی کنید متوجه خطا به شکل زیر خواهید شد.


راه حل‌های زیادی برای این مشکل ارائه شده است، و متاسفانه بسیاری از آنها در شرایط پروژه‌ی ما جوابگو نمی‌باشد (به دلیل هاست روی یک پروژه ویندوزی). تنها راه حل مطمئن (تست شده) استفاده از یک کلاس سفارشی در پروژه‌ی WCF Service  می‌باشد که مثال آن در اینجا آورده شده است.
برای رفع مشکل به پروژه WcfServiceScanner بازگشته و کلاس جدیدی را به نام CORSSupport ایجاد کرده و کدهای زیر را به آن اضافه کنید:
    public class CORSSupport : IDispatchMessageInspector
    {
        Dictionary<string, string> requiredHeaders;
        public CORSSupport(Dictionary<string, string> headers)
        {
            requiredHeaders = headers ?? new Dictionary<string, string>();
        }

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            var httpRequest = request.Properties["httpRequest"] as HttpRequestMessageProperty;
            if (httpRequest.Method.ToLower() == "options")
                instanceContext.Abort();
            return httpRequest;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            var httpResponse = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
            var httpRequest = correlationState as HttpRequestMessageProperty;

            foreach (var item in requiredHeaders)
            {
                httpResponse.Headers.Add(item.Key, item.Value);
            }
            var origin = httpRequest.Headers["origin"];
            if (origin != null)
                httpResponse.Headers.Add("Access-Control-Allow-Origin", origin);

            var method = httpRequest.Method;
            if (method.ToLower() == "options")
                httpResponse.StatusCode = System.Net.HttpStatusCode.NoContent;

        }
    }

    // Simply apply this attribute to a DataService-derived class to get
    // CORS support in that service
    [AttributeUsage(AttributeTargets.Class)]
    public class CORSSupportBehaviorAttribute : Attribute, IServiceBehavior
    {
        #region IServiceBehavior Members

        void IServiceBehavior.AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var requiredHeaders = new Dictionary<string, string>();

            //Chrome doesn't accept wildcards when authorization flag is true
            //requiredHeaders.Add("Access-Control-Allow-Origin", "*");
            requiredHeaders.Add("Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS");
            requiredHeaders.Add("Access-Control-Allow-Headers", "Accept, Origin, Authorization, X-Requested-With,Content-Type");
            requiredHeaders.Add("Access-Control-Allow-Credentials", "true");
            foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher ed in cd.Endpoints)
                {
                    ed.DispatchRuntime.MessageInspectors.Add(new CORSSupport(requiredHeaders));
                }
            }
        }

        void IServiceBehavior.Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }
فضاهای نام لازم برای این کلاس به شرح زیر می‌باشد:
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
کلاس ScannerService را باز کرده و آنرا به ویژگی
    [CORSSupportBehavior]
    public class ScannerService : IScannerService
    {
مزین نمایید.

کار تمام است، یکبار دیگر ابتدا پروژه‌ی WcfServiecScanner و سپس پروژه هاست را Build کرده و برنامه‌ی هاست را اجرا کنید. اکنون مشاهده می‌کنید که با زدن دکمه‌ی اسکن، اسکنر فرم تنظیمات اسکن را نمایش می‌دهد که پس از زدن دکمه‌ی Scan، پروسه آغاز شده و پس از اتمام، تصویر اسکن شده در صفحه‌ی وب سایت نمایش داده می‌شود.
  • #
    ‫۹ سال و ۷ ماه قبل، سه‌شنبه ۲۱ بهمن ۱۳۹۳، ساعت ۰۵:۱۸
    با سلام و تشکر خدمت شما دوست گرامی
    من تمام مراحل را به دقت ایجاد کردم ولی بازم همون خطایی که خودتون هم عنوان کردید را میده
    Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:13748/. This can be fixed by moving the resource to the same domain or enabling CORS.
    فایل پروژه را به طور کامل میزارم ببینید علت چیه
    با تشکر مجدد
    • #
      ‫۹ سال و ۷ ماه قبل، چهارشنبه ۲۲ بهمن ۱۳۹۳، ساعت ۱۹:۴۵
      این مثال را بررسی کنید: SelfHostWcfScannerService.zip
      - ابتدا سرویس self host آن‌را با دسترسی ادمین اجرا کنید.
      - بعد برنامه‌ی وب را اجرا کنید.
      • #
        ‫۹ سال و ۷ ماه قبل، چهارشنبه ۲۲ بهمن ۱۳۹۳، ساعت ۲۱:۰۰
        با تشکر 
        این مثال به قسمت Catch رفته و عکس پیشفرض رو نشان می‌دهد
        درحالی که بر روی کامپیوتر من اسکنر نصب است و با دموی DynamSoft هم به خوبی جواب گرفتم و مشکلی نداشتم
        به نظرتون چرا پنجره اسکنر رو باز نمیکنه. اسکنر مجازی میشه نصب کرد و تست گرفت 
        • #
          ‫۹ سال و ۷ ماه قبل، چهارشنبه ۲۲ بهمن ۱۳۹۳، ساعت ۲۱:۲۲
          اینجا رو بررسی کنید و ببینید خطایی که دریافت می‌کنید توضیحش چیست. موردی که خودم تست کردم این بود که وقتی روی سیستمی که اسکنر متصل نبود امتحان می‌کردم، به خطا می‌خوردم و به محض اتصال اسکنر خطا برطرف می‌شد.
        • #
          ‫۹ سال و ۷ ماه قبل، چهارشنبه ۲۲ بهمن ۱۳۹۳، ساعت ۲۱:۴۷
          در همان فایل ScannerService اگر catch را به این صورت تغییر دهید، استثناءها در کنسول سرویس نمایش داده می‌شوند.
          catch(Exception ex)
          {
            Console.WriteLine(ex);
          برای مثال این مورد را من دریافت می‌کنم:
          System.Runtime.InteropServices.COMException (0x80210015): 
          Exception from HRESULT: 0x80210015 at WIA.CommonDialogClass.ShowAcquireImage
          کد 0x80210015 به معنای عدم یافت شدن اسکنر است (که صحیح است).
      • #
        ‫۹ سال و ۷ ماه قبل، سه‌شنبه ۲۸ بهمن ۱۳۹۳، ساعت ۱۳:۳۹
        با سلام؛ من یه عکسی تهیه کردم از مراحل اجرای برنامه. ابتدا پروژه SelfHostService را Build کردم و سپس فایل exe را Run Administrator کردم و کنسول باز شد.سپس پروژه WebApplicationTest  را بوسیله مرورگر فایرفاکس اجرا کردم. ولی تصویر پیش فرض را لود کرد. ضمنا در FireBug هم خطایی نداد. مشکل  کجاست. اسکنر هم نصب هست. مرسی.

          • #
            ‫۹ سال و ۷ ماه قبل، سه‌شنبه ۲۸ بهمن ۱۳۹۳، ساعت ۱۳:۵۴
            بله انجام شد. خطای 0x80210015  میده. ولی اسکنر کاملا نصب و کار میکنه در محیط‌های دیگه.

            • #
              ‫۹ سال و ۷ ماه قبل، سه‌شنبه ۲۸ بهمن ۱۳۹۳، ساعت ۱۴:۳۸
              - معنای دوم خطای 0x80210015 عدم سازگاری درایورهای اسکنر شما با WIA است (Windows Image Acquisition). اسکنر شما احتمالا فقط TWAIN compatible است و WIA or TWAIN compatible نیست.
              - لیستی از این نوع اسکنرها
  • #
    ‫۹ سال و ۷ ماه قبل، سه‌شنبه ۲۱ بهمن ۱۳۹۳، ساعت ۱۲:۳۵
    با سلام؛ آیا امکانش هست که سورس پروژه‌ی مثال رو هم قرار بدید؟ تشکر
    • #
      ‫۹ سال و ۷ ماه قبل، سه‌شنبه ۲۱ بهمن ۱۳۹۳، ساعت ۱۴:۰۱
      سورس پروژه را از نظر قبلی می‌تونید دریافت کنید
  • #
    ‫۹ سال و ۷ ماه قبل، چهارشنبه ۲۲ بهمن ۱۳۹۳، ساعت ۲۲:۲۱
    یک نکته‌ی تکمیلی
    در متد GetScan در پایان کار، نیاز است منابع COM مورد استفاده رها شوند تا نشتی حافظه رخ ندهد:
    finally
    {
      if(image!=null) Marshal.ReleaseComObject(image);
      if(dialog!=null) Marshal.ReleaseComObject(dialog);
    }
  • #
    ‫۹ سال و ۷ ماه قبل، پنجشنبه ۱۴ اسفند ۱۳۹۳، ساعت ۱۵:۴۱
    با سلام؛ من تمامی مراحل رو طی کردم اما با خطای زیر مواجه شدم :
     Host is running.
    System.Runtime.InteropServices.COMException (0x80070050): The file exists. (Exception from HRESULT: 0x80070050)
    at WIA.ImageFileClass.SaveFile(String Filename)
    at SelfHost.ScannerService.GetScan() in g:\project\asnad\scan\SelfHostWcfScannerService\SelfHostWcfService\ScannerService.cs:line 25
    • #
      ‫۹ سال و ۷ ماه قبل، پنجشنبه ۱۴ اسفند ۱۳۹۳، ساعت ۱۶:۰۴
      سطر Path.GetTempFileName را تبدیل کنید به مسیری که به آن دسترسی دارید. همچنین بررسی کنید نام این فایل منحصربفرد باشد.
      • #
        ‫۹ سال و ۷ ماه قبل، شنبه ۱۶ اسفند ۱۳۹۳، ساعت ۲۳:۱۱
        بسیار ممنون، مشکل حل شد. یه سوال دیگه : آیا در WIA امکان اسکن دو رو هم وجود داره؟ به چه شکلی
        • #
          ‫۹ سال و ۶ ماه قبل، جمعه ۱۴ فروردین ۱۳۹۴، ساعت ۲۱:۱۲
          پاسخ شما اسکن کردن در قالب تصاویر tiff هست، که می‌توانند چندین عکس در یک عکس باشند، مثل اسکن پشت و رو کارت ملی.
          فقط برای نمایش در مرورگر به مشکل برخورد می‌کنید که یک راه استفاده از افزونه AlternaTIFF می باشد.

      • #
        ‫۹ سال و ۶ ماه قبل، شنبه ۱۵ فروردین ۱۳۹۴، ساعت ۱۷:۴۲
        Path.GetTempFileName باعث خطای The file exists می‌شود. زمانیکه با آدرس قابل دسترس و نام ثابت برای فایل جایگزین میکنم برای بار دوم همین خطا را میدهد.
        • #
          ‫۹ سال و ۶ ماه قبل، دوشنبه ۱۷ فروردین ۱۳۹۴، ساعت ۰۴:۰۴
          به نظرم یکتا بودن آدرس فایل در سمت کلاینت ضروری نمی‌باشد، در اینصورت می‌توانید یک مسیر ثابت و مشخص رو در نظر بگیرید و قبل از ذخیره، وجود اون رو بررسی کنید و چنانچه از قبل وجود داشت ابتدا آن را پاک نموده سپس ذخیره کنید.
  • #
    ‫۹ سال و ۳ ماه قبل، یکشنبه ۲۴ خرداد ۱۳۹۴، ساعت ۰۱:۱۶
    با سلام و عرض خسته نباشید.
    خیلی ممنون بابت زحمتی که کشیدید و بسیار عالی است. من می‌خواستم که پروژه SelfHostService رو به صورت Windows Service استفاده کنم البته می‌دونم که برای اینکار نمی‌شه چون پنجره اسکنر رو نمیشه نشون بده ولی شما فرض کنید همون رشته که در صورت خطا برمیگردونه رو بخواهم برگردونم به صفحه. حالا دوباره به مشکل Cors برخورد کردم در حالت قبل شما خیلی خوب اون رو حل کردید ولی برای ویندوز سرویس هم میشه قرار بدهید. با تشکر این خیلی به دردم می‌خوره.
    • #
      ‫۹ سال و ۳ ماه قبل، یکشنبه ۲۴ خرداد ۱۳۹۴، ساعت ۰۴:۱۶
      پروژه دومین نظر این بحث از بالا، سرویس را درون یک برنامه‌ی ویندوزی اجرا کرده. 
    • #
      ‫۹ سال و ۳ ماه قبل، یکشنبه ۲۴ خرداد ۱۳۹۴، ساعت ۰۵:۳۲
      اتفاقا من این پروژه رو روی ویندوز سرویس هاست کرده بودم و مشکلی هم نداشت!
      در واقع این شما نیستید که بخواهید فرمی را نشان بدهید، این مورد بر عهده کتابخانه WIA می‌باشد.
      برای اون مشکل یعنی با کلاس CORSSupportBehavior هم حل نشده؟
      بحث‌های نصب یک سرویس که خارج از مطلب هست اما به سادگی می‌تونید پیدا کنید، پس از نصب سرویس شما در متد OnStart این کد‌ها را خواهید داشت
          _host = new ServiceHost(typeof(WcfServiceScanner.ScannerService), _baseAddress);
          _host.Open();
      و در متد OnStop هم
      _host.Close();
      بقیه موارد فرقی با هاست روی یک پروژه کنسول نمی‌کند.
  • #
    ‫۸ سال و ۱۰ ماه قبل، پنجشنبه ۵ آذر ۱۳۹۴، ساعت ۱۵:۳۰
    با سلام
    در صورتی که از وب سرویس asmx یا web API یا wcf service در پروژه وبی جهت انجام عملیات اسکن استفاده کنم با خطای زیر مواجه می‌شوم :

    Creating an instance of the COM component with CLSID {850D1D11-70F3-4BE5-9A11-77AA6B2BB201} from the IClassFactory failed due to the following error: 80070005 Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).

    سطح دسترسی application pool وب سایت را نیز تغییر داده ام, اما مشکل حل نشده است .
    ظاهرا اگر عملیات دسترسی به کتابخانه WIA از طریق وب سایت انجام شود این مشکل به وجود می‌آید .
    یعنی حتما باید این کار در یک پروژه مجزای WCF انجام شود و هاست شود و سپس در وب اپلیکیشن فراخوانی شود؟ 
  • #
    ‫۸ سال و ۱۰ ماه قبل، یکشنبه ۸ آذر ۱۳۹۴، ساعت ۱۱:۵۱
    در صورتی که WCF Service را در یک ویندوز سرویس هاست نماییم , با زدن دکمه اسکن , فرم اسکنر باز نمی‌شود و بعد از مدتی خطای time out داده می‌شود.ولی اگر عملیات هاست سرویس , همانند مثال ذکر شده در این پست, بر روی کنسول اپلیکیشن انجام شود ,مشکلی ندارد.
    لطفا راهنمایی نمایید
    • #
      ‫۸ سال و ۱۰ ماه قبل، یکشنبه ۸ آذر ۱۳۹۴، ساعت ۱۲:۳۶
      مرتبط هست به سشن صفر  
  • #
    ‫۸ سال و ۹ ماه قبل، سه‌شنبه ۲۴ آذر ۱۳۹۴، ساعت ۱۲:۱۳
    سلام؛ ممنون از مطلب مفیدتون. من راهی رو گفتیم رفتم wcfservice هم run کردم و درست بود اما با اجرای WinForm  و  زدن آدرس locallhost:8010 چیزی باز نمیشه. کد زیر هم اصلا کار نمیکنه:
     <script>
      $( "#get-scan" ).click(function () {
           var url = 'http://localhost:8010/' ;
           $. get (url, function (data) {
           $( "#img-scanned" ).attr( "src" , "data:image/Jpeg;base64,  " + data.GetScanResult);
        });
      });
    پورت رو هم 6019 میذارم بازم همینطور. ممنون میشم راهنمایی کنید.
    • #
      ‫۸ سال و ۹ ماه قبل، سه‌شنبه ۲۴ آذر ۱۳۹۴، ساعت ۱۳:۳۸
      شما نیاز هست با نحوه‌ی دیباگ برنامه‌های وب آشنا بشی. بدون اون نمی‌تونی خطایابی کنی. کامنت‌ها رو هم از ابتدا بخون. بدون اون‌ها هم نمی‌تونی برنامه رو اجرا کنی. چون یک سری نکته‌ی خاص دارن که در متن اصلی نیست.
      • #
        ‫۸ سال و ۹ ماه قبل، سه‌شنبه ۲۴ آذر ۱۳۹۴، ساعت ۱۹:۰۴
        ممنون از راهنماییتون  من دیباگ کردم خطای زیر هست
        Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:6019/GetScan. (Reason: CORS header 'Access-Control-Allow-Origin' missing).

         با اینکه من اون راه حلی رو ک گفتن اضافه کردم
        • #
          ‫۸ سال و ۹ ماه قبل، چهارشنبه ۲۵ آذر ۱۳۹۴، ساعت ۰۰:۰۳
          این خطا رو که جستجو کنید راه حل‌های متنوعی برای اون گفته شده که در پروژه بنده همین روشی که اینجا معرفی کردم کار می‌کرد و متاسفانه با روش‌های دیگه به جواب نمی‌رسیدم! (^ )
          شاید اگر شما از اون روش‌ها استفاده کنید جواب بگیرید.
          یک راه اینکه کد‌های زیر رو به Web.Config پروژه WEB API خودتون اضافه کنید:
          <httpProtocol>
                <customHeaders>
                  <add name="Access-Control-Allow-Origin" value="*" />
                  <add name="Access-Control-Allow-Headers" value="Content-Type" />
                  <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
                </customHeaders>
          </httpProtocol>


           
  • #
    ‫۸ سال و ۷ ماه قبل، دوشنبه ۱۰ اسفند ۱۳۹۴، ساعت ۲۰:۲۶
    با سلام و تشکر از پست گذاشته شده.
    امکانش هست یکی از دوستان کد من رو چک کنن که چرا سرویس اجرا نمیشه؟ScanerHost.zip
    خیلی ممنون میشم
    • #
      ‫۸ سال و ۷ ماه قبل، دوشنبه ۱۰ اسفند ۱۳۹۴، ساعت ۲۲:۲۲
      - در کانفیگ کدهای شما قسمت زیر موجود نیست (مربوط است به Cross-Origin Request یا Cors و برای دسترسی به آن از طریق وب ضروری است):
            <behaviorExtensions>
              <add name="CorsSupport" type="WebHttpCors.CorsSupportBehaviorElement, WebHttpCors, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
            </behaviorExtensions>
      کدهای WebHttpCors کمی بالاتر ارسال شده‌است.
      - همچنین نحوه‌ی خطایابی و تفسیر این کدها نیز مهم هستند (^ و ^ و ^).
  • #
    ‫۵ سال و ۳ ماه قبل، یکشنبه ۲۶ خرداد ۱۳۹۸، ساعت ۲۰:۳۷
    با سلام و تشکر از مطلب مفیدتون
    من wcf رو روی یک ویندوز سرویس host کردم و از طریق یک وب اپلیکیشن موفق به فراخوانی متد getScan شدم.
    موردی که هست روی متد ShowAcquireImage  ارور تایم اوت میگیرم. 
    بقیه متدهای این کلاس مثل ShowSelectDevice یا ShowAcquisitionWizard  رو میتونم فراخوانی کنم و پاسخ هم میگیرم ولی متد مربوط به Image اجرا نمیشه.
    ممنون میشم راهنماییم کنین.
  • #
    ‫۵ سال و ۱ ماه قبل، دوشنبه ۷ مرداد ۱۳۹۸، ساعت ۰۰:۲۷
    یک نکته‌ی تکمیلی

    کتابخانه‌ی « DNTScanner.Core » امکان کار با اسکنر را در برنامه‌های NET 4x‌. و همچنین NET Core. ویندوزی میسر می‌کند. روشی که در آن مورد استفاده قرار گرفته، مشکلات مطرح شده‌ی در نظرات مطلب جاری را مانند عدم امکان استفاده‌ی از آن، در سرویس‌های پس‌زمینه را ندارد؛ از این جهت که برای دسترسی به اسکنر، هیچ نوع UI ای را نمایش نمی‌دهد و تمام تنظیمات آن با کدنویسی است.

    مثال‌ها
    - روش استفاده از آن در برنامه‌های کنسول
    - روش استفاده‌ی از آن در یک برنامه‌ی وب ASP.NET Core که قسمت اسکنر آن به صورت یک کلاینت کنسول تهیه شده‌است و ارتباط بین این دو از طریق SignalR.Core برقرار می‌شود.