اشتراک‌ها
نگارش‌های 5.0.4, 3.1.13, 2.1.26 دات نت و NET Core. منتشر شدند

Today, we are releasing the .NET March 2021 Updates. These updates contains reliability and security improvements. See the individual release notes for details on updated packages.

You can download 5.0.4 , 3.1.13, 2.1.26 versions for Windows, macOS, and Linux, for x86, x64, Arm32, and Arm64.

نگارش‌های 5.0.4, 3.1.13, 2.1.26 دات نت و NET Core. منتشر شدند
اشتراک‌ها
دوره کامل NET MAUI.

.NET MAUI Full Course : Cross Platform Development
.NET MAUI allows you to create multi-platform apps using one code base. That means you can develop for Android, iOS, macOS and of course Windows at the same time!
In this course I provide numerous examples on how to use all the tools MAUI provides. Check out the long table of contents below.  
 

دوره کامل NET MAUI.
اشتراک‌ها
Docker ها در ویندوز سرور 2016
 Today the Docker team announced the availability of a technical preview of the Docker Engine for Windows Server 2016. This is huge for us and all software teams that work primarily on the Windows platform
یک لینک هم در مورد Dockerها در ویندوز سرور 2016 از سایت weblogs.asp.net
Visual Studio 2015 Tools for Docker - August Preview 

برای اطلاعات بیشتر در مورد Docker‌ها هم می‌توانید به ترجمه این مصاحبه مراجعه کنید  ^^^
Docker ها در ویندوز سرور 2016
مطالب
امکان استفاده‌ی مستقیم از کتابخانه‌های Full .NET Framework در NET Core 2.0.
یکی از مواردی که به همراه NET Core 1.x. وجود دارد، کمبود کتابخانه‌های ثالث مخصوص آن است. برای مثال کتابخانه‌ی log4net در اوایل ارائه‌ی NET Core. نگارش مخصوص به آن‌را نداشت (البته هم اکنون دارد). باید درنظر داشت، این مورد صرفا در حالت توزیع چندسکویی برنامه‌های مبتنی بر NET Core. مشکل ایجاد می‌کرد. از این جهت که می‌توان full .NET framework را به عنوان Target Framework برنامه‌های NET Core. معرفی کرد و در این حالت برنامه بدون هیچگونه مشکلی تنها بر روی ویندوز و سرورهای ویندوزی اجرا می‌شود (و امکان دسترسی به تمامی کتابخانه‌های مخصوص full .NET framework را نیز دارا خواهد بود)؛ اما قابلیت توزیع بر روی لینوکس و مک را از دست خواهد داد.
در NET Core 2.0. از یک اصطلاحا «compatibility shim» مخصوص استفاده می‌شود که امکان افزودن ارجاعات به full framework library‌ها را بدون نیاز به تغییر target framework برنامه میسر می‌کند. یعنی در اینجا می‌توان یک کتابخانه‌ی قدیمی دات نتی را در برنامه‌های مبتنی بر NET Core. بر روی لینوکس نیز اجرا کرد و در این حالت نیازی به تبدیل اجباری این کتابخانه به نسخه‌ی NET Core. آن نیست.


NET Core 2.0. پیاده سازی کننده‌ی NET Standard 2.0. است

NET Standard‌. در حقیقت یک قرار داد است که سکوهای کاری مختلف دات نتی مانند Full .NET Framework ، Xamarin ، Mono ، UWP و غیره می‌توانند آن‌را پیاده سازی کنند. یک نمونه‌ی دیگر این پیاده سازی‌ها نیز NET Core. است. برای مثال دات نت 4.6.1، استاندارد و قرار داد شماره‌ی 2 دات نت را پیاده سازی می‌کند. به همین صورت NET Core 2.0. نیز پیاده سازی کننده‌ی این استاندارد شماره 2 است.
 با تغییرات اخیر، اکنون NuGet می‌تواند کتابخانه‌های مبتنی بر NET Standard 2. را در برنامه‌های مبتنی بر سکوهای کاری که آن‌را پیاده سازی می‌کنند، بدون مشکل اضافه کند. برای مثال می‌توان اسمبلی‌های دات نت 4.6.1 را به برنامه‌های ASP.NET Core 2.0 اضافه کرد (کاری که در نگارش 1x آن به صورت مستقیم میسر نیست) و یا می‌توان اسمبلی‌های کامپایل شده‌ی برای دات نت استاندارد 2 را به برنامه‌های مبتنی بر دات نت 4.6.1 اضافه کرد.


آیا واقعا کتابخانه‌های قدیمی دات نتی توسط برنامه‌های NET Core 2.0. در لینوکس نیز اجرا خواهند شد؟

دات نت استاندارد، بیش از یک قرار داد چیزی نیست و پیاده سازی کنندگان آن می‌توانند سطح بیشتری را نسبت به این قرار داد نیز لحاظ کنند. برای مثال دات نت 4.6.1 شامل سطح API بیشتری از دات نت استاندارد 2 است.
 به همین جهت باید درنظر داشت که امکان اضافه کردن یک بسته‌ی نیوگت از یک کتابخانه‌ی نوشته شده‌ی برای دات نت کامل در برنامه‌های دات نت Core به معنای تضمینی برای کار کردن آن در زمان اجرا نخواهد بود. از این جهت که دات نت کامل، به همراه قسمت‌هایی است که در NET Standard. وجود خارجی ندارند. بنابراین اگر کتابخانه‌ی استفاده شده صرفا این API مشترک را هدف قرار داده‌است، هم قابلیت اتصال و هم قابلیت اجرا را خواهد داشت؛ اما اگر برای مثال کسی بسته‌ی NServiceBus را به پروژه‌ی ASP.NET Core 2.0 اضافه کند، بدون مشکل کامپایل خواهد شد. اما از آنجائیکه این کتابخانه از MSMQ استفاده می‌کند که خارج از میدان دید این استاندارد است، در زمان اجرا با شکست مواجه خواهد شد.


«compatibility shim» در NET Standard 2.0. چگونه کار می‌کند؟

در NET Core.، پیاده سازی Object در System.Runtime قرار دارد و کد تولید شده‌ی توسط آن یک چنین ارجاعی را [System.Runtime]System.Object تولید می‌کند. اما در دات نت کلاسیک، System.Object در mscorlib قرار دارد. به همین جهت زمانیکه سعی کنید اسمبلی‌های دات نت کلاسیک را در NET Core 1.x. استفاده کنید، پیام یافتن نشدن نوع‌ها را دریافت خواهید کرد. اما در NET Core 2.0. یک پیاده سازی صوری (facade) از mscorlib وجود دارد که کار آن هدایت نوع درخواستی، به نوع واقعی پیاده سازی شده‌ی در NET Core. است.


در این تصویر استفاده‌ی از یک کتابخانه‌ی ثالث را مشاهده می‌کنید که ارجاعی را به [mscorlib]Microsoft.Win32.RegistryKey دارد (مبتنی بر دات نت کلاسیک است).  همچنین یک mscorlib مشخص شده‌ی به صورت facade را نیز مشاهده می‌کنید. کار آن هدایت درخواست نوع واقع شده‌ی در mscorlib، به نوع موجود [Microsoft.Win32.Registry] Microsoft.Win32.RegistryKey است و تنها زمانی کار خواهد کرد که Microsoft.Win32.RegistryKey.dll وجود خارجی داشته باشد. به این معنا که رجیستری، یک مفهوم ویندوزی است و این کتابخانه بر روی ویندوز بدون مشکل کار می‌کند. اما تحت لینوکس، این قسمت خاص با پیام PlatformNotSupportedException خاتمه خواهد یافت. اما اگر قسمت‌هایی از این کتابخانه را استفاده کنید که در تمام سکوهای کاری وجود داشته باشند، بدون مشکل قادر به استفاده‌ی از آن خواهید بود.


یک مثال: استفاده از کتابخانه‌ی رمزنگاری اطلاعات Inferno

آخرین نگارش کتابخانه‌ی رمزنگاری اطلاعات Inferno مربوط به NET 4.5.2. است. مراحل ذیل را پس از نصب SDK جدید NET Core 2.0. در خط فرمان طی می‌کنیم:
الف) ایجاد پوشه‌ی UseNET452InNetCore2 و سپس ایجاد یک پروژه‌ی کنسول جدید
dotnet new console

ب) افزودن بسته‌ی نیوگت Inferno به پروژه
 dotnet add package Inferno
این بسته بدون مشکل اضافه می‌شود؛ البته پیام اخطار ذیل نیز صادر خواهد شد (چون مبتنی بر NET 4.6.1. که پیاده سازی کننده‌ی NET Standard 2.0. است، نیست):
 log  : Installing Inferno 1.4.0.
warn : Package 'Inferno 1.4.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.0'. This package may not be fully compatible with your project.
info : Package 'Inferno' is compatible with all the specified frameworks in project 'D:\UseNET452InNetCore2\UseNET452InNetCore2.csproj'.
info : PackageReference for package 'Inferno' version '1.4.0' added to file 'D:\UseNET452InNetCore2\UseNET452InNetCore2.csproj'.
ابتدا پیام می‌دهد که این بسته ممکن است با NET Core 2.0. سازگار نباشد. سپس عنوان می‌کند که سازگاری کاملی را با پروژه‌ی جاری دارد و بسته را اضافه می‌کند.

ج) استفاده از کتابخانه‌ی Inferno جهت تولید یک عدد تصادفی thread safe
using System;
using SecurityDriven.Inferno;

namespace UseNET452InNetCore2
{
    class Program
    {
        static CryptoRandom random = new CryptoRandom();
        static void Main(string[] args)
        {
            Console.WriteLine($"rnd: {random.NextLong()}");
        }
    }
}

د) اجرای برنامه
در ادامه اگر دستور dotnet run را صادر کنیم، ابتدا اخطاری را صادر می‌کند که این بسته ممکن است دارای قسمت‌هایی باشد که با NET core 2.0. سازگار نیست و سپس خروجی نهایی را بدون مشکل اجرا کرده و نمایش می‌دهد.
 >dotnet run
warning NU1701:  This package may not be fully compatible with your project.
rnd: 8167886599578111106
مطالب
معماری لایه بندی نرم افزار #4

UI

در نهایت نوبت به طراحی و کدنویسی UI می‌رسد تا بتوانیم محصولات را به کاربر نمایش دهیم. اما قبل از شروع باید موضوعی را یادآوری کنم. اگر به یاد داشته باشید، در کلاس ProductService موجود در لایه‌ی Domain، از طریق یکی از روشهای الگوی Dependency Injection به نام Constructor Injection، فیلدی از نوع IProductRepository را مقداردهی نمودیم. حال زمانی که بخواهیم نمونه ای را از ProductService ایجاد نماییم، باید به عنوان پارامتر ورودی سازنده، شی ایجاد شده از جنس کلاس ProductRepository موجود در لایه Repository را به آن ارسال نماییم. اما از آنجایی که می‌خواهیم تفکیک پذیری لایه‌ها از بین نرود و UI بسته به نیاز خود، نمونه مورد نیاز را ایجاد نموده و به این کلاس ارسال کند، از ابزارهایی برای این منظور استفاده می‌کنیم. یکی از این ابزارها StructureMap می‌باشد که یک Inversion of Control Container یا به اختصار IoC Container نامیده می‌شود. با Inversion of Control در مباحث بعدی بیشتر آشنا خواهید شد. StructureMap ابزاری است که در زمان اجرا، پارامترهای ورودی سازنده‌ی کلاسهایی را که از الگوی Dependency Injection استفاده نموده اند و قطعا پارامتر ورودی آنها از جنس یک Interface می‌باشد را، با ایجاد شی مناسب مقداردهی می‌نماید.

به منظور استفاده از StructureMap در Visual Studio 2012 باید بر روی پروژه UI خود کلیک راست نموده و گزینه‌ی Manage NuGet Packages را انتخاب نمایید. در پنجره ظاهر شده و از سمت چپ گزینه‌ی Online و سپس در کادر جستجوی سمت راست و بالای پنجره واژه‌ی structuremap را جستجو کنید. توجه داشته باشید که باید به اینترنت متصل باشید تا بتوانید Package مورد نظر را نصب نمایید. پس از پایان عمل جستجو، در کادر میانی structuremap ظاهر می‌شود که می‌توانید با انتخاب آن و فشردن کلید Install آن را بر روی پروژه نصب نمایید.

جهت آشنایی بیشتر با NuGet و نصب آن در سایر نسخه‌های Visual Studio می‌توانید به لینک‌های زیر رجوع کنید:

1. آشنایی با  NuGetقسمت اول

2. آشنایی با  NuGetقسمت دوم

3. Installing NuGet

کلاسی با نام BootStrapper را با کد زیر به پروژه UI خود اضافه کنید:

using StructureMap;
using StructureMap.Configuration.DSL;
using SoCPatterns.Layered.Repository;
using SoCPatterns.Layered.Model;

namespace SoCPatterns.Layered.WebUI
{
    public class BootStrapper
    {
        public static void ConfigureStructureMap()
        {
            ObjectFactory.Initialize(x => x.AddRegistry<ProductRegistry>());
        }
    }
    public class ProductRegistry : Registry
    {
        public ProductRegistry()
        {
            For<IProductRepository>().Use<ProductRepository>();
        }
    }
}

ممکن است یک WinUI ایجاد کرده باشید که در این صورت به جای فضای نام SoCPatterns.Layered.WebUI از SoCPatterns.Layered.WinUI استفاده نمایید.

هدف کلاس BootStrapper این است که تمامی وابستگی‌ها را توسط StructureMap در سیستم Register نماید. زمانی که کدهای کلاینت می‌خواهند به یک کلاس از طریق StructureMap دسترسی داشته باشند، Structuremap وابستگی‌های آن کلاس را تشخیص داده و بصورت خودکار پیاده سازی واقعی (Concrete Implementation) آن کلاس را، براساس همان چیزی که ما برایش تعیین کردیم، به کلاس تزریق می‌نماید. متد ConfigureStructureMap باید در همان لحظه ای که Application آغاز به کار می‌کند فراخوانی و اجرا شود. با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:

در WebUI:

فایل Global.asax را به پروژه اضافه کنید و کد آن را بصورت زیر تغییر دهید:

namespace SoCPatterns.Layered.WebUI
{
    public class Global : System.Web.HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            BootStrapper.ConfigureStructureMap();
        }
    }
}

در WinUI:

در فایل Program.cs کد زیر را اضافه کنید:

namespace SoCPatterns.Layered.WinUI
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            BootStrapper.ConfigureStructureMap();
            Application.Run(new Form1());
        }
    }
}

سپس برای طراحی رابط کاربری، با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:

در WebUI:

صفحه Default.aspx را باز نموده و کد زیر را به آن اضافه کنید:

<asp:DropDownList AutoPostBack="true" ID="ddlCustomerType" runat="server">
    <asp:ListItem Value="0">Standard</asp:ListItem>
    <asp:ListItem Value="1">Trade</asp:ListItem>
</asp:DropDownList>
<asp:Label ID="lblErrorMessage" runat="server" ></asp:Label>
<asp:Repeater ID="rptProducts" runat="server" >
    <HeaderTemplate>
        <table>
            <tr>
                <td>Name</td>
                <td>RRP</td>
                <td>Selling Price</td>
                <td>Discount</td>
                <td>Savings</td>
            </tr>
            <tr>
                <td colspan="5"><hr /></td>
            </tr>
    </HeaderTemplate>
    <ItemTemplate>
            <tr>
                <td><%# Eval("Name") %></td>
                <td><%# Eval("RRP")%></td>
                <td><%# Eval("SellingPrice") %></td>
                <td><%# Eval("Discount") %></td>
                <td><%# Eval("Savings") %></td>
            </tr>
    </ItemTemplate>
    <FooterTemplate>
        </table>
    </FooterTemplate>
</asp:Repeater>

در WinUI:

فایل Form1.Designer.cs را باز نموده و کد آن را بصورت زیر تغییر دهید:

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
    this.cmbCustomerType = new System.Windows.Forms.ComboBox();
    this.dgvProducts = new System.Windows.Forms.DataGridView();
    this.colName = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colRrp = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colSellingPrice = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colDiscount = new System.Windows.Forms.DataGridViewTextBoxColumn();
    this.colSavings = new System.Windows.Forms.DataGridViewTextBoxColumn();
    ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).BeginInit();
    this.SuspendLayout();
    //
    // cmbCustomerType
    //
    this.cmbCustomerType.DropDownStyle =                                                                                                                                                                             System.Windows.Forms.ComboBoxStyle.DropDownList;
    this.cmbCustomerType.FormattingEnabled = true;
    this.cmbCustomerType.Items.AddRange(new object[] {
        "Standard",
        "Trade"});
    this.cmbCustomerType.Location = new System.Drawing.Point(12, 90);
    this.cmbCustomerType.Name = "cmbCustomerType";
    this.cmbCustomerType.Size = new System.Drawing.Size(121, 21);
    this.cmbCustomerType.TabIndex = 3;
    //
    // dgvProducts
    //
    this.dgvProducts.ColumnHeadersHeightSizeMode =     System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
    this.dgvProducts.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
    this.colName,
    this.colRrp,
    this.colSellingPrice,
    this.colDiscount,
    this.colSavings});
    this.dgvProducts.Location = new System.Drawing.Point(12, 117);
    this.dgvProducts.Name = "dgvProducts";
    this.dgvProducts.Size = new System.Drawing.Size(561, 206);
    this.dgvProducts.TabIndex = 2;
    //
    // colName
    //
    this.colName.DataPropertyName = "Name";
    this.colName.HeaderText = "Product Name";
    this.colName.Name = "colName";
    this.colName.ReadOnly = true;
    //
    // colRrp
    //
    this.colRrp.DataPropertyName = "Rrp";
    this.colRrp.HeaderText = "RRP";
    this.colRrp.Name = "colRrp";
    this.colRrp.ReadOnly = true;
    //
    // colSellingPrice
    //
    this.colSellingPrice.DataPropertyName = "SellingPrice";
    this.colSellingPrice.HeaderText = "Selling Price";
    this.colSellingPrice.Name = "colSellingPrice";
    this.colSellingPrice.ReadOnly = true;
    //
    // colDiscount
    //
    this.colDiscount.DataPropertyName = "Discount";
    this.colDiscount.HeaderText = "Discount";
    this.colDiscount.Name = "colDiscount";
    //
    // colSavings
    //
    this.colSavings.DataPropertyName = "Savings";
    this.colSavings.HeaderText = "Savings";
    this.colSavings.Name = "colSavings";
    this.colSavings.ReadOnly = true;
    //
    // Form1
    //
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(589, 338);
    this.Controls.Add(this.cmbCustomerType);
    this.Controls.Add(this.dgvProducts);
    this.Name = "Form1";
    this.Text = "Form1";
    ((System.ComponentModel.ISupportInitialize)(this.dgvProducts)).EndInit();
    this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ComboBox cmbCustomerType;
private System.Windows.Forms.DataGridView dgvProducts;
private System.Windows.Forms.DataGridViewTextBoxColumn colName;
private System.Windows.Forms.DataGridViewTextBoxColumn colRrp;
private System.Windows.Forms.DataGridViewTextBoxColumn colSellingPrice;
private System.Windows.Forms.DataGridViewTextBoxColumn colDiscount;
private System.Windows.Forms.DataGridViewTextBoxColumn colSavings;

سپس در Code Behind، با توجه به نوع UI خود یکی از روالهای زیر را انجام دهید:

در WebUI:

وارد کد نویسی صفحه Default.aspx شده و کد آن را بصورت زیر تغییر دهید:

using System;
using System.Collections.Generic;
using SoCPatterns.Layered.Model;
using SoCPatterns.Layered.Presentation;
using SoCPatterns.Layered.Service;
using StructureMap;

namespace SoCPatterns.Layered.WebUI
{
    public partial class Default : System.Web.UI.Page, IProductListView
    {
        private ProductListPresenter _productListPresenter;
        protected void Page_Init(object sender, EventArgs e)
        {
            _productListPresenter = new ProductListPresenter(this,ObjectFactory.GetInstance<Service.ProductService>());
            this.ddlCustomerType.SelectedIndexChanged +=
                delegate { _productListPresenter.Display(); };
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if(!Page.IsPostBack)
                _productListPresenter.Display();
        }
        public void Display(IList<ProductViewModel> products)
        {
            rptProducts.DataSource = products;
            rptProducts.DataBind();
        }
        public CustomerType CustomerType
        {
            get { return (CustomerType) int.Parse(ddlCustomerType.SelectedValue); }
        }
        public string ErrorMessage
        {
            set
            {
                lblErrorMessage.Text =
                    String.Format("<p><strong>Error:</strong><br/>{0}</p>", value);
            }
        }
    }
}

در WinUI:

وارد کدنویسی Form1 شوید و کد آن را بصورت زیر تغییر دهید:

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using SoCPatterns.Layered.Model;
using SoCPatterns.Layered.Presentation;
using SoCPatterns.Layered.Service;
using StructureMap;

namespace SoCPatterns.Layered.WinUI
{
    public partial class Form1 : Form, IProductListView
    {
        private ProductListPresenter _productListPresenter;
        public Form1()
        {
            InitializeComponent();
            _productListPresenter =
                new ProductListPresenter(this, ObjectFactory.GetInstance<Service.ProductService>());
            this.cmbCustomerType.SelectedIndexChanged +=
                delegate { _productListPresenter.Display(); };
            dgvProducts.AutoGenerateColumns = false;
            cmbCustomerType.SelectedIndex = 0;
        }
        public void Display(IList<ProductViewModel> products)
        {
            dgvProducts.DataSource = products;
        }
        public CustomerType CustomerType
        {
            get { return (CustomerType)cmbCustomerType.SelectedIndex; }
        }
        public string ErrorMessage
        {
            set
            {
                MessageBox.Show(
                    String.Format("Error:{0}{1}", Environment.NewLine, value));
            }
        }
    }
}

با توجه به کد فوق، نمونه ای را از کلاس ProductListPresenter، در لحظه‌ی نمونه سازی اولیه‌ی کلاس UI، ایجاد نمودیم. با استفاده از متد ObjectFactory.GetInstance مربوط به StructureMap، نمونه ای از کلاس ProductService ایجاد شده است و به سازنده‌ی کلاس ProductListPresenter ارسال گردیده است. در مورد Structuremap در مباحث بعدی با جزئیات بیشتری صحبت خواهم کرد. پیاده سازی معماری لایه بندی در اینجا به پایان رسید.

اما اصلا نگران نباشید، شما فقط پرواز کوتاه و مختصری را بر فراز کدهای معماری لایه بندی داشته اید که این فقط یک دید کلی را به شما در مورد این معماری داده است. این معماری هنوز جای زیادی برای کار دارد، اما در حال حاضر شما یک Applicaion با پیوند ضعیف (Loosely Coupled) بین لایه‌ها دارید که دارای قابلیت تست پذیری قوی، نگهداری و پشتیبانی آسان و تفکیک پذیری قدرتمند بین اجزای آن می‌باشد. شکل زیر تعامل بین لایه‌ها و وظایف هر یک از آنها را نمایش می‌دهد.

اشتراک‌ها
لیست کاملی از سوئیچ‌های خط فرمان اجرای مرورگر کروم

List of Chromium Command Line Switches
There are lots of command lines which can be used with the Google Chrome browser. Some change behavior of features, others are for debugging or experimenting. This page lists the available switches including their conditions and descriptions. Last automated update occurred on 2023-05-28.  

لیست کاملی از سوئیچ‌های خط فرمان اجرای مرورگر کروم