[
  {
    "path": ".editorconfig",
    "content": "﻿[*]\nend_of_line = crlf\ncharset = utf-8-bom\nindent_size = 4\ninsert_final_newline = true\ntab_width = 4\ntrim_trailing_whitespace = true\n\n[*.{xml,csproj,vbproj,props,targets}]\nindent_size =2\nindent_style = space\ntab_width =2\n\n[*.{cs,vb}]\ndotnet_sort_system_directives_first = true\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_explicit_tuple_names = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent\ndotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent\ndotnet_style_predefined_type_for_locals_parameters_members = true:silent\ndotnet_style_predefined_type_for_member_access = true:silent\ndotnet_style_prefer_auto_properties = true:silent\ndotnet_style_prefer_conditional_expression_over_assignment = true\ndotnet_style_prefer_conditional_expression_over_return = true\ndotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion\ndotnet_style_prefer_inferred_tuple_names = true:suggestion\ndotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent\ndotnet_style_qualification_for_event = false:silent\ndotnet_style_qualification_for_field = false:silent\ndotnet_style_qualification_for_method = false:silent\ndotnet_style_qualification_for_property = false:silent\ndotnet_style_readonly_field = true:suggestion\ndotnet_style_require_accessibility_modifiers = for_non_interface_members:silent\n\n[*.cs]\ncsharp_indent_case_contents = true\ncsharp_indent_labels = flush_left\ncsharp_indent_switch_labels = true\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_else = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_anonymous_types = true\ncsharp_new_line_before_members_in_object_initializers = true\ncsharp_new_line_before_open_brace = all\ncsharp_new_line_between_query_expression_clauses = true\ncsharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion\ncsharp_prefer_braces = true:silent\ncsharp_prefer_simple_default_expression = true:suggestion\ncsharp_preserve_single_line_blocks = true\ncsharp_space_after_cast = true\ncsharp_space_after_colon_in_inheritance_clause = true\ncsharp_space_after_keywords_in_control_flow_statements = true\ncsharp_space_around_binary_operators = before_and_after\ncsharp_space_before_colon_in_inheritance_clause = true\ncsharp_space_between_method_call_empty_parameter_list_parentheses = false\ncsharp_space_between_method_call_name_and_opening_parenthesis = false\ncsharp_space_between_method_call_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_empty_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_parameter_list_parentheses = false\ncsharp_space_between_parentheses = false\ncsharp_style_conditional_delegate_call = true:suggestion\ncsharp_style_deconstructed_variable_declaration = true:suggestion\ncsharp_style_expression_bodied_accessors = true:silent\ncsharp_style_expression_bodied_constructors = false:silent\ncsharp_style_expression_bodied_indexers = true:silent\ncsharp_style_expression_bodied_methods = false:silent\ncsharp_style_expression_bodied_operators = false:silent\ncsharp_style_expression_bodied_properties = true:silent\ncsharp_style_inlined_variable_declaration = true:suggestion\ncsharp_style_pattern_local_over_anonymous_function = true:suggestion\ncsharp_style_pattern_matching_over_as_with_null_check = true:suggestion\ncsharp_style_pattern_matching_over_is_with_cast_check = true:suggestion\ncsharp_style_throw_expression = true:suggestion\ncsharp_style_var_elsewhere = true:silent\ncsharp_style_var_for_built_in_types = true:silent\ncsharp_style_var_when_type_is_apparent = true:silent\n"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".github/workflows/dotnet-core.yml",
    "content": "﻿name: .NET Core\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: windows-latest\n    timeout-minutes: 15\n\n    steps:\n    - uses: actions/checkout@v1\n    - name: Setup .NET\n      uses: actions/setup-dotnet@v1\n      with:\n        dotnet-version: |\n          3.1.x\n          6.0.x\n          8.0.x\n          9.0.x\n\n    - name: Build\n      run: dotnet build -c release\n\n    - name: Test-net8.0\n      run: dotnet test -c release -f net8.0 -v:n --tl:off --no-build --logger:\"console;verbosity=detailed\"\n\n    - name: Test-net6.0\n      run: dotnet test -c release -f net6.0 -v:n --tl:off --no-build --logger:\"console;verbosity=detailed\"\n\n    - name: Pack\n      run: dotnet pack -c release --no-build\n"
  },
  {
    "path": ".github/workflows/dotnet-format.yml",
    "content": "﻿name: Code format check\n# 代码格式化机器人，详细请看 [dotnet 基于 dotnet format 的 GitHub Action 自动代码格式化机器人](https://blog.lindexi.com/post/dotnet-%E5%9F%BA%E4%BA%8E-dotnet-format-%E7%9A%84-GitHub-Action-%E8%87%AA%E5%8A%A8%E4%BB%A3%E7%A0%81%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%9C%BA%E5%99%A8%E4%BA%BA.html )\n\non: \n  push:\n    branches: \n      - main\njobs:\n  dotnet-format:\n    runs-on: windows-latest\n    steps:\n\n      - name: Checkout repo\n        uses: actions/checkout@v2\n        with:\n          ref: ${{ github.head_ref }}\n      \n      - name: Setup .NET\n        uses: actions/setup-dotnet@v1\n        with:\n          dotnet-version: |\n            3.1.x\n            6.0.x\n            8.0.x\n            9.0.x\n\n      - name: Install dotnetCampus.EncodingNormalior\n        run: dotnet tool update -g dotnetCampus.EncodingNormalior\n\n      - name: Fix encoding\n        run: EncodingNormalior -f . --TryFix true\n\n      - name: Install dotnet-format\n        run: dotnet tool install -g dotnet-format\n\n      - name: Run dotnet format\n        run: dotnet format\n\n      - name: Commit files\n        # 下面将使用机器人的账号，你可以替换为你自己的账号\n        run: |\n          git config --local user.name \"github-actions-dotnet-formatter[bot]\"\n          git config --local user.email \"41898282+github-actions[bot]@users.noreply.github.com\"\n          git commit -a -m 'Automated dotnet-format update'\n        continue-on-error: true\n        \n      - name: Create Pull Request\n        # if: steps.format.outputs.has-changes == 'true' # 如果有格式化，才继续\n        uses: peter-evans/create-pull-request@v3\n        with:\n          title: '[Bot] Automated PR to fix formatting errors'\n          body: |\n            Automated PR to fix formatting errors\n          committer: GitHub <noreply@github.com>\n          author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>\n          # 以下是给定代码审查者，需要设置仓库有权限的开发者\n          assignees: lindexi,walterlv\n          reviewers: lindexi,walterlv\n          # 对应的上传分支\n          branch: t/bot/fix-codeformatting\n"
  },
  {
    "path": ".github/workflows/nuget-tag-publish.yml",
    "content": "name: publish nuget\n\non: \n  push:\n    tags:\n    - '*'\n\njobs:\n  build:\n\n    runs-on: windows-latest\n\n    steps:\n    - uses: actions/checkout@v1\n\n    - name: Setup .NET\n      uses: actions/setup-dotnet@v1\n      with:\n        dotnet-version: |\n          3.1.x\n          6.0.x\n          8.0.x\n          9.0.x\n\n    - name: Install dotnet tool\n      run: dotnet tool install -g dotnetCampus.TagToVersion\n\n    - name: Set tag to version  \n      run: dotnet TagToVersion -t ${{ github.ref }}\n\n    - name: Build with dotnet\n      run: |\n        dotnet build --configuration Release\n        dotnet pack --configuration Release --no-build\n\n    - name: Install Nuget\n      uses: nuget/setup-nuget@v1\n      with:\n        nuget-version: '6.x'\n\n    - name: Add private GitHub registry to NuGet\n      run: |\n        nuget sources add -name github -Source https://nuget.pkg.github.com/dotnet-campus/index.json -Username dotnet-campus -Password ${{ secrets.GITHUB_TOKEN }}\n\n    - name: Push generated package to GitHub registry\n      run: |\n        nuget push .\\artifacts\\package\\release\\*.nupkg -Source github -SkipDuplicate\n        nuget push .\\artifacts\\package\\release\\*.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NugetKey }} \n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- Backup*.rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n/GetAssemblyVersionTask/Properties/launchSettings.json\n/WriteAppVersionTask/Properties/launchSettings.json\n/src/dotnetCampus.Ipc.PipeMvc/Properties/launchSettings.json\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# dotnetCampus.Ipc"
  },
  {
    "path": "Directory.Build.props",
    "content": "﻿<Project>\n\n  <Import Project=\"build\\Version.props\" />\n\n  <!-- Framework and language -->\n  <PropertyGroup>\n    <!-- Language -->\n    <LangVersion>latest</LangVersion>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <!-- Build -->\n    <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>\n    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>\n    <Deterministic>true</Deterministic>\n    <!--\n      Add NoWarn to remove build warnings\n      NU1803: Using an insecure http NuGet source\n    -->\n    <NoWarn>$(NoWarn);NU1507;NU1803;NETSDK1201;NETSDK1138;PRI257</NoWarn>\n    <!--\n      CA1416: Platform compatibility warning\n    -->\n    <WarningsAsErrors>$(WarningsAsErrors);CA1416</WarningsAsErrors>\n  </PropertyGroup>\n\n  <!-- Custom properties -->\n  <PropertyGroup>\n    <RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>\n    <ThisYear>$([System.DateTime]::Now.ToString(`yyyy`))</ThisYear>\n  </PropertyGroup>\n\n  <!-- Package and project properties -->\n  <PropertyGroup>\n    <Description>本机内多进程通讯库，稳定 IPC 通讯库</Description>\n    <Company>dotnet campus（.NET 职业技术学院）</Company>\n    <Authors>dotnet-campus</Authors>\n    <Copyright>Copyright © 2020-$(ThisYear) dotnet campus, All Rights Reserved.</Copyright>\n    <RepositoryType>git</RepositoryType>\n    <RepositoryUrl>https://github.com/dotnet-campus/dotnetCampus.Ipc</RepositoryUrl>\n    <PackageProjectUrl>https://github.com/dotnet-campus/dotnetCampus.Ipc</PackageProjectUrl>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "Directory.Packages.props",
    "content": "﻿<Project>\n  <ItemGroup>\n    <PackageVersion Include=\"coverlet.collector\" Version=\"6.0.2\" />\n    <PackageVersion Include=\"dotnetCampus.AsyncWorkerCollection.Source\" Version=\"1.7.2\" />\n    <PackageVersion Include=\"DotNetCampus.CodeAnalysisUtils\" Version=\"0.0.1-alpha.5\" />\n    <PackageVersion Include=\"DotNetCampus.CommandLine\" Version=\"4.0.1-alpha.1\" />\n    <PackageVersion Include=\"DotNetCampus.LatestCSharpFeatures\" Version=\"13.0.0\" />\n    <PackageVersion Include=\"Microsoft.AspNetCore.Hosting\" Version=\"2.2.7\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis\" Version=\"4.4.0\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.Analyzers\" Version=\"3.3.4\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.CSharp\" Version=\"4.4.0\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.MSTest\" Version=\"1.1.1\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.MSTest\" Version=\"1.1.1\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.MSTest\" Version=\"1.1.1\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.CSharp.Workspaces\" Version=\"4.4.0\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing.MSTest\" Version=\"1.1.1\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing.MSTest\" Version=\"1.1.1\" />\n    <PackageVersion Include=\"Microsoft.CodeAnalysis.VisualBasic.CodeRefactoring.Testing.MSTest\" Version=\"1.1.1\" />\n    <PackageVersion Include=\"Microsoft.CSharp\" Version=\"4.7.0\" />\n    <PackageVersion Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.10.0\" />\n    <PackageVersion Include=\"Microsoft.SourceLink.GitHub\" Version=\"1.0.0\" />\n    <PackageVersion Include=\"Moq\" Version=\"4.20.70\" />\n    <PackageVersion Include=\"MSTest.TestAdapter\" Version=\"3.4.3\" />\n    <PackageVersion Include=\"MSTest.TestFramework\" Version=\"3.4.3\" />\n    <PackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageVersion Include=\"System.Collections.Immutable\" Version=\"9.0.10\" />\n    <PackageVersion Include=\"System.IO.Pipelines\" Version=\"8.0.0\" />\n    <PackageVersion Include=\"System.IO.Pipes.AccessControl\" Version=\"5.0.0\" />\n    <PackageVersion Include=\"System.Text.Json\" Version=\"8.0.6\" />\n    <PackageVersion Include=\"System.ValueTuple\" Version=\"4.5.0\" />\n    <PackageVersion Include=\"Walterlv.NullableAttributes.Source\" Version=\"7.4.0\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "LICENSE",
    "content": "﻿MIT License\n\nCopyright (c) 2020-2023 dotnet campus\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "﻿# dotnetCampus.Ipc\n\n本机内多进程通讯库\n\n| Build | NuGet |\n|--|--|\n|![](https://github.com/dotnet-campus/dotnetCampus.Ipc/workflows/.NET%20Core/badge.svg)|[![](https://img.shields.io/nuget/v/dotnetCampus.Ipc.svg)](https://www.nuget.org/packages/dotnetCampus.Ipc)|\n\n\n## 使用方法\n\n库中提供了较为底层的通信方案，也提供了高级的封装方案（基于Json数据格式的通信方案），完整文档可参阅：\n\n- 🌀 远程对象调用\n    - [基础入门教程](./docs/IpcObject.01.md)\n    - [通讯方式详解](./docs/IpcObject.02.md)\n- 🌐 直接路由\n    - [通讯方式详解](./docs/JsonIpcDirectRouted.md)\n\n### 案例：直接路由Json通信（需要2.0.0-alpha版本以上）\n\n#### 步骤一\n\n导入nuget包 **dotnetCampus.Ipc**（需要2.0.0-alpha版本以上），并引入所需要的命名空间；\n\n``` C#\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\nusing dotnetCampus.Ipc.Pipes;\n```\n\n#### 步骤二\n\n创建实际负责IPC通信的代理对象\n\n``` C#\n\n/// <summary>\n/// 根据<paramref name=\"pipeName\"/>创建一个 JsonIpcDirectRoutedProvider 对象\n/// </summary>\n/// <param name=\"pipeName\">不同的IPC对象所使用的管道名称，一个管道名称只能被用于一个IPC对象</param>\n/// <returns></returns>\nprivate JsonIpcDirectRoutedProvider CreateJsonIpcDirectRoutedProvider(string pipeName)\n{\n    // 创建一个 IpcProvider，实际创建管道，进行IPC通信的底层对象\n    // 可在 IpcConfiguration 进行详细的配置，包括配置断线重连、日志等级、线程池等等\n    var ipcProvider = new IpcProvider(pipeName, new IpcConfiguration());\n\n    // 创建一个 JsonIpcDirectRoutedProvider，封装了通信中的Json数据解析、简化方法调用\n    var ipcDirectRoutedProvider = new JsonIpcDirectRoutedProvider(ipcProvider);\n\n    return ipcDirectRoutedProvider;\n}\n\n```\n\n#### 步骤三\n\n向IPC对象注册接受到指定消息后的处理函数（如果该IPC对象只负责发送消息，则它不需要注册消息处理回调）\n\n``` C#\n\nvar ipcDirectRoutedProvider = CreateJsonIpcDirectRoutedProvider(\"我是接收消息的IPC对象\");\n\n//对无参的通知消息注册回调函数\nipcDirectRoutedProvider.AddNotifyHandler(\"通知消息A\", () => \n{\n    Console.WriteLine(\"我是进程A，我收到了通知消息B，该消息无参数\");\n});\n\n//对参数类型为ParamType的通知消息注册回调函数\nipcDirectRoutedProvider.AddNotifyHandler<ParamType>(\"通知消息B\", param => \n{\n    Console.WriteLine($\"我是进程A，我收到了通知消息B，该消息参数：{param.Message}\");\n});\n\n//对参数类型为ParamType的请求注册回调函数并返回响应数据（可以异步处理响应、也可以无参）\nipcDirectRoutedProvider.AddRequestHandler(\"请求消息C\", (ParamType argument) =>\n{\n    //处理请求消息C\n    var response = new IpcResponse\n    {\n        Message = $\"我是进程A，我收到了请求消息C，该消息参数：{argument.Message}\"\n    };\n\n    //返回响应数据\n    return response;\n});\n\n```\n\n#### 步骤四\n\n启动服务\n\n``` C#\n\nvar ipcDirectRoutedProvider = CreateJsonIpcDirectRoutedProvider(\"我是接收消息的IPC对象\");\n\n/**\n一些消息注册（如果该IPC对象只负责发送消息，则它不需要注册消息处理回调；接受消息的一方需要注册接收到消息后的处理函数）\n……\n**/\n\n//启动该服务\nipcDirectRoutedProvider.StartServer();\n\n```\n\n#### 步骤五\n\n发送消息（如果该IPC对象只负责接收和处理消息，则它不需要发送消息）\n\n``` C#\n\nvar ipcDirectRoutedProvider = CreateJsonIpcDirectRoutedProvider(\"我是发送消息的IPC对象\");\n//启动该服务\nipcDirectRoutedProvider.StartServer();\n//根据接收方的管道名，获取需要接受到消息的IPC对象，并发送通知\nvar ipcReceivingObjectA = await ipcDirectRoutedProvider.GetAndConnectClientAsync(\"我是接收消息的IPC对象\");\nawait ipcReceivingObjectA.NotifyAsync(\"通知消息A\");\nawait ipcReceivingObjectA.NotifyAsync(\"通知消息B\", new ParamType { Message = \"我发送的通知消息是XXX\" });\nvar response = await ipcReceivingObjectA.GetResponseAsync<IpcResponse>(\"请求消息C\", new ParamType { Message = \"我发送的请求消息XXX\" });\n\n```\n\n#### 调用关系图\n\n![](./docs/image/README/zh-CN/sample0.png)\n\n*更多案例详见：* [Demo](https://github.com/dotnet-campus/dotnetCampus.Ipc/tree/master/demo)\n\n### FAQ\n\n#### AOT 支持\n\nQ: 此 Ipc 库支持 AOT 吗？\n\nA: 此 Ipc 库支持 AOT，但需要注意以下几点：\n\n- 如果是完全使用 byte[] 作为数据传输格式，则不需要任何额外的配置，直接就支持 AOT 了\n- 如果是采用 Json 通讯系列，则需要在使用 Json 序列化时，使用 `JsonSerializerOptions` 的 `TypeInfoResolver` 属性来指定类型解析器。具体的配置可以参考 [JsonSerializerOptions](https://learn.microsoft.com/dotnet/api/system.text.json.jsonserializeroptions?view=dotnet-plat-ext-7.0) 的文档。一般而言，可采用封装好的 UseSystemTextJsonIpcObjectSerializer 扩展方法辅助传入 `System.Text.Json.Serialization.JsonSerializerContext` 对象，如以下示例代码所示\n\n``` C#\n    IpcConfiguration ipcConfiguration = new IpcConfiguration()\n    {\n        // 进行设置其他配置\n    }.UseSystemTextJsonIpcObjectSerializer(SourceGenerationContext.Default);\n\n    var ipcProvider = new IpcProvider(pipeName, ipcConfiguration);\n```\n\n或者注入 IpcConfiguration 的 IpcObjectSerializer 属性，进行更加灵活的序列化配置。此时将不仅限于使用 System.Text.Json 进行序列化，也可以使用其他的序列化方式，如二进制序列化等等\n\nQ: 采用 直接路由 Json 通信（JsonIpcDirectRoutedProvider）时，如何改造让其支持 AOT 编译？\n\nA：如上问所述，可在 IpcConfiguration 里面设置 IpcObjectSerializer 属性，或调用 UseSystemTextJsonIpcObjectSerializer 扩展辅助方法。如以下示代码所示\n\n``` C#\n    // 创建一个 IpcProvider，实际创建管道，进行IPC通信的底层对象\n    // 可在 IpcConfiguration 进行详细的配置，包括配置断线重连、日志等级、线程池等等\n    IpcConfiguration ipcConfiguration = new IpcConfiguration()\n    {\n        // 进行设置其他配置\n    }.UseSystemTextJsonIpcObjectSerializer(SourceGenerationContext.Default);\n    var ipcProvider = new IpcProvider(pipeName, ipcConfiguration);\n\n    // 创建一个 JsonIpcDirectRoutedProvider，封装了通信中的Json数据解析、简化方法调用\n    var ipcDirectRoutedProvider = new JsonIpcDirectRoutedProvider(ipcProvider);\n```\n\n\n## 项目结构图\n\n![](./docs/image/README/zh-CN/Architecture0.png)\n\n## 特点\n\n- 采用两个半工命名管道\n- 采用 P2P 方式，每个端都是服务端也是客户端\n- 提供 PeerProxy 机制，利用这个机制可以进行发送和接收某个对方的信息\n- 追求稳定，而不追求高性能\n\n## 功能\n\n- [x] 通讯建立\n- [x] 消息收到回复机制\n- [x] 断线重连功能\n- [x] 大量异常处理\n\n- [x] 支持裸数据双向传输方式\n- [x] 支持裸数据请求响应模式\n- [x] 支持字符串消息协议\n- [x] 支持远程对象调用和对象存根传输方式\n- [x] 支持 NamedPipeStreamForMvc (NamedPipeMvc) 客户端服务器端 MVC 模式\n- [x] 支持直接路由的 Json 数据通讯方式\n\n\n## 感谢\n\n- [jacqueskang/IpcServiceFramework](https://github.com/jacqueskang/IpcServiceFramework)\n- [https://github.com/dotnet/aspnetcore](https://github.com/dotnet/aspnetcore) for PipeMVC\n\n## 踩过的坑\n\n- [2019-12-1-构造PipeAccessRule时请不要使用字符串指定Identity - huangtengxiao](https://huangtengxiao.gitee.io/post/%E6%9E%84%E9%80%A0PipeAccessRule%E6%97%B6%E8%AF%B7%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%8C%87%E5%AE%9AIdentity.html)\n"
  },
  {
    "path": "analyzers/dotnetCampus.Ipc.SourceGenerators/GeneratedIpcJointGenerator.cs",
    "content": "﻿using System.Text.RegularExpressions;\n\nusing Microsoft.CodeAnalysis.Text;\n\nnamespace dotnetCampus.Ipc;\n\n/// <summary>\n/// 为 GeneratedIpcJoint 类生成更多的泛型参数。\n/// </summary>\n[Generator(LanguageNames.CSharp)]\npublic class GeneratedIpcJointGenerator : IIncrementalGenerator\n{\n    public void Initialize(IncrementalGeneratorInitializationContext context)\n    {\n        var compilations = context.SyntaxProvider.CreateSyntaxProvider(\n            // 找到 GeneratedIpcJoint 类型。\n            (syntaxNode, ct) => syntaxNode is ClassDeclarationSyntax cds && cds.Identifier.ToString() == \"GeneratedIpcJoint\",\n            // 语义解析：确定是否真的是感兴趣的 IPC 接口。\n            (generatorSyntaxContext, ct) => generatorSyntaxContext)\n            .Where(x =>\n            {\n                var node = x.Node as ClassDeclarationSyntax;\n                var symbol = x.SemanticModel.GetDeclaredSymbol(x.Node) as INamedTypeSymbol;\n                return node is not null && symbol is not null && symbol.IsGenericType;\n            });\n\n        context.RegisterSourceOutput(compilations, Execute);\n    }\n\n    private void Execute(SourceProductionContext context, GeneratorSyntaxContext generatorContext)\n    {\n        var source = GenerateGeneratedIpcJoint(generatorContext);\n        context.AddSource($\"GeneratedIpcJoint.generic\", SourceText.From(source, Encoding.UTF8));\n    }\n\n    private string GenerateGeneratedIpcJoint(GeneratorSyntaxContext context) => $$\"\"\"\n        using System;\n        using System.Collections.Generic;\n        using System.Linq;\n        using System.Threading.Tasks;\n\n        using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n        namespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n        partial class GeneratedIpcJoint<TContract>\n        {\n        {{string.Join(\"\\r\\n\\r\\n\",\n            Enumerable.Range(2, 15)\n                .Select(x => GenerateMethodGroup(context, x))\n                .SelectMany(x => x))}}\n        }\n        \"\"\";\n\n    private IEnumerable<string> GenerateMethodGroup(GeneratorSyntaxContext context, int genericCount)\n    {\n        yield return GenerateVoidMethod(context, genericCount);\n        yield return GenerateReturnMethod(context, genericCount);\n        yield return GenerateAsyncVoidMethod(context, genericCount);\n        yield return GenerateAsyncReturnMethod(context, genericCount);\n    }\n\n    private string GenerateVoidMethod(GeneratorSyntaxContext context, int genericCount) => $$\"\"\"\n        /// <summary>\n        /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n        /// </summary>\n        /// <param name=\"memberId\">方法签名的 Id。</param>\n        /// <param name=\"methodInvoker\">对接实现。</param>\n        protected void MatchMethod<{{GenerateTs(genericCount)}}>(ulong memberId, Action<{{GenerateTs(genericCount)}}> methodInvoker)\n        {\n            _methods.Add(memberId, (new[] { {{GenerateTs(genericCount, \"typeof(T)\")}} }, args =>\n            {\n                methodInvoker({{GenerateTs(genericCount, i => $\"CastArg<T>(args![{i - 1}])!\")}});\n                return DefaultGarm;\n            }\n            ));\n        }\n    \"\"\";\n\n    private string GenerateReturnMethod(GeneratorSyntaxContext context, int genericCount) => $$\"\"\"\n        /// <summary>\n        /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n        /// </summary>\n        /// <param name=\"memberId\">方法签名的 Id。</param>\n        /// <param name=\"methodInvoker\">对接实现。</param>\n        protected void MatchMethod<{{GenerateTs(genericCount)}}>(ulong memberId, Func<{{GenerateTs(genericCount)}}, Task> methodInvoker)\n        {\n            _asyncMethods.Add(memberId, (new[] { {{GenerateTs(genericCount, \"typeof(T)\")}} }, async args =>\n            {\n                await methodInvoker({{GenerateTs(genericCount, i => $\"CastArg<T>(args![{i - 1}])!\")}}).ConfigureAwait(false);\n                return DefaultGarm;\n            }\n            ));\n        }\n    \"\"\";\n\n    private string GenerateAsyncVoidMethod(GeneratorSyntaxContext context, int genericCount) => $$\"\"\"\n        /// <summary>\n        /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n        /// </summary>\n        /// <param name=\"memberId\">方法签名的 Id。</param>\n        /// <param name=\"methodInvoker\">对接实现。</param>\n        protected void MatchMethod<{{GenerateTs(genericCount)}}, TReturn>(ulong memberId, Func<{{GenerateTs(genericCount)}}, Garm<TReturn>> methodInvoker)\n        {\n            _methods.Add(memberId, (new[] { {{GenerateTs(genericCount, \"typeof(T)\")}} }, args => methodInvoker({{GenerateTs(genericCount, i => $\"CastArg<T>(args![{i - 1}])!\")}})));\n        }\n    \"\"\";\n\n    private string GenerateAsyncReturnMethod(GeneratorSyntaxContext context, int genericCount) => $$\"\"\"\n        /// <summary>\n        /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n        /// </summary>\n        /// <param name=\"memberId\">方法签名的 Id。</param>\n        /// <param name=\"methodInvoker\">对接实现。</param>\n        protected void MatchMethod<{{GenerateTs(genericCount)}}, TReturn>(ulong memberId, Func<{{GenerateTs(genericCount)}}, Task<Garm<TReturn>>> methodInvoker)\n        {\n            _asyncMethods.Add(memberId, (new[] { {{GenerateTs(genericCount, \"typeof(T)\")}} }, async args => await methodInvoker({{GenerateTs(genericCount, i => $\"CastArg<T>(args![{i - 1}])!\")}}).ConfigureAwait(false)));\n        }\n    \"\"\";\n\n    private string GenerateTs(int genericCount, string? template = null)\n    {\n        var templateWithDefault = template ?? \"T\";\n        return string.Join(\", \",\n            Enumerable.Range(1, genericCount)\n                .Select(x => GenrateT(x, templateWithDefault)));\n    }\n\n    private string GenerateTs(int genericCount, Func<int, string> templateGetter)\n    {\n        return string.Join(\", \",\n            Enumerable.Range(1, genericCount)\n                .Select(x => GenrateT(x, templateGetter(x))));\n    }\n\n    /// <summary>\n    /// 使用正则表达式找到 template 中独立的 T，然后替换为 T1、T2、T3……\n    /// </summary>\n    /// <param name=\"genericIndex\">泛型序号，从 1 开始。</param>\n    /// <param name=\"template\">泛型格式化模板，默认为 T；也可以用别的，如 typeof(T)。</param>\n    private string GenrateT(int genericIndex, string template)\n    {\n        var regex = new Regex(@\"\\bT\\b\");\n        var match = regex.Match(template);\n        if (match.Success)\n        {\n            var index = match.Index;\n            var length = match.Length;\n            var prefix = template.Substring(0, index);\n            var suffix = template.Substring(index + length);\n            return prefix + $\"T{genericIndex}\" + suffix;\n        }\n        else\n        {\n            return template;\n        }\n    }\n}\n"
  },
  {
    "path": "analyzers/dotnetCampus.Ipc.SourceGenerators/Properties/GlobalUsings.cs",
    "content": "﻿global using System;\nglobal using System.Collections.Generic;\nglobal using System.Collections.Immutable;\nglobal using System.Composition;\nglobal using System.Diagnostics;\nglobal using System.Diagnostics.CodeAnalysis;\nglobal using System.Globalization;\nglobal using System.Linq;\nglobal using System.Text;\nglobal using System.Threading;\nglobal using System.Threading.Tasks;\n\nglobal using Microsoft.CodeAnalysis;\nglobal using Microsoft.CodeAnalysis.CSharp;\nglobal using Microsoft.CodeAnalysis.CSharp.Syntax;\nglobal using Microsoft.CodeAnalysis.Diagnostics;\n"
  },
  {
    "path": "analyzers/dotnetCampus.Ipc.SourceGenerators/dotnetCampus.Ipc.SourceGenerators.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netstandard2.0</TargetFramework>\n    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n    <Nullable>enable</Nullable>\n    <RootNamespace>dotnetCampus.Ipc</RootNamespace>\n    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"dotnetCampus.LatestCSharpFeatures\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.Analyzers\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.Workspaces\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Remove=\"SourceProject\\**\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "build/Version.props",
    "content": "<Project>\n  <PropertyGroup>\n    <Version>1.0.0</Version>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "demo/IpcDirectRoutedAotDemo/DemoRequest.cs",
    "content": "﻿namespace IpcDirectRoutedAotDemo;\n\npublic record DemoRequest\n{\n    public required string Value { get; init; }\n}\n"
  },
  {
    "path": "demo/IpcDirectRoutedAotDemo/DemoResponse.cs",
    "content": "﻿namespace IpcDirectRoutedAotDemo;\n\npublic record DemoResponse\n{\n    public required string Result { get; init; }\n}\n"
  },
  {
    "path": "demo/IpcDirectRoutedAotDemo/IpcDirectRoutedAotDemo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net9.0</TargetFramework>\n    <Nullable>enable</Nullable>\n\n    <IsPackable>false</IsPackable>\n    <PublishAot>true</PublishAot>\n    <IlcGenerateMstatFile>true</IlcGenerateMstatFile>\n    <IlcGenerateDgmlFile>true</IlcGenerateDgmlFile>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/IpcDirectRoutedAotDemo/NotifyInfo.cs",
    "content": "﻿namespace IpcDirectRoutedAotDemo;\n\npublic record NotifyInfo\n{\n    public required string Value { get; init; }\n}\n"
  },
  {
    "path": "demo/IpcDirectRoutedAotDemo/Program.cs",
    "content": "﻿// See https://aka.ms/new-console-template for more information\n\nusing System.Diagnostics;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\nusing dotnetCampus.Ipc.Pipes;\nusing IpcDirectRoutedAotDemo;\n\nvar notifyPath = \"NotifyFoo\";\nvar notifyPath2 = \"NotifyFoo2\";\nvar requestPath = \"RequestFoo\";\nvar requestPath2 = \"RequestFoo2\";\n\nif (args.Length == 0)\n{\n    // 首个启动的，当成服务端\n    string pipeName = Guid.NewGuid().ToString();\n    var ipcProvider = new IpcProvider(pipeName, new IpcConfiguration()\n    {\n\n    }.UseSystemTextJsonIpcObjectSerializer(SourceGenerationContext.Default));\n    var ipcDirectRoutedProvider = new JsonIpcDirectRoutedProvider(ipcProvider);\n    ipcDirectRoutedProvider.AddNotifyHandler(notifyPath, () =>\n    {\n        Console.WriteLine($\"[{Environment.ProcessId}] Receive Notify. 服务端收到通知\");\n    });\n\n    ipcDirectRoutedProvider.AddNotifyHandler(notifyPath2, (NotifyInfo notifyInfo) =>\n    {\n        Console.WriteLine($\"[{Environment.ProcessId}] Receive Notify. 服务端收到通知 NotifyInfo={notifyInfo}\");\n    });\n\n    ipcDirectRoutedProvider.AddRequestHandler(requestPath, () => \"ResponseFoo\");\n\n    ipcDirectRoutedProvider.AddRequestHandler(requestPath2, (DemoRequest request) =>\n    {\n        Console.WriteLine($\"[{Environment.ProcessId}] Receive Request. 服务端收到请求 DemoRequest={request}\");\n        return new DemoResponse() { Result = \"返回内容\" };\n    });\n\n    ipcDirectRoutedProvider.StartServer();\n    Console.WriteLine($\"[{Environment.ProcessId}] 服务启动\");\n\n    // 启动另一个进程\n    Process.Start(Environment.ProcessPath!, pipeName);\n}\nelse\n{\n    var peerName = args[0];\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端进程启动\");\n    var jsonIpcDirectRoutedProvider = new JsonIpcDirectRoutedProvider(ipcConfiguration: new IpcConfiguration()\n        .UseSystemTextJsonIpcObjectSerializer(SourceGenerationContext.Default));\n    JsonIpcDirectRoutedClientProxy jsonIpcDirectRoutedClientProxy = await jsonIpcDirectRoutedProvider.GetAndConnectClientAsync(peerName);\n\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端发送通知\");\n    await jsonIpcDirectRoutedClientProxy.NotifyAsync(notifyPath);\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端发送通知完成\");\n\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端发送通知2\");\n    await jsonIpcDirectRoutedClientProxy.NotifyAsync(notifyPath2, new NotifyInfo()\n    {\n        Value = \"通知2\"\n    });\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端发送通知2完成\");\n\n\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端发送请求\");\n    var responseValue = await jsonIpcDirectRoutedClientProxy.GetResponseAsync<string>(requestPath);\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端完成请求，收到响应内容 {responseValue}\");\n\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端发送请求2\");\n    var responseValue2 = await jsonIpcDirectRoutedClientProxy.GetResponseAsync<DemoResponse>(requestPath2, new DemoRequest()\n    {\n        Value = \"客户端请求内容\"\n    });\n    Console.WriteLine($\"[{Environment.ProcessId}] 客户端完成请求2，收到响应内容 {responseValue2}\");\n}\n\nConsole.WriteLine($\"[{Environment.ProcessId}] 等待退出\");\nConsole.Read();\nConsole.WriteLine($\"[{Environment.ProcessId}] 进程准备退出\");\n"
  },
  {
    "path": "demo/IpcDirectRoutedAotDemo/SourceGenerationContext.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\n\nnamespace IpcDirectRoutedAotDemo;\n\n[JsonSerializable(typeof(NotifyInfo))]\n[JsonSerializable(typeof(DemoRequest))]\n[JsonSerializable(typeof(DemoResponse))]\ninternal partial class SourceGenerationContext : JsonSerializerContext\n{\n}\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectClientDemo/IFoo.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace IpcRemotingObjectServerDemo; // Must the same namespace\n\n/// <summary>\n/// 可跨进程调用的接口演示。\n/// </summary>\n[IpcPublic]\npublic interface IFoo\n{\n    /// <summary>\n    /// 属性演示。支持 get/set 属性、get 只读属性，支持跨进程报告异常。\n    /// </summary>\n    string Name { get; set; }\n\n    /// <summary>\n    /// 方法演示。支持参数、返回值，支持跨进程报告异常。\n    /// </summary>\n    int Add(int a, int b);\n\n    /// <summary>\n    /// 异步方法（更推荐）演示。支持参数、返回值，支持跨进程报告异常。\n    /// </summary>\n    Task<string> AddAsync(string a, int b);\n}\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectClientDemo/IpcRemotingObjectClientDemo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\..\\src\\dotnetCampus.Ipc.Analyzers\\dotnetCampus.Ipc.Analyzers.csproj\" OutputItemType=\"Analyzer\" ReferenceOutputAssembly=\"false\" />\n    <ProjectReference Include=\"..\\..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectClientDemo/Program.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Pipes;\n\nusing IpcRemotingObjectServerDemo;\n\nvar ipcProvider = new IpcProvider(\"IpcRemotingObjectClientDemo\");\n\nipcProvider.StartServer();\n\nvar peer = await ipcProvider.GetAndConnectToPeerAsync(\"IpcRemotingObjectServerDemo\");\n\nvar foo = ipcProvider.CreateIpcProxy<IFoo>(peer);\n\nConsole.WriteLine(await foo.AddAsync(\"a\", 1));\nConsole.WriteLine(foo.Add(1, 2));\n\nConsole.Read();\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectServerDemo/Foo.cs",
    "content": "﻿namespace IpcRemotingObjectServerDemo;\n\nclass Foo : IFoo\n{\n    public string Name { get; set; } = \"Foo\";\n\n    public int Add(int a, int b)\n    {\n        Console.WriteLine($\"a({a})+b({b})={a + b}\");\n        return a + b;\n    }\n\n    public async Task<string> AddAsync(string a, int b)\n    {\n        return await Task.Run(() =>\n        {\n            Console.WriteLine($\"a({a})+b({b})={a + b}\");\n            return a + b;\n        });\n    }\n}\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectServerDemo/IFoo.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace IpcRemotingObjectServerDemo;\n\n/// <summary>\n/// 可跨进程调用的接口演示。\n/// </summary>\n[IpcPublic(IgnoresIpcException = true, Timeout = 1000)]\npublic interface IFoo\n{\n    /// <summary>\n    /// 属性演示。支持 get/set 属性、get 只读属性，支持跨进程报告异常。\n    /// </summary>\n    string Name { get; set; }\n\n    /// <summary>\n    /// 方法演示。支持参数、返回值，支持跨进程报告异常。\n    /// </summary>\n    int Add(int a, int b);\n\n    /// <summary>\n    /// 异步方法（更推荐）演示。支持参数、返回值，支持跨进程报告异常。\n    /// </summary>\n    Task<string> AddAsync(string a, int b);\n}\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectServerDemo/IpcRemotingObjectServerDemo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net6.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\..\\src\\dotnetCampus.Ipc.Analyzers\\dotnetCampus.Ipc.Analyzers.csproj\" OutputItemType=\"Analyzer\" ReferenceOutputAssembly=\"false\" />\n    <ProjectReference Include=\"..\\..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/IpcRemotingObjectDemo/IpcRemotingObjectServerDemo/Program.cs",
    "content": "﻿using System;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Pipes;\nusing IpcRemotingObjectServerDemo;\n\nvar ipcProvider = new IpcProvider(\"IpcRemotingObjectServerDemo\");\n\nipcProvider.CreateIpcJoint<IFoo>(new Foo());\nipcProvider.PeerConnected += (sender, connectedArgs) =>\n{\n    Console.WriteLine($\"PeerConnected. {connectedArgs.Peer.PeerName}\");\n};\nipcProvider.StartServer();\n\nConsole.Read();\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcClientDemo/App.xaml",
    "content": "﻿<Application x:Class=\"PipeMvcClientDemo.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:PipeMvcClientDemo\"\n             StartupUri=\"MainWindow.xaml\">\n    <Application.Resources>\n         \n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcClientDemo/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace PipeMvcClientDemo;\n/// <summary>\n/// Interaction logic for App.xaml\n/// </summary>\npublic partial class App : Application\n{\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcClientDemo/AssemblyInfo.cs",
    "content": "﻿using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcClientDemo/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"PipeMvcClientDemo.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:PipeMvcClientDemo\"\n        mc:Ignorable=\"d\"\n        Title=\"PipeMvcClientDemo\" Height=\"450\" Width=\"800\">\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\"/>\n            <RowDefinition/>\n        </Grid.RowDefinitions>\n        <StackPanel Margin=\"10,10,10,10\" Orientation=\"Horizontal\">\n            <Button x:Name=\"GetFooButton\" Margin=\"10,10,10,10\" Click=\"GetFooButton_Click\">Get</Button>\n            <Button x:Name=\"GetFooWithArgumentButton\" Margin=\"10,10,10,10\" Click=\"GetFooWithArgumentButton_Click\">Get With Argument</Button>\n            <Button x:Name=\"PostFooButton\" Margin=\"10,10,10,10\" Click=\"PostFooButton_Click\">Post</Button>\n            <Button x:Name=\"PostFooWithArgumentButton\" Margin=\"10,10,10,10\" Click=\"PostFooWithArgumentButton_Click\">Post With Argument</Button>\n            <Button x:Name=\"MultiThreadButton\" Margin=\"10,10,10,10\" Click=\"MultiThreadButton_OnClick\">多线程压测</Button>\n        </StackPanel>\n        <Grid Grid.Row=\"1\" ClipToBounds=\"True\">\n            <TextBlock x:Name=\"TraceTextBlock\" Margin=\"10,10,10,10\" VerticalAlignment=\"Bottom\"/>\n        </Grid>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcClientDemo/MainWindow.xaml.cs",
    "content": "﻿using System;\nusing System.Net.Http;\nusing System.Text;\nusing System.Text.Json;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing dotnetCampus.Ipc.PipeMvcClient;\nusing PipeMvcServerDemo;\n\nnamespace PipeMvcClientDemo;\n\n/// <summary>\n/// Interaction logic for MainWindow.xaml\n/// </summary>\npublic partial class MainWindow : Window\n{\n    public MainWindow()\n    {\n        InitializeComponent();\n\n        Loaded += MainWindow_Loaded;\n    }\n\n    private async void MainWindow_Loaded(object sender, RoutedEventArgs e)\n    {\n        Log($\"Start create PipeMvcClient.\");\n\n        var ipcPipeMvcClient = await IpcPipeMvcClientProvider.CreateIpcMvcClientAsync(\"PipeMvcServerDemo\");\n        _ipcPipeMvcClient = ipcPipeMvcClient;\n\n        Log($\"Finish create PipeMvcClient.\");\n    }\n\n    private HttpClient? _ipcPipeMvcClient;\n\n    private async void GetFooButton_Click(object sender, RoutedEventArgs e)\n    {\n        if (_ipcPipeMvcClient is null)\n        {\n            return;\n        }\n\n        Log($\"[Request][Get] IpcPipeMvcServer://api/Foo\");\n        var response = await _ipcPipeMvcClient.GetStringAsync(\"api/Foo\");\n        Log($\"[Response][Get] IpcPipeMvcServer://api/Foo {response}\");\n    }\n\n    private void Log(string message)\n    {\n        Dispatcher.InvokeAsync(() =>\n        {\n            TraceTextBlock.Text += message + \"\\r\\n\";\n\n            if (TraceTextBlock.Text.Length > 10000)\n            {\n                TraceTextBlock.Text = TraceTextBlock.Text[5000..];\n            }\n        });\n    }\n\n    private async void GetFooWithArgumentButton_Click(object sender, RoutedEventArgs e)\n    {\n        if (_ipcPipeMvcClient is null)\n        {\n            return;\n        }\n\n        Log($\"[Request][Get] IpcPipeMvcServer://api/Foo/Add\");\n        var response = await _ipcPipeMvcClient.GetStringAsync(\"api/Foo/Add?a=1&b=1\");\n        Log($\"[Response][Get] IpcPipeMvcServer://api/Foo/Add {response}\");\n    }\n\n    private async void PostFooButton_Click(object sender, RoutedEventArgs e)\n    {\n        if (_ipcPipeMvcClient is null)\n        {\n            return;\n        }\n\n        Log($\"[Request][Post] IpcPipeMvcServer://api/Foo\");\n        var response = await _ipcPipeMvcClient.PostAsync(\"api/Foo\", new StringContent(\"\"));\n        var m = await response.Content.ReadAsStringAsync();\n        Log($\"[Response][Post] IpcPipeMvcServer://api/Foo {response.StatusCode} {m}\");\n    }\n\n    private async void PostFooWithArgumentButton_Click(object sender, RoutedEventArgs e)\n    {\n        if (_ipcPipeMvcClient is null)\n        {\n            return;\n        }\n\n        Log($\"[Request][Post] IpcPipeMvcServer://api/Foo\");\n\n        var json = JsonSerializer.Serialize(new FooContent { Foo1 = \"Foo PostFooWithArgumentButton\", Foo2 = null, });\n        StringContent content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n        var response = await _ipcPipeMvcClient.PostAsync(\"api/Foo/PostFoo\", content);\n        var m = await response.Content.ReadAsStringAsync();\n        Log($\"[Response][Post] IpcPipeMvcServer://api/Foo/PostFoo {response.StatusCode} {m}\");\n    }\n\n    private void MultiThreadButton_OnClick(object sender, RoutedEventArgs e)\n    {\n        if (_ipcPipeMvcClient is null)\n        {\n            return;\n        }\n\n        var count = 10;\n        for (int i = 0; i < count; i++)\n        {\n            var id = i;\n            Task.Run(async () =>\n            {\n                Log($\"[{id}][Request][Post] IpcPipeMvcServer://api/Foo\");\n                var response = await _ipcPipeMvcClient.PostAsync(\"api/Foo\", new StringContent(\"\"));\n                var m = await response.Content.ReadAsStringAsync();\n                Log($\"[{id}][Response][Post] IpcPipeMvcServer://api/Foo {response.StatusCode} {m}\");\n\n                var a = Random.Shared.Next();\n                var b = Random.Shared.Next();\n\n                Log($\"[{id}][Request][Get] IpcPipeMvcServer://api/Foo/Add?a={a}&b={b}\");\n                m = await _ipcPipeMvcClient.GetStringAsync($\"api/Foo/Add?a={a}&b={b}\");\n                Log($\"[{id}][Response][Get] IpcPipeMvcServer://api/Foo/Add {m} {a}+{b}={a + b}\");\n\n                Log($\"[{id}][Request][Post] IpcPipeMvcServer://api/Foo\");\n                response = await _ipcPipeMvcClient.PostAsync(\"api/Foo\", new StringContent(BuildRandomText()));\n                m = await response.Content.ReadAsStringAsync();\n                Log($\"[Response][Post] IpcPipeMvcServer://api/Foo {response.StatusCode} {m}\");\n\n                Log($\"[{id}][Request][Post] IpcPipeMvcServer://api/Foo\");\n\n                var json = JsonSerializer.Serialize(new FooContent\n                {\n                    Foo1 = Random.Shared.Next(2) == 1 ? BuildRandomText() : null,\n                    Foo2 = Random.Shared.Next(2) == 1 ? BuildRandomText() : null,\n                });\n                StringContent content = new StringContent(json, Encoding.UTF8, \"application/json\");\n\n                response = await _ipcPipeMvcClient.PostAsync(\"api/Foo/PostFoo\", content);\n                m = await response.Content.ReadAsStringAsync();\n                Log($\"[{id}][Response][Post] IpcPipeMvcServer://api/Foo/PostFoo {response.StatusCode} {m}\");\n            });\n        }\n    }\n\n    private static string BuildRandomText()\n    {\n        var count = 10;\n        var text = new StringBuilder();\n        for (int i = 0; i < count; i++)\n        {\n            text.Append((char) Random.Shared.Next('a', 'z' + 1));\n        }\n\n        return text.ToString();\n    }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcClientDemo/PipeMvcClientDemo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0-windows</TargetFramework>\n    <Nullable>enable</Nullable>\n    <UseWPF>true</UseWPF>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Compile Include=\"..\\PipeMvcServerDemo\\FooContent.cs\" Link=\"FooContent.cs\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\..\\src\\PipeMvc\\dotnetCampus.Ipc.PipeMvcClient\\dotnetCampus.Ipc.PipeMvcClient.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/FooContent.cs",
    "content": "﻿namespace PipeMvcServerDemo;\n\npublic class FooContent\n{\n    public string? Foo1 { set; get; }\n    public string? Foo2 { set; get; }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/FooController.cs",
    "content": "﻿using Microsoft.AspNetCore.Mvc;\nusing Microsoft.Extensions.Logging;\n\nnamespace PipeMvcServerDemo;\n\n[Route(\"api/[controller]\")]\n[ApiController]\npublic class FooController : ControllerBase\n{\n    public FooController(ILogger<FooController> logger)\n    {\n        Logger = logger;\n    }\n\n    public ILogger<FooController> Logger { get; }\n\n    [HttpGet]\n    public IActionResult Get()\n    {\n        Logger.LogInformation(\"FooController_Get\");\n        return Ok(DateTime.Now.ToString());\n    }\n\n    [HttpGet(\"Add\")]\n    public IActionResult Add(int a, int b)\n    {\n        Logger.LogInformation($\"FooController_Add a={a};b={b}\");\n        return Ok(a + b);\n    }\n\n    [HttpPost]\n    public IActionResult Post()\n    {\n        Logger.LogInformation(\"FooController_Post\");\n        return Ok($\"POST {DateTime.Now}\");\n    }\n\n    [HttpPost(\"PostFoo\")]\n    public IActionResult PostFooContent(FooContent foo)\n    {\n        Logger.LogInformation($\"FooController_PostFooContent Foo1={foo.Foo1};Foo2={foo.Foo2 ?? \"<NULL>\"}\");\n        return Ok($\"PostFooContent Foo1={foo.Foo1};Foo2={foo.Foo2 ?? \"<NULL>\"}\");\n    }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"PipeMvcServerDemo.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:PipeMvcServerDemo\"\n        mc:Ignorable=\"d\"\n        Title=\"PipeMvcServerDemo\" Height=\"450\" Width=\"800\">\n    <Grid>\n        <TextBlock x:Name=\"TraceTextBlock\" Margin=\"10,10,10,10\" VerticalAlignment=\"Bottom\" TextWrapping=\"Wrap\"></TextBlock>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/MainWindow.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Shapes;\n\nnamespace PipeMvcServerDemo;\n/// <summary>\n/// MainWindow.xaml 的交互逻辑\n/// </summary>\npublic partial class MainWindow : Window\n{\n    public MainWindow()\n    {\n        InitializeComponent();\n    }\n\n    public void Log(string message)\n    {\n        Dispatcher.InvokeAsync(() =>\n        {\n            TraceTextBlock.Text += message + \"\\r\\n\";\n\n            if (TraceTextBlock.Text.Length > 10000)\n            {\n                TraceTextBlock.Text = TraceTextBlock.Text[5000..];\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/PipeMvcServerDemo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net6.0-windows</TargetFramework>\n    <OutputType>WinExe</OutputType>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <UseWPF>true</UseWPF>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\..\\src\\PipeMvc\\dotnetCampus.Ipc.PipeMvcServer\\dotnetCampus.Ipc.PipeMvcServer.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/Program.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Threading;\n\nusing dotnetCampus.Ipc.PipeMvcServer;\n\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Logging;\n\nnamespace PipeMvcServerDemo;\n\npublic class Program\n{\n    [STAThread]\n    static void Main(string[] args)\n    {\n        Task.Run(() => RunMvc(args));\n        RunWpf();\n    }\n\n    private static void RunWpf()\n    {\n        Application application = new Application();\n        application.Startup += (s, e) =>\n        {\n            MainWindow mainWindow = new MainWindow();\n            mainWindow.Show();\n        };\n        application.Run();\n    }\n\n    private static void RunMvc(string[] args)\n    {\n        var builder = WebApplication.CreateBuilder(args);\n        builder.WebHost.UsePipeIpcServer(\"PipeMvcServerDemo\");\n        builder.Services.AddControllers();\n        builder.Services.AddLogging(loggingBuilder =>\n        {\n            loggingBuilder.AddProvider(new DemoLogProvider());\n        });\n        var app = builder.Build();\n        app.MapControllers();\n        app.Run();\n    }\n\n    class DemoLogProvider : ILoggerProvider\n    {\n        public ILogger CreateLogger(string categoryName)\n        {\n            return new DemoLogger();\n        }\n\n        public void Dispose()\n        {\n            throw new NotImplementedException();\n        }\n\n        class DemoLogger : ILogger\n        {\n            public IDisposable BeginScope<TState>(TState state)\n            {\n                return new Empty();\n            }\n\n            class Empty : IDisposable\n            {\n                /// <inheritdoc />\n                public void Dispose()\n                {\n                }\n            }\n\n            public bool IsEnabled(LogLevel logLevel)\n            {\n                return true;\n            }\n\n            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n            {\n                var message =\n                        $\"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}][{logLevel}][EventId={eventId.Id}:{eventId.Name}] {formatter(state, exception)}\";\n                Application.Current?.Dispatcher.InvokeAsync(() =>\n                {\n                    var mainWindow = (MainWindow) Application.Current.MainWindow;\n                    mainWindow.Log(message);\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/Properties/launchSettings.json",
    "content": "﻿{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:8638\",\n      \"sslPort\": 0\n    }\n  },\n  \"profiles\": {\n    \"PipeMvcServerDemo\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"applicationUrl\": \"http://localhost:5035\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/appsettings.Development.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  }\n}\n"
  },
  {
    "path": "demo/PipeMvc/PipeMvcServerDemo/appsettings.json",
    "content": "{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\",\n      \"Microsoft.AspNetCore\": \"Warning\"\n    }\n  },\n  \"AllowedHosts\": \"*\"\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/.editorconfig",
    "content": "; This file is for unifying the coding style for different editors and IDEs.\n; More information at http://editorconfig.org\n\n# This file is the top-most EditorConfig file\nroot = true\n\n##########################################\n# Common Settings\n##########################################\n\n[*]\nindent_style = space\nend_of_line = crlf\ntrim_trailing_whitespace = true\ninsert_final_newline = true\ncharset = utf-8\n\n##########################################\n# File Extension Settings\n##########################################\n\n[*.{yml,yaml}]\nindent_size = 2\n\n[.vsconfig]\nindent_size = 2\nend_of_line = lf\n\n[*.sln]\nindent_style = tab\nindent_size = 2\n\n[*.{csproj,proj,projitems,shproj}]\nindent_size = 2\n\n[*.{json,slnf}]\nindent_size = 2\nend_of_line = lf\n\n[*.{props,targets}]\nindent_size = 2\n\n[*.xaml]\nindent_size = 2\ncharset = utf-8-bom\n\n[*.xml]\nindent_size = 2\nend_of_line = lf\n\n[*.plist]\nindent_size = 2\nindent_style = tab\nend_of_line = lf\n\n[*.manifest]\nindent_size = 2\n\n[*.appxmanifest]\nindent_size = 2\n\n[*.{json,css,webmanifest}]\nindent_size = 2\nend_of_line = lf\n\n[web.config]\nindent_size = 2\nend_of_line = lf\n\n[*.sh]\nindent_size = 2\nend_of_line = lf\n\n[*.cs]\n# EOL should be normalized by Git. See https://github.com/dotnet/format/issues/1099\nend_of_line = unset\n\n# See https://github.com/dotnet/roslyn/issues/20356#issuecomment-310143926\ntrim_trailing_whitespace = false\n\ntab_width = 4\nindent_size = 4\n\n# Sort using and Import directives with System.* appearing first\ndotnet_sort_system_directives_first = true\n\n# Avoid \"this.\" and \"Me.\" if not necessary\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_event = false:suggestion\n\n#### Naming styles ####\n\n# Naming rules\n\ndotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion\ndotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface\ndotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i\n\ndotnet_naming_rule.types_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.types_should_be_pascal_case.symbols = types\ndotnet_naming_rule.types_should_be_pascal_case.style = pascal_case\n\ndotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members\ndotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case\n\n# Symbol specifications\n\ndotnet_naming_symbols.interface.applicable_kinds = interface\ndotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.interface.required_modifiers =\n\ndotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum\ndotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.types.required_modifiers =\n\ndotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method\ndotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected\ndotnet_naming_symbols.non_field_members.required_modifiers =\n\n# Naming styles\n\ndotnet_naming_style.begins_with_i.required_prefix = I\ndotnet_naming_style.begins_with_i.required_suffix =\ndotnet_naming_style.begins_with_i.word_separator =\ndotnet_naming_style.begins_with_i.capitalization = pascal_case\n\ndotnet_naming_style.pascal_case.required_prefix =\ndotnet_naming_style.pascal_case.required_suffix =\ndotnet_naming_style.pascal_case.word_separator =\ndotnet_naming_style.pascal_case.capitalization = pascal_case\n\ndotnet_naming_style.pascal_case.required_prefix =\ndotnet_naming_style.pascal_case.required_suffix =\ndotnet_naming_style.pascal_case.word_separator =\ndotnet_naming_style.pascal_case.capitalization = pascal_case\ndotnet_style_operator_placement_when_wrapping = beginning_of_line\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion\ndotnet_style_prefer_auto_properties = true:silent\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_prefer_simplified_boolean_expressions = true:suggestion\ndotnet_style_prefer_conditional_expression_over_assignment = true:silent\ndotnet_style_prefer_conditional_expression_over_return = true:silent\ndotnet_style_explicit_tuple_names = true:suggestion\ndotnet_style_prefer_inferred_tuple_names = true:suggestion\n\ncsharp_indent_labels = one_less_than_current\ncsharp_using_directive_placement = outside_namespace:silent\ncsharp_prefer_simple_using_statement = true:suggestion\ncsharp_prefer_braces = true:silent\ncsharp_style_namespace_declarations = block_scoped:silent\ncsharp_style_prefer_method_group_conversion = true:silent\ncsharp_style_prefer_top_level_statements = true:silent\ncsharp_style_prefer_primary_constructors = true:suggestion\ncsharp_style_expression_bodied_methods = false:silent\ncsharp_style_expression_bodied_constructors = false:silent\ncsharp_style_expression_bodied_operators = false:silent\ncsharp_style_expression_bodied_properties = true:silent\ncsharp_style_expression_bodied_indexers = true:silent\ncsharp_style_expression_bodied_accessors = true:silent\ncsharp_style_expression_bodied_lambdas = true:silent\ncsharp_style_expression_bodied_local_functions = false:silent\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/.gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Mono auto generated files\nmono_crash.*\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Ww][Ii][Nn]32/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n[Ll]ogs/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUnit\n*.VisualState.xml\nTestResult.xml\nnunit-*.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# ASP.NET Scaffolding\nScaffoldingReadMe.txt\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.tlog\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Coverlet is a free, cross platform Code Coverage Tool\ncoverage*.json\ncoverage*.xml\ncoverage*.info\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# NuGet Symbol Packages\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n*.appxbundle\n*.appxupload\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- [Bb]ackup.rdl\n*- [Bb]ackup ([0-9]).rdl\n*- [Bb]ackup ([0-9][0-9]).rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio 6 auto-generated project file (contains which files were open etc.)\n*.vbp\n\n# Visual Studio 6 workspace and project file (working project files containing files to include in project)\n*.dsw\n*.dsp\n\n# Visual Studio 6 technical files\n*.ncb\n*.aps\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# Visual Studio History (VSHistory) files\n.vshistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# Backup folder for Package Reference Convert tool in Visual Studio 2017\nMigrationBackup/\n\n# Ionide (cross platform F# VS Code tools) working folder\n.ionide/\n\n# Fody - auto-generated XML schema\nFodyWeavers.xsd\n\n# VS Code files for those working on multiple tools\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n*.code-workspace\n\n# Local History for Visual Studio Code\n.history/\n\n# Windows Installer files from build outputs\n*.cab\n*.msi\n*.msix\n*.msm\n*.msp\n\n# JetBrains Rider\n*.sln.iml\n\n# Single Target Config\nsolution-config.props\n# Windows Publish Profiles\n!**/*.Windows/Properties/PublishProfiles/*.pubxml"
  },
  {
    "path": "demo/UnoDemo/IpcUno/.vsconfig",
    "content": "{\n  \"version\": \"1.0\",\n  \"components\": [\n    \"Microsoft.VisualStudio.Component.CoreEditor\",\n    \"Microsoft.VisualStudio.Workload.CoreEditor\",\n    \"Microsoft.NetCore.Component.SDK\",\n    \"Microsoft.NetCore.Component.DevelopmentTools\",\n    \"Microsoft.Net.ComponentGroup.DevelopmentPrerequisites\",\n    \"Microsoft.VisualStudio.Component.TextTemplating\",\n    \"Microsoft.VisualStudio.Component.Windows10SDK.19041\",\n    \"Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging\",\n    \"Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites\",\n    \"Microsoft.VisualStudio.Component.Debugger.JustInTime\",\n    \"Microsoft.VisualStudio.Workload.ManagedDesktop\",\n    \"Microsoft.Component.NetFX.Native\",\n    \"Microsoft.VisualStudio.Component.Graphics\",\n    \"Microsoft.VisualStudio.Component.Merq\",\n    \"Microsoft.VisualStudio.Workload.NetCrossPlat\",\n    \"Microsoft.VisualStudio.Workload.NetCoreTools\"\n  ]\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/Directory.Build.props",
    "content": "<Project>\n\n  <!--\n    If working on a single target framework, copy solution-config.props.sample to solution-config.props\n    and uncomment the appropriate lines in solution-config.props to build for the desired platforms only.\n\n    https://platform.uno/docs/articles/guides/solution-building-single-targetframework.html\n  -->\n  <Import Project=\"solution-config.props\" Condition=\"exists('solution-config.props')\" />\n\n\n  <PropertyGroup>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n\n    <DebugType>portable</DebugType>\n    <DebugSymbols>True</DebugSymbols>\n\n    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>\n\n    <!--\n      Adding NoWarn to remove build warnings\n      NU1507: Warning when there are multiple package sources when using CPM with no source mapping\n      NETSDK1201: Warning that specifying RID won't create self containing app\n      NETSDK1023:\tMicrosoft.Maui.Graphics reference required to avoid build error causes warning because it's already implicitly referenced by Maui SDK\n      PRI257: Ignore default language (en) not being one of the included resources (eg en-us, en-uk)\n      CA1416: 平台调用，本文就是平台调用，可以忽略此问题\n    -->\n    <NoWarn>$(NoWarn);NU1507;NETSDK1201;NETSDK1023;PRI257;CA1416;IDE0090</NoWarn>\n\n    <DefaultLanguage>en</DefaultLanguage>\n\n    <IsAndroid>false</IsAndroid>\n    <IsIOS>false</IsIOS>\n    <IsMac>false</IsMac>\n    <IsMacCatalyst>false</IsMacCatalyst>\n    <IsWinAppSdk>false</IsWinAppSdk>\n\n    <MauiVersion Condition=\" '$(MauiVersion)' == '' \">8.0.0-rc.2.9373</MauiVersion>\n    <AndroidMaterialVersion  Condition=\" '$(AndroidMaterialVersion)' == '' \">1.10.0.1</AndroidMaterialVersion>\n    <AndroidXNavigationVersion  Condition=\" '$(AndroidXNavigationVersion)' == '' \">2.6.0.1</AndroidXNavigationVersion>\n    <AndroidXCollectionVersion  Condition=\" '$(AndroidXCollectionVersion)' == '' \">1.3.0.1</AndroidXCollectionVersion>\n\n    <!-- Required for Hot Reload (See https://github.com/unoplatform/uno.templates/issues/376) -->\n    <GenerateAssemblyInfo Condition=\"'$(Configuration)'=='Debug'\">false</GenerateAssemblyInfo>\n  </PropertyGroup>\n\n  <Choose>\n    <When Condition=\"$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'\">\n      <PropertyGroup>\n        <IsAndroid>true</IsAndroid>\n        <SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>\n      </PropertyGroup>\n    </When>\n    <When Condition=\"$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'\">\n      <PropertyGroup>\n        <IsIOS>true</IsIOS>\n        <SupportedOSPlatformVersion>14.2</SupportedOSPlatformVersion>\n      </PropertyGroup>\n    </When>\n    <When Condition=\"$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'macos'\">\n      <PropertyGroup>\n        <IsMac>true</IsMac>\n        <SupportedOSPlatformVersion>10.14</SupportedOSPlatformVersion>\n      </PropertyGroup>\n    </When>\n    <When Condition=\"$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'\">\n      <PropertyGroup>\n        <IsMacCatalyst>true</IsMacCatalyst>\n        <SupportedOSPlatformVersion>14.0</SupportedOSPlatformVersion>\n      </PropertyGroup>\n    </When>\n    <When Condition=\"$(TargetFramework.Contains('windows10'))\">\n      <PropertyGroup>\n        <IsWinAppSdk>true</IsWinAppSdk>\n        <SupportedOSPlatformVersion>10.0.18362.0</SupportedOSPlatformVersion>\n        <TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>\n        <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>\n        <EnableCoreMrtTooling Condition=\" '$(BuildingInsideVisualStudio)' != 'true' \">false</EnableCoreMrtTooling>\n      </PropertyGroup>\n    </When>\n  </Choose>\n\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/Directory.Build.targets",
    "content": "﻿<Project>\n  <ItemGroup>\n    <!-- Removes native usings to avoid Ambiguous reference -->\n    <Using Remove=\"@(Using->HasMetadata('Platform'))\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/Directory.Packages.props",
    "content": "﻿<Project ToolsVersion=\"15.0\">\n  <ItemGroup>\n    <PackageVersion Include=\"CommunityToolkit.Mvvm\" Version=\"8.2.1\" />\n    <PackageVersion Include=\"dotnetCampus.Ipc\" Version=\"2.0.0-alpha409\" />\n    <PackageVersion Include=\"Microsoft.Maui.Controls\" Version=\"$(MauiVersion)\" />\n    <PackageVersion Include=\"Microsoft.Maui.Controls.Compatibility\" Version=\"$(MauiVersion)\" />\n    <PackageVersion Include=\"Microsoft.Extensions.Logging.Console\" Version=\"8.0.0-rc.2.23479.6\" />\n    <PackageVersion Include=\"coverlet.collector\" Version=\"6.0.0\" />\n    <PackageVersion Include=\"FluentAssertions\" Version=\"6.12.0\" />\n    <PackageVersion Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.7.2\" />\n    <PackageVersion Include=\"NUnit\" Version=\"3.13.3\" />\n    <PackageVersion Include=\"NUnit3TestAdapter\" Version=\"4.5.0\" />\n    <PackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.3\" />\n    <PackageVersion Include=\"SkiaSharp.Skottie\" Version=\"2.88.6\" />\n    <PackageVersion Include=\"SkiaSharp.Views.Uno.WinUI\" Version=\"2.88.6\" />\n    <PackageVersion Include=\"Uno.Core.Extensions.Logging.Singleton\" Version=\"4.0.1\" />\n    <PackageVersion Include=\"Uno.Extensions.Core.WinUI\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Configuration\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Hosting\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Hosting.WinUI\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Logging.WinUI\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Maui.WinUI\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Navigation\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Navigation.WinUI\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Extensions.Navigation.Toolkit.WinUI\" Version=\"3.0.11\" />\n    <PackageVersion Include=\"Uno.Material.WinUI\" Version=\"4.0.6\" />\n    <PackageVersion Include=\"Uno.Toolkit.WinUI\" Version=\"5.0.17\" />\n    <PackageVersion Include=\"Uno.Toolkit.WinUI.Material\" Version=\"5.0.17\" />\n    <PackageVersion Include=\"Uno.Resizetizer\" Version=\"1.2.0\" />\n    <PackageVersion Include=\"Uno.UI.Adapter.Microsoft.Extensions.Logging\" Version=\"5.0.41\" />\n    <PackageVersion Include=\"Uno.UniversalImageLoader\" Version=\"1.9.36\" />\n    <PackageVersion Include=\"Uno.WinUI\" Version=\"5.0.41\" />\n    <PackageVersion Include=\"Uno.WinUI.Lottie\" Version=\"5.0.41\" />\n    <PackageVersion Include=\"Uno.WinUI.DevServer\" Version=\"5.0.41\" />\n    <PackageVersion Include=\"Uno.WinUI.Skia.Gtk\" Version=\"5.0.41\" />\n    <PackageVersion Include=\"Uno.WinUI.Skia.Wpf\" Version=\"5.0.41\" />\n    <PackageVersion Include=\"Uno.UITest.Helpers\" Version=\"1.1.0-dev.70\" />\n    <PackageVersion Include=\"Xamarin.UITest\" Version=\"4.3.0\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/App.cs",
    "content": "namespace IpcUno\n{\n    public class App : EmbeddingApplication\n    {\n        public Microsoft.UI.Dispatching.DispatcherQueue Dispatcher { private set; get; } = null!;\n        protected Window? MainWindow { get; private set; }\n        protected IHost? Host { get; private set; }\n\n        protected async override void OnLaunched(LaunchActivatedEventArgs args)\n        {\n            var builder = this.CreateBuilder(args)\n                // Add navigation support for toolkit controls such as TabBar and NavigationView\n                .UseToolkitNavigation()\n#if MAUI_EMBEDDING\n            .UseMauiEmbedding<MauiControls.App>(maui => maui\n                .UseMauiControls())\n#endif\n                .Configure(host => host\n#if DEBUG\n                // Switch to Development environment when running in DEBUG\n                .UseEnvironment(Environments.Development)\n#endif\n                    .UseLogging(configure: (context, logBuilder) =>\n                    {\n                        // Configure log levels for different categories of logging\n                        logBuilder\n                            .SetMinimumLevel(\n                                context.HostingEnvironment.IsDevelopment() ?\n                                    LogLevel.Information :\n                                    LogLevel.Warning)\n\n                            // Default filters for core Uno Platform namespaces\n                            .CoreLogLevel(LogLevel.Warning);\n\n                        // Uno Platform namespace filter groups\n                        // Uncomment individual methods to see more detailed logging\n                        //// Generic Xaml events\n                        //logBuilder.XamlLogLevel(LogLevel.Debug);\n                        //// Layout specific messages\n                        //logBuilder.XamlLayoutLogLevel(LogLevel.Debug);\n                        //// Storage messages\n                        //logBuilder.StorageLogLevel(LogLevel.Debug);\n                        //// Binding related messages\n                        //logBuilder.XamlBindingLogLevel(LogLevel.Debug);\n                        //// Binder memory references tracking\n                        //logBuilder.BinderMemoryReferenceLogLevel(LogLevel.Debug);\n                        //// DevServer and HotReload related\n                        //logBuilder.HotReloadCoreLogLevel(LogLevel.Information);\n                        //// Debug JS interop\n                        //logBuilder.WebAssemblyLogLevel(LogLevel.Debug);\n\n                    }, enableUnoLogging: true)\n                    .UseConfiguration(configure: configBuilder =>\n                        configBuilder\n                            .EmbeddedSource<App>()\n                            .Section<AppConfig>()\n                    )\n                    .ConfigureServices((context, services) =>\n                    {\n                        // TODO: Register your services\n                        //services.AddSingleton<IMyService, MyService>();\n                    })\n                    .UseNavigation(RegisterRoutes)\n                );\n            MainWindow = builder.Window;\n\n#if DEBUG\n            MainWindow.EnableHotReload();\n#endif\n\n            Dispatcher = MainWindow.DispatcherQueue;\n            Host = await builder.NavigateAsync<Shell>();\n        }\n\n        private static void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)\n        {\n            views.Register(\n                new ViewMap(ViewModel: typeof(ShellViewModel)),\n                new ViewMap<ServerPage, ServerViewModel>(),\n                new DataViewMap<MainPage, MainViewModel, IpcServerEntity>(),\n                new DataViewMap<SecondPage, SecondViewModel, Entity>()\n            );\n\n            routes.Register(\n                new RouteMap(\"\", View: views.FindByViewModel<ShellViewModel>(),\n                    Nested: new RouteMap[]\n                    {\n                    new RouteMap(\"Main\", View: views.FindByViewModel<MainViewModel>()),\n                    new RouteMap(\"Second\", View: views.FindByViewModel<SecondViewModel>()),\n                    }\n                )\n            );\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/AppResources.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n          xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n  <ResourceDictionary.MergedDictionaries>\n    <!-- Load WinUI resources -->\n    <XamlControlsResources xmlns=\"using:Microsoft.UI.Xaml.Controls\" />\n    <MaterialToolkitTheme xmlns=\"using:Uno.Toolkit.UI.Material\"\n      ColorOverrideSource=\"ms-appx:///IpcUno/Styles/ColorPaletteOverride.xaml\"\n      FontOverrideSource=\"ms-appx:///IpcUno/Styles/MaterialFontsOverride.xaml\" />\n\n  </ResourceDictionary.MergedDictionaries>\n  <!-- Add resources here -->\n  <Style TargetType=\"TextBlock\">\n    <Setter Property=\"FontFamily\" Value=\"Microsoft YaHei UI\"/>\n  </Style>\n  <Style TargetType=\"Button\">\n    <Setter Property=\"FontFamily\" Value=\"Microsoft YaHei UI\"/>\n  </Style>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Assets/SharedAssets.md",
    "content": "# Shared Assets\n\nSee documentation about assets here: https://github.com/unoplatform/uno/blob/master/doc/articles/features/working-with-assets.md\n\n## Here is a cheat sheet\n\n1. Add the image file to the `Assets` directory of a shared project.\n2. Set the build action to `Content`.\n3. (Recommended) Provide an asset for various scales/dpi\n\n### Examples\n\n```text\n\\Assets\\Images\\logo.scale-100.png\n\\Assets\\Images\\logo.scale-200.png\n\\Assets\\Images\\logo.scale-400.png\n\n\\Assets\\Images\\scale-100\\logo.png\n\\Assets\\Images\\scale-200\\logo.png\n\\Assets\\Images\\scale-400\\logo.png\n```\n\n### Table of scales\n\n| Scale | WinUI       | iOS/MacCatalyst | Android |\n|-------|:-----------:|:---------------:|:-------:|\n| `100` | scale-100   | @1x             | mdpi    |\n| `125` | scale-125   | N/A             | N/A     |\n| `150` | scale-150   | N/A             | hdpi    |\n| `200` | scale-200   | @2x             | xhdpi   |\n| `300` | scale-300   | @3x             | xxhdpi  |\n| `400` | scale-400   | N/A             | xxxhdpi |\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Business/Models/AppConfig.cs",
    "content": "namespace IpcUno.Business.Models\n{\n    public record AppConfig\n    {\n        public string? Environment { get; init; }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Business/Models/ConnectedPeerModel.cs",
    "content": "﻿using System.Collections.ObjectModel;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\n\nusing Windows.ApplicationModel.Core;\nusing Windows.UI.Core;\n\nnamespace IpcUno.Business.Models\n{\n    public class ConnectedPeerModel\n    {\n        public ConnectedPeerModel()\n        {\n            Peer = null!;\n        }\n\n        public ConnectedPeerModel(PeerProxy peer)\n        {\n            Peer = peer;\n            peer.MessageReceived += Peer_MessageReceived;\n        }\n\n        private void Peer_MessageReceived(object? sender, IPeerMessageArgs e)\n        {\n            var streamReader = new StreamReader(e.Message.Body.ToMemoryStream());\n            var message = streamReader.ReadToEnd();\n\n            _ = CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => \n            {\n                AddMessage(PeerName, message);\n            });\n        }\n\n        public void AddMessage(string name, string message)\n        {\n            MessageList.Add($\"{name} {DateTime.Now:yyyy/MM/dd hh:mm:ss.fff}:\\r\\n{message}\");\n        }\n\n        public ObservableCollection<string> MessageList { get; } = new ObservableCollection<string>();\n\n        public PeerProxy Peer { get; }\n\n        public string PeerName => Peer.PeerName;\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Business/Models/Entity.cs",
    "content": "namespace IpcUno.Business.Models\n{\n    public record Entity(string Name);\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Business/Models/IpcServerEntity.cs",
    "content": "namespace IpcUno.Business.Models\n{\n    public record IpcServerEntity(string Name);\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/GlobalSuppressions.cs",
    "content": "// This file is used by Code Analysis to maintain SuppressMessage\n// attributes that are applied to this project.\n// Project-level suppressions either have no target or are given\n// a specific target and scoped to a namespace, type, member, etc.\n\nusing System.Diagnostics.CodeAnalysis;\n\n[assembly: SuppressMessage(\"Interoperability\", \"CA1416:验证平台兼容性\", Justification = \"<挂起>\")]\n\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/GlobalUsings.cs",
    "content": "﻿global using System.Collections.Immutable;\nglobal using System.Windows.Input;\n\nglobal using Microsoft.Extensions.DependencyInjection;\n\nglobal using Windows.Networking.Connectivity;\nglobal using Windows.Storage;\n\nglobal using Microsoft.Extensions.Hosting;\nglobal using Microsoft.Extensions.Logging;\nglobal using Microsoft.UI.Xaml;\nglobal using Microsoft.UI.Xaml.Controls;\nglobal using Microsoft.UI.Xaml.Media;\nglobal using Microsoft.UI.Xaml.Navigation;\nglobal using Microsoft.Extensions.Options;\n\nglobal using IpcUno.Business.Models;\nglobal using IpcUno.Presentation;\n\n#if MAUI_EMBEDDING\nglobal using IpcUno.MauiControls;\n#endif\nglobal using Uno.UI;\n\nglobal using Windows.ApplicationModel;\n\nglobal using ApplicationExecutionState = Windows.ApplicationModel.Activation.ApplicationExecutionState;\n\nglobal using CommunityToolkit.Mvvm.ComponentModel;\nglobal using CommunityToolkit.Mvvm.Input;\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/IpcUno.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFrameworks Condition=\"$([MSBuild]::IsOSPlatform('windows')) or '$(EnableWindowsTargeting)' == 'true'\">$(TargetFrameworks);net8.0-windows10.0.19041</TargetFrameworks>\n    <TargetFrameworks>$(TargetFrameworks);net8.0;</TargetFrameworks>\n    <TargetFrameworks Condition=\"'$(OverrideTargetFrameworks)'!=''\">$(OverrideTargetFrameworks)</TargetFrameworks>\n\n    <!-- Ensures the .xr.xml files are generated in a proper layout folder -->\n    <GenerateLibraryLayout>true</GenerateLibraryLayout>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"dotnetCampus.Ipc\" />\n    <PackageReference Include=\"Uno.WinUI\" />\n    <PackageReference Include=\"Uno.Resizetizer\" />\n    <PackageReference Include=\"CommunityToolkit.Mvvm\" />\n    <PackageReference Include=\"Uno.Extensions.Configuration\" />\n    <PackageReference Include=\"Uno.Extensions.Logging.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Maui.WinUI\" />\n    <PackageReference Include=\"Uno.Material.WinUI\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI.Material\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Core.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Hosting.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.WinUI\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Console\" />\n    <PackageReference Include=\"Microsoft.Maui.Controls\" />\n    <PackageReference Include=\"Microsoft.Maui.Controls.Compatibility\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"$(IsAndroid)\">\n    <PackageReference Include=\"Xamarin.Google.Android.Material\" VersionOverride=\"$(AndroidMaterialVersion)\" />\n    <PackageReference Include=\"Xamarin.AndroidX.Navigation.UI\" VersionOverride=\"$(AndroidXNavigationVersion)\" />\n    <PackageReference Include=\"Xamarin.AndroidX.Navigation.Fragment\" VersionOverride=\"$(AndroidXNavigationVersion)\" />\n    <PackageReference Include=\"Xamarin.AndroidX.Navigation.Runtime\" VersionOverride=\"$(AndroidXNavigationVersion)\" />\n    <PackageReference Include=\"Xamarin.AndroidX.Navigation.Common\" VersionOverride=\"$(AndroidXNavigationVersion)\" />\n    <PackageReference Include=\"Xamarin.AndroidX.Collection\" VersionOverride=\"$(AndroidXCollectionVersion)\" />\n    <PackageReference Include=\"Xamarin.AndroidX.Collection.Ktx\" VersionOverride=\"$(AndroidXCollectionVersion)\" />    \n  </ItemGroup>\n\n  <Choose>\n    <When Condition=\"$(IsWinAppSdk)\">\n      <PropertyGroup>\n        <!--\n        If you encounter this error message:\n\n          error NETSDK1148: A referenced assembly was compiled using a newer version of Microsoft.Windows.SDK.NET.dll.\n          Please update to a newer .NET SDK in order to reference this assembly.\n\n        This means that the two packages below must be aligned with the \"build\" version number of\n        the \"Microsoft.Windows.SDK.BuildTools\" package above, and the \"revision\" version number\n        must be the highest found in https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref.\n        -->\n        <!-- <WindowsSdkPackageVersion>10.0.22621.28</WindowsSdkPackageVersion> -->\n      </PropertyGroup>\n    </When>\n    <Otherwise>\n      <ItemGroup>\n        <PackageReference Include=\"Uno.WinUI.Lottie\" />\n        <PackageReference Include=\"Uno.WinUI.DevServer\" Condition=\"'$(Configuration)'=='Debug'\" />\n\n        <!-- Include all images by default - matches the __WindowsAppSdkDefaultImageIncludes property in the WindowsAppSDK -->\n        <Content Include=\"Assets\\**;**/*.png;**/*.bmp;**/*.jpg;**/*.dds;**/*.tif;**/*.tga;**/*.gif\" Exclude=\"$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder);**\\*.svg\" />\n        <Page Include=\"**\\*.xaml\" Exclude=\"$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)\" />\n        <Compile Update=\"**\\*.xaml.cs\">\n          <DependentUpon>%(Filename)</DependentUpon>\n        </Compile>\n        <PRIResource Include=\"**\\*.resw\" Exclude=\"$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)\" />\n      </ItemGroup>\n    </Otherwise>\n  </Choose>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IpcUno.MauiControls\\IpcUno.MauiControls.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <UnoImage Include=\"Assets\\**\\*.svg\" />\n    <EmbeddedResource Include=\"appsettings.json\" />\n    <EmbeddedResource Include=\"appsettings.*.json\" DependentUpon=\"appsettings.json\" />\n    <UpToDateCheckInput Include=\"**\\*.xaml\" Exclude=\"bin\\**\\*.xaml;obj\\**\\*.xaml\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/AddConnectPage.xaml",
    "content": "﻿<Page x:Class=\"IpcUno.Presentation.AddConnectPage\"\n      xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:local=\"using:IpcUno.Presentation\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:uen=\"using:Uno.Extensions.Navigation.UI\"\n      xmlns:utu=\"using:Uno.Toolkit.UI\"\n      xmlns:um=\"using:Uno.Material\"\n      Background=\"{ThemeResource BackgroundBrush}\">\n\n  <Grid>\n    <Grid HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n      <StackPanel>\n        <TextBlock HorizontalAlignment=\"Center\" FontSize=\"100\">添加设备</TextBlock>\n\n        <Grid Margin=\"10,10,10,10\">\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"></ColumnDefinition>\n            <ColumnDefinition Width=\"*\"></ColumnDefinition>\n          </Grid.ColumnDefinitions>\n          <TextBlock FontSize=\"30\" Text=\"服务器名：\" VerticalAlignment=\"Center\"></TextBlock>\n          <TextBox x:Name=\"ServerNameTextBox\" Grid.Column=\"1\" FontSize=\"30\"></TextBox>\n        </Grid>\n        <Grid >\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"*\"></ColumnDefinition>\n            <ColumnDefinition Width=\"*\"></ColumnDefinition>\n          </Grid.ColumnDefinitions>\n          <Button Margin=\"10,10,10,10\" Content=\"连接现有服务\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Stretch\" Click=\"ConnectServerButton_OnClick\"></Button>\n          <Button Grid.Column=\"1\" Margin=\"10,10,10,10\" IsEnabled=\"false\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Stretch\" Content=\"启动新服务\" Click=\"StartServerButton_OnClick\"></Button>\n        </Grid>\n      </StackPanel>\n    </Grid>\n  </Grid>\n</Page>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/AddConnectPage.xaml.cs",
    "content": "namespace IpcUno.Presentation\n{\n    public sealed partial class AddConnectPage : Page\n    {\n        public AddConnectPage()\n        {\n            this.InitializeComponent();\n        }\n\n        private void ConnectServerButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            ServerConnecting?.Invoke(this, ServerNameTextBox.Text);\n        }\n\n        private void StartServerButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            ServerStarting?.Invoke(this, ServerNameTextBox.Text);\n            BuildServerName();\n        }\n\n        private void BuildServerName()\n        {\n            ServerNameTextBox.Text = System.IO.Path.GetRandomFileName();\n        }\n\n        public event EventHandler<string>? ServerConnecting;\n\n        public event EventHandler<string>? ServerStarting;\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/ChatPage.xaml",
    "content": "﻿<Page x:Class=\"IpcUno.Presentation.ChatPage\"\n      xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:local=\"using:IpcUno.Presentation\"\n      xmlns:model=\"using:IpcUno.Business.Models\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:uen=\"using:Uno.Extensions.Navigation.UI\"\n      xmlns:utu=\"using:Uno.Toolkit.UI\"\n      xmlns:um=\"using:Uno.Material\"\n      Background=\"Transparent\" >\n\n  <Grid utu:SafeArea.Insets=\"VisibleBounds\">\n    <Grid>\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\" />\n        <RowDefinition />\n        <RowDefinition Height=\"Auto\" />\n        <RowDefinition Height=\"Auto\" />\n      </Grid.RowDefinitions>\n      <TextBlock Margin=\"10,10,10,10\" Text=\"{Binding Path=PeerName}\" />\n      <ListView x:Name=\"MessageListView\" Grid.Row=\"1\" ItemsSource=\"{Binding Path=MessageList}\" Background=\"White\">\n        <ListView.ItemTemplate>\n          <DataTemplate>\n            <TextBlock Text=\"{Binding}\" TextWrapping=\"Wrap\" Margin=\"5 0 0 0\"/>\n          </DataTemplate>\n        </ListView.ItemTemplate>\n      </ListView>\n      <TextBox x:Name=\"MessageTextBox\" Grid.Row=\"2\" Margin=\"10,10,10,10\"\n          Height=\"100\" FontFamily=\"Microsoft YaHei UI\" TextWrapping=\"Wrap\" AcceptsReturn=\"True\" VerticalContentAlignment=\"Top\"/>\n      <Button Grid.Row=\"3\" Margin=\"10,0,10,10\" HorizontalAlignment=\"Right\" Content=\"Send\" Click=\"SendButton_OnClick\" />\n    </Grid>\n  </Grid>\n</Page>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/ChatPage.xaml.cs",
    "content": "using System.Reflection;\nusing System.Text;\n\nusing IpcUno.Utils;\n\nnamespace IpcUno.Presentation\n{\n    public sealed partial class ChatPage : Page\n    {\n        public ChatPage(ConnectedPeerModel model, string serverName)\n        {\n            DataContext = model;\n            this.InitializeComponent();\n            Model = model;\n            ServerName = serverName;\n\n            Loaded += ChatPage_Loaded;\n        }\n\n        private void ChatPage_Loaded(object sender, RoutedEventArgs e)\n        {\n            MessageListView.ScrollToBottom();\n\n            // 有消息过来，自动滚动到最下\n            Model.MessageList.CollectionChanged += (o, args) =>\n            {\n                _ = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () =>\n                 {\n                     MessageListView.ScrollToBottom();\n                 });\n            };\n        }\n\n        public string ServerName { get; }\n\n        public ConnectedPeerModel Model { get; }\n\n        private async void SendButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            Model.AddMessage(ServerName, MessageTextBox.Text);\n            await Model.Peer.NotifyAsync(new dotnetCampus.Ipc.Messages.IpcMessage(\"CharPage\", Encoding.UTF8.GetBytes(MessageTextBox.Text)));\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/MainPage.xaml",
    "content": "﻿<Page x:Class=\"IpcUno.Presentation.MainPage\"\n      xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:local=\"using:IpcUno.Presentation\"\n      xmlns:model=\"using:IpcUno.Business.Models\"\n      xmlns:uen=\"using:Uno.Extensions.Navigation.UI\"\n      xmlns:utu=\"using:Uno.Toolkit.UI\"\n      xmlns:um=\"using:Uno.Material\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:maui=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:not_maui=\"http://notmaui\"\n      mc:Ignorable=\"d not_maui\"\n      xmlns:controls=\"using:IpcUno.MauiControls\"\n      NavigationCacheMode=\"Required\"\n      Background=\"{ThemeResource BackgroundBrush}\"\n      d:DataContext=\"{d:DesignInstance local:MainViewModel}\">\n\n  <Grid utu:SafeArea.Insets=\"VisibleBounds\">\n    <Grid x:Name=\"MainGrid\">\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"></RowDefinition>\n        <RowDefinition Height=\"*\"></RowDefinition>\n        <RowDefinition Height=\"Auto\"></RowDefinition>\n      </Grid.RowDefinitions>\n      <Grid x:Name=\"HeaderGrid\">\n        <!--上方标题，写明当前的服务器名-->\n        <Grid Margin=\"10,10,10,10\">\n          <Grid.ColumnDefinitions>\n            <ColumnDefinition Width=\"Auto\"></ColumnDefinition>\n            <ColumnDefinition Width=\"*\"></ColumnDefinition>\n          </Grid.ColumnDefinitions>\n          <TextBlock VerticalAlignment=\"Center\" Text=\"本服务器名：\"></TextBlock>\n          <TextBox x:Name=\"ServerNameTextBox\" Grid.Column=\"1\" Style=\"{x:Null}\" IsReadOnly=\"True\" MaxWidth=\"300\" HorizontalAlignment=\"Left\" Height=\"10\" MaxHeight=\"30\"\n                   BorderThickness=\"0\" Background=\"Transparent\"\n                   VerticalContentAlignment=\"Center\" Text=\"{Binding CurrentServerName}\"></TextBox>\n        </Grid>\n      </Grid>\n      <Grid Grid.Row=\"1\">\n        <!--下方，左侧写明列表，右侧进行通讯-->\n        <Grid.ColumnDefinitions>\n          <ColumnDefinition Width=\"200\"></ColumnDefinition>\n          <ColumnDefinition></ColumnDefinition>\n        </Grid.ColumnDefinitions>\n        <Grid>\n          <Grid.RowDefinitions>\n            <RowDefinition></RowDefinition>\n            <RowDefinition Height=\"Auto\"></RowDefinition>\n          </Grid.RowDefinitions>\n          <ListView x:Name=\"ConnectedPeerListView\" BorderThickness=\"1\" BorderBrush=\"Gray\" ItemsSource=\"{Binding ConnectedPeerModelList}\" SelectionChanged=\"ConnectedPeerListView_SelectionChanged\">\n            <ListView.ItemTemplate>\n              <DataTemplate x:DataType=\"model:ConnectedPeerModel\">\n                <TextBlock Margin=\"5 0 0 0\" Text=\"{Binding Path=PeerName}\"></TextBlock>\n              </DataTemplate>\n            </ListView.ItemTemplate>\n          </ListView>\n          <Button x:Name=\"AddConnectButton\" Grid.Row=\"1\" Margin=\"10,10,10,10\" Content=\"+\"\n                  HorizontalAlignment=\"Stretch\" Click=\"AddConnectButton_Click\"></Button>\n        </Grid>\n        <Grid Grid.Column=\"1\">\n          <Grid.RowDefinitions>\n            <RowDefinition Height=\"*\"></RowDefinition>\n            <RowDefinition Height=\"200\"></RowDefinition>\n          </Grid.RowDefinitions>\n          <Grid>\n            <Border Background=\"#A6A6A6\"></Border>\n            <ContentControl x:Name=\"MainPanelContentControl\" HorizontalAlignment=\"Stretch\" VerticalAlignment=\"Stretch\"\n                            HorizontalContentAlignment=\"Stretch\" VerticalContentAlignment=\"Stretch\"></ContentControl>\n          </Grid>\n          <Grid Grid.Row=\"1\" Margin=\"10,10,10,10\">\n            <TextBox x:Name=\"LogTextBox\" FontFamily=\"Microsoft YaHei UI\" TextWrapping=\"Wrap\" IsReadOnly=\"true\" AcceptsReturn=\"True\" VerticalContentAlignment=\"Top\"></TextBox>\n          </Grid>\n        </Grid>\n      </Grid>\n    </Grid>\n  </Grid>\n</Page>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/MainPage.xaml.cs",
    "content": "using System.Diagnostics;\n\nusing IpcUno.Utils;\n\nnamespace IpcUno.Presentation\n{\n    public sealed partial class MainPage : Page\n    {\n        public MainPage()\n        {\n            this.InitializeComponent();\n            DataContextChanged += MainPage_DataContextChanged;\n            Loaded += MainPage_Loaded;\n        }\n\n        private void MainPage_Loaded(object sender, RoutedEventArgs e)\n        {\n            ShowAddConnectPage();\n        }\n\n        private void MainPage_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)\n        {\n            // 在这个时机可以拿到 ViewModel 对象\n            ViewModel.AddedLogMessage += ViewModel_AddedLogMessage;\n        }\n\n        private void ViewModel_AddedLogMessage(object? sender, string message)\n        {\n            // 收到日志\n            LogTextBox.Text += $\"{DateTime.Now:hh:mm:ss,fff} {message}\\r\\n\";\n            // 延迟一下，防止界面还没刷新就执行滚动\n            _ = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low, () =>\n            {\n                LogTextBox.ScrollToBottom();\n            });\n        }\n\n        public MainViewModel ViewModel => (MainViewModel) DataContext;\n\n        private void ConnectedPeerListView_SelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (e.AddedItems.Count == 1)\n            {\n                var connectedPeerModel = (ConnectedPeerModel) e.AddedItems[0]!;\n                MainPanelContentControl.Content = new ChatPage(connectedPeerModel, ViewModel.CurrentServerName);\n            }\n        }\n\n        private void AddConnectButton_Click(object sender, RoutedEventArgs e)\n        {\n            ShowAddConnectPage();\n        }\n\n        private void ShowAddConnectPage()\n        {\n            ConnectedPeerListView.SelectedItem = null;\n\n            AddConnectPage addConnectPage = new AddConnectPage();\n            addConnectPage.ServerConnecting += async (s, e) =>\n            {\n                var serverName = e;\n                await ViewModel.ConnectAsync(serverName);\n\n                ConnectedPeerListView.SelectedItem = ViewModel.ConnectedPeerModelList.FirstOrDefault(t => t.PeerName == serverName);\n            };\n\n            MainPanelContentControl.Content = addConnectPage;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/MainViewModel.cs",
    "content": "using System.Collections.ObjectModel;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Pipes;\n\nusing Microsoft.UI.Dispatching;\n\nusing Windows.ApplicationModel.Core;\nusing Windows.UI.Core;\n\nnamespace IpcUno.Presentation\n{\n    public partial class MainViewModel : ObservableObject, IInjectable<IServiceProvider>\n    {\n        [ObservableProperty]\n        private string _currentServerName = \"dotnet_campus\";\n        private readonly IpcProvider _ipcProvider;\n\n        public ObservableCollection<ConnectedPeerModel> ConnectedPeerModelList { get; } = new ObservableCollection<ConnectedPeerModel>();\n\n        public MainViewModel(IpcServerEntity entity)\n        {\n            _currentServerName = entity.Name;\n            _ipcProvider = new IpcProvider(_currentServerName);\n            _ipcProvider.PeerConnected += IpcProvider_PeerConnected;\n            _ipcProvider.StartServer();\n        }\n\n        private void IpcProvider_PeerConnected(object? sender, dotnetCampus.Ipc.Context.PeerConnectedArgs e)\n        {\n            Log($\"[被动连接] {e.Peer.PeerName}\");\n            _ = AddPeer(e.Peer);\n        }\n\n        private async Task AddPeer(PeerProxy peer)\n        {\n            //var dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread(); // WinUI: null\n            //var currentView = CoreApplication.GetCurrentView();// WinUI: System.Runtime.InteropServices.COMException:“Element not found.\n            //var dispatcher = ((IpcUno.App) IpcUno.App.Current).Dispatcher;\n            //await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>\n            //{\n            //    var currentPeer = ConnectedPeerModelList.FirstOrDefault(temp => temp.PeerName == peer.PeerName);\n            //    if (currentPeer != null)\n            //    {\n            //        currentPeer.Peer.PeerConnectionBroken -= Peer_PeerConnectBroke;\n            //        ConnectedPeerModelList.Remove(currentPeer);\n            //    }\n\n            //    ConnectedPeerModelList.Add(new ConnectedPeerModel(peer));\n\n            //    peer.PeerConnectionBroken += Peer_PeerConnectBroke;\n            //});\n\n            var dispatcher = ((IpcUno.App) IpcUno.App.Current).Dispatcher;\n            TaskCompletionSource source = new();\n            dispatcher.TryEnqueue(() =>\n            {\n                var currentPeer = ConnectedPeerModelList.FirstOrDefault(temp => temp.PeerName == peer.PeerName);\n                if (currentPeer != null)\n                {\n                    currentPeer.Peer.PeerConnectionBroken -= Peer_PeerConnectBroke;\n                    ConnectedPeerModelList.Remove(currentPeer);\n                }\n\n                ConnectedPeerModelList.Add(new ConnectedPeerModel(peer));\n\n                peer.PeerConnectionBroken += Peer_PeerConnectBroke;\n                source.SetResult();\n            });\n            await source.Task;\n        }\n\n        private void Peer_PeerConnectBroke(object? sender, IPeerConnectionBrokenArgs e)\n        {\n            var peer = (PeerProxy) sender!;\n            Log($\"[连接断开] {peer.PeerName}\");\n        }\n\n        private void Log(string message)\n        {\n            AddedLogMessage?.Invoke(this, message);\n        }\n\n        public event EventHandler<string>? AddedLogMessage;\n\n        public void Inject(IServiceProvider entity)\n        {\n        }\n\n        public async Task ConnectAsync(string serverName)\n        {\n            Log($\"[开始连接] {serverName}\");\n            var peer = await _ipcProvider.GetAndConnectToPeerAsync(serverName).ConfigureAwait(false);\n            await AddPeer(peer);\n            Log($\"[完成连接] {serverName}\");\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/SecondPage.xaml",
    "content": "﻿<Page x:Class=\"IpcUno.Presentation.SecondPage\"\n      xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:local=\"using:IpcUno.Presentation\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:uen=\"using:Uno.Extensions.Navigation.UI\"\n      xmlns:utu=\"using:Uno.Toolkit.UI\"\n      xmlns:um=\"using:Uno.Material\"\n      Background=\"{ThemeResource BackgroundBrush}\">\n\n  <Grid utu:SafeArea.Insets=\"VisibleBounds\">\n  </Grid>\n</Page>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/SecondPage.xaml.cs",
    "content": "﻿namespace IpcUno.Presentation\n{\n    public sealed partial class SecondPage : Page\n    {\n        public SecondPage()\n        {\n            this.InitializeComponent();\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/SecondViewModel.cs",
    "content": "namespace IpcUno.Presentation\n{\n    public partial record SecondViewModel(Entity Entity)\n    {\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/ServerPage.xaml",
    "content": "﻿<Page x:Class=\"IpcUno.Presentation.ServerPage\"\n      xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:local=\"using:IpcUno.Presentation\"\n      xmlns:uen=\"using:Uno.Extensions.Navigation.UI\"\n      xmlns:utu=\"using:Uno.Toolkit.UI\"\n      xmlns:um=\"using:Uno.Material\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:maui=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:not_maui=\"http://notmaui\"\n      mc:Ignorable=\"d not_maui\"\n      xmlns:controls=\"using:IpcUno.MauiControls\"\n      NavigationCacheMode=\"Required\"\n      Background=\"{ThemeResource BackgroundBrush}\"\n      d:DataContext=\"{d:DesignInstance local:ServerPage}\">\n\n  <Grid utu:SafeArea.Insets=\"VisibleBounds\">\n    <Grid HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n      <Grid.RowDefinitions>\n        <RowDefinition Height=\"Auto\"></RowDefinition>\n        <RowDefinition Height=\"Auto\"></RowDefinition>\n        <RowDefinition Height=\"Auto\"></RowDefinition>\n      </Grid.RowDefinitions>\n      <TextBlock Margin=\"10,10,2,10\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" FontSize=\"60\" \n                 Text=\"本地服务器名:\"></TextBlock>\n      <TextBox x:Name=\"ServerNameTextBox\" Grid.Row=\"1\" Margin=\"2,10,10,10\" \n               HorizontalAlignment=\"Center\" FontSize=\"50\"\n               FontFamily=\"Microsoft YaHei UI\"\n               Text=\"{Binding CurrentServerName,Mode=TwoWay}\"></TextBox>\n      <Button x:Name=\"StartServerButton\"  Grid.Row=\"2\" Margin=\"10,50,10,10\" HorizontalAlignment=\"Center\" Width=\"300\" FontSize=\"50\" \n              Content=\"启动服务器\" Command=\"{Binding NavigateMainPageCommand}\"></Button>\n    </Grid>\n  </Grid>\n</Page>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/ServerPage.xaml.cs",
    "content": "namespace IpcUno.Presentation\n{\n    public sealed partial class ServerPage : Page\n    {\n        public ServerPage()\n        {\n            this.InitializeComponent();            \n        }\n\n        public ServerViewModel ViewModel => (ServerViewModel) DataContext;\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/ServerViewModel.cs",
    "content": "namespace IpcUno.Presentation\n{\n    public partial class ServerViewModel : ObservableObject\n    {\n        public ServerViewModel(INavigator navigator)\n        {\n            _navigator = navigator;\n        }\n\n        private readonly INavigator _navigator;\n\n        [ObservableProperty]\n        private string _currentServerName = \"dotnet_campus\";\n\n        [RelayCommand]\n        private void NavigateMainPage()\n        {\n            _ = _navigator.NavigateViewModelAsync<MainViewModel>(this, data: new IpcServerEntity(CurrentServerName));\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/Shell.xaml",
    "content": "﻿<UserControl x:Class=\"IpcUno.Presentation.Shell\"\n      xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n      xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n      xmlns:local=\"using:IpcUno.Presentation\"\n      xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n      xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n      xmlns:utu=\"using:Uno.Toolkit.UI\"\n      mc:Ignorable=\"d\"\n      d:DesignHeight=\"300\"\n      d:DesignWidth=\"400\">\n  <Border Background=\"{ThemeResource BackgroundBrush}\">\n    <utu:ExtendedSplashScreen x:Name=\"Splash\"\n                HorizontalAlignment=\"Stretch\"\n                VerticalAlignment=\"Stretch\"\n                HorizontalContentAlignment=\"Stretch\"\n                VerticalContentAlignment=\"Stretch\">\n      <utu:ExtendedSplashScreen.LoadingContentTemplate>\n        <DataTemplate>\n          <Grid>\n            <Grid.RowDefinitions>\n              <RowDefinition Height=\"2*\" />\n              <RowDefinition />\n            </Grid.RowDefinitions>\n\n            <ProgressRing IsActive=\"True\"\n                  Grid.Row=\"1\"\n                  VerticalAlignment=\"Center\"\n                  HorizontalAlignment=\"Center\"\n                  Height=\"100\"\n                  Width=\"100\" />\n          </Grid>\n        </DataTemplate>\n      </utu:ExtendedSplashScreen.LoadingContentTemplate>\n    </utu:ExtendedSplashScreen>\n  </Border>\n</UserControl>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/Shell.xaml.cs",
    "content": "﻿namespace IpcUno.Presentation\n{\n    public sealed partial class Shell : UserControl, IContentControlProvider\n    {\n        public Shell()\n        {\n            this.InitializeComponent();\n        }\n        public ContentControl ContentControl => Splash;\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Presentation/ShellViewModel.cs",
    "content": "namespace IpcUno.Presentation\n{\n    public class ShellViewModel\n    {\n        private readonly INavigator _navigator;\n\n        public ShellViewModel(\n            INavigator navigator)\n        {\n            _navigator = navigator;\n            _ = Start();\n        }\n\n        public async Task Start()\n        {\n            await _navigator.NavigateViewModelAsync<ServerViewModel>(this);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Strings/en/Resources.resw",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"ApplicationName\" xml:space=\"preserve\">\n    <value>IpcUno-en</value>\n  </data>\n</root>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Styles/ColorPaletteOverride.xaml",
    "content": "﻿<!-- This file is generated by a tool from the file ColorPaletteOverride.zip - - YOU SHOULD NOT EDIT IT manually.-->\n<ResourceDictionary xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n  <ResourceDictionary.ThemeDictionaries>\n    <ResourceDictionary x:Key=\"Light\">\n      <Color x:Key=\"PrimaryColor\">#5946D2</Color>\n      <Color x:Key=\"OnPrimaryColor\">#FFFFFF</Color>\n      <Color x:Key=\"PrimaryContainerColor\">#E5DEFF</Color>\n      <Color x:Key=\"OnPrimaryContainerColor\">#170065</Color>\n      <Color x:Key=\"SecondaryColor\">#6B4EA2</Color>\n      <Color x:Key=\"OnSecondaryColor\">#FFFFFF</Color>\n      <Color x:Key=\"SecondaryContainerColor\">#EBDDFF</Color>\n      <Color x:Key=\"OnSecondaryContainerColor\">#220555</Color>\n      <Color x:Key=\"TertiaryColor\">#0061A4</Color>\n      <Color x:Key=\"OnTertiaryColor\">#FFFFFF</Color>\n      <Color x:Key=\"TertiaryContainerColor\">#CFE4FF</Color>\n      <Color x:Key=\"OnTertiaryContainerColor\">#001D36</Color>\n      <Color x:Key=\"ErrorColor\">#B3261E</Color>\n      <Color x:Key=\"ErrorContainerColor\">#F9DEDC</Color>\n      <Color x:Key=\"OnErrorColor\">#FFFFFF</Color>\n      <Color x:Key=\"OnErrorContainerColor\">#410E0B</Color>\n      <Color x:Key=\"BackgroundColor\">#FCFBFF</Color>\n      <Color x:Key=\"OnBackgroundColor\">#1C1B1F</Color>\n      <Color x:Key=\"SurfaceColor\">#FFFFFF</Color>\n      <Color x:Key=\"OnSurfaceColor\">#1C1B1F</Color>\n      <Color x:Key=\"SurfaceVariantColor\">#F2EFF5</Color>\n      <Color x:Key=\"OnSurfaceVariantColor\">#8B8494</Color>\n      <Color x:Key=\"OutlineColor\">#79747E</Color>\n      <Color x:Key=\"OnSurfaceInverseColor\">#F4EFF4</Color>\n      <Color x:Key=\"SurfaceInverseColor\">#313033</Color>\n      <Color x:Key=\"PrimaryInverseColor\">#C8BFFF</Color>\n      <Color x:Key=\"SurfaceTintColor\">#5946D2</Color>\n      <Color x:Key=\"OutlineVariantColor\">#C9C5D0</Color>\n    </ResourceDictionary>\n    <ResourceDictionary x:Key=\"Dark\">\n      <Color x:Key=\"PrimaryColor\">#C7BFFF</Color>\n      <Color x:Key=\"OnPrimaryColor\">#2A009F</Color>\n      <Color x:Key=\"PrimaryContainerColor\">#4129BA</Color>\n      <Color x:Key=\"OnPrimaryContainerColor\">#E4DFFF</Color>\n      <Color x:Key=\"SecondaryColor\">#CDC2DC</Color>\n      <Color x:Key=\"OnSecondaryColor\">#332D41</Color>\n      <Color x:Key=\"SecondaryContainerColor\">#433C52</Color>\n      <Color x:Key=\"OnSecondaryContainerColor\">#EDDFFF</Color>\n      <Color x:Key=\"TertiaryColor\">#9FCAFF</Color>\n      <Color x:Key=\"OnTertiaryColor\">#003258</Color>\n      <Color x:Key=\"TertiaryContainerColor\">#00497D</Color>\n      <Color x:Key=\"OnTertiaryContainerColor\">#D1E4FF</Color>\n      <Color x:Key=\"ErrorColor\">#FFB4AB</Color>\n      <Color x:Key=\"ErrorContainerColor\">#93000A</Color>\n      <Color x:Key=\"OnErrorColor\">#690005</Color>\n      <Color x:Key=\"OnErrorContainerColor\">#FFDAD6</Color>\n      <Color x:Key=\"BackgroundColor\">#1C1B1F</Color>\n      <Color x:Key=\"OnBackgroundColor\">#E5E1E6</Color>\n      <Color x:Key=\"SurfaceColor\">#302D37</Color>\n      <Color x:Key=\"OnSurfaceColor\">#E6E1E5</Color>\n      <Color x:Key=\"SurfaceVariantColor\">#47464F</Color>\n      <Color x:Key=\"OnSurfaceVariantColor\">#C9C5D0</Color>\n      <Color x:Key=\"OutlineColor\">#928F99</Color>\n      <Color x:Key=\"OnSurfaceInverseColor\">#1C1B1F</Color>\n      <Color x:Key=\"SurfaceInverseColor\">#E6E1E5</Color>\n      <Color x:Key=\"PrimaryInverseColor\">#2A009F</Color>\n      <Color x:Key=\"SurfaceTintColor\">#C7BFFF</Color>\n      <Color x:Key=\"OutlineVariantColor\">#57545D</Color>\n    </ResourceDictionary>\n  </ResourceDictionary.ThemeDictionaries>\n</ResourceDictionary>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Styles/MaterialFontsOverride.xaml",
    "content": "﻿<ResourceDictionary xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n          xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n  <FontFamily x:Key=\"MaterialLightFontFamily\">ms-appx:///Uno.Fonts.Roboto/Fonts/Roboto-Light.ttf#Roboto</FontFamily>\n  <FontFamily x:Key=\"MaterialMediumFontFamily\">ms-appx:///Uno.Fonts.Roboto/Fonts/Roboto-Medium.ttf#Roboto</FontFamily>\n  <FontFamily x:Key=\"MaterialRegularFontFamily\">ms-appx:///Uno.Fonts.Roboto/Fonts/Roboto-Regular.ttf#Roboto</FontFamily>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Utils/ScrollViewerExtensions.cs",
    "content": "namespace IpcUno.Utils\n{\n    static class ScrollViewerExtensions\n    {\n        public static void ScrollToBottom(this TextBox textBox)\n        {\n            ScrollToBottomInner(textBox);\n        }\n\n        public static void ScrollToBottom(this ListView listView)\n        {\n            ScrollToBottomInner(listView);\n        }\n\n        private static void ScrollToBottomInner(UIElement element)\n        {\n            if (element.VisualDescendant<ScrollViewer>() is { } scrollViewer)\n            {\n                scrollViewer.ChangeView(0.0f, scrollViewer.ExtentHeight, 1.0f, true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Utils/TreeExtensions.cs",
    "content": "namespace IpcUno.Utils\n{\n    static class TreeExtensions\n    {\n        public static T? VisualDescendant<T>(this UIElement element) where T : DependencyObject\n            => VisualDescendant<T>((DependencyObject) element);\n\n        public static T? VisualDescendant<T>(DependencyObject element) where T : DependencyObject\n        {\n            if (element is T)\n            {\n                return (T) element;\n            }\n\n            T? foundElement = default;\n            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)\n            {\n                var child = VisualTreeHelper.GetChild(element, i);\n                foundElement = VisualDescendant<T>(child);\n                if (foundElement != null)\n                {\n                    break;\n                }\n            }\n\n            return foundElement;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/Utils/UISpyHelper.cs",
    "content": "using System.Diagnostics;\n\nnamespace IpcUno.Utils\n{\n    static class UISpyHelper\n    {\n        public static void Spy(this DependencyObject element)\n        {\n            Uno.Extensions.IndentedStringBuilder builder = new ();\n            SpyInner(element, builder);\n            var spyText = builder.ToString();\n            Debug.WriteLine(spyText);\n        }\n\n        private static void SpyInner(DependencyObject element, Uno.Extensions.IndentedStringBuilder builder)\n        {\n            var name = string.Empty;\n            if (element is FrameworkElement frameworkElement)\n            {\n                name = frameworkElement.Name;\n            }\n            builder.AppendLine($\"{name}({element.GetType().FullName})\\r\\n\");\n\n            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)\n            {\n                using var t = builder.Indent();\n                var child = VisualTreeHelper.GetChild(element, i);\n                SpyInner(child, builder);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/appsettings.development.json",
    "content": "﻿{\n  \"AppConfig\": {\n    \"Environment\": \"Development\"\n  }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno/appsettings.json",
    "content": "{\n  \"AppConfig\": {\n    \"Environment\": \"Production\"\n  }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Base/AppHead.xaml",
    "content": "﻿<local:App x:Class=\"IpcUno.AppHead\"\n       xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n       xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n       xmlns:wasm=\"http://platform.uno/wasm\"\n       xmlns:local=\"using:IpcUno\"\n       xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n       mc:Ignorable=\"wasm\">\n\n  <local:App.Resources>\n    <ResourceDictionary>\n      <ResourceDictionary.MergedDictionaries>\n        <ResourceDictionary Source=\"ms-appx:///IpcUno/AppResources.xaml\" />\n      </ResourceDictionary.MergedDictionaries>\n    </ResourceDictionary>\n  </local:App.Resources>\n\n</local:App>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Base/AppHead.xaml.cs",
    "content": "using Microsoft.UI.Xaml;\n\nusing Uno.Resizetizer;\n\nnamespace IpcUno\n{\n    public sealed partial class AppHead : App\n    {\n        /// <summary>\n        /// Initializes the singleton application object. This is the first line of authored code\n        /// executed, and as such is the logical equivalent of main() or WinMain().\n        /// </summary>\n        public AppHead()\n        {\n            this.InitializeComponent();\n        }\n\n        /// <summary>\n        /// Invoked when the application is launched normally by the end user.  Other entry points\n        /// will be used such as when the application is launched to open a specific file.\n        /// </summary>\n        /// <param name=\"args\">Details about the launch request and process.</param>\n        protected override void OnLaunched(LaunchActivatedEventArgs args)\n        {\n            base.OnLaunched(args);\n\n            MainWindow.SetWindowIcon();\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Base/IpcUno.Base.csproj",
    "content": "<Project Sdk=\"Microsoft.Build.NoTargets/3.7.0\">\n  <PropertyGroup>\n    <!-- NOTE: The TargetFramework is required by MSBuild but not used as this project is not built. -->\n    <TargetFramework>net8.0</TargetFramework>\n    <EnableDefaultItems>false</EnableDefaultItems>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <None Include=\"**\\*\" Exclude=\"obj\\**;bin\\**;*.csproj\" />\n    <None Update=\"AppHead.xaml.cs\" DependentUpon=\"AppHead.xaml\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Base/base.props",
    "content": "<Project>\n  <ItemGroup>\n    <PackageReference Include=\"Uno.Resizetizer\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Include=\"$(MSBuildThisFileDirectory)AppHead.xaml\" />\n    <ApplicationDefinition Include=\"$(MSBuildThisFileDirectory)AppHead.xaml\"\n                SubType=\"Designer\"\n                XamlRuntime=\"WinUI\"\n                Generator=\"MSBuild:Compile\"\n                Link=\"AppHead.xaml\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)AppHead.xaml.cs\"\n        XamlRuntime=\"WinUI\"\n        DependentUpon=\"AppHead.xaml\"\n        Link=\"AppHead.xaml.cs\" />\n    <UnoIcon Include=\"$(MSBuildThisFileDirectory)Icons\\icon.svg\"\n        ForegroundFile=\"$(MSBuildThisFileDirectory)Icons\\icon_foreground.svg\"\n        ForegroundScale=\"0.65\"\n        Color=\"#00000000\" />\n    <UnoSplashScreen\n      Include=\"$(MSBuildThisFileDirectory)Splash\\splash_screen.svg\"\n      BaseSize=\"128,128\"\n      Color=\"#FFFFFF\" />\n    <!-- NOTE: Files explicitly linked to display in the head projects for clarity. -->\n    <None Include=\"$(MSBuildThisFileDirectory)Icons\\icon.svg\" Link=\"Icons\\icon.svg\" />\n    <None Include=\"$(MSBuildThisFileDirectory)Icons\\icon_foreground.svg\" Link=\"Icons\\icon_foreground.svg\" />\n    <None Include=\"$(MSBuildThisFileDirectory)Splash\\splash_screen.svg\" Link=\"Splash\\splash_screen.svg\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/App.xaml",
    "content": "﻿<?xml version = \"1.0\" encoding = \"UTF-8\" ?>\n<Application xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\n             xmlns:local=\"clr-namespace:IpcUno\"\n\t\t\t x:Class=\"IpcUno.MauiControls.App\">\n    <Application.Resources>\n        <ResourceDictionary>\n            <ResourceDictionary.MergedDictionaries>\n                <ResourceDictionary Source=\"Styles/Colors.xaml\" />\n                <ResourceDictionary Source=\"Styles/Styles.xaml\" />\n            </ResourceDictionary.MergedDictionaries>\n            <local:UnoImageConverter x:Key=\"UnoImageConverter\" />\n        </ResourceDictionary>\n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/App.xaml.cs",
    "content": "﻿namespace IpcUno.MauiControls\n{\n    public partial class App : Application\n    {\n        public App()\n        {\n            InitializeComponent();\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/AppBuilderExtensions.cs",
    "content": "namespace IpcUno\n{\n    public static class AppBuilderExtensions\n    {\n        public static MauiAppBuilder UseMauiControls(this MauiAppBuilder builder) =>\n            builder\n                .ConfigureFonts(fonts =>\n                {\n                    fonts.AddFont(\"IpcUno/Assets/Fonts/OpenSansRegular.ttf\", \"OpenSansRegular\");\n                    fonts.AddFont(\"IpcUno/Assets/Fonts/OpenSansSemibold.ttf\", \"OpenSansSemibold\");\n                });\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/EmbeddedControl.xaml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<ContentView xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\"\n             x:Class=\"IpcUno.MauiControls.EmbeddedControl\"\n             HorizontalOptions=\"Fill\"\n             VerticalOptions=\"Fill\">\n  <VerticalStackLayout\n    Spacing=\"25\"\n    Padding=\"30,0\"\n    VerticalOptions=\"Center\">\n\n    <Image\n      BindingContext=\"IpcUno/Assets/Images/dotnet_bot.png\"\n      Source=\"{Binding Converter={StaticResource UnoImageConverter}}\"\n      SemanticProperties.Description=\"Cute dot net bot waving hi to you!\"\n      HeightRequest=\"200\"\n      HorizontalOptions=\"Center\" />\n\n    <Label\n      Text=\"Hello, World!\"\n      SemanticProperties.HeadingLevel=\"Level1\"\n      FontSize=\"32\"\n      HorizontalOptions=\"Center\" />\n\n    <Label\n      Text=\"Welcome to .NET Multi-platform App UI\"\n      SemanticProperties.HeadingLevel=\"Level2\"\n      SemanticProperties.Description=\"Welcome to dot net Multi platform App U I\"\n      FontSize=\"18\"\n      HorizontalOptions=\"Center\" />\n\n    <Button\n      Text=\"{Binding CounterText}\"\n      SemanticProperties.Hint=\"Counts the number of times you click\"\n      Command=\"{Binding Counter}\"\n      HorizontalOptions=\"Center\" />\n\n  </VerticalStackLayout>\n</ContentView>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/EmbeddedControl.xaml.cs",
    "content": "namespace IpcUno.MauiControls\n{\n    public partial class EmbeddedControl : ContentView\n    {\n        public EmbeddedControl()\n        {\n            InitializeComponent();\n        }\n\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/IpcUno.MauiControls.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks Condition=\"$([MSBuild]::IsOSPlatform('windows')) or '$(EnableWindowsTargeting)' == 'true'\">$(TargetFrameworks);net8.0-windows10.0.19041</TargetFrameworks>\n    <TargetFrameworks>$(TargetFrameworks);net8.0;</TargetFrameworks>\n    <TargetFrameworks Condition=\"'$(OverrideTargetFrameworks)'!=''\">$(OverrideTargetFrameworks)</TargetFrameworks>\n\n    <UseMaui>true</UseMaui>\n    <SingleProject>true</SingleProject>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.Maui.Controls\" />\n    <PackageReference Include=\"Microsoft.Maui.Controls.Compatibility\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/Styles/Colors.xaml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<?xaml-comp compile=\"true\" ?>\n<ResourceDictionary \n    xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\">\n\n    <Color x:Key=\"Primary\">#512BD4</Color>\n    <Color x:Key=\"Secondary\">#DFD8F7</Color>\n    <Color x:Key=\"Tertiary\">#2B0B98</Color>\n    <Color x:Key=\"White\">White</Color>\n    <Color x:Key=\"Black\">Black</Color>\n    <Color x:Key=\"Gray100\">#E1E1E1</Color>\n    <Color x:Key=\"Gray200\">#C8C8C8</Color>\n    <Color x:Key=\"Gray300\">#ACACAC</Color>\n    <Color x:Key=\"Gray400\">#919191</Color>\n    <Color x:Key=\"Gray500\">#6E6E6E</Color>\n    <Color x:Key=\"Gray600\">#404040</Color>\n    <Color x:Key=\"Gray900\">#212121</Color>\n    <Color x:Key=\"Gray950\">#141414</Color>\n    <SolidColorBrush x:Key=\"PrimaryBrush\" Color=\"{StaticResource Primary}\"/>\n    <SolidColorBrush x:Key=\"SecondaryBrush\" Color=\"{StaticResource Secondary}\"/>\n    <SolidColorBrush x:Key=\"TertiaryBrush\" Color=\"{StaticResource Tertiary}\"/>\n    <SolidColorBrush x:Key=\"WhiteBrush\" Color=\"{StaticResource White}\"/>\n    <SolidColorBrush x:Key=\"BlackBrush\" Color=\"{StaticResource Black}\"/>\n    <SolidColorBrush x:Key=\"Gray100Brush\" Color=\"{StaticResource Gray100}\"/>\n    <SolidColorBrush x:Key=\"Gray200Brush\" Color=\"{StaticResource Gray200}\"/>\n    <SolidColorBrush x:Key=\"Gray300Brush\" Color=\"{StaticResource Gray300}\"/>\n    <SolidColorBrush x:Key=\"Gray400Brush\" Color=\"{StaticResource Gray400}\"/>\n    <SolidColorBrush x:Key=\"Gray500Brush\" Color=\"{StaticResource Gray500}\"/>\n    <SolidColorBrush x:Key=\"Gray600Brush\" Color=\"{StaticResource Gray600}\"/>\n    <SolidColorBrush x:Key=\"Gray900Brush\" Color=\"{StaticResource Gray900}\"/>\n    <SolidColorBrush x:Key=\"Gray950Brush\" Color=\"{StaticResource Gray950}\"/>\n\n    <Color x:Key=\"Yellow100Accent\">#F7B548</Color>\n    <Color x:Key=\"Yellow200Accent\">#FFD590</Color>\n    <Color x:Key=\"Yellow300Accent\">#FFE5B9</Color>\n    <Color x:Key=\"Cyan100Accent\">#28C2D1</Color>\n    <Color x:Key=\"Cyan200Accent\">#7BDDEF</Color>\n    <Color x:Key=\"Cyan300Accent\">#C3F2F4</Color>\n    <Color x:Key=\"Blue100Accent\">#3E8EED</Color>\n    <Color x:Key=\"Blue200Accent\">#72ACF1</Color>\n    <Color x:Key=\"Blue300Accent\">#A7CBF6</Color>\n\n</ResourceDictionary>"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/Styles/Styles.xaml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<?xaml-comp compile=\"true\" ?>\n<ResourceDictionary \n    xmlns=\"http://schemas.microsoft.com/dotnet/2021/maui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2009/xaml\">\n\n    <Style TargetType=\"ActivityIndicator\">\n        <Setter Property=\"Color\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n    </Style>\n\n    <Style TargetType=\"IndicatorView\">\n        <Setter Property=\"IndicatorColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}\"/>\n        <Setter Property=\"SelectedIndicatorColor\" Value=\"{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}\"/>\n    </Style>\n\n    <Style TargetType=\"Border\">\n        <Setter Property=\"Stroke\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}\" />\n        <Setter Property=\"StrokeShape\" Value=\"Rectangle\"/>\n        <Setter Property=\"StrokeThickness\" Value=\"1\"/>\n    </Style>\n\n    <Style TargetType=\"BoxView\">\n        <Setter Property=\"Color\" Value=\"{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}\" />\n    </Style>\n\n    <Style TargetType=\"Button\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Primary}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"CornerRadius\" Value=\"8\"/>\n        <Setter Property=\"Padding\" Value=\"14,10\"/>\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}\" />\n                            <Setter Property=\"BackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"CheckBox\">\n        <Setter Property=\"Color\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"Color\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"DatePicker\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"Editor\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\" />\n        <Setter Property=\"PlaceholderColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}\" />\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"Entry\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\" />\n        <Setter Property=\"PlaceholderColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}\" />\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"Frame\">\n        <Setter Property=\"HasShadow\" Value=\"False\" />\n        <Setter Property=\"BorderColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}\" />\n        <Setter Property=\"CornerRadius\" Value=\"8\" />\n    </Style>\n\n    <Style TargetType=\"ImageButton\">\n        <Setter Property=\"Opacity\" Value=\"1\" />\n        <Setter Property=\"BorderColor\" Value=\"Transparent\"/>\n        <Setter Property=\"BorderWidth\" Value=\"0\"/>\n        <Setter Property=\"CornerRadius\" Value=\"0\"/>\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"Opacity\" Value=\"0.5\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"Label\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\" />\n        <Setter Property=\"FontSize\" Value=\"14\" />\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"ListView\">\n        <Setter Property=\"SeparatorColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}\" />\n        <Setter Property=\"RefreshControlColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}\" />\n    </Style>\n\n    <Style TargetType=\"Picker\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}\" />\n        <Setter Property=\"TitleColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                            <Setter Property=\"TitleColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"ProgressBar\">\n        <Setter Property=\"ProgressColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"ProgressColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"RadioButton\">\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\"/>\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"RefreshView\">\n        <Setter Property=\"RefreshColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}\" />\n    </Style>\n\n    <Style TargetType=\"SearchBar\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}\" />\n        <Setter Property=\"PlaceholderColor\" Value=\"{StaticResource Gray500}\" />\n        <Setter Property=\"CancelButtonColor\" Value=\"{StaticResource Gray500}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\" />\n        <Setter Property=\"FontSize\" Value=\"14\" />\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                            <Setter Property=\"PlaceholderColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"SearchHandler\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}\" />\n        <Setter Property=\"PlaceholderColor\" Value=\"{StaticResource Gray500}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\" />\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\" />\n        <Setter Property=\"FontSize\" Value=\"14\" />\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                            <Setter Property=\"PlaceholderColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"Shadow\">\n        <Setter Property=\"Radius\" Value=\"15\" />\n        <Setter Property=\"Opacity\" Value=\"0.5\" />\n        <Setter Property=\"Brush\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}\" />\n        <Setter Property=\"Offset\" Value=\"10,10\" />\n    </Style>\n\n    <Style TargetType=\"Slider\">\n        <Setter Property=\"MinimumTrackColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"MaximumTrackColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}\" />\n        <Setter Property=\"ThumbColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"MinimumTrackColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\"/>\n                            <Setter Property=\"MaximumTrackColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\"/>\n                            <Setter Property=\"ThumbColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\"/>\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"SwipeItem\">\n        <Setter Property=\"BackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}\" />\n    </Style>\n\n    <Style TargetType=\"Switch\">\n        <Setter Property=\"OnColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"ThumbColor\" Value=\"{StaticResource White}\" />\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"OnColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                            <Setter Property=\"ThumbColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                    <VisualState x:Name=\"On\">\n                        <VisualState.Setters>\n                            <Setter Property=\"OnColor\" Value=\"{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}\" />\n                            <Setter Property=\"ThumbColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                    <VisualState x:Name=\"Off\">\n                        <VisualState.Setters>\n                            <Setter Property=\"ThumbColor\" Value=\"{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"TimePicker\">\n        <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}\" />\n        <Setter Property=\"BackgroundColor\" Value=\"Transparent\"/>\n        <Setter Property=\"FontFamily\" Value=\"OpenSansRegular\"/>\n        <Setter Property=\"FontSize\" Value=\"14\"/>\n        <Setter Property=\"MinimumHeightRequest\" Value=\"44\"/>\n        <Setter Property=\"MinimumWidthRequest\" Value=\"44\"/>\n        <Setter Property=\"VisualStateManager.VisualStateGroups\">\n            <VisualStateGroupList>\n                <VisualStateGroup x:Name=\"CommonStates\">\n                    <VisualState x:Name=\"Normal\" />\n                    <VisualState x:Name=\"Disabled\">\n                        <VisualState.Setters>\n                            <Setter Property=\"TextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}\" />\n                        </VisualState.Setters>\n                    </VisualState>\n                </VisualStateGroup>\n            </VisualStateGroupList>\n        </Setter>\n    </Style>\n\n    <Style TargetType=\"Page\" ApplyToDerivedTypes=\"True\">\n        <Setter Property=\"Padding\" Value=\"0\"/>\n        <Setter Property=\"BackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}\" />\n    </Style>\n\n    <Style TargetType=\"Shell\" ApplyToDerivedTypes=\"True\">\n        <Setter Property=\"Shell.BackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}\" />\n        <Setter Property=\"Shell.ForegroundColor\" Value=\"{OnPlatform WinUI={StaticResource Primary}, Default={StaticResource White}}\" />\n        <Setter Property=\"Shell.TitleColor\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource White}}\" />\n        <Setter Property=\"Shell.DisabledColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}\" />\n        <Setter Property=\"Shell.UnselectedColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}\" />\n        <Setter Property=\"Shell.NavBarHasShadow\" Value=\"False\" />\n        <Setter Property=\"Shell.TabBarBackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}\" />\n        <Setter Property=\"Shell.TabBarForegroundColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"Shell.TabBarTitleColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"Shell.TabBarUnselectedColor\" Value=\"{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}\" />\n    </Style>\n\n    <Style TargetType=\"NavigationPage\">\n        <Setter Property=\"BarBackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource Gray950}}\" />\n        <Setter Property=\"BarTextColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}\" />\n        <Setter Property=\"IconColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}\" />\n    </Style>\n\n    <Style TargetType=\"TabbedPage\">\n        <Setter Property=\"BarBackgroundColor\" Value=\"{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}\" />\n        <Setter Property=\"BarTextColor\" Value=\"{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}\" />\n        <Setter Property=\"UnselectedTabColor\" Value=\"{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}\" />\n        <Setter Property=\"SelectedTabColor\" Value=\"{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}\" />\n    </Style>\n\n</ResourceDictionary>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.MauiControls/UnoImageConverter.cs",
    "content": "using System.Globalization;\n\nnamespace IpcUno\n{\n    public class UnoImageConverter : IValueConverter\n    {\n        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)\n        {\n#if ANDROID\n        return (value + \"\").Replace('/','_').Replace('\\\\','_');\n#else\n            return value;\n#endif\n        }\n\n        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.Gtk/IpcUno.Skia.Gtk.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType Condition=\"'$(Configuration)'=='Release'\">WinExe</OutputType>\n    <OutputType Condition=\"'$(Configuration)'=='Debug'\">Exe</OutputType>\n    <TargetFramework>net8.0</TargetFramework>\n    <ApplicationManifest>app.manifest</ApplicationManifest>\n  </PropertyGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Package.appxmanifest\" />\n    <Manifest Include=\"$(ApplicationManifest)\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Uno.WinUI.Skia.Gtk\" />\n    <PackageReference Include=\"CommunityToolkit.Mvvm\" />\n    <PackageReference Include=\"Uno.Extensions.Configuration\" />\n    <PackageReference Include=\"Uno.Extensions.Logging.WinUI\" />\n    <PackageReference Include=\"Uno.Material.WinUI\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI.Material\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Hosting.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.WinUI\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Console\" />\n    <PackageReference Include=\"SkiaSharp.Views.Uno.WinUI\" />\n    <PackageReference Include=\"SkiaSharp.Skottie\" />\n    <PackageReference Include=\"Uno.WinUI.DevServer\" Condition=\"'$(Configuration)'=='Debug'\" />\n    <PackageReference Include=\"Uno.UI.Adapter.Microsoft.Extensions.Logging\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IpcUno\\IpcUno.csproj\" />\n  </ItemGroup>\n  <Import Project=\"..\\IpcUno.Base\\base.props\" />\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.Gtk/Package.appxmanifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<Package\n  xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"\n  xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"\n  xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"\n  IgnorableNamespaces=\"uap rescap\">\n\n  <Identity\n    Name=\"IpcUno\"\n    Publisher=\"O=lindexi\"\n    Version=\"1.0.0.0\" />\n\n  <Properties>\n    <DisplayName>IpcUno</DisplayName>\n    <PublisherDisplayName>IpcUno</PublisherDisplayName>\n  </Properties>\n\n  <Dependencies>\n    <TargetDeviceFamily Name=\"Windows.Universal\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.19041.0\" />\n    <TargetDeviceFamily Name=\"Windows.Desktop\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.19041.0\" />\n  </Dependencies>\n\n  <Resources>\n    <Resource Language=\"x-generate\"/>\n  </Resources>\n\n  <Applications>\n    <Application Id=\"App\"\n      Executable=\"$targetnametoken$.exe\"\n      EntryPoint=\"$targetentrypoint$\">\n      <uap:VisualElements\n        DisplayName=\"IpcUno\"\n        Description=\"IpcUno\">\n        <uap:SplashScreen />\n      </uap:VisualElements>\n    </Application>\n  </Applications>\n\n  <Capabilities>\n    <rescap:Capability Name=\"runFullTrust\" />\n  </Capabilities>\n</Package>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.Gtk/Program.cs",
    "content": "using System;\n\nusing GLib;\n\nusing Uno.UI.Runtime.Skia.Gtk;\n\nnamespace IpcUno.Skia.Gtk\n{\n    public class Program\n    {\n        public static void Main(string[] args)\n        {\n            ExceptionManager.UnhandledException += delegate (UnhandledExceptionArgs expArgs)\n            {\n                Console.WriteLine(\"GLIB UNHANDLED EXCEPTION\" + expArgs.ExceptionObject.ToString());\n                expArgs.ExitApplication = true;\n            };\n\n            var host = new GtkHost(() => new AppHead());\n            // 修复虚拟机界面闪烁\n            host.RenderSurfaceType = RenderSurfaceType.Software;\n\n            host.Run();\n        }\n    }\n}\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.Gtk/app.manifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"IpcUno.Gtk\"/>\n  <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n    <security>\n      <requestedPrivileges xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n        <!-- UAC Manifest Options\n             If you want to change the Windows User Account Control level replace the\n             requestedExecutionLevel node with one of the following.\n\n        <requestedExecutionLevel  level=\"asInvoker\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"requireAdministrator\" uiAccess=\"false\" />\n        <requestedExecutionLevel  level=\"highestAvailable\" uiAccess=\"false\" />\n\n            Specifying requestedExecutionLevel element will disable file and registry virtualization.\n            Remove this element if your application requires this virtualization for backwards\n            compatibility.\n        -->\n        <requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" />\n      </requestedPrivileges>\n    </security>\n  </trustInfo>\n\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- A list of the Windows versions that this application has been tested on\n           and is designed to work with. Uncomment the appropriate elements\n           and Windows will automatically select the most compatible environment. -->\n\n      <!-- Windows Vista -->\n      <!--<supportedOS Id=\"{e2011457-1546-43c5-a5fe-008deee3d3f0}\" />-->\n\n      <!-- Windows 7 -->\n      <!--<supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\" />-->\n\n      <!-- Windows 8 -->\n      <!--<supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" />-->\n\n      <!-- Windows 8.1 -->\n      <!--<supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\" />-->\n\n      <!-- Windows 10 -->\n      <!--<supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" />-->\n\n    </application>\n  </compatibility>\n\n  <!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher\n       DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need\n       to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should\n       also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->\n\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/PM</dpiAware>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness>\n    </windowsSettings>\n  </application>\n\n\n  <!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->\n  <!--\n  <dependency>\n    <dependentAssembly>\n      <assemblyIdentity\n          type=\"win32\"\n          name=\"Microsoft.Windows.Common-Controls\"\n          version=\"6.0.0.0\"\n          processorArchitecture=\"*\"\n          publicKeyToken=\"6595b64144ccf1df\"\n          language=\"*\"\n        />\n    </dependentAssembly>\n  </dependency>\n  -->\n\n</assembly>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.WPF/IpcUno.Skia.WPF.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType Condition=\"'$(Configuration)'=='Release'\">WinExe</OutputType>\n    <OutputType Condition=\"'$(Configuration)'=='Debug'\">Exe</OutputType>\n    <TargetFramework>net8.0-windows</TargetFramework>\n    <UseWPF>true</UseWPF>\n    <ApplicationManifest>app.manifest</ApplicationManifest>\n  </PropertyGroup>\n  <ItemGroup Label=\"AssemblyInfo\">\n    <AssemblyAttribute Include=\"System.Runtime.InteropServices.ComVisibleAttribute\">\n      <_Parameter1>false</_Parameter1>\n    </AssemblyAttribute>\n    <AssemblyAttribute Include=\"System.Windows.ThemeInfo\">\n      <_Parameter1>System.Windows.ResourceDictionaryLocation.None</_Parameter1>\n      <_Parameter1_IsLiteral>true</_Parameter1_IsLiteral>\n      <_Parameter2>System.Windows.ResourceDictionaryLocation.SourceAssembly</_Parameter2>\n      <_Parameter2_IsLiteral>true</_Parameter2_IsLiteral>\n    </AssemblyAttribute>\n  </ItemGroup>\n  <ItemGroup>\n    <EmbeddedResource Include=\"Package.appxmanifest\" />\n    <Manifest Include=\"$(ApplicationManifest)\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Uno.WinUI.Skia.Wpf\" />\n    <PackageReference Include=\"CommunityToolkit.Mvvm\" />\n    <PackageReference Include=\"Uno.Extensions.Configuration\" />\n    <PackageReference Include=\"Uno.Extensions.Logging.WinUI\" />\n    <PackageReference Include=\"Uno.Material.WinUI\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI.Material\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Hosting.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.WinUI\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Console\" />\n    <PackageReference Include=\"SkiaSharp.Views.Uno.WinUI\" />\n    <PackageReference Include=\"SkiaSharp.Skottie\" />\n    <PackageReference Include=\"Uno.WinUI.DevServer\" Condition=\"'$(Configuration)'=='Debug'\" />\n    <PackageReference Include=\"Uno.UI.Adapter.Microsoft.Extensions.Logging\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ApplicationDefinition Include=\"Wpf\\App.xaml\" XamlRuntime=\"Wpf\" />\n  </ItemGroup>\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IpcUno\\IpcUno.csproj\" />\n  </ItemGroup>\n  <Import Project=\"..\\IpcUno.Base\\base.props\" />\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.WPF/Package.appxmanifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<Package\n  xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"\n  xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"\n  xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"\n  IgnorableNamespaces=\"uap rescap\">\n\n  <Identity\n    Name=\"IpcUno\"\n    Publisher=\"O=lindexi\"\n    Version=\"1.0.0.0\" />\n\n  <Properties>\n    <DisplayName>IpcUno</DisplayName>\n    <PublisherDisplayName>IpcUno</PublisherDisplayName>\n  </Properties>\n\n  <Dependencies>\n    <TargetDeviceFamily Name=\"Windows.Universal\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.19041.0\" />\n    <TargetDeviceFamily Name=\"Windows.Desktop\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.19041.0\" />\n  </Dependencies>\n\n  <Resources>\n    <Resource Language=\"x-generate\"/>\n  </Resources>\n\n  <Applications>\n    <Application Id=\"App\"\n      Executable=\"$targetnametoken$.exe\"\n      EntryPoint=\"$targetentrypoint$\">\n      <uap:VisualElements\n        DisplayName=\"IpcUno\"\n        Description=\"IpcUno\">\n        <uap:SplashScreen />\n      </uap:VisualElements>\n    </Application>\n  </Applications>\n\n  <Capabilities>\n    <rescap:Capability Name=\"runFullTrust\" />\n  </Capabilities>\n</Package>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.WPF/Wpf/App.xaml",
    "content": "﻿<Application x:Class=\"IpcUno.WPF.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:IpcUno.WPF\">\n  <Application.Resources>\n\n  </Application.Resources>\n</Application>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.WPF/Wpf/App.xaml.cs",
    "content": "using Uno.UI.Runtime.Skia.Wpf;\n\nusing WpfApp = System.Windows.Application;\n\nnamespace IpcUno.WPF\n{\n    public partial class App : WpfApp\n    {\n        public App()\n        {\n            var host = new WpfHost(Dispatcher, () => new AppHead());\n            host.Run();\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Skia.WPF/app.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"IpcUno.Skia.WPF.app\"/>\n\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8.\n      For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1\n\n      It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" />\n    </application>\n  </compatibility>\n\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <!-- The combination of below two tags have the following effect:\n           1) Per-Monitor for >= Windows 10 Anniversary Update\n           2) System < Windows 10 Anniversary Update\n      -->\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/PM</dpiAware>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness>\n    </windowsSettings>\n  </application>\n</assembly>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Tests/AppInfoTests.cs",
    "content": "namespace IpcUno.Tests\n{\n    public class AppInfoTests\n    {\n        [SetUp]\n        public void Setup()\n        {\n        }\n\n        [Test]\n        public void AppInfoCreation()\n        {\n            var appInfo = new AppConfig { Environment = \"Test\" };\n\n            appInfo.Should().NotBeNull();\n            appInfo.Environment.Should().Be(\"Test\");\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Tests/GlobalUsings.cs",
    "content": "global using FluentAssertions;\n\nglobal using IpcUno.Business.Models;\n\nglobal using NUnit.Framework;\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Tests/IpcUno.Tests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"FluentAssertions\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" />\n    <PackageReference Include=\"NUnit\" />\n    <PackageReference Include=\"NUnit3TestAdapter\" />\n    <PackageReference Include=\"coverlet.collector\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IpcUno\\IpcUno.csproj\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.UITests/Constants.cs",
    "content": "namespace IpcUno.UITests\n{\n    public class Constants\n    {\n        public readonly static string WebAssemblyDefaultUri = \"http://localhost:5001/\";\n        public readonly static string iOSAppName = \"com.companyname.IpcUno\";\n        public readonly static string AndroidAppName = \"com.companyname.IpcUno\";\n        public readonly static string iOSDeviceNameOrId = \"iPad Pro (12.9-inch) (3rd generation)\";\n\n        public readonly static Platform CurrentPlatform = Platform.Browser;\n        public readonly static Browser WebAssemblyBrowser = Browser.Chrome;\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.UITests/Given_MainPage.cs",
    "content": "namespace IpcUno.UITests\n{\n    public class Given_MainPage : TestBase\n    {\n        [Test]\n        public async Task When_SmokeTest()\n        {\n            // NOTICE\n            // To run UITests, Run the WASM target without debugger. Note\n            // the port that is being used and update the Constants.cs file\n            // in the UITests project with the correct port number.\n\n            // Add delay to allow for the splash screen to disappear\n            await Task.Delay(5000);\n\n\n            // Query for the SecondPageButton and then tap it\n            Query xamlButton = q => q.All().Marked(\"SecondPageButton\");\n            App.WaitForElement(xamlButton);\n            App.Tap(xamlButton);\n\n            // Take a screenshot and add it to the test results\n            TakeScreenshot(\"After tapped\");\n        }\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.UITests/GlobalUsings.cs",
    "content": "global using NUnit.Framework;\n\nglobal using Uno.UITest;\nglobal using Uno.UITest.Helpers.Queries;\nglobal using Uno.UITests.Helpers;\n\nglobal using Query = System.Func<Uno.UITest.IAppQuery, Uno.UITest.IAppQuery>;\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.UITests/IpcUno.UITests.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net8.0</TargetFramework>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"FluentAssertions\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" />\n    <PackageReference Include=\"Newtonsoft.Json\" />\n    <PackageReference Include=\"NUnit\" />\n    <PackageReference Include=\"NUnit3TestAdapter\" />\n    <PackageReference Include=\"Uno.UITest.Helpers\" />\n    <PackageReference Include=\"Xamarin.UITest\" />\n  </ItemGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.UITests/TestBase.cs",
    "content": "\nnamespace IpcUno.UITests\n{\n    public class TestBase\n    {\n        private IApp? _app;\n\n        static TestBase()\n        {\n            AppInitializer.TestEnvironment.AndroidAppName = Constants.AndroidAppName;\n            AppInitializer.TestEnvironment.WebAssemblyDefaultUri = Constants.WebAssemblyDefaultUri;\n            AppInitializer.TestEnvironment.iOSAppName = Constants.iOSAppName;\n            AppInitializer.TestEnvironment.AndroidAppName = Constants.AndroidAppName;\n            AppInitializer.TestEnvironment.iOSDeviceNameOrId = Constants.iOSDeviceNameOrId;\n            AppInitializer.TestEnvironment.CurrentPlatform = Constants.CurrentPlatform;\n            AppInitializer.TestEnvironment.WebAssemblyBrowser = Constants.WebAssemblyBrowser;\n\n#if DEBUG\n        AppInitializer.TestEnvironment.WebAssemblyHeadless = false;\n#endif\n\n            // Start the app only once, so the tests runs don't restart it\n            // and gain some time for the tests.\n            AppInitializer.ColdStartApp();\n        }\n\n        protected IApp App\n        {\n            get => _app!;\n            private set\n            {\n                _app = value;\n                Uno.UITest.Helpers.Queries.Helpers.App = value;\n            }\n        }\n\n        [SetUp]\n        public void SetUpTest()\n        {\n            App = AppInitializer.AttachToApp();\n        }\n\n        [TearDown]\n        public void TearDownTest()\n        {\n            TakeScreenshot(\"teardown\");\n        }\n\n        public FileInfo TakeScreenshot(string stepName)\n        {\n            var title = $\"{TestContext.CurrentContext.Test.Name}_{stepName}\"\n                .Replace(\" \", \"_\")\n                .Replace(\".\", \"_\");\n\n            var fileInfo = App.Screenshot(title);\n\n            var fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileInfo.Name);\n            if (fileNameWithoutExt != title && fileInfo.DirectoryName != null)\n            {\n                var destFileName = Path\n                    .Combine(fileInfo.DirectoryName, title + Path.GetExtension(fileInfo.Name));\n\n                if (File.Exists(destFileName))\n                {\n                    File.Delete(destFileName);\n                }\n\n                File.Move(fileInfo.FullName, destFileName);\n\n                TestContext.AddTestAttachment(destFileName, stepName);\n\n                fileInfo = new FileInfo(destFileName);\n            }\n            else\n            {\n                TestContext.AddTestAttachment(fileInfo.FullName, stepName);\n            }\n\n            return fileInfo;\n        }\n\n    }\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/IpcUno.Windows.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>\n    <RootNamespace>IpcUno.Windows</RootNamespace>\n    <ApplicationManifest>app.manifest</ApplicationManifest>\n    <Platforms>x86;x64;arm64</Platforms>\n    <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>\n    <PublishProfile>win-$(Platform).pubxml</PublishProfile>\n    <UseWinUI>true</UseWinUI>\n    <EnableMsixTooling>true</EnableMsixTooling>\n\n    <!-- Bundles the WinAppSDK binaries -->\n    <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>\n\n    <!-- <SelfContained>true</SelfContained> -->\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Content Include=\"Images\\**\" />\n    <Manifest Include=\"$(ApplicationManifest)\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Uno.WinUI\" />\n    <PackageReference Include=\"Microsoft.WindowsAppSDK\" VersionOverride=\"1.4.231008000\" />\n    <PackageReference Include=\"Microsoft.Windows.SDK.BuildTools\" VersionOverride=\"10.0.22621.756\" />\n    <PackageReference Include=\"CommunityToolkit.Mvvm\" />\n    <PackageReference Include=\"Uno.Extensions.Configuration\" />\n    <PackageReference Include=\"Uno.Extensions.Logging.WinUI\" />\n    <PackageReference Include=\"Uno.Material.WinUI\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI.Material\" />\n    <PackageReference Include=\"Uno.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Hosting.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.Toolkit.WinUI\" />\n    <PackageReference Include=\"Uno.Extensions.Navigation.WinUI\" />\n    <PackageReference Include=\"Microsoft.Extensions.Logging.Console\" />\n    <PackageReference Include=\"Uno.Core.Extensions.Logging.Singleton\" />\n    <PackageReference Include=\"Uno.UI.Adapter.Microsoft.Extensions.Logging\" />\n    <PackageReference Include=\"Microsoft.Maui.Controls\" />\n    <PackageReference Include=\"Microsoft.Maui.Controls.Compatibility\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <!--\n    If you encounter this error message:\n\n      error NETSDK1148: A referenced assembly was compiled using a newer version of Microsoft.Windows.SDK.NET.dll.\n      Please update to a newer .NET SDK in order to reference this assembly.\n\n    This means that the two packages below must be aligned with the \"build\" version number of\n    the \"Microsoft.Windows.SDK.BuildTools\" package above, and the \"revision\" version number\n    must be the highest found in https://www.nuget.org/packages/Microsoft.Windows.SDK.NET.Ref.\n    -->\n    <!-- <FrameworkReference Update=\"Microsoft.Windows.SDK.NET.Ref\" RuntimeFrameworkVersion=\"10.0.22621.28\" />\n    <FrameworkReference Update=\"Microsoft.Windows.SDK.NET.Ref\" TargetingPackVersion=\"10.0.22621.28\" /> -->\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\IpcUno\\IpcUno.csproj\" />\n  </ItemGroup>\n\n  <!--\n    Defining the \"Msix\" ProjectCapability here allows the Single-project MSIX Packaging\n    Tools extension to be activated for this project even if the Windows App SDK Nuget\n    package has not yet been restored.\n  -->\n  <ItemGroup Condition=\"'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'\">\n    <ProjectCapability Include=\"Msix\"/>\n  </ItemGroup>\n\n  <!--\n    Defining the \"HasPackageAndPublishMenuAddedByProject\" property here allows the Solution\n    Explorer \"Package and Publish\" context menu entry to be enabled for this project even if\n    the Windows App SDK Nuget package has not yet been restored.\n  -->\n  <PropertyGroup Condition=\"'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'\">\n    <HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>\n  </PropertyGroup>\n\n  <Import Project=\"..\\IpcUno.Base\\base.props\" />\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/Package.appxmanifest",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<Package\n  xmlns=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10\"\n  xmlns:uap=\"http://schemas.microsoft.com/appx/manifest/uap/windows10\"\n  xmlns:rescap=\"http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities\"\n  IgnorableNamespaces=\"uap rescap\">\n\n  <Identity\n    Name=\"IpcUno\"\n    Publisher=\"O=lindexi\"\n    Version=\"1.0.0.0\" />\n\n  <Properties>\n    <DisplayName>IpcUno</DisplayName>\n    <PublisherDisplayName>IpcUno</PublisherDisplayName>\n  </Properties>\n\n  <Dependencies>\n    <TargetDeviceFamily Name=\"Windows.Universal\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.19041.0\" />\n    <TargetDeviceFamily Name=\"Windows.Desktop\" MinVersion=\"10.0.17763.0\" MaxVersionTested=\"10.0.19041.0\" />\n  </Dependencies>\n\n  <Resources>\n    <Resource Language=\"x-generate\"/>\n  </Resources>\n\n  <Applications>\n    <Application Id=\"App\"\n      Executable=\"$targetnametoken$.exe\"\n      EntryPoint=\"$targetentrypoint$\">\n      <uap:VisualElements\n        DisplayName=\"IpcUno\"\n        Description=\"IpcUno\">\n        <uap:SplashScreen />\n      </uap:VisualElements>\n    </Application>\n  </Applications>\n\n  <Capabilities>\n    <rescap:Capability Name=\"runFullTrust\" />\n  </Capabilities>\n</Package>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/Properties/PublishProfiles/win-arm64.pubxml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121.\n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n\t<PropertyGroup>\n\t\t<PublishProtocol>FileSystem</PublishProtocol>\n\t\t<Platform>arm64</Platform>\n\t\t<RuntimeIdentifier>win-arm64</RuntimeIdentifier>\n\t\t<PublishDir>bin\\$(Configuration)\\$(TargetFramework)\\$(RuntimeIdentifier)\\publish\\</PublishDir>\n\t\t<SelfContained>true</SelfContained>\n\t\t<PublishSingleFile>False</PublishSingleFile>\n\t\t<PublishReadyToRun Condition=\"'$(Configuration)' == 'Debug'\">False</PublishReadyToRun>\n\t\t<PublishReadyToRun Condition=\"'$(Configuration)' != 'Debug'\">True</PublishReadyToRun>\n\t\t<!-- Note: Trimming disabled by default as there may still be an issues with PublishTrimmed support: https://github.com/microsoft/CsWinRT/issues/373 -->\n\t\t<!-- \n\t\t<PublishTrimmed Condition=\"'$(Configuration)' != 'Debug'\">True</PublishTrimmed>\n\t\t<TrimMode>partial</TrimMode>\n\t\t<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings> \n\t\t-->\n\t</PropertyGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/Properties/PublishProfiles/win-x64.pubxml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121.\n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n\t<PropertyGroup>\n\t\t<PublishProtocol>FileSystem</PublishProtocol>\n\t\t<Platform>x64</Platform>\n\t\t<RuntimeIdentifier>win-x64</RuntimeIdentifier>\n\t\t<PublishDir>bin\\$(Configuration)\\$(TargetFramework)\\$(RuntimeIdentifier)\\publish\\</PublishDir>\n\t\t<SelfContained>true</SelfContained>\n\t\t<PublishSingleFile>False</PublishSingleFile>\n\t\t<PublishReadyToRun Condition=\"'$(Configuration)' == 'Debug'\">False</PublishReadyToRun>\n\t\t<PublishReadyToRun Condition=\"'$(Configuration)' != 'Debug'\">True</PublishReadyToRun>\n\t\t<!-- Note: Trimming disabled by default as there may still be an issues with PublishTrimmed support: https://github.com/microsoft/CsWinRT/issues/373 -->\n\t\t<!-- \n\t\t<PublishTrimmed Condition=\"'$(Configuration)' != 'Debug'\">True</PublishTrimmed>\n\t\t<TrimMode>partial</TrimMode>\n\t\t<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings> \n\t\t-->\n\t</PropertyGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/Properties/PublishProfiles/win-x86.pubxml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121.\n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n\t<PropertyGroup>\n\t\t<PublishProtocol>FileSystem</PublishProtocol>\n\t\t<Platform>x86</Platform>\n\t\t<RuntimeIdentifier>win-x86</RuntimeIdentifier>\n\t\t<PublishDir>bin\\$(Configuration)\\$(TargetFramework)\\$(RuntimeIdentifier)\\publish\\</PublishDir>\n\t\t<SelfContained>true</SelfContained>\n\t\t<PublishSingleFile>False</PublishSingleFile>\n\t\t<PublishReadyToRun Condition=\"'$(Configuration)' == 'Debug'\">False</PublishReadyToRun>\n\t\t<PublishReadyToRun Condition=\"'$(Configuration)' != 'Debug'\">True</PublishReadyToRun>\n\t\t<!-- Note: Trimming disabled by default as there may still be an issues with PublishTrimmed support: https://github.com/microsoft/CsWinRT/issues/373 -->\n\t\t<!-- \n\t\t<PublishTrimmed Condition=\"'$(Configuration)' != 'Debug'\">True</PublishTrimmed>\n\t\t<TrimMode>partial</TrimMode>\n\t\t<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings> \n\t\t-->\n\t</PropertyGroup>\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/Properties/launchsettings.json",
    "content": "{\n\t\"profiles\": {\n\t\t\"IpcUno.Windows (Unpackaged)\": {\n\t\t\t\"commandName\": \"Project\"\n\t\t},\n\t\t\"IpcUno.Windows (Package)\": {\n\t\t\t\"commandName\": \"MsixPackage\"\n\t\t}\n\t}\n}"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/Resources.lang-en-us.resw",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"Placeholder\" xml:space=\"preserve\">\n    <value>Hello World!</value>\n  </data>\n</root>"
  },
  {
    "path": "demo/UnoDemo/IpcUno/IpcUno.Windows/app.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <assemblyIdentity version=\"1.0.0.0\" name=\"IpcUno.Windows.app\"/>\n\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8.\n      For more info see https://docs.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1\n\n      It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\" />\n    </application>\n  </compatibility>\n\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <!-- The combination of below two tags have the following effect:\n           1) Per-Monitor for >= Windows 10 Anniversary Update\n           2) System < Windows 10 Anniversary Update\n      -->\n      <dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/PM</dpiAware>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness>\n    </windowsSettings>\n  </application>\n</assembly>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno/solution-config.props.sample",
    "content": "<Project>\n    <!--\n        This file is used to control the platforms compiled by visual studio, and\n            allow for a faster build when testing for a single platform.\n\n            Instructions:\n            1) Copy this file and remove the \".sample\" name\n            2) Uncomment and adjust the properties below\n            3) Make sure to do a Rebuild, so that nuget restores the proper packages for the new target\n    -->\n\n    <PropertyGroup>\n        <!-- Uncomment each line for each platform that you want to build: -->\n\n        <!-- <OverrideTargetFrameworks Condition=\"''!='hint: Windows App Sdk (WinUI)'\">$(OverrideTargetFrameworks);net8.0-windows10.0.19041.0</OverrideTargetFrameworks> -->\n        <!-- <OverrideTargetFrameworks Condition=\"''!='hint: WASM, Skia'\">$(OverrideTargetFrameworks);net8.0</OverrideTargetFrameworks> -->\n        <!-- <OverrideTargetFrameworks Condition=\"''!='hint: .NET 6.0 iOS'\">$(OverrideTargetFrameworks);net8.0-ios</OverrideTargetFrameworks> -->\n        <!-- <OverrideTargetFrameworks Condition=\"''!='hint: .NET 6.0 Android'\">$(OverrideTargetFrameworks);net8.0-android</OverrideTargetFrameworks> -->\n        <!-- <OverrideTargetFrameworks Condition=\"''!='hint: .NET 6.0 macOS Catalyst'\">$(OverrideTargetFrameworks);net8.0-maccatalyst</OverrideTargetFrameworks> -->\n    </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/UnoDemo/IpcUno.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.7.34221.43\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Source\", \"Source\", \"{1B6E0487-7207-4102-9E46-14787F078E86}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Platforms\", \"Platforms\", \"{0FBA80CA-0A69-4C57-93AF-67B4915C8EF7}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Tests\", \"Tests\", \"{3AB30A61-B6F3-4649-A1B3-93D204916A45}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.MauiControls\", \"IpcUno\\IpcUno.MauiControls\\IpcUno.MauiControls.csproj\", \"{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno\", \"IpcUno\\IpcUno\\IpcUno.csproj\", \"{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.Tests\", \"IpcUno\\IpcUno.Tests\\IpcUno.Tests.csproj\", \"{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.UITests\", \"IpcUno\\IpcUno.UITests\\IpcUno.UITests.csproj\", \"{80BBDA19-1F4E-4FFC-A900-A69D020CA361}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.Base\", \"IpcUno\\IpcUno.Base\\IpcUno.Base.csproj\", \"{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.Windows\", \"IpcUno\\IpcUno.Windows\\IpcUno.Windows.csproj\", \"{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.Skia.Gtk\", \"IpcUno\\IpcUno.Skia.Gtk\\IpcUno.Skia.Gtk.csproj\", \"{739B0B13-14CF-477D-B809-7E4A45B6CBBF}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcUno.Skia.Wpf\", \"IpcUno\\IpcUno.Skia.WPF\\IpcUno.Skia.Wpf.csproj\", \"{488A069D-3F83-4DA6-95D5-36E08FC6E37F}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{E3B5F737-6DB2-4859-A2B0-BB8FB459E854}\"\n\tProjectSection(SolutionItems) = preProject\n\t\tIpcUno\\.gitignore = IpcUno\\.gitignore\n\t\tIpcUno\\Directory.Build.props = IpcUno\\Directory.Build.props\n\t\tIpcUno\\Directory.Build.targets = IpcUno\\Directory.Build.targets\n\t\tIpcUno\\Directory.Packages.props = IpcUno\\Directory.Packages.props\n\t\tIpcUno\\solution-config.props.sample = IpcUno\\solution-config.props.sample\n\tEndProjectSection\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|arm64 = Debug|arm64\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|arm64 = Release|arm64\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|x64.Build.0 = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E}.Release|x86.Build.0 = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|x64.Build.0 = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C}.Release|x86.Build.0 = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|x64.Build.0 = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE}.Release|x86.Build.0 = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|x64.Build.0 = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361}.Release|x86.Build.0 = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|x64.Build.0 = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6}.Release|x86.Build.0 = Release|Any CPU\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|Any CPU.ActiveCfg = Debug|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|Any CPU.Build.0 = Debug|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|Any CPU.Deploy.0 = Debug|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|arm64.ActiveCfg = Debug|arm64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|arm64.Build.0 = Debug|arm64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|arm64.Deploy.0 = Debug|arm64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|x64.ActiveCfg = Debug|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|x64.Build.0 = Debug|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|x64.Deploy.0 = Debug|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|x86.ActiveCfg = Debug|x86\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|x86.Build.0 = Debug|x86\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Debug|x86.Deploy.0 = Debug|x86\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|Any CPU.ActiveCfg = Release|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|Any CPU.Build.0 = Release|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|Any CPU.Deploy.0 = Release|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|arm64.ActiveCfg = Release|arm64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|arm64.Build.0 = Release|arm64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|arm64.Deploy.0 = Release|arm64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|x64.ActiveCfg = Release|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|x64.Build.0 = Release|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|x64.Deploy.0 = Release|x64\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|x86.ActiveCfg = Release|x86\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|x86.Build.0 = Release|x86\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54}.Release|x86.Deploy.0 = Release|x86\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|x64.Build.0 = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF}.Release|x86.Build.0 = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|arm64.ActiveCfg = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|arm64.Build.0 = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|arm64.ActiveCfg = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|arm64.Build.0 = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|x64.Build.0 = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{0FBA80CA-0A69-4C57-93AF-67B4915C8EF7} = {1B6E0487-7207-4102-9E46-14787F078E86}\n\t\t{620029A5-505D-4CCC-A90F-E7AD7DEB1F4E} = {1B6E0487-7207-4102-9E46-14787F078E86}\n\t\t{D655B8FA-AC61-4EF0-92DE-63D2A2E6D62C} = {1B6E0487-7207-4102-9E46-14787F078E86}\n\t\t{4AD964C6-6AC0-4BDC-BDDE-9FF6820D30AE} = {3AB30A61-B6F3-4649-A1B3-93D204916A45}\n\t\t{80BBDA19-1F4E-4FFC-A900-A69D020CA361} = {3AB30A61-B6F3-4649-A1B3-93D204916A45}\n\t\t{10B546E6-7B7E-4A11-95B5-FE5E417AF0F6} = {0FBA80CA-0A69-4C57-93AF-67B4915C8EF7}\n\t\t{0495F020-EFEF-4D9D-88ED-0E8A9DCB8B54} = {0FBA80CA-0A69-4C57-93AF-67B4915C8EF7}\n\t\t{739B0B13-14CF-477D-B809-7E4A45B6CBBF} = {0FBA80CA-0A69-4C57-93AF-67B4915C8EF7}\n\t\t{488A069D-3F83-4DA6-95D5-36E08FC6E37F} = {0FBA80CA-0A69-4C57-93AF-67B4915C8EF7}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {65F2C17B-2D6F-4A29-A6BD-006ADAC74758}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "demo/UnoDemo/README.md",
    "content": "﻿# IpcUno\n\n这是一个使用 UNO 框架开发的 Ipc 测试应用软件，用于测试在 Windows 和 Linux 等平台下的 IPC 库的行为。当前已经在 UOS 统信系统上测试通过\n\n![](http://image.acmx.xyz/lindexi%2F20239201555207210.jpg)\n\n此 Demo 项目是从原本的 WPF 版本迁移到 UNO 版本的，详细请看 [从 WPF 搬迁到 UOS 下的 UNO 的笔记](https://blog.lindexi.com/post/%E4%BB%8E-WPF-%E6%90%AC%E8%BF%81%E5%88%B0-UOS-%E4%B8%8B%E7%9A%84-UNO-%E7%9A%84%E7%AC%94%E8%AE%B0.html )"
  },
  {
    "path": "demo/dotnetCampus.Ipc.Demo/Program.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Threading;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Demo\n{\n    internal class Program\n    {\n        private static void Main(string[] args)\n        {\n            // 为了测试 NET 45 和 NET Core 3.1 版本，才这么写的\n            MainAsync(args).GetAwaiter().GetResult();\n        }\n\n        private static async Task MainAsync(string[] args)\n        {\n            var ipcProvider = new IpcProvider();\n            ipcProvider.StartServer();\n\n            ipcProvider.PeerConnected += (sender, connectedArgs) =>\n            {\n                Console.WriteLine($\"[{GetCurrentProcessId()}] 收到 {connectedArgs.Peer.PeerName} 的连接\");\n\n                connectedArgs.Peer.MessageReceived += async (o, messageArgs) =>\n                {\n                    Console.WriteLine($\"[{GetCurrentProcessId()}] 收到 {messageArgs.PeerName} 的消息：{GetMessageText(messageArgs.Message.Body)}\");\n\n                    await Task.Delay(TimeSpan.FromSeconds(1));\n\n                    // 反向发送消息给对方\n                    Console.WriteLine($\"[{GetCurrentProcessId()}] 向 {connectedArgs.Peer.PeerName} 回复消息\");\n                    await connectedArgs.Peer.NotifyAsync(new IpcMessage(\"回复\", Encoding.UTF8.GetBytes($\"收到你的消息\")));\n                    Console.WriteLine($\"[{GetCurrentProcessId()}] 完成向 {connectedArgs.Peer.PeerName} 回复消息\");\n                };\n            };\n\n            if (args.Length == 0)\n            {\n                var currentName = ipcProvider.IpcContext.PipeName;\n                // 将当前的进程的 Name 传递给另一个进程，用来达成通讯\n                var currentProcess = Process.GetCurrentProcess();\n                var mainModule = currentProcess.MainModule;\n                if (mainModule == null)\n                {\n                    throw new InvalidOperationException(\"无法获取当前进程的主模块路径。\");\n                }\n\n                var mainModuleFileName = mainModule.FileName;\n                Console.WriteLine($\"[{GetCurrentProcessId()}] 准备启动 {mainModuleFileName} 参数：{currentName}\");\n\n                Process.Start(mainModuleFileName, currentName);\n            }\n            else\n            {\n                // 这是被启动的进程，主动连接发送消息\n                Console.WriteLine($\"[{GetCurrentProcessId()}] 开始连接对方进程\");\n\n                var peer = await ipcProvider.GetAndConnectToPeerAsync(args[0]);\n                peer.MessageReceived += (sender, messageArgs) =>\n                {\n                    Console.WriteLine(\n                        $\"[{GetCurrentProcessId()}] 收到 {peer.PeerName} 的回复消息：{GetMessageText(messageArgs.Message.Body)}\");\n                };\n                await peer.NotifyAsync(new IpcMessage(\"Hello\",\n                    Encoding.UTF8.GetBytes($\"Hello,进程号是 {GetCurrentProcessId()} 发送过来消息\")));\n                Console.WriteLine($\"[{GetCurrentProcessId()}] 完成发送消息\");\n            }\n\n            Console.WriteLine($\"[{GetCurrentProcessId()}] 等待退出\");\n            Console.Read();\n            Console.WriteLine($\"[{GetCurrentProcessId()}] 进程准备退出\");\n        }\n\n        private static int GetCurrentProcessId()\n        {\n            return Process.GetCurrentProcess().Id;\n        }\n\n        private static string GetMessageText(IpcMessageBody body)\n        {\n            return Encoding.UTF8.GetString(body.Buffer, body.Start, body.Length);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.Demo/dotnetCampus.Ipc.Demo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>net9.0;netcoreapp3.1;net45</TargetFrameworks>\n    <Nullable>enable</Nullable>\n\n    <IsPackable>false</IsPackable>\n    <PublishAot Condition=\"'$(TargetFramework)' == 'net9.0'\">true</PublishAot>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/App.xaml",
    "content": "﻿<Application x:Class=\"dotnetCampus.Ipc.WpfDemo.App\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"clr-namespace:dotnetCampus.Ipc.WpfDemo\"\n             StartupUri=\"MainWindow.xaml\">\n    <Application.Resources>\n         \n    </Application.Resources>\n</Application>\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/App.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Configuration;\nusing System.Data;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Windows;\n\nnamespace dotnetCampus.Ipc.WpfDemo\n{\n    /// <summary>\n    /// Interaction logic for App.xaml\n    /// </summary>\n    public partial class App : Application\n    {\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/AssemblyInfo.cs",
    "content": "﻿using System.Windows;\n\n[assembly: ThemeInfo(\n    ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located\n                                     //(used if a resource is not found in the page,\n                                     // or application resource dictionaries)\n    ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located\n                                              //(used if a resource is not found in the page,\n                                              // app, or any theme specific resource dictionaries)\n)]\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/ConnectedPeerModel.cs",
    "content": "﻿using System;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Windows.Threading;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.WpfDemo\n{\n    public class ConnectedPeerModel\n    {\n        public ConnectedPeerModel()\n        {\n            Peer = null!;\n        }\n\n        public ConnectedPeerModel(PeerProxy peer)\n        {\n            Peer = peer;\n            peer.MessageReceived += Peer_MessageReceived;\n        }\n\n        private void Peer_MessageReceived(object? sender, IPeerMessageArgs e)\n        {\n            var streamReader = new StreamReader(e.Message.Body.ToMemoryStream());\n            var message = streamReader.ReadToEnd();\n\n            Dispatcher.InvokeAsync(() =>\n            {\n                AddMessage(PeerName, message);\n            });\n        }\n\n        public void AddMessage(string name, string message)\n        {\n            MessageList.Add($\"{name} {DateTime.Now:yyyy/MM/dd hh:mm:ss.fff}:\\r\\n{message}\");\n        }\n\n        public ObservableCollection<string> MessageList { get; } = new ObservableCollection<string>();\n\n        public PeerProxy Peer { get; }\n\n        public string PeerName => Peer.PeerName;\n\n        private Dispatcher Dispatcher { get; } = Dispatcher.CurrentDispatcher;\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/DispatcherSwitcher.cs",
    "content": "﻿using System;\nusing System.Runtime.CompilerServices;\nusing System.Threading.Tasks;\nusing System.Windows.Threading;\n\n// ReSharper disable once CheckNamespace\nnamespace Walterlv.ThreadSwitchingTasks\n{\n    // 细节请看 [将 C++/WinRT 中的线程切换体验带到 C# 中来（WPF 版本）_walterlv - 吕毅-CSDN博客_c++ winrt](https://blog.csdn.net/WPwalter/article/details/90344486)\n    public static class DispatcherSwitcher\n    {\n        public static ThreadPoolAwaiter ResumeBackground() => new ThreadPoolAwaiter();\n\n        public static ThreadPoolAwaiter ResumeBackground(this Dispatcher dispatcher)\n            => new ThreadPoolAwaiter();\n\n        public static DispatcherAwaiter ResumeForeground(this Dispatcher dispatcher) =>\n            new DispatcherAwaiter(dispatcher);\n\n        public class ThreadPoolAwaiter : INotifyCompletion\n        {\n            public void OnCompleted(Action continuation)\n            {\n                Task.Run(() =>\n                {\n                    IsCompleted = true;\n                    continuation();\n                });\n            }\n\n            public bool IsCompleted { get; private set; }\n\n            public void GetResult()\n            {\n            }\n\n            public ThreadPoolAwaiter GetAwaiter() => this;\n        }\n\n        public class DispatcherAwaiter : INotifyCompletion\n        {\n            private readonly Dispatcher _dispatcher;\n\n            public DispatcherAwaiter(Dispatcher dispatcher) => _dispatcher = dispatcher;\n\n            public void OnCompleted(Action continuation)\n            {\n                _dispatcher.InvokeAsync(() =>\n                {\n                    IsCompleted = true;\n                    continuation();\n                });\n            }\n\n            public bool IsCompleted { get; private set; }\n\n            public void GetResult()\n            {\n            }\n\n            public DispatcherAwaiter GetAwaiter() => this;\n        }\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/MainWindow.xaml",
    "content": "﻿<Window x:Class=\"dotnetCampus.Ipc.WpfDemo.MainWindow\"\n        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:local=\"clr-namespace:dotnetCampus.Ipc.WpfDemo\"\n        mc:Ignorable=\"d\" WindowState=\"Maximized\"\n        x:Name=\"Root\"\n        Title=\"dotnetCampus.Ipc.WpfDemo\" Height=\"1000\" Width=\"1900\">\n    <Grid>\n        <local:ServerPage x:Name=\"ServerPage\" ServerStarting=\"ServerPage_OnServerStarting\"></local:ServerPage>\n\n        <Grid x:Name=\"MainGrid\" Visibility=\"Collapsed\">\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\"></RowDefinition>\n                <RowDefinition Height=\"*\"></RowDefinition>\n                <RowDefinition Height=\"Auto\"></RowDefinition>\n            </Grid.RowDefinitions>\n\n            <Grid x:Name=\"HeaderGrid\">\n                <Grid Margin=\"10,10,10,10\">\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"Auto\"></ColumnDefinition>\n                        <ColumnDefinition Width=\"*\"></ColumnDefinition>\n                    </Grid.ColumnDefinitions>\n                    <TextBlock Text=\"本服务器名：\"></TextBlock>\n                    <TextBox x:Name=\"ServerNameTextBox\" Grid.Column=\"1\" IsReadOnly=\"True\" MaxWidth=\"300\" HorizontalAlignment=\"Left\"></TextBox>\n                </Grid>\n            </Grid>\n            <Grid Grid.Row=\"1\">\n                <Grid.ColumnDefinitions>\n                    <ColumnDefinition Width=\"200\"></ColumnDefinition>\n                    <ColumnDefinition></ColumnDefinition>\n                </Grid.ColumnDefinitions>\n                <Grid>\n                    <Grid.RowDefinitions>\n                        <RowDefinition></RowDefinition>\n                        <RowDefinition Height=\"Auto\"></RowDefinition>\n                    </Grid.RowDefinitions>\n                    <ListView x:Name=\"ConnectedPeerListView\"\n                              ItemsSource=\"{Binding ElementName=Root,Path=ConnectedPeerModelList}\"\n                              SelectionChanged=\"ConnectedPeerListView_OnSelectionChanged\">\n                        <ListView.ItemTemplate>\n                            <DataTemplate DataType=\"{x:Type local:ConnectedPeerModel}\">\n                                <TextBlock Text=\"{Binding Path=PeerName}\"></TextBlock>\n                            </DataTemplate>\n                        </ListView.ItemTemplate>\n                    </ListView>\n                    <Button x:Name=\"AddConnectButton\" Grid.Row=\"1\" Margin=\"10,10,10,10\" Content=\"+\" Click=\"AddConnectButton_OnClick\"></Button>\n                </Grid>\n                <Grid Grid.Column=\"1\">\n                    <Grid.RowDefinitions>\n                        <RowDefinition Height=\"*\"></RowDefinition>\n                        <RowDefinition Height=\"200\"></RowDefinition>\n                    </Grid.RowDefinitions>\n                    <Grid>\n                        <Border Background=\"#A6A6A6\"></Border>\n                        <ContentControl x:Name=\"MainPanelContentControl\"></ContentControl>\n                    </Grid>\n                    <Grid Grid.Row=\"1\" Margin=\"10,10,10,10\">\n                        <TextBox x:Name=\"LogTextBox\" TextWrapping=\"Wrap\" AcceptsReturn=\"True\" ></TextBox>\n                    </Grid>\n                </Grid>\n            </Grid>\n        </Grid>\n    </Grid>\n</Window>\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/MainWindow.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing DotNetCampus.Cli;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.WpfDemo.View;\n\nusing Walterlv.ThreadSwitchingTasks;\n\nnamespace dotnetCampus.Ipc.WpfDemo\n{\n    /// <summary>\n    /// Interaction logic for MainWindow.xaml\n    /// </summary>\n    public partial class MainWindow : Window\n    {\n        public MainWindow()\n        {\n            InitializeComponent();\n            DataContext = this;\n\n            var options = CommandLine.Parse(Environment.GetCommandLineArgs().Skip(1).ToArray()).As<Options>();\n            if (!string.IsNullOrEmpty(options.ServerName))\n            {\n                Debugger.Launch();\n                Debugger.Break();\n\n                StartServer(options.ServerName);\n\n                if (!string.IsNullOrEmpty(options.PeerName))\n                {\n                    ConnectToPeer(options.PeerName);\n                }\n            }\n\n            Title = $\"dotnetCampus.Ipc.WpfDemo PID={Process.GetCurrentProcess().Id}\";\n        }\n\n        private async void ConnectToPeer(string peerName)\n        {\n            var peer = await IpcProvider.GetAndConnectToPeerAsync(peerName);\n            AddPeer(peer);\n        }\n\n        private void ServerPage_OnServerStarting(object? sender, string e)\n        {\n            StartServer(e);\n        }\n\n        private void StartServer(string serverName)\n        {\n            var ipcProvider = new IpcProvider(serverName);\n            ipcProvider.StartServer();\n            ipcProvider.PeerConnected += IpcProvider_PeerConnected;\n            IpcProvider = ipcProvider;\n\n            Log($\"Start Server Name={serverName}\");\n\n            ServerPage.Visibility = Visibility.Collapsed;\n            MainGrid.Visibility = Visibility.Visible;\n\n            ServerNameTextBox.Text = serverName;\n        }\n\n\n        /* 项目“dotnetCampus.Ipc.WpfDemo (net45)”的未合并的更改\n        在此之前:\n                private void IpcProvider_PeerConnected(object? sender, PipeCore.Context.PeerConnectedArgs e)\n        在此之后:\n                private void IpcProvider_PeerConnected(object? sender, PeerConnectedArgs e)\n        */\n\n        /* 项目“dotnetCampus.Ipc.WpfDemo (net45)”的未合并的更改\n        在此之前:\n                private void IpcProvider_PeerConnected(object? sender, Context.EventArgs.PeerConnectedArgs e)\n        在此之后:\n                private void IpcProvider_PeerConnected(object? sender, PeerConnectedArgs e)\n        */\n        private void IpcProvider_PeerConnected(object? sender, Context.PeerConnectedArgs e)\n        {\n            AddPeer(e.Peer);\n        }\n\n        private void AddPeer(PeerProxy peer)\n        {\n            Dispatcher.InvokeAsync(() =>\n            {\n                Log($\"收到 {peer.PeerName} 连接\");\n\n                var currentPeer = ConnectedPeerModelList.FirstOrDefault(temp => temp.PeerName == peer.PeerName);\n                if (currentPeer != null)\n                {\n                    currentPeer.Peer.PeerConnectionBroken -= Peer_PeerConnectBroke;\n                    ConnectedPeerModelList.Remove(currentPeer);\n                }\n\n                ConnectedPeerModelList.Add(new ConnectedPeerModel(peer));\n\n                peer.PeerConnectionBroken += Peer_PeerConnectBroke;\n            });\n        }\n\n        private void Peer_PeerConnectBroke(object? sender, IPeerConnectionBrokenArgs e)\n        {\n            var peer = (PeerProxy) sender!;\n            Log($\"[连接断开] {peer.PeerName}\");\n        }\n\n        private IpcProvider IpcProvider { set; get; } = null!;\n\n        private async void Log(string message)\n        {\n            await Dispatcher.ResumeForeground();\n            LogTextBox.Text += DateTime.Now.ToString(\"hh:mm:ss.fff\") + \" \" + message + \"\\r\\n\";\n            if (LogTextBox.Text.Length > 2000)\n            {\n                LogTextBox.Text = LogTextBox.Text.Substring(1000);\n            }\n\n            LogTextBox.SelectionStart = LogTextBox.Text.Length - 1;\n            LogTextBox.SelectionLength = 0;\n            LogTextBox.ScrollToEnd();\n        }\n\n        private void AddConnectButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            var addConnectPage = new AddConnectPage();\n            addConnectPage.ServerStarting += (o, args) =>\n            {\n                var assembly = GetType().Assembly;\n                var file = assembly.Location;\n                if (System.IO.Path.GetExtension(file).Equals(\".dll\", StringComparison.OrdinalIgnoreCase))\n                {\n                    file = System.IO.Path.GetFileNameWithoutExtension(file) + \".exe\";\n                }\n\n                Process.Start(file, $\"--server-name {args} --peer-name {ServerNameTextBox.Text}\");\n                Log($\"启动对方服务 {args}\");\n            };\n            addConnectPage.ServerConnecting += (o, args) =>\n            {\n                ConnectToPeer(args);\n            };\n\n            MainPanelContentControl.Content = addConnectPage;\n        }\n\n        public ObservableCollection<ConnectedPeerModel> ConnectedPeerModelList { get; } =\n            new ObservableCollection<ConnectedPeerModel>();\n\n        private void ConnectedPeerListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)\n        {\n            if (e.AddedItems.Count == 1)\n            {\n                var connectedPeerModel = (ConnectedPeerModel) e.AddedItems[0]!;\n                var charPage = new CharPage()\n                {\n                    ConnectedPeerModel = connectedPeerModel,\n                    ServerName = ServerNameTextBox.Text\n                };\n                MainPanelContentControl.Content = charPage;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/Options.cs",
    "content": "﻿using DotNetCampus.Cli.Compiler;\n\nnamespace dotnetCampus.Ipc.WpfDemo;\n\npublic class Options\n{\n    /// <summary>\n    /// 本机的服务名\n    /// </summary>\n    [Option]\n    public string? ServerName { get; set; }\n\n    /// <summary>\n    /// 对方的服务名\n    /// </summary>\n    [Option]\n    public string? PeerName { get; set; }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/AddConnectPage.xaml",
    "content": "﻿<UserControl x:Class=\"dotnetCampus.Ipc.WpfDemo.View.AddConnectPage\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:dotnetCampus.Ipc.WpfDemo.View\"\n             mc:Ignorable=\"d\" \n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <Grid>\n        <Grid HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n            <StackPanel>\n                <TextBlock HorizontalAlignment=\"Center\" FontSize=\"100\">添加设备</TextBlock>\n\n                <Grid Margin=\"10,10,10,10\">\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"Auto\"></ColumnDefinition>\n                        <ColumnDefinition Width=\"*\"></ColumnDefinition>\n                    </Grid.ColumnDefinitions>\n                    <TextBlock FontSize=\"30\" Text=\"服务器名：\"></TextBlock>\n                    <TextBox x:Name=\"ServerNameTextBox\" FontSize=\"30\" Grid.Column=\"1\"></TextBox>\n                </Grid>\n                <Grid >\n                    <Grid.ColumnDefinitions>\n                        <ColumnDefinition Width=\"*\"></ColumnDefinition>\n                        <ColumnDefinition Width=\"*\"></ColumnDefinition>\n                    </Grid.ColumnDefinitions>\n                    <Button Margin=\"10,10,10,10\" Content=\"连接现有服务\" Click=\"ConnectServerButton_OnClick\"></Button>\n                    <Button Margin=\"10,10,10,10\" Grid.Column=\"1\" Content=\"启动新服务\" Click=\"StartServerButton_OnClick\"></Button>\n                </Grid>\n            </StackPanel>\n          \n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/AddConnectPage.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace dotnetCampus.Ipc.WpfDemo.View\n{\n    /// <summary>\n    /// AddConnectPage.xaml 的交互逻辑\n    /// </summary>\n    public partial class AddConnectPage : UserControl\n    {\n        public AddConnectPage()\n        {\n            InitializeComponent();\n            BuildServerName();\n        }\n\n        private void ConnectServerButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            ServerConnecting?.Invoke(this, ServerNameTextBox.Text);\n        }\n\n        private void StartServerButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            ServerStarting?.Invoke(this, ServerNameTextBox.Text);\n            BuildServerName();\n        }\n\n        private void BuildServerName()\n        {\n            ServerNameTextBox.Text = System.IO.Path.GetRandomFileName();\n        }\n\n        public event EventHandler<string>? ServerConnecting;\n\n        public event EventHandler<string>? ServerStarting;\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/CharPage.xaml",
    "content": "﻿<UserControl x:Class=\"dotnetCampus.Ipc.WpfDemo.View.CharPage\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:local=\"clr-namespace:dotnetCampus.Ipc.WpfDemo.View\"\n             xmlns:wpfDemo=\"clr-namespace:dotnetCampus.Ipc.WpfDemo\"\n             mc:Ignorable=\"d\"\n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <d:Page.DataContext>\n        <wpfDemo:ConnectedPeerModel />\n    </d:Page.DataContext>\n    <Grid>\n        <Grid.RowDefinitions>\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition />\n            <RowDefinition Height=\"Auto\" />\n            <RowDefinition Height=\"Auto\" />\n        </Grid.RowDefinitions>\n        <TextBlock Margin=\"10,10,10,10\" Text=\"{Binding Path=PeerName}\" />\n        <ListView x:Name=\"MessageListView\" Grid.Row=\"1\" ItemsSource=\"{Binding Path=MessageList}\" ScrollViewer.HorizontalScrollBarVisibility=\"Disabled\">\n            <ListView.ItemTemplate>\n                <DataTemplate>\n                    <TextBlock Text=\"{Binding}\" TextWrapping=\"Wrap\"/>\n                </DataTemplate>\n            </ListView.ItemTemplate>\n        </ListView>\n        <TextBox x:Name=\"MessageTextBox\" Grid.Row=\"2\" Margin=\"10,10,10,10\"\n                 Height=\"100\" AcceptsReturn=\"True\" TextWrapping=\"Wrap\" />\n        <RepeatButton Grid.Row=\"3\" Margin=\"10,0,10,10\" HorizontalAlignment=\"Right\" Content=\"Send\" Click=\"SendButton_OnClick\" />\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/CharPage.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Text;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\nusing System.Windows.Threading;\n\nnamespace dotnetCampus.Ipc.WpfDemo.View\n{\n    /// <summary>\n    /// CharPage.xaml 的交互逻辑\n    /// </summary>\n    public partial class CharPage : UserControl\n    {\n        public CharPage()\n        {\n            InitializeComponent();\n\n            Loaded += CharPage_Loaded;\n        }\n\n        private void CharPage_Loaded(object sender, RoutedEventArgs e)\n        {\n            Debug.Assert(ConnectedPeerModel != null);\n\n            DataContext = ConnectedPeerModel;\n            MessageListView.ScrollToBottom();\n\n            // 有消息过来，自动滚动到最下\n            ConnectedPeerModel.MessageList.CollectionChanged += (o, args) =>\n            {\n                Dispatcher.InvokeAsync(() =>\n                {\n                    MessageListView.ScrollToBottom();\n                }, DispatcherPriority.Background);\n            };\n        }\n\n        public ConnectedPeerModel ConnectedPeerModel { set; get; } = null!;\n\n        public string ServerName { set; get; } = null!;\n\n        private async void SendButton_OnClick(object sender, RoutedEventArgs e)\n        {\n            ConnectedPeerModel.AddMessage(ServerName, MessageTextBox.Text);\n            await ConnectedPeerModel.Peer.IpcMessageWriter.WriteMessageAsync(MessageTextBox.Text, \"CharPage\")\n                .ConfigureAwait(false);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/ListViewExtensions.cs",
    "content": "﻿using System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Media;\n\nnamespace dotnetCampus.Ipc.WpfDemo.View\n{\n    public static class ListViewExtensions\n    {\n        public static void ScrollToBottom(this ListView listView)\n        {\n            DependencyObject border = VisualTreeHelper.GetChild(listView, 0);\n            ScrollViewer scrollViewer = (ScrollViewer) VisualTreeHelper.GetChild(border, 0);\n            scrollViewer.ScrollToBottom();\n        }\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/ServerPage.xaml",
    "content": "﻿<UserControl x:Class=\"dotnetCampus.Ipc.WpfDemo.ServerPage\"\n             xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\" \n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\" \n             xmlns:local=\"clr-namespace:dotnetCampus.Ipc.WpfDemo\"\n             mc:Ignorable=\"d\" \n             x:Name=\"Root\"\n             d:DesignHeight=\"450\" d:DesignWidth=\"800\">\n    <Grid>\n        <Grid HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\">\n            <Grid.RowDefinitions>\n                <RowDefinition Height=\"Auto\"></RowDefinition>\n                <RowDefinition Height=\"Auto\"></RowDefinition>\n                <RowDefinition Height=\"Auto\"></RowDefinition>\n            </Grid.RowDefinitions>\n            <TextBlock Margin=\"10,10,2,10\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" FontSize=\"60\" \n                       Text=\"本地服务器名:\"></TextBlock>\n            <TextBox x:Name=\"ServerNameTextBox\" Grid.Row=\"1\" Margin=\"2,10,10,10\" \n                     HorizontalAlignment=\"Center\" FontSize=\"50\" \n                     Text=\"{Binding ElementName=Root,Path=ServerName,Mode=TwoWay}\"></TextBox>\n            <Button Grid.Row=\"2\" Margin=\"10,50,10,10\" HorizontalAlignment=\"Center\" Width=\"300\" FontSize=\"50\" \n                    Content=\"启动服务器\" \n                    Click=\"Button_OnClick\"></Button>\n        </Grid>\n    </Grid>\n</UserControl>\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/View/ServerPage.xaml.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\nusing System.Windows;\nusing System.Windows.Controls;\nusing System.Windows.Data;\nusing System.Windows.Documents;\nusing System.Windows.Input;\nusing System.Windows.Media;\nusing System.Windows.Media.Imaging;\nusing System.Windows.Navigation;\nusing System.Windows.Shapes;\n\nnamespace dotnetCampus.Ipc.WpfDemo\n{\n    /// <summary>\n    /// ServerPage.xaml 的交互逻辑\n    /// </summary>\n    public partial class ServerPage : UserControl\n    {\n        public ServerPage()\n        {\n            InitializeComponent();\n        }\n\n        public static readonly DependencyProperty ServerNameProperty = DependencyProperty.Register(\n            \"ServerName\", typeof(string), typeof(ServerPage), new PropertyMetadata(\"dotnet campus\"));\n\n        public string ServerName\n        {\n            get { return (string) GetValue(ServerNameProperty); }\n            set { SetValue(ServerNameProperty, value); }\n        }\n\n        private void Button_OnClick(object sender, RoutedEventArgs e)\n        {\n            OnServerStarting(ServerName);\n            var button = (Button) sender;\n            button.IsEnabled = false;\n            ServerNameTextBox.IsReadOnly = true;\n        }\n\n        /// <summary>\n        /// 启动服务\n        /// </summary>\n        public event EventHandler<string>? ServerStarting;\n\n        protected virtual void OnServerStarting(string e)\n        {\n            ServerStarting?.Invoke(this, e);\n        }\n    }\n}\n"
  },
  {
    "path": "demo/dotnetCampus.Ipc.WpfDemo/dotnetCampus.Ipc.WpfDemo.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.WindowsDesktop\">\n\n  <PropertyGroup>\n    <OutputType>WinExe</OutputType>\n    <TargetFramework>net6.0-windows</TargetFramework>\n    <UseWPF>true</UseWPF>\n    <Nullable>enable</Nullable>\n\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n      <PackageReference Include=\"dotnetCampus.CommandLine\" />\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "docs/IpcObject.01.md",
    "content": "# 使用「远程对象调用」的方式做进程间通信\n\n假设你有一个接口 `IFoo`，有 A、B 两个进程想做进程间通信。通过 IPC 的「远程对象调用」的方式，你可以让 A 进程调用 B 进程的 `IFoo` 接口方法，就像调用本地对象一样。\n\n## 快速入门\n\n先定义好一个接口，这个接口将可被远程调用：\n\n```csharp\n/// <summary>\n/// 可跨进程调用的接口演示。\n/// </summary>\n[IpcPublic]\npublic interface IFoo\n{\n    /// <summary>\n    /// 属性演示。支持 get/set 属性、get 只读属性，支持跨进程报告异常。\n    /// </summary>\n    string Name { get; set; }\n\n    /// <summary>\n    /// 方法演示。支持参数、返回值，支持跨进程报告异常。\n    /// </summary>\n    int Add(int a, int b);\n\n    /// <summary>\n    /// 异步方法（更推荐）演示。支持参数、返回值，支持跨进程报告异常。\n    /// </summary>\n    Task<string> AddAsync(string a, int b);\n}\n```\n\n现在，我们有两个进程 A 和 B：\n\n- A 进程是调用端，想调用 B 进程的 `IFoo` 接口方法。\n- B 进程是被调用端，提供 `IFoo` 接口的实现。\n\n为了实现这样的跨进程调用，我们需要在 A 进程和 B 进程分别进行一些 IPC 的初始化。\n\n```csharp\n// A 进程 Program.cs\n\n// 1. 初始化 IPC\nvar ipcProvider = new IpcProvider(\"IPC-A\");\n// 2. 启动 IPC（以支持双向通信）\nipcProvider.StartServer();\n// 3. 连接进程 B\nvar peer = await ipcProvider.GetAndConnectToPeerAsync(\"IPC-B\");\n\n// 获取来自 B 进程的 IFoo 接口的「代理」（Proxy）\nvar foo = ipcProvider.CreateIpcProxy<IFoo>(peer);\n\n// 现在，开始自由地享受 IFoo 的远程调用吧！就好像它从来都是自己进程内的对象一样\nConsole.WriteLine(foo.Name);\nConsole.WriteLine(foo.Add(1, 2));\nConsole.WriteLine(await foo.AddAsync(\"a\", 1));\nConsole.Read();\n```\n\n```csharp\n// B 进程 Program.cs\n\n// 1. 初始化 IPC\nvar ipcProvider = new IpcProvider(\"IPC-B\");\n\n// 创建 IFoo 的实际对象，然后为其创建一个「对接」（Joint）\nipcProvider.CreateIpcJoint<IFoo>(new Foo());\n\n// 2. 启动 IPC（以支持双向通信）\n//    这里，我们提前创建好了「对接」，再启动 IPC；这样 A 进程连接 B 进程时，就能马上使用 IFoo 了\nipcProvider.StartServer();\n\nConsole.Read();\n```\n\n```csharp\n// B 进程，IFoo 的实际实现\nclass Foo : IFoo\n{\n    public string Name { get; set; } = \"Foo\";\n\n    public int Add(int a, int b)\n    {\n        Console.WriteLine($\"a({a})+b({b})={a + b}\");\n        return a + b;\n    }\n\n    public async Task<string> AddAsync(string a, int b)\n    {\n        return await Task.Run(() =>\n        {\n            Console.WriteLine($\"a({a})+b({b})={a + b}\");\n            return a + b;\n        });\n    }\n}\n```\n\n以上，就是「远程对象调用」所需要的所有示例代码了。所有代码来自本仓库 <https://github.com/dotnet-campus/dotnetCampus.Ipc/tree/main/demo/IpcRemotingObjectDemo>。\n\n## 进阶用法\n\n### `IpcPublic`\n\n上述 IPC 初始化的部分不变，实现不变的情况下，接口 `IFoo` 上的 `[IpcPublic]` 接口还有更多玩法。\n\n```csharp\n// IgnoresIpcException = true\n//  - A 进程调用 IFoo 的「代理」时，忽略所有的 IPC 异常（即对方进程断开、IFoo 接口出现方法签名的变更等）\n//  - 但 A 进程仍然能收到 B 进程 IFoo 实现类的业务异常（如 ArgumentNullException）\n// Timeout = 1000\n//  - A 进程调用 IFoo 的「代理」时，最多等待 1000 毫秒\n//  - 超出时间没有返回，则方法会立即返回；如果方法带有返回值，则会返回返回类型的默认值\n[IpcPublic(IgnoresIpcException = true, Timeout = 1000)]\n```\n\n这些是对 `IFoo` 这个接口的全局设置。当然，还可以对它内部的每个成员单独设置更多属性：\n\n```csharp\n// DefaultReturn = \"Error\"\n//  - A 进程调用 IFoo「代理」的此方法时，如果真发生了 IPC 异常，则会返回指定的默认值\n//  - 在这里，是返回 \"Error\"，而不是 string 的默认值 null（这可以避免破坏可空性）\n[IpcMethod(DefaultReturn = \"Error\", IgnoresIpcException = true, Timeout = 2000)]\nTask<string> AddAsync(string a, int b);\n```\n\n特别的，对于 void 返回值的方法，还有一个属性 `WaitsVoid`：\n\n```csharp\n// WaitsVoid = true\n//  - 默认情况下，IPC 不会等待 void 方法返回（因为库作者 @walterlv 认为如果你想等待，改用异步方法更好）\n//  - 这意味着你甚至还收不到 B 进程此方法实现的异常\n//  - 但如果你确实希望这个 void 像一个本地 void 一样等待，可以设置此属性\n[IpcMethod(WaitsVoid = true)]\nvoid Add(int a, int b);\n```\n\n哦，对了，属性上也有属性可以设置哦：\n\n```csharp\n// IsReadonly = true\n//  - 神奇吧，一个 get/set 属性设成只读有什么作用？\n//  - 这意味着 A 进程通过「代理」获取此属性的值时，会假设此属性不会再变了，于是缓存起来，只拿这一次；以后都使用这次的缓存\n[IpcProperty(IsReadonly = true)]\nstring Name { get; set; }\n```\n\n你可能注意到我们还有一个 `IpcEvent` 可以标在事件上，不过很遗憾地告诉你，目前还没实现事件。所以你会看到我们写了个分析器告诉你不要这么做。\n\n### `IpcShape`\n\n好了，现在更麻烦的事来了。假设你有三个进程 A、B、C：\n\n- A 仍然是调用端\n- B 仍然是被调用端\n- 新增了一个 C，跟 A 一样是调用端，但希望用不同的方式调用 `IFoo` 这个接口怎么办？\n\n我们前面介绍了 `[IpcPublic]` 特性，它可以用来标记接口及其成员，以详细定制各个成员的 IPC 行为。但是，它一旦在接口上标记了，就意味着所有进程对这个接口的调用都会遵循这个标记的规则。\n\n有没有什么方法，能够允许我 A 和 C 进程使用不同的规则来调用 `IFoo` 接口呢？\n\n答案就是 `[IpcShape]` 特性：\n\n- 你可以在 C 进程里额外定义一个 `IFoo` 接口的空实现\n- 然后逐一设置这个空实现中你希望与 `IFoo` 接口中所定义的不同的调用规则\n\n```csharp\n[IpcShape(typeof(IFoo))]\ninternal class IpcFooShape : IFoo\n{\n    // IFoo 接口上没有设置此属性，所以 A 进程是默认方式访问这个属性的\n    // 但是 C 进程通过 IpcShape，在不影响 A 进程访问规则的情况下，定制了 C 进程下的访问规则\n    [IpcProperty(IsReadonly = true)]\n    public string Name { get; set; } = null!;\n\n    public int Add(int a, int b) => throw null!;\n    public async Task<string> AddAsync(string a, int b) => throw null!;\n}\n```\n\n那么 C 进程的初始化需要有所变化：\n\n```diff\n    var ipcProvider = new IpcProvider(\"IpcRemotingObjectClientDemo\");\n    ipcProvider.StartServer();\n    var peer = await ipcProvider.GetAndConnectToPeerAsync(\"IpcRemotingObjectServerDemo\");\n\n    // 获取来自 B 进程的 IFoo 接口的「代理」（Proxy）\n--  var foo = ipcProvider.CreateIpcProxy<IFoo>(peer);\n++  // 不过这次，我们使用了 IpcFooShape 这个「形状」（Shape）作为「代理」（Proxy）\n++  var foo = ipcProvider.CreateIpcProxy<IFoo, IpcFooShape>(peer);\n\n    Console.WriteLine(foo.Name);\n    Console.WriteLine(foo.Add(1, 2));\n    Console.WriteLine(await foo.AddAsync(\"a\", 1));\n    Console.Read();\n```\n\n## 高级用法\n\nDotNetCampus.Ipc「远程对象调用」支持你嵌套 IPC 对象，这意味着你可以实现更加复杂的 IPC 需求。\n\n```csharp\n/// <summary>\n/// 嵌套 IPC 类型演示。\n/// </summary>\n[IpcPublic]\npublic interface IBar\n{\n    /// <summary>\n    /// 这是一个超复杂的方法，参数和返回值都是 IPC 对象。\n    /// </summary>\n    Task<IBaz> AddAsync(IQux qux, IQuux quux);\n}\n```\n\n想想看，这里的每个类型，谁是调用方，谁是被调用方（实现方）？\n\n假设 A 进程试图调用 B 进程的 `IBar` 接口：\n\n| 类型    | A 进程           | B 进程           | 描述                                         |\n| ------- | ---------------- | ---------------- | -------------------------------------------- |\n| `IBar`  | 代理             | 实现（需要对接） | A 进程调用 `IBar` 的代理以访问 B 进程        |\n| `IBaz`  | 代理             | 对接（无需对接） | A 进程拿到了 B 进程的 `IBaz` 的代理          |\n| `IQux`  | 实现（无需对接） | 代理             | A 进程有一个 IQux 的实现，会传给 B 进程去用  |\n| `IQuux` | 实现（无需对接） | 代理             | A 进程有一个 IQuux 的实现，会传给 B 进程去用 |\n\n这里的 `IBaz` `IQux` `IQuux` 都需要标记 `[IpcPublic]` 或 `[IpcShape]`（当然，我们的分析器也会提示你需要标的）。但好在你不需要额外编写对接代码；我的意思是 IPC 的初始化代码里，你只需要处理 `IBar` 这一个接口就够了，剩下的 DotNetCampus.Ipc 会帮你完成。\n\n## 异常处理\n\n本 IPC 库有两种类型的异常：\n\n- `IpcLocalException`: 表示异常发生在本地进程中\n- `IpcRemoteException`: 表示异常发生在远端进程中，或者发生在 IPC 通信过程中\n\n你可能在初始化等过程中收到各种异常，不过「远程对象调用」中只会收到以下这些：\n\n- 本地异常 `IpcLocalException`\n    - 「远程对象调用」几乎不会发生本地异常\n- 代理异常，如果 IPC 接口的实现方法内抛出了以下这几种异常，则会在调用方也代理出相同类型的异常（这个列表详见 [这里](../src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/GeneratedProxyExceptionModel.cs)，你也可以提 PR 修改这个列表）\n    - `ArgumentException`\n    - `ArgumentNullException`\n    - `BadImageFormatException`\n    - `InvalidCastException`\n    - `InvalidOperationException`\n    - `NotImplementedException`\n    - `NotSupportedException`\n    - `NullReferenceException`\n- 远端异常 `IpcRemoteException`\n    - `IpcInvokingException`: 如果 IPC 接口的实现方法内抛出了上述异常之外的其他异常，则会包装成此异常\n    - `IpcInvokingTimeoutException`: 远程对象调用超时（如前面所说，设置 `Timeout` 属性后可以支持超时）\n\n所以，大多数情况下，你只需要像一个本地对象一样去处理异常即可。\n\n不过，如果你想更加可靠一些处理异常，我们正计划做「自动代理」功能，以便能更好地用通用的方式来处理「远程对象调用」中发生的远端**非业务性**异常。功能计划中，文件夹已经建好了，请耐心等待。\n\n## 性能和 AOT 兼容性\n\n1. DotNetCampus.Ipc 库使用源生成器生成「代理」（Proxy）和「对接」（Joint）的代码，旨在提升性能和确保 AOT 兼容性。\n    - 目前还仍有少量代码在使用反射，不过我们计划很快将其完全消除\n2. DotNetCampus.Ipc 已移除 Newtonsoft.Json 库，完全使用 System.Text.Json 并配合源生成器来做跨进程对象的传输，旨在大幅减少 AOT 之后的大小。\n    - **想要享受到完全移除 Newtonsoft.Json 库的好处，你的项目框架至少要到 .NET 8**\n\n## 最佳实践\n\n为了更好地发挥 IPC「远程对象调用」的代码编写直观性优势，同时又避免不太喜欢的行为，库作者 @walterlv 推荐：\n\n1. IPC 接口中尽量全部使用异步方法\n    - 除非你希望这个对象用起来更加像一个本地对象一样，拥有属性、同步的方法\n2. 如果一定要用同步方法，也请避免使用 void 返回值\n    - 如果真用了 void 返回值，请认真考虑一下要不要设置 `WaitsVoid` 属性\n\n好了，就这些。其他你随便。\n"
  },
  {
    "path": "docs/IpcObject.02.md",
    "content": "# IpcRemotingObject\n\n使用 .NET Remoting 模式的对象远程调用的 IPC 通讯方式。\n\n## 概念\n\n在 .NET Remoting 里，可以在当前进程里拿到一个对象，这个对象的实际执行逻辑是放在另一个进程执行的。此设计的优势在于，业务层拿到一个对象，可以无视此对象实际 IPC 通讯细节。\n\n## 顶层使用方法\n\n顶层业务层使用这一套可以分为两个步骤：\n\n1. 获取远程对象；\n2. 使用对象。\n\n获取远程对象需要两个先决参数，分别是本地 IPC 提供器 IpcProvider 以及服务端的代理 PeerProxy 对象。在本 IPC 框架里，约定了使用 IpcProvider 类型提供 IPC 的通讯接口，使用 PeerProxy 代表一个远程 IPC 对象。\n\n在 IpcRemotingObject 机制里，需要通过 IpcProvider 进行基础的通讯逻辑，通过 PeerProxy 了解服务端是谁。\n\n假定有 IIpcJointTestObject 接口，此接口将定义 IPC 远程调用对象，获取此远程对象可用如下代码：\n\n```csharp\nvar ipcJointTestObjectProxy = ipcProvider.CreateIpcProxy<IIpcJointTestObject, IpcJointTestObjectIpcProxy>(peerProxy);\n```\n\n通过如上代码即可拿到 IIpcJointTestObject 类型的 ipcJointTestObjectProxy 对象。\n\n其他任何业务逻辑里面，使用 ipcJointTestObjectProxy 对象就和使用其他对象一样，无须特殊考虑。\n\n## 逻辑定义\n\n大部分情况下，以下逻辑定义，除了实际的类型逻辑代码外，其他的都由工具自动生成。为了展示更多的细节，以下将列出一个 IPC 远程调用对象的定义方式。\n\n框架约定的远程调用对象的定义包括如下部分：\n\n- 接口定义：所有给到业务层使用的，都必须要求通过接口出发。决策原因是在 .NET Core 或以上版本，透明代理对接口更加好友。通过接口方式可以在不动用任何黑科技，即可完成全框架开发。\n- 实际逻辑：实际的类型的逻辑，此逻辑将在远程执行。\n- 代理类：用于给本地使用的代理类型，继承远程调用对象的接口定义和 GeneratedIpcProxy 类型。在代理类里面，所有的成员都应该调用 IPC 通讯，调用远程的对象进行处理逻辑。\n- 对接类：继承 GeneratedIpcJoint 的类型，用于对接 IPC 通讯框架和具体实现逻辑的类型，用于将 IPC 的消息转换为实际调用代码，作为对接 IPC 消息和具体方法或属性调用的类型。\n\n![](image/IpcRemotingObject/IpcRemotingObject0.png)\n\n以下采用 IIpcJointTestObject 远程调用对象的定义作为例子。\n\n假定有业务需要 IIpcJointTestObject 接口类型实现业务，此业务具体是在另一个进程（此进程称为远程进程）执行代码逻辑，调用端在当前进程（此进程称为本地进程）进行触发，接口定义如下：\n\n```csharp\n    /// <summary>\n    /// 定义 IPC 接口\n    /// </summary>\n    public interface IIpcJointTestObject\n    {\n        void TestMethod2(string arg1, int arg2);\n        Task TestMethod2Async(string arg1, int arg2);\n\n        void TestMethod1();\n        Task TestMethod1Async();\n    }\n```\n\n具体的 IpcJointTestRealObject 对象执行逻辑如下：\n\n```csharp\n    /// <summary>\n    /// 实际的对象，包含实际的业务逻辑\n    /// </summary>\n    [IpcPublic(typeof(IIpcJointTestObject), typeof(IpcJointTestObjectIpcProxy), typeof(IpcJointTestObjectIpcJoint))]\n    class IpcJointTestRealObject : IIpcJointTestObject\n    {\n        public void TestMethod2(string arg1, int arg2)\n        {\n        \t// 忽略代码逻辑\n        }\n\n        public Task TestMethod2Async(string arg1, int arg2)\n        {\n            // 忽略代码逻辑\n        }\n\n        public void TestMethod1()\n        {\n            // 忽略代码逻辑\n        }\n\n        public Task TestMethod1Async()\n        {\n            // 忽略代码逻辑\n        }\n    }\n```\n\n此 IpcJointTestRealObject 类型就是实际的执行代码逻辑，按照框架约定，需要给此类型加上 IpcPublic 特性。特性里的 IpcJointTestObjectIpcProxy 和 IpcJointTestObjectIpcJoint 分别是代理类和对接类。这两个类型定义如下：\n\n代理类型的作用是给本地进程所使用的对象，此对象继承远程调用对象的接口定义，也就是 IpcJointTestObjectIpcProxy 代理类型继承 IIpcJointTestObject 接口。但是代理类型没有执行实际的逻辑，所有的成员都是调用 IPC 通讯，让远程执行执行返回结果。\n\n为了方便代理类型调用 IPC 通讯，要求代理类型同时继承 GeneratedIpcProxy 类型，代码如下：\n\n```csharp\n    /// <summary>\n    /// 这是在 IPC 客户端执行的代码，给 IPC 客户端的各个业务使用的实际对象，此对象里面的所有方法都是调用 IPC 发送请求到 IPC 服务端执行\n    /// </summary>\n    /// 此为生成代码\n    class IpcJointTestObjectIpcProxy : GeneratedIpcProxy<IIpcJointTestObject>, IIpcJointTestObject\n    {\n        public void TestMethod2(string arg1, int arg2)\n        {\n            CallMethod(new object[] { arg1, arg2 }).Wait();\n        }\n\n        public async Task TestMethod2Async(string arg1, int arg2)\n        {\n            await CallMethodAsync(new object[] { arg1, arg2 });\n        }\n\n        public void TestMethod1()\n        {\n            CallMethod().Wait();\n        }\n\n        public async Task TestMethod1Async()\n        {\n            await CallMethodAsync();\n        }\n    }\n```\n\n在远程进行收到代理类型发送过来的请求时，需要执行到具体的逻辑，也就是 IpcJointTestRealObject 的代码逻辑。为了让 IPC 发送过来的请求与具体的代码调用关联，应该定义对接类型。对接类将定义某个请求对应具体逻辑的调用方法，如代码：\n\n```csharp\n    /// <summary>\n    /// 这是在 IPC 服务端执行的代码，用于关联 IPC 客户端发过来的请求的处理方式，如何对应到实际的对象\n    /// </summary>\n    /// 此为生成代码\n    class IpcJointTestObjectIpcJoint : GeneratedIpcJoint<IIpcJointTestObject>\n    {\n        protected override void MatchMembers(IIpcJointTestObject real)\n        {\n            MatchMethod(nameof(IIpcJointTestObject.TestMethod1), new Action(() => real.TestMethod1()));\n            MatchMethod(nameof(IIpcJointTestObject.TestMethod1Async), new Func<Task>(() => real.TestMethod1Async()));\n\n            MatchMethod(nameof(IIpcJointTestObject.TestMethod2), new Action<string, int>((a0, a1) => real.TestMethod2(a0, a1)));\n            MatchMethod(nameof(IIpcJointTestObject.TestMethod2Async), new Func<string, int, Task>((a0, a1) => real.TestMethod2Async(a0, a1)));\n        }\n    }\n```\n\n以上代码里，有以下类型是开发者业务端需要定义的：\n\n- 接口 IIpcJointTestObject 的代码；\n- 具体实现逻辑 IpcJointTestRealObject 的代码。\n\n有以下类型是框架的代码生成工具创建的：\n\n- 代理类 IpcJointTestObjectIpcProxy 的代码，这些代码都是相似的；\n- 对接类 IpcJointTestObjectIpcJoint 的代码，用于从请求调用具体的对象。\n\n定义完成类型，需要注入到 IPC 框架里才能生效。要求在远程进程里，调用如下代码进行注入：\n\n```csharp\n  // 创建实际的对象\n  var ipcJointTestRealObject = new IpcJointTestRealObject();\n\n  // 注册关联\n  ipcProvider.CreateIpcJoint<IIpcJointTestObject, IpcJointTestObjectIpcJoint>(ipcJointTestRealObject);\n```\n\n具体的使用最简例子如下：\n\n```csharp\n// 进程1的代码： \n                // A 客户端\n                var ipcProviderA = new IpcProvider();\n                ipcProviderA.StartServer();\n\n                // 先让 A 连接到 B 上，连接之后，才能向 B 获取到对象代理\n                var peer = await ipcProviderA.GetAndConnectToPeerAsync(ipcProviderB.IpcContext.PipeName);\n\n                // 通过在客户端里，创建对象代理，即可拿到代理。代理是给本机使用，但实际执行逻辑，是发送远程调用\n                var ipcJointTestObjectProxy = ipcProviderA.CreateIpcProxy<IIpcJointTestObject, IpcJointTestObjectIpcProxy>(peer);\n\n                // 无参，无返回值 同步方法\n                ipcJointTestObjectProxy.TestMethod1();\n\n                // 无参，无返回值 异步方法\n                await ipcJointTestObjectProxy.TestMethod1Async();\n\n                // 带基础类型参数，无返回值同步方法\n                string a0 = \"lindexi is doubi\";\n                int a1 = 2;\n\n                ipcJointTestObjectProxy.TestMethod2(a0, a1);\n\n                // 带基础类型参数，无返回值异步方法\n                await ipcJointTestObjectProxy.TestMethod2Async(a0, a1);\n\n\n// 进程2的代码：\n                // B 服务端\n                var ipcProviderB = new IpcProvider();\n                var ipcJointTestRealObject = new IpcJointTestRealObject();\n\n                // 注册关联\n                ipcProviderB.CreateIpcJoint<IIpcJointTestObject, IpcJointTestObjectIpcJoint>(ipcJointTestRealObject);\n\n                // 注册完成再启动\n                ipcProviderB.StartServer();\n```\n\n通过以上代码可以看到，只有定义远程调用对象，以及注入和获取远程对象逻辑和 IPC 框架相关。如何使用远程对象和 IPC 框架无关。"
  },
  {
    "path": "docs/JsonIpcDirectRouted.md",
    "content": "# JsonIpcDirectRouted\n\n使用直接路由和 Json 通讯格式的 IPC 通讯方式\n\n## 概念和特点\n\n这是采用直接路由的调度方式的 IPC 通讯，直接路由可以理解为每个路由是一个字符串，要求请求和响应的代码所标识的字符串相同才能被相互识别。整个 IPC 里的通讯格式采用的是 Json 方式，要求传入和传出的对象都可以被 Json 序列化和反序列化\n\n这是对底层 IPC 的上层封装，提供了业务友好的调用方式。可以支持请求响应和单向通知两个模式，通讯上分为服务端和客户端。服务端可以定义响应和通知的处理逻辑，客户端可以发起对服务端的请求\n\n## 顶层使用方法\n\n### 服务端的创建和定义\n\n服务端的创建需要给服务端一个服务名，此服务名需要和客户端约定，可以让客户端通过此服务名连接上此服务端\n\n```csharp\n            // 初始化服务端\n            var serverName = \"JsonIpcDirectRoutedProviderTest_Request_1\";\n            var serverProvider = new JsonIpcDirectRoutedProvider(serverName);\n```\n\n服务名仅要求是一个合法的管道名即可，一般采用大小写英文字符数字下划线组成，长度不超过 256 个字符\n\n在服务端上可以定义响应和通知的处理逻辑，以下代码定义的是对名为 “Foo1” 的直接路由的请求的处理逻辑\n\n```csharp\n            serverProvider.AddRequestHandler(\"Foo1\", (FakeArgument arg) =>\n            {\n                return new FakeResult(\"Ok\");\n            });\n```\n\n以上定义的逻辑代码即可让客户端将 FakeArgument 类型的对象，通过请求名为 \"Foo1\" 的路由地址给到服务端处理。服务端处理之后将会返回 FakeResult 类型的对象\n\n服务端定义通知的处理逻辑例子如下，通知只有从客户端发过来的参数，不需要返回任何对象给到客户端，即客户端只是发过来一条通知给到服务端\n\n```csharp\n            var routedPath = \"FooPath\";\n            serverProvider.AddNotifyHandler(routedPath, (FakeArgument arg) =>\n            {\n            });\n```\n\n服务端完成了路由事件的定义之后，即可通过 StartServer 方法进行启动\n\n```csharp\n            serverProvider.StartServer();\n```\n\n从 IPC 的设计上，要求在 StartServer 启动服务之前完成所有对路由事件的定义。在 StartServer 之后，禁止再 AddRequestHandler 或 AddNotifyHandler 添加处理逻辑。此设计是为了保证消息不丢失，防止存在消息在路由事件定义完成之前收到而丢失\n\n以上连在一起的服务端的定义和启动代码如下\n\n```csharp\n            // 初始化服务端\n            var serverName = \"JsonIpcDirectRoutedProviderTest_Request_1\";\n            var serverProvider = new JsonIpcDirectRoutedProvider(serverName);\n\n            serverProvider.AddRequestHandler(\"Foo1\", (FakeArgument arg) =>\n            {\n                return new FakeResult(\"Ok\");\n            });\n\n            serverProvider.AddRequestHandler(\"Foo2\", (FakeArgument2 arg) =>\n            {\n                return new FakeResult2(\"Ok\");\n            });\n\n            serverProvider.AddRequestHandler(\"Foo3\", (FakeArgument3 arg) =>\n            {\n                return new FakeResult3(\"Ok\");\n            });\n\n            var routedPath = \"FooPath\";\n            serverProvider.AddNotifyHandler(routedPath, (FakeArgument arg) =>\n            {\n            });\n\n            serverProvider.AddNotifyHandler(\"FooPath1\", (FakeArgument1 arg) =>\n            {\n            });\n\n            serverProvider.AddNotifyHandler(\"FooPath2\", (FakeArgument2 arg) =>\n            {\n            });\n\n            serverProvider.StartServer();\n```\n\n### 客户端的创建和通讯\n\n本质上的 JsonIpcDirectRouted 依然是 P2P 的方式，而不是 客户端-服务端 的方式。客户端的创建也需要从 JsonIpcDirectRoutedProvider 获取到\n\n```csharp\n            // 创建客户端\n            // 允许无参数，如果只是做客户端使用的话\n            JsonIpcDirectRoutedProvider clientProvider = new();\n            // 对于 clientProvider 来说，可选调用 StartServer 方法\n            var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n```\n\n无论是服务端还是客户端，都需要创建 JsonIpcDirectRoutedProvider 对象。不同点在于，一个纯客户端的业务逻辑可以不给 JsonIpcDirectRoutedProvider 传入服务名，此时意味着只允许连接别人不允许别人主动连接过来\n\n获取客户端时，需要调用 GetAndConnectClientAsync 方法传入服务端的服务名。如果此时的服务端还没启动，将会在 await 里面异步等待服务端启动且连接上服务端\n\n获取到客户端对象之后，即可对服务器发起请求获取响应，也可以单向给服务端发送通知。以下是对服务端发起请求获取响应的例子\n\n```csharp\n            var argument = new FakeArgument(\"TestName\", 1);\n            FakeResult result = await clientProxy.GetResponseAsync<FakeResult>(\"Foo1\", argument);\n```\n\n以上代码的 GetResponseAsync 第一个参数表示的是所请求的路由地址，第二个参数是一个对象，将会被 Json 序列化然后发送给服务端。返回值的 FakeResult 是服务端处理的返回值\n\n以下是发送通知给服务端的例子\n\n```csharp\n            var argument = new FakeArgument(\"TestName\", 1);\n            await clientProxy.NotifyAsync(\"FooPath\", argument);\n```\n\n发送通知时 await 返回只代表服务端收到了通知，不代表服务端处理通知完成\n\n连在一起的客户端创建和通讯的代码如下\n\n```csharp\n            // 创建客户端\n            // 允许无参数，如果只是做客户端使用的话\n            JsonIpcDirectRoutedProvider clientProvider = new();\n            // 对于 clientProvider 来说，可选调用 StartServer 方法\n            var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n            var result = await clientProxy.GetResponseAsync<FakeResult>(\"Foo1\", argument);\n\n            await clientProxy.NotifyAsync(\"Foo1\", argument);\n```\n\n## 高级定制\n\n### 控制 IPC 基础服务\n\n在 JsonIpcDirectRoutedProvider 的构造函数可以传入 IpcProvider 对象。于是可以通过此方式对 IpcProvider 对象进行配置，从而实现对 JsonIpcDirectRoutedProvider 进行底层 IPC 服务的配置，如以下代码\n\n```csharp\n            // 初始化服务端\n            var serverName = \"JsonIpcDirectRoutedProviderTest_Notify_3\";\n            // 这样的创建方式也是对 IPC 连接最高定制的方式\n            IpcProvider ipcProvider = new(serverName);\n            var serverProvider = new JsonIpcDirectRoutedProvider(ipcProvider);\n```\n\n创建 IpcProvider 过程中，可以进行大量的配置逻辑。以及可以通过 IpcProvider 的 Dispose 方法进行手动释放\n\n### 多个服务端对象共用管道\n\n只需让多个 JsonIpcDirectRoutedProvider 对象共用一个 IpcProvider 即可共用管道。允许多个 JsonIpcDirectRoutedProvider 实例共用相同的 IpcProvider 对象，每个服务端对象处理不同的路由事件，如以下代码\n\n```csharp\n            // 初始化服务端\n            var serverName = \"JsonIpcDirectRoutedProviderTest_Notify_3\";\n            // 这样的创建方式也是对 IPC 连接最高定制的方式\n            IpcProvider ipcProvider = new(serverName);\n            var serverProvider1 = new JsonIpcDirectRoutedProvider(ipcProvider);\n\n            serverProvider1.AddNotifyHandler(\"Foo1\", (FakeArgument arg) =>\n            {\n            });\n\n            // 再次开启一个服务，共用相同的 IpcProvider 对象\n            var serverProvider2 = new JsonIpcDirectRoutedProvider(ipcProvider);\n\n            serverProvider2.AddNotifyHandler(\"Foo2\", (FakeArgument arg) =>\n            {\n            });\n            serverProvider1.StartServer();\n            serverProvider2.StartServer();\n```\n\n此时根据客户端发送的通讯的路由地址，将调度到不同的服务端\n\n```csharp\n            // 创建客户端\n            // 允许无参数，如果只是做客户端使用的话\n            JsonIpcDirectRoutedProvider clientProvider = new();\n            var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n            await clientProxy.NotifyAsync(\"Foo1\", argument);\n            // 预期这条消息是在第二个服务处理的\n            await clientProxy.NotifyAsync(\"Foo2\", argument);\n```\n\n如果有多个服务端对相同的路由地址进行处理，只有先添加的才能收到消息\n\n### 服务端获取客户端的信息\n\n如果服务端需要了解当前的请求或通知是由哪个客户端发起的，可以通过 AddNotifyHandler 和 AddRequestHandler 方法的高级重载拿到 JsonIpcDirectRoutedContext 对象，如以下代码\n\n```csharp\n            serverProvider.AddNotifyHandler<FakeArgument>(\"Foo2\", (arg, context) =>\n            {\n                // 可以获取到客户端名\n                var clientName = context.PeerName;\n            });\n```\n\n如以上代码即可通过 JsonIpcDirectRoutedContext 拿到 PeerName 客户端名"
  },
  {
    "path": "dotnetCampus.Ipc.sln",
    "content": "Microsoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.1.31911.260\nMinimumVisualStudioVersion = 15.0.26124.0\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc\", \"src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\", \"{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}\"\n\tProjectSection(ProjectDependencies) = postProject\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2} = {5035AC84-F775-422F-BB5F-E3713404DCB2}\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"tests\", \"tests\", \"{C0B9B0B5-D172-4309-A8C4-2C8B77E470CD}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.Tests\", \"tests\\dotnetCampus.Ipc.Tests\\dotnetCampus.Ipc.Tests.csproj\", \"{E007FBCE-2F83-499F-9060-7D1FB673E24B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.Demo\", \"demo\\dotnetCampus.Ipc.Demo\\dotnetCampus.Ipc.Demo.csproj\", \"{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"demo\", \"demo\", \"{65FA7B8E-7D2E-41D1-9740-BBB7D8B8ABE3}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.WpfDemo\", \"demo\\dotnetCampus.Ipc.WpfDemo\\dotnetCampus.Ipc.WpfDemo.csproj\", \"{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"PipeMvc\", \"PipeMvc\", \"{716AC4F5-1A7F-4469-9418-24FB2990AC6E}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.PipeMvcServer\", \"src\\PipeMvc\\dotnetCampus.Ipc.PipeMvcServer\\dotnetCampus.Ipc.PipeMvcServer.csproj\", \"{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.PipeMvcClient\", \"src\\PipeMvc\\dotnetCampus.Ipc.PipeMvcClient\\dotnetCampus.Ipc.PipeMvcClient.csproj\", \"{1ACE3261-CC3D-4442-8C83-516721B3DA46}\"\nEndProject\nProject(\"{D954291E-2A0B-460D-934E-DC6B0785DB48}\") = \"dotnetCampus.Ipc.PipeMvcShare\", \"src\\PipeMvc\\dotnetCampus.Ipc.PipeMvcShare\\dotnetCampus.Ipc.PipeMvcShare.shproj\", \"{05ACE3BB-61E5-4592-AC73-747850DB1081}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Build\", \"Build\", \"{11767A0B-CBFB-4323-BCEE-DE1CDD6C4B4B}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.editorconfig = .editorconfig\n\t\t.gitattributes = .gitattributes\n\t\t.gitignore = .gitignore\n\t\tCHANGELOG.md = CHANGELOG.md\n\t\tDirectory.Build.props = Directory.Build.props\n\t\tDirectory.Packages.props = Directory.Packages.props\n\t\tLICENSE = LICENSE\n\t\tREADME.md = README.md\n\t\tbuild\\Version.props = build\\Version.props\n\tEndProjectSection\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.Analyzers\", \"src\\dotnetCampus.Ipc.Analyzers\\dotnetCampus.Ipc.Analyzers.csproj\", \"{5035AC84-F775-422F-BB5F-E3713404DCB2}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"samples\", \"samples\", \"{40A33EB6-6290-477F-B489-0EE4EB96F4FC}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.Analyzers.Tests\", \"tests\\dotnetCampus.Ipc.Analyzers.Tests\\dotnetCampus.Ipc.Analyzers.Tests.csproj\", \"{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Demos\", \"Demos\", \"{798348B6-734A-44C6-A3D5-8926B2EACD84}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PipeMvcClientDemo\", \"demo\\PipeMvc\\PipeMvcClientDemo\\PipeMvcClientDemo.csproj\", \"{DD752947-4AA7-40BA-94BC-B8FC40C719D0}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"PipeMvcServerDemo\", \"demo\\PipeMvc\\PipeMvcServerDemo\\PipeMvcServerDemo.csproj\", \"{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"IpcRemotingObjectDemo\", \"IpcRemotingObjectDemo\", \"{927CE552-9207-47AD-9595-650C5A0624A9}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcRemotingObjectServerDemo\", \"demo\\IpcRemotingObjectDemo\\IpcRemotingObjectServerDemo\\IpcRemotingObjectServerDemo.csproj\", \"{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"IpcRemotingObjectClientDemo\", \"demo\\IpcRemotingObjectDemo\\IpcRemotingObjectClientDemo\\IpcRemotingObjectClientDemo.csproj\", \"{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.FakeTests\", \"tests\\dotnetCampus.Ipc.FakeTests\\dotnetCampus.Ipc.FakeTests.csproj\", \"{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"dotnetCampus.Ipc.SourceGenerators\", \"analyzers\\dotnetCampus.Ipc.SourceGenerators\\dotnetCampus.Ipc.SourceGenerators.csproj\", \"{8B2F7D63-0326-458A-8BB5-A18B9C594372}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"IpcDirectRoutedAotDemo\", \"demo\\IpcDirectRoutedAotDemo\\IpcDirectRoutedAotDemo.csproj\", \"{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x64.Build.0 = Release|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{F7ED61F4-920C-49EB-8DC1-74B2BE6AF272}.Release|x86.Build.0 = Release|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B}.Release|x86.Build.0 = Release|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Release|x64.Build.0 = Release|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72}.Release|x86.Build.0 = Release|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Release|x64.Build.0 = Release|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A}.Release|x86.Build.0 = Release|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Release|x64.Build.0 = Release|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A}.Release|x86.Build.0 = Release|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Release|x64.Build.0 = Release|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46}.Release|x86.Build.0 = Release|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Release|x64.Build.0 = Release|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{5035AC84-F775-422F-BB5F-E3713404DCB2}.Release|x86.Build.0 = Release|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Release|x64.Build.0 = Release|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7}.Release|x86.Build.0 = Release|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Release|x64.Build.0 = Release|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0}.Release|x86.Build.0 = Release|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Release|x64.Build.0 = Release|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48}.Release|x86.Build.0 = Release|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Release|x64.Build.0 = Release|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292}.Release|x86.Build.0 = Release|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B}.Release|x86.Build.0 = Release|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Release|x64.Build.0 = Release|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B}.Release|x86.Build.0 = Release|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Release|x64.Build.0 = Release|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{8B2F7D63-0326-458A-8BB5-A18B9C594372}.Release|x86.Build.0 = Release|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Release|x64.Build.0 = Release|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{E007FBCE-2F83-499F-9060-7D1FB673E24B} = {C0B9B0B5-D172-4309-A8C4-2C8B77E470CD}\n\t\t{4D078B2A-029B-4B53-8FA9-5D2F9330BC72} = {65FA7B8E-7D2E-41D1-9740-BBB7D8B8ABE3}\n\t\t{0E10A5EB-7E48-408A-A4E7-8AB2A7AF143A} = {65FA7B8E-7D2E-41D1-9740-BBB7D8B8ABE3}\n\t\t{0F6B9C0C-7A64-4B21-BC28-E52565245A1A} = {716AC4F5-1A7F-4469-9418-24FB2990AC6E}\n\t\t{1ACE3261-CC3D-4442-8C83-516721B3DA46} = {716AC4F5-1A7F-4469-9418-24FB2990AC6E}\n\t\t{05ACE3BB-61E5-4592-AC73-747850DB1081} = {716AC4F5-1A7F-4469-9418-24FB2990AC6E}\n\t\t{31AE0FDD-404A-46D1-B8AB-A75EF09198E7} = {C0B9B0B5-D172-4309-A8C4-2C8B77E470CD}\n\t\t{798348B6-734A-44C6-A3D5-8926B2EACD84} = {716AC4F5-1A7F-4469-9418-24FB2990AC6E}\n\t\t{DD752947-4AA7-40BA-94BC-B8FC40C719D0} = {798348B6-734A-44C6-A3D5-8926B2EACD84}\n\t\t{FCD18445-1D4C-4B28-86F2-CA1092CEAC48} = {798348B6-734A-44C6-A3D5-8926B2EACD84}\n\t\t{927CE552-9207-47AD-9595-650C5A0624A9} = {65FA7B8E-7D2E-41D1-9740-BBB7D8B8ABE3}\n\t\t{CB343EAE-9DA2-49F3-B39B-E9CDDCEB2292} = {927CE552-9207-47AD-9595-650C5A0624A9}\n\t\t{6639929E-3AC1-47D9-BE74-97FDF3C29B6B} = {927CE552-9207-47AD-9595-650C5A0624A9}\n\t\t{FCE2D295-1A3B-404B-8B45-21CCBE340A4B} = {C0B9B0B5-D172-4309-A8C4-2C8B77E470CD}\n\t\t{91F795EA-BE18-7F11-4E23-DD9E7EA8824C} = {65FA7B8E-7D2E-41D1-9740-BBB7D8B8ABE3}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {C8828641-2F8C-4B6A-BF1D-F8F3C8C8454D}\n\tEndGlobalSection\n\tGlobalSection(SharedMSBuildProjectFiles) = preSolution\n\t\tsrc\\PipeMvc\\dotnetCampus.Ipc.PipeMvcShare\\dotnetCampus.Ipc.PipeMvcShare.projitems*{05ace3bb-61e5-4592-ac73-747850db1081}*SharedItemsImports = 13\n\t\tsrc\\PipeMvc\\dotnetCampus.Ipc.PipeMvcShare\\dotnetCampus.Ipc.PipeMvcShare.projitems*{0f6b9c0c-7a64-4b21-bc28-e52565245a1a}*SharedItemsImports = 5\n\t\tsrc\\PipeMvc\\dotnetCampus.Ipc.PipeMvcShare\\dotnetCampus.Ipc.PipeMvcShare.projitems*{1ace3261-cc3d-4442-8c83-516721b3da46}*SharedItemsImports = 5\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "dotnetCampus.Ipc.sln.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/UserDictionary/Words/=Walterlv/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcClient/IpcNamedPipeClientHandler.cs",
    "content": "﻿using System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.PipeMvcServer.IpcFramework;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.PipeMvcClient\n{\n    class IpcNamedPipeClientHandler : HttpMessageHandler\n    {\n        public IpcNamedPipeClientHandler(PeerProxy serverProxy, IpcProvider? clientIpcProvider)\n        {\n            ServerProxy = serverProxy;\n            ClientIpcProvider = clientIpcProvider;\n        }\n\n        private PeerProxy ServerProxy { get; }\n\n        /// <summary>\n        /// 客户端的 IPC 服务\n        /// </summary>\n        /// 只是引用对象，不然 IpcProvider 将被回收\n        /// 可空，不给就不给咯，只是做引用\n        private IpcProvider? ClientIpcProvider { get; }\n\n        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n        {\n            var message = HttpMessageSerializer.Serialize(request);\n\n            // 创建 IPC 消息的 Tag 内容，此 Tag 内容仅用来调试和记录日志\n            var ipcMessageTag = request.RequestUri?.ToString() ?? request.Method.ToString();\n\n            // 通过 PeerProxy 发送 IPC 请求，此时的 IPC 请求将会被 PipeMvcServer 处理\n            // 在 PipeMvcServer 里面，将通过 ASP.NET Core MVC 框架层进行调度，分发到对应的控制器处理\n            // 控制器处理完成之后，将由 MVC 框架层将控制器的输出交给 PipeMvcServer 层\n            // 在 PipeMvcServer 层收到控制器的输出之后，将通过 IPC 框架，将输出返回给 PipeMvcClient 端\n            // 当 PipeMvcClient 收到输出返回值后，以下的 await 方法将会返回\n            var response = await ServerProxy.GetResponseAsync(new IpcMessage(ipcMessageTag, message));\n\n            return HttpMessageSerializer.DeserializeToResponse(response.Body);\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            ClientIpcProvider?.Dispose();\n            base.Dispose(disposing);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcClient/IpcPipeMvcClientProvider.cs",
    "content": "﻿using System;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.PipeMvcServer.IpcFramework;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.PipeMvcClient\n{\n    /// <summary>\n    /// 提供给客户端调用 MVC 的 Ipc 服务的功能\n    /// </summary>\n    public static class IpcPipeMvcClientProvider\n    {\n        /// <summary>\n        /// 获取访问 Mvc 的 Ipc 服务的对象\n        /// </summary>\n        /// <param name=\"ipcPipeMvcServerName\">对方 Ipc 服务名</param>\n        /// <param name=\"clientIpcProvider\">可选，用来进行 Ipc 连接的本地服务。如不传或是空，将创建新的 Ipc 连接服务</param>\n        /// <returns></returns>\n        public static async Task<HttpClient> CreateIpcMvcClientAsync(string ipcPipeMvcServerName, IpcProvider? clientIpcProvider = null)\n        {\n            if (clientIpcProvider == null)\n            {\n                clientIpcProvider = new IpcProvider();\n                clientIpcProvider.StartServer();\n            }\n\n            var peer = await clientIpcProvider.GetAndConnectToPeerAsync(ipcPipeMvcServerName);\n\n            return new HttpClient(new IpcNamedPipeClientHandler(peer, clientIpcProvider))\n            {\n                BaseAddress = new Uri(IpcPipeMvcContext.BaseAddressUrl),\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcClient/dotnetCampus.Ipc.PipeMvcClient.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net5.0</TargetFramework>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <Nullable>enable</Nullable>\n    <IsPackable>true</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n  <Import Project=\"..\\dotnetCampus.Ipc.PipeMvcShare\\dotnetCampus.Ipc.PipeMvcShare.projitems\" Label=\"Shared\" />\n\n</Project>\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ApplicationWrapper.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Hosting.Server;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal abstract class ApplicationWrapper\n    {\n        internal abstract object CreateContext(IFeatureCollection features);\n\n        internal abstract Task ProcessRequestAsync(object context);\n\n        internal abstract void DisposeContext(object context, Exception? exception);\n    }\n\n    internal class ApplicationWrapper<TContext> : ApplicationWrapper, IHttpApplication<TContext> where TContext : notnull\n    {\n        private readonly IHttpApplication<TContext> _application;\n        private readonly Action _preProcessRequestAsync;\n\n        public ApplicationWrapper(IHttpApplication<TContext> application, Action preProcessRequestAsync)\n        {\n            _application = application;\n            _preProcessRequestAsync = preProcessRequestAsync;\n        }\n\n        internal override object CreateContext(IFeatureCollection features)\n        {\n            return ((IHttpApplication<TContext>) this).CreateContext(features);\n        }\n\n        TContext IHttpApplication<TContext>.CreateContext(IFeatureCollection features)\n        {\n            return _application.CreateContext(features);\n        }\n\n        internal override void DisposeContext(object context, Exception? exception)\n        {\n            ((IHttpApplication<TContext>) this).DisposeContext((TContext) context, exception);\n        }\n\n        void IHttpApplication<TContext>.DisposeContext(TContext context, Exception? exception)\n        {\n            _application.DisposeContext(context, exception);\n        }\n\n        internal override Task ProcessRequestAsync(object context)\n        {\n            return ((IHttpApplication<TContext>) this).ProcessRequestAsync((TContext) context);\n        }\n\n        Task IHttpApplication<TContext>.ProcessRequestAsync(TContext context)\n        {\n            _preProcessRequestAsync();\n            return _application.ProcessRequestAsync(context);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/AsyncStreamWrapper.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.IO;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class AsyncStreamWrapper : Stream\n    {\n        private readonly Stream _inner;\n        private readonly Func<bool> _allowSynchronousIO;\n\n        internal AsyncStreamWrapper(Stream inner, Func<bool> allowSynchronousIO)\n        {\n            _inner = inner;\n            _allowSynchronousIO = allowSynchronousIO;\n        }\n\n        public override bool CanRead => _inner.CanRead;\n\n        public override bool CanSeek => false;\n\n        public override bool CanWrite => _inner.CanWrite;\n\n        public override long Length => throw new NotSupportedException(\"The stream is not seekable.\");\n\n        public override long Position\n        {\n            get => throw new NotSupportedException(\"The stream is not seekable.\");\n            set => throw new NotSupportedException(\"The stream is not seekable.\");\n        }\n\n        public override void Flush()\n        {\n            // Not blocking Flush because things like StreamWriter.Dispose() always call it.\n            _inner.Flush();\n        }\n\n        public override Task FlushAsync(CancellationToken cancellationToken)\n        {\n            return _inner.FlushAsync(cancellationToken);\n        }\n\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            if (!_allowSynchronousIO())\n            {\n                throw new InvalidOperationException(\"Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true.\");\n            }\n\n            return _inner.Read(buffer, offset, count);\n        }\n\n        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)\n        {\n            return _inner.ReadAsync(buffer, offset, count, cancellationToken);\n        }\n\n        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)\n        {\n            return _inner.ReadAsync(buffer, cancellationToken);\n        }\n\n        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)\n        {\n            return _inner.BeginRead(buffer, offset, count, callback, state);\n        }\n\n        public override int EndRead(IAsyncResult asyncResult)\n        {\n            return _inner.EndRead(asyncResult);\n        }\n\n        public override long Seek(long offset, SeekOrigin origin)\n        {\n            throw new NotSupportedException(\"The stream is not seekable.\");\n        }\n\n        public override void SetLength(long value)\n        {\n            throw new NotSupportedException(\"The stream is not seekable.\");\n        }\n\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            if (!_allowSynchronousIO())\n            {\n                throw new InvalidOperationException(\"Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.\");\n            }\n\n            _inner.Write(buffer, offset, count);\n        }\n\n        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)\n        {\n            return _inner.BeginWrite(buffer, offset, count, callback, state);\n        }\n\n        public override void EndWrite(IAsyncResult asyncResult)\n        {\n            _inner.EndWrite(asyncResult);\n        }\n\n        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)\n        {\n            return _inner.WriteAsync(buffer, offset, count, cancellationToken);\n        }\n\n        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)\n        {\n            return _inner.WriteAsync(buffer, cancellationToken);\n        }\n\n        public override void Close()\n        {\n            // Don't dispose the inner stream, we don't want to impact the client stream\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            // Don't dispose the inner stream, we don't want to impact the client stream\n        }\n\n        public override ValueTask DisposeAsync()\n        {\n            // Don't dispose the inner stream, we don't want to impact the client stream\n            return default;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ClientHandler.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics.Contracts;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Linq;\nusing System.Net;\nusing System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Hosting.Server;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\nusing Microsoft.Net.Http.Headers;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// This adapts HttpRequestMessages to ASP.NET Core requests, dispatches them through the pipeline, and returns the\n    /// associated HttpResponseMessage.\n    /// </summary>\n    internal class ClientHandler : HttpMessageHandler\n    {\n        private readonly ApplicationWrapper _application;\n        private readonly PathString _pathBase;\n\n        /// <summary>\n        /// Create a new handler.\n        /// </summary>\n        /// <param name=\"pathBase\">The base path.</param>\n        /// <param name=\"application\">The <see cref=\"IHttpApplication{TContext}\"/>.</param>\n        internal ClientHandler(PathString pathBase, ApplicationWrapper application)\n        {\n            _application = application ?? throw new ArgumentNullException(nameof(application));\n\n            // PathString.StartsWithSegments that we use below requires the base path to not end in a slash.\n            if (pathBase.HasValue && pathBase.Value.EndsWith('/'))\n            {\n                pathBase = new PathString(pathBase.Value[..^1]); // All but the last character\n            }\n            _pathBase = pathBase;\n        }\n\n        internal bool AllowSynchronousIO { get; set; }\n\n        internal bool PreserveExecutionContext { get; set; }\n\n        /// <summary>\n        /// This adapts HttpRequestMessages to ASP.NET Core requests, dispatches them through the pipeline, and returns the\n        /// associated HttpResponseMessage.\n        /// </summary>\n        /// <param name=\"request\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        protected override async Task<HttpResponseMessage> SendAsync(\n            HttpRequestMessage request,\n            CancellationToken cancellationToken)\n        {\n            return await SendInnerAsync(request, cancellationToken);\n        }\n\n        public async Task<HttpResponseMessage> SendInnerAsync(HttpRequestMessage request, CancellationToken cancellationToken)\n        {\n            if (request == null)\n            {\n                throw new ArgumentNullException(nameof(request));\n            }\n\n            var contextBuilder = new HttpContextBuilder(_application, AllowSynchronousIO, PreserveExecutionContext);\n\n            var requestContent = request.Content;\n\n            if (requestContent != null)\n            {\n                // Read content from the request HttpContent into a pipe in a background task. This will allow the request\n                // delegate to start before the request HttpContent is complete. A background task allows duplex streaming scenarios.\n                contextBuilder.SendRequestStream(async writer =>\n                {\n                    if (requestContent is StreamContent)\n                    {\n                        // This is odd but required for backwards compat. If StreamContent is passed in then seek to beginning.\n                        // This is safe because StreamContent.ReadAsStreamAsync doesn't block. It will return the inner stream.\n                        var body = await requestContent.ReadAsStreamAsync();\n                        if (body.CanSeek)\n                        {\n                            // This body may have been consumed before, rewind it.\n                            body.Seek(0, SeekOrigin.Begin);\n                        }\n\n                        await body.CopyToAsync(writer);\n                    }\n                    else\n                    {\n                        await requestContent.CopyToAsync(writer.AsStream());\n                    }\n\n                    await writer.CompleteAsync();\n                });\n            }\n\n            contextBuilder.Configure((context, reader) =>\n            {\n                var req = context.Request;\n\n                req.Protocol = HttpProtocol.GetHttpProtocol(request.Version);\n                req.Method = request.Method.ToString();\n                req.Scheme = request.RequestUri!.Scheme;\n\n                var canHaveBody = false;\n                if (requestContent != null)\n                {\n                    canHaveBody = true;\n                    // Chunked takes precedence over Content-Length, don't create a request with both Content-Length and chunked.\n                    if (request.Headers.TransferEncodingChunked != true)\n                    {\n                        // Reading the ContentLength will add it to the Headers‼\n                        // https://github.com/dotnet/runtime/blob/874399ab15e47c2b4b7c6533cc37d27d47cb5242/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HttpContentHeaders.cs#L68-L87\n                        var contentLength = requestContent.Headers.ContentLength;\n                        if (!contentLength.HasValue && request.Version == HttpVersion.Version11)\n                        {\n                            // HTTP/1.1 requests with a body require either Content-Length or Transfer-Encoding: chunked.\n                            request.Headers.TransferEncodingChunked = true;\n                        }\n                        else if (contentLength == 0)\n                        {\n                            canHaveBody = false;\n                        }\n                    }\n\n                    foreach (var header in requestContent.Headers)\n                    {\n                        req.Headers.Append(header.Key, header.Value.ToArray());\n                    }\n\n                    if (canHaveBody)\n                    {\n                        req.Body = new AsyncStreamWrapper(reader.AsStream(), () => contextBuilder.AllowSynchronousIO);\n                    }\n                }\n\n                context.Features.Set<IHttpRequestBodyDetectionFeature>(new RequestBodyDetectionFeature(canHaveBody));\n\n                foreach (var header in request.Headers)\n                {\n                    // User-Agent is a space delineated single line header but HttpRequestHeaders parses it as multiple elements.\n                    if (string.Equals(header.Key, HeaderNames.UserAgent, StringComparison.OrdinalIgnoreCase))\n                    {\n                        req.Headers.Append(header.Key, string.Join(\" \", header.Value));\n                    }\n                    else\n                    {\n                        req.Headers.Append(header.Key, header.Value.ToArray());\n                    }\n                }\n\n                if (!req.Host.HasValue)\n                {\n                    // If Host wasn't explicitly set as a header, let's infer it from the Uri\n                    req.Host = HostString.FromUriComponent(request.RequestUri);\n                    if (request.RequestUri.IsDefaultPort)\n                    {\n                        req.Host = new HostString(req.Host.Host);\n                    }\n                }\n\n                req.Path = PathString.FromUriComponent(request.RequestUri);\n                req.PathBase = PathString.Empty;\n                if (req.Path.StartsWithSegments(_pathBase, out var remainder))\n                {\n                    req.Path = remainder;\n                    req.PathBase = _pathBase;\n                }\n\n                req.QueryString = QueryString.FromUriComponent(request.RequestUri);\n            });\n\n            var response = new HttpResponseMessage();\n\n            // Copy trailers to the response message when the response stream is complete\n            contextBuilder.RegisterResponseReadCompleteCallback(context =>\n            {\n                var responseTrailersFeature = context.Features.Get<IHttpResponseTrailersFeature>();\n\n                // Trailers collection is settable so double check the app hasn't set it to null.\n                if (responseTrailersFeature?.Trailers != null)\n                {\n                    foreach (var trailer in responseTrailersFeature.Trailers)\n                    {\n                        bool success =\n                            response.TrailingHeaders.TryAddWithoutValidation(trailer.Key, (IEnumerable<string>) trailer.Value);\n                        Contract.Assert(success, \"Bad trailer\");\n                    }\n                }\n            });\n\n            var httpContext = await contextBuilder.SendAsync(cancellationToken);\n\n            response.StatusCode = (HttpStatusCode) httpContext.Response.StatusCode;\n            response.ReasonPhrase = httpContext.Features.Get<IHttpResponseFeature>()!.ReasonPhrase;\n            response.RequestMessage = request;\n            response.Version = request.Version;\n\n            response.Content = new StreamContent(httpContext.Response.Body);\n\n            foreach (var header in httpContext.Response.Headers)\n            {\n                if (!response.Headers.TryAddWithoutValidation(header.Key, (IEnumerable<string>) header.Value))\n                {\n                    bool success =\n                        response.Content.Headers.TryAddWithoutValidation(header.Key, (IEnumerable<string>) header.Value);\n                    Contract.Assert(success, \"Bad header\");\n                }\n            }\n\n            return response;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/HttpContextBuilder.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class HttpContextBuilder : IHttpBodyControlFeature, IHttpResetFeature\n    {\n        private readonly ApplicationWrapper _application;\n        private readonly bool _preserveExecutionContext;\n        private readonly HttpContext _httpContext;\n\n        private readonly TaskCompletionSource<HttpContext> _responseTcs = new TaskCompletionSource<HttpContext>(TaskCreationOptions.RunContinuationsAsynchronously);\n        private readonly ResponseBodyReaderStream _responseReaderStream;\n        private readonly ResponseBodyPipeWriter _responsePipeWriter;\n        private readonly ResponseFeature _responseFeature;\n        private readonly RequestLifetimeFeature _requestLifetimeFeature;\n        private readonly ResponseTrailersFeature _responseTrailersFeature = new ResponseTrailersFeature();\n        private bool _pipelineFinished;\n        private bool _returningResponse;\n        private object? _testContext;\n        private readonly Pipe _requestPipe;\n\n        private Action<HttpContext>? _responseReadCompleteCallback;\n        private Task? _sendRequestStreamTask;\n\n        internal HttpContextBuilder(ApplicationWrapper application, bool allowSynchronousIO, bool preserveExecutionContext)\n        {\n            _application = application ?? throw new ArgumentNullException(nameof(application));\n            AllowSynchronousIO = allowSynchronousIO;\n            _preserveExecutionContext = preserveExecutionContext;\n            _httpContext = new DefaultHttpContext();\n            _responseFeature = new ResponseFeature(Abort);\n            _requestLifetimeFeature = new RequestLifetimeFeature(Abort);\n\n            var request = _httpContext.Request;\n            request.Protocol = HttpProtocol.Http11;\n            request.Method = HttpMethods.Get;\n\n            _requestPipe = new Pipe();\n\n            var responsePipe = new Pipe();\n            _responseReaderStream = new ResponseBodyReaderStream(responsePipe, ClientInitiatedAbort, ResponseBodyReadComplete);\n            _responsePipeWriter = new ResponseBodyPipeWriter(responsePipe, ReturnResponseMessageAsync);\n            _responseFeature.Body = new ResponseBodyWriterStream(_responsePipeWriter, () => AllowSynchronousIO);\n            _responseFeature.BodyWriter = _responsePipeWriter;\n\n            _httpContext.Features.Set<IHttpBodyControlFeature>(this);\n            _httpContext.Features.Set<IHttpResponseFeature>(_responseFeature);\n            _httpContext.Features.Set<IHttpResponseBodyFeature>(_responseFeature);\n            _httpContext.Features.Set<IHttpRequestLifetimeFeature>(_requestLifetimeFeature);\n            _httpContext.Features.Set<IHttpResponseTrailersFeature>(_responseTrailersFeature);\n            _httpContext.Features.Set<IHttpUpgradeFeature>(new UpgradeFeature());\n        }\n\n        public bool AllowSynchronousIO { get; set; }\n\n        internal void Configure(Action<HttpContext, PipeReader> configureContext)\n        {\n            if (configureContext == null)\n            {\n                throw new ArgumentNullException(nameof(configureContext));\n            }\n\n            configureContext(_httpContext, _requestPipe.Reader);\n        }\n\n        internal void SendRequestStream(Func<PipeWriter, Task> sendRequestStream)\n        {\n            if (sendRequestStream == null)\n            {\n                throw new ArgumentNullException(nameof(sendRequestStream));\n            }\n\n            _sendRequestStreamTask = sendRequestStream(_requestPipe.Writer);\n        }\n\n        internal void RegisterResponseReadCompleteCallback(Action<HttpContext> responseReadCompleteCallback)\n        {\n            _responseReadCompleteCallback = responseReadCompleteCallback;\n        }\n\n        /// <summary>\n        /// Start processing the request.\n        /// </summary>\n        /// <returns></returns>\n        internal Task<HttpContext> SendAsync(CancellationToken cancellationToken)\n        {\n            var registration = cancellationToken.Register(ClientInitiatedAbort);\n\n            // Everything inside this function happens in the SERVER's execution context (unless PreserveExecutionContext is true)\n            async Task RunRequestAsync()\n            {\n                // HTTP/2 specific features must be added after the request has been configured.\n                if (HttpProtocol.IsHttp2(_httpContext.Request.Protocol) ||\n                    HttpProtocol.IsHttp3(_httpContext.Request.Protocol))\n                {\n                    _httpContext.Features.Set<IHttpResetFeature>(this);\n                }\n\n                // This will configure IHttpContextAccessor so it needs to happen INSIDE this function,\n                // since we are now inside the Server's execution context. If it happens outside this cont\n                // it will be lost when we abandon the execution context.\n                _testContext = _application.CreateContext(_httpContext.Features);\n                try\n                {\n                    await _application.ProcessRequestAsync(_testContext);\n\n                    // Determine whether request body was complete when the delegate exited.\n                    // This could throw an error if there was a pending server read. Needs to\n                    // happen before completing the response so the response returns the error.\n                    var requestBodyInProgress = RequestBodyReadInProgress();\n                    if (requestBodyInProgress)\n                    {\n                        // If request is still in progress then abort it.\n                        CancelRequestBody();\n                    }\n\n                    // Matches Kestrel server: response is completed before request is drained\n                    await CompleteResponseAsync();\n\n                    if (!requestBodyInProgress)\n                    {\n                        // Writer was already completed in send request callback.\n                        await _requestPipe.Reader.CompleteAsync();\n\n                        // Don't wait for request to drain. It could block indefinitely. In a real server\n                        // we would wait for a timeout and then kill the socket.\n                        // Potential future improvement: add logging that the request timed out\n                    }\n\n                    _application.DisposeContext(_testContext, exception: null);\n                }\n                catch (Exception ex)\n                {\n                    Abort(ex);\n                    _application.DisposeContext(_testContext, ex);\n                }\n                finally\n                {\n                    registration.Dispose();\n                }\n            }\n\n            // Async offload, don't let the test code block the caller.\n            if (_preserveExecutionContext)\n            {\n                _ = Task.Factory.StartNew(RunRequestAsync, default, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);\n            }\n            else\n            {\n                ThreadPool.UnsafeQueueUserWorkItem(_ =>\n                {\n                    _ = RunRequestAsync();\n                }, null);\n            }\n\n            return _responseTcs.Task;\n        }\n\n        // Triggered by request CancellationToken canceling or response stream Disposal.\n        internal void ClientInitiatedAbort()\n        {\n            if (!_pipelineFinished)\n            {\n                // We don't want to trigger the token for already completed responses.\n                _requestLifetimeFeature.Cancel();\n            }\n\n            // Writes will still succeed, the app will only get an error if they check the CT.\n            _responseReaderStream.Abort(new IOException(\"The client aborted the request.\"));\n\n            // Cancel any pending request async activity when the client aborts a duplex\n            // streaming scenario by disposing the HttpResponseMessage.\n            CancelRequestBody();\n        }\n\n        private void ResponseBodyReadComplete()\n        {\n            _responseReadCompleteCallback?.Invoke(_httpContext);\n        }\n\n        private bool RequestBodyReadInProgress()\n        {\n            try\n            {\n                return !_requestPipe.Reader.TryRead(out var result) || !result.IsCompleted;\n            }\n            catch (Exception ex)\n            {\n                throw new InvalidOperationException(\"An error occurred when completing the request. Request delegate may have finished while there is a pending read of the request body.\", ex);\n            }\n        }\n\n        internal async Task CompleteResponseAsync()\n        {\n            _pipelineFinished = true;\n            await ReturnResponseMessageAsync();\n            _responsePipeWriter.Complete();\n            await _responseFeature.FireOnResponseCompletedAsync();\n        }\n\n        internal async Task ReturnResponseMessageAsync()\n        {\n            // Check if the response is already returning because the TrySetResult below could happen a bit late\n            // (as it happens on a different thread) by which point the CompleteResponseAsync could run and calls this\n            // method again.\n            if (!_returningResponse)\n            {\n                _returningResponse = true;\n\n                try\n                {\n                    await _responseFeature.FireOnSendingHeadersAsync();\n                }\n                catch (Exception ex)\n                {\n                    Abort(ex);\n                    return;\n                }\n\n                // Copy the feature collection so we're not multi-threading on the same collection.\n                var newFeatures = new FeatureCollection();\n                foreach (var pair in _httpContext.Features)\n                {\n                    newFeatures[pair.Key] = pair.Value;\n                }\n                var serverResponseFeature = _httpContext.Features.Get<IHttpResponseFeature>()!;\n                // The client gets a deep copy of this so they can interact with the body stream independently of the server.\n                var clientResponseFeature = new HttpResponseFeature()\n                {\n                    StatusCode = serverResponseFeature.StatusCode,\n                    ReasonPhrase = serverResponseFeature.ReasonPhrase,\n                    Headers = serverResponseFeature.Headers,\n                    Body = _responseReaderStream\n                };\n                newFeatures.Set<IHttpResponseFeature>(clientResponseFeature);\n                newFeatures.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(_responseReaderStream));\n                _responseTcs.TrySetResult(new DefaultHttpContext(newFeatures));\n            }\n        }\n\n        internal void Abort(Exception exception)\n        {\n            _responsePipeWriter.Abort(exception);\n            _responseReaderStream.Abort(exception);\n            _requestLifetimeFeature.Cancel();\n            _responseTcs.TrySetException(exception);\n            CancelRequestBody();\n        }\n\n        private void CancelRequestBody()\n        {\n            _requestPipe.Writer.CancelPendingFlush();\n            _requestPipe.Reader.CancelPendingRead();\n        }\n\n        void IHttpResetFeature.Reset(int errorCode)\n        {\n            Abort(new HttpResetTestException(errorCode));\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/HttpResetTestException.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// Used to surface to the test client that the application invoked <see cref=\"IHttpResetFeature.Reset\"/>\n    /// </summary>\n    public class HttpResetTestException : Exception\n    {\n        /// <summary>\n        /// Creates a new test exception\n        /// </summary>\n        /// <param name=\"errorCode\">The error code passed to <see cref=\"IHttpResetFeature.Reset\"/></param>\n        public HttpResetTestException(int errorCode)\n            : base($\"The application reset the request with error code {errorCode}.\")\n        {\n            ErrorCode = errorCode;\n        }\n\n        /// <summary>\n        /// The error code passed to <see cref=\"IHttpResetFeature.Reset\"/>\n        /// </summary>\n        public int ErrorCode { get; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/IpcServer.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nusing System;\nusing System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing dotnetCampus.Ipc.PipeMvcServer.IpcFramework;\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Hosting.Server;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Options;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// An <see cref=\"IServer\"/> implementation for executing tests.\n    /// </summary>\n    public class IpcServer : IServer\n    {\n        private readonly IWebHost? _hostInstance;\n        private bool _disposed;\n        private ApplicationWrapper? _application;\n\n        /// <summary>\n        /// For use with IHostBuilder.\n        /// </summary>\n        /// <param name=\"services\"></param>\n        /// <param name=\"optionsAccessor\"></param>\n        public IpcServer(IServiceProvider services, IOptions<IpcServerOptions> optionsAccessor)\n            : this(services, new FeatureCollection(), optionsAccessor)\n        {\n\n        }\n\n        /// <summary>\n        /// For use with IHostBuilder.\n        /// </summary>\n        /// <param name=\"services\"></param>\n        /// <param name=\"featureCollection\"></param>\n        /// <param name=\"optionsAccessor\"></param>\n        public IpcServer(IServiceProvider services, IFeatureCollection featureCollection, IOptions<IpcServerOptions> optionsAccessor)\n        {\n            Services = services ?? throw new ArgumentNullException(nameof(services));\n            Features = featureCollection ?? throw new ArgumentNullException(nameof(featureCollection));\n            var options = optionsAccessor?.Value ?? throw new ArgumentNullException(nameof(optionsAccessor));\n            AllowSynchronousIO = options.AllowSynchronousIO;\n            PreserveExecutionContext = options.PreserveExecutionContext;\n            BaseAddress = options.BaseAddress;\n\n            var ipcCore = Services.GetRequiredService<IpcPipeMvcServerCore>();\n            IpcPipeMvcServerCore = ipcCore;\n        }\n\n        private IpcPipeMvcServerCore IpcPipeMvcServerCore { get; }\n\n        /// <summary>\n        /// For use with IHostBuilder.\n        /// </summary>\n        /// <param name=\"services\"></param>\n        public IpcServer(IServiceProvider services)\n            : this(services, new FeatureCollection())\n        {\n        }\n\n        /// <summary>\n        /// For use with IHostBuilder.\n        /// </summary>\n        /// <param name=\"services\"></param>\n        /// <param name=\"featureCollection\"></param>\n        public IpcServer(IServiceProvider services, IFeatureCollection featureCollection)\n            : this(services, featureCollection, Options.Create(new IpcServerOptions()))\n        {\n            Services = services ?? throw new ArgumentNullException(nameof(services));\n            Features = featureCollection ?? throw new ArgumentNullException(nameof(featureCollection));\n        }\n\n        /// <summary>\n        /// For use with IWebHostBuilder.\n        /// </summary>\n        /// <param name=\"builder\"></param>\n        public IpcServer(IWebHostBuilder builder)\n            : this(builder, new FeatureCollection())\n        {\n        }\n\n        /// <summary>\n        /// For use with IWebHostBuilder.\n        /// </summary>\n        /// <param name=\"builder\"></param>\n        /// <param name=\"featureCollection\"></param>\n        public IpcServer(IWebHostBuilder builder, IFeatureCollection featureCollection)\n        {\n            if (builder == null)\n            {\n                throw new ArgumentNullException(nameof(builder));\n            }\n\n            Features = featureCollection ?? throw new ArgumentNullException(nameof(featureCollection));\n\n            var host = builder.UseServer(this).Build();\n            host.StartAsync().GetAwaiter().GetResult();\n            _hostInstance = host;\n\n            Services = host.Services;\n\n            var ipcCore = Services.GetRequiredService<IpcPipeMvcServerCore>();\n            IpcPipeMvcServerCore = ipcCore;\n        }\n\n        /// <summary>\n        /// Gets or sets the base address associated with the HttpClient returned by the test server. Defaults to http://localhost/.\n        /// </summary>\n        public Uri BaseAddress { get; set; } = new Uri(IpcPipeMvcContext.BaseAddressUrl);\n\n        /// <summary>\n        /// Gets the <see cref=\"IWebHost\" /> instance associated with the test server.\n        /// </summary>\n        public IWebHost Host\n        {\n            get\n            {\n                return _hostInstance\n                    ?? throw new InvalidOperationException(\"The IpcServer constructor was not called with a IWebHostBuilder so IWebHost is not available.\");\n            }\n        }\n\n        /// <summary>\n        /// Gets the service provider associated with the test server.\n        /// </summary>\n        public IServiceProvider Services { get; }\n\n        /// <summary>\n        /// Gets the collection of server features associated with the test server.\n        /// </summary>\n        public IFeatureCollection Features { get; }\n\n        /// <summary>\n        /// Gets or sets a value that controls whether synchronous IO is allowed for the <see cref=\"HttpContext.Request\"/> and <see cref=\"HttpContext.Response\"/>. The default value is <see langword=\"false\" />.\n        /// </summary>\n        public bool AllowSynchronousIO { get; set; }\n\n        /// <summary>\n        /// Gets or sets a value that controls if <see cref=\"ExecutionContext\"/> and <see cref=\"AsyncLocal{T}\"/> values are preserved from the client to the server. The default value is <see langword=\"false\" />.\n        /// </summary>\n        public bool PreserveExecutionContext { get; set; }\n\n        internal ApplicationWrapper Application\n        {\n            get => _application ?? throw new InvalidOperationException(\"The server has not been started or no web application was configured.\");\n        }\n\n        /// <summary>\n        /// Creates a custom <see cref=\"HttpMessageHandler\" /> for processing HTTP requests/responses with the test server.\n        /// </summary>\n        public HttpMessageHandler CreateHandler()\n        {\n            var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);\n            return new ClientHandler(pathBase, Application) { AllowSynchronousIO = AllowSynchronousIO, PreserveExecutionContext = PreserveExecutionContext };\n        }\n\n        /// <summary>\n        /// Creates a <see cref=\"HttpClient\" /> for processing HTTP requests/responses with the test server.\n        /// </summary>\n        public HttpClient CreateClient()\n        {\n            return new HttpClient(CreateHandler()) { BaseAddress = BaseAddress };\n        }\n\n        ///// <summary>\n        ///// Creates a <see cref=\"WebSocketClient\" /> for interacting with the test server.\n        ///// </summary>\n        //public WebSocketClient CreateWebSocketClient()\n        //{\n        //    var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);\n        //    return new WebSocketClient(pathBase, Application) { AllowSynchronousIO = AllowSynchronousIO, PreserveExecutionContext = PreserveExecutionContext };\n        //}\n\n        ///// <summary>\n        ///// Begins constructing a request message for submission.\n        ///// </summary>\n        ///// <param name=\"path\"></param>\n        ///// <returns><see cref=\"RequestBuilder\"/> to use in constructing additional request details.</returns>\n        //public RequestBuilder CreateRequest(string path)\n        //{\n        //    return new RequestBuilder(this, path);\n        //}\n\n        /// <summary>\n        /// Creates, configures, sends, and returns a <see cref=\"HttpContext\"/>. This completes as soon as the response is started.\n        /// </summary>\n        /// <returns></returns>\n        public async Task<HttpContext> SendAsync(Action<HttpContext> configureContext, CancellationToken cancellationToken = default)\n        {\n            if (configureContext == null)\n            {\n                throw new ArgumentNullException(nameof(configureContext));\n            }\n\n            var builder = new HttpContextBuilder(Application, AllowSynchronousIO, PreserveExecutionContext);\n            builder.Configure((context, reader) =>\n            {\n                var request = context.Request;\n                request.Scheme = BaseAddress.Scheme;\n                request.Host = HostString.FromUriComponent(BaseAddress);\n                if (BaseAddress.IsDefaultPort)\n                {\n                    request.Host = new HostString(request.Host.Host);\n                }\n                var pathBase = PathString.FromUriComponent(BaseAddress);\n                if (pathBase.HasValue && pathBase.Value.EndsWith('/'))\n                {\n                    pathBase = new PathString(pathBase.Value[..^1]); // All but the last character.\n                }\n                request.PathBase = pathBase;\n            });\n            builder.Configure((context, reader) => configureContext(context));\n            // TODO: Wrap the request body if any?\n            return await builder.SendAsync(cancellationToken).ConfigureAwait(false);\n        }\n\n        /// <summary>\n        /// Dispoes the <see cref=\"IWebHost\" /> object associated with the test server.\n        /// </summary>\n        public void Dispose()\n        {\n            if (!_disposed)\n            {\n                _disposed = true;\n                _hostInstance?.Dispose();\n            }\n        }\n\n        Task IServer.StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)\n        {\n            _application = new ApplicationWrapper<TContext>(application, () =>\n            {\n                if (_disposed)\n                {\n                    throw new ObjectDisposedException(GetType().FullName);\n                }\n            });\n\n            IpcPipeMvcServerCore.Start();\n\n            return Task.CompletedTask;\n        }\n\n        Task IServer.StopAsync(CancellationToken cancellationToken)\n        {\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/IpcServerOptions.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nusing System;\nusing System.Threading;\nusing dotnetCampus.Ipc.PipeMvcServer.IpcFramework;\n\nusing Microsoft.AspNetCore.Http;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// Options for the test server.\n    /// </summary>\n    public class IpcServerOptions\n    {\n        /// <summary>\n        /// 获取或设置 Ipc 服务的管道名。如为空将默认使用随机的命名\n        /// </summary>\n        public string? IpcPipeName { set; get; }\n\n        /// <summary>\n        /// Gets a value that controls whether synchronous IO is allowed for the <see cref=\"HttpContext.Request\"/> and <see cref=\"HttpContext.Response\"/>. The default value is <see langword=\"false\" />.\n        /// </summary>\n        public bool AllowSynchronousIO => false;\n\n        /// <summary>\n        /// Gets or sets a value that controls if <see cref=\"ExecutionContext\"/> and <see cref=\"AsyncLocal{T}\"/> values are preserved from the client to the server. The default value is <see langword=\"false\" />.\n        /// </summary>\n        public bool PreserveExecutionContext { get; set; }\n\n        /// <summary>\n        /// Gets or sets the base address associated with the HttpClient returned by the test server. Defaults to http://localhost/ 也就是 <see cref=\"IpcPipeMvcContext.BaseAddressUrl\"/> 的值.\n        /// </summary>\n        public Uri BaseAddress { get; set; } = new Uri(IpcPipeMvcContext.BaseAddressUrl);\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/NoopHostLifetime.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.Extensions.Hosting;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class NoopHostLifetime : IHostLifetime\n    {\n        public Task StopAsync(CancellationToken cancellationToken)\n        {\n            return Task.CompletedTask;\n        }\n\n        public Task WaitForStartAsync(CancellationToken cancellationToken)\n        {\n            return Task.CompletedTask;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/RequestBodyDetectionFeature.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class RequestBodyDetectionFeature : IHttpRequestBodyDetectionFeature\n    {\n        public RequestBodyDetectionFeature(bool canHaveBody)\n        {\n            CanHaveBody = canHaveBody;\n        }\n\n        public bool CanHaveBody { get; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/RequestBuilder.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.IO;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// Used to construct a HttpRequestMessage object.\n    /// </summary>\n    internal class RequestBuilder\n    {\n        private readonly HttpRequestMessage _req;\n\n        /// <summary>\n        /// Construct a new HttpRequestMessage with the given path.\n        /// </summary>\n        /// <param name=\"server\"></param>\n        /// <param name=\"path\"></param>\n        public RequestBuilder(IpcServer server, string path)\n        {\n            IpcServer = server ?? throw new ArgumentNullException(nameof(server));\n            _req = new HttpRequestMessage(HttpMethod.Get, path);\n        }\n\n        /// <summary>\n        /// Gets the <see cref=\"IpcServer\"/> instance for which the request is being built.\n        /// </summary>\n        public IpcServer IpcServer { get; }\n\n        /// <summary>\n        /// Configure any HttpRequestMessage properties.\n        /// </summary>\n        /// <param name=\"configure\"></param>\n        /// <returns>This <see cref=\"RequestBuilder\"/> for chaining.</returns>\n        public RequestBuilder And(Action<HttpRequestMessage> configure)\n        {\n            if (configure == null)\n            {\n                throw new ArgumentNullException(nameof(configure));\n            }\n\n            configure(_req);\n            return this;\n        }\n\n        /// <summary>\n        /// Add the given header and value to the request or request content.\n        /// </summary>\n        /// <param name=\"name\"></param>\n        /// <param name=\"value\"></param>\n        /// <returns>This <see cref=\"RequestBuilder\"/> for chaining.</returns>\n        public RequestBuilder AddHeader(string name, string value)\n        {\n            if (!_req.Headers.TryAddWithoutValidation(name, value))\n            {\n                if (_req.Content == null)\n                {\n                    _req.Content = new StreamContent(Stream.Null);\n                }\n                if (!_req.Content.Headers.TryAddWithoutValidation(name, value))\n                {\n                    // TODO: throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Resources.InvalidHeaderName, name), \"name\");\n                    throw new ArgumentException(\"Invalid header name: \" + name, nameof(name));\n                }\n            }\n            return this;\n        }\n\n        /// <summary>\n        /// Set the request method and start processing the request.\n        /// </summary>\n        /// <param name=\"method\"></param>\n        /// <returns>The resulting <see cref=\"HttpResponseMessage\"/>.</returns>\n        public Task<HttpResponseMessage> SendAsync(string method)\n        {\n            _req.Method = new HttpMethod(method);\n            return IpcServer.CreateClient().SendAsync(_req);\n        }\n\n        /// <summary>\n        /// Set the request method to GET and start processing the request.\n        /// </summary>\n        /// <returns>The resulting <see cref=\"HttpResponseMessage\"/>.</returns>\n        public Task<HttpResponseMessage> GetAsync()\n        {\n            _req.Method = HttpMethod.Get;\n            return IpcServer.CreateClient().SendAsync(_req);\n        }\n\n        /// <summary>\n        /// Set the request method to POST and start processing the request.\n        /// </summary>\n        /// <returns>The resulting <see cref=\"HttpResponseMessage\"/>.</returns>\n        public Task<HttpResponseMessage> PostAsync()\n        {\n            _req.Method = HttpMethod.Post;\n            return IpcServer.CreateClient().SendAsync(_req);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/RequestFeature.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System.IO;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class RequestFeature : IHttpRequestFeature\n    {\n        public RequestFeature()\n        {\n            Body = Stream.Null;\n            Headers = new HeaderDictionary();\n            Method = \"GET\";\n            Path = \"\";\n            PathBase = \"\";\n            Protocol = HttpProtocol.Http11;\n            QueryString = \"\";\n            Scheme = \"http\";\n            RawTarget = \"\";\n        }\n\n        public Stream Body { get; set; }\n\n        public IHeaderDictionary Headers { get; set; }\n\n        public string Method { get; set; }\n\n        public string Path { get; set; }\n\n        public string PathBase { get; set; }\n\n        public string Protocol { get; set; }\n\n        public string QueryString { get; set; }\n\n        public string Scheme { get; set; }\n\n        public string RawTarget { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/RequestLifetimeFeature.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Threading;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class RequestLifetimeFeature : IHttpRequestLifetimeFeature\n    {\n        private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();\n        private readonly Action<Exception> _abort;\n\n        public RequestLifetimeFeature(Action<Exception> abort)\n        {\n            RequestAborted = _cancellationTokenSource.Token;\n            _abort = abort;\n        }\n\n        public CancellationToken RequestAborted { get; set; }\n\n        internal void Cancel()\n        {\n            _cancellationTokenSource.Cancel();\n        }\n\n        void IHttpRequestLifetimeFeature.Abort()\n        {\n            _abort(new Exception(\"The application aborted the request.\"));\n            _cancellationTokenSource.Cancel();\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ResponseBodyPipeWriter.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Diagnostics.Contracts;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class ResponseBodyPipeWriter : PipeWriter\n    {\n        private readonly Func<Task> _onFirstWriteAsync;\n        private readonly Pipe _pipe;\n\n        private bool _firstWrite;\n        private bool _complete;\n\n        internal ResponseBodyPipeWriter(Pipe pipe, Func<Task> onFirstWriteAsync)\n        {\n            _pipe = pipe ?? throw new ArgumentNullException(nameof(pipe));\n            _onFirstWriteAsync = onFirstWriteAsync ?? throw new ArgumentNullException(nameof(onFirstWriteAsync));\n            _firstWrite = true;\n        }\n\n        public override async ValueTask<FlushResult> FlushAsync(CancellationToken cancellationToken)\n        {\n            cancellationToken.ThrowIfCancellationRequested();\n            CheckNotComplete();\n\n            await FirstWriteAsync();\n            return await _pipe.Writer.FlushAsync(cancellationToken);\n        }\n\n        private Task FirstWriteAsync()\n        {\n            if (_firstWrite)\n            {\n                _firstWrite = false;\n                return _onFirstWriteAsync();\n            }\n            return Task.CompletedTask;\n        }\n\n        internal void Abort(Exception innerException)\n        {\n            Contract.Requires(innerException != null);\n            _complete = true;\n            _pipe.Writer.Complete(new IOException(string.Empty, innerException));\n        }\n\n        internal void Complete()\n        {\n            if (_complete)\n            {\n                return;\n            }\n\n            // Throw for further writes, but not reads. Allow reads to drain the buffered data and then return 0 for further reads.\n            _complete = true;\n            _pipe.Writer.Complete();\n        }\n\n        private void CheckNotComplete()\n        {\n            if (_complete)\n            {\n                throw new IOException(\"The request was aborted or the pipeline has finished.\");\n            }\n        }\n\n        public override void Complete(Exception? exception = null)\n        {\n            // No-op in the non-error case\n            if (exception != null)\n            {\n                Abort(exception);\n            }\n        }\n\n        public override void CancelPendingFlush() => _pipe.Writer.CancelPendingFlush();\n\n        public override void Advance(int bytes)\n        {\n            CheckNotComplete();\n            _pipe.Writer.Advance(bytes);\n        }\n\n        public override Memory<byte> GetMemory(int sizeHint = 0)\n        {\n            CheckNotComplete();\n            return _pipe.Writer.GetMemory(sizeHint);\n        }\n\n        public override Span<byte> GetSpan(int sizeHint = 0)\n        {\n            CheckNotComplete();\n            return _pipe.Writer.GetSpan(sizeHint);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ResponseBodyReaderStream.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Buffers;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// The client's view of the response body.\n    /// </summary>\n    internal class ResponseBodyReaderStream : Stream\n    {\n        private bool _readerComplete;\n        private bool _aborted;\n        private Exception? _abortException;\n\n        private readonly object _abortLock = new object();\n        private readonly Action _abortRequest;\n        private readonly Action _readComplete;\n        private readonly Pipe _pipe;\n\n        internal ResponseBodyReaderStream(Pipe pipe, Action abortRequest, Action readComplete)\n        {\n            _pipe = pipe ?? throw new ArgumentNullException(nameof(pipe));\n            _abortRequest = abortRequest ?? throw new ArgumentNullException(nameof(abortRequest));\n            _readComplete = readComplete;\n        }\n\n        public override bool CanRead => true;\n\n        public override bool CanSeek => false;\n\n        public override bool CanWrite => false;\n\n        #region NotSupported\n\n        public override long Length => throw new NotSupportedException();\n\n        public override long Position\n        {\n            get => throw new NotSupportedException();\n            set => throw new NotSupportedException();\n        }\n\n        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();\n\n        public override void SetLength(long value) => throw new NotSupportedException();\n\n        public override void Flush() => throw new NotSupportedException();\n\n        public override Task FlushAsync(CancellationToken cancellationToken) => throw new NotSupportedException();\n\n        // Write with count 0 will still trigger OnFirstWrite\n        public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();\n\n        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException();\n\n        #endregion NotSupported\n\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult();\n        }\n\n        public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)\n        {\n            VerifyBuffer(buffer, offset, count);\n            CheckAborted();\n\n            if (_readerComplete)\n            {\n                return 0;\n            }\n\n            using var registration = cancellationToken.Register(Cancel);\n            var result = await _pipe.Reader.ReadAsync(cancellationToken);\n\n            if (result.IsCanceled)\n            {\n                throw new OperationCanceledException();\n            }\n\n            if (result.Buffer.IsEmpty && result.IsCompleted)\n            {\n                _readComplete();\n                _readerComplete = true;\n                return 0;\n            }\n\n            var readableBuffer = result.Buffer;\n            var actual = Math.Min(readableBuffer.Length, count);\n            readableBuffer = readableBuffer.Slice(0, actual);\n            readableBuffer.CopyTo(new Span<byte>(buffer, offset, count));\n            _pipe.Reader.AdvanceTo(readableBuffer.End);\n            return (int) actual;\n        }\n\n        private static void VerifyBuffer(byte[] buffer, int offset, int count)\n        {\n            if (buffer == null)\n            {\n                throw new ArgumentNullException(nameof(buffer));\n            }\n            if (offset < 0 || offset > buffer.Length)\n            {\n                throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty);\n            }\n            if (count <= 0 || count > buffer.Length - offset)\n            {\n                throw new ArgumentOutOfRangeException(nameof(count), count, string.Empty);\n            }\n        }\n\n        internal void Cancel()\n        {\n            Abort(new OperationCanceledException());\n        }\n\n        internal void Abort(Exception innerException)\n        {\n            Debug.Assert(innerException != null);\n\n            lock (_abortLock)\n            {\n                _abortException = innerException;\n                _aborted = true;\n            }\n\n            _pipe.Reader.CancelPendingRead();\n        }\n\n        private void CheckAborted()\n        {\n            lock (_abortLock)\n            {\n                if (_aborted)\n                {\n                    throw new IOException(string.Empty, _abortException);\n                }\n            }\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            if (disposing)\n            {\n                _abortRequest();\n            }\n\n            _pipe.Reader.Complete();\n\n            base.Dispose(disposing);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ResponseBodyWriterStream.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.IO;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class ResponseBodyWriterStream : Stream\n    {\n        private readonly ResponseBodyPipeWriter _responseWriter;\n        private readonly Func<bool> _allowSynchronousIO;\n\n        public ResponseBodyWriterStream(ResponseBodyPipeWriter responseWriter, Func<bool> allowSynchronousIO)\n        {\n            _responseWriter = responseWriter;\n            _allowSynchronousIO = allowSynchronousIO;\n        }\n\n        public override bool CanRead => false;\n\n        public override bool CanSeek => false;\n\n        public override bool CanWrite => true;\n\n        public override long Length => throw new NotSupportedException();\n\n        public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }\n\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override long Seek(long offset, SeekOrigin origin)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override void SetLength(long value)\n        {\n            throw new NotSupportedException();\n        }\n\n        public override void Flush()\n        {\n            if (!_allowSynchronousIO())\n            {\n                throw new InvalidOperationException(\"Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.\");\n            }\n\n            FlushAsync().GetAwaiter().GetResult();\n        }\n\n        public override async Task FlushAsync(CancellationToken cancellationToken)\n        {\n            await _responseWriter.FlushAsync(cancellationToken);\n        }\n\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            if (!_allowSynchronousIO())\n            {\n                throw new InvalidOperationException(\"Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true.\");\n            }\n\n            // The Pipe Write method requires calling FlushAsync to notify the reader. Call WriteAsync instead.\n            WriteAsync(buffer, offset, count).GetAwaiter().GetResult();\n        }\n\n        public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)\n        {\n            await _responseWriter.WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken);\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ResponseFeature.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class ResponseFeature : IHttpResponseFeature, IHttpResponseBodyFeature\n    {\n        private readonly HeaderDictionary _headers = new HeaderDictionary();\n        private readonly Action<Exception> _abort;\n\n        private Func<Task> _responseStartingAsync = () => Task.CompletedTask;\n        private Func<Task> _responseCompletedAsync = () => Task.CompletedTask;\n        private int _statusCode;\n        private string? _reasonPhrase;\n\n        public ResponseFeature(Action<Exception> abort)\n        {\n            Headers = _headers;\n\n            // 200 is the default status code all the way down to the host, so we set it\n            // here to be consistent with the rest of the hosts when writing tests.\n            StatusCode = 200;\n            _abort = abort;\n        }\n\n        public int StatusCode\n        {\n            get => _statusCode;\n            set\n            {\n                if (HasStarted)\n                {\n                    throw new InvalidOperationException(\"The status code cannot be set, the response has already started.\");\n                }\n                if (value < 100)\n                {\n                    throw new ArgumentOutOfRangeException(nameof(value), value, \"The status code cannot be set to a value less than 100\");\n                }\n\n                _statusCode = value;\n            }\n        }\n\n        public string? ReasonPhrase\n        {\n            get => _reasonPhrase;\n            set\n            {\n                if (HasStarted)\n                {\n                    throw new InvalidOperationException(\"The reason phrase cannot be set, the response has already started.\");\n                }\n\n                _reasonPhrase = value;\n            }\n        }\n\n        public IHeaderDictionary Headers { get; set; }\n\n        public Stream Body { get; set; } = default!;\n\n        public Stream Stream => Body;\n\n        internal PipeWriter BodyWriter { get; set; } = default!;\n\n        public PipeWriter Writer => BodyWriter;\n\n        public bool HasStarted { get; set; }\n\n        public void OnStarting(Func<object, Task> callback, object state)\n        {\n            if (HasStarted)\n            {\n                throw new InvalidOperationException();\n            }\n\n            var prior = _responseStartingAsync;\n            _responseStartingAsync = async () =>\n            {\n                await callback(state);\n                await prior();\n            };\n        }\n\n        public void OnCompleted(Func<object, Task> callback, object state)\n        {\n            var prior = _responseCompletedAsync;\n            _responseCompletedAsync = async () =>\n            {\n                try\n                {\n                    await callback(state);\n                }\n                finally\n                {\n                    await prior();\n                }\n            };\n        }\n\n        public async Task FireOnSendingHeadersAsync()\n        {\n            if (!HasStarted)\n            {\n                try\n                {\n                    await _responseStartingAsync();\n                }\n                finally\n                {\n                    HasStarted = true;\n                    _headers.IsReadOnly = true;\n                }\n            }\n        }\n\n        public Task FireOnResponseCompletedAsync()\n        {\n            return _responseCompletedAsync();\n        }\n\n        public async Task StartAsync(CancellationToken token = default)\n        {\n            try\n            {\n                await FireOnSendingHeadersAsync();\n            }\n            catch (Exception ex)\n            {\n                _abort(ex);\n                throw;\n            }\n        }\n\n        public void DisableBuffering()\n        {\n        }\n\n        public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellation)\n        {\n            return SendFileFallback.SendFileAsync(Stream, path, offset, count, cancellation);\n        }\n\n        public Task CompleteAsync()\n        {\n            return Writer.CompleteAsync().AsTask();\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/ResponseTrailersFeature.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class ResponseTrailersFeature : IHttpResponseTrailersFeature\n    {\n        public IHeaderDictionary Trailers { get; set; } = new HeaderDictionary();\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/TestWebSocket.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net.WebSockets;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class TestWebSocket : WebSocket\n    {\n        private readonly ReceiverSenderBuffer _receiveBuffer;\n        private readonly ReceiverSenderBuffer _sendBuffer;\n        private readonly string? _subProtocol;\n        private WebSocketState _state;\n        private WebSocketCloseStatus? _closeStatus;\n        private string? _closeStatusDescription;\n        private Message? _receiveMessage;\n\n        public static Tuple<TestWebSocket, TestWebSocket> CreatePair(string? subProtocol)\n        {\n            var buffers = new[] { new ReceiverSenderBuffer(), new ReceiverSenderBuffer() };\n            return Tuple.Create(\n                new TestWebSocket(subProtocol, buffers[0], buffers[1]),\n                new TestWebSocket(subProtocol, buffers[1], buffers[0]));\n        }\n\n        public override WebSocketCloseStatus? CloseStatus\n        {\n            get { return _closeStatus; }\n        }\n\n        public override string? CloseStatusDescription\n        {\n            get { return _closeStatusDescription; }\n        }\n\n        public override WebSocketState State\n        {\n            get { return _state; }\n        }\n\n        public override string? SubProtocol\n        {\n            get { return _subProtocol; }\n        }\n\n        public override async Task CloseAsync(WebSocketCloseStatus closeStatus, string? statusDescription, CancellationToken cancellationToken)\n        {\n            ThrowIfDisposed();\n\n            if (State == WebSocketState.Open || State == WebSocketState.CloseReceived)\n            {\n                // Send a close message.\n                await CloseOutputAsync(closeStatus, statusDescription, cancellationToken);\n            }\n\n            if (State == WebSocketState.CloseSent)\n            {\n                // Do a receiving drain\n                var data = new byte[1024];\n                WebSocketReceiveResult result;\n                do\n                {\n                    result = await ReceiveAsync(new ArraySegment<byte>(data), cancellationToken);\n                }\n                while (result.MessageType != WebSocketMessageType.Close);\n            }\n        }\n\n        public override async Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string? statusDescription, CancellationToken cancellationToken)\n        {\n            ThrowIfDisposed();\n            ThrowIfOutputClosed();\n\n            var message = new Message(closeStatus, statusDescription);\n            await _sendBuffer.SendAsync(message, cancellationToken);\n\n            if (State == WebSocketState.Open)\n            {\n                _state = WebSocketState.CloseSent;\n            }\n            else if (State == WebSocketState.CloseReceived)\n            {\n                _state = WebSocketState.Closed;\n                Close();\n            }\n        }\n\n        public override void Abort()\n        {\n            if (_state >= WebSocketState.Closed) // or Aborted\n            {\n                return;\n            }\n\n            _state = WebSocketState.Aborted;\n            Close();\n        }\n\n        public override void Dispose()\n        {\n            if (_state >= WebSocketState.Closed) // or Aborted\n            {\n                return;\n            }\n\n            _state = WebSocketState.Closed;\n            Close();\n        }\n\n        public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)\n        {\n            ThrowIfDisposed();\n            ThrowIfInputClosed();\n            ValidateSegment(buffer);\n            // TODO: InvalidOperationException if any receives are currently in progress.\n\n            Message? receiveMessage = _receiveMessage;\n            _receiveMessage = null;\n            if (receiveMessage == null)\n            {\n                receiveMessage = await _receiveBuffer.ReceiveAsync(cancellationToken);\n            }\n            if (receiveMessage.MessageType == WebSocketMessageType.Close)\n            {\n                _closeStatus = receiveMessage.CloseStatus;\n                _closeStatusDescription = receiveMessage.CloseStatusDescription ?? string.Empty;\n                var result = new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, _closeStatus, _closeStatusDescription);\n                if (_state == WebSocketState.Open)\n                {\n                    _state = WebSocketState.CloseReceived;\n                }\n                else if (_state == WebSocketState.CloseSent)\n                {\n                    _state = WebSocketState.Closed;\n                    Close();\n                }\n                return result;\n            }\n            else\n            {\n                int count = Math.Min(buffer.Count, receiveMessage.Buffer.Count);\n                bool endOfMessage = count == receiveMessage.Buffer.Count;\n                Array.Copy(receiveMessage.Buffer.Array!, receiveMessage.Buffer.Offset, buffer.Array!, buffer.Offset, count);\n                if (!endOfMessage)\n                {\n                    receiveMessage.Buffer = new ArraySegment<byte>(receiveMessage.Buffer.Array!, receiveMessage.Buffer.Offset + count, receiveMessage.Buffer.Count - count);\n                    _receiveMessage = receiveMessage;\n                }\n                endOfMessage = endOfMessage && receiveMessage.EndOfMessage;\n                return new WebSocketReceiveResult(count, receiveMessage.MessageType, endOfMessage);\n            }\n        }\n\n        public override Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken)\n        {\n            ValidateSegment(buffer);\n            if (messageType != WebSocketMessageType.Binary && messageType != WebSocketMessageType.Text)\n            {\n                // Block control frames\n                throw new ArgumentOutOfRangeException(nameof(messageType), messageType, string.Empty);\n            }\n\n            var message = new Message(buffer, messageType, endOfMessage);\n            return _sendBuffer.SendAsync(message, cancellationToken);\n        }\n\n        private void Close()\n        {\n            _receiveBuffer.SetReceiverClosed();\n            _sendBuffer.SetSenderClosed();\n        }\n\n        private void ThrowIfDisposed()\n        {\n            if (_state >= WebSocketState.Closed) // or Aborted\n            {\n                throw new ObjectDisposedException(typeof(TestWebSocket).FullName);\n            }\n        }\n\n        private void ThrowIfOutputClosed()\n        {\n            if (State == WebSocketState.CloseSent)\n            {\n                throw new InvalidOperationException(\"Close already sent.\");\n            }\n        }\n\n        private void ThrowIfInputClosed()\n        {\n            if (State == WebSocketState.CloseReceived)\n            {\n                throw new InvalidOperationException(\"Close already received.\");\n            }\n        }\n\n        private void ValidateSegment(ArraySegment<byte> buffer)\n        {\n            if (buffer.Array == null)\n            {\n                throw new ArgumentNullException(nameof(buffer));\n            }\n            if (buffer.Offset < 0 || buffer.Offset > buffer.Array.Length)\n            {\n                throw new ArgumentOutOfRangeException(nameof(buffer), buffer.Offset, string.Empty);\n            }\n            if (buffer.Count < 0 || buffer.Count > buffer.Array.Length - buffer.Offset)\n            {\n                throw new ArgumentOutOfRangeException(nameof(buffer), buffer.Count, string.Empty);\n            }\n        }\n\n        private TestWebSocket(string? subProtocol, ReceiverSenderBuffer readBuffer, ReceiverSenderBuffer writeBuffer)\n        {\n            _state = WebSocketState.Open;\n            _subProtocol = subProtocol;\n            _receiveBuffer = readBuffer;\n            _sendBuffer = writeBuffer;\n        }\n\n        private class Message\n        {\n            public Message(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage)\n            {\n                Buffer = buffer;\n                CloseStatus = null;\n                CloseStatusDescription = null;\n                EndOfMessage = endOfMessage;\n                MessageType = messageType;\n            }\n\n            public Message(WebSocketCloseStatus? closeStatus, string? closeStatusDescription)\n            {\n                Buffer = new ArraySegment<byte>(Array.Empty<byte>());\n                CloseStatus = closeStatus;\n                CloseStatusDescription = closeStatusDescription;\n                MessageType = WebSocketMessageType.Close;\n                EndOfMessage = true;\n            }\n\n            public WebSocketCloseStatus? CloseStatus { get; set; }\n            public string? CloseStatusDescription { get; set; }\n            public ArraySegment<byte> Buffer { get; set; }\n            public bool EndOfMessage { get; set; }\n            public WebSocketMessageType MessageType { get; set; }\n        }\n\n        private class ReceiverSenderBuffer\n        {\n            private bool _receiverClosed;\n            private bool _senderClosed;\n            private bool _disposed;\n            private readonly SemaphoreSlim _sem;\n            private readonly Queue<Message> _messageQueue;\n\n            public ReceiverSenderBuffer()\n            {\n                _sem = new SemaphoreSlim(0);\n                _messageQueue = new Queue<Message>();\n            }\n\n            public virtual async Task<Message> ReceiveAsync(CancellationToken cancellationToken)\n            {\n                if (_disposed)\n                {\n                    ThrowNoReceive();\n                }\n                await _sem.WaitAsync(cancellationToken);\n                lock (_messageQueue)\n                {\n                    if (_messageQueue.Count == 0)\n                    {\n                        _disposed = true;\n                        _sem.Dispose();\n                        ThrowNoReceive();\n                    }\n                    return _messageQueue.Dequeue();\n                }\n            }\n\n            public virtual Task SendAsync(Message message, CancellationToken cancellationToken)\n            {\n                lock (_messageQueue)\n                {\n                    if (_senderClosed)\n                    {\n                        throw new ObjectDisposedException(typeof(TestWebSocket).FullName);\n                    }\n                    if (_receiverClosed)\n                    {\n                        throw new IOException(\"The remote end closed the connection.\", new ObjectDisposedException(typeof(TestWebSocket).FullName));\n                    }\n\n                    // we return immediately so we need to copy the buffer since the sender can re-use it\n                    var array = new byte[message.Buffer.Count];\n                    Array.Copy(message.Buffer.Array!, message.Buffer.Offset, array, 0, message.Buffer.Count);\n                    message.Buffer = new ArraySegment<byte>(array);\n\n                    _messageQueue.Enqueue(message);\n                    _sem.Release();\n\n                    return Task.FromResult(true);\n                }\n            }\n\n            public void SetReceiverClosed()\n            {\n                lock (_messageQueue)\n                {\n                    if (!_receiverClosed)\n                    {\n                        _receiverClosed = true;\n                        if (!_disposed)\n                        {\n                            _sem.Release();\n                        }\n                    }\n                }\n            }\n\n            public void SetSenderClosed()\n            {\n                lock (_messageQueue)\n                {\n                    if (!_senderClosed)\n                    {\n                        _senderClosed = true;\n                        if (!_disposed)\n                        {\n                            _sem.Release();\n                        }\n                    }\n                }\n            }\n\n            private void ThrowNoReceive()\n            {\n                if (_receiverClosed)\n                {\n                    throw new ObjectDisposedException(typeof(TestWebSocket).FullName);\n                }\n                else // _senderClosed\n                {\n                    throw new IOException(\"The remote end closed the connection.\", new ObjectDisposedException(typeof(TestWebSocket).FullName));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/UpgradeFeature.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http.Features;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    internal class UpgradeFeature : IHttpUpgradeFeature\n    {\n        public bool IsUpgradableRequest => false;\n\n        // TestHost provides an IHttpWebSocketFeature so it wont call UpgradeAsync()\n        public Task<Stream> UpgradeAsync()\n        {\n            throw new NotSupportedException();\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/WebHostBuilderFactory.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    ///// <summary>\n    ///// A factory for creating <see cref=\"IWebHostBuilder\" /> instances.\n    ///// </summary>\n    //public static class WebHostBuilderFactory\n    //{\n    //    /// <summary>\n    //    /// Resolves an <see cref=\"IWebHostBuilder\" /> defined in the entry point of an assembly.\n    //    /// </summary>\n    //    /// <param name=\"assembly\">The assembly to look for an <see cref=\"IWebHostBuilder\"/> in.</param>\n    //    /// <param name=\"args\">The arguments to use when creating the <see cref=\"IWebHostBuilder\"/> instance.</param>\n    //    /// <returns>An <see cref=\"IWebHostBuilder\"/> instance retrieved from the assembly in <paramref name=\"assembly\"/>.</returns>\n    //    public static IWebHostBuilder? CreateFromAssemblyEntryPoint(Assembly assembly, string[] args)\n    //    {\n    //        var factory = HostFactoryResolver.ResolveWebHostBuilderFactory<IWebHostBuilder>(assembly);\n    //        return factory?.Invoke(args);\n    //    }\n\n    //    /// <summary>\n    //    /// Resolves an <see cref=\"IWebHostBuilder\" /> defined in an assembly where <typeparamref name=\"T\"/> is declared.\n    //    /// </summary>\n    //    /// <param name=\"args\">The arguments to use when creating the <see cref=\"IWebHostBuilder\"/> instance.</param>\n    //    /// <typeparam name=\"T\">Type contained in the target assembly</typeparam>\n    //    /// <returns>An <see cref=\"IWebHostBuilder\"/> instance retrieved from the assembly.</returns>\n    //    public static IWebHostBuilder? CreateFromTypesAssemblyEntryPoint<T>(string[] args) =>\n    //        CreateFromAssemblyEntryPoint(typeof(T).Assembly, args);\n    //}\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/HostFramework/WebSocketClient.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n// Copy From: https://github.com/dotnet/aspnetcore\n\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Net.WebSockets;\nusing System.Security.Cryptography;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Microsoft.AspNetCore.Http;\nusing Microsoft.AspNetCore.Http.Features;\nusing Microsoft.Net.Http.Headers;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.HostFramework\n{\n    /// <summary>\n    /// Provides a client for connecting over WebSockets to a test server.\n    /// </summary>\n    class WebSocketClient\n    {\n        private readonly ApplicationWrapper _application;\n        private readonly PathString _pathBase;\n\n        internal WebSocketClient(PathString pathBase, ApplicationWrapper application)\n        {\n            _application = application ?? throw new ArgumentNullException(nameof(application));\n\n            // PathString.StartsWithSegments that we use below requires the base path to not end in a slash.\n            if (pathBase.HasValue && pathBase.Value.EndsWith('/'))\n            {\n                pathBase = new PathString(pathBase.Value[..^1]); // All but the last character.\n            }\n            _pathBase = pathBase;\n\n            SubProtocols = new List<string>();\n        }\n\n        /// <summary>\n        /// Gets the list of WebSocket subprotocols that are established in the initial handshake.\n        /// </summary>\n        public IList<string> SubProtocols { get; }\n\n        /// <summary>\n        /// Gets or sets the handler used to configure the outgoing request to the WebSocket endpoint.\n        /// </summary>\n        public Action<HttpRequest>? ConfigureRequest { get; set; }\n\n        internal bool AllowSynchronousIO { get; set; }\n        internal bool PreserveExecutionContext { get; set; }\n\n        /// <summary>\n        /// Establishes a WebSocket connection to an endpoint.\n        /// </summary>\n        /// <param name=\"uri\">The <see cref=\"Uri\" /> of the endpoint.</param>\n        /// <param name=\"cancellationToken\">A <see cref=\"CancellationToken\"/> used to terminate the connection.</param>\n        public async Task<WebSocket> ConnectAsync(Uri uri, CancellationToken cancellationToken)\n        {\n            WebSocketFeature? webSocketFeature = null;\n            var contextBuilder = new HttpContextBuilder(_application, AllowSynchronousIO, PreserveExecutionContext);\n            contextBuilder.Configure((context, reader) =>\n            {\n                var request = context.Request;\n                var scheme = uri.Scheme;\n                scheme = (scheme == \"ws\") ? \"http\" : scheme;\n                scheme = (scheme == \"wss\") ? \"https\" : scheme;\n                request.Scheme = scheme;\n                if (!request.Host.HasValue)\n                {\n                    request.Host = uri.IsDefaultPort\n                        ? new HostString(HostString.FromUriComponent(uri).Host)\n                        : HostString.FromUriComponent(uri);\n                }\n                request.Path = PathString.FromUriComponent(uri);\n                request.PathBase = PathString.Empty;\n                if (request.Path.StartsWithSegments(_pathBase, out var remainder))\n                {\n                    request.Path = remainder;\n                    request.PathBase = _pathBase;\n                }\n                request.QueryString = QueryString.FromUriComponent(uri);\n                request.Headers.Add(HeaderNames.Connection, new string[] { \"Upgrade\" });\n                request.Headers.Add(HeaderNames.Upgrade, new string[] { \"websocket\" });\n                request.Headers.Add(HeaderNames.SecWebSocketVersion, new string[] { \"13\" });\n                request.Headers.Add(HeaderNames.SecWebSocketKey, new string[] { CreateRequestKey() });\n                if (SubProtocols.Any())\n                {\n                    request.Headers.Add(HeaderNames.SecWebSocketProtocol, SubProtocols.ToArray());\n                }\n\n                request.Body = Stream.Null;\n\n                // WebSocket\n                webSocketFeature = new WebSocketFeature(context);\n                context.Features.Set<IHttpWebSocketFeature>(webSocketFeature);\n\n                ConfigureRequest?.Invoke(context.Request);\n            });\n\n            var httpContext = await contextBuilder.SendAsync(cancellationToken);\n\n            if (httpContext.Response.StatusCode != StatusCodes.Status101SwitchingProtocols)\n            {\n                throw new InvalidOperationException(\"Incomplete handshake, status code: \" + httpContext.Response.StatusCode);\n            }\n\n            Debug.Assert(webSocketFeature != null);\n            if (webSocketFeature.ClientWebSocket == null)\n            {\n                throw new InvalidOperationException(\"Incomplete handshake\");\n            }\n\n            return webSocketFeature.ClientWebSocket;\n        }\n\n        private string CreateRequestKey()\n        {\n            byte[] data = new byte[16];\n            RandomNumberGenerator.Fill(data);\n            return Convert.ToBase64String(data);\n        }\n\n        private class WebSocketFeature : IHttpWebSocketFeature\n        {\n            private readonly HttpContext _httpContext;\n\n            public WebSocketFeature(HttpContext context)\n            {\n                _httpContext = context;\n            }\n\n            bool IHttpWebSocketFeature.IsWebSocketRequest => true;\n\n            public WebSocket? ClientWebSocket { get; private set; }\n\n            public WebSocket? ServerWebSocket { get; private set; }\n\n            async Task<WebSocket> IHttpWebSocketFeature.AcceptAsync(WebSocketAcceptContext context)\n            {\n                var websockets = TestWebSocket.CreatePair(context.SubProtocol);\n                if (_httpContext.Response.HasStarted)\n                {\n                    throw new InvalidOperationException(\"The response has already started\");\n                }\n\n                _httpContext.Response.StatusCode = StatusCodes.Status101SwitchingProtocols;\n                ClientWebSocket = websockets.Item1;\n                ServerWebSocket = websockets.Item2;\n                await _httpContext.Response.Body.FlushAsync(_httpContext.RequestAborted); // Send headers to the client\n                return ServerWebSocket;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/IpcFramework/IpcPipeMvcServerCore.cs",
    "content": "﻿using System;\nusing System.Threading;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.PipeMvcServer.HostFramework;\nusing dotnetCampus.Ipc.Pipes;\n\nusing Microsoft.AspNetCore.Hosting.Server;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class IpcPipeMvcServerCore\n    {\n        public IpcPipeMvcServerCore(IServiceProvider serviceProvider, string? ipcServerName)\n        {\n            ipcServerName ??= \"IpcPipeMvcServer\" + Guid.NewGuid().ToString(\"N\");\n\n            IpcServer = new IpcProvider(ipcServerName, new IpcConfiguration()\n            {\n                DefaultIpcRequestHandler = new DelegateIpcRequestHandler(async context =>\n                {\n                    var server = (IpcServer) serviceProvider.GetRequiredService<IServer>();\n\n                    var requestMessage = HttpMessageSerializer.DeserializeToRequest(context.IpcBufferMessage.Body);\n\n                    var clientHandler = (ClientHandler) server.CreateHandler();\n                    var response = await clientHandler.SendInnerAsync(requestMessage, CancellationToken.None);\n\n                    var responseByteList = HttpMessageSerializer.Serialize(response);\n\n                    return new IpcResponseMessageResult(new IpcMessage($\"[Response][{requestMessage.Method}] {requestMessage.RequestUri}\", responseByteList));\n                })\n            });\n        }\n\n        public void Start() => IpcServer.StartServer();\n        public IpcProvider IpcServer { set; get; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/Properties/launchSettings.json",
    "content": "{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false,\n    \"anonymousAuthentication\": true,\n    \"iisExpress\": {\n      \"applicationUrl\": \"http://localhost:57002/\",\n      \"sslPort\": 44362\n    }\n  },\n  \"profiles\": {\n    \"IIS Express\": {\n      \"commandName\": \"IISExpress\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n    \"dotnetCampus.Ipc.PipeMvcServer\": {\n      \"commandName\": \"Project\",\n      \"launchBrowser\": true,\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      },\n      \"applicationUrl\": \"https://localhost:5001;http://localhost:5000\"\n    }\n  }\n}"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/WebHostBuilderExtensions.cs",
    "content": "﻿// Licensed to the .NET Foundation under one or more agreements.\n// The .NET Foundation licenses this file to you under the MIT license.\n\nusing System;\n\nusing dotnetCampus.Ipc.PipeMvcServer.HostFramework;\nusing dotnetCampus.Ipc.PipeMvcServer.IpcFramework;\n\nusing Microsoft.AspNetCore.Hosting;\nusing Microsoft.AspNetCore.Hosting.Server;\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.Extensions.Options;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer\n{\n    /// <summary>\n    /// Contains extensions for configuring the <see cref=\"IWebHostBuilder\" /> instance.\n    /// </summary>\n    public static class WebHostBuilderExtensions\n    {\n        /// <summary>\n        /// Enables the <see cref=\"IpcServer\" /> service. 启用命名管道IPC服务\n        /// </summary>\n        /// <param name=\"builder\">The <see cref=\"IWebHostBuilder\"/>.</param>\n        /// <param name=\"ipcPipeName\">设置 Ipc 服务的管道名</param>\n        /// <returns>The <see cref=\"IWebHostBuilder\"/>.</returns>\n        public static IWebHostBuilder UsePipeIpcServer(this IWebHostBuilder builder, string ipcPipeName)\n        {\n            return builder.ConfigureServices(services =>\n            {\n                services.AddSingleton<IHostLifetime, NoopHostLifetime>();\n                services.AddSingleton<IServer, IpcServer>();\n\n                services.AddSingleton<IpcPipeMvcServerCore>(s => new IpcPipeMvcServerCore(s, ipcPipeName));\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/dotnetCampus.Ipc.PipeMvcServer.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <Description>NamedPipeStreamForMvc</Description>\n    <TargetFramework>net5.0</TargetFramework>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <Nullable>enable</Nullable>\n    <OutputType>Library</OutputType>\n    <IsPackable>true</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.AspNetCore.Hosting\" />\n    <PackageReference Include=\"System.IO.Pipelines\" />\n    <!-- <PackageReference Include=\"Microsoft.Extensions.HostFactoryResolver.Sources\" /> -->\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n  <Import Project=\"..\\dotnetCampus.Ipc.PipeMvcShare\\dotnetCampus.Ipc.PipeMvcShare.projitems\" Label=\"Shared\" />\n\n</Project>\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcServer/dotnetCampus.Ipc.PipeMvcServer.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=hostframework/@EntryIndexedValue\">False</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ipcframework/@EntryIndexedValue\">False</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HeaderContent.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System.Collections.Generic;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HeaderContent\n    {\n        public string Key { set; get; }\n        public List<string> Value { set; get; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpMessageSerializer.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System.Net.Http;\nusing System.Text;\n\nusing dotnetCampus.Ipc.Messages;\n\nusing Newtonsoft.Json;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    static class HttpMessageSerializer\n    {\n        public static byte[] Serialize(HttpResponseMessage response)\n        {\n            var httpResponseMessageContentBase = new HttpResponseMessageSerializeContent(response);\n            var json = JsonConvert.SerializeObject(httpResponseMessageContentBase);\n\n            return Encoding.UTF8.GetBytes(json);\n        }\n\n        public static byte[] Serialize(HttpRequestMessage request)\n        {\n            var json = JsonConvert.SerializeObject(new HttpRequestMessageSerializeContent(request));\n\n            return Encoding.UTF8.GetBytes(json);\n        }\n\n        internal static HttpResponseMessage DeserializeToResponse(IpcMessageBody body)\n        {\n            var span = body.AsSpan();\n            var json = Encoding.UTF8.GetString(span);\n            var content = JsonConvert.DeserializeObject<HttpResponseMessageDeserializeContent>(json);\n\n            return content.ToHttpResponseMessage();\n        }\n\n        public static HttpRequestMessage DeserializeToRequest(IpcMessageBody body)\n        {\n            var span = body.AsSpan();\n            var json = Encoding.UTF8.GetString(span);\n            var httpRequestMessageDeserializeContent = JsonConvert.DeserializeObject<HttpRequestMessageDeserializeContent>(json);\n            return httpRequestMessageDeserializeContent.ToHttpResponseMessage();\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpRequestMessageContentBase.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System;\nusing System.IO;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Threading;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HttpRequestMessageContentBase\n    {\n        public HttpRequestMessageContentBase(HttpRequestMessage message)\n        {\n            Version = message.Version;\n            VersionPolicy = message.VersionPolicy;\n            Method = message.Method;\n            RequestUri = message.RequestUri;\n            Options = message.Options;\n\n            if (message.Content != null)\n            {\n                ContentHeaders = message.Content.Headers;\n                using var memoryStream = new MemoryStream();\n                message.Content.CopyTo(memoryStream, null, CancellationToken.None);\n                ContentBase64 = Convert.ToBase64String(memoryStream.ToArray());\n            }\n        }\n\n        public HttpRequestMessageContentBase()\n        {\n        }\n\n\n        public string ContentBase64 { set; get; }\n\n        public Version Version { set; get; }\n        public HttpVersionPolicy VersionPolicy { set; get; }\n\n        public HttpMethod Method { set; get; }\n        public Uri? RequestUri { set; get; }\n        public HttpRequestOptions Options { set; get; }\n        public HttpContentHeaders ContentHeaders { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpRequestMessageDeserializeContent.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net.Http;\n\nusing Newtonsoft.Json.Linq;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HttpRequestMessageDeserializeContent : HttpRequestMessageContentBase\n    {\n        public HttpRequestMessage ToHttpResponseMessage()\n        {\n            var result = new HttpRequestMessage()\n            {\n                Version = Version,\n                VersionPolicy = VersionPolicy,\n                Method = Method,\n                RequestUri = RequestUri,\n            };\n\n            if (ContentBase64 is not null)\n            {\n                var memoryStream = new MemoryStream(Convert.FromBase64String(ContentBase64));\n                //var text = Encoding.UTF8.GetString(memoryStream.ToArray());\n                var streamContent = new StreamContent(memoryStream);\n                result.Content = streamContent;\n            }\n\n            var headerContentList = ContentHeaders?.ToObject<List<HeaderContent>>();\n\n            if (headerContentList != null)\n            {\n                foreach (var headerContent in headerContentList)\n                {\n                    result.Content.Headers.Add(headerContent.Key, headerContent.Value);\n                }\n            }\n\n            headerContentList = Headers.ToObject<List<HeaderContent>>();\n            if (headerContentList != null)\n            {\n                foreach (var headerContent in headerContentList)\n                {\n                    result.Headers.Add(headerContent.Key, headerContent.Value);\n                }\n            }\n\n\n            return result;\n        }\n\n        public JContainer Headers { set; get; }\n\n        /// <summary>\n        /// 使用 <see cref=\"JContainer\"/> 表示的 <see cref=\"ContentHeaders\"/> 内容。特意命名和基类型相同，这样序列化时可以自动转换\n        /// </summary>\n        public new JContainer ContentHeaders { set; get; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpRequestMessageSerializeContent.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System.Net.Http;\nusing System.Net.Http.Headers;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HttpRequestMessageSerializeContent : HttpRequestMessageContentBase\n    {\n        public HttpRequestMessageSerializeContent(HttpRequestMessage message) : base(message)\n        {\n            Headers = message.Headers;\n        }\n\n        public HttpRequestHeaders Headers { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpResponseMessageContentBase.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System;\nusing System.IO;\nusing System.Net;\nusing System.Net.Http;\nusing System.Threading;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HttpResponseMessageContentBase\n    {\n        public HttpResponseMessageContentBase()\n        {\n        }\n\n        public HttpResponseMessageContentBase(HttpResponseMessage message)\n        {\n            if (message.Content != null)\n            {\n                using var memoryStream = new MemoryStream();\n                message.Content.CopyTo(memoryStream, null, CancellationToken.None);\n                ContentBase64 = Convert.ToBase64String(memoryStream.ToArray());\n\n            }\n\n            Version = message.Version;\n            StatusCode = message.StatusCode;\n        }\n\n        public string ContentBase64 { set; get; }\n        public Version Version { get; set; }\n        public HttpStatusCode StatusCode { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpResponseMessageDeserializeContent.cs",
    "content": "﻿#nullable disable // 序列化的代码，不需要可空\n\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Net.Http;\nusing System.Text;\nusing Newtonsoft.Json.Linq;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HttpResponseMessageDeserializeContent : HttpResponseMessageContentBase\n    {\n        public JContainer Headers { set; get; }\n        public JContainer ContentHeaders { get; set; }\n\n        public HttpResponseMessage ToHttpResponseMessage()\n        {\n            var httpResponseMessage = new HttpResponseMessage(StatusCode)\n            {\n                Version = Version,\n            };\n\n            //foreach (var httpResponseHeader in Headers)\n            //{\n            //    httpResponseMessage.Headers.Add(httpResponseHeader.Key, httpResponseHeader.Value);\n            //}\n\n            var memoryStream = new MemoryStream(Convert.FromBase64String(ContentBase64));\n            var text = Encoding.UTF8.GetString(memoryStream.ToArray());\n            var streamContent = new StreamContent(memoryStream);\n            httpResponseMessage.Content = streamContent;\n\n            var headerContentList = ContentHeaders.ToObject<List<HeaderContent>>();\n\n            if (headerContentList != null)\n            {\n                foreach (var headerContent in headerContentList)\n                {\n                    httpResponseMessage.Content.Headers.Add(headerContent.Key, headerContent.Value);\n                }\n            }\n\n            headerContentList = Headers.ToObject<List<HeaderContent>>();\n            if (headerContentList != null)\n            {\n                foreach (var headerContent in headerContentList)\n                {\n                    httpResponseMessage.Headers.Add(headerContent.Key, headerContent.Value);\n                }\n            }\n\n            return httpResponseMessage;\n        }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/HttpResponseMessageSerializeContent.cs",
    "content": "﻿using System.Net.Http;\nusing System.Net.Http.Headers;\n\n#nullable disable // 序列化的代码，不需要可空\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class HttpResponseMessageSerializeContent : HttpResponseMessageContentBase\n    {\n        public HttpResponseMessageSerializeContent(HttpResponseMessage message) : base(message)\n        {\n            Headers = message.Headers;\n            ContentHeaders = message.Content?.Headers;\n        }\n        public HttpResponseHeaders Headers { get; set; }\n#nullable enable\n        public HttpContentHeaders? ContentHeaders { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/IpcPipeMvcContext.cs",
    "content": "﻿namespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class IpcPipeMvcContext\n    {\n        public const string BaseAddressUrl = \"http://localhost/\";\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/IpcResponseMessageResult.cs",
    "content": "﻿using dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.PipeMvcServer.IpcFramework\n{\n    class IpcResponseMessageResult : IIpcResponseMessage\n    {\n        public IpcResponseMessageResult(IpcMessage responseMessage)\n        {\n            ResponseMessage = responseMessage;\n        }\n\n        public IpcMessage ResponseMessage { get; }\n    }\n}\n"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/dotnetCampus.Ipc.PipeMvcShare.projitems",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <MSBuildAllProjects Condition=\"'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' &lt; '16.0'\">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>\n    <HasSharedItems>true</HasSharedItems>\n    <SharedGUID>05ace3bb-61e5-4592-ac73-747850db1081</SharedGUID>\n  </PropertyGroup>\n  <PropertyGroup Label=\"Configuration\">\n    <Import_RootNamespace>dotnetCampus.Ipc.PipeMvcShare</Import_RootNamespace>\n  </PropertyGroup>\n  <ItemGroup>\n    <Compile Include=\"$(MSBuildThisFileDirectory)HeaderContent.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpMessageSerializer.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpRequestMessageContentBase.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpRequestMessageDeserializeContent.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpRequestMessageSerializeContent.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpResponseMessageContentBase.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpResponseMessageDeserializeContent.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)HttpResponseMessageSerializeContent.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)IpcPipeMvcContext.cs\" />\n    <Compile Include=\"$(MSBuildThisFileDirectory)IpcResponseMessageResult.cs\" />\n  </ItemGroup>\n</Project>"
  },
  {
    "path": "src/PipeMvc/dotnetCampus.Ipc.PipeMvcShare/dotnetCampus.Ipc.PipeMvcShare.shproj",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup Label=\"Globals\">\n    <ProjectGuid>05ace3bb-61e5-4592-ac73-747850db1081</ProjectGuid>\n    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>\n  </PropertyGroup>\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <Import Project=\"$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)\\CodeSharing\\Microsoft.CodeSharing.Common.Default.props\" />\n  <Import Project=\"$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)\\CodeSharing\\Microsoft.CodeSharing.Common.props\" />\n  <PropertyGroup />\n  <Import Project=\"dotnetCampus.Ipc.PipeMvcShare.projitems\" Label=\"Shared\" />\n  <Import Project=\"$(MSBuildExtensionsPath32)\\Microsoft\\VisualStudio\\v$(VisualStudioVersion)\\CodeSharing\\Microsoft.CodeSharing.CSharp.targets\" />\n</Project>\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/AssemblyIpcProxyAttribute.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 由编译器自动生成，将 IPC 类型与其自动生成的代理类型关联起来（没有关联对接类型）。\n/// </summary>\n[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]\n#if !IPC_ANALYZER\npublic\n#endif\nclass AssemblyIpcProxyAttribute : Attribute\n{\n    /// <summary>\n    /// 由编译器自动生成，将 IPC 类型与其自动生成的代理和对接类型关联起来。\n    /// </summary>\n    /// <param name=\"contractType\">契约接口类型。</param>\n    /// <param name=\"ipcType\">IPC 类型（即标记了 <see cref=\"IpcShapeAttribute\"/> 的类型）。</param>\n    /// <param name=\"proxyType\">代理类型。</param>\n    public AssemblyIpcProxyAttribute(Type contractType, Type ipcType, Type proxyType)\n    {\n        ContractType = contractType ?? throw new ArgumentNullException(nameof(contractType));\n        IpcType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n        ProxyType = proxyType ?? throw new ArgumentNullException(nameof(proxyType));\n    }\n\n    /// <summary>\n    /// 契约类型。\n    /// </summary>\n    public Type ContractType { get; }\n\n    /// <summary>\n    /// IPC 类型（即标记了 <see cref=\"IpcShapeAttribute\"/> 的类型）。\n    /// </summary>\n    public Type IpcType { get; set; }\n\n    /// <summary>\n    /// 代理类型。\n    /// </summary>\n    public Type ProxyType { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/AssemblyIpcProxyJointAttribute.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 由编译器自动生成，将 IPC 类型与其自动生成的代理和对接类型关联起来。\n/// </summary>\n[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true, Inherited = false)]\n#if !IPC_ANALYZER\npublic\n#endif\nclass AssemblyIpcProxyJointAttribute : Attribute\n{\n    /// <summary>\n    /// 由编译器自动生成，将 IPC 类型与其自动生成的代理和对接类型关联起来。\n    /// </summary>\n    /// <param name=\"ipcType\">契约类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）。</param>\n    /// <param name=\"proxyType\">代理类型。</param>\n    /// <param name=\"jointType\">对接类型。</param>\n    public AssemblyIpcProxyJointAttribute(Type ipcType, Type proxyType, Type jointType)\n    {\n        IpcType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n        ProxyType = proxyType ?? throw new ArgumentNullException(nameof(proxyType));\n        JointType = jointType ?? throw new ArgumentNullException(nameof(jointType));\n    }\n\n    /// <summary>\n    /// 契约类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）。\n    /// </summary>\n    public Type IpcType { get; set; }\n\n    /// <summary>\n    /// 代理类型。\n    /// </summary>\n    public Type ProxyType { get; set; }\n\n    /// <summary>\n    /// 对接类型。\n    /// </summary>\n    public Type JointType { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/IpcEventAttribute.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 指定此事件的 IPC 代理访问方式和对接方式。\n/// </summary>\n[AttributeUsage(AttributeTargets.Event, AllowMultiple = false, Inherited = true)]\n#if !IPC_ANALYZER\npublic\n#endif\nsealed class IpcEventAttribute : IpcMemberAttribute\n{\n    /// <summary>\n    /// 指定此事件的 IPC 代理访问方式和对接方式。\n    /// </summary>\n    public IpcEventAttribute()\n    {\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/IpcMemberAttribute.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 指定成员的 IPC 代理访问方式和对接方式。\n/// </summary>\n[EditorBrowsable(EditorBrowsableState.Never)]\n#if !IPC_ANALYZER\npublic\n#endif\nabstract class IpcMemberAttribute : Attribute\n{\n    /// <summary>\n    /// 指定此成员的 IPC 代理访问方式和对接方式。\n    /// </summary>\n    protected IpcMemberAttribute()\n    {\n    }\n\n    /// <summary>\n    /// 如果指定为 true，则在 IPC 发生异常时会忽略这些异常，并返回默认值。\n    /// <para>\n    /// 如果同时设置了默认值，则异常时会使用此默认值；如果没有设置默认值，则异常时会使用类型默认值 default。\n    /// </para>\n    /// </summary>\n    /// <remarks>\n    /// 请注意：\n    /// <list type=\"bullet\">\n    /// <item>此特性仅忽略 IPC 连接异常和 IPC 超时异常（例如进程退出、连接断开等），而不会忽略普通业务异常（例如业务实现中抛出了 <see cref=\"NullReferenceException\"/> 等）。</item>\n    /// <item>另外，如果 IPC 框架内部出现了 bug 导致了异常，也不会因此而忽略。</item>\n    /// </list>\n    /// </remarks>\n    [DefaultValue(false)]\n    public bool IgnoresIpcException { get; set; }\n\n    /// <summary>\n    /// 设定此成员执行的超时时间（毫秒）。如果自此成员执行开始直至超时时间后依然没有返回，则：\n    /// <list type=\"bullet\">\n    /// <item>默认会引发 <see cref=\"dotnetCampus.Ipc.Exceptions.IpcInvokingTimeoutException\"/>。</item>\n    /// <item>通过在类型或成员上设置 <see cref=\"IgnoresIpcException\"/> 可阻止引发超时异常而改为返回默认值。</item>\n    /// </list>\n    /// 如果类型上已经标记了超时但不希望某个成员设置超时，可单独在此成员上标记 Timeout = 0。\n    /// </summary>\n    [DefaultValue(0)]\n    public int Timeout { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/IpcMethodAttribute.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 指定此方法的 IPC 代理访问方式和对接方式。\n/// </summary>\n[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]\n#if !IPC_ANALYZER\npublic\n#endif\nclass IpcMethodAttribute : IpcMemberAttribute\n{\n    /// <summary>\n    /// 指定此方法的 IPC 代理访问方式和对接方式。\n    /// </summary>\n    public IpcMethodAttribute()\n    {\n    }\n\n    /// <summary>\n    /// 在指定了 <see cref=\"IpcMemberAttribute.IgnoresIpcException\"/> 或者 <see cref=\"IpcMemberAttribute.Timeout\"/> 的情况下，如果真的发生了 IPC 连接异常或超时，则会使用此默认值。\n    /// <list type=\"number\">\n    /// <item>不指定此值时，会使用属性或返回值类型的默认值（即 default）。</item>\n    /// <item>请使用字符串形式编写值（例如 \"IntPtr.Zero\" ，含英文引号），编译后会自动将其转为真实代码。</item>\n    /// <item>\n    /// 接上条，以下写法会在编译时转成对应的语句：\n    /// <code>\n    /// | 写法（含引号）   | 转成的语句       |\n    /// |------------------|------------------|\n    /// | null             | null             |\n    /// | \"null\"           | null             |\n    /// | default          | default          |\n    /// | \"default\"        | default          |\n    /// | \"\\\"text\\\"\"       | \"text\"           |\n    /// | @\"\"\"text\"\"\"      | \"text\"           |\n    /// | \"new Foo(2)\"     | new Foo(2)       |\n    /// | \"SomeEnum.Value\" | SomeEnum.Value   |\n    /// </code>\n    /// </item>\n    /// </list>\n    /// </summary>\n    [DefaultValue(null)]\n#if NET8_0_OR_GREATER\n    [StringSyntax(\"csharp\")]\n#endif\n    public string? DefaultReturn { get; set; }\n\n    /// <summary>\n    /// 如果一个方法返回值是 void，那么此属性决定代理调用此方法时是否需要等待对方执行完成。\n    /// 默认为 false，即不等待对方执行完成。\n    /// </summary>\n    [DefaultValue(false)]\n    public bool WaitsVoid { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/IpcPropertyAttribute.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 指定此属性的 IPC 代理访问方式和对接方式。\n/// </summary>\n[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]\n#if !IPC_ANALYZER\npublic\n#endif\nsealed class IpcPropertyAttribute : IpcMemberAttribute\n{\n    /// <summary>\n    /// 指定此属性的 IPC 代理访问方式和对接方式。\n    /// </summary>\n    public IpcPropertyAttribute()\n    {\n    }\n\n    /// <summary>\n    /// 在指定了 <see cref=\"IpcMemberAttribute.IgnoresIpcException\"/> 或者 <see cref=\"IpcMemberAttribute.Timeout\"/> 的情况下，如果真的发生了 IPC 连接异常或超时，则会使用此默认值。\n    /// <list type=\"number\">\n    /// <item>不指定此值时，会使用属性或返回值类型的默认值（即 default）。</item>\n    /// <item>请使用字符串形式编写值（例如 \"IntPtr.Zero\" ，含英文引号），编译后会自动将其转为真实代码。</item>\n    /// <item>\n    /// 接上条，以下写法会在编译时转成对应的语句：\n    /// <code>\n    /// | 写法（含引号）   | 转成的语句       |\n    /// |------------------|------------------|\n    /// | null             | null             |\n    /// | \"null\"           | null             |\n    /// | default          | default          |\n    /// | \"default\"        | default          |\n    /// | \"\\\"text\\\"\"       | \"text\"           |\n    /// | @\"\"\"text\"\"\"      | \"text\"           |\n    /// | \"new Foo(2)\"     | new Foo(2)       |\n    /// | \"SomeEnum.Value\" | SomeEnum.Value   |\n    /// </code>\n    /// </item>\n    /// </list>\n    /// </summary>\n    [DefaultValue(null)]\n#if NET8_0_OR_GREATER\n    [StringSyntax(\"csharp\")]\n#endif\n    public string? DefaultReturn { get; set; }\n\n    /// <summary>\n    /// 标记一个属性是对于 IPC 代理访问来说是只读的。\n    /// 当通过 IPC 访问过一次这个属性后，此属性不再变化，后续无需再通过 IPC 读取，可直接使用本地缓存的值。\n    /// </summary>\n    [DefaultValue(false)]\n    public bool IsReadonly { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/IpcPublicAttribute.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 指示此接口在 IPC 中公开，可被其他进程发现并使用。\n/// <para>\n/// 这个特性不会在接口的继承树中传递。因此即使基接口标记了此特性，所有派生接口想要作为公开的 IPC 对象也必须依次标记。\n/// </para>\n/// </summary>\n[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]\n#if !IPC_ANALYZER\npublic\n#endif\nclass IpcPublicAttribute : Attribute\n{\n    /// <summary>\n    /// 指示此接口在 IPC 中公开，可被其他进程发现并使用。\n    /// </summary>\n    public IpcPublicAttribute()\n    {\n    }\n\n    /// <summary>\n    /// 如果指定为 true，则本接口中的所有成员在 IPC 调用发生异常时会忽略这些异常，并返回默认值。\n    /// <para>默认值会采用属性类型或方法返回值类型的默认值（即 default），如果希望额外指定默认值，请：</para>\n    /// <list type=\"bullet\">\n    /// <item>在单独的属性上使用 IpcPropertyAttribute.DefaultReturn</item>\n    /// <item>在单独的方法上使用 IpcMethodAttribute.DefaultReturn</item>\n    /// <item>在单独的事件上使用 IpcEventAttribute.DefaultReturn</item>\n    /// </list>\n    /// </summary>\n    /// <remarks>\n    /// 请注意：\n    /// <list type=\"bullet\">\n    /// <item>此特性仅忽略 IPC 连接异常和 IPC 超时异常（例如进程退出、连接断开等），而不会忽略普通业务异常（例如业务实现中抛出了 <see cref=\"NullReferenceException\"/> 等）。</item>\n    /// <item>另外，如果 IPC 框架内部出现了 bug 导致了异常，也不会因此而忽略。</item>\n    /// </list>\n    /// </remarks>\n    [DefaultValue(false)]\n    public bool IgnoresIpcException { get; set; }\n\n    /// <summary>\n    /// 设定此接口中所有成员执行的默认超时时间（毫秒）。如果自成员执行开始直至超时时间后依然没有返回，则：\n    /// <list type=\"bullet\">\n    /// <item>默认会引发 <see cref=\"dotnetCampus.Ipc.Exceptions.IpcInvokingTimeoutException\"/>。</item>\n    /// <item>通过在类型或成员上设置 <see cref=\"IgnoresIpcException\"/> 可阻止引发超时异常而改为返回默认值。</item>\n    /// </list>\n    /// </summary>\n    [DefaultValue(0)]\n    public int Timeout { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/Attributes/IpcShapeAttribute.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.CompilerServices.Attributes;\n\n/// <summary>\n/// 标记一个类型是 IPC 形状代理。这个类型的所有成员都没有实现，唯一的作用就是定义 IPC 类型中的每个方法应如何通过 IPC 代理来访问。\n/// </summary>\n[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]\n#if !IPC_ANALYZER\npublic\n#endif\nclass IpcShapeAttribute : IpcPublicAttribute\n{\n    /// <summary>\n    /// 标记一个类型是 IPC 形状代理。这个类型的所有成员都没有实现，唯一的作用就是定义 IPC 类型中的每个方法应如何通过 IPC 代理来访问。\n    /// </summary>\n    /// <param name=\"contractType\">此形状代理所代理的契约类型。</param>\n    public IpcShapeAttribute(Type contractType)\n    {\n        ContractType = contractType;\n    }\n\n    /// <summary>\n    /// 此形状代理所代理的契约类型。\n    /// <para>一个形状代理只能代理一个契约类型。</para>\n    /// </summary>\n    public Type ContractType { get; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Contexts/GeneratedIpcJointResponse.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Serialization;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Contexts;\n\ninternal class GeneratedIpcJointResponse : IIpcResponseMessage\n{\n    internal static GeneratedIpcJointResponse Empty { get; } = new GeneratedIpcJointResponse();\n\n    private GeneratedIpcJointResponse()\n    {\n    }\n\n    private GeneratedIpcJointResponse(IpcMessage message)\n    {\n        ResponseMessage = message;\n    }\n\n    public IpcMessage ResponseMessage { get; }\n\n    internal static async Task<GeneratedIpcJointResponse> FromAsyncReturnModel(\n        Task<GeneratedProxyMemberReturnModel?> asyncReturnModel,\n        IIpcObjectSerializer serializer)\n    {\n        var returnModel = await asyncReturnModel.ConfigureAwait(false);\n        var message = returnModel is null\n            ? new IpcMessage()\n            : serializer.SerializeToIpcMessage((ulong)KnownMessageHeaders.RemoteObjectMessageHeader, returnModel, \"Return\");\n        return new GeneratedIpcJointResponse(message);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Garms/Garm.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.Serialization;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// 提供非泛型的临时 GARM 模型，最终使用时请转换为泛型版本再用。\n/// </summary>\ninternal readonly record struct Garm : IGarmObject\n{\n    private readonly object? _value;\n\n    /// <summary>\n    /// 创建一个存储 IPC 中 <see cref=\"IpcJsonElement\"/> 的临时 GARM 模型。\n    /// </summary>\n    /// <param name=\"value\">模型的值。</param>\n    /// <param name=\"valueType\">模型中值的类型。</param>\n    public Garm(IpcJsonElement value, Type valueType)\n    {\n        _value = value;\n        ValueType = valueType ?? throw new ArgumentNullException(nameof(valueType));\n        IpcType = null;\n    }\n\n    /// <summary>\n    /// 创建一个存储 IPC 中 IPC 代理对象的临时 GARM 模型。\n    /// </summary>\n    /// <param name=\"value\">模型的值。</param>\n    /// <param name=\"ipcType\">模型中的 IPC 类型。</param>\n    public Garm(object? value, Type ipcType)\n    {\n        _value = value;\n        ValueType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n        IpcType = ipcType;\n    }\n\n    /// <summary>\n    /// 无法获取 GARM 模型的值！请先使用 <see cref=\"ToGeneric{T}\"/> 转换为泛型类型后再获取值。\n    /// </summary>\n    /// <remarks>\n    /// 数据在 IPC 通道中的传输会经过如下步骤：\n    /// proxy -> IPC model in proxy side -> IPC model in joint side -> joint\n    /// 其中，proxy 和 joint 均由编译器在用户端生成了泛型代码，因此这些上下文中都可以获取到泛型类型。\n    /// 但中间的传输过程中没有泛型上下文，所以会用到此临时类型中转值。\n    /// 由于中转的值可能是 JsonElement 这种未确定类型的值，为避免代码中意外写出错误的代码，因此禁止在此类型中获取值。\n    /// </remarks>\n    object? IGarmObject.Value => throw new InvalidOperationException(\"当 GARM 未获得泛型类型时，不可获取其值。请等待泛型上下文中调用 ToGeneric<T> 转换为泛型类型后再获取值。\");\n\n    /// <summary>\n    /// 获取 GARM 模型的值的类型。\n    /// </summary>\n    public Type ValueType { get; }\n\n    /// <summary>\n    /// 如果对象是 IPC 类型，则此属性为 IPC 类型的类型对象；否则此属性为 null。\n    /// </summary>\n    public Type? IpcType { get; }\n\n    /// <summary>\n    /// 将 GARM 模型转换为泛型 GARM 模型。\n    /// </summary>\n    /// <typeparam name=\"T\">泛型类型。</typeparam>\n    /// <returns>泛型 GARM 模型。</returns>\n    public Garm<T> ToGeneric<T>(IIpcObjectSerializer serializer) => _value switch\n    {\n        // 是 null。\n        null => new Garm<T>(default, IpcType),\n        // 是一个普通的 IPC 对象。\n        IpcJsonElement jsonElement => new Garm<T>(IpcJsonElement.Deserialize<T>(jsonElement, serializer), IpcType),\n        // 是一个 IPC 代理对象。\n        _ => new Garm<T>((T?) _value, IpcType),\n    };\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Garms/Garm.generic.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// <para>IPC GARM 模型。</para>\n/// <para>GARM = Generated Argument and Return Model 生成类的参数与返回值模型。</para>\n/// 在生成的 IPC 通信类中，用来表示参数和返回值。通过此类型表示的参数和返回值可以区分序列化的普通对象和需要 IPC 代理访问的 IPC 对象。\n/// <list type=\"bullet\">\n/// <item>对于 IPC 代理，只有参数需要使用此模型，返回值不需要。因为参数可能是一个尚未对接的 IPC 对象。</item>\n/// <item>对于 IPC 对接，只有返回值需要使用此模型，参数不需要。因为返回值可能是一个尚未对接的 IPC 对象。</item>\n/// </list>\n/// </summary>\npublic readonly struct Garm<T> : IGarmObject\n{\n    /// <summary>\n    /// 创建一个 GARM 模型。\n    /// </summary>\n    public Garm()\n    {\n        Value = default;\n        IpcType = null;\n    }\n\n    /// <summary>\n    /// 创建一个 GARM 模型。\n    /// </summary>\n    /// <param name=\"value\">对象的值。</param>\n    public Garm(T? value)\n    {\n        Value = value;\n        IpcType = null;\n    }\n\n    /// <summary>\n    /// 创建一个 GARM 模型。\n    /// </summary>\n    /// <param name=\"value\">对象的值。</param>\n    /// <param name=\"ipcType\">对象的 IPC 类型。</param>\n    public Garm(T? value, Type? ipcType)\n    {\n        Value = value;\n        IpcType = ipcType;\n    }\n\n    /// <summary>\n    /// 如果对象是 IPC 类型，则此属性为 IPC 类型的类型对象；否则此属性为 null。\n    /// <para>IPC 类型为标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型。</para>\n    /// </summary>\n    internal Type? IpcType { get; }\n\n    /// <summary>\n    /// 对象的值。\n    /// </summary>\n#nullable disable\n    public T Value { get; }\n#nullable restore\n\n    object? IGarmObject.Value => Value;\n\n    Type IGarmObject.ValueType => typeof(T);\n\n    Type? IGarmObject.IpcType => IpcType;\n\n    /// <summary>\n    /// 将 IPC 代理的“非 IPC 类型”参数或 IPC 对接的“非 IPC 类型”返回值转换成“非 IPC 类型”的 GARM 模型。\n    /// </summary>\n    /// <param name=\"value\">任意值。</param>\n    public static implicit operator Garm<T>(T value)\n    {\n        if (value is IGarmObject go)\n        {\n            return new Garm<T>((T?) go.Value, go.IpcType);\n        }\n        return new Garm<T>(value);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Garms/IGarmObject.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// 为 <see cref=\"Garm{T}\"/> 提供非泛型访问的方法。\n/// </summary>\npublic interface IGarmObject\n{\n    /// <summary>\n    /// 获取 <see cref=\"Garm{T}\"/> 的值。\n    /// </summary>\n    /// <remarks>\n    /// 对于发送方，值可能是：<see langword=\"null\"/>、可 JSON 序列化的任意对象（即将被序列化）、IPC 代理对象。<br/>\n    /// 对于接收方，值可能是：<see langword=\"null\"/>、<see cref=\"IpcJsonElement\"/>（用于延迟反序列化）、IPC 代理对象。\n    /// </remarks>\n    object? Value { get; }\n\n    /// <summary>\n    /// 值的类型，即 <see cref=\"Garm{T}\"/> 中的 T 的类型。\n    /// </summary>\n    public Type ValueType { get; }\n\n    /// <summary>\n    /// 如果对象是 IPC 类型，则此属性为 IPC 类型的类型对象；否则此属性为 null。\n    /// </summary>\n    Type? IpcType { get; }\n}\n\ninternal static class GarmObjectExtensions\n{\n    internal static IGarmObject Default { get; } = new Garm<object?>();\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/GeneratedIpcFactory.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.ComponentModel;\nusing System.Runtime.CompilerServices;\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\n#if !NET5_0_OR_GREATER\nusing System.Reflection;\n#endif\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// 此工厂包含 <see cref=\"Ipc\"/> 库中对 <see cref=\"IIpcProvider\"/> 的上下文扩展，\n/// 因此可基于此上下文创建仅限于此 <see cref=\"Ipc\"/> 库的功能类，包括：\n/// <list type=\"bullet\">\n/// <item><see cref=\"IIpcRequestHandler\"/></item>\n/// <item><see cref=\"GeneratedIpcProxy{TContract}\"/></item>\n/// <item><see cref=\"GeneratedIpcJoint{TContract}\"/></item>\n/// </list>\n/// </summary>\npublic static class GeneratedIpcFactory\n{\n    /// <summary>\n    /// 编译期 IPC 类型（标记了 <see cref=\"IpcPublicAttribute\"/> 的接口）到代理对接对象的创建器。\n    /// </summary>\n    internal static ConcurrentDictionary<Type, (Func<GeneratedIpcProxy> ProxyFactory, Func<GeneratedIpcJoint> JointFactory)> IpcPublicFactories { get; } = [];\n\n    /// <summary>\n    /// 编译期 IPC 类型（标记了 <see cref=\"IpcShapeAttribute\"/> 的形状代理类型）到代理对接对象的创建器。\n    /// </summary>\n    private static ConcurrentDictionary<Type, Func<GeneratedIpcProxy>> IpcShapeFactories { get; } = [];\n\n    /// <summary>\n    /// 由源生成器调用，注册 IPC 对象的代理与对接创建器。\n    /// </summary>\n    /// <param name=\"proxyFactory\">IPC 代理创建器。</param>\n    /// <param name=\"jointFactory\">IPC 对接创建器。</param>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public static void RegisterIpcPublic<TPublic>(Func<GeneratedIpcProxy<TPublic>> proxyFactory, Func<GeneratedIpcJoint<TPublic>> jointFactory)\n        where TPublic : class\n    {\n        IpcPublicFactories[typeof(TPublic)] = (proxyFactory, jointFactory);\n    }\n\n    /// <summary>\n    /// 由源生成器调用，注册 IPC 对象的形状代理。\n    /// </summary>\n    /// <param name=\"shapeFactory\">IPC 形状代理创建器。</param>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <typeparam name=\"TShape\">IPC 对象的形状类型。</typeparam>\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    public static void RegisterIpcShape<TPublic, TShape>(Func<GeneratedIpcProxy<TPublic>> shapeFactory)\n        where TPublic : class\n        where TShape : class\n    {\n        IpcShapeFactories[typeof(TShape)] = shapeFactory;\n    }\n\n    /// <summary>\n    /// 创建用于通过 IPC 访问其他端 <typeparamref name=\"TPublic\"/> 类型的代理对象。\n    /// </summary>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <param name=\"ipcProvider\">关联的 <see cref=\"IIpcProvider\"/>。</param>\n    /// <param name=\"peer\">IPC 远端。</param>\n    /// <param name=\"ipcObjectId\">如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。</param>\n    /// <returns>契约类型。</returns>\n    public static TPublic CreateIpcProxy<TPublic>(this IIpcProvider ipcProvider, IPeerProxy peer, string? ipcObjectId = null)\n        where TPublic : class\n    {\n        if (SafeGetIpcPublicFactories<TPublic>().ProxyFactory is not { } proxyFactory)\n        {\n            throw new ArgumentException(\n                $\"接口 {typeof(TPublic).Name} 上没有找到 {nameof(IpcPublicAttribute)} 特性，因此不知道如何创建 {typeof(TPublic).Name} 的 IPC 代理。\",\n                nameof(TPublic));\n        }\n\n        var proxy = (GeneratedIpcProxy<TPublic>)proxyFactory();\n        proxy.Context = ipcProvider.GetGeneratedContext();\n        proxy.PeerProxy = peer;\n        proxy.ObjectId = ipcObjectId;\n        return (TPublic)(object)proxy;\n    }\n\n    /// <summary>\n    /// 创建用于通过 IPC 访问其他端 <typeparamref name=\"TPublic\"/> 类型的代理对象。\n    /// </summary>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <param name=\"ipcProvider\">关联的 <see cref=\"IIpcProvider\"/>。</param>\n    /// <param name=\"peer\">IPC 远端。</param>\n    /// <param name=\"ipcProxyConfigs\">指定创建的 IPC 代理在进行 IPC 通信时应使用的相关配置。</param>\n    /// <param name=\"ipcObjectId\">如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。</param>\n    /// <returns>契约类型。</returns>\n    public static TPublic CreateIpcProxy<TPublic>(this IIpcProvider ipcProvider, IPeerProxy peer, IpcProxyConfigs ipcProxyConfigs,\n        string? ipcObjectId = null)\n        where TPublic : class\n    {\n        if (SafeGetIpcPublicFactories<TPublic>().ProxyFactory is not { } proxyFactory)\n        {\n            throw new ArgumentException(\n                $\"接口 {typeof(TPublic).Name} 上没有找到 {nameof(IpcPublicAttribute)} 特性，因此不知道如何创建 {typeof(TPublic).Name} 的 IPC 代理。\",\n                nameof(TPublic));\n        }\n\n        var proxy = (GeneratedIpcProxy<TPublic>)proxyFactory();\n        proxy.Context = ipcProvider.GetGeneratedContext();\n        proxy.PeerProxy = peer;\n        proxy.ObjectId = ipcObjectId;\n        proxy.RuntimeConfigs = ipcProxyConfigs;\n        return (TPublic)(object)proxy;\n    }\n\n    /// <summary>\n    /// 创建用于通过 IPC 访问其他端 <typeparamref name=\"TPublic\"/> 类型的代理对象。\n    /// </summary>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <typeparam name=\"TShape\">用于配置 IPC 代理行为的 IPC 形状代理类型。</typeparam>\n    /// <param name=\"ipcProvider\">关联的 <see cref=\"IIpcProvider\"/>。</param>\n    /// <param name=\"peer\">IPC 远端。</param>\n    /// <param name=\"ipcObjectId\">如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。</param>\n    /// <returns>契约类型。</returns>\n    public static TPublic CreateIpcProxy<TPublic, TShape>(this IIpcProvider ipcProvider, IPeerProxy peer, string? ipcObjectId = null)\n        where TPublic : class\n    {\n        if (SafeGetIpcShapeFactory<TPublic, TShape>() is not { } proxyFactory)\n        {\n            throw new ArgumentException(\n                $\"类型 {typeof(TShape).Name} 上没有找到 {nameof(IpcShapeAttribute)} 特性，因此不知道如何创建 {typeof(TPublic).Name} 的 IPC 代理。\",\n                nameof(TShape));\n        }\n\n        var proxy = (GeneratedIpcProxy<TPublic>)proxyFactory();\n        proxy.Context = ipcProvider.GetGeneratedContext();\n        proxy.PeerProxy = peer;\n        proxy.ObjectId = ipcObjectId;\n        return (TPublic)(object)proxy;\n    }\n\n    /// <summary>\n    /// 创建用于对接来自其他端通过 IPC 访问 <typeparamref name=\"TPublic\"/> 类型的对接对象。\n    /// </summary>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <param name=\"ipcProvider\">关联的 <see cref=\"IIpcProvider\"/>。</param>\n    /// <param name=\"realInstance\">真实的对象。</param>\n    /// <param name=\"ipcObjectId\">如果被对接的对象有多个实例，请设置此 Id 值以对接正确的实例。</param>\n    public static TPublic CreateIpcJoint<TPublic>(this IIpcProvider ipcProvider, TPublic realInstance, string? ipcObjectId = null)\n        where TPublic : class\n    {\n        var realType = realInstance.GetType();\n        if (SafeGetIpcPublicFactories<TPublic>().JointFactory is not { } jointFactory)\n        {\n            throw new ArgumentException(\n                $\"类型 {typeof(TPublic).Name} 上没有找到 {nameof(IpcPublicAttribute)} 特性，因此不知道如何创建 {typeof(TPublic).Name} 的 IPC 对接。\",\n                nameof(realInstance));\n        }\n\n        var context = ipcProvider.GetGeneratedContext();\n        var joint = (GeneratedIpcJoint<TPublic>)jointFactory();\n        joint.Context = context;\n        joint.SetInstance(realInstance);\n        context.JointManager.AddPublicIpcObject(joint, ipcObjectId);\n        return realInstance;\n    }\n\n    /// <summary>\n    /// 安全地获取 IPC 公共类型的代理与对接创建器。\n    /// </summary>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <returns>代理与对接创建器。</returns>\n    /// <remarks>\n    /// 此方法虽然可能在线程并发时多次创建 IPC 公共类型的代理与对接创建器，但最终只会缓存并返回一个创建器。<br/>\n    /// 又由于此方法是幂等的，无论执行多少次，都不会往字典里存放多余的创建器；所以总体而言是线程安全的。\n    /// </remarks>\n    private static (Func<GeneratedIpcProxy>? ProxyFactory, Func<GeneratedIpcJoint>? JointFactory) SafeGetIpcPublicFactories<TPublic>()\n        where TPublic : class\n    {\n        if (IpcPublicFactories.TryGetValue(typeof(TPublic), out var factories))\n        {\n            return factories;\n        }\n\n#if !NET5_0_OR_GREATER\n        // 兼容旧版本 .NET 的反射实现。\n        foreach (var attribute in typeof(TPublic).Assembly.GetCustomAttributes<AssemblyIpcProxyJointAttribute>())\n        {\n            IpcPublicFactories.TryAdd(attribute.IpcType, (\n                () => (GeneratedIpcProxy)Activator.CreateInstance(attribute.ProxyType)!,\n                () => (GeneratedIpcJoint)Activator.CreateInstance(attribute.JointType)!\n            ));\n        }\n        if (IpcPublicFactories.TryGetValue(typeof(TPublic), out factories))\n        {\n            return factories;\n        }\n#endif\n\n        return default;\n    }\n\n    /// <summary>\n    /// 安全地获取 IPC 形状类型的代理创建器。\n    /// </summary>\n    /// <typeparam name=\"TPublic\">IPC 对象的契约类型。</typeparam>\n    /// <typeparam name=\"TShape\">IPC 对象的形状类型。</typeparam>\n    /// <returns>代理创建器。</returns>\n    /// <remarks>\n    /// 此方法虽然可能在线程并发时多次创建 IPC 公共类型的代理与对接创建器，但最终只会缓存并返回一个创建器。<br/>\n    /// 又由于此方法是幂等的，无论执行多少次，都不会往字典里存放多余的创建器；所以总体而言是线程安全的。\n    /// </remarks>\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    private static Func<GeneratedIpcProxy>? SafeGetIpcShapeFactory<TPublic, TShape>()\n        where TPublic : class\n    {\n        if (IpcShapeFactories.TryGetValue(typeof(TShape), out var factories))\n        {\n            return factories;\n        }\n\n#if !NET5_0_OR_GREATER\n        // 兼容旧版本 .NET 的反射实现。\n        foreach (var attribute in typeof(TShape).Assembly.GetCustomAttributes<AssemblyIpcProxyAttribute>())\n        {\n            IpcShapeFactories.TryAdd(attribute.IpcType, () => (GeneratedIpcProxy)Activator.CreateInstance(attribute.ProxyType)!);\n        }\n        if (IpcShapeFactories.TryGetValue(typeof(TShape), out factories))\n        {\n            return factories;\n        }\n#endif\n\n        return null;\n    }\n\n    private static GeneratedProxyJointIpcContext GetGeneratedContext(this IIpcProvider ipcProvider)\n        => ipcProvider.IpcContext.GeneratedProxyJointIpcContext;\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/GeneratedIpcJoint.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.Exceptions;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// 为自动生成的 IPC 对接类提供基类。\n/// </summary>\npublic abstract class GeneratedIpcJoint\n{\n    private GeneratedProxyJointIpcContext? _context;\n\n    /// <summary>\n    /// 提供基于 .NET 类型的 IPC 传输上下文信息。\n    /// </summary>\n    internal GeneratedProxyJointIpcContext Context\n    {\n        get => _context ?? throw new IpcRemoteException($\"基于 .NET 类型的 IPC 传输机制应使用 {typeof(GeneratedIpcFactory)} 工厂类型来构造。\");\n        set => _context = value ?? throw new ArgumentNullException(nameof(value));\n    }\n\n    /// <summary>\n    /// 设置此对接对象的真实实例。\n    /// </summary>\n    /// <param name=\"realInstance\">真实实例。</param>\n    internal abstract void SetInstance(object realInstance);\n\n    internal abstract IGarmObject GetProperty(ulong memberId, string propertyName);\n    internal abstract IGarmObject SetProperty(ulong memberId, string propertyName, IGarmObject value);\n    internal abstract IGarmObject CallMethod(ulong memberId, string methodName, IGarmObject[] args);\n    internal abstract Task<IGarmObject> CallMethodAsync(ulong memberId, string methodName, IGarmObject[] args);\n    internal abstract Type[] GetParameterTypes(ulong memberId, string? memberName, MemberInvokingType invokingType);\n}\n\n/// <summary>\n/// 为自动生成的 IPC 对接类提供基类。\n/// </summary>\n/// <typeparam name=\"TContract\">应该对接的契约类型（必须是一个接口）。</typeparam>\npublic abstract partial class GeneratedIpcJoint<TContract> : GeneratedIpcJoint where TContract : class\n{\n    /// <summary>\n    /// 获取默认的 GARM 对象。\n    /// </summary>\n    private static IGarmObject DefaultGarm => GarmObjectExtensions.Default;\n\n    /// <summary>\n    /// 此 IPC 对接（Joint）将对接到此真实实例。\n    /// </summary>\n    private TContract _real = null!;\n\n    /// <summary>\n    /// 获取属性值的方法集合。\n    /// </summary>\n    private readonly Dictionary<ulong, (Type[] Types, Func<IGarmObject> Method)> _propertyGetters = [];\n\n    /// <summary>\n    /// 设置属性值的方法集合（Key 为 MemberId，用于标识一个接口内的唯一一个成员，其中属性的 get 和 set 分别是两个不同的成员）。\n    /// </summary>\n    private readonly Dictionary<ulong, (Type[] Types, Action<IGarmObject> Method)> _propertySetters = [];\n\n    /// <summary>\n    /// 调用方法的方法集合（Key 为 MemberId，用于标识一个接口内的唯一一个成员，其中属性的 get 和 set 分别是两个不同的成员）。\n    /// </summary>\n    private readonly Dictionary<ulong, (Type[] Types, Func<IGarmObject[], IGarmObject> Method)> _methods = [];\n\n    /// <summary>\n    /// 调用异步方法的方法集合。\n    /// </summary>\n    private readonly Dictionary<ulong, (Type[] Types, Func<IGarmObject[], Task<IGarmObject>> Method)> _asyncMethods = [];\n\n    /// <summary>\n    /// 设置此对接对象的真实实例。\n    /// </summary>\n    /// <param name=\"realInstance\">真实实例。</param>\n    internal sealed override void SetInstance(object realInstance) => SetInstance((TContract)realInstance);\n\n    /// <summary>\n    /// 设置此对接对象的真实实例。\n    /// </summary>\n    /// <param name=\"realInstance\">真实实例。</param>\n    internal void SetInstance(TContract realInstance)\n    {\n        _propertyGetters.Clear();\n        _propertySetters.Clear();\n        _methods.Clear();\n        _asyncMethods.Clear();\n\n        _real = realInstance ?? throw new ArgumentNullException(nameof(realInstance));\n        MatchMembers(realInstance);\n    }\n\n    /// <summary>\n    /// 派生类重写此方法时，通过调用一系列 MatchXxx 方法来将 <typeparamref name=\"TContract\"/> 接口中的所有成员与对接方法进行匹配。\n    /// </summary>\n    /// <param name=\"realInstance\">当对接时，可使用此参数来访问真实对象。</param>\n    protected abstract void MatchMembers(TContract realInstance);\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod(ulong memberId, Action methodInvoker)\n    {\n        _methods.Add(memberId, ([], _ =>\n                {\n                    methodInvoker();\n                    return DefaultGarm;\n                }\n            ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<TReturn>(ulong memberId, Func<Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([], _ => methodInvoker()));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod(ulong memberId, Func<Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([], async _ =>\n                {\n                    await methodInvoker().ConfigureAwait(false);\n                    return DefaultGarm;\n                }\n            ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<TReturn>(ulong memberId, Func<Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([], async _ => await methodInvoker().ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T>(ulong memberId, Action<T> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T)], args =>\n                {\n                    methodInvoker(CastArg<T>(args[0])!);\n                    return DefaultGarm;\n                }\n            ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T>(ulong memberId, Func<T, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T)], async args =>\n                {\n                    await methodInvoker(CastArg<T>(args[0])!).ConfigureAwait(false);\n                    return DefaultGarm;\n                }\n            ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T, TReturn>(ulong memberId, Func<T, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T)], args => methodInvoker(CastArg<T>(args[0])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T, TReturn>(ulong memberId, Func<T, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T)], async args => await methodInvoker(CastArg<T>(args[0])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个属性，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"getter\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"getPropertyId\">属性 get 的签名的 Id。</param>\n    /// <param name=\"getter\">get 的对接实现。</param>\n    protected void MatchProperty<T>(ulong getPropertyId, Func<Garm<T>> getter)\n    {\n        _propertyGetters.Add(getPropertyId, ([], () => getter()));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个属性，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"getter\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"getPropertyId\">属性 get 的签名的 Id。</param>\n    /// <param name=\"setPropertyId\">属性 set 的签名的 Id。</param>\n    /// <param name=\"getter\">get 的对接实现。</param>\n    /// <param name=\"setter\">set 的对接实现。</param>\n    protected void MatchProperty<T>(ulong getPropertyId, ulong setPropertyId, Func<Garm<T>> getter, Action<T> setter)\n    {\n        _propertyGetters.Add(getPropertyId, ([], () => getter()));\n        _propertySetters.Add(setPropertyId, ([typeof(T)], value => setter(CastArg<T>(value)!)));\n    }\n\n    internal sealed override IGarmObject GetProperty(ulong memberId, string propertyName)\n    {\n        if (_propertyGetters.TryGetValue(memberId, out var tuple))\n        {\n            return tuple.Method();\n        }\n        throw CreateMethodNotMatchException(memberId, propertyName);\n    }\n\n    internal sealed override IGarmObject SetProperty(ulong memberId, string propertyName, IGarmObject value)\n    {\n        if (_propertySetters.TryGetValue(memberId, out var tuple))\n        {\n            tuple.Method(value);\n            return DefaultGarm;\n        }\n        throw CreateMethodNotMatchException(memberId, propertyName);\n    }\n\n    internal sealed override IGarmObject CallMethod(ulong memberId, string methodName, IGarmObject[] args)\n    {\n        if (_methods.TryGetValue(memberId, out var tuple))\n        {\n            return tuple.Method(args);\n        }\n        throw CreateMethodNotMatchException(memberId, methodName);\n    }\n\n    internal sealed override async Task<IGarmObject> CallMethodAsync(ulong memberId, string methodName, IGarmObject[] args)\n    {\n        if (_asyncMethods.TryGetValue(memberId, out var tuple))\n        {\n            return await tuple.Method(args).ConfigureAwait(false);\n        }\n        throw CreateMethodNotMatchException(memberId, methodName);\n    }\n\n    private T? CastArg<T>(IGarmObject argModel) => argModel switch\n    {\n        Garm go => go.ToGeneric<T>(Context.ObjectSerializer).Value,\n        Garm<T> go => go.Value,\n        _ => throw new InvalidOperationException(\"不可能进入此分支，因为所有参数都应该是 GARM 对象。\"),\n    };\n\n    internal override Type[] GetParameterTypes(ulong memberId, string? memberName, MemberInvokingType invokingType)\n    {\n        if (invokingType is MemberInvokingType.GetProperty)\n        {\n            return _propertyGetters.TryGetValue(memberId, out var tuple)\n                ? tuple.Types\n                : throw CreateMethodNotMatchException(memberId, memberName);\n        }\n        if (invokingType is MemberInvokingType.SetProperty)\n        {\n            return _propertySetters.TryGetValue(memberId, out var tuple)\n                ? tuple.Types\n                : throw CreateMethodNotMatchException(memberId, memberName);\n        }\n        if (invokingType is MemberInvokingType.Method)\n        {\n            return _methods.TryGetValue(memberId, out var tuple)\n                ? tuple.Types\n                : throw CreateMethodNotMatchException(memberId, memberName);\n        }\n        if (invokingType is MemberInvokingType.AsyncMethod)\n        {\n            return _asyncMethods.TryGetValue(memberId, out var tuple)\n                ? tuple.Types\n                : throw CreateMethodNotMatchException(memberId, memberName);\n        }\n        throw CreateMethodNotMatchException(memberId, memberName);\n    }\n\n    private Exception CreateMethodNotMatchException(ulong memberId, string? memberName)\n    {\n        return new IpcMemberNotFoundException(\n            $\"无法对接 Id 为 {memberId} 的 {typeof(TContract).FullName}.{memberName} 成员，因为没有在 {GetType().FullName} 的 IPC 对接类中进行匹配。\");\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/GeneratedIpcJoint.generic.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\npartial class GeneratedIpcJoint<TContract>\n{\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2>(ulong memberId, Action<T1, T2> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2>(ulong memberId, Func<T1, T2, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, TReturn>(ulong memberId, Func<T1, T2, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, TReturn>(ulong memberId, Func<T1, T2, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3>(ulong memberId, Action<T1, T2, T3> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3>(ulong memberId, Func<T1, T2, T3, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, TReturn>(ulong memberId, Func<T1, T2, T3, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, TReturn>(ulong memberId, Func<T1, T2, T3, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4>(ulong memberId, Action<T1, T2, T3, T4> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4>(ulong memberId, Func<T1, T2, T3, T4, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, TReturn>(ulong memberId, Func<T1, T2, T3, T4, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, TReturn>(ulong memberId, Func<T1, T2, T3, T4, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5>(ulong memberId, Action<T1, T2, T3, T4, T5> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5>(ulong memberId, Func<T1, T2, T3, T4, T5, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6>(ulong memberId, Action<T1, T2, T3, T4, T5, T6> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10)], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11),\n                ], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11),\n                ], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11),\n        ], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11),\n        ], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12),\n                ], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12),\n                ], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12),\n        ], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12),\n        ], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13),\n                ], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13),\n                ], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13),\n        ], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13),\n        ], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14),\n                ], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14),\n                ], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14),\n        ], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14),\n        ], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15),\n                ], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15),\n                ], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15),\n        ], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15),\n        ], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!).ConfigureAwait(false)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(ulong memberId, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15), typeof(T16),\n                ], args =>\n        {\n            methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!, CastArg<T16>(args[15])!);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Task> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15), typeof(T16),\n                ], async args =>\n        {\n            await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!, CastArg<T16>(args[15])!).ConfigureAwait(false);\n            return DefaultGarm;\n        }\n        ));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Garm<TReturn>> methodInvoker)\n    {\n        _methods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15), typeof(T16),\n        ], args => methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!, CastArg<T16>(args[15])!)));\n    }\n\n    /// <summary>\n    /// 匹配一个 IPC 目标对象上的某个方法，使其他 IPC 节点访问此 IPC 对象时能执行 <paramref name=\"methodInvoker\"/> 所指向的具体实现。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"methodInvoker\">对接实现。</param>\n    protected void MatchMethod<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TReturn>(ulong memberId, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Task<Garm<TReturn>>> methodInvoker)\n    {\n        _asyncMethods.Add(memberId, ([typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5), typeof(T6), typeof(T7), typeof(T8), typeof(T9), typeof(T10), typeof(T11), typeof(T12), typeof(T13), typeof(T14), typeof(T15), typeof(T16),\n        ], async args => await methodInvoker(CastArg<T1>(args[0])!, CastArg<T2>(args[1])!, CastArg<T3>(args[2])!, CastArg<T4>(args[3])!, CastArg<T5>(args[4])!, CastArg<T6>(args[5])!, CastArg<T7>(args[6])!, CastArg<T8>(args[7])!, CastArg<T9>(args[8])!, CastArg<T10>(args[9])!, CastArg<T11>(args[10])!, CastArg<T12>(args[11])!, CastArg<T13>(args[12])!, CastArg<T14>(args[13])!, CastArg<T15>(args[14])!, CastArg<T16>(args[15])!).ConfigureAwait(false)));\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/GeneratedIpcProxy.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.ExceptionServices;\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Utils;\nusing dotnetCampus.Ipc.Exceptions;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// 提供给自动生成的代理对象使用，以便能够生成通过 IPC 方式访问目标成员的能力。\n/// </summary>\npublic abstract partial class GeneratedIpcProxy\n{\n    /// <summary>\n    /// 为库内派生类提供 IPC 代理调用的辅助方法。\n    /// </summary>\n    private protected IpcProxyInvokingHelper Invoker { get; } = new IpcProxyInvokingHelper();\n\n    /// <summary>\n    /// 提供基于 .NET 类型的 IPC 传输上下文信息。\n    /// </summary>\n    public GeneratedProxyJointIpcContext Context\n    {\n        get => Invoker.Context;\n        internal set => Invoker.Context = value;\n    }\n\n    /// <summary>\n    /// 获取或设置目标 IPC 节点。\n    /// 如果设定为 null，则所有请求将返回默认值。（未来可能运行在抛出异常和返回默认值之间进行选择。）\n    /// </summary>\n    public IPeerProxy? PeerProxy\n    {\n        get => Invoker.PeerProxy;\n        internal set => Invoker.PeerProxy = value;\n    }\n\n    /// <summary>\n    /// 如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。\n    /// </summary>\n    public string TypeName\n    {\n        get => Invoker.TypeName;\n        internal set => Invoker.TypeName = value;\n    }\n\n    /// <summary>\n    /// 如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。\n    /// </summary>\n    public string? ObjectId\n    {\n        get => Invoker.ObjectId;\n        internal set => Invoker.ObjectId = value;\n    }\n\n    /// <summary>\n    /// 除了编译时确定的 IPC 代理访问配置之外，还可以额外指定一个运行时的配置。优先级最低，在编译时配置没有设的时候使用。\n    /// </summary>\n    internal IpcProxyConfigs? RuntimeConfigs { get; set; }\n}\n\n/// <summary>\n/// 提供给自动生成的代理对象使用，以便能够生成通过 IPC 方式访问目标成员的能力。\n/// </summary>\npublic abstract class GeneratedIpcProxy<TContract> : GeneratedIpcProxy where TContract : class\n{\n    private readonly ConcurrentDictionary<string, object?> _readonlyPropertyValues = new(StringComparer.Ordinal);\n\n    /// <summary>\n    /// 创建 <typeparamref name=\"TContract\"/> 类型的 IPC 代理对象。\n    /// </summary>\n    protected GeneratedIpcProxy()\n    {\n        TypeName = typeof(TContract).FullName!;\n    }\n\n    /// <summary>\n    /// 通过 IPC 访问目标对象上某属性的值。\n    /// </summary>\n    /// <typeparam name=\"T\">属性类型。</typeparam>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"info\">包含属性上标记的调用此 IPC 属性的个性化方式。</param>\n    /// <param name=\"propertyName\">属性名称。</param>\n    /// <returns>可异步等待的属性的值。</returns>\n    protected async Task<T> GetValueAsync<T>(ulong memberId, IpcMemberInfo info, [CallerMemberName] string propertyName = \"\")\n    {\n        if (info.IsReadonly ?? false)\n        {\n            // 通过 IPC 访问目标对象上标记了 IpcPropertyAttribute 的属性。如果曾访问过，会将这个值缓存下来供下次无 IPC 访问。\n            // 当发生并发时，可能导致多次通过 IPC 访问此属性的值，但此方法依然是线程安全的。\n            if (_readonlyPropertyValues.TryGetValue(propertyName, out var cachedValue))\n            {\n                // 当只读字典中存在此属性的缓存时，直接取缓存。\n                return (T)cachedValue!;\n            }\n            // 否则，通过 IPC 访问获取此属性的值后设入缓存。（这里可能存在并发情况，会导致浪费的 IPC 访问，但能确保数据一致性）。\n            var value = await IpcInvokeAsync<T>(MemberInvokingType.GetProperty, memberId, propertyName, null, info).ConfigureAwait(false);\n            _readonlyPropertyValues.TryAdd(propertyName, value);\n            return value!;\n        }\n        else\n        {\n            return await IpcInvokeAsync<T>(MemberInvokingType.GetProperty, memberId, propertyName, null, info).ConfigureAwait(false);\n        }\n    }\n\n    /// <summary>\n    /// 通过 IPC 设置目标对象上某属性的值。\n    /// </summary>\n    /// <typeparam name=\"T\">属性类型。</typeparam>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"value\">要设置的属性的值。</param>\n    /// <param name=\"info\">包含属性上标记的调用此 IPC 属性的个性化方式。</param>\n    /// <param name=\"propertyName\">属性名称。</param>\n    /// <returns>可异步等待的属性设置。</returns>\n    protected Task SetValueAsync<T>(ulong memberId, Garm<T> value, IpcMemberInfo info, [CallerMemberName] string propertyName = \"\")\n    {\n        return IpcInvokeAsync<object>(MemberInvokingType.SetProperty, memberId, propertyName, [value], info);\n    }\n\n    /// <summary>\n    /// 通过 IPC 调用目标对象上的某个方法。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"args\">方法参数列表。</param>\n    /// <param name=\"info\">包含方法上标记的调用此 IPC 方法的个性化方式。</param>\n    /// <param name=\"methodName\">方法名。</param>\n    /// <returns>可异步等待方法返回值的可等待对象。</returns>\n    protected Task CallMethod(ulong memberId, IGarmObject[]? args, IpcMemberInfo info, [CallerMemberName] string methodName = \"\")\n    {\n        return IpcInvokeAsync<object>(MemberInvokingType.Method, memberId, methodName, args, info);\n    }\n\n    /// <summary>\n    /// 通过 IPC 调用目标对象上的某个方法。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"args\">方法参数列表。</param>\n    /// <param name=\"info\">包含方法上标记的调用此 IPC 方法的个性化方式。</param>\n    /// <param name=\"methodName\">方法名。</param>\n    /// <returns>可异步等待方法返回值的可等待对象。</returns>\n    protected Task<T> CallMethod<T>(ulong memberId, IGarmObject[]? args, IpcMemberInfo info, [CallerMemberName] string methodName = \"\")\n    {\n        return IpcInvokeAsync<T>(MemberInvokingType.Method, memberId, methodName, args, info);\n    }\n\n    /// <summary>\n    /// 通过 IPC 调用目标对象上的某个异步方法。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"args\">方法参数列表。</param>\n    /// <param name=\"info\">包含方法上标记的调用此 IPC 方法的个性化方式。</param>\n    /// <param name=\"methodName\">方法名。</param>\n    /// <returns>可异步等待方法返回值的可等待对象。</returns>\n    protected Task CallMethodAsync(ulong memberId, IGarmObject[]? args, IpcMemberInfo info, [CallerMemberName] string methodName = \"\")\n    {\n        return IpcInvokeAsync<object>(MemberInvokingType.AsyncMethod, memberId, methodName, args, info);\n    }\n\n    /// <summary>\n    /// 通过 IPC 调用目标对象上的某个异步方法。\n    /// </summary>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"args\">方法参数列表。</param>\n    /// <param name=\"info\">包含方法上标记的调用此 IPC 方法的个性化方式。</param>\n    /// <param name=\"methodName\">方法名。</param>\n    /// <returns>可异步等待方法返回值的可等待对象。</returns>\n    protected Task<T> CallMethodAsync<T>(ulong memberId, IGarmObject[]? args, IpcMemberInfo info, [CallerMemberName] string methodName = \"\")\n    {\n        return IpcInvokeAsync<T>(MemberInvokingType.AsyncMethod, memberId, methodName, args, info);\n    }\n\n    /// <summary>\n    /// 在 <see cref=\"IpcMethodAttribute\"/> 和 <see cref=\"IpcPropertyAttribute\"/> 中标记的访问属性和调用方法相关的个性化方式，大多数将在此方法中被实践，少数在自动生成代理类时被实践。\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"callType\">调用类型（属性还是方法）。</param>\n    /// <param name=\"memberId\">方法签名的 Id。</param>\n    /// <param name=\"memberName\">成员名。</param>\n    /// <param name=\"args\">调用参数。</param>\n    /// <param name=\"info\">包含属性上标记的调用此 IPC 成员的个性化方式。</param>\n    /// <returns>可异步等待方法返回值的可等待对象。</returns>\n    private async Task<T> IpcInvokeAsync<T>(MemberInvokingType callType, ulong memberId, string memberName, IGarmObject[]? args, IpcMemberInfo info)\n    {\n        var ignoresIpcException = info.IgnoresIpcException ?? RuntimeConfigs?.IgnoresIpcException ?? false;\n        try\n        {\n            var result = (info.Timeout ?? RuntimeConfigs?.Timeout) is { } timeout and > 0\n                ? await InvokeWithTimeoutAsync<T>(\n                    callType,\n                    memberId,\n                    memberName,\n                    args,\n                    timeout,\n                    ignoresIpcException,\n                    info.DefaultReturn).ConfigureAwait(false)\n                : await Invoker.IpcInvokeAsync<T>(\n                    callType,\n                    memberId,\n                    memberName,\n                    args).ConfigureAwait(false);\n            return result!;\n        }\n        catch (IpcRemoteException) when (ignoresIpcException)\n        {\n            // 如果目标要求忽略异常，则返回指定值或默认值。\n            return info.DefaultReturn is { } defaultReturn ? (T)defaultReturn : default!;\n        }\n        catch (AggregateException ex) when (ex.InnerExceptions.Count >= 1)\n        {\n            var innerException = ex.Flatten().InnerExceptions[0];\n            if (innerException is IpcRemoteException)\n            {\n                // 如果目标要求忽略异常，则返回指定值或默认值。\n                return info.DefaultReturn is { } defaultReturn ? (T)defaultReturn : default!;\n            }\n            else\n            {\n                // 尽量不抛出难以捕获的 AggregateException 而改用内部异常。\n                ExceptionDispatchInfo.Capture(innerException).Throw();\n                throw;\n            }\n        }\n    }\n\n    private async Task<T> InvokeWithTimeoutAsync<T>(MemberInvokingType callType, ulong memberId, string memberName, IGarmObject[]? args,\n        int millisecondsTimeout, bool ignoreException, object? defaultReturn)\n    {\n        var ipcTask = Invoker.IpcInvokeAsync<T>(callType, memberId, memberName, args);\n        var timeoutTask = Task.Delay(millisecondsTimeout);\n        var task = await Task.WhenAny(ipcTask, timeoutTask).ConfigureAwait(false);\n        if (task == ipcTask)\n        {\n            // 任务正常完成。\n            return ipcTask.Result!;\n        }\n        if (ignoreException)\n        {\n            // 任务超时（不抛异常）。\n            IgnoreTaskExceptionsAsync(ipcTask);\n            return defaultReturn is null ? default! : (T)defaultReturn;\n        }\n        // 任务超时（抛异常）。\n        IgnoreTaskExceptionsAsync(ipcTask);\n        throw new IpcInvokingTimeoutException(memberName, TimeSpan.FromMilliseconds(millisecondsTimeout));\n    }\n\n    /// <summary>\n    /// 吞掉业务上已不再需要返回值的异常，防止异常泄漏到后台线程或 Task 中。\n    /// </summary>\n    /// <param name=\"task\">要吞掉异常的任务。</param>\n    private static async void IgnoreTaskExceptionsAsync(Task task)\n    {\n        try\n        {\n            await task;\n        }\n        catch\n        {\n            // 吞掉业务上已不再需要返回值的异常，防止异常泄漏到后台线程或 Task 中。\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/GeneratedProxyJointIpcContext.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Serialization;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies\n{\n    /// <summary>\n    /// 绑定到 <see cref=\"IIpcProvider\"/> 后可提供基于 .NET 类型的 IPC 传输。\n    /// </summary>\n    public class GeneratedProxyJointIpcContext\n    {\n        /// <summary>\n        /// 创建 <see cref=\"GeneratedProxyJointIpcContext\"/> 的新实例。\n        /// </summary>\n        /// <param name=\"ipcContext\"></param>\n        internal GeneratedProxyJointIpcContext(IpcContext ipcContext)\n        {\n            ObjectSerializer = ipcContext.IpcConfiguration.IpcObjectSerializer;\n            JointManager = new PublicIpcJointManager(this);\n            RequestHandler = new GeneratedProxyJointIpcRequestHandler(this, ipcContext);\n        }\n\n        /// <summary>\n        /// 包含 IPC 对接的管理。\n        /// </summary>\n        internal PublicIpcJointManager JointManager { get; }\n\n        /// <summary>\n        /// 请将此属性赋值给 IpcConfiguration.DefaultIpcRequestHandler 以获得 .NET 类型的 IPC 传输访问能力。\n        /// </summary>\n        public IIpcRequestHandler RequestHandler { get; }\n\n        /// <summary>\n        /// 用于序列化和反序列化 IPC 对象的序列化器。\n        /// </summary>\n        public IIpcObjectSerializer ObjectSerializer { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/GeneratedProxyJointIpcRequestHandler.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies\n{\n    /// <summary>\n    /// 为生成的 <see cref=\"GeneratedIpcJoint{TContract}\"/> 提供默认的对 <see cref=\"GeneratedIpcProxy{TContract}\"/> 的对接。\n    /// </summary>\n    internal sealed class GeneratedProxyJointIpcRequestHandler : IIpcRequestHandler\n    {\n        private readonly IpcContext _ipcContext;\n\n        internal GeneratedProxyJointIpcContext Owner { get; }\n\n        internal GeneratedProxyJointIpcRequestHandler(GeneratedProxyJointIpcContext context, IpcContext ipcContext)\n        {\n            Owner = context;\n            _ipcContext = ipcContext;\n        }\n\n        Task<IIpcResponseMessage> IIpcRequestHandler.HandleRequest(IIpcRequestContext requestContext)\n        {\n            return _ipcContext.TaskPool.Run(async () =>\n            {\n                if (Owner.JointManager.TryJoint(requestContext, out var responseTask))\n                {\n                    requestContext.Handled = true;\n                    var response = await responseTask.ConfigureAwait(false);\n                    return response;\n                }\n\n                return KnownIpcResponseMessages.CannotHandle;\n            }, _ipcContext.Logger);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/IpcProxyConfigs.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\n\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n// 注意：本文件中的配置类因为未来对修改开放，所以**不适合**提取接口。如坚持提取，未来定会造成兼容性问题！\n\n/// <summary>\n/// 为 IPC 代理访问提供配置。\n/// <para>注意：</para>\n/// <list type=\"number\">\n/// <item>使用此方式指定的 IPC 代理配置优先级为“最低”，低于 <see cref=\"IpcPublicAttribute\"/> 指定的配置，更低于在接口的成员上单独指定的配置。因此仅在接口上或接口的成员上没有指定 IPC 代理配置时才会生效。</item>\n/// <item>如需覆盖接口上指定的 IPC 代理配置，请额外生成一个 IPC 形状代理（）</item>\n/// </list>\n/// </summary>\npublic class IpcProxyConfigs\n{\n    /// <summary>\n    /// 如果指定为 true，则本类型在 IPC 调用发生异常时会忽略这些异常，并返回默认值。\n    /// <para>默认值会采用属性类型或方法返回值类型的默认值（即 default），如果希望额外指定默认值，请：</para>\n    /// <list type=\"bullet\">\n    /// <item>在单独的属性上使用 IpcPropertyAttribute.DefaultReturn</item>\n    /// <item>在单独的属性上使用 IpcMethodAttribute.DefaultReturn</item>\n    /// </list>\n    /// </summary>\n    /// <remarks>\n    /// 请注意：\n    /// <list type=\"bullet\">\n    /// <item>此特性仅忽略 IPC 连接异常和 IPC 超时异常（例如进程退出、连接断开等），而不会忽略普通业务异常（例如业务实现中抛出了 <see cref=\"NullReferenceException\"/> 等）。</item>\n    /// <item>另外，如果 IPC 框架内部出现了 bug 导致了异常，也不会因此而忽略。</item>\n    /// </list>\n    /// </remarks>\n    [DefaultValue(false)]\n    public bool IgnoresIpcException { get; set; }\n\n    /// <summary>\n    /// 设定此类型中所有成员执行的默认超时时间（毫秒）。如果自成员执行开始直至超时时间后依然没有返回，则：\n    /// <list type=\"bullet\">\n    /// <item>默认会引发 <see cref=\"dotnetCampus.Ipc.Exceptions.IpcInvokingTimeoutException\"/>。</item>\n    /// <item>通过在类型或成员上设置 <see cref=\"IgnoresIpcException\"/> 可阻止引发超时异常而改为返回默认值。</item>\n    /// </list>\n    /// </summary>\n    [DefaultValue(0)]\n    public int Timeout { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/GeneratedIpcProxyJointInstanceCache.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\nusing dotnetCampus.Ipc.Utils.Caching;\nusing static dotnetCampus.Ipc.CompilerServices.GeneratedProxies.GeneratedIpcFactory;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n/// <summary>\n/// 在跨进程的 IPC 方法调用中，方法参数或返回值可能并不是基本类型，而是另一个 IPC 类型的实例。\n/// 本类型提供辅助方法判断和转换 <see cref=\"object\"/> 到可被序列化的 IPC 类型，并辅助生成 IPC 类型的代理和对接。\n/// </summary>\ninternal static class GeneratedIpcProxyJointInstanceCache\n{\n    /// <summary>\n    /// 为代理返回值或对接参数自动创建的 <see cref=\"GeneratedIpcProxy\"/> 对象缓存。\n    /// </summary>\n    private static readonly CachePool<(Type IpcPublicType, string? ObjectId, string PeerName), GeneratedIpcProxy> ProxyCache = new(ConvertTypeAndIdToProxy, true);\n\n    /// <summary>\n    /// 为代理参数或对接返回值自动创建的 <see cref=\"GeneratedIpcJoint\"/> 对象缓存。\n    /// </summary>\n    private static readonly CachePool<(PublicIpcJointManager JointManager, Type IpcPublicType, object RealInstance), (GeneratedIpcJoint JointInstance, string? ObjectId)> JointCache = new(ConvertTypeAndIdToJoint, true);\n\n    /// <summary>\n    /// 当从 IPC 传输过来一些对象信息时，通过此方法可以判断此对象是否是一个 IPC 公开的对象。\n    /// 如果是一个 IPC 公开的对象，则可以从 <paramref name=\"proxyReturnOrJointArg\"/> 获取到这个对象的本地代理。\n    /// </summary>\n    /// <param name=\"context\">基于 .NET 类型进行 IPC 传输的上下文信息。</param>\n    /// <param name=\"peerProxy\">IPC 对方端。</param>\n    /// <param name=\"ipcType\">标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型。</param>\n    /// <param name=\"objectId\">如果可能有同一个契约类型的多个对象，则在此传入此对象的 IPC 访问 Id。</param>\n    /// <param name=\"proxyReturnOrJointArg\">如果经判断是一个 IPC 公开的对象，则可以从此参数中获取到这个对象的本地代理。</param>\n    /// <returns>如果这是一个 IPC 公开的对象，则返回 true，如果只是一个普通对象，则返回 false。</returns>\n    public static bool TryCreateProxyFromSerializationInfo(this GeneratedProxyJointIpcContext context,\n        IPeerProxy peerProxy, Type? ipcType, string? objectId,\n        out object? proxyReturnOrJointArg)\n    {\n        if (ipcType is not null && IpcPublicFactories.ContainsKey(ipcType))\n        {\n            var proxyInstance = ProxyCache[(ipcType, objectId, peerProxy.PeerName)];\n            proxyInstance.Context = context;\n            proxyInstance.PeerProxy = peerProxy;\n            proxyReturnOrJointArg = proxyInstance;\n            return true;\n        }\n\n        proxyReturnOrJointArg = null;\n        return false;\n    }\n\n    /// <summary>\n    /// 当试图返回一个 IPC 对象时，创建此对象的 IPC 对接，然后将此对接转换为可被 IPC 传输的对象信息。\n    /// </summary>\n    /// <param name=\"context\">基于 .NET 类型进行 IPC 传输的上下文信息。</param>\n    /// <param name=\"proxyArgOrJointReturnModel\">IPC 本地对接对象。</param>\n    /// <param name=\"objectId\">如果可能有同一个契约类型的多个对象，则在此传入此对象的 IPC 访问 Id。</param>\n    /// <param name=\"ipcTypeFullName\">对象真实实例的类型名称。</param>\n    /// <returns></returns>\n    public static bool TryCreateSerializationInfoFromIpcRealInstance(this GeneratedProxyJointIpcContext context,\n        IGarmObject proxyArgOrJointReturnModel,\n        out string? objectId,\n        [NotNullWhen(true)] out string? ipcTypeFullName)\n    {\n        if (proxyArgOrJointReturnModel.IpcType is { } ipcType\n            && IpcPublicFactories[ipcType].JointFactory is not null\n            && proxyArgOrJointReturnModel.Value is { } value)\n        {\n            // 这是一个 IPC 对象，需要传递 IPC 信息。\n            (_, objectId) = JointCache[(context.JointManager, ipcType, value)];\n            ipcTypeFullName = ipcType.FullName!;\n            return true;\n        }\n\n        // 这是一个普通对象，序列化即可。\n        objectId = null;\n        ipcTypeFullName = null;\n        return false;\n    }\n\n    public static GeneratedIpcProxy ConvertTypeAndIdToProxy((Type IpcPublicType, string? ObjectId, string PeerName) key)\n    {\n        var (proxyType, objectId, _) = key;\n        var proxyInstance = IpcPublicFactories[proxyType].ProxyFactory();\n        proxyInstance.ObjectId = objectId;\n        return proxyInstance;\n    }\n\n    public static (GeneratedIpcJoint JointInstance, string? ObjectId) ConvertTypeAndIdToJoint((PublicIpcJointManager JointManager, Type IpcPublicType, object RealInstance) key)\n    {\n        var (jointManager, ipcPublicType, realInstance) = key;\n        var objectId = Guid.NewGuid().ToString();\n        var jointInstance = IpcPublicFactories[ipcPublicType].JointFactory();\n        jointInstance.Context = jointManager.Context;\n        jointInstance.SetInstance(realInstance);\n        jointManager.AddPublicIpcObject(ipcPublicType, jointInstance, objectId);\n        return (jointInstance, objectId);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/GeneratedProxyExceptionModel.cs",
    "content": "﻿using System.Linq.Expressions;\nusing System.Reflection;\nusing System.Runtime.ExceptionServices;\nusing System.Runtime.Serialization;\nusing dotnetCampus.Ipc.Exceptions;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#endif\n#if NET6_0_OR_GREATER\nusing System.Text.Json.Serialization;\n#endif\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n[DataContract]\ninternal class GeneratedProxyExceptionModel\n{\n    public GeneratedProxyExceptionModel()\n    {\n    }\n\n    public GeneratedProxyExceptionModel(Exception exception)\n    {\n        ExceptionType = exception.GetType().FullName;\n        Message = exception.Message;\n        StackTrace = exception.StackTrace;\n        ExtraInfo = null;\n    }\n\n#if UseNewtonsoftJson\n    [JsonProperty(\"t\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"t\")]\n#endif\n    public string? ExceptionType { get; set; }\n\n#if UseNewtonsoftJson\n    [JsonProperty(\"m\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"m\")]\n#endif\n    public string? Message { get; set; }\n\n#if UseNewtonsoftJson\n    [JsonProperty(\"s\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"s\")]\n#endif\n    public string? StackTrace { get; set; }\n\n#if UseNewtonsoftJson\n    [JsonProperty(\"x\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"x\")]\n#endif\n    public string? ExtraInfo { get; set; }\n\n    public void Throw()\n    {\n        if (ExceptionType is { } typeName)\n        {\n            if (ExceptionRebuilders.TryGetValue(typeName, out var builder))\n            {\n                var exception = builder(Message, ExtraInfo);\n                if (StackTrace is { } stackTrace)\n                {\n#if NET6_0_OR_GREATER\n                    exception = ExceptionDispatchInfo.SetRemoteStackTrace(exception, stackTrace);\n#else\n                    exception = ExceptionHacker.SetRemoteStackTrace(exception, stackTrace);\n#endif\n                }\n                ExceptionDispatchInfo.Capture(exception).Throw();\n            }\n            else\n            {\n                throw new IpcInvokingException(\n                    $\"远端抛出了异常 {typeName}: {Message}\\n如需抛出普通异常，请联系 IPC 库作者将异常添加到 ExceptionRebuilders 中。\",\n                    StackTrace);\n            }\n        }\n\n        throw new InvalidOperationException(\"无法抛出远端对象的异常，因为无法获知异常的类型。\");\n    }\n\n    private static readonly Dictionary<string, Func<string?, string?, Exception>> ExceptionRebuilders = new(StringComparer.Ordinal)\n    {\n        { typeof(ArgumentException).FullName!, (m, _) => new ArgumentException(m) },\n        { typeof(ArgumentNullException).FullName!, (m, e) => new ArgumentNullException(e, m) },\n        { typeof(BadImageFormatException).FullName!, (m, _) => new BadImageFormatException(m) },\n        { typeof(InvalidCastException).FullName!, (m, _) => new InvalidCastException(m) },\n        { typeof(InvalidOperationException).FullName!, (m, _) => new InvalidOperationException(m) },\n        { typeof(NotImplementedException).FullName!, (m, _) => new NotImplementedException(m) },\n        { typeof(NotSupportedException).FullName!, (m, _) => new NotSupportedException(m) },\n        { typeof(NullReferenceException).FullName!, (m, _) => new NullReferenceException(m) },\n    };\n}\n\n#if NET6_0_OR_GREATER\n#else\nfile static class ExceptionHacker\n{\n    static ExceptionHacker()\n    {\n        HackExceptionStackTraceLazy = new Lazy<Func<Exception, string, Exception>>(() => HackExceptionStackTrace, LazyThreadSafetyMode.None);\n    }\n\n    internal static Exception SetRemoteStackTrace(Exception source, string stackTrace)\n    {\n        try\n        {\n            HackExceptionStackTraceLazy.Value(source, stackTrace);\n        }\n        catch\n        {\n            // 新框架已经处理了，不管旧框架的死活。\n        }\n        return source;\n    }\n\n    private static readonly Lazy<Func<Exception, string, Exception>> HackExceptionStackTraceLazy;\n\n    private static readonly Func<Exception, string, Exception> HackExceptionStackTrace = new Func<Func<Exception, string, Exception>>(() =>\n    {\n        var source = Expression.Parameter(typeof(Exception));\n        var stackTrace = Expression.Parameter(typeof(string));\n        // Try to get _remoteStackTraceString first, then fallback to _stackTraceString for older .NET Frameworks\n        var fieldInfo = typeof(Exception).GetField(\"_remoteStackTraceString\", BindingFlags.NonPublic | BindingFlags.Instance)\n                        ?? typeof(Exception).GetField(\"_stackTraceString\", BindingFlags.NonPublic | BindingFlags.Instance);\n        if (fieldInfo == null)\n        {\n            // If neither field exists, just return the exception unchanged\n            return (ex, st) => ex;\n        }\n        var assign = Expression.Assign(Expression.Field(source, fieldInfo), stackTrace);\n        return Expression.Lambda<Func<Exception, string, Exception>>(Expression.Block(assign, source), source, stackTrace).Compile();\n    })();\n}\n#endif\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/GeneratedProxyMemberInvokeModel.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Runtime.Serialization;\nusing System.Text;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#endif\n#if NET6_0_OR_GREATER\nusing System.Text.Json.Serialization;\n#endif\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n/// <summary>\n/// IPC 传输过程中使用的内部模型，表示如何调用一个远端的方法。\n/// 目前为调试方便，暂使用 JSON 格式，易读。后续如果成为性能瓶颈，则可换成字节流。\n/// </summary>\n[DataContract]\ninternal class GeneratedProxyMemberInvokeModel\n{\n    private string? _id;\n    private string? _contractFullTypeName;\n\n    /// <summary>\n    /// 远端对象 Id。\n    /// 当同一个契约类型的对象存在多个时，则需要通过此 Id 进行区分。\n    /// 空字符串（\"\"）和空值（null）是相同含义，允许设 null 值，但获取时永不为 null（会自动转换为空字符串）。\n    /// </summary>\n    [AllowNull]\n#if UseNewtonsoftJson\n    [JsonProperty(\"i\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"i\")]\n#endif\n    public string Id\n    {\n        get => _id ?? \"\";\n        set => _id = value;\n    }\n\n    /// <summary>\n    /// 远端对象的契约类型名称（含命名空间，不含 Token）。\n    /// </summary>\n#if UseNewtonsoftJson\n    [JsonProperty(\"t\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"t\")]\n#endif\n    public string ContractFullTypeName\n    {\n        get => _contractFullTypeName ?? throw new InvalidOperationException(\"在获取 ContractFullTypeName 之前，必须先对其反序列化。\");\n        set => _contractFullTypeName = value;\n    }\n\n    /// <summary>\n    /// 调用的成员 Id（由源代码生成器自动生成，唯一表示一个属性或方法）。\n    /// </summary>\n#if UseNewtonsoftJson\n    [JsonProperty(\"d\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"d\")]\n#endif\n    public ulong MemberId { get; set; }\n\n    /// <summary>\n    /// 调用的成员名称（属性名、方法名）。\n    /// </summary>\n#if UseNewtonsoftJson\n    [JsonProperty(\"m\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"m\")]\n#endif\n    public string? MemberName { get; set; }\n\n    /// <summary>\n    /// 指定如何调用远端的对象。目前支持读属性、写属性、调用方法和调用异步方法。\n    /// </summary>\n#if UseNewtonsoftJson\n    [JsonProperty(\"c\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"c\")]\n#endif\n    public MemberInvokingType CallType { get; set; }\n\n    /// <summary>\n    /// 参数列表。\n    /// </summary>\n#if UseNewtonsoftJson\n    [JsonProperty(\"a\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"a\")]\n#endif\n    public GeneratedProxyObjectModel?[]? Args { get; set; }\n\n    /// <summary>\n    /// 把方法调用过程以调用栈一帧的方式表示出来。\n    /// </summary>\n    /// <returns></returns>\n    public override string ToString()\n    {\n        var isMethod = CallType is MemberInvokingType.Method or MemberInvokingType.AsyncMethod;\n        var builder = new StringBuilder();\n        if (!string.IsNullOrEmpty(Id))\n        {\n            builder.Append('[').Append(Id).Append(']');\n        }\n        if (CallType is MemberInvokingType.AsyncMethod)\n        {\n            builder.Append(\"async \");\n        }\n        if (ContractFullTypeName is { } typeName)\n        {\n            builder.Append(typeName);\n        }\n        if (MemberName is { } memberName)\n        {\n            builder.Append('.');\n            if (CallType is MemberInvokingType.GetProperty)\n            {\n                builder.Append(\"get_\");\n            }\n            else if (CallType is MemberInvokingType.SetProperty)\n            {\n                builder.Append(\"set_\");\n            }\n            builder.Append(memberName);\n            if (isMethod)\n            {\n                builder.Append('(');\n            }\n            else if (CallType is MemberInvokingType.SetProperty)\n            {\n                builder.Append('=');\n            }\n        }\n        if (Args is { } args)\n        {\n            for (var i = 0; i < args.Length; i++)\n            {\n                var arg = args[i]?.Value;\n                if (i != 0)\n                {\n                    builder.Append(\", \");\n                }\n                builder.Append(arg?.ToString() ?? \"null\");\n            }\n        }\n        if (isMethod)\n        {\n            builder.Append(')');\n        }\n        return builder.ToString();\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/GeneratedProxyMemberReturnModel.cs",
    "content": "﻿using System.Runtime.Serialization;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#endif\n#if NET6_0_OR_GREATER\nusing System.Text.Json.Serialization;\n#endif\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n[DataContract]\ninternal class GeneratedProxyMemberReturnModel\n{\n    public GeneratedProxyMemberReturnModel()\n    {\n    }\n\n    public GeneratedProxyMemberReturnModel(Exception exception)\n    {\n        if (exception is null)\n        {\n            throw new ArgumentNullException(nameof(exception));\n        }\n\n        Exception = new GeneratedProxyExceptionModel(exception);\n    }\n\n    public GeneratedProxyMemberReturnModel(IpcJsonElement @return)\n    {\n        // 当返回对象为其他类型时，将尝试进行序列化。\n        Return = new GeneratedProxyObjectModel { Value = @return };\n    }\n\n#if DEBUG\n#if UseNewtonsoftJson\n    [JsonProperty(\"i\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"i\")]\n#endif\n    public GeneratedProxyMemberInvokeModel? Invoking { get; set; }\n#endif\n\n#if UseNewtonsoftJson\n    [JsonProperty(\"r\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"r\")]\n#endif\n    public GeneratedProxyObjectModel? Return { get; set; }\n\n#if UseNewtonsoftJson\n    [JsonProperty(\"e\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"e\")]\n#endif\n    public GeneratedProxyExceptionModel? Exception { get; set; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/GeneratedProxyObjectModel.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Runtime.Serialization;\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#endif\n#if NET6_0_OR_GREATER\nusing System.Text.Json.Serialization;\n#endif\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n[DataContract]\ninternal class GeneratedProxyObjectModel\n{\n    private string? _id;\n\n    /// <summary>\n    /// 远端对象 Id。\n    /// 当同一个契约类型的对象存在多个时，则需要通过此 Id 进行区分。\n    /// 空字符串（\"\"）和空值（null）是相同含义，允许设 null 值，但获取时永不为 null（会自动转换为空字符串）。\n    /// </summary>\n    [AllowNull]\n#if UseNewtonsoftJson\n    [JsonProperty(\"i\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"i\")]\n#endif\n    public string Id\n    {\n        get => _id ?? \"\";\n        set => _id = value;\n    }\n\n    /// <summary>\n    /// 远端对象的契约类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 的类型，不支持 <see cref=\"IpcShapeAttribute\"/>）的名称。\n    /// </summary>\n#if UseNewtonsoftJson\n    [JsonProperty(\"t\")]\n#endif\n#if NET6_0_OR_GREATER\n    [JsonPropertyName(\"t\")]\n#endif\n    public string? IpcPublicTypeFullName { get; set; }\n\n    /// <summary>\n    /// 远端对象的值。\n    /// </summary>\n#if NET6_0_OR_GREATER\n    [System.Text.Json.Serialization.JsonIgnore]\n#endif\n#if UseNewtonsoftJson\n    [Newtonsoft.Json.JsonIgnore]\n#endif\n    public IpcJsonElement Value { get; set; }\n\n#if UseNewtonsoftJson\n    /// <summary>\n    /// 原始 JSON 格式的远端对象的值。\n    /// </summary>\n    [Newtonsoft.Json.JsonProperty(\"v\")]\n#if NET6_0_OR_GREATER\n    [System.Text.Json.Serialization.JsonIgnore]\n#endif\n    public Newtonsoft.Json.Linq.JToken? RawValueOnNewtonsoftJson\n    {\n        get => Value.RawValueOnNewtonsoftJson;\n        set => Value = new IpcJsonElement { RawValueOnNewtonsoftJson = value };\n    }\n#endif\n\n#if NET6_0_OR_GREATER\n    /// <summary>\n    /// 原始 JSON 格式的远端对象的值。\n    /// </summary>\n    [System.Text.Json.Serialization.JsonPropertyName(\"v\")]\n#if UseNewtonsoftJson\n    [Newtonsoft.Json.JsonIgnore]\n#endif\n    public System.Text.Json.JsonElement? RawValueOnSystemTextJson\n    {\n        get => Value.RawValueOnSystemTextJson;\n        set => Value = new IpcJsonElement { RawValueOnSystemTextJson = value };\n    }\n#endif\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/IpcJsonElement.cs",
    "content": "﻿using dotnetCampus.Ipc.Serialization;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json.Linq;\n#else\nusing System.Text.Json;\n#endif\n\n// ReSharper disable ReplaceWithSingleAssignment.False\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n/// <summary>\n/// 表示一个即将发送向远端的对象，或从远端接收到的对象。\n/// </summary>\npublic readonly record struct IpcJsonElement\n{\n#if UseNewtonsoftJson\n    /// <summary>\n    /// 原始 JSON 格式的远端对象的值。\n    /// </summary>\n    public Newtonsoft.Json.Linq.JToken? RawValueOnNewtonsoftJson { get; init; }\n#endif\n\n#if NET6_0_OR_GREATER\n    /// <summary>\n    /// 原始 JSON 格式的远端对象的值。\n    /// </summary>\n    public System.Text.Json.JsonElement? RawValueOnSystemTextJson { get; init; }\n#endif\n\n    /// <summary>\n    /// 判断此 IPC 远端对象是否表示 <see langword=\"null\"/>。\n    /// </summary>\n    public bool IsNull\n    {\n        get\n        {\n#if UseNewtonsoftJson\n            var isNewtonsoftJsonNull = false;\n            if (RawValueOnNewtonsoftJson is null)\n            {\n                isNewtonsoftJsonNull = true;\n            }\n            if (RawValueOnNewtonsoftJson?.Type == Newtonsoft.Json.Linq.JTokenType.Null)\n            {\n                isNewtonsoftJsonNull = true;\n            }\n#else\n            var isNewtonsoftJsonNull = true;\n#endif\n\n            var isSystemTextJsonNull = false;\n#if NET6_0_OR_GREATER\n            if (RawValueOnSystemTextJson is null)\n            {\n                isSystemTextJsonNull = true;\n            }\n            if (RawValueOnSystemTextJson?.ValueKind == System.Text.Json.JsonValueKind.Null)\n            {\n                isSystemTextJsonNull = true;\n            }\n#endif\n            return isNewtonsoftJsonNull && isSystemTextJsonNull;\n        }\n    }\n\n    /// <inheritdoc />\n    public override string ToString()\n    {\n#if UseNewtonsoftJson\n        if (RawValueOnNewtonsoftJson is { } jToken)\n        {\n            return jToken.ToString();\n        }\n#endif\n#if NET6_0_OR_GREATER\n        if (RawValueOnSystemTextJson is { } json)\n        {\n            return json.ToString();\n        }\n#endif\n        return \"<null>\";\n    }\n\n    /// <summary>\n    /// 将一个本地对象序列化成 IPC 可传输的 JSON 对象。\n    /// </summary>\n    /// <param name=\"value\">本地对象。</param>\n    /// <param name=\"serializer\">对象序列化器。</param>\n    /// <returns>IPC 可传输的 JSON 对象。</returns>\n    public static IpcJsonElement Serialize(object? value, IIpcObjectSerializer serializer)\n    {\n        return value switch\n        {\n            null => new IpcJsonElement(),\n#if UseNewtonsoftJson\n            Newtonsoft.Json.Linq.JToken => throw new InvalidOperationException($\"编写错误检查，不应该传入 JSON 对象 {value.GetType().FullName}。\"),\n#endif\n#if NET6_0_OR_GREATER\n            System.Text.Json.JsonElement => throw new InvalidOperationException($\"编写错误检查，不应该传入 JSON 对象 {value.GetType().FullName}。\"),\n#endif\n            _ => serializer.SerializeToElement(value),\n        };\n    }\n\n    /// <summary>\n    /// 将 IPC 可传输的 JSON 对象反序列化成本地对象。\n    /// </summary>\n    /// <param name=\"jsonElement\">IPC 可传输的 JSON 对象。</param>\n    /// <param name=\"serializer\">对象序列化器。</param>\n    /// <typeparam name=\"T\">本地对象类型。</typeparam>\n    /// <returns>本地对象。</returns>\n    public static T? Deserialize<T>(IpcJsonElement jsonElement, IIpcObjectSerializer serializer)\n    {\n        if (jsonElement.IsNull)\n        {\n            return default!;\n        }\n\n#if UseNewtonsoftJson\n        if (jsonElement.RawValueOnNewtonsoftJson is JValue jValue)\n        {\n            return jValue.ToObject<T>();\n        }\n        if (jsonElement.RawValueOnNewtonsoftJson is JObject jObject)\n        {\n            return jObject.ToObject<T>();\n        }\n        if (jsonElement.RawValueOnNewtonsoftJson is JArray jArray)\n        {\n            return jArray.ToObject<T>();\n        }\n        if (jsonElement.RawValueOnNewtonsoftJson is not null)\n        {\n            throw new NotSupportedException(\"不支持将其他 JToken 类型转换成 IPC 业务类型。\");\n        }\n#endif\n\n#if NET6_0_OR_GREATER\n        if (jsonElement.RawValueOnSystemTextJson is { } json)\n        {\n            return serializer.Deserialize<T>(jsonElement);\n        }\n#endif\n\n        throw new InvalidOperationException(\"不可能进入的分支，前面一定已经成功反序列化了。\");\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/IpcMemberInfo.cs",
    "content": "﻿// ReSharper disable CheckNamespace\n\nusing dotnetCampus.Ipc.Utils;\n\n#pragma warning disable format\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\npartial class GeneratedIpcProxy\n{\n    /// <summary>\n    /// 仅供 <see cref=\"GeneratedIpcProxy\"/> 的自动生成的派生类与基类传递参数使用，包含参数传递所需的各种个性化需求。\n    /// </summary>\n    protected readonly record struct IpcMemberInfo\n    {\n        /// <summary>\n        /// 存储 <see cref=\"Timeout\"/> 的数值部分。\n        /// </summary>\n        private readonly int _timeoutValue;\n\n        /// <summary>\n        /// 存储可空布尔值的集合。\n        /// </summary>\n        private readonly NullableBooleans _booleans;\n\n        /// <summary>\n        /// 在指定了 <see cref=\"IgnoresIpcException\"/> 或者 <see cref=\"Timeout\"/> 的情况下，如果真的发生了异常或超时，则会使用此默认值。\n        /// <list type=\"number\">\n        /// <item>不指定此值时，会使用属性或返回值类型的默认值（即 default）。</item>\n        /// <item>如果此值可使用编译时常量来表示，则直接写在这里即可。</item>\n        /// <item>如果此值无法写成编译时常量，请使用字符串形式编写（例如 \"IntPtr.Zero\" ，含英文引号），编译后会自动将其转为真实代码。</item>\n        /// <item>\n        /// 接上条，如果你确实是一个 object 返回值但默认值是一个字符串，请写成以下两种中的一种：\n        /// <list type=\"bullet\">\n        /// <item>@\"\"\"字符串值\"\"\"（含“@”及所有的英文引号）</item>\n        /// <item>\"\\\"字符串值\\\"\"（含所有的英文引号及斜杠）</item>\n        /// </list>\n        /// </item>\n        /// </list>\n        /// </summary>\n        public object? DefaultReturn { get; init; }\n\n        /// <summary>\n        /// 设定此方法执行的超时时间（毫秒）。如果自此方法执行开始直至超时时间后依然没有返回，则会引发 <see cref=\"dotnetCampus.Ipc.Exceptions.IpcInvokingTimeoutException\"/>。\n        /// </summary>\n        public int? Timeout\n        {\n            get => _booleans.GetBooleanAt(0) ? _timeoutValue : null;\n            init\n            {\n                _booleans.SetBooleanAt(0, value is not null);\n                _timeoutValue = value ?? 0;\n            }\n        }\n\n        /// <summary>\n        /// 标记一个属性是对于 IPC 代理访问来说是只读的。\n        /// 当通过 IPC 访问过一次这个属性后，此属性不再变化，后续无需再通过 IPC 读取，可直接使用本地缓存的值。\n        /// </summary>\n        public bool? IsReadonly\n        {\n            get => _booleans[0];\n            init => _booleans[0] = value;\n        }\n\n        /// <summary>\n        /// 如果指定为 true，则在 IPC 发生异常时会忽略这些异常，并返回默认值。\n        /// <para>\n        /// 如果同时设置了默认值，则异常时会使用此默认值；如果没有设置默认值，则异常时会使用类型默认值 default。\n        /// </para>\n        /// </summary>\n        /// <remarks>\n        /// 请注意：\n        /// <list type=\"bullet\">\n        /// <item>此特性仅忽略 IPC 连接异常和 IPC 超时异常（例如进程退出、连接断开等），而不会忽略普通业务异常（例如业务实现中抛出了 <see cref=\"NullReferenceException\"/> 等）。</item>\n        /// <item>另外，如果 IPC 框架内部出现了 bug 导致了异常，也不会因此而忽略。</item>\n        /// </list>\n        /// </remarks>\n        public bool? IgnoresIpcException\n        {\n            get => _booleans[1];\n            init => _booleans[1] = value;\n        }\n\n        /// <summary>\n        /// 如果一个方法返回值是 void，那么此属性决定代理调用此方法时是否需要等待对方执行完成。\n        /// 默认为 false，即不等待对方执行完成。\n        /// </summary>\n        public bool? WaitsVoid\n        {\n            get => _booleans[2];\n            init => _booleans[2] = value;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Models/MemberInvokingType.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n/// <summary>\n/// 自动生成的 IPC 调用代码中，目前支持的所有成员调用类型。\n/// </summary>\n/// <remarks>\n/// 后续支持更多调用类型时，将扩充此枚举。\n/// </remarks>\ninternal enum MemberInvokingType\n{\n    /// <summary>\n    /// 未知的调用类型。\n    /// 此值作为 IPC 传输过程中因为未传输或传输出错后的默认值。\n    /// </summary>\n    Unknown,\n\n    /// <summary>\n    /// 获取属性的值。\n    /// </summary>\n    GetProperty,\n\n    /// <summary>\n    /// 设置属性的值。\n    /// </summary>\n    SetProperty,\n\n    /// <summary>\n    /// 方法调用。\n    /// </summary>\n    Method,\n\n    /// <summary>\n    /// 异步方法调用。\n    /// </summary>\n    AsyncMethod,\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/PublicIpcJointManager.cs",
    "content": "﻿using System.Collections.Concurrent;\nusing System.Diagnostics.CodeAnalysis;\n\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Contexts;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Serialization;\nusing dotnetCampus.Ipc.Utils;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\n\n/// <summary>\n/// 辅助管理 IPC 远程代理的自动生成的对接类。\n/// </summary>\npublic class PublicIpcJointManager\n{\n    /// <summary>\n    /// 包含所有目前已公开的 IPC 实例。\n    /// </summary>\n    private readonly ConcurrentDictionary<(string typeFullName, string objectId), GeneratedIpcJoint> _joints = new();\n\n    internal PublicIpcJointManager(GeneratedProxyJointIpcContext context)\n    {\n        Context = context ?? throw new ArgumentNullException(nameof(context));\n    }\n\n    /// <summary>\n    /// 包含基于 .NET 类型的 IPC 传输上下文信息。\n    /// </summary>\n    public GeneratedProxyJointIpcContext Context { get; }\n\n    /// <summary>\n    /// 以契约 <typeparamref name=\"TContract\"/> 的方式公开对象 <paramref name=\"joint\"/>，使其可被其他进程发现并使用。\n    /// </summary>\n    /// <typeparam name=\"TContract\">契约类型。</typeparam>\n    /// <param name=\"ipcObjectId\">如果希望同一个契约类型公开多个实例，需用此 Id 加以区分。</param>\n    /// <param name=\"joint\">此契约类型的对接类。</param>\n    public void AddPublicIpcObject<TContract>(GeneratedIpcJoint<TContract> joint, string? ipcObjectId = \"\") where TContract : class\n    {\n        if (!_joints.TryAdd((typeof(TContract).FullName!, ipcObjectId ?? \"\"), joint))\n        {\n            throw new InvalidOperationException($\"不可重复公开相同契约 {typeof(TContract).FullName} 且相同 Id {ipcObjectId} 的多个 IPC 对接实例。\");\n        }\n    }\n\n    /// <summary>\n    /// 以契约 <paramref name=\"contractType\"/> 的方式设置对象 <paramref name=\"joint\"/> 可被其他进程发现并使用。\n    /// </summary>\n    /// <param name=\"contractType\">契约类型。</param>\n    /// <param name=\"ipcObjectId\">如果希望同一个契约类型公开多个实例，需用此 Id 加以区分。</param>\n    /// <param name=\"joint\">此契约类型的对接类。</param>\n    public void AddPublicIpcObject(Type contractType, GeneratedIpcJoint joint, string? ipcObjectId = \"\")\n    {\n        if (!_joints.TryAdd((contractType.FullName!, ipcObjectId ?? \"\"), joint))\n        {\n            throw new InvalidOperationException($\"不可重复公开相同契约 {contractType.FullName} 且相同 Id {ipcObjectId} 的多个 IPC 对接实例。\");\n        }\n    }\n\n    /// <summary>\n    /// 当收到 IPC 消息时调用此方法可以尝试检查此消息是否是来自一个 IPC 类型的代理。如果是，那么会查询本进程中的对接类来对接此代理。\n    /// </summary>\n    /// <param name=\"request\">IPC 消息的消息体。</param>\n    /// <param name=\"responseTask\">IPC 回应消息的消息体，可异步等待。</param>\n    /// <returns>\n    /// 当此消息来自 IPC 代理，并且本进程存在能对接此代理的类型，则返回 true；否则返回 false。\n    /// </returns>\n    public bool TryJoint(IIpcRequestContext request, out Task<IIpcResponseMessage> responseTask)\n    {\n        if (Context.ObjectSerializer.TryDeserializeFromIpcMessage<GeneratedProxyMemberInvokeModel>(\n                    request.IpcBufferMessage, (ulong)KnownMessageHeaders.RemoteObjectMessageHeader, out var requestModel)\n            && TryFindJoint(requestModel, out var joint)\n            && requestModel.MemberName is not null)\n        {\n            var returnModelTask = InvokeAndReturn(joint, requestModel, request.Peer);\n            responseTask = GeneratedIpcJointResponse.FromAsyncReturnModel(returnModelTask, Context.ObjectSerializer)\n                .As<GeneratedIpcJointResponse, IIpcResponseMessage>();\n            return true;\n        }\n\n        responseTask = Task.FromResult<IIpcResponseMessage>(GeneratedIpcJointResponse.Empty);\n        return false;\n    }\n\n    internal IEnumerable<string> EnumerateJointNames()\n    {\n        foreach (var joint in _joints)\n        {\n            var (typeName, objectId) = joint.Key;\n            if (string.IsNullOrEmpty(objectId))\n            {\n                yield return typeName;\n            }\n            else\n            {\n                yield return $\"{typeName}({objectId})\";\n            }\n        }\n    }\n\n    private bool TryFindJoint(GeneratedProxyMemberInvokeModel model, [NotNullWhen(true)] out GeneratedIpcJoint? joint)\n    {\n        var id = model.Id;\n        if (string.IsNullOrWhiteSpace(model.ContractFullTypeName)\n            || string.IsNullOrWhiteSpace(model.MemberName)\n            || model.CallType is MemberInvokingType.Unknown)\n        {\n            joint = null;\n            return false;\n        }\n\n        if (_joints.TryGetValue((model.ContractFullTypeName, id), out joint))\n        {\n            return true;\n        }\n        else\n        {\n#if DEBUG\n            throw new InvalidOperationException($\"没有发现针对 {model.ContractFullTypeName} 的对接，无法处理此消息。消息处理丢失可能导致对方端等死，必须调查。\");\n#else\n                joint = null;\n                return false;\n#endif\n        }\n    }\n\n    private async Task<GeneratedProxyMemberReturnModel?> InvokeAndReturn(\n        GeneratedIpcJoint joint,\n        GeneratedProxyMemberInvokeModel requestModel,\n        IPeerProxy peer)\n    {\n        GeneratedProxyMemberReturnModel? @return;\n        try\n        {\n            var parameterTypes = joint.GetParameterTypes(requestModel.MemberId, requestModel.MemberName, requestModel.CallType);\n            var args = ExtractArgsFromArgsModel(parameterTypes, requestModel.Args, peer);\n            var returnValue = await InvokeMember(joint, requestModel.CallType, requestModel.MemberId, requestModel.MemberName!, args).ConfigureAwait(false);\n            @return = CreateReturnModelFromReturnObject(returnValue);\n#if DEBUG\n            @return.Invoking = requestModel;\n#endif\n        }\n        catch (Exception ex)\n        {\n            @return = new GeneratedProxyMemberReturnModel(ex);\n        }\n        return @return;\n    }\n\n    /// <summary>\n    /// 预处理一组需要进行 IPC 代理访问的参数。\n    /// </summary>\n    /// <param name=\"parameterTypes\">参数类型列表。</param>\n    /// <param name=\"argModels\">参数模型列表。</param>\n    /// <param name=\"peer\">如果某个参数的模型表示需要通过代理访问一个 IPC 远端对象，则会用到这个远端。</param>\n    /// <returns>参数实例列表。</returns>\n    private IGarmObject[] ExtractArgsFromArgsModel(\n        Type[] parameterTypes,\n        GeneratedProxyObjectModel?[]? argModels,\n        IPeerProxy peer)\n    {\n        if (argModels is null || argModels.Length is 0)\n        {\n            return [];\n        }\n\n        var args = new IGarmObject[argModels.Length];\n        for (var i = 0; i < args.Length; i++)\n        {\n            var parameterType = parameterTypes[i];\n            var argModel = argModels[i];\n            if (argModel is null)\n            {\n                // 如果参数模型为 null，那么就是一个 null 参数。\n                args[i] = new Garm(null, parameterType);\n            }\n            else\n            {\n                var ipcType = parameterType;\n                if (Context.TryCreateProxyFromSerializationInfo(peer, ipcType, argModel.Id, out var proxy))\n                {\n                    // 如果参数模型表示需要通过代理访问一个 IPC 远端对象，则创建一个代理对象。\n                    args[i] = new Garm(proxy, ipcType);\n                }\n                else\n                {\n                    // 如果参数模型表示是一个普通的值，则直接使用这个值。\n                    args[i] = new Garm(argModel.Value, parameterType);\n                }\n            }\n        }\n        return args;\n    }\n\n    /// <summary>\n    /// 处理一个 IPC 对接的返回值。\n    /// 额外的，如果其返回的是一个 IPC 公开的类型，则将其加入到新的 IPC 对接管理中。\n    /// </summary>\n    /// <param name=\"returnModel\">真实的返回值或返回实例。</param>\n    /// <returns>可被序列化进行 IPC 传输的返回值模型。</returns>\n    private GeneratedProxyMemberReturnModel CreateReturnModelFromReturnObject(IGarmObject returnModel)\n    {\n        if (Context.TryCreateSerializationInfoFromIpcRealInstance(returnModel, out var objectId, out var ipcTypeFullName))\n        {\n            // 如果是一个 IPC 对象。\n            return new GeneratedProxyMemberReturnModel\n            {\n                Return = new GeneratedProxyObjectModel\n                {\n                    Id = objectId,\n                    IpcPublicTypeFullName = ipcTypeFullName,\n                },\n            };\n        }\n        else\n        {\n            // 如果是一个普通对象。\n            return new GeneratedProxyMemberReturnModel(IpcJsonElement.Serialize(returnModel.Value, Context.ObjectSerializer));\n        }\n    }\n\n    private static async Task<IGarmObject> InvokeMember(GeneratedIpcJoint joint, MemberInvokingType callType, ulong memberId, string memberName, IGarmObject[] args)\n    {\n        return callType switch\n        {\n            MemberInvokingType.GetProperty => joint.GetProperty(memberId, memberName),\n            MemberInvokingType.SetProperty => joint.SetProperty(memberId, memberName, args.FirstOrDefault() ?? GarmObjectExtensions.Default),\n            // ReSharper disable once MethodHasAsyncOverload\n            MemberInvokingType.Method => joint.CallMethod(memberId, memberName, args),\n            MemberInvokingType.AsyncMethod => await joint.CallMethodAsync(memberId, memberName, args).ConfigureAwait(false),\n            _ => GarmObjectExtensions.Default,\n        };\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/CompilerServices/GeneratedProxies/Utils/IpcProxyInvokingHelper.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Serialization;\n\nnamespace dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Utils;\n\ninternal class IpcProxyInvokingHelper\n{\n    private GeneratedProxyJointIpcContext? _context;\n    private string? _typeName;\n\n    /// <summary>\n    /// 提供基于 .NET 类型的 IPC 传输上下文信息。\n    /// </summary>\n    internal GeneratedProxyJointIpcContext Context\n    {\n        get => _context ?? throw new IpcLocalException($\"基于 .NET 类型的 IPC 传输机制应使用 {typeof(GeneratedIpcFactory)} 工厂类型来构造。\");\n        set => _context = value ?? throw new ArgumentNullException(nameof(value));\n    }\n\n    /// <summary>\n    /// 获取或设置目标 IPC 节点。\n    /// 如果设定为 null，则所有请求将返回默认值。（未来可能运行在抛出异常和返回默认值之间进行选择。）\n    /// </summary>\n    internal IPeerProxy? PeerProxy { get; set; }\n\n    /// <summary>\n    /// 如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。\n    /// </summary>\n    internal string TypeName\n    {\n        get => _typeName ?? throw new IpcLocalException($\"基于 .NET 类型的 IPC 传输机制应使用 {typeof(GeneratedIpcFactory)} 工厂类型来构造。\");\n        set => _typeName = value;\n    }\n\n    /// <summary>\n    /// 如果要调用的远端对象有多个实例，请设置此 Id 值以找到期望的实例。\n    /// </summary>\n    internal string? ObjectId { get; set; }\n\n    internal async Task<T?> IpcInvokeAsync<T>(MemberInvokingType callType, ulong memberId, string memberName, IGarmObject[]? args)\n    {\n        if (PeerProxy is null)\n        {\n            return default;\n        }\n\n        var returnModel = await IpcInvokeAsync(new GeneratedProxyMemberInvokeModel\n        {\n            Id = ObjectId,\n            ContractFullTypeName = TypeName,\n            CallType = callType,\n            MemberId = memberId,\n            MemberName = memberName,\n            Args = args?.Select(SerializeArg).ToArray(),\n        }).ConfigureAwait(false);\n\n        if (returnModel is null)\n        {\n            // 如果远端返回 null，则本地代理返回 null。\n            return default;\n        }\n\n        if (returnModel.Exception is { } exceptionModel)\n        {\n            // 如果远端抛出了异常，则本地代理抛出相同的异常。\n            exceptionModel.Throw();\n        }\n\n        if (returnModel.Return is { } model)\n        {\n            var ipcType = string.IsNullOrWhiteSpace(model.IpcPublicTypeFullName)\n                ? null\n                : typeof(T);\n            if (ipcType is not null\n                && Context.TryCreateProxyFromSerializationInfo(PeerProxy,\n                    ipcType, model.Id, out var proxyInstance))\n            {\n                // 如果远端返回 IPC 公开的对象，则本地获取此对象的代理并返回。\n                return (T?) proxyInstance;\n            }\n        }\n\n        // 其他情况直接使用反序列化的值返回。\n        return Cast<T>(returnModel.Return?.Value);\n    }\n\n    private async Task<GeneratedProxyMemberReturnModel?> IpcInvokeAsync(GeneratedProxyMemberInvokeModel model)\n    {\n        if (PeerProxy is null)\n        {\n            return null;\n        }\n\n        var header = (ulong)KnownMessageHeaders.RemoteObjectMessageHeader;\n        var requestMessage = Context.ObjectSerializer.SerializeToIpcMessage(header, model, model.ToString());\n        //requestMessage = new IpcMessage(requestMessage.Tag, requestMessage.Body, CoreMessageType.JsonObject);\n        var responseMessage = await PeerProxy.GetResponseAsync(requestMessage).ConfigureAwait(false);\n        if (Context.ObjectSerializer.TryDeserializeFromIpcMessage<GeneratedProxyMemberReturnModel>(responseMessage, header, out var returnModel))\n        {\n            return returnModel;\n        }\n        else\n        {\n            throw new NotSupportedException($\"请谨慎对待此异常！无法处理 IPC 代理调用的返回值。RequestMessage 为：{requestMessage}\");\n        }\n    }\n\n    private T? Cast<T>(IpcJsonElement? arg) => arg is { } jsonElement\n        ? IpcJsonElement.Deserialize<T>(jsonElement, Context.ObjectSerializer)\n        : default!;\n\n    private GeneratedProxyObjectModel? SerializeArg(IGarmObject argModel)\n    {\n        if (PeerProxy is null)\n        {\n            return null;\n        }\n\n        if (Context.TryCreateSerializationInfoFromIpcRealInstance(argModel, out var objectId, out var ipcTypeFullName))\n        {\n            // 如果此参数是一个 IPC 对象。\n            return new GeneratedProxyObjectModel\n            {\n                Id = objectId,\n                IpcPublicTypeFullName = ipcTypeFullName,\n            };\n        }\n        else\n        {\n            // 如果此参数只是一个普通对象。\n            return new GeneratedProxyObjectModel\n            {\n                Value = IpcJsonElement.Serialize(argModel.Value, Context.ObjectSerializer),\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/AckArgs.cs",
    "content": "﻿using System;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 回复消息的事件参数\n    /// </summary>\n    public class AckArgs : EventArgs\n    {\n        /// <summary>\n        /// 创建回复消息的事件参数\n        /// </summary>\n        /// <param name=\"peerName\"></param>\n        /// <param name=\"ack\"></param>\n        public AckArgs(string peerName, in Ack ack)\n        {\n            Ack = ack;\n            PeerName = peerName;\n        }\n\n        /// <summary>\n        /// 消息编号\n        /// </summary>\n        public Ack Ack { get; }\n\n        /// <summary>\n        /// 发送方的名字\n        /// </summary>\n        public string PeerName { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/AckTask.cs",
    "content": "﻿using System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 回复 Ack 的任务，用于在 <see cref=\"AckManager\"/> 收到回复的时候设置任务完成\n    /// </summary>\n    // todo 此类型可以准备删除\n    class AckTask\n    {\n        /// <summary>\n        /// 创建回复 Ack 的任务\n        /// </summary>\n        /// <param name=\"peerName\"></param>\n        /// <param name=\"ack\"></param>\n        /// <param name=\"task\"></param>\n        /// <param name=\"tag\">用于调试的信息</param>\n        public AckTask(string peerName, in Ack ack, TaskCompletionSource<bool> task, string tag)\n        {\n            PeerName = peerName;\n            Ack = ack;\n            Task = task;\n            Summary = tag;\n        }\n\n        public TaskCompletionSource<bool> Task { get; }\n\n        public string Summary { get; }\n\n        public Ack Ack { get; }\n\n        public string PeerName { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/ConnectToExistingPeerResult.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Utils;\n\nnamespace dotnetCampus.Ipc.Context;\n\n/// <summary>\n/// 连接已经存在的 Peer 的结果\n/// </summary>\npublic readonly struct ConnectToExistingPeerResult\n{\n    internal ConnectToExistingPeerResult(PeerProxy? peerProxy, Task peerConnectFinishedTask)\n    {\n        PeerProxy = peerProxy;\n        PeerConnectFinishedTask = peerConnectFinishedTask;\n    }\n\n    /// <summary>\n    /// 连接到的 Peer 的代理\n    /// </summary>\n    public PeerProxy? PeerProxy { get; }\n\n    /// <summary>\n    /// 用于等待 Peer 完成连接完成\n    /// </summary>\n    public Task PeerConnectFinishedTask { get; }\n\n    /// <summary>\n    /// 是否连接成功\n    /// </summary>\n    [MemberNotNullWhen(true, nameof(PeerProxy))]\n    public bool IsSuccess => PeerProxy != null;\n\n    internal static ConnectToExistingPeerResult Fail() => new ConnectToExistingPeerResult(null, TaskUtils.CompletedTask);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/DelegateIpcRequestHandler.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 基于委托的 IPC 请求处理\n    /// </summary>\n    public class DelegateIpcRequestHandler : IIpcRequestHandler\n    {\n        /// <summary>\n        /// 创建基于委托的 IPC 请求处理\n        /// </summary>\n        /// <param name=\"handler\"></param>\n        public DelegateIpcRequestHandler(Func<IIpcRequestContext, Task<IIpcResponseMessage>> handler)\n        {\n            _handler = handler;\n        }\n\n        /// <summary>\n        /// 创建基于委托的 IPC 请求处理\n        /// </summary>\n        public DelegateIpcRequestHandler(Func<IIpcRequestContext, IIpcResponseMessage> handler)\n        {\n            _handler = c => Task.FromResult(handler(c));\n        }\n\n        Task<IIpcResponseMessage> IIpcRequestHandler.HandleRequest(IIpcRequestContext requestContext)\n        {\n            return _handler(requestContext);\n        }\n\n        private readonly Func<IIpcRequestContext, Task<IIpcResponseMessage>> _handler;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IIpcRequestContext.cs",
    "content": "﻿using dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 客户端请求的上下文\n    /// </summary>\n    public interface IIpcRequestContext\n    {\n        /// <summary>\n        /// 是否已处理\n        /// </summary>\n        bool Handled { get; set; }\n\n        /// <summary>\n        /// 收到客户端发生过来的消息\n        /// </summary>\n        IpcMessage IpcBufferMessage { get; }\n\n        /// <summary>\n        /// 发送请求的对方\n        /// </summary>\n        IPeerProxy Peer { get; }\n    }\n\n    internal interface ICoreIpcRequestContext\n    {\n        public CoreMessageType CoreMessageType { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IPeerConnectionBrokenArgs.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 对方连接断开事件参数\n    /// </summary>\n    public interface IPeerConnectionBrokenArgs\n    {\n    }\n\n    /// <summary>\n    /// 断开的原因\n    /// </summary>\n    public enum BrokenReason\n    {\n        /// <summary>\n        /// 未知原因，此时将会触发重连机制\n        /// </summary>\n        Unknown,\n\n        /// <summary>\n        /// 业务端显式退出，正常退出，不会触发重连机制\n        /// </summary>\n        BusinessExit,\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IPeerMessageArgs.cs",
    "content": "﻿using System.IO;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 收到的对方的信息事件参数\n    /// </summary>\n    public interface IPeerMessageArgs\n    {\n        /// <summary>\n        /// 来自其他端的消息。\n        /// </summary>\n        IpcMessage Message { get; }\n\n        /// <summary>\n        /// 对方的名字，此名字是对方的服务器名字，可以用来连接\n        /// </summary>\n        string PeerName { get; }\n\n        /// <summary>\n        /// 尝试根据 <paramref name=\"requiredHeader\"/> 获取有效负载内容。如果当前的 <see cref=\"Message\"/> 不包含 <paramref name=\"requiredHeader\"/> 头信息，将返回 false 值。如包含，则将 <see cref=\"Message\"/> 去掉 <paramref name=\"requiredHeader\"/> 长度之后作为 <paramref name=\"subMessage\"/> 返回，同时返回 true 值\n        /// </summary>\n        /// <param name=\"requiredHeader\"></param>\n        /// <param name=\"subMessage\"></param>\n        /// <returns></returns>\n        bool TryGetPayload(byte[] requiredHeader, out IpcMessage subMessage);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IPeerReconnectedArgs.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 对方断开重连事件参数\n    /// </summary>\n    public interface IPeerReconnectedArgs\n    {\n\n    }\n\n    class PeerReconnectedArgs : EventArgs, IPeerReconnectedArgs\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcBufferMessageContext.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 包含多条信息的上下文\n    /// </summary>\n    /// 不开放，因为我不认为上层能用对\n    readonly struct IpcBufferMessageContext\n    {\n        /// <summary>\n        /// 创建包含多条信息的上下文\n        /// </summary>\n        /// <param name=\"summary\">表示写入的是什么内容，用于调试</param>\n        /// <param name=\"ipcMessageCommandType\">命令类型，用于分开框架内的消息和业务的</param>\n        /// <param name=\"ipcBufferMessageList\"></param>\n        public IpcBufferMessageContext(string summary, IpcMessageCommandType ipcMessageCommandType, params IpcMessageBody[] ipcBufferMessageList)\n        {\n            Tag = summary;\n            IpcMessageCommandType = ipcMessageCommandType;\n            IpcBufferMessageList = ipcBufferMessageList;\n        }\n\n        /// <summary>\n        /// 命令类型，用于分开框架内的消息和业务的\n        /// </summary>\n        public IpcMessageCommandType IpcMessageCommandType { get; }\n\n        public IpcMessageBody[] IpcBufferMessageList { get; }\n\n        /// <summary>\n        /// 表示内容是什么用于调试\n        /// </summary>\n        public string Tag { get; }\n\n        public int Length\n        {\n            get\n            {\n                var length = 0;\n                foreach (var ipcBufferMessage in IpcBufferMessageList)\n                {\n                    length += ipcBufferMessage.Length;\n                }\n\n                return length;\n            }\n        }\n\n        /// <summary>\n        /// 和其他的合并然后创建新的\n        /// </summary>\n        /// <param name=\"ipcMessageCommandType\"></param>\n        /// <param name=\"mergeBefore\">将加入的内容合并到新的消息前面，为 true 合并到前面，否则合并到后面</param>\n        /// <param name=\"ipcBufferMessageList\"></param>\n        /// <returns></returns>\n        public IpcBufferMessageContext BuildWithCombine(IpcMessageCommandType ipcMessageCommandType, bool mergeBefore, params IpcMessageBody[] ipcBufferMessageList)\n            => BuildWithCombine(Tag, ipcMessageCommandType, mergeBefore, ipcBufferMessageList);\n\n        /// <summary>\n        /// 和其他的合并然后创建新的\n        /// </summary>\n        /// <param name=\"summary\">表示写入的是什么内容，用于调试</param>\n        /// <param name=\"ipcMessageCommandType\"></param>\n        /// <param name=\"mergeBefore\">将加入的内容合并到新的消息前面，为 true 合并到前面，否则合并到后面</param>\n        /// <param name=\"ipcBufferMessageList\"></param>\n        /// <returns></returns>\n        public IpcBufferMessageContext BuildWithCombine(string summary, IpcMessageCommandType ipcMessageCommandType, bool mergeBefore, params IpcMessageBody[] ipcBufferMessageList)\n        {\n            var newIpcBufferMessageList = new List<IpcMessageBody>(ipcBufferMessageList.Length + IpcBufferMessageList.Length);\n            if (mergeBefore)\n            {\n                newIpcBufferMessageList.AddRange(ipcBufferMessageList);\n                newIpcBufferMessageList.AddRange(IpcBufferMessageList);\n            }\n            else\n            {\n                newIpcBufferMessageList.AddRange(IpcBufferMessageList);\n                newIpcBufferMessageList.AddRange(ipcBufferMessageList);\n            }\n\n            return new IpcBufferMessageContext(summary, ipcMessageCommandType, newIpcBufferMessageList.ToArray());\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcClientRequestArgs.cs",
    "content": "﻿using System;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 来自客户端的请求事件参数\n    /// </summary>\n    public class IpcClientRequestArgs : EventArgs\n    {\n        /// <summary>\n        /// 创建来自客户端的请求事件参数\n        /// </summary>\n        /// <param name=\"messageId\"></param>\n        /// <param name=\"ipcBufferMessage\"></param>\n        /// <param name=\"messageCommandType\">消息命令类型</param>\n        internal IpcClientRequestArgs(in IpcClientRequestMessageId messageId, in IpcMessageBody ipcBufferMessage, IpcMessageCommandType messageCommandType)\n        {\n            MessageId = messageId;\n            IpcMessageBody = ipcBufferMessage;\n            MessageCommandType = messageCommandType;\n        }\n\n        /// <summary>\n        /// 消息号\n        /// </summary>\n        public IpcClientRequestMessageId MessageId { get; }\n\n        /// <summary>\n        /// 收到客户端发生过来的消息\n        /// </summary>\n        public IpcMessageBody IpcMessageBody { get; }\n\n        internal IpcMessageCommandType MessageCommandType { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcConfiguration.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Pipes.PipeConnectors;\nusing dotnetCampus.Ipc.Serialization;\nusing dotnetCampus.Ipc.Threading;\nusing dotnetCampus.Ipc.Utils.Buffers;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 进程间通讯的配置\n    /// </summary>\n    public class IpcConfiguration\n    {\n        /// <summary>\n        /// 自动重连 Peer 是否开启，如开启，在断开后将会自动尝试去重新连接。设置为 true 时，推荐同时设置 <see cref=\"IpcClientPipeConnector\"/> 属性，以防无限重试。\n        /// </summary>\n        public bool AutoReconnectPeers { get; set; } = false;\n\n        private readonly List<IIpcRequestHandler> _ipcRequestHandlers = new();\n\n        /// <summary>\n        /// 消息内容允许最大的长度。超过这个长度，咋不上天\n        /// <para>\n        /// 如果真有那么大的内容准备传的，自己开共享内存或写文件等方式传输，然后通过 IPC 告知对方如何获取即可\n        /// </para>\n        /// </summary>\n        public const int MaxMessageLength = ushort.MaxValue * byte.MaxValue;\n\n        /// <summary>\n        /// 用于内部使用的数组分配池\n        /// </summary>\n        public ISharedArrayPool SharedArrayPool { get; set; } = new SharedArrayPool();\n\n        /// <summary>\n        /// 决定如何调度 IPC 通知到业务的代码。\n        /// </summary>\n        public IpcTaskScheduling IpcTaskScheduling { get; set; }\n\n        /// <summary>\n        /// 添加自定义的 IPC 线程池。此属性一旦设置，将会让 <see cref=\"IpcTaskScheduling\"/> 被无效掉\n        /// </summary>\n        public CustomIpcThreadPoolBase? CustomIpcThreadPool { set; get; }\n\n        /// <summary>\n        /// 为 IPC 记录日志。\n        /// </summary>\n        /// <remarks>根据当前的 Ipc 名获取到日志对象，一般写法是<code>IpcLoggerProvider = name => new IpcLogger(name)</code></remarks>\n        public Func<string, IpcLogger>? IpcLoggerProvider { get; set; }\n\n        /// <summary>\n        /// 处理通讯相关业务的定义\n        /// </summary>\n        public IIpcRequestHandler DefaultIpcRequestHandler { set; get; } = new EmptyIpcRequestHandler();\n\n        /// <summary>\n        /// 每一条消息的头，用于处理消息的黏包和通讯损坏问题\n        /// </summary>\n        /// 在选用 Pipe 通讯，基本不存在通讯损坏等问题，也就是这个 Header 其实用途不大\n        /// 这个 Header 的内容就是 dotnet campus 的 Ascii 数组\n        /// dotnet campus 0x64, 0x6F, 0x74, 0x6E, 0x65, 0x74, 0x20, 0x63, 0x61, 0x6D, 0x70, 0x75, 0x73\n        /// 大概的消息通讯方式如下，详细请看 <see cref=\"IpcMessageConverter\"/> 的代码\n        /*\n         * Message:\n         * Header\n         * Length\n         * Content\n         */\n        public byte[] MessageHeader { set; get; } =\n            {0x64, 0x6F, 0x74, 0x6E, 0x65, 0x74, 0x20, 0x63, 0x61, 0x6D, 0x70, 0x75, 0x73};\n\n        /// <summary>\n        /// 设置或获取客户端的管道连接方法\n        /// </summary>\n        public IIpcClientPipeConnector? IpcClientPipeConnector { set; get; }\n\n        /// <summary>\n        /// 提供给框架调用，用于注入框架特殊处理的请求处理器。\n        /// </summary>\n        /// <param name=\"handlers\">框架特殊处理的请求处理器。</param>\n        internal void AddFrameworkRequestHandlers(params IIpcRequestHandler[] handlers)\n        {\n            _ipcRequestHandlers.AddRange(handlers);\n        }\n\n        /// <inheritdoc cref=\"AddFrameworkRequestHandlers\"/>\n        internal void AddFrameworkRequestHandler(IIpcRequestHandler handler)\n        {\n            _ipcRequestHandlers.Add(handler);\n        }\n\n        /// <summary>\n        /// 获取框架和业务的请求处理器。\n        /// </summary>\n        /// <returns>按顺序返回框架注入的请求处理器、业务默认指定的请求处理器。</returns>\n        internal IEnumerable<IIpcRequestHandler> GetIpcRequestHandlers()\n        {\n            foreach (var handler in _ipcRequestHandlers)\n            {\n                yield return handler;\n            }\n            if (DefaultIpcRequestHandler is { } @default)\n            {\n                yield return @default;\n            }\n        }\n\n        /// <summary>\n        /// 用在 IPC 里面的对象序列化器\n        /// </summary>\n        public IIpcObjectSerializer IpcObjectSerializer\n        {\n#if UseNewtonsoftJson\n            get => _ipcObjectSerializer ??= DefaultNewtonsoftJsonSerializer;\n#elif NET6_0_OR_GREATER\n            get => _ipcObjectSerializer ?? DefaultSystemTextJsonIpcObjectSerializer;\n#endif\n            set => _ipcObjectSerializer = value;\n        }\n\n        private IIpcObjectSerializer? _ipcObjectSerializer;\n\n#if UseNewtonsoftJson\n        /// <summary>\n        /// 默认的 Newtonsoft.Json 序列化器\n        /// </summary>\n        public static NewtonsoftJsonIpcObjectSerializer DefaultNewtonsoftJsonSerializer\n        // 不加上锁了，这里不管线程安全，最多就是多创建几个对象而已，不会影响业务逻辑\n            => _defaultNewtonsoftJsonSerializer ??= new NewtonsoftJsonIpcObjectSerializer();\n        private static NewtonsoftJsonIpcObjectSerializer? _defaultNewtonsoftJsonSerializer;\n#elif NET6_0_OR_GREATER\n        /// <summary>\n        /// 默认的 System.Text.Json 序列化器\n        /// </summary>\n        public static SystemTextJsonIpcObjectSerializer DefaultSystemTextJsonIpcObjectSerializer\n        // 不加上锁了，这里不管线程安全，最多就是多创建几个对象而已，不会影响业务逻辑\n            => _defaultSystemTextJsonIpcObjectSerializer ??= new SystemTextJsonIpcObjectSerializer();\n        private static SystemTextJsonIpcObjectSerializer? _defaultSystemTextJsonIpcObjectSerializer;\n#endif\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcConfigurationExtensions.cs",
    "content": "﻿using dotnetCampus.Ipc.Serialization;\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#endif\n\nnamespace dotnetCampus.Ipc.Context;\n\n/// <summary>\n/// IPC 配置扩展\n/// </summary>\npublic static class IpcConfigurationExtensions\n{\n#if UseNewtonsoftJson\n    /// <summary>\n    /// 使用 Newtonsoft.Json 作为 IPC 对象序列化器\n    /// </summary>\n    /// <param name=\"configuration\"></param>\n    /// <param name=\"jsonSerializer\"></param>\n    /// <returns></returns>\n    public static IpcConfiguration UseNewtonsoftJsonIpcObjectSerializer(this IpcConfiguration configuration, JsonSerializer? jsonSerializer)\n    {\n        if (jsonSerializer is null)\n        {\n            configuration.IpcObjectSerializer = IpcConfiguration.DefaultNewtonsoftJsonSerializer;\n        }\n        else\n        {\n            configuration.IpcObjectSerializer = new NewtonsoftJsonIpcObjectSerializer(jsonSerializer);\n        }\n\n        return configuration;\n    }\n#endif\n\n#if NET6_0_OR_GREATER\n    /// <summary>\n    /// 使用 System.Text.Json 作为 IPC 对象序列化器\n    /// </summary>\n    /// <param name=\"configuration\"></param>\n    /// <param name=\"context\"></param>\n    /// <returns></returns>\n    public static IpcConfiguration UseSystemTextJsonIpcObjectSerializer(this IpcConfiguration configuration,\n        System.Text.Json.Serialization.JsonSerializerContext context)\n    {\n        configuration.IpcObjectSerializer = new SystemTextJsonIpcObjectSerializer(context);\n        return configuration;\n    }\n#endif\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcContext.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Pipes.PipeConnectors;\nusing dotnetCampus.Ipc.Threading;\nusing dotnetCampus.Ipc.Threading.Tasks;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 用于作为 Ipc 库的上下文，包括各个过程需要使用的工具和配置等\n    /// </summary>\n    public class IpcContext\n    {\n        /// <summary>\n        /// 默认的管道名\n        /// </summary>\n        public const string DefaultPipeName = \"dotnet campus\";\n\n        private static readonly IpcTask DefaultIpcTask = new(new IpcThreadPool());\n\n        /// <summary>\n        /// 创建上下文\n        /// </summary>\n        /// <param name=\"ipcProvider\"></param>\n        /// <param name=\"pipeName\">管道名，也将被做来作为服务器名或当前服务名</param>\n        /// <param name=\"ipcConfiguration\"></param>\n        public IpcContext(IpcProvider ipcProvider, string pipeName, IpcConfiguration? ipcConfiguration = null)\n        {\n            IpcProvider = ipcProvider;\n            PipeName = pipeName;\n\n            AckManager = new AckManager();\n            IpcRequestHandlerProvider = new IpcRequestHandlerProvider(this);\n\n            IpcConfiguration = ipcConfiguration ?? new IpcConfiguration();\n            GeneratedProxyJointIpcContext = new GeneratedProxyJointIpcContext(this);\n\n            IpcClientPipeConnector = IpcConfiguration.IpcClientPipeConnector;\n\n            if (IpcConfiguration.CustomIpcThreadPool is { } customIpcThreadPool)\n            {\n                TaskPool = new IpcTask(customIpcThreadPool);\n            }\n            else\n            {\n                TaskPool = IpcConfiguration.IpcTaskScheduling is IpcTaskScheduling.GlobalConcurrent\n                    // 支持并发的 IPC 将共用同一个线程池。\n                    ? DefaultIpcTask\n                    // 要求在同一线程调度的 IPC 将近似独享一个“线程”。\n                    : new IpcTask(new IpcSingleThreadPool());\n            }\n\n            Logger = IpcConfiguration.IpcLoggerProvider?.Invoke(pipeName) ?? new IpcLogger(pipeName);\n        }\n\n        internal AckManager AckManager { get; }\n\n        internal GeneratedProxyJointIpcContext GeneratedProxyJointIpcContext { get; }\n\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return $\"[{PipeName}]\";\n        }\n\n        internal IpcConfiguration IpcConfiguration { get; }\n\n        /// <summary>\n        /// 获取客户端的管道连接方法\n        /// </summary>\n        internal IIpcClientPipeConnector? IpcClientPipeConnector { get; }\n\n        internal IpcProvider IpcProvider { get; }\n\n        internal IpcRequestHandlerProvider IpcRequestHandlerProvider { get; }\n\n        internal IpcMessageResponseManager IpcMessageResponseManager { get; } = new IpcMessageResponseManager();\n\n        /// <summary>\n        /// 管道名，本地服务器名\n        /// </summary>\n        public string PipeName { get; }\n\n        internal PeerRegisterProvider PeerRegisterProvider { get; } = new PeerRegisterProvider();\n\n        internal ILogger Logger { get; }\n\n        /// <summary>\n        /// 供 IPC 使用的线程池。\n        /// 特点为按顺序触发执行，但如果前一个任务执行超时，下一个任务将转到其他线程中执行。\n        /// 适用于：\n        ///  1. 期望执行顺序与触发顺序一致；\n        ///  2. 大多数为小型任务，但可能会出现一些难以预料到的长时间的任务；\n        ///  3. 不阻塞调用线程。\n        /// </summary>\n        internal IpcTask TaskPool { get; }\n\n        // 当前干掉回应的逻辑\n        ///// <summary>\n        ///// 规定回应 ack 的值使用的 ack 是最大值\n        ///// </summary>\n        //internal Ack AckUsedForReply { get; } = new Ack(ulong.MaxValue);\n\n        internal bool IsDisposing { set; get; }\n        internal bool IsDisposed { set; get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcInternalPeerConnectedArgs.cs",
    "content": "﻿using System;\nusing System.IO;\n\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 对方连接的事件参数\n    /// 用于在框架内部使用\n    /// </summary>\n    internal class IpcInternalPeerConnectedArgs : EventArgs\n    {\n        /// <summary>\n        /// 创建对方连接的事件参数\n        /// </summary>\n        /// <param name=\"peerName\"></param>\n        /// <param name=\"namedPipeServerStream\"></param>\n        /// <param name=\"ack\"></param>\n        /// <param name=\"serverStreamMessageReader\"></param>\n        internal IpcInternalPeerConnectedArgs(string peerName, Stream namedPipeServerStream, in Ack ack,\n            ServerStreamMessageReader serverStreamMessageReader)\n        {\n            PeerName = peerName;\n            NamedPipeServerStream = namedPipeServerStream;\n            Ack = ack;\n            ServerStreamMessageReader = serverStreamMessageReader;\n        }\n\n        /// <summary>\n        /// 对方的服务器连接名\n        /// </summary>\n        public string PeerName { get; }\n\n        /// <summary>\n        /// 用于接受对方的通讯服务，只读\n        /// </summary>\n        public Stream NamedPipeServerStream { get; }\n\n        /// <summary>\n        /// 消息编号\n        /// </summary>\n        public Ack Ack { get; }\n\n        internal ServerStreamMessageReader ServerStreamMessageReader { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcPipeServerMessageProviderPeerConnectionBrokenArgs.cs",
    "content": "﻿using System;\nusing dotnetCampus.Ipc.Internals;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    internal class IpcPipeServerMessageProviderPeerConnectionBrokenArgs : EventArgs\n    {\n        public IpcPipeServerMessageProviderPeerConnectionBrokenArgs(IpcPipeServerMessageProvider ipcPipeServerMessageProvider, PeerConnectionBrokenArgs peerConnectionBrokenArgs)\n        {\n            IpcPipeServerMessageProvider = ipcPipeServerMessageProvider;\n            PeerConnectionBrokenArgs = peerConnectionBrokenArgs;\n        }\n\n        public PeerConnectionBrokenArgs PeerConnectionBrokenArgs { get; }\n        public IpcPipeServerMessageProvider IpcPipeServerMessageProvider { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcRequest.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.ComponentModel;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 发送给服务器的请求数据\n    /// </summary>\n    /// Copy From: https://github.com/jacqueskang/IpcServiceFramework.git\n    [Obsolete(\"此类型不再使用，等待下次大版本更新一起删掉\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    // todo 等待下次大版本更新一起删掉\n    public class IpcRequest\n    {\n        public IpcSerializableType ObjectType { set; get; }\n\n        /// <summary>\n        /// 用来标识服务器端的对象\n        /// </summary>\n        public ulong ObjectId { set; get; }\n\n        public string MethodName { get; set; }\n\n        public List<IpcRequestParameter> ParameterList { set; get; }\n\n        public List<IpcSerializableType> GenericArgumentList { set; get; }\n\n        public IpcSerializableType ReturnType { set; get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcRequestParameter.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    public class IpcRequestParameter\n    {\n        public IpcSerializableType ParameterType { set; get; }\n\n        public object Value { set; get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcRequestParameterType.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    public class IpcRequestParameterType : IpcSerializableType\n    {\n        public IpcRequestParameterType()\n        {\n        }\n\n        public IpcRequestParameterType(Type type) : base(type)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcResponse.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Runtime.Serialization;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 从服务器返回的信息\n    /// </summary>\n    /// Copy From: https://github.com/jacqueskang/IpcServiceFramework.git\n    [Obsolete(\"此类型不再使用，等待下次大版本更新一起删掉\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    // todo 等待下次大版本更新一起删掉\n    public class IpcResponse\n    {\n        public static IpcResponse Success(object data)\n            => new IpcResponse(IpcStatus.Ok, data, null, null);\n\n        public static IpcResponse BadRequest()\n            => new IpcResponse(IpcStatus.BadRequest, null, null, null);\n\n        public static IpcResponse BadRequest(string errorDetails)\n            => new IpcResponse(IpcStatus.BadRequest, null, errorDetails, null);\n\n        public static IpcResponse BadRequest(string errorDetails, Exception innerException)\n            => new IpcResponse(IpcStatus.BadRequest, null, errorDetails, innerException);\n\n        public static IpcResponse InternalServerError()\n            => new IpcResponse(IpcStatus.InternalServerError, null, null, null);\n\n        public static IpcResponse InternalServerError(string errorDetails)\n            => new IpcResponse(IpcStatus.InternalServerError, null, errorDetails, null);\n\n        public static IpcResponse InternalServerError(string errorDetails, Exception innerException)\n            => new IpcResponse(IpcStatus.InternalServerError, null, errorDetails, innerException);\n\n        public IpcResponse(\n            IpcStatus status,\n            object data,\n            string errorMessage,\n            Exception innerException)\n        {\n            Status = status;\n            Data = data;\n            ErrorMessage = errorMessage;\n            InnerException = innerException;\n        }\n\n        [DataMember]\n        public IpcStatus Status { get; }\n\n        [DataMember]\n        public object Data { get; }\n\n        [DataMember]\n        public string ErrorMessage { get; set; }\n\n        [DataMember]\n        public Exception InnerException { get; }\n\n        public bool Succeed() => Status == IpcStatus.Ok;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcSerializableType.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 可以被进行序列化的 <see cref=\"Type\"/> 类\n    /// </summary>\n    public class IpcSerializableType\n    {\n        public string? TypeFullName { get; set; }\n\n        public IpcSerializableType()\n        {\n        }\n\n        public IpcSerializableType(Type type)\n        {\n            TypeFullName = type.FullName;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/IpcStatus.cs",
    "content": "﻿using System.ComponentModel;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 从服务器返回的值\n    /// </summary>\n    /// Copy From: https://github.com/jacqueskang/IpcServiceFramework.git\n    [Obsolete(\"此类型不再使用，等待下次大版本更新一起删掉\")]\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    // todo 等待下次大版本更新一起删掉\n    public enum IpcStatus : int\n    {\n        Unknown = 0,\n        Ok = 200,\n        BadRequest = 400,\n        InternalServerError = 500,\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/KnownMessageHeaders.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text;\n\nnamespace dotnetCampus.Ipc.Context;\n\n/// <summary>\n/// 已知的消息头\n/// </summary>\n/// 现在已有三套通讯方法：\n/// - RemoteObject\n/// - MVC\n/// - Raw\n/// 其中 Raw 不加头，完全都裸通讯方式\npublic enum KnownMessageHeaders : ulong\n{\n    /// <summary>\n    /// 发送的消息是 RemoteObject 通讯的消息\n    /// </summary>\n    RemoteObjectMessageHeader\n        // 消息头是 R(e)m(ote)O(b)j(ect) 的 RmOj 几个字符组成的 long 头\n        = 0x526D4F6A,\n\n    /// <summary>\n    /// 发送的是 Json 的直接路由消息\n    /// </summary>\n    JsonIpcDirectRoutedMessageHeader\n        // JsonDrRt\n        = 0x745272446E6F734A,\n\n    /// <summary>\n    /// 发送的是裸 byte 的直接路由消息\n    /// </summary>\n    RawByteIpcDirectRoutedMessageHeader\n        // RwBtDrRt\n        = 0x7452724474427752,\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/LoggingContext/IpcContextLoggerExtension.cs",
    "content": "﻿using dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.IO;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Context.LoggingContext;\n\ninternal static class IpcContextLoggerExtension\n{\n    /// <summary>\n    /// 记录接收到业务消息\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"stream\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    public static void LogReceiveMessage(this IpcContext context, ByteListMessageStream stream,\n        string remotePeerName)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var ipcMessageBody = new IpcMessageBody(stream.GetBuffer(), (int) stream.Position,\n            (int) (stream.Length - stream.Position));\n\n        context.Logger.Log(logLevel, LoggerEventIds.ReceiveMessageEventId,\n            new ReceiveMessageBodyLogState(ipcMessageBody, context.PipeName, remotePeerName, isBusinessMessage: true),\n            null,\n            ReceiveMessageBodyLogState.Format);\n    }\n\n    /// <summary>\n    /// 记录接收到原始裸消息，包含消息头信息\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"ipcMessageResult\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    public static void LogReceiveOriginMessage(this IpcContext context, IpcMessageResult ipcMessageResult,\n        string? remotePeerName)\n    {\n        const LogLevel logLevel = LogLevel.Trace; // 一般框架信息是不关注的\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var ipcMessageBody = new IpcMessageBody(ipcMessageResult.IpcMessageContext.MessageBuffer, 0,\n            (int) ipcMessageResult.IpcMessageContext.MessageLength);\n        context.Logger.Log(logLevel, LoggerEventIds.ReceiveOriginMessageEventId,\n            new ReceiveMessageBodyLogState(ipcMessageBody, context.PipeName, remotePeerName, isBusinessMessage: false),\n            null,\n            ReceiveMessageBodyLogState.Format);\n    }\n\n    /// <summary>\n    /// 记录发送消息\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"buffer\"></param>\n    /// <param name=\"offset\"></param>\n    /// <param name=\"count\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    public static void LogSendMessage(this IpcContext context, byte[] buffer, int offset, int count,\n        string remotePeerName)\n    {\n        LogSendMessage(context, new IpcMessageBody(buffer, offset, count), remotePeerName);\n    }\n\n    /// <summary>\n    /// 记录发送消息\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"ipcMessageBody\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    public static void LogSendMessage(this IpcContext context, in IpcMessageBody ipcMessageBody, string remotePeerName)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        context.Logger.Log(logLevel, LoggerEventIds.SendMessageEventId,\n            new SendMessageBodyLogState(ipcMessageBody, context.PipeName, remotePeerName),\n            null,\n            SendMessageBodyLogState.Format);\n    }\n\n    /// <summary>\n    /// 记录发送消息\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"message\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    public static void LogSendMessage(this IpcContext context, in IpcBufferMessageContext message,\n        string remotePeerName)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        context.Logger.Log(logLevel, LoggerEventIds.SendMessageEventId,\n            new SendMessageBodiesLogState(message.IpcBufferMessageList, context.PipeName, remotePeerName), null,\n            SendMessageBodiesLogState.Format);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/LoggingContext/IpcMessageBodyFormatter.cs",
    "content": "﻿using System.Text;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context.LoggingContext;\n\nstatic class IpcMessageBodyFormatter\n{\n    public static int GetSendHeaderLength(string localPeerName, string remotePeerName)\n    {\n        // Send from {LocalPeerName} To {RemotePeerName}:\n        return SendText.Length + FromText.Length + localPeerName.Length + ToText.Length +\n               remotePeerName.Length +\n               \" : \".Length;\n    }\n\n    public static void AppendSendHeader(StringBuilder stringBuilder, string localPeerName, string remotePeerName)\n    {\n        stringBuilder.Append(SendText)\n            .Append(FromText)\n            .Append(localPeerName)\n            .Append(ToText)\n            .Append(remotePeerName)\n            .Append(\" : \");\n    }\n\n    public static int GetReceiveHeaderLength(string localPeerName, string remotePeerName)\n    {\n        // Receive from {RemotePeerName} To {LocalPeerName}:\n        return ReceiveText.Length + FromText.Length + localPeerName.Length + ToText.Length +\n               remotePeerName.Length +\n               \" : \".Length;\n    }\n\n    public static void AppendReceiveHeader(StringBuilder stringBuilder, string localPeerName, string remotePeerName)\n    {\n        stringBuilder.Append(ReceiveText)\n            .Append(FromText)\n            .Append(remotePeerName)\n            .Append(ToText)\n            .Append(localPeerName)\n            .Append(\" : \");\n    }\n\n    public static int GetIpcMessageBodyAsBinaryLength(IpcMessageBody ipcMessageBody)\n    {\n        return ipcMessageBody.Length * 3 /*一个byte转成两个字符加一个空格*/;\n    }\n\n    public static void AppendIpcMessageBodyAsBinary(StringBuilder stringBuilder, in IpcMessageBody ipcMessageBody)\n    {\n        for (int i = ipcMessageBody.Start; i < ipcMessageBody.Length; i++)\n        {\n            stringBuilder.Append(ipcMessageBody.Buffer[i].ToString(\"X2\"));\n            if (i != ipcMessageBody.Length - 1)\n            {\n                // 不是最后一个\n                stringBuilder.Append(' ');\n            }\n        }\n    }\n\n    private const string ReceiveText = \"Receive \";\n    private const string SendText = \"Send \";\n    private const string FromText = \"from \";\n    private const string ToText = \" To \";\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/LoggingContext/LoggerEventIds.cs",
    "content": "﻿using dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Context.LoggingContext;\n\ninternal static class LoggerEventIds\n{\n    public static EventId CommonDebugEventId => new EventId(0, \"Debug\");\n\n    /// <summary>\n    /// 发送消息\n    /// </summary>\n    public static EventId SendMessageEventId => new EventId(1, \"SendMessage\");\n\n    /// <summary>\n    /// 收到框架的消息，裸消息，包括消息头\n    /// </summary>\n    public static EventId ReceiveOriginMessageEventId => new EventId(2, \"ReceiveOriginMessage\");\n\n    /// <summary>\n    /// 收到消息，业务消息\n    /// </summary>\n    public static EventId ReceiveMessageEventId => new EventId(2, \"ReceiveMessage\");\n\n    /// <summary>\n    /// 收到 JsonIpcDirectRouted 通知消息\n    /// </summary>\n    public static EventId ReceiveJsonIpcDirectRoutedNotifyEventId => new EventId(3, \"ReceiveJsonIpcDirectRoutedNotify\");\n\n    /// <summary>\n    /// 收到 JsonIpcDirectRouted 请求消息\n    /// </summary>\n    public static EventId ReceiveJsonIpcDirectRoutedRequestEventId => new EventId(4, \"ReceiveJsonIpcDirectRoutedRequest\");\n\n    /// <summary>\n    /// 发送 JsonIpcDirectRouted 响应消息\n    /// </summary>\n    /// 在 <see cref=\"ReceiveJsonIpcDirectRoutedRequestEventId\"/> 之后返回的响应\n    public static EventId SendJsonIpcDirectRoutedResponseEventId => new EventId(5, \"SendJsonIpcDirectRoutedResponse\");\n\n    /// <summary>\n    /// 发送 JsonIpcDirectRouted 通知消息\n    /// </summary>\n    public static EventId SendJsonIpcDirectRoutedNotifyEventId => new EventId(6, \"SendJsonIpcDirectRoutedNotify\");\n\n    /// <summary>\n    /// 发送 JsonIpcDirectRouted 请求消息\n    /// </summary>\n    public static EventId SendJsonIpcDirectRoutedRequestEventId => new EventId(7, \"SendJsonIpcDirectRoutedRequest\");\n\n    /// <summary>\n    /// 收到 JsonIpcDirectRouted 响应消息\n    /// </summary>\n    /// 在客户端发送 <see cref=\"SendJsonIpcDirectRoutedRequestEventId\"/> 之后收到服务端的响应\n    public static EventId ReceiveJsonIpcDirectRoutedResponseEventId => new EventId(8, \"SendJsonIpcDirectRoutedRequest\");\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/LoggingContext/ReceiveMessageBodyLogState.cs",
    "content": "﻿using System;\nusing System.Text;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context.LoggingContext;\n\n/// <summary>\n/// 收到的消息\n/// </summary>\npublic readonly struct ReceiveMessageBodyLogState\n{\n    /// <summary>\n    /// 收到的消息\n    /// </summary>\n    public ReceiveMessageBodyLogState(IpcMessageBody ipcMessageBody, string localPeerName, string? remotePeerName,\n        bool isBusinessMessage)\n    {\n        IpcMessageBody = ipcMessageBody;\n        LocalPeerName = localPeerName;\n        IsBusinessMessage = isBusinessMessage;\n        RemotePeerName = remotePeerName ?? string.Empty;\n    }\n\n    /// <summary>\n    /// 是否业务消息。如果不是，那消息包含消息头\n    /// </summary>\n    public bool IsBusinessMessage { get; }\n\n    /// <summary>\n    /// 消息内容\n    /// </summary>\n    public IpcMessageBody IpcMessageBody { get; }\n\n    /// <summary>\n    /// 本地当前的 Peer 名\n    /// </summary>\n    public string LocalPeerName { get; }\n\n    /// <summary>\n    /// 远端对方的 Peer 名\n    /// </summary>\n    public string RemotePeerName { get; }\n\n    /// <summary>\n    /// 格式化为 UTF8 字符串\n    /// </summary>\n    /// <returns></returns>\n    public string FormatAsText()\n    {\n        return\n            $\"Receive from {RemotePeerName} To {LocalPeerName}: {Encoding.UTF8.GetString(IpcMessageBody.Buffer, IpcMessageBody.Start, IpcMessageBody.Length)}\";\n    }\n\n    /// <summary>\n    /// 格式化二进制文本\n    /// </summary>\n    /// <returns></returns>\n    public string FormatAsBinary()\n    {\n        var length = IpcMessageBodyFormatter.GetReceiveHeaderLength(LocalPeerName, RemotePeerName)\n                     + IpcMessageBodyFormatter.GetIpcMessageBodyAsBinaryLength(IpcMessageBody);\n        var stringBuilder = new StringBuilder(length);\n        IpcMessageBodyFormatter.AppendReceiveHeader(stringBuilder, LocalPeerName, RemotePeerName);\n        IpcMessageBodyFormatter.AppendIpcMessageBodyAsBinary(stringBuilder, IpcMessageBody);\n\n        return stringBuilder.ToString();\n    }\n\n    /// <summary>\n    /// 格式化\n    /// </summary>\n    /// <param name=\"state\"></param>\n    /// <param name=\"exception\"></param>\n    /// <returns></returns>\n    public static string Format(ReceiveMessageBodyLogState state, Exception? exception)\n    {\n        return state.FormatAsBinary();\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/LoggingContext/SendMessageBodiesLogState.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Text;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context.LoggingContext;\n\n/// <summary>\n/// 发送消息的日志信息\n/// </summary>\npublic readonly struct SendMessageBodiesLogState\n{\n    /// <summary>\n    /// 发送消息的日志信息\n    /// </summary>\n    internal SendMessageBodiesLogState(IpcMessageBody[] ipcBufferMessageList, string localPeerName,\n        string remotePeerName)\n    {\n        IpcBufferMessageList = ipcBufferMessageList;\n        LocalPeerName = localPeerName;\n        RemotePeerName = remotePeerName;\n    }\n\n    /// <summary>\n    /// 发送的消息内容\n    /// </summary>\n    public IpcMessageBody[] IpcBufferMessageList { get; }\n\n    /// <summary>\n    /// 本地当前的 Peer 名\n    /// </summary>\n    public string LocalPeerName { get; }\n\n    /// <summary>\n    /// 远端对方的 Peer 名\n    /// </summary>\n    public string RemotePeerName { get; }\n\n    /// <summary>\n    /// 格式化为 UTF8 字符串\n    /// </summary>\n    /// <returns></returns>\n    public string FormatAsText()\n    {\n        var result = $\"Send from {LocalPeerName} To {RemotePeerName}: \";\n        foreach (var ipcMessageBody in IpcBufferMessageList)\n        {\n            result += Encoding.UTF8.GetString(ipcMessageBody.Buffer, ipcMessageBody.Length, ipcMessageBody.Length);\n        }\n\n        return result;\n    }\n\n    /// <summary>\n    /// 格式化二进制文本\n    /// </summary>\n    /// <returns></returns>\n    public string FormatAsBinary()\n    {\n        // Send from {LocalPeerName} To {RemotePeerName}:\n        var length = IpcMessageBodyFormatter.GetSendHeaderLength(LocalPeerName, RemotePeerName);\n        length += IpcBufferMessageList.Sum(t => t.Length) * 3 /*一个byte转成两个字符加一个空格*/;\n        var stringBuilder = new StringBuilder(length);\n        IpcMessageBodyFormatter.AppendSendHeader(stringBuilder, LocalPeerName, RemotePeerName);\n\n        bool isFirst = true;\n        foreach (var ipcMessageBody in IpcBufferMessageList)\n        {\n            if (!isFirst)\n            {\n                // 不是第一个的，需要加上空格。因为上一段记录结束没有加上空格\n                stringBuilder.Append(' ');\n            }\n\n            isFirst = false;\n\n            IpcMessageBodyFormatter.AppendIpcMessageBodyAsBinary(stringBuilder, in ipcMessageBody);\n        }\n\n        return stringBuilder.ToString();\n    }\n\n    /// <summary>\n    /// 格式化\n    /// </summary>\n    /// <param name=\"state\"></param>\n    /// <param name=\"exception\"></param>\n    /// <returns></returns>\n    public static string Format(SendMessageBodiesLogState state, Exception? exception)\n    {\n        return state.FormatAsBinary();\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/LoggingContext/SendMessageBodyLogState.cs",
    "content": "﻿using System;\nusing System.Text;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context.LoggingContext;\n\n/// <summary>\n/// 发送消息的日志信息\n/// </summary>\npublic readonly struct SendMessageBodyLogState\n{\n    /// <summary>\n    /// 发送消息的日志信息\n    /// </summary>\n    public SendMessageBodyLogState(IpcMessageBody ipcMessageBody, string localPeerName, string remotePeerName)\n    {\n        IpcMessageBody = ipcMessageBody;\n        LocalPeerName = localPeerName;\n        RemotePeerName = remotePeerName;\n    }\n\n    /// <summary>\n    /// 发送的消息内容\n    /// </summary>\n    public IpcMessageBody IpcMessageBody { get; }\n\n    /// <summary>\n    /// 本地当前的 Peer 名\n    /// </summary>\n    public string LocalPeerName { get; }\n\n    /// <summary>\n    /// 远端对方的 Peer 名\n    /// </summary>\n    public string RemotePeerName { get; }\n\n    /// <summary>\n    /// 格式化为 UTF8 字符串\n    /// </summary>\n    /// <returns></returns>\n    public string FormatAsText()\n    {\n        return\n            $\"Send from {LocalPeerName} To {RemotePeerName}: {Encoding.UTF8.GetString(IpcMessageBody.Buffer, IpcMessageBody.Start, IpcMessageBody.Length)}\";\n    }\n\n    /// <summary>\n    /// 格式化二进制文本\n    /// </summary>\n    /// <returns></returns>\n    public string FormatAsBinary()\n    {\n        var length = IpcMessageBodyFormatter.GetSendHeaderLength(LocalPeerName, RemotePeerName)\n                     + IpcMessageBodyFormatter.GetIpcMessageBodyAsBinaryLength(IpcMessageBody);\n        var stringBuilder = new StringBuilder(length);\n        IpcMessageBodyFormatter.AppendSendHeader(stringBuilder, LocalPeerName, RemotePeerName);\n        IpcMessageBodyFormatter.AppendIpcMessageBodyAsBinary(stringBuilder, IpcMessageBody);\n\n        return stringBuilder.ToString();\n    }\n\n    /// <summary>\n    /// 格式化\n    /// </summary>\n    /// <param name=\"state\"></param>\n    /// <param name=\"exception\"></param>\n    /// <returns></returns>\n    public static string Format(SendMessageBodyLogState state, Exception? exception)\n    {\n        return state.FormatAsBinary();\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/PeerConnectedArgs.cs",
    "content": "﻿using System;\n\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 对方连接的事件参数\n    /// </summary>\n    /// 这是给上层使用的事件参数\n    public class PeerConnectedArgs : EventArgs\n    {\n        /// <summary>\n        /// 创建对方连接的事件参数\n        /// </summary>\n        /// <param name=\"peer\"></param>\n        public PeerConnectedArgs(PeerProxy peer)\n        {\n            Peer = peer;\n        }\n\n        /// <summary>\n        /// 获取用来代表对方的属性\n        /// </summary>\n        public PeerProxy Peer { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/PeerConnectionBrokenArgs.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 对方连接断开事件参数\n    /// </summary>\n    public class PeerConnectionBrokenArgs : EventArgs, IPeerConnectionBrokenArgs\n    {\n\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/PeerMessageArgs.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\n\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 对方通讯的消息事件参数\n    /// </summary>\n    public class PeerMessageArgs : EventArgs, IPeerMessageArgs\n    {\n        /// <summary>\n        /// 创建对方通讯的消息事件参数\n        /// </summary>\n        /// <param name=\"peerName\"></param>\n        /// <param name=\"message\"></param>\n        /// <param name=\"ack\"></param>\n        /// <param name=\"messageCommandType\"></param>\n        [DebuggerStepThrough]\n        internal PeerMessageArgs(string peerName, IpcMessage message, in Ack ack, IpcMessageCommandType messageCommandType)\n        {\n            Message = message;\n            Ack = ack;\n            MessageCommandType = messageCommandType;\n            PeerName = peerName;\n        }\n\n        /// <summary>\n        /// 用于读取消息的内容\n        /// </summary>\n        public IpcMessage Message { get; }\n\n        /// <summary>\n        /// 消息编号\n        /// </summary>\n        public Ack Ack { get; }\n\n        /// <summary>\n        /// 对方的名字，此名字是对方的服务器名字，可以用来连接\n        /// </summary>\n        public string PeerName { get; }\n\n        public bool TryGetPayload(byte[] requiredHeader, out IpcMessage subMessage) =>\n            PeerMessageArgsExtension.TryGetPayload(this, requiredHeader, out subMessage);\n\n        internal IpcMessageCommandType MessageCommandType { get; }\n\n        /// <summary>\n        /// 表示是否被上一级处理了，可以通过 <see cref=\"HandlerMessage\"/> 了解处理者的信息\n        /// </summary>\n        public bool Handle { private set; get; }\n\n        /// <summary>\n        /// 处理者的消息\n        /// </summary>\n        /// 框架大了，不能只有 <see cref=\"Handle\"/> 一个属性，还需要能做到调试，调试是谁处理了，因此加添加了这个属性\n        public string? HandlerMessage { private set; get; }\n\n        /// <summary>\n        /// 设置被处理，同时添加 <paramref name=\"message\"/> 用于调试的信息\n        /// </summary>\n        /// <param name=\"message\">用于调试的信息，请记录是谁设置的，原因是什么</param>\n        public void SetHandle(string message)\n        {\n            Handle = true;\n            HandlerMessage = message;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Context/PeerStreamMessageArgs.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Context\n{\n    /// <summary>\n    /// 替代 <see cref=\"PeerMessageArgs\"/> 在 IPC 框架内进行高性能传递。\n    /// </summary>\n    internal class PeerStreamMessageArgs : EventArgs\n    {\n        /// <summary>\n        /// 创建对方通讯的消息事件参数\n        /// </summary>\n        /// <param name=\"ipcMessageContext\"></param>\n        /// <param name=\"peerName\"></param>\n        /// <param name=\"messageStream\"></param>\n        /// <param name=\"ack\"></param>\n        /// <param name=\"messageCommandType\"></param>\n        [DebuggerStepThrough]\n        internal PeerStreamMessageArgs(IpcMessageContext ipcMessageContext, string peerName, Stream messageStream, in Ack ack, IpcMessageCommandType messageCommandType)\n        {\n            IpcMessageContext = ipcMessageContext;\n            PeerName = peerName;\n            MessageStream = messageStream;\n            Ack = ack;\n            MessageCommandType = messageCommandType;\n        }\n\n        internal IpcMessageContext IpcMessageContext { get; }\n\n        /// <summary>\n        /// 用于读取消息的内容\n        /// </summary>\n        internal Stream MessageStream { get; }\n\n        /// <summary>\n        /// 消息编号\n        /// </summary>\n        public Ack Ack { get; }\n\n        /// <summary>\n        /// 对方的名字，此名字是对方的服务器名字，可以用来连接\n        /// </summary>\n        public string PeerName { get; }\n\n        internal IpcMessageCommandType MessageCommandType { get; }\n\n        /// <summary>\n        /// 表示是否被上一级处理了，可以通过 <see cref=\"HandlerMessage\"/> 了解处理者的信息\n        /// </summary>\n        public bool Handle { private set; get; }\n\n        /// <summary>\n        /// 处理者的消息\n        /// </summary>\n        /// 框架大了，不能只有 <see cref=\"Handle\"/> 一个属性，还需要能做到调试，调试是谁处理了，因此加添加了这个属性\n        public string? HandlerMessage { private set; get; }\n\n        /// <summary>\n        /// 设置被处理，同时添加 <paramref name=\"message\"/> 用于调试的信息\n        /// </summary>\n        /// <param name=\"message\">用于调试的信息，请记录是谁设置的，原因是什么</param>\n        public void SetHandle(string message)\n        {\n            Handle = true;\n            HandlerMessage = message;\n        }\n\n        internal PeerMessageArgs ToPeerMessageArgs()\n        {\n            var message = new IpcMessage(\"MessageReceived\", new IpcMessageBody(IpcMessageContext.MessageBuffer, (int) MessageStream.Position, (int) (IpcMessageContext.MessageLength - MessageStream.Position)));\n            return new PeerMessageArgs(PeerName, message, Ack, MessageCommandType);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Diagnostics/IIpcMessageInspector.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Diagnostics\n{\n    /// <summary>\n    /// 实现此接口并注册到 <see cref=\"IpcMessageInspectorManager\"/> 中可检查所有 IPC 收发的消息内容，以供调试。\n    /// </summary>\n    public interface IIpcMessageInspector\n    {\n        /// <summary>\n        /// 实现此方法以检查从业务端发起的消息。\n        /// </summary>\n        /// <param name=\"context\">包含消息发送的上下文。</param>\n        void Send(IpcMessageInspectionContext context);\n\n        /// <summary>\n        /// 实现此方法以检查从框架最终发出的消息。\n        /// </summary>\n        /// <param name=\"context\">包含消息发送的上下文。</param>\n        void SendCore(IpcMessageInspectionContext context);\n\n        /// <summary>\n        /// 实现此方法以检查从框架收到的最原始的消息内容。\n        /// </summary>\n        /// <param name=\"context\">包含消息接收的上下文。</param>\n        void ReceiveCore(IpcMessageInspectionContext context);\n\n        /// <summary>\n        /// 实现此方法以检查从收到消息后发到业务后的业务部分。\n        /// </summary>\n        /// <param name=\"context\">包含消息接收的上下文。</param>\n        void Receive(IpcMessageInspectionContext context);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Diagnostics/IpcMessageInspectionContext.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Diagnostics\n{\n    /// <summary>\n    /// 为 <see cref=\"IIpcMessageInspector\"/> 的检查提供上下文。\n    /// </summary>\n    public sealed class IpcMessageInspectionContext\n    {\n        private readonly IEnumerable<IpcMessageBody> _messageParts;\n\n        internal IpcMessageInspectionContext(string localPeerName, string remotePeerName, Ack? ack, string tag, IEnumerable<IpcMessageBody> messageParts)\n        {\n            LocalPeerName = localPeerName ?? throw new ArgumentNullException(nameof(localPeerName));\n            RemotePeerName = remotePeerName ?? throw new ArgumentNullException(nameof(remotePeerName));\n            Ack = ack;\n            Tag = tag ?? throw new ArgumentNullException(nameof(tag));\n            _messageParts = messageParts ?? throw new ArgumentNullException(nameof(messageParts));\n        }\n\n        /// <summary>\n        /// 本地 IPC 服务名称。\n        /// </summary>\n        public string LocalPeerName { get; }\n\n        /// <summary>\n        /// 发送目标的名称。\n        /// </summary>\n        public string RemotePeerName { get; }\n\n        /// <summary>\n        /// 标记此消息的描述性信息。\n        /// </summary>\n        public string Tag { get; }\n\n        /// <summary>\n        /// 消息的序号。\n        /// </summary>\n        public Ack? Ack { get; }\n\n        /// <summary>\n        /// 获取单个有意义消息的不同部分。其中，业务端检查时只能获取到业务内容的那一部分；框架端检查时可获取到消息头的非关键部分。\n        /// </summary>\n        public IEnumerable<IpcMessageBody> GetMessageParts() => _messageParts;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Diagnostics/IpcMessageInspectorManager.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\n\nnamespace dotnetCampus.Ipc.Diagnostics\n{\n    /// <summary>\n    /// 管理 IPC 消息收发时的检查器。\n    /// </summary>\n    public class IpcMessageInspectorManager\n    {\n        private static readonly ConcurrentDictionary<string, IpcMessageInspectorManager> Managers = new();\n\n        /// <summary>\n        /// 根据本地 Peer 名称查找 IPC 消息收发的检查器。\n        /// </summary>\n        /// <param name=\"peerName\"></param>\n        /// <returns></returns>\n        public static IpcMessageInspectorManager FromLocalPeerName(string peerName)\n        {\n            return Managers.GetOrAdd(peerName, name => new IpcMessageInspectorManager(name));\n        }\n\n        private readonly string _localPeerName;\n\n        private readonly ConcurrentDictionary<IIpcMessageInspector, IIpcMessageInspector> _inspectors = new();\n\n        private IpcMessageInspectorManager(string peerName)\n        {\n            _localPeerName = peerName ?? throw new ArgumentNullException(nameof(peerName));\n        }\n\n        /// <summary>\n        /// 注册一个 IPC 消息检查器。\n        /// </summary>\n        /// <param name=\"inspector\"></param>\n        public void RegisterInspector(IIpcMessageInspector inspector)\n        {\n            _inspectors.TryAdd(inspector, inspector);\n        }\n\n        internal void Call(Action<IIpcMessageInspector> caller)\n        {\n            foreach (var inspectorPair in _inspectors)\n            {\n                caller(inspectorPair.Key);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Diagnostics/IpcMessageTracker.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Runtime.CompilerServices;\n\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Diagnostics\n{\n    interface IIpcMessageTracker\n    {\n        string Tag { get; }\n\n        void Debug(string message);\n    }\n\n    /// <summary>\n    /// <para>在 IPC 框架内部，提供消息收发的全程追踪。无论此消息被封装还是解包，都会在此类型的帮助下包含全程追踪信息。</para>\n    /// 在以下情况下，此追踪可完全保证某个消息的来源和去路：\n    /// <list type=\"bullet\">\n    /// <item>业务方准备发一条消息直至此消息最终通过管道发出的全链路。</item>\n    /// <item>管道收到一条消息直至这条消息传递给业务的全链路。</item>\n    /// </list>\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    internal class IpcMessageTracker<T> : IIpcMessageTracker\n    {\n        /// <summary>\n        /// 本地 Peer 名称。\n        /// </summary>\n        private readonly string _localPeerName;\n\n        /// <summary>\n        /// 本消息将发至此 Peer 或本消息从此 Peer 来。\n        /// </summary>\n        private readonly string _remotePeerName;\n\n        /// <summary>\n        /// 输出日志的方法。\n        /// </summary>\n        private readonly ILogger _logger;\n\n        /// <summary>\n        /// 记录追踪过程中产生的所有日志。\n        /// </summary>\n        private readonly List<string> _trackingLogs;\n\n        /// <summary>\n        /// 使用追踪器追踪某个消息。\n        /// </summary>\n        /// <param name=\"localPeerName\">本地 Peer 名称。</param>\n        /// <param name=\"remotePeerName\">本消息将发至此 Peer 或本消息从此 Peer 来。</param>\n        /// <param name=\"message\">要追踪的消息。</param>\n        /// <param name=\"tag\">此消息的业务标记。</param>\n        /// <param name=\"logger\">日志。</param>\n        public IpcMessageTracker(string localPeerName, string remotePeerName, T message, string tag, ILogger logger)\n        {\n            _localPeerName = localPeerName ?? throw new ArgumentNullException(nameof(localPeerName));\n            _remotePeerName = remotePeerName ?? throw new ArgumentNullException(nameof(remotePeerName));\n            Tag = tag ?? \"\";\n            _logger = logger ?? throw new ArgumentNullException(nameof(logger));\n            _trackingLogs = new();\n            Message = message;\n        }\n\n        /// <summary>\n        /// 继续追踪封装或解包的消息。\n        /// </summary>\n        /// <param name=\"localPeerName\">本地 Peer 名称。</param>\n        /// <param name=\"remotePeerName\">本消息将发至此 Peer 或本消息从此 Peer 来。</param>\n        /// <param name=\"message\">要追踪的消息。</param>\n        /// <param name=\"tag\">此消息的业务标记。</param>\n        /// <param name=\"logger\">日志。</param>\n        /// <param name=\"trackingLogs\">已记录的追踪。</param>\n        private IpcMessageTracker(string localPeerName, string remotePeerName, T message, string tag, ILogger logger, List<string> trackingLogs)\n        {\n            _localPeerName = localPeerName ?? throw new ArgumentNullException(nameof(localPeerName));\n            _remotePeerName = remotePeerName ?? throw new ArgumentNullException(nameof(remotePeerName));\n            Tag = tag ?? \"\";\n            _logger = logger ?? throw new ArgumentNullException(nameof(logger));\n            _trackingLogs = trackingLogs;\n            Message = message;\n        }\n\n        /// <summary>\n        /// 此消息的业务标记。\n        /// </summary>\n        public string Tag { get; }\n\n        /// <summary>\n        /// 追踪的对象\n        /// </summary>\n        public T Message { get; }\n\n        /// <summary>\n        /// 对追踪的消息包 <see cref=\"Message\"/> 进行封装或解包后，返回对此封装或解包后的新追踪器。\n        /// </summary>\n        /// <typeparam name=\"TNext\">封装或解包后的新消息类型。</typeparam>\n        /// <param name=\"nextMessage\">封装或解包后的新消息实例。</param>\n        /// <returns>对新消息的追踪器，具有原消息的追踪记录。</returns>\n        public IpcMessageTracker<TNext> TrackNext<TNext>(TNext nextMessage)\n        {\n            return new IpcMessageTracker<TNext>(_localPeerName, _remotePeerName, nextMessage, Tag, _logger, _trackingLogs);\n        }\n\n        [Conditional(\"DEBUG\")]\n        public void Debug(string message, [CallerMemberName] string memberName = \"\")\n        {\n            _logger.Log(LogLevel.Trace, new EventId(0, _localPeerName), this, null,\n                (s, e) => $\"[IPC] [{Tag}] [{memberName ?? \"null\"}] {message}\");\n        }\n\n        void IIpcMessageTracker.Debug(string message)\n        {\n            _logger.Log(LogLevel.Trace, new EventId(0, _localPeerName), this, null,\n                (s, e) => $\"[IPC] [{Tag}] {message}\");\n        }\n\n        /// <summary>\n        /// 标记正在执行关键步骤，然后将全部消息内容记录下来用于调试。\n        /// </summary>\n        /// <param name=\"stepName\">步骤名。</param>\n        /// <param name=\"ack\">消息序号（为 null 表示无法确定 ACK）。</param>\n        /// <param name=\"message\">消息体。</param>\n        [Conditional(\"DEBUG\")]\n        internal void CriticalStep(string stepName, Ack? ack, IpcMessageBody message)\n        {\n            CriticalStep(stepName, ack, new[] { message });\n        }\n\n        /// <summary>\n        /// 标记正在执行关键步骤，然后将全部消息内容记录下来用于调试。\n        /// </summary>\n        /// <param name=\"stepName\">步骤名。</param>\n        /// <param name=\"ack\">消息序号（为 null 表示无法确定 ACK）。</param>\n        /// <param name=\"messages\">消息体（不含关键消息头，含其他消息头）。</param>\n        [Conditional(\"DEBUG\")]\n        internal void CriticalStep(string stepName, Ack? ack, IEnumerable<IpcMessageBody> messages)\n        {\n            var manager = IpcMessageInspectorManager.FromLocalPeerName(_localPeerName);\n            var context = new IpcMessageInspectionContext(_localPeerName, _remotePeerName, ack, Tag, messages);\n            manager.Call(stepName switch\n            {\n                // 从业务端发起的请求或回复。\n                \"Send\" => i => i.Send(context),\n                // 框架层最终发送的请求或回复。\n                \"SendCore\" => i => i.SendCore(context),\n                // 框架层最开始收到的消息。\n                \"ReceiveCore\" => i => i.ReceiveCore(context),\n                // 从收到后发至业务端的消息。\n                \"Receive\" => i => i.Receive(context),\n                _ => throw new NotSupportedException($\"暂不支持检查 {stepName} 关键步骤名称的消息。\"),\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcClientPipeConnectionException.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// IPC的客户端连接失败异常\n/// </summary>\npublic class IpcClientPipeConnectionException : IpcRemoteException\n{\n    /// <summary>\n    /// IPC的客户端连接失败异常\n    /// </summary>\n    /// <param name=\"peerName\">连接的服务名</param>\n    /// <param name=\"message\"></param>\n    public IpcClientPipeConnectionException(string peerName, string? message = null) : this(peerName, null, message)\n    {\n    }\n\n    /// <summary>\n    /// IPC的客户端连接失败异常\n    /// </summary>\n    public IpcClientPipeConnectionException(string peerName, Exception? innerException, string? message = null) : base(message, innerException)\n    {\n        PeerName = peerName;\n        _message = message ?? innerException?.Message;\n    }\n\n    /// <inheritdoc />\n    public override string Message => _message ?? $\"连接管道服务失败。服务管道名:{PeerName}\";\n\n    /// <summary>\n    /// 连接的服务名\n    /// </summary>\n    public string PeerName { get; }\n\n    private readonly string? _message;\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcException.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Exceptions\n{\n    /// <summary>\n    /// 所有 IPC 相关的异常的基类。\n    /// </summary>\n    public class IpcException : Exception\n    {\n        /// <summary>\n        /// 创建 <see cref=\"IpcException\"/> 的新实例。\n        /// </summary>\n        public IpcException() : base()\n        {\n        }\n\n        /// <summary>\n        /// 创建带有自定义消息的 <see cref=\"IpcException\"/> 的新实例。\n        /// </summary>\n        /// <param name=\"message\">自定义消息。</param>\n        public IpcException(string message) : base(message)\n        {\n        }\n\n        /// <summary>\n        /// 创建带有自定义消息和内部异常的 <see cref=\"IpcException\"/> 的新实例。\n        /// </summary>\n        /// <param name=\"message\">自定义消息。</param>\n        /// <param name=\"innerException\">内部异常。</param>\n        public IpcException(string? message, Exception? innerException) : base(message, innerException)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcInvokingException.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 当获取设置属性值或调用方法时，如果实现方法内抛出了异常，且不是常见的异常类型（参见 <see cref=\"GeneratedProxyExceptionModel\"/>），则会用此异常包装。\n/// </summary>\ninternal class IpcInvokingException(string message, string? remoteStackTrace) : IpcRemoteException(message, remoteStackTrace);\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcInvokingTimeoutException.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 当获取设置属性值或调用方法时，如果指定了 <see cref=\"IpcMemberAttribute.Timeout\"/> 但未在超时时间完成前完成调用，则会抛出此异常。\n/// </summary>\ninternal class IpcInvokingTimeoutException : IpcRemoteException\n{\n    public IpcInvokingTimeoutException(string memberName, TimeSpan timeout) : base()\n    {\n        MemberName = VerifyMemberName(memberName);\n        Timeout = VerifyTimeout(timeout);\n    }\n\n    public IpcInvokingTimeoutException(string memberName, TimeSpan timeout, string message) : base(message)\n    {\n        MemberName = VerifyMemberName(memberName);\n        Timeout = VerifyTimeout(timeout);\n    }\n\n    public IpcInvokingTimeoutException(string memberName, TimeSpan timeout, string message, Exception innerException) : base(message, innerException)\n    {\n        MemberName = VerifyMemberName(memberName);\n        Timeout = VerifyTimeout(timeout);\n    }\n\n    /// <summary>\n    /// 获取本次调用的成员名称。\n    /// </summary>\n    public string MemberName { get; set; }\n\n    /// <summary>\n    /// 获取本次调用所设置的超时时间（毫秒）。\n    /// </summary>\n    public TimeSpan Timeout { get; set; }\n\n    private string VerifyMemberName(string? memberName)\n    {\n        if (string.IsNullOrWhiteSpace(memberName))\n        {\n            throw new ArgumentException($\"“{nameof(memberName)}”不能为 null 或空白。\", nameof(memberName));\n        }\n\n        return memberName;\n    }\n\n    private TimeSpan VerifyTimeout(TimeSpan timeout)\n    {\n        if (timeout <= TimeSpan.Zero)\n        {\n            throw new ArgumentException($\"超时时间不能为 0 或负值，当前为 {timeout} 毫秒。\", nameof(timeout));\n        }\n\n        return timeout;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcLocalException.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 所有由本地问题导致的 IPC 异常。\n/// </summary>\npublic class IpcLocalException : IpcException\n{\n    /// <summary>\n    /// 创建 <see cref=\"IpcRemoteException\"/> 的新实例。\n    /// </summary>\n    public IpcLocalException() : base()\n    {\n    }\n\n    /// <summary>\n    /// 创建带有自定义消息的 <see cref=\"IpcLocalException\"/> 的新实例。\n    /// </summary>\n    /// <param name=\"message\">自定义消息。</param>\n    public IpcLocalException(string message) : base(message)\n    {\n    }\n\n    /// <summary>\n    /// 创建带有自定义消息和内部异常的 <see cref=\"IpcLocalException\"/> 的新实例。\n    /// </summary>\n    /// <param name=\"message\">自定义消息。</param>\n    /// <param name=\"innerException\">内部异常。</param>\n    public IpcLocalException(string message, Exception innerException) : base(message, innerException)\n    {\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcMemberNotFoundException.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// IPC 代理或对接匹配找不到目标签名的成员时抛出。\n/// </summary>\npublic class IpcMemberNotFoundException : IpcLocalException\n{\n    /// <summary>\n    /// 初始化 <see cref=\"IpcMemberNotFoundException\"/> 类的新实例。\n    /// </summary>\n    public IpcMemberNotFoundException() : base()\n    {\n    }\n\n    /// <summary>\n    /// 初始化 <see cref=\"IpcMemberNotFoundException\"/> 类的新实例。\n    /// </summary>\n    /// <param name=\"message\">异常提示信息。</param>\n    public IpcMemberNotFoundException(string message) : base(message)\n    {\n    }\n\n    /// <summary>\n    /// 初始化 <see cref=\"IpcMemberNotFoundException\"/> 类的新实例。\n    /// </summary>\n    /// <param name=\"message\">异常提示信息。</param>\n    /// <param name=\"innerException\">引起当前异常的异常。</param>\n    public IpcMemberNotFoundException(string message, Exception innerException) : base(message, innerException)\n    {\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcPeerConnectionBrokenException.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Exceptions\n{\n    /// <summary>\n    /// 远端对方断开连接异常\n    /// </summary>\n    public class IpcPeerConnectionBrokenException : IpcRemoteException\n    {\n        /// <summary>\n        /// 远端对方断开连接异常\n        /// </summary>\n        public IpcPeerConnectionBrokenException() : base($\"对方已断开\")\n        {\n        }\n\n        /// <summary>\n        /// 远端对方断开连接异常\n        /// </summary>\n        public IpcPeerConnectionBrokenException(string message) : base(message)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcPipeConnectionException.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Exceptions\n{\n    /// <summary>\n    /// 进行管道连接时的异常\n    /// </summary>\n    public class IpcPipeConnectionException : IpcLocalException\n    {\n        internal IpcPipeConnectionException(string connectingPipeName, string localClientName, string remoteServerName, string message, Exception innerException) : base(message, innerException)\n        {\n            ConnectingPipeName = connectingPipeName;\n            LocalPeerName = localClientName;\n            RemotePeerName = remoteServerName;\n        }\n\n        /// <summary>\n        /// 正在连接中的管道名。按照当前的设计，应该和 <see cref=\"RemotePeerName\"/> 是相同的值\n        /// </summary>\n        public string ConnectingPipeName { get; }\n\n        /// <summary>\n        /// 本地当前的 Peer 名\n        /// </summary>\n        public string LocalPeerName { get; }\n\n        /// <summary>\n        /// 远端对方的 Peer 名\n        /// </summary>\n        public string RemotePeerName { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/IpcRemoteException.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Exceptions\n{\n    /// <summary>\n    /// 所有由远端问题导致的 IPC 异常。\n    /// </summary>\n    public class IpcRemoteException : IpcException\n    {\n        private readonly string? _remoteStackTrace;\n\n        /// <summary>\n        /// 创建 <see cref=\"IpcRemoteException\"/> 的新实例。\n        /// </summary>\n        public IpcRemoteException()\n        {\n        }\n\n        /// <summary>\n        /// 创建带有自定义消息的 <see cref=\"IpcRemoteException\"/> 的新实例。\n        /// </summary>\n        /// <param name=\"message\">自定义消息。</param>\n        public IpcRemoteException(string message) : base(message)\n        {\n        }\n\n        /// <summary>\n        /// 创建带有自定义消息和远端堆栈的 <see cref=\"IpcRemoteException\"/> 的新实例。\n        /// </summary>\n        /// <param name=\"message\">自定义消息。</param>\n        /// <param name=\"remoteStackTrace\">远端堆栈。</param>\n        public IpcRemoteException(string message, string? remoteStackTrace) : base(message)\n        {\n            _remoteStackTrace = remoteStackTrace;\n        }\n\n        /// <summary>\n        /// 创建带有自定义消息和内部异常的 <see cref=\"IpcRemoteException\"/> 的新实例。\n        /// </summary>\n        /// <param name=\"message\">自定义消息。</param>\n        /// <param name=\"innerException\">内部异常。</param>\n        public IpcRemoteException(string? message, Exception? innerException) : base(message, innerException)\n        {\n        }\n\n        /// <summary>\n        /// 远端出现异常时的调用堆栈。\n        /// </summary>\n        public override string? StackTrace => _remoteStackTrace ?? base.StackTrace;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/JsonIpcDirectRouteSerializeLocalException.cs",
    "content": "﻿using dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 直接路由进行序列化过程中的异常，这是一个比较模糊边界的异常，虽然是因为本地序列化抛出的异常，但也可能是远端发送过来的数据不符合预期导致的，归属于远端异常也是可以的\n/// </summary>\npublic class JsonIpcDirectRouteSerializeLocalException : JsonIpcDirectRoutedLocalException\n{\n    internal JsonIpcDirectRouteSerializeLocalException(IpcMessage responseMessage, Type responseType, Exception innerException) : base(DefaultMessage, innerException)\n    {\n        ResponseMessage = responseMessage;\n        ResponseType = responseType;\n    }\n\n    /// <summary>\n    /// 响应消息\n    /// </summary>\n    public IpcMessage ResponseMessage { get; }\n\n    /// <summary>\n    /// 要求的响应类型\n    /// </summary>\n    public Type ResponseType { get; }\n\n    internal const string DefaultMessage = \"Json Ipc DirectRoute Serialize Exception.\";\n\n    /// <inheritdoc />\n    public override string Message => $\"{base.Message} ResponseType={ResponseType} ResponseMessage={ResponseMessage.ToDebugString()} {InnerException?.Message}\";\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/JsonIpcDirectRoutedCanNotFindRequestHandlerException.cs",
    "content": "﻿using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 直接路由里面的由远端处理请求过程中导致的异常，找不到请求对应的处理器\n/// </summary>\npublic class JsonIpcDirectRoutedCanNotFindRequestHandlerException : JsonIpcDirectRoutedHandleRequestRemoteException\n{\n    internal JsonIpcDirectRoutedCanNotFindRequestHandlerException(PeerProxy remotePeer, string routedPath, JsonIpcDirectRoutedHandleRequestExceptionResponse exceptionResponse) : base\n    (\n        remotePeer,\n        routedPath,\n        exceptionResponse\n    )\n    {\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/JsonIpcDirectRoutedHandleRequestRemoteException.cs",
    "content": "﻿using dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 直接路由里面的由远端处理请求过程中导致的异常\n/// </summary>\npublic class JsonIpcDirectRoutedHandleRequestRemoteException : JsonIpcDirectRoutedRemoteException\n{\n    internal JsonIpcDirectRoutedHandleRequestRemoteException(PeerProxy remotePeer, string routedPath,\n        JsonIpcDirectRoutedHandleRequestExceptionResponse exceptionResponse) : base\n    (\n         $\"\"\"\n         JsonIpcDirectRouted remote handle request exception.\n         RemotePeer: {remotePeer.PeerName}\n         RoutedPath: {routedPath}\n         RemoteExceptionType: {exceptionResponse.ExceptionInfo!.ExceptionType}\n         RemoteExceptionMessage: {exceptionResponse.ExceptionInfo!.ExceptionMessage}\n         \"\"\", exceptionResponse.ExceptionInfo!.ExceptionStackTrace\n    )\n    {\n        RemotePeer = remotePeer;\n        RoutedPath = routedPath;\n        ExceptionResponse = exceptionResponse;\n    }\n\n    public PeerProxy RemotePeer { get; }\n    public string RoutedPath { get; }\n\n    public string RemoteExceptionType => ExceptionInfo.ExceptionType!;\n    public string? RemoteExceptionMessage => ExceptionInfo.ExceptionMessage;\n\n    internal JsonIpcDirectRoutedHandleRequestExceptionResponse ExceptionResponse { get; }\n\n    internal JsonIpcDirectRoutedHandleRequestExceptionResponse.JsonIpcDirectRoutedHandleRequestExceptionInfo\n        ExceptionInfo => ExceptionResponse.ExceptionInfo!;\n\n    /// <inheritdoc />\n    public override string ToString()\n    {\n        return $\"\"\"\n                {Message}\n                \n                RemoteException:{ExceptionInfo.ExceptionToString}\n                \"\"\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/JsonIpcDirectRoutedLocalException.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 直接路由里面的由本地问题导致的异常\n/// </summary>\npublic class JsonIpcDirectRoutedLocalException : IpcLocalException\n{\n    internal JsonIpcDirectRoutedLocalException()\n    {\n    }\n\n    internal JsonIpcDirectRoutedLocalException(string message) : base(message)\n    {\n    }\n\n    internal JsonIpcDirectRoutedLocalException(string message, Exception innerException) : base(message, innerException)\n    {\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Exceptions/JsonIpcDirectRoutedRemoteException.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Exceptions;\n\n/// <summary>\n/// 直接路由里面的由远端问题导致的异常\n/// </summary>\npublic class JsonIpcDirectRoutedRemoteException : IpcRemoteException\n{\n    internal JsonIpcDirectRoutedRemoteException()\n    {\n    }\n\n    internal JsonIpcDirectRoutedRemoteException(string message) : base(message)\n    {\n    }\n\n    internal JsonIpcDirectRoutedRemoteException(string message, string? remoteStackTrace) : base(message, remoteStackTrace)\n    {\n    }\n\n    internal JsonIpcDirectRoutedRemoteException(string? message, Exception? innerException) : base(message, innerException)\n    {\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IIpcProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\n\nnamespace dotnetCampus.Ipc\n{\n    /// <summary>\n    /// 对等 IPC 通信的总提供类型。\n    /// </summary>\n    public interface IIpcProvider\n    {\n        /// <summary>\n        /// 上下文信息\n        /// </summary>\n        IpcContext IpcContext { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IIpcRequestHandler.cs",
    "content": "﻿using System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc\n{\n    /// <summary>\n    /// 用于在服务器端处理客户端请求的处理器\n    /// </summary>\n    public interface IIpcRequestHandler\n    {\n        /// <summary>\n        /// 处理客户端发过来的请求\n        /// </summary>\n        /// <param name=\"requestContext\"></param>\n        /// <returns></returns>\n        Task<IIpcResponseMessage> HandleRequest(IIpcRequestContext requestContext);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IMessageWriter.cs",
    "content": "﻿using System.Runtime.CompilerServices;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc\n{\n    /// <summary>\n    /// 用于表示发送消息\n    /// </summary>\n    public interface IRawMessageWriter\n    {\n        /// <summary>\n        /// 向服务端发送消息\n        /// </summary>\n        /// <param name=\"data\"></param>\n        /// <param name=\"offset\"></param>\n        /// <param name=\"length\"></param>\n        /// <param name=\"tag\">这一次写入的是什么内容，用于调试</param>\n        /// <returns></returns>\n        Task WriteMessageAsync(byte[] data, int offset, int length, [CallerMemberName] string tag = \"\");\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IPeerProxy.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc\n{\n    /// <summary>\n    /// 用于表示远程的对方\n    /// </summary>\n    public interface IPeerProxy\n    {\n        /// <summary>\n        /// 对方的服务器名\n        /// </summary>\n        string PeerName { get; }\n\n        /// <summary>\n        /// 发送请求给对方，请求对方的响应。这是客户端-服务器端模式\n        /// </summary>\n        /// <param name=\"request\"></param>\n        /// <returns></returns>\n        Task NotifyAsync(IpcMessage request);\n\n        /// <summary>\n        /// 发送请求给对方，请求对方的响应。这是客户端-服务器端模式\n        /// </summary>\n        /// <param name=\"request\"></param>\n        /// <returns></returns>\n        Task<IpcMessage> GetResponseAsync(IpcMessage request);\n\n        /// <summary>\n        /// 当收到消息时触发\n        /// </summary>\n        event EventHandler<IPeerMessageArgs> MessageReceived;\n\n        /// <summary>\n        /// 对方连接断开事件\n        /// </summary>\n        event EventHandler<IPeerConnectionBrokenArgs> PeerConnectionBroken;\n\n        /// <summary>\n        /// 对方断开重连\n        /// </summary>\n        event EventHandler<IPeerReconnectedArgs> PeerReconnected;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/AckManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    internal class AckManager\n    {\n        private ulong _currentAck;\n\n        /*\n        public AckManager(IpcContext ipcContext)\n        {\n            IpcContext = ipcContext;\n        }\n\n        private IpcContext IpcContext { get; }\n        */\n\n        public Ack CurrentAck\n        {\n            set\n            {\n                // 又不是啥重要的数据，瞎改就瞎改咯\n                //lock (Locker)\n                //{\n                //    var ack = value.Value;\n                //    if (ack > ulong.MaxValue - ushort.MaxValue) ack = 0;\n\n                //    _currentAck = ack;\n                //}\n\n                var ack = value.Value;\n                if (ack > ulong.MaxValue - ushort.MaxValue)\n                {\n                    // 我预计这是不会进入的\n                    ack = 0;\n                }\n\n                if (ack < _currentAck)\n                {\n                    // 意思一下，加个值\n                    _currentAck++;\n                }\n                else\n                {\n                    _currentAck = ack;\n                }\n            }\n            get\n            {\n                //lock (Locker)\n                //{\n                //    return _currentAck;\n                //}\n                return _currentAck;\n            }\n        }\n\n        /*\n        /// <summary>\n        /// 后续不单独发送 ACK 了，因此就不再需要这个信息了\n        /// </summary>\n        [Obsolete(DebugContext.DoNotUseAck)]\n        // ACK 0x41, 0x43, 0x4B\n        public byte[] AckHeader { get; } = { 0x41, 0x43, 0x4B };\n\n        private object Locker => AckHeader;\n        */\n\n        public Ack GetAck()\n        {\n            //lock (Locker)\n            //{\n            //    CurrentAck = CurrentAck.Value + 1;\n            //    while (AckTaskList.TryGetValue(CurrentAck.Value, out _))\n            //    {\n            //        CurrentAck = CurrentAck.Value + 1;\n            //    }\n            //}\n\n            CurrentAck = CurrentAck.Value + 1;\n            return CurrentAck;\n        }\n\n        /*\n        public bool IsAckMessage(Stream stream, out Ack ack)\n        {\n            var position = stream.Position;\n            if (IsAckMessageInner(stream, out ack))\n            {\n                return true;\n            }\n\n            stream.Position = position;\n            return false;\n        }\n\n        /// <summary>\n        /// 后续不单独发送 ACK 了，因此就不再需要这个信息了\n        /// </summary>\n        [Obsolete(DebugContext.DoNotUseAck)]\n        public byte[] BuildAckMessage(Ack receivedAck)\n        {\n            const int ackLength = sizeof(ulong) + sizeof(ulong);\n            byte[] buffer = new byte[AckHeader.Length + ackLength];\n\n            Array.Copy(AckHeader, buffer, AckHeader.Length);\n\n            var memoryStream = new MemoryStream(buffer)\n            {\n                Position = AckHeader.Length\n            };\n            var binaryWriter = new BinaryWriter(memoryStream);\n            binaryWriter.Write(receivedAck.Value);\n            lock (Locker)\n            {\n                CurrentAck = Math.Max(CurrentAck.Value, receivedAck.Value);\n            }\n\n            CurrentAck = CurrentAck.Value + 1;\n            binaryWriter.Write(CurrentAck.Value);\n\n            return buffer;\n        }\n\n        private bool IsAckMessageInner(Stream stream, out Ack ack)\n        {\n            /*\n             * AckHeader\n             * 回复的 ACK ulong\n             * 推荐的下一次使用的Ack ulong\n             #1#\n            ack = 0;\n            const int ackLength = sizeof(ulong) + sizeof(ulong);\n            if (stream.Length - stream.Position != AckHeader.Length + ackLength) return false;\n\n            if (!IsAckHeader(stream)) return false;\n\n            var binaryReader = new BinaryReader(stream);\n            ack = binaryReader.ReadUInt64();\n            var nextAck = binaryReader.ReadUInt64();\n\n            lock (Locker)\n            {\n                CurrentAck = Math.Max(CurrentAck.Value, nextAck);\n            }\n\n            return true;\n        }\n\n        private bool IsAckHeader(Stream stream)\n        {\n            foreach (var ack in AckHeader)\n            {\n                if (stream.ReadByte() != ack)\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        /// <summary>\n        /// 执行任务，直到收到回复才返回或超时等\n        /// </summary>\n        /// <param name=\"task\"></param>\n        /// <param name=\"peerName\"></param>\n        /// <param name=\"timeout\"></param>\n        /// <param name=\"maxRetryCount\"></param>\n        /// <param name=\"summary\"></param>\n        /// <param name=\"logger\"></param>\n        /// <returns></returns>\n        [Obsolete(DebugContext.DoNotUseAck)]\n        internal async Task<bool> DoWillReceivedAck(Func<Ack, Task> task, string peerName, TimeSpan timeout,\n            uint maxRetryCount, string summary, ILogger logger)\n        {\n            for (uint i = 0; i < maxRetryCount; i++)\n            {\n                var ack = GetAck();\n                var taskCompletionSource = new TaskCompletionSource<bool>();\n\n                logger.Debug($\"[{nameof(AckManager)}.{nameof(DoWillReceivedAck)}] StartSend Count={i} {ack} Summary={summary} PeerName={peerName}\");\n\n                var ackTask = new AckTask(peerName, ack, taskCompletionSource, summary);\n                RegisterAckTask(ackTask);\n\n                // 先注册，然后执行任务，解决任务速度太快，收到消息然后再注册\n                await task(ack);\n\n                await Task.WhenAny(taskCompletionSource.Task, Task.Delay(timeout));\n                taskCompletionSource.TrySetResult(false);\n                if (await taskCompletionSource.Task)\n                {\n                    logger.Debug($\"[{nameof(AckManager)}.{nameof(DoWillReceivedAck)}] Finish Send Count={i} {ack} Summary={summary} PeerName={peerName}\");\n\n                    // 执行完成\n                    return true;\n                }\n                else\n                {\n                    logger.Debug($\"[{nameof(AckManager)}.{nameof(DoWillReceivedAck)}] Fail Send Count={i} {ack} Summary={summary} PeerName={peerName}\");\n                }\n            }\n\n            logger.Debug($\"[{nameof(AckManager)}.{nameof(DoWillReceivedAck)}] Cannot Send Summary={summary} PeerName={peerName}\");\n\n            // 执行失败\n            return false;\n        }\n\n\n        [Obsolete(DebugContext.DoNotUseAck)]\n        internal void RegisterAckTask(AckTask ackTask)\n        {\n            AddToAckTaskList();\n\n            WaitForTask();\n\n            async void WaitForTask()\n            {\n                await ackTask.Task.Task;\n\n                lock (Locker)\n                {\n                    if (AckTaskList.Remove(ackTask.Ack.Value, out var removedTask))\n                    {\n                        Debug.Assert(ReferenceEquals(removedTask, ackTask));\n                    }\n                    else\n                    {\n                        Debug.Assert(false, \"收到的一定存在\");\n                    }\n                }\n            }\n\n            void AddToAckTaskList()\n            {\n                lock (Locker)\n                {\n                    if (AckTaskList.TryAdd(ackTask.Ack.Value, ackTask))\n                    {\n                    }\n                    else\n                    {\n                        // 理论上是找不到的\n                        throw new ArgumentException(\n                            $\"传入的消息的 ack 已经存在 Ack={ackTask.Ack.Value} Summary={ackTask.Summary}\");\n                    }\n                }\n            }\n        }\n\n        private Dictionary<ulong, AckTask> AckTaskList { get; } = new Dictionary<ulong, AckTask>();\n\n        */\n        internal void OnAckReceived(object? sender, AckArgs e)\n        {\n            CurrentAck = e.Ack;\n            /*\n            // 其他的也不用干了\n\n            AckTask ackTask;\n\n            lock (Locker)\n            {\n                if (!AckTaskList.TryGetValue(e.Ack.Value, out ackTask!))\n                {\n                    // 被干掉了，也许是因为等待太久\n                    return;\n                }\n            }\n\n            if (ackTask.PeerName.Equals(e.PeerName))\n            {\n                // 此时也许是等待太久，因此需要使用 Try 方法\n                ackTask.Task.TrySetResult(true);\n            }\n            else\n            {\n                // 不是发生给这个客户端的，只是 ack 相同，这个类被改错\n            }\n            */\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/DebugContext.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Internals\n{\n    class DebugContext\n    {\n        public const string DoNotUseAck = \"当前不再需要回复ACK信息，因为管道通讯形式是不需要获取对方是否收到\";\n\n        public const string OverMaxMessageLength = \"消息内容允许最大的长度。超过这个长度，咋不上天。如果真有那么大的内容准备传的，自己开共享内存或写文件等方式传输，然后通过 IPC 告知对方如何获取即可\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/EmptyIpcRequestHandler.cs",
    "content": "﻿using System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    class EmptyIpcRequestHandler : IIpcRequestHandler\n    {\n        public Task<IIpcResponseMessage> HandleRequest(IIpcRequestContext requestContext)\n        {\n            // 我又不知道业务，不知道怎么玩……\n            var responseMessage = new IpcMessage(nameof(EmptyIpcRequestHandler), new IpcMessageBody(new byte[0]));\n            return Task.FromResult((IIpcResponseMessage) new IpcHandleRequestMessageResult(responseMessage));\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/IClientMessageWriter.cs",
    "content": "﻿using System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    internal interface IClientMessageWriter : IRawMessageWriter\n    {\n        Task WriteMessageAsync(in IpcBufferMessageContext ipcBufferMessageContext);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/IpcHandleRequestMessageResult.cs",
    "content": "﻿using System.Diagnostics;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    class IpcHandleRequestMessageResult : IIpcResponseMessage\n    {\n        [DebuggerStepThrough]\n        public IpcHandleRequestMessageResult(IpcMessage returnMessage)\n        {\n            ResponseMessage = returnMessage;\n        }\n\n        public IpcMessage ResponseMessage { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/IpcMessageConverter.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Diagnostics;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Buffers;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.IO;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    /// <summary>\n    /// 消息的封包和解包代码，用于将传入的内容包装为 Ipc 通讯使用的二进制内容，或将 Ipc 通讯使用的二进制内容读取为业务端使用的内容\n    /// </summary>\n    internal static class IpcMessageConverter\n    {\n        public static async Task WriteAsync(Stream stream, byte[] messageHeader, Ack ack,\n            IpcBufferMessageContext context, ISharedArrayPool pool)\n        {\n            // 准备变量。\n            var commandType = context.IpcMessageCommandType;\n            VerifyMessageLength(context.Length);\n\n            // 发送消息头。\n            await WriteHeaderAsync(stream, messageHeader, ack, commandType, (uint) context.Length, pool)\n                .ConfigureAwait(false);\n\n            // 发送消息体。\n            foreach (var ipcBufferMessage in context.IpcBufferMessageList)\n            {\n                await stream.WriteAsync(ipcBufferMessage.Buffer, ipcBufferMessage.Start, ipcBufferMessage.Length).ConfigureAwait(false);\n            }\n        }\n\n        public static async Task WriteAsync(Stream stream, byte[] messageHeader, Ack ack,\n            IpcMessageCommandType ipcMessageCommandType, byte[] buffer, int offset, int count, ISharedArrayPool pool, string? tag = null)\n        {\n            VerifyMessageLength(count, tag);\n\n            await WriteHeaderAsync(stream, messageHeader, ack, ipcMessageCommandType, (uint) count, pool)\n                .ConfigureAwait(false);\n\n            await stream.WriteAsync(buffer, offset, count);\n        }\n\n        private static void VerifyMessageLength(int messageLength, string? tag = null)\n        {\n            if (messageLength > IpcConfiguration.MaxMessageLength)\n            {\n                throw new ArgumentException($\"Message Length too long  MessageLength={messageLength} MaxMessageLength={IpcConfiguration.MaxMessageLength}. {DebugContext.OverMaxMessageLength}\")\n                {\n                    Data =\n                    {\n                        { \"Message Length\", messageLength },\n                        { \"Tag\", tag ?? string.Empty }\n                    }\n                };\n            }\n        }\n\n        public static async Task WriteHeaderAsync(Stream stream, byte[] messageHeader, Ack ack,\n            IpcMessageCommandType ipcMessageCommandType, UInt32 contentLength, ISharedArrayPool pool)\n        {\n            /*\n            * UInt16 Message Header Length 消息头的长度\n            * byte[] Message Header        消息头的内容\n            * UInt32 Version        当前IPC服务的版本\n            * UInt64 Ack            用于给对方确认收到消息使用\n            * UInt32 Empty          给以后版本使用的值\n            * Int16 Command Type    命令类型，业务端的值将会是 0 而框架层采用其他值\n            * UInt32 Content Length 这条消息的内容长度\n            * byte[] Content        实际的内容\n            */\n\n            // 当前版本默认是 1 版本，这个值用来后续如果有协议上的更改时，兼容旧版本使用\n            // - 版本是 0 的版本，每条消息都有回复 ack 的值\n            const uint version = 1;\n\n            // 统计长度\n            var messageHeaderLength = (ushort) messageHeader.Length;\n\n            var totalByteCount =\n                // UInt16 Message Header Length 消息头的长度\n                sizeof(UInt16)\n                // byte[] Message Header        消息头的内容\n                + messageHeaderLength\n                // UInt32 Version        当前IPC服务的版本\n                + sizeof(UInt32)\n                // UInt64 Ack            用于给对方确认收到消息使用\n                + sizeof(UInt64)\n                // UInt32 Empty          给以后版本使用的值\n                + sizeof(UInt32)\n                // Int16 Command Type    命令类型，业务端的值将会是 0 而框架层采用其他值\n                + sizeof(Int16)\n                // UInt32 Content Length 这条消息的内容长度\n                + sizeof(UInt32);\n\n            var bytes = pool.Rent(totalByteCount);\n            using var memoryStream = new MemoryStream(bytes);\n            using var binaryWriter = new BinaryWriter(memoryStream);\n            // UInt16 Message Header Length 消息头的长度\n            binaryWriter.Write(messageHeaderLength);\n            // byte[] Message Header        消息头的内容\n            binaryWriter.Write(messageHeader);\n            // UInt32 Version\n            binaryWriter.Write(version);\n            // UInt64 Ack\n            binaryWriter.Write(ack.Value);\n            // UInt32 Empty\n            binaryWriter.Write(uint.MinValue);\n            // Int16 Command Type   命令类型，业务端的值将会是 0 而框架层采用其他值\n            var commandType = (ushort) ipcMessageCommandType;\n            binaryWriter.Write(commandType);\n            // UInt32 Content Length 这条消息的内容长度\n            binaryWriter.Write(contentLength);\n\n            await stream.WriteAsync(bytes, 0, totalByteCount).ConfigureAwait(false);\n\n            pool.Return(bytes);\n        }\n\n        public static async Task<StreamReadResult<IpcMessageResult>> ReadAsync(Stream stream,\n            byte[] messageHeader, ISharedArrayPool sharedArrayPool)\n        {\n            /*\n             * UInt16 Message Header Length 消息头的长度\n             * byte[] Message Header        消息头的内容\n             * UInt32 Version        当前IPC服务的版本\n             * UInt64 Ack            用于给对方确认收到消息使用\n             * UInt32 Empty          给以后版本使用的值\n             * Int16 Command Type   命令类型，业务端的值将会是 0 而框架层采用其他值\n             * UInt32 Content Length 这条消息的内容长度\n             * byte[] Content        实际的内容\n             */\n\n            var headerResult = await GetHeader(stream, messageHeader, sharedArrayPool);\n            if (headerResult.IsEndOfStream)\n            {\n                return StreamReadResult<IpcMessageResult>.EndOfStream;\n            }\n\n            if (!headerResult.Result)\n            {\n                // 消息不对，忽略\n                return new StreamReadResult<IpcMessageResult>(new IpcMessageResult(\"Message Header no match\"));\n            }\n\n            var binaryReader = new AsyncBinaryReader(stream, sharedArrayPool);\n            // UInt32 Version        当前IPC服务的版本\n            var versionResult = await binaryReader.ReadUInt32Async();\n            if (versionResult.IsEndOfStream)\n            {\n                return StreamReadResult<IpcMessageResult>.EndOfStream;\n            }\n            var version = versionResult.Result;\n            Debug.Assert(version == 1);\n            if (version == 0)\n            {\n                // 这是上个版本的，但是不兼容了\n                var ipcMessageResult = new IpcMessageResult(\"收到版本为 0 的旧版本消息，但是不兼容此版本\");\n                return new StreamReadResult<IpcMessageResult>(ipcMessageResult);\n            }\n\n            // UInt64 Ack            用于给对方确认收到消息使用\n            var ackResult = await binaryReader.ReadReadUInt64Async();\n            if (ackResult.IsEndOfStream)\n            {\n                return StreamReadResult<IpcMessageResult>.EndOfStream;\n            }\n            var ack = ackResult.Result;\n\n            // UInt32 Empty          给以后版本使用的值\n            var empty = await binaryReader.ReadUInt32Async();\n            Debug.Assert(empty.Result == 0);\n\n            // Int16 Command Type   命令类型，业务端的值将会是大于 0 而框架层采用其他值\n            var commandTypeResult = await binaryReader.ReadUInt16Async();\n            if (commandTypeResult.IsEndOfStream)\n            {\n                return StreamReadResult<IpcMessageResult>.EndOfStream;\n            }\n            var commandType = (IpcMessageCommandType) commandTypeResult.Result;\n\n            // UInt32 Content Length 这条消息的内容长度\n            var messageLengthResult = await binaryReader.ReadUInt32Async();\n            if (messageLengthResult.IsEndOfStream)\n            {\n                return StreamReadResult<IpcMessageResult>.EndOfStream;\n            }\n\n            var messageLength = messageLengthResult.Result;\n            if (messageLength > IpcConfiguration.MaxMessageLength)\n            {\n                // 太长了\n                var ipcMessageResult = new IpcMessageResult(\n                    $\"Message Length too long  MessageLength={messageLength} MaxMessageLength={IpcConfiguration.MaxMessageLength}. {DebugContext.OverMaxMessageLength}\");\n                return new StreamReadResult<IpcMessageResult>(ipcMessageResult);\n            }\n\n            // todo 这里申请的内存看起来没有被释放\n            var messageBuffer = sharedArrayPool.Rent((int) messageLength);\n            // byte[] Content        实际的内容\n            var readCountResult = await ReadBufferAsync(stream, messageBuffer, (int) messageLength);\n            if (readCountResult.IsEndOfStream)\n            {\n                return StreamReadResult<IpcMessageResult>.EndOfStream;\n            }\n\n            var readCount = readCountResult.Result;\n\n            Debug.Assert(readCount == messageLength);\n\n            var ipcMessageContext = new IpcMessageContext(ack, messageBuffer, messageLength, sharedArrayPool);\n            return new StreamReadResult<IpcMessageResult>(new IpcMessageResult(success: true, ipcMessageContext, commandType));\n        }\n\n        private static async Task<StreamReadResult<int>> ReadBufferAsync(Stream stream, byte[] messageBuffer, int messageLength)\n        {\n            var readCount = 0;\n\n            do\n            {\n                var n = await stream.ReadAsync(messageBuffer, readCount, messageLength - readCount);\n\n                if (n == 0)\n                {\n                    return StreamReadResult<int>.EndOfStream;\n                }\n\n                readCount += n;\n            } while (readCount < messageLength);\n\n            return new StreamReadResult<int>(readCount);\n        }\n\n        private static async Task<StreamReadResult<bool>> GetHeader(Stream stream, byte[] messageHeader, ISharedArrayPool sharedArrayPool)\n        {\n            var binaryReader = new AsyncBinaryReader(stream, sharedArrayPool);\n            var messageHeaderLengthResult = await binaryReader.ReadUInt16Async();\n            if (messageHeaderLengthResult.IsEndOfStream)\n            {\n                return StreamReadResult<bool>.EndOfStream;\n            }\n\n            var messageHeaderLength = messageHeaderLengthResult.Result;\n\n            //Debug.Assert(messageHeaderLength == messageHeader.Length);\n            if (messageHeaderLength != messageHeader.Length)\n            {\n                // 消息不对，忽略\n                return new StreamReadResult<bool>(false);\n            }\n\n            var messageHeaderBuffer = sharedArrayPool.Rent(messageHeader.Length);\n\n            try\n            {\n                var readCountResult = await ReadBufferAsync(stream, messageHeaderBuffer, messageHeader.Length);\n                if (readCountResult.IsEndOfStream)\n                {\n                    return StreamReadResult<bool>.EndOfStream;\n                }\n\n                var readCount = readCountResult.Result;\n\n                Debug.Assert(readCount == messageHeader.Length);\n                if (ByteListExtensions.Equals(messageHeaderBuffer, messageHeader, readCount))\n                {\n                    // 读对了\n                    return new StreamReadResult<bool>(true);\n                }\n                else\n                {\n                    // 发过来的消息是出错的\n                    return new StreamReadResult<bool>(false);\n                }\n            }\n            finally\n            {\n                sharedArrayPool.Return(messageHeaderBuffer);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/IpcPipeServerMessageProvider.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Pipes;\nusing System.Runtime.InteropServices;\nusing System.Security.AccessControl;\nusing System.Security.Permissions;\nusing System.Security.Principal;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nusing Microsoft.Win32.SafeHandles;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    /// <summary>\n    /// 提供一个客户端连接\n    /// </summary>\n    internal class IpcPipeServerMessageProvider : IDisposable\n    {\n        public IpcPipeServerMessageProvider(IpcContext ipcContext, IpcServerService ipcServerService)\n        {\n            IpcContext = ipcContext;\n            IpcServerService = ipcServerService;\n        }\n\n        private NamedPipeServerStream NamedPipeServerStream { set; get; } = null!;\n\n        /// <summary>\n        /// 被对方连接\n        /// </summary>\n        private string PeerName => ServerStreamMessageReader?.PeerName ?? \"NoConnected\";\n\n        /// <summary>\n        /// 自身的名字\n        /// </summary>\n        public string PipeName => IpcContext.PipeName;\n\n        public IpcContext IpcContext { get; }\n        public IpcServerService IpcServerService { get; }\n\n\n        public async Task Start()\n        {\n            var namedPipeServerStream = CreateNamedPipeServerStream();\n\n            NamedPipeServerStream = namedPipeServerStream;\n\n            try\n            {\n#if NETCOREAPP\n                await namedPipeServerStream.WaitForConnectionAsync().ConfigureAwait(false);\n#else\n                await Task.Factory.FromAsync(namedPipeServerStream.BeginWaitForConnection,\n                namedPipeServerStream.EndWaitForConnection, null).ConfigureAwait(false);\n#endif\n            }\n            catch (IOException)\n            {\n                // \"管道已结束。\"\n                // 当前服务关闭，此时异常符合预期\n                return;\n            }\n            catch (ObjectDisposedException)\n            {\n                // 当等待客户端连上此服务端期间，被调用了 Dispose 方法后，会抛出此异常。\n                // 日志在 Dispose 方法里记。\n                return;\n            }\n            //var streamMessageConverter = new StreamMessageConverter(namedPipeServerStream,\n            //    IpcConfiguration.MessageHeader, IpcConfiguration.SharedArrayPool);\n            //streamMessageConverter.MessageReceived += OnClientConnectReceived;\n            //StreamMessageConverter = streamMessageConverter;\n            //streamMessageConverter.Start();\n\n            var serverStreamMessageConverter = new ServerStreamMessageReader(IpcContext, NamedPipeServerStream);\n            ServerStreamMessageReader = serverStreamMessageConverter;\n\n            //serverStreamMessageConverter.AckRequested += ServerStreamMessageConverter_AckRequested;\n            serverStreamMessageConverter.AckReceived += IpcContext.AckManager.OnAckReceived;\n            serverStreamMessageConverter.PeerConnected += IpcServerService.OnPeerConnected;\n            serverStreamMessageConverter.MessageReceived += IpcServerService.OnMessageReceived;\n            serverStreamMessageConverter.PeerConnectBroke += (sender, args) =>\n                PeerConnectBroke?.Invoke(sender, new IpcPipeServerMessageProviderPeerConnectionBrokenArgs(this, args));\n\n            serverStreamMessageConverter.Run();\n        }\n\n        /// <summary>\n        /// 创建 NamedPipeServerStream 对象\n        /// </summary>\n        /// <returns></returns>\n        /// 在 Windows 下，支持管理员权限和非管理员权限的进程相互通讯\n        private NamedPipeServerStream CreateNamedPipeServerStream()\n        {\n            NamedPipeServerStream namedPipeServerStream;\n\n#if NETCOREAPP\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                // 用来在 Windows 下，混用管理员权限和非管理员权限的管道\n                SecurityIdentifier securityIdentifier = new SecurityIdentifier(\n                    WellKnownSidType.AuthenticatedUserSid, null);\n\n                PipeSecurity pipeSecurity = new PipeSecurity();\n                pipeSecurity.AddAccessRule(new PipeAccessRule(securityIdentifier,\n                    PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance,\n                    AccessControlType.Allow));\n\n#if NET6_0_OR_GREATER\n                // 这个 NamedPipeServerStreamAcl 是在 .NET 5 引入的\n                namedPipeServerStream = NamedPipeServerStreamAcl.Create\n                (\n                    PipeName,\n                    // 本框架使用两个半工做双向通讯，因此这里只是接收，不做发送\n                    PipeDirection.In,\n                    // 旧框架采用默认为 260 个实例链接，这里减少 10 个，没有具体的理由，待测试\n                    250,\n                    // 默认都采用 byte 方式\n                    PipeTransmissionMode.Byte,\n                    // 采用异步的方式。如果没有设置，默认是同步方式，即使有 Async 的方法，底层也是走同步\n                    PipeOptions.Asynchronous,\n                    inBufferSize: 0, // If it is 0, the buffer size is allocated as needed.\n                    outBufferSize: 0, // If it is 0, the buffer size is allocated as needed.\n                    pipeSecurity\n                    //, HandleInheritability.None 默认值\n                    //, PipeAccessRights.ReadWrite 默认值\n                );\n#else\n                // ===== 仅 .NET Core 3.1 使用：通过 P/Invoke 创建带 PipeSecurity 的命名管道 =====\n                // .NET Core 3.1 上 NamedPipeServerStream 既不接受 PipeSecurity，\n                // 也没有 NamedPipeServerStreamAcl（该 API 仅 .NET 5+ 才在 System.IO.Pipes.AccessControl 中提供）；\n                // 而 System.IO.Pipes.AccessControl 包虽然能装上并暴露 PipesAclExtensions.SetAccessControl，\n                // 但 .NET Core 3.1 上 NamedPipeServerStream 创建出的句柄不带 WRITE_DAC 权限，\n                // 事后再 SetAccessControl 会在 SetSecurityInfo 处抛 UnauthorizedAccessException。\n                // 因此通过 P/Invoke 调用 CreateNamedPipe，在创建时直接传入安全描述符。\n                namedPipeServerStream = CreateNamedPipeServerStreamWithSecurity(PipeName,\n                    // 本框架使用两个半工做双向通讯，因此这里只是接收，不做发送\n                    PipeDirection.In,\n                    // 旧框架采用默认为 260 个实例链接，这里减少 10 个，没有具体的理由，待测试\n                    250,\n                    // 默认都采用 byte 方式\n\n                    PipeTransmissionMode.Byte,\n                    // 采用异步的方式。如果没有设置，默认是同步方式，即使有 Async 的方法，底层也是走同步\n                    PipeOptions.Asynchronous,\n                   inBufferSize: 0, // If it is 0, the buffer size is allocated as needed.\n                    outBufferSize: 0, // If it is 0, the buffer size is allocated as needed.\n                    pipeSecurity);\n#endif\n                return namedPipeServerStream;\n            }\n\n#elif NETFRAMEWORK\n            // .Net Framework 就不再判断 Windows 了\n            SecurityIdentifier securityIdentifier = new SecurityIdentifier(\n                WellKnownSidType.AuthenticatedUserSid, null);\n\n            PipeSecurity pipeSecurity = new PipeSecurity();\n            pipeSecurity.AddAccessRule(new PipeAccessRule(securityIdentifier,\n                PipeAccessRights.ReadWrite | PipeAccessRights.CreateNewInstance,\n                AccessControlType.Allow));\n\n            namedPipeServerStream = new NamedPipeServerStream\n            (\n                PipeName,\n                // 本框架使用两个半工做双向通讯，因此这里只是接收，不做发送\n                PipeDirection.In,\n                // 旧框架采用默认为 260 个实例链接，这里减少 10 个，没有具体的理由，待测试\n                250,\n                // 默认都采用 byte 方式\n                PipeTransmissionMode.Byte,\n                // 采用异步的方式。如果没有设置，默认是同步方式，即使有 Async 的方法，底层也是走同步\n                PipeOptions.Asynchronous,\n                0,\n                0,\n                pipeSecurity\n            );\n            return namedPipeServerStream;\n#endif\n            // 如果非 Windows 平台，或者非 .NET 6 / .Net Framework 应用，那就不加上权限\n            namedPipeServerStream = new NamedPipeServerStream\n        (\n            PipeName,\n            // 本框架使用两个半工做双向通讯，因此这里只是接收，不做发送\n            PipeDirection.In,\n            // 旧框架采用默认为 260 个实例链接，这里减少 10 个，没有具体的理由，待测试\n            250,\n            // 默认都采用 byte 方式\n            PipeTransmissionMode.Byte,\n            // 采用异步的方式。如果没有设置，默认是同步方式，即使有 Async 的方法，底层也是走同步\n            PipeOptions.Asynchronous\n        );\n\n            return namedPipeServerStream;\n        }\n\n        /*\n        private void ServerStreamMessageConverter_AckRequested(object? sender, Ack e)\n        {\n            SendAck(e);\n        }\n        */\n        private ServerStreamMessageReader? ServerStreamMessageReader { set; get; }\n        public event EventHandler<IpcPipeServerMessageProviderPeerConnectionBrokenArgs>? PeerConnectBroke;\n\n        /*\n        private async void SendAck(Ack receivedAck) => await SendAckAsync(receivedAck);\n\n        private async Task SendAckAsync(Ack receivedAck)\n        {\n            IpcContext.Logger.Debug($\"[{nameof(IpcServerService)}] SendAck {receivedAck} to {PeerName}\");\n            var ipcProvider = IpcContext.IpcProvider;\n            var peerProxy = await ipcProvider.GetOrCreatePeerProxyAsync(PeerName);\n            var ipcClient = peerProxy.IpcClientService;\n            await ipcClient.SendAckAsync(receivedAck);\n        }\n        */\n\n        public void Dispose()\n        {\n            if (ServerStreamMessageReader is null)\n            {\n                // 证明此时还没完全连接\n                NamedPipeServerStream.Dispose();\n            }\n            else\n            {\n                // 证明已连接完成，此时不需要释放 NamedPipeServerStream 类\n                // 不在这一层释放 NamedPipeServerStream 类\n                ServerStreamMessageReader.Dispose();\n            }\n        }\n\n#if NETCOREAPP && !NET5_0_OR_GREATER\n        // ===== 仅 .NET Core 3.1 使用：通过 P/Invoke 创建带 PipeSecurity 的命名管道 =====\n        private const uint PIPE_ACCESS_DUPLEX = 0x00000003;\n        private const uint PIPE_ACCESS_INBOUND = 0x00000001;\n        private const uint PIPE_ACCESS_OUTBOUND = 0x00000002;\n        private const uint FILE_FLAG_OVERLAPPED = 0x40000000;\n        private const uint FILE_FLAG_WRITE_THROUGH = 0x80000000;\n        private const uint PIPE_TYPE_BYTE = 0x00000000;\n        private const uint PIPE_TYPE_MESSAGE = 0x00000004;\n\n        [StructLayout(LayoutKind.Sequential)]\n        struct SECURITY_ATTRIBUTES\n        {\n            public int nLength;\n            public IntPtr lpSecurityDescriptor;\n            public int bInheritHandle;\n        }\n\n        [DllImport(\"kernel32.dll\", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = \"CreateNamedPipeW\")]\n        private static extern SafePipeHandle CreateNamedPipe(\n            string lpName,\n            uint dwOpenMode,\n            uint dwPipeMode,\n            uint nMaxInstances,\n            uint nOutBufferSize,\n            uint nInBufferSize,\n            uint nDefaultTimeOut,\n            ref SECURITY_ATTRIBUTES lpSecurityAttributes);\n\n        /// <summary>\n        /// 通过 Win32 CreateNamedPipe 创建带访问控制的命名管道，并包装为 <see cref=\"NamedPipeServerStream\"/>。\n        /// 这是 .NET Core 3.1 上唯一能在创建时附带 <see cref=\"PipeSecurity\"/> 的方式。\n        /// </summary>\n        private static NamedPipeServerStream CreateNamedPipeServerStreamWithSecurity(\n            string pipeName, PipeDirection direction, int maxInstances,\n            PipeTransmissionMode transmissionMode, PipeOptions options,\n            int inBufferSize, int outBufferSize, PipeSecurity pipeSecurity)\n        {\n            uint openMode;\n            switch (direction)\n            {\n                case PipeDirection.In: openMode = PIPE_ACCESS_INBOUND; break;\n                case PipeDirection.Out: openMode = PIPE_ACCESS_OUTBOUND; break;\n                default: openMode = PIPE_ACCESS_DUPLEX; break;\n            }\n\n            if ((options & PipeOptions.Asynchronous) != 0) openMode |= FILE_FLAG_OVERLAPPED;\n            if ((options & PipeOptions.WriteThrough) != 0) openMode |= FILE_FLAG_WRITE_THROUGH;\n\n            uint pipeMode = transmissionMode == PipeTransmissionMode.Message\n                ? PIPE_TYPE_MESSAGE\n                : PIPE_TYPE_BYTE;\n\n            // 将托管 PipeSecurity 转为原生安全描述符\n            var sdBytes = pipeSecurity.GetSecurityDescriptorBinaryForm();\n            GCHandle pinned = GCHandle.Alloc(sdBytes, GCHandleType.Pinned);\n            try\n            {\n                var sa = new SECURITY_ATTRIBUTES\n                {\n                    nLength = Marshal.SizeOf(typeof(SECURITY_ATTRIBUTES)),\n                    lpSecurityDescriptor = pinned.AddrOfPinnedObject(),\n                    bInheritHandle = 0,\n                };\n\n                var fullName = @\"\\\\.\\pipe\\\" + pipeName;\n                var handle = CreateNamedPipe(fullName, openMode, pipeMode,\n                    (uint) maxInstances, (uint) outBufferSize, (uint) inBufferSize, 0, ref sa);\n\n                if (handle.IsInvalid)\n                {\n                    var error = Marshal.GetLastWin32Error();\n                    handle.Dispose();\n                    // 与原构造函数行为保持一致：管道已存在 / 被占用时抛 IOException，\n                    // 权限不足时抛 UnauthorizedAccessException，外层 catch 会处理。\n                    const int ERROR_ACCESS_DENIED = 5;\n                    if (error == ERROR_ACCESS_DENIED)\n                    {\n                        throw new UnauthorizedAccessException();\n                    }\n\n                    throw new IOException(\"CreateNamedPipe 失败，Win32 错误码：\" + error);\n                }\n\n                var isAsync = (options & PipeOptions.Asynchronous) != 0;\n                return new NamedPipeServerStream(direction, isAsync, false, handle);\n            }\n            finally\n            {\n                pinned.Free();\n            }\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/PeerManager.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Threading.Tasks;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    /// <summary>\n    /// 管理所有连接方\n    /// </summary>\n    public interface IPeerManager\n    {\n        /// <summary>\n        /// 和 <see cref=\"IpcProvider\"/> 不同的是，无论是主动连接还是被动连过来的，都会触发此事件\n        /// </summary>\n        event EventHandler<PeerConnectedArgs>? PeerConnected;\n\n        /// <summary>\n        /// 当前连接的数量。无论是主动连接的还是被连接的对方都会记录在此。此属性仅有记日志的作用。由于 IPC 将会不断多进程连接和断开，所以这个数量是不断变化的。可能获取的一刻，实际情况就和此不相同\n        /// </summary>\n        int CurrentConnectedPeerProxyCount { get; }\n\n        /// <summary>\n        /// 获取当前连接到的 <see cref=\"PeerProxy\"/> 列表。无论是主动连接的还是被连接的对方都会记录在此。仅表示获取时的状态，由于 IPC 将会不断多进程连接和断开，所以这个列表是不断变化的。可能获取的一刻，实际情况就和此不相同\n        /// </summary>\n        /// <returns></returns>\n        IReadOnlyList<PeerProxy> GetCurrentConnectedPeerProxyList();\n    }\n\n    class PeerManager : IPeerManager, IDisposable\n    {\n        public PeerManager(IpcProvider ipcProvider)\n        {\n            _ipcProvider = ipcProvider;\n        }\n\n        public bool TryAdd(PeerProxy peerProxy)\n        {\n            peerProxy.PeerConnectionBroken += PeerProxy_PeerConnectionBroken;\n            OnAdd(peerProxy);\n            return ConnectedPeerProxyDictionary.TryAdd(peerProxy.PeerName, peerProxy);\n        }\n\n        public bool TryGetValue(string key, [NotNullWhen(true)] out PeerProxy? peer)\n        {\n            return ConnectedPeerProxyDictionary.TryGetValue(key, out peer);\n        }\n\n        /// <summary>\n        /// 删除断开的对方\n        /// </summary>\n        /// <param name=\"peerProxy\"></param>\n        public void RemovePeerProxy(PeerProxy peerProxy)\n        {\n            if (!peerProxy.IsBroken)\n            {\n                throw new ArgumentException($\"Must remove the Broken peer. PeerName={peerProxy.PeerName}\");\n            }\n\n            ConnectedPeerProxyDictionary.TryRemove(peerProxy.PeerName, out var value);\n\n            if (ReferenceEquals(peerProxy, value) || value is null)\n            {\n                // 这是预期的\n            }\n            else\n            {\n                // 居然放在列表里面的，和当前断开连接的不是相同的 Peer 那么将此加入回去\n                if (Debugger.IsAttached)\n                {\n                    // 请将德熙叫过来，理论上不会进入这个分支\n                    Debugger.Break();\n                    throw new InvalidOperationException(\n                        $\"Peer 断开之后，从已有列表删除时发现列表里面记录的 Peer 和当前的不是相同的一个。仅调试下抛出。PeerName={peerProxy.PeerName}\");\n                }\n\n                ConnectedPeerProxyDictionary.TryAdd(value.PeerName, value);\n            }\n        }\n\n        /// <summary>\n        /// 等待对方连接完成\n        /// </summary>\n        /// <param name=\"peerProxy\"></param>\n        /// <returns></returns>\n        public async Task WaitForPeerConnectFinishedAsync(PeerProxy peerProxy)\n        {\n            await peerProxy.WaitForFinishedTaskCompletionSource.Task;\n\n            OnAdd(peerProxy);\n            // 更新或注册，用于解决之前注册的实际上是断开的连接\n            ConnectedPeerProxyDictionary.AddOrUpdate(peerProxy.PeerName, peerProxy, (s, proxy) => proxy);\n        }\n\n        private void OnAdd(PeerProxy peerProxy)\n        {\n            if (AutoReconnectPeers)\n            {\n                peerProxy.PeerReConnector ??= new PeerReConnector(peerProxy, _ipcProvider);\n\n                peerProxy.PeerReConnector.ReconnectFail -= PeerReConnector_ReconnectFail;\n                peerProxy.PeerReConnector.ReconnectFail += PeerReConnector_ReconnectFail;\n            }\n\n            if (!ConnectedPeerProxyDictionary.ContainsKey(peerProxy.PeerName))\n            {\n                // 没有从字典找到，证明是首次连接到的。或曾经断开过的，此后再连接的\n                PeerConnected?.Invoke(this, new PeerConnectedArgs(peerProxy));\n            }\n        }\n\n        /// <inheritdoc />\n        public event EventHandler<PeerConnectedArgs>? PeerConnected;\n\n        /// <inheritdoc />\n        public int CurrentConnectedPeerProxyCount => ConnectedPeerProxyDictionary.Count;\n\n        /// <inheritdoc />\n        public IReadOnlyList<PeerProxy> GetCurrentConnectedPeerProxyList()\n        {\n            // 这里是线程安全的，但只会返回当前的状态\n            return ConnectedPeerProxyDictionary.Values.ToList();\n        }\n\n        private void PeerReConnector_ReconnectFail(object? sender, ReconnectFailEventArgs e)\n        {\n            // 重新连接失败，此时需要清理\n            // 如果不清理，将会导致过了一会，有新的连接进来，使用和上次需要重连相同的 PeerName 的不会再次触发事件\n            RemovePeerProxy(e.PeerProxy);\n        }\n\n        private bool AutoReconnectPeers => _ipcProvider.IpcServerService.IpcContext.IpcConfiguration.AutoReconnectPeers;\n\n        public void Dispose()\n        {\n            foreach (var pair in ConnectedPeerProxyDictionary)\n            {\n                var peer = pair.Value;\n                // 为什么 PeerProxy 不加上 IDisposable 方法\n                // 因为这个类在上层业务使用，被上层业务调释放就可以让框架不能使用\n                peer.DisposePeer();\n            }\n        }\n\n        private ConcurrentDictionary<string/*PeerName*/, PeerProxy> ConnectedPeerProxyDictionary { get; } =\n            new ConcurrentDictionary<string, PeerProxy>();\n        private readonly IpcProvider _ipcProvider;\n\n        private void PeerProxy_PeerConnectionBroken(object? sender, IPeerConnectionBrokenArgs e)\n        {\n            if (AutoReconnectPeers)\n            {\n                // 如果需要自动连接，那么就不需要删除记录\n                return;\n            }\n\n            var peerProxy = (PeerProxy) sender!;\n            RemovePeerProxy(peerProxy);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/PeerReConnector.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    /// <summary>\n    /// 重新连接器\n    /// </summary>\n    class PeerReConnector\n    {\n        public PeerReConnector(PeerProxy peerProxy, IpcProvider ipcProvider)\n        {\n            _peerProxy = peerProxy;\n            _ipcProvider = ipcProvider;\n\n            peerProxy.PeerConnectionBroken += PeerProxy_PeerConnectionBroken;\n        }\n\n        private void PeerProxy_PeerConnectionBroken(object? sender, IPeerConnectionBrokenArgs e)\n        {\n            Reconnect();\n        }\n\n        private readonly PeerProxy _peerProxy;\n        private readonly IpcProvider _ipcProvider;\n\n        public event EventHandler<ReconnectFailEventArgs>? ReconnectFail;\n\n        private async void Reconnect()\n        {\n            try\n            {\n                var ipcClientService = _ipcProvider.CreateIpcClientService(_peerProxy.PeerName);\n                var success = await TryReconnectAsync(ipcClientService);\n\n                if (success)\n                {\n                    _peerProxy.Reconnect(ipcClientService);\n                }\n                else\n                {\n                    _ipcProvider.IpcContext.Logger.Error($\"[PeerReConnector][Reconnect] Fail. PeerName={_peerProxy.PeerName}\");\n\n                    ReconnectFail?.Invoke(this, new ReconnectFailEventArgs(_peerProxy, _ipcProvider));\n                }\n            }\n            catch (Exception e)\n            {\n                // 线程顶层，吃掉所有的异常\n                _ipcProvider.IpcContext.Logger.Error(e, $\"[PeerReConnector][Reconnect] Reconnect Peer Fail. PeerName={_peerProxy.PeerName}\");\n            }\n        }\n\n        private async Task<bool> TryReconnectAsync(IpcClientService ipcClientService)\n        {\n            for (int i = 0; i < 16; i++)\n            {\n                try\n                {\n                    return await ipcClientService.StartInternalAsync(isReConnect: true, shouldRegisterToPeer: true, onlyConnectToExistingPeer: false);\n                }\n                // ## 此异常有两种\n                catch (FileNotFoundException)\n                {\n                    // 1. 一种来自于 namedPipeClientStream.ConnectAsync()，刚调用时还能获取到管道句柄，但马上与之连接时便已断开。\n                    await Task.Delay(16);\n                }\n                catch (IOException)\n                {\n                    // 2. 另一种来自 RegisterToPeer()，前面已经连上了，但试图发消息时便已断开。\n                    await Task.Delay(16);\n                }\n                // 不会出现此异常。原本是通过异常控制是否成功，才需要判断\n                //catch (IpcClientPipeConnectionException exception)\n                //{\n                //    // 业务层判断不能重新连接了，必定失败\n                //    // 返回就可以了\n                //    _ipcProvider.IpcContext.Logger.Error($\"[PeerReConnector][Reconnect][IpcClientPipeConnectionException] {exception}\");\n                //    return false;\n                //}\n                catch (Exception exception)\n                {\n                    // 未知的异常，不再继续\n                    _ipcProvider.IpcContext.Logger.Error($\"[PeerReConnector][Reconnect]{exception}\");\n                    return false;\n                }\n                // ## 然而，为什么一连上就断开了呢？\n                //\n                // 这是因为每个端有两条管道，各自作为服务端和客户端。\n                // 当重连时，靠的是服务端管道读到末尾来判断的；但此时重连的却是客户端。\n                // 有极少情况下，这两条的断开时间间隔足够长到本方法的客户端已开始重连。\n                // 那么，本方法的客户端在一开始试图连接对方时连上了，但随即就完成了之前没完成的断开，于是出现 FileNotFoundException。\n                //\n                // ## 那么，如何解决呢？\n                //\n                // 通过重连，我们可以缓解因对方正在断开导致的我们误连。通过重连多次，可以更大概率缓解以至于解决此异常。\n                //\n                // ## 是否有后续问题？\n                //\n                // 有可能本方法已全部完成之后才断开吗？不可能，因为 RegisterToPeer() 会发消息的，如果是对方进程退出等原因导致的断连，那么消息根本就无法发送。\n                // 因为本调用内会置一个 TaskCompleteSource，所以也会导致一起等待此任务的其他发送全部失败，而解决方法就是在其他发送处也重试。\n            }\n\n            return false;\n        }\n    }\n\n    class ReconnectFailEventArgs : EventArgs\n    {\n        public ReconnectFailEventArgs(PeerProxy peerProxy, IpcProvider ipcProvider)\n        {\n            PeerProxy = peerProxy;\n            IpcProvider = ipcProvider;\n        }\n\n        public PeerProxy PeerProxy { get; }\n        public IpcProvider IpcProvider { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/PeerRegisterProvider.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Text;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    /// <summary>\n    /// 用于进行 Ipc 连接时的建立通讯，建立通讯的时候需要向对方发送自己的管道名，用于让对方连接\n    /// </summary>\n    internal class PeerRegisterProvider\n    {\n        public IpcBufferMessageContext BuildPeerRegisterMessage(string peerName)\n        {\n            /*\n             * byte[] Header\n             * Int32 PipeNameLength\n             * byte[] PipeName\n             */\n            var peerRegisterHeaderIpcBufferMessage = new IpcMessageBody(PeerRegisterHeader);\n            var buffer = Encoding.UTF8.GetBytes(peerName);\n\n            var peerNameLengthBufferMessage = new IpcMessageBody(BitConverter.GetBytes(buffer.Length));\n            var peerNameIpcBufferMessage = new IpcMessageBody(buffer);\n\n            return new IpcBufferMessageContext($\"PeerRegisterMessage PipeName={peerName}\",\n                IpcMessageCommandType.PeerRegister, peerRegisterHeaderIpcBufferMessage, peerNameLengthBufferMessage,\n                peerNameIpcBufferMessage);\n        }\n\n        public bool TryParsePeerRegisterMessage(Stream stream, out string peerName)\n        {\n            var position = stream.Position;\n            var isPeerRegisterMessage = TryParsePeerRegisterMessageInner(stream, position, out peerName);\n            if (isPeerRegisterMessage)\n            {\n                return true;\n            }\n            else\n            {\n                stream.Position = position;\n                return false;\n            }\n        }\n\n        private bool TryParsePeerRegisterMessageInner(Stream stream, long position, out string peerName)\n        {\n            peerName = string.Empty;\n            if (stream.Length - position <= PeerRegisterHeader.Length)\n            {\n                return false;\n            }\n\n            for (var i = 0; i < PeerRegisterHeader.Length; i++)\n            {\n                if (stream.ReadByte() != PeerRegisterHeader[i])\n                {\n                    return false;\n                }\n            }\n\n            var binaryReader = new BinaryReader(stream);\n            var peerNameLength = binaryReader.ReadInt32();\n            peerName = new string(binaryReader.ReadChars(peerNameLength));\n            return true;\n        }\n\n        // PeerRegisterHeader 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x20\n        private byte[] PeerRegisterHeader { get; } =\n        {\n            0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72,\n            0x20\n        };\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Internals/ServerStreamMessageReader.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Context.LoggingContext;\nusing dotnetCampus.Ipc.Diagnostics;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.IO;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Internals\n{\n    /// <summary>\n    /// 基础的数据读取\n    /// </summary>\n    [DebuggerDisplay(\"ServerStreamMessageReader [{\" + nameof(IpcContext) + \"}]\")]\n    class ServerStreamMessageReader : IDisposable\n    {\n        public ServerStreamMessageReader(IpcContext ipcContext, Stream stream)\n        {\n            IpcContext = ipcContext;\n            Stream = stream;\n        }\n\n        public IpcContext IpcContext { get; }\n        private ILogger Logger => IpcContext.Logger;\n\n        /// <summary>\n        /// 被对方连接的对方设备名\n        /// </summary>\n        public string PeerName { set; get; } = null!;\n\n        public bool IsConnected => !string.IsNullOrEmpty(PeerName);\n\n        private IpcConfiguration IpcConfiguration => IpcContext.IpcConfiguration;\n        private Stream Stream { get; }\n\n        /*\n        /// <summary>\n        /// 请求发送回复的 ack 消息\n        /// </summary>\n        [Obsolete(DebugContext.DoNotUseAck)]\n        internal event EventHandler<Ack>? AckRequested;\n        */\n\n        /// <summary>\n        /// 当收到对方确定收到消息时触发\n        /// </summary>\n        public event EventHandler<AckArgs>? AckReceived;\n\n        /// <summary>\n        /// 当收到消息时触发\n        /// </summary>\n        public event EventHandler<PeerStreamMessageArgs>? MessageReceived;\n\n        /// <summary>\n        /// 当有对方连接时触发\n        /// </summary>\n        public event EventHandler<IpcInternalPeerConnectedArgs>? PeerConnected;\n\n        public async void Run()\n        {\n            try\n            {\n                // 开始跑的时候一定还没有相连的 RemotePeer 因此 PeerName 一定是空\n                Debug.Assert(string.IsNullOrEmpty(PeerName));\n                IpcContext.Logger.Debug($\"[ServerStreamMessageReader][Run] Start Run. LocalPeerName={IpcContext.PipeName}\");\n                await RunAsync().ConfigureAwait(false);\n            }\n            catch (Exception e)\n            {\n                // 当前是后台线程了，不能接受任何的抛出\n                Logger.Error(e, $\"[ServerStreamMessageReader][Run] Exception={e.Message}; LocalPeerName={IpcContext.PipeName}; RemotePeerName={PeerName};\");\n            }\n        }\n\n        public async Task RunAsync()\n        {\n            while (!_isDisposed)\n            {\n                try\n                {\n                    var ipcMessageResult = await IpcMessageConverter.ReadAsync(Stream,\n                        IpcConfiguration.MessageHeader,\n                        IpcConfiguration.SharedArrayPool).ConfigureAwait(false);\n\n                    if (ipcMessageResult.IsEndOfStream)\n                    {\n                        IpcContext.Logger.Information($\"[ServerStreamMessageReader][PeerConnectBroke] 对方已关闭 LocalPeerName={IpcContext.PipeName}; RemotePeerName={PeerName};\");\n\n                        OnPeerConnectBroke(new PeerConnectionBrokenArgs());\n                        return;\n                    }\n\n                    DispatchMessage(ipcMessageResult.Result);\n                }\n                catch (EndOfStreamException)\n                {\n#if DEBUG\n                    // 理论上不会再抛此异常\n                    if (Debugger.IsAttached)\n                    {\n                        Debugger.Break();\n                    }\n#endif\n                    //// 对方关闭了\n                    //// [断开某个进程 使用大量CPU在读取 · Issue #15 · dotnet-campus/dotnetCampus.Ipc](https://github.com/dotnet-campus/dotnetCampus.Ipc/issues/15 )\n                    //IpcContext.Logger.Error($\"对方已关闭\");\n\n                    //OnPeerConnectBroke(new PeerConnectionBrokenArgs());\n                    //return;\n\n                    throw;\n                }\n                catch (Exception e)\n                {\n                    if (e is ObjectDisposedException)\n                    {\n                        if (_isDisposed)\n                        {\n                            // 符合预期\n                            // A 线程调用 Dispose 方法释放 Stream 属性\n                            // B 线程刚好正在读取内容\n                            // 此时将会在 IpcMessageConverter 收到 ObjectDisposedException 异常\n                        }\n                        else\n                        {\n                            // 不符合预期，莫名被释放了\n#if DEBUG\n                            Debugger.Break();\n#endif\n                            IpcContext.Logger.Error(e, $\"[ServerStreamMessageReader][Error] ObjectDisposedException without _isDisposed. LocalPeerName={IpcContext.PipeName}; RemotePeerName={PeerName};\");\n                        }\n\n                        OnPeerConnectBroke(new PeerConnectionBrokenArgs());\n                    }\n                    else\n                    {\n                        IpcContext.Logger.Error(e, $\"[ServerStreamMessageReader][Error] Exception={e.Message};LocalPeerName={IpcContext.PipeName}; RemotePeerName={PeerName};\");\n                    }\n                }\n            }\n        }\n\n        /// <summary>\n        /// 调度消息\n        /// </summary>\n        /// <param name=\"ipcMessageResult\"></param>\n        private void DispatchMessage(IpcMessageResult ipcMessageResult)\n        {\n            var success = ipcMessageResult.Success;\n            var ipcMessageContext = ipcMessageResult.IpcMessageContext;\n            var ipcMessageCommandType = ipcMessageResult.IpcMessageCommandType;\n\n            if (!success)\n            {\n                // 没有成功哇\n                CriticalTrackReceiveCore(ipcMessageResult, \"接收消息未成功\");\n                return;\n            }\n\n            IpcContext.LogReceiveOriginMessage(ipcMessageResult, PeerName);\n\n            var stream = new ByteListMessageStream(ipcMessageContext);\n\n            if (ipcMessageCommandType.HasFlag(IpcMessageCommandType.PeerRegister))\n            {\n                var isPeerRegisterMessage = IpcContext.PeerRegisterProvider.TryParsePeerRegisterMessage(stream, out var peerName);\n                // 下面这条消息是不需要发出的，这是调度开始的消息，很多都没准备完成。替换为使用 Logger 输出日志\n                //var tracker = CriticalTrackReceiveCore(ipcMessageResult, peerName);\n\n                if (IsConnected)\n                {\n                    // 对方是不是挂了？居然重复注册\n                    // 注册的逻辑可是框架层做的哦，业务层可决定不了\n                    // 可能存在的原因是注册的时候，本进程太忙碌，于是对方连续给了两条注册消息过来，这也是预期的\n\n                    if (string.Equals(PeerName, peerName))\n                    {\n                        // 也许是对方发送两条注册消息过来\n                        Logger.Warning($\"[DispatchMessage] PeerRegister message receive twice. 注册消息收到两次 PeerName={PeerName}\");\n                    }\n                    else\n                    {\n                        // 对方想改名而已，然而这是不允许的\n                        Logger.Warning($\"[DispatchMessage] PeerRegister message receive twice. 注册消息收到两次 OldPeerName={PeerName} ;NewPeerName={peerName}\");\n                    }\n                }\n\n                if (isPeerRegisterMessage)\n                {\n                    PeerName = peerName;\n\n                    OnPeerConnected(new IpcInternalPeerConnectedArgs(peerName, Stream, ipcMessageContext.Ack,\n                        this));\n\n                    if (string.IsNullOrEmpty(peerName))\n                    {\n                        // 这难道是 lsj 的号？居然名字是空的\n                    }\n                }\n                else\n                {\n                    // 版本不对？消息明明是说注册的，然而解析失败\n                }\n\n                stream.Dispose();\n            }\n            // 只有业务的才能发给上层\n            else if (ipcMessageCommandType.HasFlag(IpcMessageCommandType.Business))\n            {\n                CriticalTrackReceiveCore(ipcMessageResult, \"无法识别的端\");\n                if (IsConnected)\n                {\n                    IpcContext.LogReceiveMessage(stream, PeerName);\n\n                    OnMessageReceived(new PeerStreamMessageArgs(ipcMessageContext, PeerName, stream, ipcMessageContext.Ack, ipcMessageCommandType));\n                }\n                else\n                {\n                    // 还没注册完成哇\n                    Logger.Warning(\"[DispatchMessage] Receive business message before Connected. 在建立连接之前收到业务消息\");\n                }\n            }\n            else\n            {\n                CriticalTrackReceiveCore(ipcMessageResult, \"无法识别的消息\");\n                // 不知道这是啥消息哇\n                // 但是更新一下 ack 意思一下还可以\n                OnAckReceived(new AckArgs(PeerName, ipcMessageContext.Ack));\n\n                stream.Dispose();\n            }\n        }\n\n        [Conditional(\"DEBUG\")]\n        private void CriticalTrackReceiveCore(IpcMessageResult result, string message)\n        {\n            var tracker = new IpcMessageTracker<IpcMessageContext>(\n                IpcContext.PipeName,\n                PeerName,\n                result.IpcMessageContext,\n                \"DispatchMessage \" + message,\n                IpcContext.Logger);\n            tracker.CriticalStep(\"ReceiveCore\",\n                result.IpcMessageContext.Ack,\n                new IpcMessageBody(result.IpcMessageContext.MessageBuffer, 0,\n                    (int) result.IpcMessageContext.MessageLength));\n        }\n\n#if false // 下面是注释的代码\n        private async Task WaitForConnectionAsync()\n        {\n            while (!_isDisposed)\n            {\n                try\n                {\n                    var ipcMessageResult = await IpcMessageConverter.ReadAsync(Stream,\n                        IpcConfiguration.MessageHeader,\n                        IpcConfiguration.SharedArrayPool).ConfigureAwait(false);\n                    var success = ipcMessageResult.Success;\n                    var ipcMessageContext = ipcMessageResult.IpcMessageContext;\n\n                    // 这不是业务消息\n                    Debug.Assert(!ipcMessageResult.IpcMessageCommandType.HasFlag(IpcMessageCommandType.Business));\n\n                    if (success)\n                    {\n                        var stream = new ByteListMessageStream(ipcMessageContext);\n\n                        var isPeerRegisterMessage =\n                            IpcContext.PeerRegisterProvider.TryParsePeerRegisterMessage(stream, out var peerName);\n\n                        if (isPeerRegisterMessage)\n                        {\n                            // ReSharper disable once MethodHasAsyncOverload\n                            PeerName = peerName;\n\n                            OnPeerConnected(new IpcInternalPeerConnectedArgs(peerName, Stream, ipcMessageContext.Ack,\n                                this));\n\n                            //SendAckAndRegisterToPeer(ipcMessageContext.Ack);\n                            //SendAck(ipcMessageContext.Ack);\n                            //// 不等待对方收到，因为对方也在等待\n                            ////await SendAckAsync(ipcMessageContext.Ack);\n                        }\n\n                        // 如果是 对方的注册消息 同时也许是回应的消息，所以不能加上 else if 判断\n                        if (IpcContext.AckManager.IsAckMessage(stream, out var ack))\n                        {\n                            // 只有作为去连接对方的时候，才会收到这个消息\n                            IpcContext.Logger.Debug($\"[{nameof(IpcServerService)}] AckReceived {ack} From {PeerName} 收到SendAckAndRegisterToPeer消息\");\n                            OnAckReceived(new AckArgs(PeerName, ack));\n\n                            if (isPeerRegisterMessage)\n                            {\n                                // 这是一条本地主动去连接对方，然后收到对方的反过来的连接的信息，此时需要回复对方\n                                // 参阅 SendAckAndRegisterToPeer 方法的实现\n                                //SendAck(ipcMessageContext.Ack);\n                                OnAckRequested(ipcMessageContext.Ack);\n                            }\n                        }\n                        else\n                        {\n                            // 后续需要要求重发设备名\n                        }\n\n                        if (isPeerRegisterMessage)\n                        {\n                            // 收到注册消息了\n                            break;\n                        }\n                    }\n                }\n                catch (EndOfStreamException)\n                {\n                    // 对方关闭了\n                    // [断开某个进程 使用大量CPU在读取 · Issue #15 · dotnet-campus/dotnetCampus.Ipc](https://github.com/dotnet-campus/dotnetCampus.Ipc/issues/15 )\n                    IpcContext.Logger.Error($\"对方已关闭\");\n                    return;\n                }\n                catch (Exception e)\n                {\n                    IpcContext.Logger.Error(e);\n                }\n            }\n        }\n\n        private async Task ReadMessageAsync()\n        {\n            while (!_isDisposed)\n            {\n                try\n                {\n                    var ipcMessageResult = await IpcMessageConverter.ReadAsync(Stream,\n                        IpcConfiguration.MessageHeader,\n                        IpcConfiguration.SharedArrayPool);\n                    var success = ipcMessageResult.Success;\n                    var ipcMessageContext = ipcMessageResult.IpcMessageContext;\n                    var ipcMessageCommandType = ipcMessageResult.IpcMessageCommandType;\n\n                    if (success)\n                    {\n                        var stream = new ByteListMessageStream(ipcMessageContext);\n\n                        if (ipcMessageCommandType.HasFlag(IpcMessageCommandType.SendAck) && IpcContext.AckManager.IsAckMessage(stream, out var ack))\n                        {\n                            IpcContext.Logger.Debug($\"[{nameof(IpcServerService)}] AckReceived {ack} From {PeerName}\");\n                            OnAckReceived(new AckArgs(PeerName, ack));\n                            // 如果是收到 ack 回复了，那么只需要向 AckManager 注册\n                            Debug.Assert(ipcMessageContext.Ack.Value == IpcContext.AckUsedForReply.Value);\n                        }\n                        // 只有业务的才能发给上层\n                        else if (ipcMessageCommandType.HasFlag(IpcMessageCommandType.Business))\n                        {\n                            ack = ipcMessageContext.Ack;\n                            OnAckRequested(ack);\n                            OnMessageReceived(new PeerMessageArgs(PeerName, stream, ack, ipcMessageCommandType));\n                        }\n                        else\n                        {\n                            // 有不能解析的信息，后续需要告诉开发\n                            // 依然回复一条 Ack 消息给对方，让对方不用重复发送\n                            OnAckRequested(ipcMessageContext.Ack);\n                        }\n                    }\n                }\n                catch (EndOfStreamException)\n                {\n                    // 对方关闭了\n                    // [断开某个进程 使用大量CPU在读取 · Issue #15 · dotnet-campus/dotnetCampus.Ipc](https://github.com/dotnet-campus/dotnetCampus.Ipc/issues/15 )\n                    IpcContext.Logger.Error($\"对方已关闭\");\n\n                    OnPeerConnectBroke(new PeerConnectionBrokenArgs());\n                    return;\n                }\n                catch (Exception e)\n                {\n                    IpcContext.Logger.Error(e);\n                }\n            }\n        }\n\n        [Obsolete(DebugContext.DoNotUseAck)]\n        private void OnAckRequested(in Ack e)\n        {\n            throw new NotSupportedException(DebugContext.DoNotUseAck);\n\n            Logger.Debug($\"[{nameof(ServerStreamMessageReader)}][{nameof(OnAckRequested)}] 请求回复 Ack 消息 {e}\");\n            AckRequested?.Invoke(this, e);\n        }\n#endif\n\n        /// <summary>\n        /// 对方连接断开\n        /// </summary>\n        public event EventHandler<PeerConnectionBrokenArgs>? PeerConnectBroke;\n\n        private void OnAckReceived(AckArgs e)\n        {\n            AckReceived?.Invoke(this, e);\n        }\n\n        private void OnMessageReceived(PeerStreamMessageArgs e)\n        {\n            MessageReceived?.Invoke(this, e);\n        }\n\n        private void OnPeerConnected(IpcInternalPeerConnectedArgs e)\n        {\n            PeerConnected?.Invoke(this, e);\n        }\n\n        ~ServerStreamMessageReader()\n        {\n            try\n            {\n                Dispose(false);\n            }\n            catch (Exception)\n            {\n                // 如果在此抛出，那么进程将会退出\n            }\n        }\n\n        protected virtual void Dispose(bool disposing)\n        {\n            if (disposing)\n            {\n            }\n\n            _isDisposed = true;\n            Stream.Dispose();\n        }\n\n        private bool _isDisposed;\n\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n\n        private void OnPeerConnectBroke(PeerConnectionBrokenArgs e)\n        {\n            PeerConnectBroke?.Invoke(this, e);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcMessageCommandType.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc\n{\n    /// <summary>\n    /// 用于作为命令类型，用于框架的命令和业务的命令\n    /// </summary>\n    [Flags]\n    internal enum IpcMessageCommandType : short\n    {\n        /// <summary>\n        /// 向对方服务器注册\n        /// </summary>\n        PeerRegister = -1,\n\n        /*\n        /// <summary>\n        /// 发送回复信息\n        /// </summary>\n        SendAck = 0B0010,\n\n        /// <summary>\n        /// 发送回复信息，同时向对方服务器注册\n        /// </summary>\n        SendAckAndRegisterToPeer = PeerRegister | SendAck,\n        */\n\n        /// <summary>\n        /// 业务层的消息\n        /// </summary>\n        /// 所有大于 0 的都是业务层消息\n        Business = 1,\n\n        /// <summary>\n        /// 请求消息，业务层消息。\n        /// </summary>\n        RequestMessage = (1 << 1) | Business,\n\n        /// <summary>\n        /// 响应消息，业务层消息。\n        /// </summary>\n        ResponseMessage = (1 << 2) | Business,\n\n        ///// <summary>\n        ///// 请求的细分类型，IPC 框架无法识别和处理此消息体。\n        ///// </summary>\n        //RawRequestMessage = (1 << 3) | RequestMessage,\n\n        ///// <summary>\n        ///// 响应的细分类型，IPC 框架无法识别和处理此消息体。\n        ///// </summary>\n        //RawResponseMessage = (1 << 3) | ResponseMessage,\n\n        ///// <summary>\n        ///// 请求的细分类型，IPC 框架知道此消息体是可被处理的字符串。\n        ///// </summary>\n        //StringRequestMessage = (1 << 4) | RequestMessage,\n\n        ///// <summary>\n        ///// 响应的细分类型，IPC 框架知道此消息体是可被处理的字符串。\n        ///// </summary>\n        //StringResponseMessage = (1 << 4) | ResponseMessage,\n\n        ///// <summary>\n        ///// 请求的细分类型，IPC 框架知道此消息体是可被处理的 .NET 对象。\n        ///// </summary>\n        //ObjectRequestMessage = (1 << 5) | RequestMessage,\n\n        ///// <summary>\n        ///// 响应的细分类型，IPC 框架知道此消息体是可被处理的 .NET 对象。\n        ///// </summary>\n        //ObjectResponseMessage = (1 << 5) | ResponseMessage,\n\n        /// <summary>\n        /// 其他消息。\n        /// </summary>\n        Unknown = short.MaxValue,\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcMessageWriter.cs",
    "content": "﻿using System.Runtime.CompilerServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc\n{\n    /// <summary>\n    /// 提供消息的写入方法\n    /// </summary>\n    public class IpcMessageWriter : IRawMessageWriter\n    {\n        /// <summary>\n        /// 创建提供消息的写入方法\n        /// </summary>\n        /// <param name=\"messageWriter\">实际用来写入的方法</param>\n        public IpcMessageWriter(IRawMessageWriter messageWriter)\n        {\n            RawWriter = messageWriter;\n        }\n\n        private IRawMessageWriter RawWriter { get; }\n\n        /// <inheritdoc />\n        public Task WriteMessageAsync(byte[] buffer, int offset, int count, [CallerMemberName] string tag = \"\")\n        {\n            return RawWriter.WriteMessageAsync(buffer, offset, count, tag);\n        }\n\n        /// <summary>\n        /// 向对方发送消息\n        /// </summary>\n        /// <param name=\"message\">字符串消息，将会被使用Utf-8编码转换然后发送</param>\n        /// <param name=\"tag\"></param>\n        /// <returns></returns>\n        public Task WriteMessageAsync(string message, string? tag = null)\n        {\n            tag ??= message;\n            var messageBuffer = Encoding.UTF8.GetBytes(message);\n            return WriteMessageAsync(messageBuffer, 0, messageBuffer.Length, tag);\n        }\n\n        /// <summary>\n        /// 向对方发送消息\n        /// </summary>\n        /// <param name=\"message\">字符串消息，将会被使用Utf-8编码转换然后发送</param>\n        /// <returns></returns>\n        public Task WriteMessageAsync(IpcMessage message)\n        {\n            return WriteMessageAsync(message.Body.Buffer, message.Body.Start, message.Body.Length, message.Tag);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Base_/IpcDirectRoutedClientProxyBase.cs",
    "content": "﻿using System.IO;\nusing System.Text;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 直接路由的客户端基类\n/// </summary>\npublic abstract class IpcDirectRoutedClientProxyBase\n{\n    /// <summary>\n    /// 业务头\n    /// </summary>\n    protected abstract ulong BusinessHeader { get; }\n\n    /// <summary>\n    /// 写入消息头，包括路由地址和业务头\n    /// </summary>\n    /// <param name=\"stream\"></param>\n    /// <param name=\"routedPath\"></param>\n    protected void WriteHeader(MemoryStream stream, string routedPath)\n    {\n        using var binaryWriter = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);\n        IpcDirectRoutedMessageWriter.WriteHeader(binaryWriter, BusinessHeader, routedPath);\n    }\n\n    /// <summary>\n    /// 从 MemoryStream 转换为 <see cref=\"IpcMessage\"/> 对象。将会取出 MemoryStream 的 Buffer 封装为 <see cref=\"IpcMessage\"/> 对象\n    /// </summary>\n    /// <param name=\"stream\">要求是自己可控创建的 MemoryStream 对象，不能传入从池里获取的对象，且在 ToIpcMessage 之后再没有修改</param>\n    /// <param name=\"tag\"></param>\n    /// <returns></returns>\n    protected IpcMessage ToIpcMessage(MemoryStream stream, string tag = \"\")\n    {\n        var buffer = stream.GetBuffer();\n        var length = (int) stream.Position;\n\n        return new IpcMessage(tag, new IpcMessageBody(buffer, start: 0, length));\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Base_/IpcDirectRoutedProviderBase.cs",
    "content": "﻿using System;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 直接路由的 IPC 通讯\n/// </summary>\npublic abstract class IpcDirectRoutedProviderBase\n{\n    /// <summary>\n    /// 直接路由的 IPC 通讯\n    /// </summary>\n    /// <param name=\"pipeName\">管道名，也是当前服务的 PeerName 名。不填将会随机生成</param>\n    /// <param name=\"ipcConfiguration\"></param>\n    protected IpcDirectRoutedProviderBase(string? pipeName = null, IpcConfiguration? ipcConfiguration = null)\n    {\n        pipeName ??= $\"IpcDirectRouted_{Guid.NewGuid():N}\";\n        var ipcProvider = new IpcProvider(pipeName, ipcConfiguration);\n        IpcProvider = ipcProvider;\n    }\n\n    /// <summary>\n    /// 直接路由的 IPC 通讯。使用已配置的 <see cref=\"IpcProvider\"/> 对象，允许多个直接路由的 IPC 通讯共用相同的 <see cref=\"IpcProvider\"/> 对象，共用一条管道\n    /// </summary>\n    /// <param name=\"ipcProvider\">将使用此对象作为基础</param>\n    protected IpcDirectRoutedProviderBase(IpcProvider ipcProvider)\n    {\n        IpcProvider = ipcProvider;\n    }\n\n    /// <summary>\n    /// 实际发送和接收消息的 Ipc 提供器\n    /// </summary>\n    public IpcProvider IpcProvider { get; }\n    private protected ILogger Logger => IpcProvider.IpcContext.Logger;\n    private bool _isStarted;\n\n    /// <summary>\n    /// 启动服务。启动服务之后将不能再添加通知处理和请求处理\n    /// </summary>\n    public void StartServer()\n    {\n        if (IpcProvider.IsStarted)\n        {\n            // 如果在当前框架启动之前，已经启动了 IPC 服务，那就记录一条调试信息\n            Logger.Debug($\"[{nameof(JsonIpcDirectRoutedProvider)}][StartServer] 在 JsonIpcDirectRouted 框架启动服务之前，传入的 {nameof(IpcProvider)} 已经启动。可能启动的 {nameof(IpcProvider)} 已在接收消息，接收掉的消息将不会被 JsonIpcDirectRouted 框架处理。可能丢失消息\");\n        }\n\n        // 处理请求消息\n        var requestHandler = new RequestHandler(this);\n        IpcProvider.IpcContext.IpcConfiguration.AddFrameworkRequestHandler(requestHandler);\n\n        IpcProvider.StartServer();\n        // 处理 Notify 消息\n        IpcProvider.IpcServerService.MessageReceived += IpcServerService_MessageReceived;\n\n        _isStarted = true;\n    }\n\n    /// <summary>\n    /// 如果已经启动，抛出异常\n    /// </summary>\n    /// <exception cref=\"InvalidOperationException\"></exception>\n    protected void ThrowIfStarted()\n    {\n        if (_isStarted)\n        {\n            throw new InvalidOperationException($\"禁止在启动之后再次添加处理\");\n        }\n    }\n\n    /// <summary>\n    /// 业务头，用来分多个不同的通讯方式\n    /// </summary>\n    protected abstract ulong BusinessHeader { get; }\n\n    private void IpcServerService_MessageReceived(object? sender, PeerMessageArgs e)\n    {\n        if (e.Handle)\n        {\n            return;\n        }\n\n        // 这里是全部的消息都会进入的，但是这里通过判断业务头，只处理感兴趣的\n        if (TryHandleMessage(e.Message, out var message))\n        {\n            try\n            {\n                OnHandleNotify(message.Value, e);\n            }\n            catch (Exception exception)\n            {\n                // 不能让这里的异常对外抛出，否则其他业务也许莫名不执行\n                Logger.Error(exception, $\"[{nameof(IpcDirectRoutedProviderBase)}] HandleNotify\");\n            }\n        }\n    }\n\n    /// <summary>\n    /// 处理通知\n    /// </summary>\n    /// <param name=\"message\"></param>\n    /// <param name=\"e\"></param>\n    protected abstract void OnHandleNotify(IpcDirectRoutedMessage message, PeerMessageArgs e);\n\n    class RequestHandler : IIpcRequestHandler\n    {\n        public RequestHandler(IpcDirectRoutedProviderBase ipcDirectRoutedProviderBase)\n        {\n            IpcDirectRoutedProviderBase = ipcDirectRoutedProviderBase;\n        }\n\n        private IpcDirectRoutedProviderBase IpcDirectRoutedProviderBase { get; }\n\n        public Task<IIpcResponseMessage> HandleRequest(IIpcRequestContext requestContext)\n        {\n            if (IpcDirectRoutedProviderBase.TryHandleMessage(requestContext.IpcBufferMessage, out var message))\n            {\n                try\n                {\n                    return IpcDirectRoutedProviderBase.OnHandleRequestAsync(message.Value, requestContext);\n                }\n                catch (Exception e)\n                {\n                    IpcDirectRoutedProviderBase.Logger.Error(e, $\"[{nameof(IpcDirectRoutedProviderBase)}] HandleRequest\");\n                }\n            }\n\n            return Task.FromResult(KnownIpcResponseMessages.CannotHandle);\n        }\n    }\n\n    /// <summary>\n    /// 处理请求\n    /// </summary>\n    /// <param name=\"message\"></param>\n    /// <param name=\"requestContext\"></param>\n    /// <returns></returns>\n    protected abstract Task<IIpcResponseMessage> OnHandleRequestAsync(IpcDirectRoutedMessage message, IIpcRequestContext requestContext);\n\n    private bool TryHandleMessage(in IpcMessage ipcMessage, [NotNullWhen(true)] out IpcDirectRoutedMessage? ipcDirectRoutedMessage)\n    {\n        try\n        {\n            if (ipcMessage.TryGetPayload(BusinessHeader, out var message))\n            {\n                var stream = new MemoryStream(message.Body.Buffer, message.Body.Start, message.Body.Length, writable: true, publiclyVisible: true);\n                using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true);\n                var routedPath = binaryReader.ReadString();\n                ipcDirectRoutedMessage = new IpcDirectRoutedMessage(routedPath, stream, message);\n                return true;\n            }\n        }\n        catch (Exception e)\n        {\n            Logger.Error(e, $\"HandleMessage\");\n        }\n\n        ipcDirectRoutedMessage = default;\n        return false;\n    }\n\n    /// <summary>\n    /// 表示 IPC 直接路由消息\n    /// </summary>\n    protected readonly struct IpcDirectRoutedMessage\n    {\n        /// <summary>\n        /// 创建 IPC 直接路由消息\n        /// </summary>\n        /// <param name=\"routedPath\"></param>\n        /// <param name=\"stream\"></param>\n        /// <param name=\"payloadIpcMessage\">有效负载消息</param>\n        public IpcDirectRoutedMessage(string routedPath, MemoryStream stream,\n            IpcMessage payloadIpcMessage)\n        {\n            RoutedPath = routedPath;\n            Stream = stream;\n            PayloadIpcMessage = payloadIpcMessage;\n        }\n\n        /// <summary>\n        /// 获取消息体\n        /// </summary>\n        /// <returns></returns>\n        public IpcMessageBody GetData()\n        {\n            // 消息体为从有效负载信息里面，加上消息体本身读取掉的信息\n            var position = (int) Stream.Position;\n            var payload = PayloadIpcMessage.Body;\n            var data = new IpcMessageBody(payload.Buffer, payload.Start + position, payload.Length - position);\n            return data;\n        }\n\n        /// <summary>\n        /// 路由地址\n        /// </summary>\n        public string RoutedPath { get; }\n\n        /// <summary>\n        /// 消息本身，从有效负载消息创建。如需获取消息体，请使用 <see cref=\"GetData\"/> 方法\n        /// </summary>\n        public MemoryStream Stream { get; }\n\n        /// <summary>\n        /// 有效负载消息。包含路由头 <see cref=\"RoutedPath\"/> 信息和消息体\n        /// </summary>\n        public IpcMessage PayloadIpcMessage { get; }\n    }\n}\n\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/IpcDirectRoutedMessageWriter.cs",
    "content": "﻿using System.IO;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\nstatic class IpcDirectRoutedMessageWriter\n{\n    /// <summary>\n    /// 写入消息头\n    /// </summary>\n    /// <param name=\"writer\"></param>\n    /// <param name=\"businessMessageHeader\"></param>\n    /// <param name=\"routedPath\"></param>\n    public static void WriteHeader(BinaryWriter writer, ulong businessMessageHeader, string routedPath)\n    {\n        writer.Write(businessMessageHeader);\n        writer.Write(routedPath);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedCanNotFindRequestHandlerExceptionInfo.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\ninternal static class JsonIpcDirectRoutedCanNotFindRequestHandlerExceptionInfo\n{\n    public static JsonIpcDirectRoutedHandleRequestExceptionResponse CreateExceptionResponse(string routedPath)\n    {\n        return new JsonIpcDirectRoutedHandleRequestExceptionResponse()\n        {\n            ExceptionInfo = new JsonIpcDirectRoutedHandleRequestExceptionResponse.JsonIpcDirectRoutedHandleRequestExceptionInfo()\n            {\n                ExceptionMessage = $\"Can not find '{routedPath}' request Handler. 找不到 '{routedPath}' 请求的处理器，请确保服务端已经及时注册该路由处理器，也可能是客户端服务端版本不匹配问题\",\n                ExceptionType = ExceptionType,\n            }\n        };\n    }\n\n    public static bool IsCanNotFindRequestHandlerException([NotNullWhen(true)] JsonIpcDirectRoutedHandleRequestExceptionResponse? response)\n    {\n        return string.Equals(ExceptionType, response?.ExceptionInfo?.ExceptionType, StringComparison.Ordinal);\n    }\n\n    // 异常类型做好标识，确保可以唯一识别。不使用 nameof 的原因是，确保后续如果有改动，还能做好兼容\n    private const string ExceptionType = \"__JsonIpcDirectRoutedCanNotFindRequestHandlerException__\";\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedClientProxy.cs",
    "content": "﻿using System.Diagnostics;\nusing System.IO;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Serialization;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 提供 Json 直接路由的 IPC 通讯的客户端\n/// </summary>\npublic class JsonIpcDirectRoutedClientProxy : IpcDirectRoutedClientProxyBase\n{\n    /// <summary>\n    /// 创建 Json 直接路由的 IPC 通讯的客户端\n    /// </summary>\n    public JsonIpcDirectRoutedClientProxy(PeerProxy peerProxy)\n    {\n        _peerProxy = peerProxy;\n    }\n\n    private readonly PeerProxy _peerProxy;\n    private IpcContext IpcContext => _peerProxy.IpcContext;\n    private IIpcObjectSerializer IpcObjectSerializer => IpcContext.IpcConfiguration.IpcObjectSerializer;\n\n    /// <summary>\n    /// 不带参数的通知服务端\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <returns></returns>\n    public Task NotifyAsync(string routedPath)\n        => NotifyAsync(routedPath, JsonIpcDirectRoutedParameterlessType.Instance);\n\n    /// <summary>\n    /// 通知服务端\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"routedPath\">路由地址</param>\n    /// <param name=\"obj\"></param>\n    /// <returns>返回仅代表消息到达服务端，不代表服务端业务上处理完成</returns>\n    public Task NotifyAsync<T>(string routedPath, T obj) where T : class\n    {\n        IpcMessage ipcMessage = BuildMessage(routedPath, obj);\n        IpcContext.LogSendJsonIpcDirectRoutedNotify(routedPath, _peerProxy.PeerName, ipcMessage.Body);\n        return _peerProxy.NotifyAsync(ipcMessage);\n    }\n\n    /// <summary>\n    /// 获取服务端的响应，不带参数\n    /// </summary>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\">路由地址</param>\n    /// <returns></returns>\n    public Task<TResponse?> GetResponseAsync<TResponse>(string routedPath) where TResponse : class\n        => GetResponseAsync<TResponse>(routedPath, JsonIpcDirectRoutedParameterlessType.Instance);\n\n    /// <summary>\n    /// 获取服务端的响应\n    /// </summary>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\">路由地址</param>\n    /// <param name=\"obj\">发送给服务端的对象</param>\n    /// <returns></returns>\n    public async Task<TResponse?> GetResponseAsync<TResponse>(string routedPath, object obj) where TResponse : class\n    {\n        IpcMessage ipcMessage = BuildMessage(routedPath, obj);\n        IpcContext.LogSendJsonIpcDirectRoutedRequest(routedPath, _peerProxy.PeerName, ipcMessage.Body);\n\n        IpcMessage responseMessage = await _peerProxy.GetResponseAsync(ipcMessage);\n\n        using var memoryStream = responseMessage.Body.ToMemoryStream();\n        IpcContext.LogReceiveJsonIpcDirectRoutedResponse(routedPath, _peerProxy.PeerName, memoryStream);\n\n        try\n        {\n            // 反序列化响应。正常情况下，会需要反序列化两次\n            // 第一次，获取是否存在异常信息\n            // 第二次，读取真正的响应数据\n            var exceptionResponse =\n                IpcObjectSerializer.Deserialize<JsonIpcDirectRoutedHandleRequestExceptionResponse>(memoryStream);\n\n            // 是否为没有找到处理器的情况\n            if (JsonIpcDirectRoutedCanNotFindRequestHandlerExceptionInfo\n                .IsCanNotFindRequestHandlerException(exceptionResponse))\n            {\n                throw new JsonIpcDirectRoutedCanNotFindRequestHandlerException(_peerProxy, routedPath,\n                    exceptionResponse);\n            }\n\n            // 是否为存在异常的情况，判断方式就是判断是否存在异常类型。因为异常类型在框架内是必然有异常就会赋值的\n            bool existsException = !string.IsNullOrEmpty(exceptionResponse?.ExceptionInfo?.ExceptionType);\n            if (existsException)\n            {\n                Debug.Assert(exceptionResponse != null);\n                Debug.Assert(exceptionResponse!.ExceptionInfo != null);\n                // 远端异常，准备构建异常信息抛出\n                throw new JsonIpcDirectRoutedHandleRequestRemoteException(_peerProxy, routedPath, exceptionResponse);\n            }\n            else\n            {\n                // 没异常，正常处理\n                // 重新设置内存流的位置，前面反序列化异常信息时已经读取了内存流\n                memoryStream.Position = 0;\n            }\n\n            return IpcObjectSerializer.Deserialize<TResponse>(memoryStream);\n        }\n        catch (IpcException)\n        {\n            // 自己框架内抛出的异常，那就原封不动继续抛出\n            throw;\n        }\n        catch (Exception e)\n        {\n            // 序列化错误\n            throw new JsonIpcDirectRouteSerializeLocalException(responseMessage, typeof(TResponse), e);\n        }\n    }\n\n    private IpcMessage BuildMessage(string routedPath, object obj)\n    {\n        using var memoryStream = new MemoryStream();\n        WriteHeader(memoryStream, routedPath);\n\n        IpcObjectSerializer.Serialize(memoryStream, obj);\n\n        return ToIpcMessage(memoryStream, $\"Message {routedPath}\");\n    }\n\n    /// <inheritdoc />\n    protected override ulong BusinessHeader => (ulong) KnownMessageHeaders.JsonIpcDirectRoutedMessageHeader;\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedContext.cs",
    "content": "﻿namespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 提供 Json 直接路由的 IPC 通讯的上下文\n/// </summary>\npublic class JsonIpcDirectRoutedContext\n{\n    /// <summary>\n    /// 创建提供 Json 直接路由的 IPC 通讯的上下文\n    /// </summary>\n    /// <param name=\"peerName\"></param>\n    public JsonIpcDirectRoutedContext(string peerName)\n    {\n        PeerName = peerName;\n    }\n\n    /// <summary>\n    /// 通讯方的 Peer 名\n    /// </summary>\n    public string PeerName { get; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedHandleRequestExceptionInfo.cs",
    "content": "﻿#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#endif\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 直接路由的 IPC 通讯的异常信息，即 IPC 服务端的业务层通讯异常。如参数不匹配、执行的业务端逻辑抛出异常等\n/// </summary>\ninternal class JsonIpcDirectRoutedHandleRequestExceptionResponse\n{\n#if UseNewtonsoftJson\n    [JsonProperty(\"__$Exception\")]\n#endif\n#if NET6_0_OR_GREATER\n    [System.Text.Json.Serialization.JsonPropertyName(\"__$Exception\")]\n#endif\n    public JsonIpcDirectRoutedHandleRequestExceptionInfo? ExceptionInfo { get; set; }\n\n    internal class JsonIpcDirectRoutedHandleRequestExceptionInfo\n    {\n        public string? ExceptionType { get; set; }\n        public string? ExceptionMessage { get; set; }\n        public string? ExceptionStackTrace { get; set; }\n\n        public string? ExceptionToString { get; set; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedLogStateMessageType.cs",
    "content": "﻿namespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 提供 Json 直接路由的 IPC 通讯的日志消息类型\n/// </summary>\npublic enum JsonIpcDirectRoutedLogStateMessageType\n{\n    // 服务端\n    /// <summary>\n    /// 收到通知\n    /// </summary>\n    ReceiveNotify,\n\n    /// <summary>\n    /// 收到请求\n    /// </summary>\n    ReceiveRequest,\n\n    /// <summary>\n    /// 发送响应\n    /// </summary>\n    SendResponse,\n\n    // 客户端\n    /// <summary>\n    /// 发送通知\n    /// </summary>\n    SendNotify,\n\n    /// <summary>\n    /// 发送请求\n    /// </summary>\n    SendRequest,\n\n    /// <summary>\n    /// 收到响应\n    /// </summary>\n    ReceiveResponse,\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedLoggerExtension.cs",
    "content": "﻿using System.IO;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Context.LoggingContext;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\ninternal static class JsonIpcDirectRoutedLoggerExtension\n{\n    /// <summary>\n    /// [客户端] 记录发送请求\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    /// <param name=\"ipcMessageBody\"></param>\n    public static void LogSendJsonIpcDirectRoutedRequest(this IpcContext context, string routedPath,\n        string remotePeerName, in IpcMessageBody ipcMessageBody)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var eventId = LoggerEventIds.ReceiveJsonIpcDirectRoutedResponseEventId;\n        var stream = ipcMessageBody.ToMemoryStream();\n        var state = new JsonIpcDirectRoutedMessageLogState(routedPath, context.PipeName, remotePeerName, JsonIpcDirectRoutedLogStateMessageType.SendRequest, stream);\n\n        context.Logger.Log(logLevel, eventId, state, null,\n            JsonIpcDirectRoutedMessageLogState.Format);\n    }\n\n    /// <summary>\n    /// [客户端] 记录收到响应\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    /// <param name=\"stream\"></param>\n    public static void LogReceiveJsonIpcDirectRoutedResponse(this IpcContext context, string routedPath,\n        string remotePeerName, MemoryStream stream)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var eventId = LoggerEventIds.ReceiveJsonIpcDirectRoutedResponseEventId;\n        var state = new JsonIpcDirectRoutedMessageLogState(routedPath, context.PipeName, remotePeerName, JsonIpcDirectRoutedLogStateMessageType.ReceiveResponse, stream);\n\n        context.Logger.Log(logLevel, eventId, state, null,\n            JsonIpcDirectRoutedMessageLogState.Format);\n    }\n\n    /// <summary>\n    /// [客户端] 记录发送通知\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    /// <param name=\"ipcMessageBody\"></param>\n    public static void LogSendJsonIpcDirectRoutedNotify(this IpcContext context, string routedPath,\n        string remotePeerName, in IpcMessageBody ipcMessageBody)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var eventId = LoggerEventIds.SendJsonIpcDirectRoutedNotifyEventId;\n        using var stream = ipcMessageBody.ToMemoryStream();\n        var state = new JsonIpcDirectRoutedMessageLogState(routedPath, context.PipeName, remotePeerName, JsonIpcDirectRoutedLogStateMessageType.SendNotify, stream);\n\n        context.Logger.Log(logLevel, eventId, state, null,\n            JsonIpcDirectRoutedMessageLogState.Format);\n    }\n\n    /// <summary>\n    /// [服务端] 记录发送响应\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    /// <param name=\"ipcMessageBody\"></param>\n    public static void LogSendJsonIpcDirectRoutedResponse(this IpcContext context, string routedPath,\n        string remotePeerName, in IpcMessageBody ipcMessageBody)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var eventId = LoggerEventIds.SendJsonIpcDirectRoutedResponseEventId;\n        using var stream = ipcMessageBody.ToMemoryStream();\n        var state = new JsonIpcDirectRoutedMessageLogState(routedPath, context.PipeName, remotePeerName, JsonIpcDirectRoutedLogStateMessageType.SendResponse, stream);\n\n        context.Logger.Log(logLevel, eventId, state, null,\n            JsonIpcDirectRoutedMessageLogState.Format);\n    }\n\n    /// <summary>\n    /// [服务端] 记录收到请求\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    /// <param name=\"stream\"></param>\n    public static void LogReceiveJsonIpcDirectRoutedRequest(this IpcContext context, string routedPath,\n        string remotePeerName, MemoryStream stream)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var eventId = LoggerEventIds.ReceiveJsonIpcDirectRoutedRequestEventId;\n\n        var state = new JsonIpcDirectRoutedMessageLogState(routedPath, context.PipeName, remotePeerName, JsonIpcDirectRoutedLogStateMessageType.ReceiveRequest, stream);\n\n        context.Logger.Log(logLevel, eventId, state, null,\n            JsonIpcDirectRoutedMessageLogState.Format);\n    }\n\n    /// <summary>\n    /// [服务端] 记录发送通知\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"remotePeerName\"></param>\n    /// <param name=\"stream\"></param>\n    public static void LogReceiveJsonIpcDirectRoutedNotify(this IpcContext context, string routedPath,\n        string remotePeerName, MemoryStream stream)\n    {\n        const LogLevel logLevel = LogLevel.Debug;\n        if (!context.Logger.IsEnabled(logLevel))\n        {\n            return;\n        }\n\n        var eventId = LoggerEventIds.ReceiveJsonIpcDirectRoutedNotifyEventId;\n\n        var state = new JsonIpcDirectRoutedMessageLogState(routedPath, context.PipeName, remotePeerName, JsonIpcDirectRoutedLogStateMessageType.ReceiveNotify, stream);\n\n        context.Logger.Log(logLevel, eventId, state, null,\n            JsonIpcDirectRoutedMessageLogState.Format);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedMessageLogState.cs",
    "content": "﻿using System;\nusing System.IO;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 用于日志记录 JsonIpcDirectRouted 消息的结构体\n/// </summary>\npublic readonly struct JsonIpcDirectRoutedMessageLogState\n{\n    internal JsonIpcDirectRoutedMessageLogState(string routedPath, string localPeerName, string remotePeerName,\n        JsonIpcDirectRoutedLogStateMessageType messageType,\n        MemoryStream stream)\n    {\n        RoutedPath = routedPath;\n        LocalPeerName = localPeerName;\n        RemotePeerName = remotePeerName;\n        MessageType = messageType;\n        Stream = stream;\n    }\n\n    /// <summary>\n    /// 路由地址\n    /// </summary>\n    public string RoutedPath { get; }\n\n    /// <summary>\n    /// 本地当前的 Peer 名\n    /// </summary>\n    public string LocalPeerName { get; }\n\n    /// <summary>\n    /// 远端对方的 Peer 名\n    /// </summary>\n    public string RemotePeerName { get; }\n\n    /// <summary>\n    /// 消息类型\n    /// </summary>\n    public JsonIpcDirectRoutedLogStateMessageType MessageType { get; }\n\n    private MemoryStream Stream { get; }\n\n    /// <summary>\n    /// 获取消息体的 Json 文本\n    /// </summary>\n    /// <returns></returns>\n    public string GetJsonText()\n    {\n        var position = Stream.Position;\n        var streamReader = new StreamReader(Stream);\n        var jsonText = streamReader.ReadToEnd();\n        Stream.Position = position;\n        return jsonText;\n    }\n\n    /// <summary>\n    /// 格式化\n    /// </summary>\n    /// <param name=\"state\"></param>\n    /// <param name=\"exception\"></param>\n    /// <returns></returns>\n    public static string Format(JsonIpcDirectRoutedMessageLogState state, Exception? exception)\n    {\n        var action = state.MessageType switch\n        {\n            JsonIpcDirectRoutedLogStateMessageType.ReceiveNotify => \"Receive Notify\",\n            JsonIpcDirectRoutedLogStateMessageType.ReceiveRequest => \"Receive Request\",\n            JsonIpcDirectRoutedLogStateMessageType.SendResponse => \"Send Response\",\n\n            JsonIpcDirectRoutedLogStateMessageType.SendNotify => \"Send Notify\",\n            JsonIpcDirectRoutedLogStateMessageType.SendRequest => \"Send Request\",\n            JsonIpcDirectRoutedLogStateMessageType.ReceiveResponse => \"Receive Response\",\n\n            _ => string.Empty\n        };\n\n        return\n            $\"[JsonIpcDirectRouted][{action}] Path={state.RoutedPath} Remote={state.RemotePeerName} Local={state.LocalPeerName} Body={state.GetJsonText()}\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedParameterlessType.cs",
    "content": "﻿namespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 表示在 JsonIpcDirectRouted 使用的无参类型\n/// </summary>\ninternal class JsonIpcDirectRoutedParameterlessType\n{\n    /// <summary>\n    /// 无参类型的实例，使用具体的类型而不是 null 是为了序列化时能够返回 `{}` 内容，从而提升兼容性\n    /// 比如客户端是旧版本，旧版本时约定是无参。但是服务端是新版本，新版本时约定是有参。此时的服务端依然能够收到旧客户端发送过来的消息，且消息参数不为空，只是里面没有内容。如果服务端强行约束参数，则可以收到异常\n    /// </summary>\n    public static JsonIpcDirectRoutedParameterlessType Instance\n    {\n        get => _instance ??= new JsonIpcDirectRoutedParameterlessType();\n    }\n\n    private static JsonIpcDirectRoutedParameterlessType? _instance;\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/Json_/JsonIpcDirectRoutedProvider.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing System.IO;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Serialization;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.Logging;\n\n#if !NETCOREAPP\nusing ValueTask = System.Threading.Tasks.Task;\n#endif\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 提供 Json 直接路由的 IPC 通讯\n/// </summary>\npublic class JsonIpcDirectRoutedProvider : IpcDirectRoutedProviderBase\n{\n    /// <summary>\n    /// 创建 Json 直接路由的 IPC 通讯\n    /// </summary>\n    /// <param name=\"pipeName\"></param>\n    /// <param name=\"ipcConfiguration\"></param>\n    public JsonIpcDirectRoutedProvider(string? pipeName = null, IpcConfiguration? ipcConfiguration = null) : base(pipeName, ipcConfiguration)\n    {\n    }\n\n    /// <summary>\n    /// 创建 Json 直接路由的 IPC 通讯\n    /// </summary>\n    /// <param name=\"ipcProvider\"></param>\n    public JsonIpcDirectRoutedProvider(IpcProvider ipcProvider) : base(ipcProvider)\n    {\n    }\n\n    /// <summary>\n    /// 获取对 <paramref name=\"serverPeerName\"/> 请求的客户端\n    /// </summary>\n    /// <param name=\"serverPeerName\"></param>\n    /// <returns></returns>\n    public async Task<JsonIpcDirectRoutedClientProxy> GetAndConnectClientAsync(string serverPeerName)\n    {\n        var peer = await IpcProvider.GetAndConnectToPeerAsync(serverPeerName);\n        return new JsonIpcDirectRoutedClientProxy(peer);\n    }\n\n    #region Notify\n\n    /// <summary>\n    /// 添加通知的处理，无参\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler(string routedPath, Action handler)\n        => AddNotifyHandler<JsonIpcDirectRoutedParameterlessType>(routedPath, _ => handler());\n\n    /// <summary>\n    /// 添加通知的处理，无参\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler(string routedPath, Func<Task> handler)\n        => AddNotifyHandler<JsonIpcDirectRoutedParameterlessType>(routedPath, _ => handler());\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler<T>(string routedPath, Func<T, Task> handler)\n    {\n        AddNotifyHandler<T>(routedPath, (args, _) => handler(args));\n    }\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler<T>(string routedPath, Func<T, JsonIpcDirectRoutedContext, Task> handler)\n    {\n        Task HandleNotify(MemoryStream stream, JsonIpcDirectRoutedContext context)\n        {\n            var argument = ToObject<T>(stream);\n            return handler(argument!, context);\n        }\n\n        AddNotifyHandler(routedPath, new NotifyHandler() { AsyncHandler = HandleNotify });\n    }\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler<T>(string routedPath, Action<T> handler)\n    {\n        AddNotifyHandler<T>(routedPath, (args, _) => handler(args));\n    }\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <typeparam name=\"T\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler<T>(string routedPath, Action<T, JsonIpcDirectRoutedContext> handler)\n    {\n        void HandleNotify(MemoryStream stream, JsonIpcDirectRoutedContext context)\n        {\n            var argument = ToObject<T>(stream);\n            handler(argument!, context);\n        }\n\n        AddNotifyHandler(routedPath, new NotifyHandler() { SyncHandler = HandleNotify });\n    }\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"notifyHandler\"></param>\n    /// <exception cref=\"InvalidOperationException\"></exception>\n    private void AddNotifyHandler(string routedPath, NotifyHandler notifyHandler)\n    {\n        ThrowIfStarted();\n\n        if (!HandleNotifyDictionary.TryAdd(routedPath, notifyHandler))\n        {\n            throw new InvalidOperationException($\"重复添加对 {routedPath} 的处理\");\n        }\n    }\n\n    private class NotifyHandler\n    {\n        public Func<MemoryStream, JsonIpcDirectRoutedContext, Task>? AsyncHandler { get; set; }\n        public Action<MemoryStream, JsonIpcDirectRoutedContext>? SyncHandler { get; set; }\n    }\n\n\n    private ConcurrentDictionary<string, NotifyHandler> HandleNotifyDictionary { get; } = new ConcurrentDictionary<string, NotifyHandler>();\n\n    /// <inheritdoc />\n    protected override ulong BusinessHeader => (ulong) KnownMessageHeaders.JsonIpcDirectRoutedMessageHeader;\n\n    /// <inheritdoc />\n    protected override void OnHandleNotify(IpcDirectRoutedMessage message, PeerMessageArgs e)\n    {\n        // 接下来进行调度\n        var routedPath = message.RoutedPath;\n        var stream = message.Stream;\n        if (HandleNotifyDictionary.TryGetValue(routedPath, out var handleNotify))\n        {\n            var context = new JsonIpcDirectRoutedContext(e.PeerName);\n            e.SetHandle(\"JsonIpcDirectRouted Handled in MessageReceived\");\n\n            IpcProvider.IpcContext.LogReceiveJsonIpcDirectRoutedNotify(routedPath, e.PeerName, stream);\n\n            try\n            {\n                // 分为同步和异步两个版本，防止异步版本执行过程没有等待，导致原本期望顺序执行的业务变成了并发执行\n                if (handleNotify.SyncHandler is { } syncHandler)\n                {\n                    // 不等了，也没啥业务\n                    _ = IpcProvider.IpcContext.TaskPool.Run(() =>\n                    {\n                        syncHandler(stream, context);\n                    });\n                }\n                else if (handleNotify.AsyncHandler is { } asyncHandler)\n                {\n                    // 不等了，也没啥业务\n                    _ = IpcProvider.IpcContext.TaskPool.Run(async () =>\n                    {\n                        await asyncHandler(stream, context);\n                        return true;\n                    });\n                }\n                else\n                {\n                    // 不能吧，如果进入此分支则表示框架里面的代码有问题\n                    Logger.Error($\"[{nameof(JsonIpcDirectRoutedProvider)}] HandleNotify not found any handler. IPC Library internal error! 框架内部异常\");\n                }\n            }\n            catch (Exception exception)\n            {\n                // 不能让这里的异常对外抛出，否则其他业务也许莫名不执行\n                Logger.Error(exception, $\"[{nameof(JsonIpcDirectRoutedProvider)}] HandleNotify Method={handleNotify.SyncHandler?.Method ?? handleNotify.AsyncHandler?.Method}\");\n            }\n        }\n        else\n        {\n            // 考虑可能有多个实例，每个实例处理不同的业务情况\n            //IpcProvider.IpcContext.Logger.Warning($\"找不到对 {routedPath} 的 {nameof(JsonIpcDirectRoutedProvider)} 处理，是否忘记调用 {nameof(AddNotifyHandler)} 添加处理\");\n        }\n    }\n\n    #endregion\n\n    #region Request Response\n\n    /// <summary>\n    /// 添加请求的处理，无参请求\n    /// </summary>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler<TResponse>(string routedPath, Func<TResponse> handler) =>\n        AddRequestHandler<JsonIpcDirectRoutedParameterlessType, TResponse>(routedPath, _ => handler());\n\n    /// <summary>\n    /// 添加请求的处理，无参请求\n    /// </summary>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler<TResponse>(string routedPath, Func<Task<TResponse>> handler) =>\n        AddRequestHandler<JsonIpcDirectRoutedParameterlessType, TResponse>(routedPath, _ => handler());\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <typeparam name=\"TRequest\"></typeparam>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler<TRequest, TResponse>(string routedPath, Func<TRequest, TResponse> handler)\n    {\n        AddRequestHandler<TRequest, TResponse>(routedPath, (request, _) =>\n        {\n            var response = handler(request);\n            return Task.FromResult(response);\n        });\n    }\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <typeparam name=\"TRequest\"></typeparam>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler<TRequest, TResponse>(string routedPath, Func<TRequest, Task<TResponse>> handler)\n    {\n        AddRequestHandler<TRequest, TResponse>(routedPath, (request, _) => handler(request));\n    }\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <typeparam name=\"TRequest\"></typeparam>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler<TRequest, TResponse>(string routedPath,\n        Func<TRequest, JsonIpcDirectRoutedContext, TResponse> handler)\n    {\n        AddRequestHandler<TRequest, TResponse>(routedPath, (request, context) =>\n        {\n            var result = handler(request, context);\n            return Task.FromResult(result);\n        });\n    }\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <typeparam name=\"TRequest\"></typeparam>\n    /// <typeparam name=\"TResponse\"></typeparam>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    /// <exception cref=\"InvalidOperationException\"></exception>\n    public void AddRequestHandler<TRequest, TResponse>(string routedPath,\n        Func<TRequest, JsonIpcDirectRoutedContext, Task<TResponse>> handler)\n    {\n        ThrowIfStarted();\n        HandleRequest handleRequest = HandleRequest;\n\n        if (!HandleRequestDictionary.TryAdd(routedPath, handleRequest))\n        {\n            throw new InvalidOperationException($\"重复添加对 {routedPath} 的处理\");\n        }\n\n#if NETCOREAPP\n        async ValueTask<IpcMessage> HandleRequest(MemoryStream stream, JsonIpcDirectRoutedContext context)\n#else\n        async Task<IpcMessage> HandleRequest(MemoryStream stream, JsonIpcDirectRoutedContext context)\n#endif\n        {\n            var argument = ToObject<TRequest>(stream);\n            // 业务端处理参数，返回响应内容\n            var response = await handler(argument!, context);\n\n            IpcMessageBody ipcMessageBody = ResponseToIpcMessageBody(response);\n            return new IpcMessage($\"Handle '{routedPath}' response\", ipcMessageBody);\n        }\n    }\n\n#if NETCOREAPP\n    private delegate ValueTask<IpcMessage> HandleRequest(MemoryStream stream, JsonIpcDirectRoutedContext context);\n#else\n    private delegate Task<IpcMessage> HandleRequest(MemoryStream stream, JsonIpcDirectRoutedContext context);\n#endif\n\n    private ConcurrentDictionary<string, HandleRequest> HandleRequestDictionary { get; } =\n        new ConcurrentDictionary<string, HandleRequest>();\n\n    /// <summary>\n    /// 处理请求的核心逻辑\n    /// </summary>\n    /// <param name=\"message\"></param>\n    /// <param name=\"requestContext\"></param>\n    /// <returns></returns>\n    protected override async Task<IIpcResponseMessage> OnHandleRequestAsync(IpcDirectRoutedMessage message, IIpcRequestContext requestContext)\n    {\n        var routedPath = message.RoutedPath;\n        var stream = message.Stream;\n\n        if (HandleRequestDictionary.TryGetValue(routedPath, out var handler))\n        {\n            var context = new JsonIpcDirectRoutedContext(requestContext.Peer.PeerName);\n            var taskPool = IpcProvider.IpcContext.TaskPool;\n\n            IpcProvider.IpcContext.LogReceiveJsonIpcDirectRoutedRequest(routedPath, requestContext.Peer.PeerName,\n                stream);\n\n            try\n            {\n                var ipcMessage = await taskPool.Run(async () =>\n                {\n                    return await handler(stream, context);\n                });\n\n                IpcProvider.IpcContext.LogSendJsonIpcDirectRoutedResponse(routedPath, requestContext.Peer.PeerName, ipcMessage.Body);\n                IIpcResponseMessage response = new IpcHandleRequestMessageResult(ipcMessage);\n                return response;\n            }\n            catch (Exception exception)\n            {\n                // 由于 handler 是业务端传过来的，在框架层需要接住异常，否则 IPC 框架将会因为某个业务抛出异常然后丢失消息\n                Logger.Error(exception, $\"[{nameof(JsonIpcDirectRoutedProvider)}] HandleRequest Method={handler.Method} RoutedPath={routedPath}\");\n                // 经过实际业务大量测试，不会有因为错误调度而导致进入非预期分支的异常，能够进入到此异常的，都是业务端报错。更加正确的处理是将其返回给到另一端 Peer 端，返回时，给出异常信息，这样才能更好的方便上层业务调试\n                // 经过实际业务大量测试发现，很多时候服务端的业务执行逻辑抛出了异常，但客户端业务层没有收到，开发者经常都会第一时间尝试调查客户端业务层，结果毫无所获。为了更好的调试体验，决定在此出现异常时，包装为 IPC 远端异常数据返回。当前只有 JsonIpc 方式能够进行包装，暂不知道 RawByte 等方式可以进行如何封装\n\n                //// 也有可能是错误处理了不应该调度到这里的业务处理的消息从而抛出异常，继续调度到下一项\n                //return KnownIpcResponseMessages.CannotHandle;\n\n                var response = new JsonIpcDirectRoutedHandleRequestExceptionResponse()\n                {\n                    ExceptionInfo = new JsonIpcDirectRoutedHandleRequestExceptionResponse.JsonIpcDirectRoutedHandleRequestExceptionInfo()\n                    {\n                        ExceptionType = exception.GetType().FullName,\n                        ExceptionMessage = exception.Message,\n                        ExceptionStackTrace = exception.StackTrace,\n                        // 在 ToString 过程里面会包含 Inner 异常等的信息，一般靠这个就足够了\n                        ExceptionToString = exception.ToString(),\n                    }\n                };\n\n                IpcMessageBody ipcMessageBody = ResponseToIpcMessageBody(response);\n                var ipcMessage = new IpcMessage($\"Handle '{routedPath}' response exception\", ipcMessageBody);\n                return new IpcHandleRequestMessageResult(ipcMessage);\n            }\n        }\n        else\n        {\n            // 尽管说客户端能够推测出 RoutedPath 是什么，但这个类型仅仅是为了以后方便扩展\n            // 和让输出有东西，方便抓取信息了解内容\n            var response = JsonIpcDirectRoutedCanNotFindRequestHandlerExceptionInfo.CreateExceptionResponse(routedPath);\n\n            IpcMessageBody ipcMessageBody = ResponseToIpcMessageBody(response);\n            var ipcMessage = new IpcMessage($\"Can not find '{routedPath}' request Handler\", ipcMessageBody);\n\n            // 考虑可能有多个实例，每个实例处理不同的业务情况\n            //JsonIpcDirectRoutedProvider.IpcProvider.IpcContext.Logger.Warning($\"找不到对 {routedPath} 的 {nameof(JsonIpcDirectRoutedProvider)} 处理，是否忘记调用 {nameof(AddRequestHandler)} 添加处理\");\n            // 返回不能处理即可。最后一次返回不能处理则会将此信息传递给到客户端。如果后续还有其他的实例处理了请求信息，则能够覆盖本条信息。正常来说不会有非常多个实例，即使有所浪费，预计也不多\n            return KnownIpcResponseMessages.CreateCanNotHandleResponseMessage(ipcMessage);\n        }\n    }\n\n    private IpcMessageBody ResponseToIpcMessageBody<TResponse>(TResponse response)\n    {\n        var responseMemoryStream = new MemoryStream();\n\n        JsonSerializer.Serialize(responseMemoryStream, response);\n\n        var buffer = responseMemoryStream.GetBuffer();\n        var length = (int) responseMemoryStream.Position;\n        return new IpcMessageBody(buffer, 0, length);\n    }\n\n    #endregion\n\n    private IIpcObjectSerializer JsonSerializer => IpcProvider.IpcContext.IpcConfiguration.IpcObjectSerializer;\n\n    private T? ToObject<T>(MemoryStream stream)\n    {\n        return JsonSerializer.Deserialize<T>(stream);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/RawByte_/RawByteIpcDirectRoutedClientProxy.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Threading.Tasks;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\n/// <summary>\n/// 传输裸的 byte 数据的直接路由的 IPC 通讯客户端\n/// </summary>\npublic class RawByteIpcDirectRoutedClientProxy : IpcDirectRoutedClientProxyBase\n{\n    public RawByteIpcDirectRoutedClientProxy(IPeerProxy peerProxy)\n    {\n        _peerProxy = peerProxy;\n    }\n\n    private readonly IPeerProxy _peerProxy;\n\n#if NETCOREAPP\n\n    public Task NotfiyAsync(string routedPath, in IpcMessageBody data)\n        => NotfiyAsync(routedPath, data.AsSpan());\n\n    public Task NotfiyAsync(string routedPath, Span<byte> data)\n    {\n        IpcMessage ipcMessage = BuildMessage(routedPath, data);\n        return _peerProxy.NotifyAsync(ipcMessage);\n    }\n\n    public async Task<IpcMessageBody> GetResponseAsync(string routedPath, IpcMessageBody ipcMessageBody)\n    {\n        IpcMessage ipcMessage = BuildMessage(routedPath, ipcMessageBody.AsSpan());\n\n        var response = await _peerProxy.GetResponseAsync(ipcMessage);\n        return response.Body;\n    }\n\n    private IpcMessage BuildMessage(string routedPath, Span<byte> data)\n    {\n        using var memoryStream = new MemoryStream();\n        WriteHeader(memoryStream, routedPath);\n\n        memoryStream.Write(data);\n\n        return ToIpcMessage(memoryStream, $\"Message To {routedPath}\");\n    }\n#else\n    public Task NotfiyAsync(string routedPath, IpcMessageBody data)\n    {\n        IpcMessage ipcMessage = BuildMessage(routedPath, data);\n        return _peerProxy.NotifyAsync(ipcMessage);\n    }\n\n    public async Task<IpcMessageBody> GetResponseAsync(string routedPath, IpcMessageBody ipcMessageBody)\n    {\n        IpcMessage ipcMessage = BuildMessage(routedPath, ipcMessageBody);\n\n        var response = await _peerProxy.GetResponseAsync(ipcMessage);\n        return response.Body;\n    }\n\n    private IpcMessage BuildMessage(string routedPath, in IpcMessageBody data)\n    {\n        using var memoryStream = new MemoryStream();\n        WriteHeader(memoryStream, routedPath);\n\n        memoryStream.Write(data.Buffer, data.Start, data.Length);\n\n        return ToIpcMessage(memoryStream, $\"Message To {routedPath}\");\n    }\n#endif\n    protected override ulong BusinessHeader => (ulong) KnownMessageHeaders.RawByteIpcDirectRoutedMessageHeader;\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/IpcRouteds/DirectRouteds/RawByte_/RawByteIpcDirectRoutedProvider.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.IO;\nusing System.Threading.Tasks;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\npublic class RawByteIpcDirectRoutedProvider : IpcDirectRoutedProviderBase\n{\n    public RawByteIpcDirectRoutedProvider(string? pipeName = null, IpcConfiguration? ipcConfiguration = null) : base(\n        pipeName, ipcConfiguration)\n    {\n    }\n\n    public RawByteIpcDirectRoutedProvider(IpcProvider ipcProvider) : base(ipcProvider)\n    {\n    }\n\n    protected override ulong BusinessHeader => (ulong) KnownMessageHeaders.RawByteIpcDirectRoutedMessageHeader;\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler(string routedPath, Func<IpcMessageBody, Task> handler) =>\n        AddNotifyHandler(routedPath, (data, _) => handler(data));\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler(string routedPath, Func<IpcMessageBody, JsonIpcDirectRoutedContext, Task> handler)\n    {\n        AddNotifyHandler(routedPath, HandlerNotify);\n\n        async void HandlerNotify(IpcMessageBody data, JsonIpcDirectRoutedContext context)\n        {\n            try\n            {\n                await handler(data, context);\n            }\n            catch (Exception e)\n            {\n                // 线程顶层，不能再抛出异常\n                Logger.Warning($\"Handle {routedPath} with exception. {e}\");\n            }\n        }\n    }\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddNotifyHandler(string routedPath, Action<IpcMessageBody> handler) =>\n        AddNotifyHandler(routedPath, (data, _) => handler(data));\n\n    /// <summary>\n    /// 添加通知的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    /// <exception cref=\"InvalidOperationException\"></exception>\n    public void AddNotifyHandler(string routedPath, Action<IpcMessageBody, JsonIpcDirectRoutedContext> handler)\n    {\n        ThrowIfStarted();\n\n        if (!HandleNotifyDictionary.TryAdd(routedPath, HandleNotify))\n        {\n            throw new InvalidOperationException($\"重复添加对 {routedPath} 的处理\");\n        }\n\n        void HandleNotify(IpcMessageBody data, JsonIpcDirectRoutedContext context)\n        {\n            handler(data, context);\n        }\n    }\n\n    private ConcurrentDictionary<string, HandleNotify> HandleNotifyDictionary { get; } =\n        new ConcurrentDictionary<string, HandleNotify>();\n\n    private delegate void HandleNotify(IpcMessageBody data, JsonIpcDirectRoutedContext context);\n\n    protected override void OnHandleNotify(IpcDirectRoutedMessage message, PeerMessageArgs e)\n    {\n        var routedPath = message.RoutedPath;\n\n        if (HandleNotifyDictionary.TryGetValue(routedPath, out var handleNotify))\n        {\n            var data = message.GetData();\n\n            var context = new JsonIpcDirectRoutedContext(e.PeerName);\n            e.SetHandle(\"RawByteIpcDirectRouted Handled in MessageReceived\");\n\n            try\n            {\n                // 不等了，也没啥业务\n                _ = IpcProvider.IpcContext.TaskPool.Run(() =>\n                {\n                    handleNotify(data, context);\n                });\n            }\n            catch (Exception exception)\n            {\n                // 不能让这里的异常对外抛出，否则其他业务也许莫名不执行\n                Logger.Error(exception,\n                    $\"[{nameof(RawByteIpcDirectRoutedProvider)}] HandleNotify Method={handleNotify.Method}\");\n            }\n        }\n        else\n        {\n            // 考虑可能有多个实例，每个实例处理不同的业务情况\n        }\n    }\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler(string routedPath,\n        Func<IpcMessageBody, IpcMessage> handler)\n        => AddRequestHandler(routedPath, (data, _) => handler(data));\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler(string routedPath,\n        Func<IpcMessageBody, JsonIpcDirectRoutedContext, IpcMessage> handler)\n        => AddRequestHandler(routedPath, (data, context) => Task.FromResult(handler(data, context)));\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler(string routedPath, Func<IpcMessageBody, Task<IpcMessage>> handler)\n        => AddRequestHandler(routedPath, (data, _) => handler(data));\n\n    /// <summary>\n    /// 添加请求的处理\n    /// </summary>\n    /// <param name=\"routedPath\"></param>\n    /// <param name=\"handler\"></param>\n    public void AddRequestHandler(string routedPath,\n        Func<IpcMessageBody, JsonIpcDirectRoutedContext, Task<IpcMessage>> handler)\n    {\n        ThrowIfStarted();\n\n        if (!HandleRequestDictionary.TryAdd(routedPath, HandleRequest))\n        {\n            throw new InvalidOperationException($\"重复添加对 {routedPath} 的处理\");\n        }\n\n        Task<IpcMessage> HandleRequest(IpcMessageBody data, JsonIpcDirectRoutedContext context)\n        {\n            return handler(data, context);\n        }\n    }\n\n    private delegate Task<IpcMessage> HandleRequest(IpcMessageBody data, JsonIpcDirectRoutedContext context);\n\n    private ConcurrentDictionary<string, HandleRequest> HandleRequestDictionary { get; } =\n        new ConcurrentDictionary<string, HandleRequest>();\n\n    protected override async Task<IIpcResponseMessage> OnHandleRequestAsync(IpcDirectRoutedMessage message,\n        IIpcRequestContext requestContext)\n    {\n        var routedPath = message.RoutedPath;\n\n        if (HandleRequestDictionary.TryGetValue(routedPath, out var handler))\n        {\n            var context = new JsonIpcDirectRoutedContext(requestContext.Peer.PeerName);\n            var taskPool = IpcProvider.IpcContext.TaskPool;\n\n            var data = message.GetData();\n\n            try\n            {\n                var ipcMessage = await taskPool.Run(async () =>\n                {\n                    return await handler(data, context);\n                });\n\n                IIpcResponseMessage response = new IpcHandleRequestMessageResult(ipcMessage);\n                return response;\n            }\n            catch (Exception exception)\n            {\n                // 由于 handler 是业务端传过来的，在框架层需要接住异常，否则 IPC 框架将会因为某个业务抛出异常然后丢失消息\n                Logger.Error(exception,\n                    $\"[{nameof(RawByteIpcDirectRoutedProvider)}] HandleNotify Method={handler.Method}\");\n                // 也有可能是错误处理了不应该调度到这里的业务处理的消息从而抛出异常，继续调度到下一项\n                return KnownIpcResponseMessages.CannotHandle;\n            }\n        }\n        else\n        {\n            // 考虑可能有多个实例，每个实例处理不同的业务情况\n            return KnownIpcResponseMessages.CannotHandle;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/Ack.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// 用于作为消息的回复编号\n    /// <para>\n    /// 仅为了不直接使用 <see cref=\"ulong\"/> 而定义一个类型，解决使用 <see cref=\"ulong\"/> 和业务上存在混乱\n    /// </para>\n    /// </summary>\n    public readonly struct Ack\n    {\n        /// <summary>\n        /// 创建消息的回复号\n        /// </summary>\n        /// <param name=\"ack\"></param>\n        public Ack(ulong ack)\n        {\n            Value = ack;\n        }\n\n        /// <summary>\n        /// 具体的编号值\n        /// </summary>\n        public ulong Value { get; }\n\n        /// <summary>\n        /// 转换逻辑\n        /// </summary>\n        /// <param name=\"ack\"></param>\n        public static implicit operator Ack(ulong ack)\n        {\n            return new Ack(ack);\n        }\n\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return $\"Ack={Value}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/CoreMessageType.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// 在 IPC 框架内部用来标识消息的类型。\n    /// 此枚举是内部的，不要求每一套 IPC 实现都完全实现这里的所有类型的消息，因此这里可以提供目前能识别的所有类型消息的完全集合。\n    /// </summary>\n    [Flags]\n    internal enum CoreMessageType\n    {\n        /// <summary>\n        /// 框架内部必须处理的消息或回应。\n        /// </summary>\n        NotMessageBody = 0,\n\n        /// <summary>\n        /// 无特殊标识的消息。IPC 框架无法准确得知此消息的具体内容。\n        /// </summary>\n        Raw = 1 << 0,\n\n        /// <summary>\n        /// 标记为字符串的消息。IPC 框架知道消息体是一个字符串。\n        /// </summary>\n        String = 1 << 1,\n\n        /// <summary>\n        /// 标记为 .NET 对象的消息。IPC 框架知道消息体是用 JSON 序列化过的 .NET 对象。\n        /// </summary>\n        JsonObject = 1 << 2,\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IIpcResponseMessage.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// 处理客户端请求的结果\n    /// </summary>\n    public interface IIpcResponseMessage\n    {\n        /// <summary>\n        /// 返回给对方的信息\n        /// </summary>\n        IpcMessage ResponseMessage { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IpcClientRequestMessage.cs",
    "content": "﻿using System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\n\nnamespace dotnetCampus.Ipc.Messages\n{\n    class IpcClientRequestMessage\n    {\n        public IpcClientRequestMessage(IpcBufferMessageContext ipcBufferMessageContext, Task<IpcMessageBody> task, IpcClientRequestMessageId messageId)\n        {\n            IpcBufferMessageContext = ipcBufferMessageContext;\n            Task = task;\n            MessageId = messageId;\n        }\n\n        public IpcBufferMessageContext IpcBufferMessageContext { get; }\n\n        /// <summary>\n        /// 用于等待消息被对方回复完成\n        /// </summary>\n        public Task<IpcMessageBody> Task { get; }\n\n        public IpcClientRequestMessageId MessageId { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IpcClientRequestMessageId.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// 客户端请求的信息号\n    /// </summary>\n    /// 通过信息号可以用来在服务器端返回，让客户端知道服务器端返回的响应是对应那个信息\n    public readonly struct IpcClientRequestMessageId\n    {\n        /// <summary>\n        /// 创建信息号\n        /// </summary>\n        /// <param name=\"messageIdValue\"></param>\n        public IpcClientRequestMessageId(ulong messageIdValue)\n        {\n            MessageIdValue = messageIdValue;\n        }\n\n        /// <summary>\n        /// 信息号的值\n        /// </summary>\n        public ulong MessageIdValue { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IpcMessage.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Text;\nusing dotnetCampus.Ipc.Context;\n\nnamespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// 可在 IPC 框架中传输（收、发）的消息。\n    /// </summary>\n    public readonly struct IpcMessage\n    {\n        /// <summary>\n        /// 创建一条可在 IPC 框架中传输的消息。\n        /// </summary>\n        /// <param name=\"tag\">请标记此消息用于在调试过程中追踪。</param>\n        /// <param name=\"body\">IPC 消息的具体内容。</param>\n        [DebuggerStepThrough]\n        public IpcMessage(string tag, IpcMessageBody body) : this(tag, body, (ulong) 0)\n        {\n        }\n\n        /// <summary>\n        /// 创建一条可在 IPC 框架中传输的消息。\n        /// </summary>\n        /// <param name=\"tag\"></param>\n        /// <param name=\"body\"></param>\n        /// <param name=\"ipcMessageHeader\"></param>\n        [DebuggerStepThrough]\n        public IpcMessage(string tag, IpcMessageBody body, ulong ipcMessageHeader)\n        {\n            Tag = tag;\n            Body = body;\n            IpcMessageHeader = ipcMessageHeader;\n        }\n\n        /// <summary>\n        /// 创建一条可在 IPC 框架中传输的消息。\n        /// </summary>\n        /// <param name=\"tag\">请标记此消息用于在调试过程中追踪。</param>\n        /// <param name=\"data\">IPC 消息的具体内容。</param>\n        [DebuggerStepThrough]\n        public IpcMessage(string tag, byte[] data) : this(tag, new IpcMessageBody(data))\n        {\n        }\n\n        /// <summary>\n        /// 用于在调试过程中追踪此 IPC 消息。\n        /// </summary>\n        public string Tag { get; }\n\n        /// <summary>\n        /// IPC 消息的具体内容。\n        /// </summary>\n        public IpcMessageBody Body { get; }\n\n        ///// <summary>\n        ///// 标记此消息可被 IPC 框架识别和处理的类型。\n        ///// </summary>\n        //internal CoreMessageType CoreMessageType { get; }\n\n        /// <summary>\n        /// 消息头类型，用来标识这条消息属于什么机制发送的消息。默认是 0 表示 Raw 裸消息。为 0 时，将不会带在发送的数据里面。框架内预设的消息类型，请参阅 <see cref=\"KnownMessageHeaders\"/> 类\n        /// </summary>\n        public ulong IpcMessageHeader { get; }\n\n        /// <summary>\n        /// 调试使用的属性\n        /// </summary>\n        public KnownMessageHeaders Header => (KnownMessageHeaders) IpcMessageHeader;\n\n        internal IpcBufferMessageContext ToIpcBufferMessageContextWithMessageHeader(IpcMessageCommandType ipcMessageCommandType)\n        {\n            if (IpcMessageHeader == 0)\n            {\n                return new IpcBufferMessageContext(Tag, ipcMessageCommandType, Body);\n            }\n\n            var header = BitConverter.GetBytes(IpcMessageHeader);\n            var ipcBufferMessageContext =\n                new IpcBufferMessageContext(Tag, ipcMessageCommandType, new IpcMessageBody(header), Body);\n            return ipcBufferMessageContext;\n        }\n\n        internal string ToDebugString()\n        {\n            var guessBodyText = \"\";\n            try\n            {\n                // 猜测 Body 的内容\n                guessBodyText = Encoding.UTF8.GetString(Body.Buffer, Body.Start, Body.Length);\n            }\n            catch\n            {\n                // 忽略\n            }\n\n            return $\"[IpcMessage] Header={Header};Tag={Tag};Body=[{string.Join(\" \", Body.Buffer.Skip(Body.Start).Take(Body.Length).Select(t => t.ToString(\"X2\")))}](GuessText={guessBodyText})\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IpcMessageBody.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\n\nnamespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// 表示一段 Ipc 消息内容\n    /// </summary>\n    public readonly struct IpcMessageBody\n    {\n        /// <summary>\n        /// 创建一段 Ipc 消息内容\n        /// </summary>\n        /// <param name=\"buffer\"></param>\n        [DebuggerStepThrough]\n        public IpcMessageBody(byte[] buffer)\n        {\n            Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));\n            Start = 0;\n            Length = buffer.Length;\n        }\n\n        /// <summary>\n        /// 创建一段 Ipc 消息内容\n        /// </summary>\n        /// <param name=\"buffer\"></param>\n        /// <param name=\"start\"></param>\n        /// <param name=\"length\"></param>\n        [DebuggerStepThrough]\n        public IpcMessageBody(byte[] buffer, int start, int length)\n        {\n            if (start < 0 || start > buffer.Length - 1)\n            {\n                throw new ArgumentOutOfRangeException(nameof(start), \"消息体长度必须大于 0。如果此消息来自发送方，请检查是否发送了消息体长度为 0 的消息。\");\n            }\n\n            if (length < 0 || start + length > buffer.Length)\n            {\n                throw new ArgumentOutOfRangeException(nameof(length));\n            }\n\n            Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));\n            Start = start;\n            Length = length;\n        }\n\n        /// <summary>\n        /// 缓存数据\n        /// </summary>\n        public byte[] Buffer { get; }\n\n        /// <summary>\n        /// 缓存数据的起始点\n        /// </summary>\n        public int Start { get; }\n\n        /// <summary>\n        /// 数据长度\n        /// </summary>\n        public int Length { get; }\n\n        internal static IpcMessageBody EmptyIpcMessageBody =>\n#if NET45\n            new IpcMessageBody(new byte[0]);\n#else\n            new IpcMessageBody(Array.Empty<byte>());\n#endif\n    }\n\n    /// <summary>\n    /// 给 <see cref=\"IpcMessageBody\"/> 的扩展\n    /// </summary>\n    public static class IpcMessageBodyExtensions\n    {\n        /// <summary>\n        /// 转换为 <see cref=\"MemoryStream\"/> 对象\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <returns></returns>\n        public static MemoryStream ToMemoryStream(this IpcMessageBody message) =>\n            new MemoryStream(message.Buffer, message.Start, message.Length, false, publiclyVisible: true);\n\n#if NETCOREAPP3_1_OR_GREATER\n        /// <summary>\n        /// 转换为 Span 类型\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <returns></returns>\n        public static Span<byte> AsSpan(this IpcMessageBody message) => new Span<byte>(message.Buffer, message.Start, message.Length);\n#endif\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IpcMessageContext.cs",
    "content": "﻿using dotnetCampus.Ipc.Utils.Buffers;\n\nnamespace dotnetCampus.Ipc.Messages\n{\n    internal readonly struct IpcMessageContext\n    {\n        public IpcMessageContext(in ulong ack, byte[] messageBuffer, in uint messageLength,\n            ISharedArrayPool sharedArrayPool)\n        {\n            Ack = ack;\n            MessageBuffer = messageBuffer;\n            MessageLength = messageLength;\n            SharedArrayPool = sharedArrayPool;\n        }\n\n        public Ack Ack { get; }\n        public byte[] MessageBuffer { get; }\n        public uint MessageLength { get; }\n        public ISharedArrayPool SharedArrayPool { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/IpcMessageResult.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Messages\n{\n    class IpcMessageResult\n    {\n        public IpcMessageResult(bool success, in IpcMessageContext ipcMessageContext = default,\n            in IpcMessageCommandType ipcMessageCommandType = IpcMessageCommandType.Unknown)\n        {\n            Success = success;\n            IpcMessageContext = ipcMessageContext;\n            IpcMessageCommandType = ipcMessageCommandType;\n        }\n\n        public IpcMessageResult(string debugText) : this(success: false)\n        {\n            DebugText = debugText;\n        }\n\n        public bool Success { get; }\n        public IpcMessageContext IpcMessageContext { get; }\n\n        /// <summary>\n        /// 用于调试的信息\n        /// </summary>\n        public string? DebugText { get; }\n\n        internal IpcMessageCommandType IpcMessageCommandType { get; }\n\n        public void Deconstruct(out bool success, out IpcMessageContext ipcMessageContext)\n        {\n            success = Success;\n            ipcMessageContext = IpcMessageContext;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Messages/KnownResponseMessages.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\n\nnamespace dotnetCampus.Ipc.Messages\n{\n    /// <summary>\n    /// IPC 框架已知的 IPC 响应消息。\n    /// </summary>\n    public static class KnownIpcResponseMessages\n    {\n        /// <summary>\n        /// 不会处理此种类型的 IPC 消息，因此返回了一个“不会处理”响应。\n        /// </summary>\n        public static IIpcResponseMessage CannotHandle { get; } = new NamedCanNotHandleIpcResponseMessage(nameof(CannotHandle));\n\n        /// <summary>\n        /// 是否传入的响应消息是一个“不会/能处理”响应消息。\n        /// </summary>\n        /// <param name=\"responseMessage\"></param>\n        /// <returns></returns>\n        internal static bool IsCanNotHandleResponseMessage(IIpcResponseMessage responseMessage)\n        {\n            return responseMessage is NamedCanNotHandleIpcResponseMessage;\n        }\n\n        /// <summary>\n        /// 是否自定义的“不会/能处理”响应消息。\n        /// </summary>\n        /// <param name=\"responseMessage\"></param>\n        /// <returns></returns>\n        internal static bool IsCustomCanNotHandleResponseMessage(IIpcResponseMessage responseMessage)\n        {\n            return responseMessage is NamedCanNotHandleIpcResponseMessage namedCanNotHandleIpcResponseMessage &&\n                   namedCanNotHandleIpcResponseMessage.ResponseMessage.Body.Length > 0;\n        }\n\n        /// <summary>\n        /// 创建带特殊信息的“不会/能处理”响应消息。\n        /// </summary>\n        /// <param name=\"responseMessage\"></param>\n        /// <returns></returns>\n        internal static IIpcResponseMessage CreateCanNotHandleResponseMessage(IpcMessage responseMessage)\n        {\n            return new NamedCanNotHandleIpcResponseMessage(responseMessage);\n        }\n\n        [DebuggerDisplay(\"IpcResponseMessage.{\" + nameof(Name) + \",nq}\")]\n        private sealed class NamedCanNotHandleIpcResponseMessage : IIpcResponseMessage, IEquatable<NamedCanNotHandleIpcResponseMessage>\n        {\n            public NamedCanNotHandleIpcResponseMessage(string name)\n            {\n                Name = name;\n                ResponseMessage = new(name, new byte[0]);\n            }\n\n            public NamedCanNotHandleIpcResponseMessage(IpcMessage responseMessage)\n            {\n                Name = responseMessage.Tag;\n                ResponseMessage = responseMessage;\n            }\n\n            internal string Name { get; }\n\n            public IpcMessage ResponseMessage { get; }\n\n            public override bool Equals(object? obj)\n            {\n                return obj is NamedCanNotHandleIpcResponseMessage message && Equals(message);\n            }\n\n            public bool Equals(NamedCanNotHandleIpcResponseMessage? other)\n            {\n                return other is not null && Name == other.Name;\n            }\n\n            public override int GetHashCode()\n            {\n                return 539060726 + EqualityComparer<string>.Default.GetHashCode(Name);\n            }\n\n            public static bool operator ==(NamedCanNotHandleIpcResponseMessage left, NamedCanNotHandleIpcResponseMessage right)\n            {\n                return left.Equals(right);\n            }\n\n            public static bool operator !=(NamedCanNotHandleIpcResponseMessage left, NamedCanNotHandleIpcResponseMessage right)\n            {\n                return !(left == right);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Package/build/Package.props",
    "content": "<Project>\n\n  <PropertyGroup>\n    <!--\n      The analyzers in this package is very important, so we treat related warnings as errors.\n      - CS8785: Analyzer does not generate sources\n      - CS9057: Analyzer version is greater than the compiler version\n    -->\n    <WarningsAsErrors>$(WarningsAsErrors);CS8785;CS9057</WarningsAsErrors>\n  </PropertyGroup>\n\n</Project>\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcClientService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Pipes;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.ExceptionServices;\nusing System.Runtime.InteropServices;\nusing System.Security.Principal;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Context.LoggingContext;\nusing dotnetCampus.Ipc.Diagnostics;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes.PipeConnectors;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.IO;\nusing dotnetCampus.Ipc.Utils.Logging;\nusing dotnetCampus.Threading;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    /// 管道的客户端，用于发送消息\n    /// </summary>\n    /// 采用两个半工的管道做到双向通讯，这里的管道客户端用于发送\n    public class IpcClientService : IRawMessageWriter, IDisposable, IClientMessageWriter\n    {\n        /// <summary>\n        /// 连接其他端，用来发送\n        /// </summary>\n        /// <param name=\"ipcContext\"></param>\n        /// <param name=\"peerName\">对方</param>\n        internal IpcClientService(IpcContext ipcContext, string peerName = IpcContext.DefaultPipeName)\n        {\n            IpcContext = ipcContext;\n            PeerName = peerName;\n\n            DoubleBufferTask = new DoubleBufferTask<Func<Task>>(DoTask);\n        }\n\n        private async Task DoTask(List<Func<Task>> list)\n        {\n            foreach (var func in list)\n            {\n                try\n                {\n                    await func().ConfigureAwait(false);\n                }\n                catch (Exception e)\n                {\n                    IpcContext.Logger.Error($\"[{nameof(IpcClientService)}.{nameof(DoTask)}] {e}\");\n                }\n            }\n        }\n\n        private readonly TaskCompletionSource<NamedPipeClientStreamResult> _namedPipeClientStreamTaskCompletionSource = new TaskCompletionSource<NamedPipeClientStreamResult>();\n\n        private Task<NamedPipeClientStreamResult> NamedPipeClientStreamTask => _namedPipeClientStreamTaskCompletionSource.Task;\n\n        readonly struct NamedPipeClientStreamResult\n        {\n            public NamedPipeClientStreamResult(NamedPipeClientStream? namedPipeClientStream)\n            {\n                NamedPipeClientStream = namedPipeClientStream!;\n                Exception = null;\n            }\n\n            public NamedPipeClientStreamResult(Exception exception)\n            {\n                Exception = exception;\n                NamedPipeClientStream = null!;\n            }\n\n            public bool Success => NamedPipeClientStream is not null;\n            public NamedPipeClientStream NamedPipeClientStream { get; }\n            public Exception? Exception { get; }\n        }\n\n        internal AckManager AckManager => IpcContext.AckManager;\n\n        private IpcConfiguration IpcConfiguration => IpcContext.IpcConfiguration;\n\n        /// <summary>\n        /// 上下文\n        /// </summary>\n        public IpcContext IpcContext { get; }\n\n        private PeerRegisterProvider PeerRegisterProvider => IpcContext.PeerRegisterProvider;\n\n        /// <summary>\n        /// 对方\n        /// </summary>\n        public string PeerName { get; }\n\n        private ILogger Logger => IpcContext.Logger;\n\n        /// <summary>\n        /// 启动客户端，启动的时候将会去主动连接服务端，然后向服务端注册自身\n        /// </summary>\n        /// <param name=\"shouldRegisterToPeer\">是否需要向对方注册</param>\n        /// <exception cref=\"IpcClientPipeConnectionException\">连接失败时抛出</exception>\n        /// <returns></returns>\n        public async Task Start(bool shouldRegisterToPeer = true)\n        {\n            var result = await StartInternalAsync(isReConnect: false, shouldRegisterToPeer, onlyConnectToExistingPeer: false/*不是只连接存在的对方，如果对方还不存在，则进行等待*/);\n\n            if (!result)\n            {\n                throw new IpcClientPipeConnectionException(PeerName);\n            }\n        }\n\n        /// <summary>\n        /// 尝试连接到已经存在的 Peer 方法\n        /// </summary>\n        /// <returns></returns>\n        internal Task<bool> TryConnectToExistingPeerAsync()\n        {\n            return StartInternalAsync(isReConnect: false, shouldRegisterToPeer: false, onlyConnectToExistingPeer: true);\n        }\n\n        /// <inheritdoc cref=\"Start\"/>\n        /// <param name=\"isReConnect\">是否属于重新连接</param>\n        /// <param name=\"shouldRegisterToPeer\">是否需要向对方注册</param>\n        /// <param name=\"onlyConnectToExistingPeer\">只连接存在的对方</param>\n        /// <returns>True:启动成功</returns>\n        internal async Task<bool> StartInternalAsync(bool isReConnect, bool shouldRegisterToPeer, bool onlyConnectToExistingPeer)\n        {\n            var localClient = IpcContext.PipeName;\n            var remoteServer = PeerName;\n\n            Logger.Trace($\"StartInternalAsync Connecting NamedPipe. LocalClient:'{localClient}';RemoteServer:'{remoteServer}'\");\n\n            if (onlyConnectToExistingPeer)\n            {\n                if (!PipeHelper.IsPipeExists(remoteServer))\n                {\n                    // 如果只连接存在的 Peer 且当前不存在，则直接返回\n                    _namedPipeClientStreamTaskCompletionSource.TrySetResult(new NamedPipeClientStreamResult(namedPipeClientStream: null));\n                    return false;\n                }\n            }\n\n            var namedPipeClientStream = new NamedPipeClientStream(\".\", PeerName, PipeDirection.Out,\n                PipeOptions.None, TokenImpersonationLevel.Impersonation);\n\n            try\n            {\n                var result = await ConnectNamedPipeAsync(isReConnect, namedPipeClientStream, onlyConnectToExistingPeer);\n                if (!result)\n                {\n                    _namedPipeClientStreamTaskCompletionSource.TrySetResult(new NamedPipeClientStreamResult(namedPipeClientStream: null));\n\n                    return false;\n                }\n            }\n            catch (Exception e)\n            {\n                // 理论上不应该存在任何异常的才对，但是由于开放给上层业务端定制。如果存在任何业务端的异常，那就应该设置给 _namedPipeClientStreamTaskCompletionSource 里。否则有一些逻辑将会进入等待，如 Write 系列，等待的 _namedPipeClientStreamTaskCompletionSource 的 Task 将永远不会被释放\n                // 包装到 IpcClientPipeConnectionException 里面，方便其他逻辑捕获异常。毕竟要是上层业务端定制的逻辑抛出奇怪类型的异常，那调用 Write 系列的就不好捕获\n                _namedPipeClientStreamTaskCompletionSource.TrySetResult(new NamedPipeClientStreamResult(new IpcClientPipeConnectionException(PeerName, e)));\n\n                // 为什么不能调用 SetException 方法？因为以下被注释的代码如果被调用，如果没有任何逻辑等待 _namedPipeClientStreamTaskCompletionSource 的 Task 将会抛出到 TaskScheduler.UnobservedTaskException 里。虽然没有什么事情发生，但是对于某些客户端来说，会让一些伙伴以为存在大坑\n                //_namedPipeClientStreamTaskCompletionSource.TrySetException\n                throw;\n            }\n\n            _namedPipeClientStreamTaskCompletionSource.TrySetResult(new NamedPipeClientStreamResult(namedPipeClientStream));\n\n            if (shouldRegisterToPeer)\n            {\n                // 启动之后，向对方注册，此时对方是服务器\n                await RegisterToPeerAsync();\n            }\n\n            return true;\n        }\n\n        /// <summary>\n        /// 连接命名管道\n        /// </summary>\n        /// <param name=\"isReConnect\">是否属于重新连接</param>\n        /// <param name=\"namedPipeClientStream\"></param>\n        /// <param name=\"onlyConnectToExistingPeer\">只连接存在的对方</param>\n        /// <returns>True 连接成功</returns>\n        /// 独立方法，方便 dnspy 调试\n        private async Task<bool> ConnectNamedPipeAsync(bool isReConnect, NamedPipeClientStream namedPipeClientStream, bool onlyConnectToExistingPeer)\n        {\n            var connector = IpcContext.IpcClientPipeConnector;\n\n            if (connector == null)\n            {\n                var timeout = Timeout.Infinite;\n                if (onlyConnectToExistingPeer)\n                {\n                    // 如果是只连接存在的对方的情况，即使对方存在，也需要设置一个短暂的超时时间\n                    // 为什么这里还需要设置超时时间，这是因为可能上一步判断 IsPipeExists 时，对方还是存在的，然而当前准备连接的时候，对方已经挂了，因此不能无限等待，需要设置一个短暂的时间\n                    timeout = 10;\n                }\n\n                try\n                {\n                    await DefaultConnectNamedPipeAsync(namedPipeClientStream, timeout);\n                }\n                catch (Exception e)\n                {\n                    if (onlyConnectToExistingPeer && e is IpcPipeConnectionException ipcPipeConnectionException)\n                    {\n                        if (ipcPipeConnectionException.InnerException is TimeoutException)\n                        {\n                            // 如果是只连接存在的对方，且连接超时了，则直接返回 false 证明对方现在无法被连接上，正常就是对方不存在\n                            return false;\n                        }\n                    }\n\n                    throw;\n                }\n                return true;\n            }\n            else\n            {\n                return await CustomConnectNamedPipeAsync(connector, isReConnect, namedPipeClientStream, onlyConnectToExistingPeer);\n            }\n        }\n\n        /// <summary>\n        /// 自定义的连接方式\n        /// </summary>\n        /// <param name=\"ipcClientPipeConnector\"></param>\n        /// <param name=\"isReConnect\">是否属于重新连接</param>\n        /// <param name=\"namedPipeClientStream\"></param>\n        /// <param name=\"onlyConnectToExistingPeer\"></param>\n        /// <returns></returns>\n        private async Task<bool> CustomConnectNamedPipeAsync(IIpcClientPipeConnector ipcClientPipeConnector,\n            bool isReConnect,\n            NamedPipeClientStream namedPipeClientStream, bool onlyConnectToExistingPeer)\n        {\n            Logger.Trace($\"Connecting NamedPipe by {nameof(CustomConnectNamedPipeAsync)}. LocalClient:'{IpcContext.PipeName}';RemoteServer:'{PeerName}'\");\n            var cancellationToken = CancellationToken.None;\n\n            if (onlyConnectToExistingPeer)\n            {\n                cancellationToken = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)).Token;\n            }\n\n            var ipcClientPipeConnectContext = new IpcClientPipeConnectionContext(PeerName, namedPipeClientStream, cancellationToken, isReConnect);\n            var result = await ipcClientPipeConnector.ConnectNamedPipeAsync(ipcClientPipeConnectContext);\n            Logger.Trace($\"Connected NamedPipe by {nameof(CustomConnectNamedPipeAsync)} Success={result.Success} {result.Reason}. LocalClient:'{IpcContext.PipeName}';RemoteServer:'{PeerName}'\");\n\n            return result.Success;\n        }\n\n        /// <summary>\n        /// 默认的连接方式\n        /// </summary>\n        /// <param name=\"namedPipeClientStream\"></param>\n        /// <param name=\"timeout\"></param>\n        /// <returns></returns>\n        private async Task DefaultConnectNamedPipeAsync(NamedPipeClientStream namedPipeClientStream, int timeout)\n        {\n            var localClient = IpcContext.PipeName;\n            var remoteServer = PeerName;\n\n            Logger.Trace($\"Connecting NamedPipe by {nameof(DefaultConnectNamedPipeAsync)}. LocalClient:'{localClient}';RemoteServer:'{remoteServer}'\");\n            // 由于 dotnet 6 和以下版本的 ConnectAsync 的实现，只是通过 Task.Run 方法而已，因此统一采用相同的方法即可\n\n            await Task.Run(ConnectNamedPipe);\n\n            void ConnectNamedPipe()\n            {\n                try\n                {\n                    namedPipeClientStream.Connect(timeout);\n\n                    // 强行捕获变量，方便调试是在等待哪个连接\n                    Logger.Trace($\"Connected NamedPipe by {nameof(DefaultConnectNamedPipeAsync)}. LocalClient:'{localClient}';RemoteServer:'{remoteServer}'\");\n                }\n                catch (Exception e)\n                {\n                    // 管道连接失败了，比如抛出的是 UnauthorizedAccessException 异常\n                    var message =\n                        $\"IPC Pipe connect to `{remoteServer}` exception by {nameof(DefaultConnectNamedPipeAsync)}. LocalClient:`{localClient}`;RemoteServer:`{remoteServer}`. Exception:{e.Message}\";\n\n                    Logger.Trace(message);\n\n                    throw new IpcPipeConnectionException(remoteServer, localClient, remoteServer, message, e);\n                }\n            }\n        }\n\n        internal async Task RegisterToPeerAsync()\n        {\n            Logger.Trace($\"[{nameof(IpcClientService)}] StartRegisterToPeer PipeName={IpcContext.PipeName}\");\n\n            // 注册自己\n            var peerRegisterMessage = PeerRegisterProvider.BuildPeerRegisterMessage(IpcContext.PipeName);\n            var peerRegisterMessageTracker = new IpcMessageTracker<IpcBufferMessageContext>(\n                IpcContext.PipeName, PeerName, peerRegisterMessage,\n                $\"PeerRegisterMessage PipeName={IpcContext.PipeName}\", IpcContext.Logger);\n            await WriteMessageAsync(peerRegisterMessageTracker);\n        }\n\n        /// <summary>\n        /// 停止客户端\n        /// </summary>\n        public void Stop()\n        {\n            // 告诉服务器端不连接\n        }\n\n        /// <summary>\n        /// 向服务端发送消息\n        /// </summary>\n        /// <remarks>\n        /// 框架层使用的\n        /// </remarks>\n        internal async Task WriteMessageAsync(IpcMessageTracker<IpcBufferMessageContext> tracker)\n        {\n            VerifyNotDisposed();\n\n            try\n            {\n                await DoubleBufferTask.AddTaskAsync(WriteMessageAsyncInner).ConfigureAwait(false);\n            }\n            catch (InvalidOperationException ex)\n            {\n                // 这里的 InvalidOperationException 对应 DoubleBufferTask.AddTask 里抛出的异常。\n                // 在逻辑上确实是使用错误，抛出 InvalidOperationException 是合适的；\n                // 但因为 IPC 的断开发生在任何时刻，根本无法提前规避，所以实际上这里指的是 IPC 远端异常。\n                throw new IpcRemoteException($\"因为已无法连接对方，所以 IPC 消息发送失败。Tag={tracker.Tag} LocalClient:'{IpcContext.PipeName}';RemoteServer:'{PeerName}'\", ex);\n                // @lindexi，这里违背了异常处理原则里的“不应捕获使用异常”的原则，所以 DoubleBufferTask 的设计需要修改，加一个 TryAddTaskAsync 以应对并发场景。\n            }\n\n            async Task WriteMessageAsyncInner()\n            {\n                if (IsDisposed)\n                {\n                    return;\n                }\n\n                var result = await NamedPipeClientStreamTask.ConfigureAwait(false);\n                if (result.Success is false)\n                {\n                    // 理论上框架内不会进入此分支\n                    if (Debugger.IsAttached)\n                    {\n                        // 框架内不应该进入此分支\n                        Debugger.Break();\n                    }\n\n                    if (result.Exception is not null)\n                    {\n                        ExceptionDispatchInfo.Capture(result.Exception).Throw();\n                    }\n\n                    return;\n                }\n\n                var stream = result.NamedPipeClientStream;\n\n                // 追踪、校验消息。\n                var ack = AckManager.GetAck();\n                tracker.Debug(\"IPC start writing...\");\n                tracker.CriticalStep(\"SendCore\", ack, tracker.Message.IpcBufferMessageList);\n\n                IpcContext.LogSendMessage(tracker.Message, PeerName);\n\n                try\n                {\n                    // 发送消息。\n                    await IpcMessageConverter.WriteAsync\n                    (\n                        stream,\n                        IpcConfiguration.MessageHeader,\n                        ack,\n                        tracker.Message,\n                        IpcConfiguration.SharedArrayPool\n                    ).ConfigureAwait(false);\n                    await stream.FlushAsync().ConfigureAwait(false);\n\n                    // 追踪消息。\n                    tracker.Debug(\"IPC finish writing.\");\n                }\n                catch (IOException e)\n                {\n                    // 比如 Pipe is broken. 等异常\n                    tracker.Debug(\"IPC write fail.\");\n                    // 重新封装异常，让上层可以获取到更多信息，且可以使用 IPC 的异常类型进行判断\n                    throw new IpcRemoteException($\"IPC write fail. Tag={tracker.Tag} LocalClient:'{IpcContext.PipeName}';RemoteServer:'{PeerName}'\", e);\n                }\n            }\n        }\n\n        /// <summary>\n        /// 向服务端发送消息\n        /// </summary>\n        /// <param name=\"tracker\"></param>\n        /// <returns></returns>\n        /// <remarks>\n        /// 业务层使用的\n        /// </remarks>\n        internal async Task WriteMessageAsync(IpcMessageTracker<IpcMessage> tracker)\n        {\n            VerifyNotDisposed();\n\n            await DoubleBufferTask.AddTaskAsync(WriteMessageAsyncInner);\n\n            async Task WriteMessageAsyncInner()\n            {\n                if (IsDisposed)\n                {\n                    return;\n                }\n\n                var result = await NamedPipeClientStreamTask.ConfigureAwait(false);\n                if (result.Success is false)\n                {\n                    if (result.Exception is not null)\n                    {\n                        ExceptionDispatchInfo.Capture(result.Exception).Throw();\n                    }\n\n                    return;\n                }\n\n                var stream = result.NamedPipeClientStream;\n\n                var ipcMessageBody = tracker.Message.Body;\n\n                // 追踪、校验消息。\n                var ack = AckManager.GetAck();\n                tracker.Debug(\"IPC start writing...\");\n                tracker.CriticalStep(\"SendCore\", ack, ipcMessageBody);\n\n                IpcContext.LogSendMessage(in ipcMessageBody, PeerName);\n\n                // 发送消息。\n                await IpcMessageConverter.WriteAsync\n                (\n                    stream,\n                    IpcConfiguration.MessageHeader,\n                    AckManager.GetAck(),\n                    // 表示这是业务层的消息\n                    IpcMessageCommandType.Business,\n                    ipcMessageBody.Buffer,\n                    ipcMessageBody.Start,\n                    ipcMessageBody.Length,\n                    IpcConfiguration.SharedArrayPool,\n                    tracker.Tag\n                );\n\n                await stream.FlushAsync().ConfigureAwait(false);\n\n                // 追踪消息。\n                tracker.Debug(\"IPC finish writing.\");\n            }\n        }\n\n        /// <summary>\n        /// 向服务端发送消息\n        /// </summary>\n        /// <param name=\"buffer\"></param>\n        /// <param name=\"offset\"></param>\n        /// <param name=\"count\"></param>\n        /// <param name=\"tag\">这一次写入的是什么内容，用于调试</param>\n        /// <returns></returns>\n        /// <remarks>\n        /// 业务层使用的\n        /// </remarks>\n        public async Task WriteMessageAsync(byte[] buffer, int offset, int count,\n            [CallerMemberName] string tag = null!)\n        {\n            VerifyNotDisposed();\n\n            await DoubleBufferTask.AddTaskAsync(WriteMessageAsyncInner);\n\n            async Task WriteMessageAsyncInner()\n            {\n                if (IsDisposed)\n                {\n                    return;\n                }\n\n                var currentTag = tag;\n                var result = await NamedPipeClientStreamTask.ConfigureAwait(false);\n                if (result.Success is false)\n                {\n                    if (result.Exception is not null)\n                    {\n                        ExceptionDispatchInfo.Capture(result.Exception).Throw();\n                    }\n\n                    return;\n                }\n\n                IpcContext.LogSendMessage(buffer, offset, count, PeerName);\n\n                var stream = result.NamedPipeClientStream;\n                await IpcMessageConverter.WriteAsync\n                (\n                    stream,\n                    IpcConfiguration.MessageHeader,\n                    AckManager.GetAck(),\n                    // 表示这是业务层的消息\n                    IpcMessageCommandType.Business,\n                    buffer,\n                    offset,\n                    count,\n                    IpcConfiguration.SharedArrayPool,\n                    currentTag\n                );\n                await stream.FlushAsync().ConfigureAwait(false);\n            }\n        }\n\n        /*\n        private async Task QueueWriteAsync(Func<Ack, Task> task, string summary)\n        {\n            async Task CreateDoubleBufferTaskFunc(Ack ack)\n            {\n                await DoubleBufferTask.AddTaskAsync(async () => { await task(ack); });\n            }\n\n            await AckManager.DoWillReceivedAck(CreateDoubleBufferTaskFunc, PeerName, TimeSpan.FromSeconds(3), maxRetryCount: 10, summary,\n                IpcContext.Logger);\n        }\n        */\n\n\n        private DoubleBufferTask<Func<Task>> DoubleBufferTask { get; }\n\n        /*\n        /// <summary>\n        /// 向服务器端发送收到某条消息，或用于回复某条消息已收到\n        /// </summary>\n        /// <param name=\"receivedAck\"></param>\n        /// <returns></returns>\n        /// 不需要回复，因为如果消息能发送过去到对方，就是对方收到消息了\n        [Obsolete(DebugContext.DoNotUseAck)]\n        public async Task SendAckAsync(Ack receivedAck)\n        {\n            Logger.Debug($\"[{nameof(IpcClientService)}][{nameof(SendAckAsync)}] {receivedAck} Start AddTaskAsync\");\n\n            var ackMessage = AckManager.BuildAckMessage(receivedAck);\n\n            // 这里不能调用 WriteMessageAsync 方法，因为这些方法都使用了 QueueWriteAsync 方法，在这里面将会不断尝试发送信息，需要收到对方的 ack 才能完成。而作为回复 ack 消息的逻辑，如果还需要等待对方回复 ack 那么将会存在相互等待。本地回复对方的 ack 消息需要等待对方的 ack 消息，而对方的 ack 消息又需要等待本地的回复\n            await DoubleBufferTask.AddTaskAsync(async () =>\n            {\n                await IpcMessageConverter.WriteAsync\n                (\n                    NamedPipeClientStream,\n                    IpcConfiguration.MessageHeader,\n                    ack: IpcContext.AckUsedForReply,\n                    // 需要使用框架的命令\n                    ipcMessageCommandType: IpcMessageCommandType.SendAck,\n                    buffer: ackMessage,\n                    offset: 0,\n                    count: ackMessage.Length,\n                    summary: nameof(SendAckAsync),\n                    Logger\n                );\n                await NamedPipeClientStream.FlushAsync();\n            });\n        }\n        */\n\n        /// <inheritdoc />\n        public void Dispose()\n        {\n            if (IsDisposed)\n            {\n                return;\n            }\n\n            IsDisposed = true;\n\n            if (NamedPipeClientStreamTask.IsCompleted)\n            {\n                var result = NamedPipeClientStreamTask.Result;\n                if (result.Success)\n                {\n                    result.NamedPipeClientStream.Dispose();\n                }\n            }\n\n            DoubleBufferTask.Finish();\n        }\n\n        private bool IsDisposed { set; get; }\n\n        private void VerifyNotDisposed()\n        {\n            if (IsDisposed)\n            {\n                throw new ObjectDisposedException(nameof(IpcClientService));\n            }\n        }\n\n        Task IClientMessageWriter.WriteMessageAsync(in IpcBufferMessageContext ipcBufferMessageContext)\n        {\n            var peerRegisterMessageTracker = new IpcMessageTracker<IpcBufferMessageContext>(\n                IpcContext.PipeName, PeerName, ipcBufferMessageContext,\n                $\"PeerRegisterMessage PipeName={IpcContext.PipeName}\", IpcContext.Logger);\n            return WriteMessageAsync(peerRegisterMessageTracker);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcMessageManagerBase.cs",
    "content": "﻿using System;\nusing System.IO;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    class IpcMessageManagerBase\n    {\n        // 为什么将请求和响应的消息封装都放在一个类里面？这是为了方便更改，和调试\n        // 如果放在两个类或两个文件里面，也许就不好调试对应关系\n        protected static IpcBufferMessageContext CreateResponseMessageInner(IpcClientRequestMessageId messageId, in IpcMessage response)\n        {\n            /*\n             * MessageHeader\n             * MessageId\n             * Response Message Length\n             * Response Message\n             */\n            var messageLength = response.Body.Length;\n            var currentMessageIdByteList = BitConverter.GetBytes(messageId.MessageIdValue);\n\n            IpcMessageBody businessHeader;\n            if (response.IpcMessageHeader == 0)\n            {\n                businessHeader = IpcMessageBody.EmptyIpcMessageBody;\n            }\n            else\n            {\n                // 需要带上头的消息\n                messageLength += sizeof(ulong);\n\n                // 有业务头，加上业务头\n                businessHeader = new IpcMessageBody(BitConverter.GetBytes(response.IpcMessageHeader));\n            }\n\n            var responseMessageLengthByteList = BitConverter.GetBytes(messageLength);\n\n            return new IpcBufferMessageContext\n            (\n                response.Tag,\n                IpcMessageCommandType.ResponseMessage,\n                new[]\n                {\n                    new IpcMessageBody(ResponseMessageHeader),\n                    new IpcMessageBody(currentMessageIdByteList),\n                    new IpcMessageBody(responseMessageLengthByteList),\n                    businessHeader,\n                    response.Body\n                }\n            );\n        }\n\n        protected static IpcBufferMessageContext CreateRequestMessageInner(in IpcMessage request, ulong currentMessageId)\n        {\n            /*\n             * MessageHeader\n             * MessageId\n             * Request Message Length\n             * Request Message\n             */\n            var currentMessageIdByteList = BitConverter.GetBytes(currentMessageId);\n\n            var messageLength = request.Body.Length;\n\n            IpcMessageBody businessHeader;\n            if (request.IpcMessageHeader == 0)\n            {\n                businessHeader = IpcMessageBody.EmptyIpcMessageBody;\n            }\n            else\n            {\n                // 需要带上头的消息\n                messageLength += sizeof(ulong);\n\n                // 有业务头，加上业务头\n                businessHeader = new IpcMessageBody(BitConverter.GetBytes(request.IpcMessageHeader));\n            }\n\n            var requestMessageLengthByteList = BitConverter.GetBytes(messageLength);\n\n            return new IpcBufferMessageContext\n            (\n                request.Tag,\n                IpcMessageCommandType.RequestMessage,\n                new[]\n                {\n                    new IpcMessageBody(RequestMessageHeader),\n                    new IpcMessageBody(currentMessageIdByteList),\n                    new IpcMessageBody(requestMessageLengthByteList),\n                    businessHeader,\n                    request.Body\n                }\n            );\n        }\n\n        private static bool CheckHeader(Stream stream, byte[] header)\n        {\n            for (var i = 0; i < header.Length; i++)\n            {\n                if (stream.ReadByte() == header[i])\n                {\n                }\n                else\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        protected static bool CheckResponseHeader(Stream stream)\n        {\n            var header = ResponseMessageHeader;\n\n            return CheckHeader(stream, header);\n        }\n        protected static bool CheckRequestHeader(Stream stream)\n        {\n            var header = RequestMessageHeader;\n            return CheckHeader(stream, header);\n        }\n\n\n        /// <summary>\n        /// 用于标识请求消息\n        /// </summary>\n        /// 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74 0x00 就是 Request 字符\n        protected static byte[] RequestMessageHeader { get; } = { 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x00 };\n\n        protected static byte[] ResponseMessageHeader { get; } = { 0x52, 0x65, 0x73, 0x70, 0x6F, 0x6E, 0x73, 0x65 };\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcMessageRequestManager.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.IO;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    /// 请求管理\n    /// <para/>\n    /// 这是用来期待发送一条消息之后，在对方的业务层能有回复的消息 <para/>\n    /// 这是服务器-客户端模型 <para/>\n    /// 客户端向服务器发起请求，服务器端业务层处理之后返回响应信息 <para/>\n    /// 通过调用 <see cref=\"CreateRequestMessage\"/> 方法创建出请求消息 <para/>\n    /// 然后将此消息的 <see cref=\"IpcClientRequestMessage.IpcBufferMessageContext\"/> 通过现有 <see cref=\"PeerProxy\"/> 发送到服务器端。同时客户端可以使用 <see cref=\"IpcClientRequestMessage.Task\"/> 进行等待 <para/>\n    /// 服务器端接收到 <see cref=\"IpcClientRequestMessage\"/> 的内容，将会在 <see cref=\"OnIpcClientRequestReceived\"/> 事件触发，这个事件将会带上 <see cref=\"IpcClientRequestArgs\"/> 参数 <para/>\n    /// 在服务器端处理完成之后，底层的方法是通过调用 <see cref=\"IpcMessageResponseManager.CreateResponseMessage\"/> 方法创建响应消息，通过 <see cref=\"PeerProxy\"/> 发送给客户端 <para/>\n    /// 客户端收到了服务器端的响应信息，将会释放 <see cref=\"IpcClientRequestMessage.Task\"/> 任务，客户端从 <see cref=\"IpcClientRequestMessage.Task\"/> 可以拿到服务器端的返回值\n    /// </summary>\n    class IpcMessageRequestManager : IpcMessageManagerBase\n    {\n        /// <summary>\n        /// 创建请求管理\n        /// </summary>\n        /// <param name=\"ipcContext\"></param>\n        public IpcMessageRequestManager(IpcContext ipcContext)\n        {\n            IpcContext = ipcContext;\n        }\n\n        internal IpcContext IpcContext { get; }\n\n        /// <summary>\n        /// 等待响应的数量\n        /// </summary>\n        public int WaitingResponseCount\n        {\n            get\n            {\n                lock (Locker)\n                {\n                    return TaskList.Count;\n                }\n            }\n        }\n\n        public IpcClientRequestMessage CreateRequestMessage(IpcMessage request)\n        {\n            ulong currentMessageId;\n            var task = new TaskCompletionSource<IpcMessageBody>();\n            lock (Locker)\n            {\n                currentMessageId = CurrentMessageId;\n                // 在超过 ulong.MaxValue 之后，将会是 0 这个值\n                CurrentMessageId++;\n\n                TaskList[currentMessageId] = task;\n            }\n\n            var requestMessage = CreateRequestMessageInner(request, currentMessageId);\n            return new IpcClientRequestMessage(requestMessage, task.Task, new IpcClientRequestMessageId(currentMessageId));\n        }\n\n        private Dictionary<ulong, TaskCompletionSource<IpcMessageBody>> TaskList { get; } =\n            new Dictionary<ulong, TaskCompletionSource<IpcMessageBody>>();\n\n        /// <summary>\n        /// 收到消息，包括收到别人的请求消息，和收到别人的响应消息\n        /// </summary>\n        /// <param name=\"args\"></param>\n        public void OnReceiveMessage(PeerStreamMessageArgs args)\n        {\n            HandleResponse(args);\n            if (args.Handle)\n            {\n                return;\n            }\n\n            HandleRequest(args);\n        }\n\n        /// <summary>\n        /// 断开连接之后的炸掉所有的请求任务\n        /// </summary>\n        /// 重发？其实会丢失上下文，因此不合适\n        /// 如某个业务需要连续发送三条请求消息才能实现\n        /// 但是前面两条消息成功，在发送第三条请求时，对方进程退出\n        /// 如果让第三条请求重新发送，将会让新的重连的对方收到一条非预期的消息\n        ///\n        /// 然而如果不重发的话，也许如上面例子的三条消息差距很大，前两条消息发送之后\n        /// 对方进程挂了，等待一会，才发送第三条消息\n        /// 这也是坑\n        ///\n        /// 然而此时也触发了断开的事件，也许此时的业务端可以处理\n        public void BreakAllRequestTaskByIpcBroken()\n        {\n            List<TaskCompletionSource<IpcMessageBody>> taskList;\n            lock (Locker)\n            {\n                taskList = TaskList.Select(temp => temp.Value).ToList();\n                TaskList.Clear();\n            }\n\n            foreach (var taskCompletionSource in taskList)\n            {\n                var ipcPeerConnectionBrokenException = new IpcPeerConnectionBrokenException();\n                if (taskCompletionSource.TrySetException(ipcPeerConnectionBrokenException))\n                {\n\n                }\n                else\n                {\n                    // 难道在断开的时候，刚好收到消息了？\n                }\n            }\n        }\n\n        private void HandleRequest(PeerStreamMessageArgs args)\n        {\n            if (!args.MessageCommandType.HasFlag(IpcMessageCommandType.RequestMessage))\n            {\n                // 如果没有请求标识，那么返回。\n                return;\n            }\n\n            var message = args.MessageStream;\n            if (message.Length < RequestMessageHeader.Length + sizeof(ulong))\n            {\n                return;\n            }\n\n            if (CheckRequestHeader(message))\n            {\n                // 标记在这一级消费\n                args.SetHandle(message: nameof(HandleRequest));\n\n                var binaryReader = new BinaryReader(message);\n                var messageId = binaryReader.ReadUInt64();\n                var requestMessageLength = binaryReader.ReadInt32();\n                // 兼容旧版本 2.0.0-alpha412 的行为，MessageReceived 中收到的是已经去掉 Request 头的数据\n                var currentPosition = message.Position;\n\n                try\n                {\n                    IpcMessageBody ipcBufferMessage;\n                    if (message is ByteListMessageStream byteListMessageStream)\n                    {\n                        var messageBuffer = byteListMessageStream.IpcMessageContext.MessageBuffer;\n\n                        ipcBufferMessage = new IpcMessageBody(messageBuffer, (int) currentPosition, requestMessageLength);\n                    }\n                    else\n                    {\n                        var requestMessageByteList = binaryReader.ReadBytes(requestMessageLength);\n                        ipcBufferMessage = new IpcMessageBody(requestMessageByteList);\n                    }\n\n                    var ipcClientRequestArgs =\n                        new IpcClientRequestArgs(new IpcClientRequestMessageId(messageId),\n                            ipcBufferMessage,\n                            args.MessageCommandType);\n                    OnIpcClientRequestReceived?.Invoke(this, ipcClientRequestArgs);\n                }\n                finally\n                {\n                    message.Position = currentPosition;\n                }\n            }\n        }\n\n        public event EventHandler<IpcClientRequestArgs>? OnIpcClientRequestReceived;\n\n        private void HandleResponse(PeerStreamMessageArgs args)\n        {\n            if (!args.MessageCommandType.HasFlag(IpcMessageCommandType.ResponseMessage))\n            {\n                // 如果没有命令标识，那么返回\n                return;\n            }\n\n            var message = args.MessageStream;\n\n            if (message.Length < ResponseMessageHeader.Length + sizeof(ulong))\n            {\n                return;\n            }\n\n            if (CheckResponseHeader(message))\n            {\n                // 标记在这一级消费\n                args.SetHandle(message: nameof(HandleResponse));\n\n                var binaryReader = new BinaryReader(message);\n                var messageId = binaryReader.ReadUInt64();\n                TaskCompletionSource<IpcMessageBody>? task = null;\n                lock (Locker)\n                {\n                    // 下面这句代码在 .NET 45 不存在，因此不能使用。换成两次调用，反正性能差不多\n                    //TaskList.Remove(messageId, out task);\n                    if (TaskList.TryGetValue(messageId, out task))\n                    {\n                        TaskList.Remove(messageId);\n                    }\n                    else\n                    {\n                        return;\n                    }\n                }\n\n                // ReSharper disable once ConditionIsAlwaysTrueOrFalse\n                if (task == null)\n                {\n                    return;\n                }\n\n                var responseMessageLength = binaryReader.ReadInt32();\n\n                var currentPosition = message.Position;\n                try\n                {\n                    var responseMessageByteList = binaryReader.ReadBytes(responseMessageLength);\n\n                    // 为什么需要放在线程池执行？原因是在使用 GetResponse 请求时，收到对方的请求，进入以下代码。此时的代码运行在 DispatchMessage 消息循环线程里面\n                    // 通过 CLR 的逻辑可以了解到，在调用 TaskCompletionSource 的 SetResult 方法\n                    // 将会让原本 await TaskCompletionSource.Task 的代码继续执行，在调用 SetResult 方法的线程上继续执行\n                    // 也就是 GetResponse 返回时的执行代码线程就是 DispatchMessage 消息循环线程\n                    // 但是 GetResponse 返回的代码执行是业务代码，如果在业务上锁住了，\n                    // 例如 https://github.com/dotnet-campus/dotnetCampus.Ipc/pull/67/commits/74f7a25446f2107f3556ac97659a89b37f82f885 的 \"IPC 代理生成：IPC 参数和异步 IPC 返回值\" 单元测试，在 GetResponse 之后进行异步转同步等待下一条消息\n                    // 此时消息循环线程进入等待，需要等待下一条消息才能继续。但是下一条消息需要等待消息循环线程收到下一条消息。于是相互等待\n                    // 让业务代码在消息循环线程上运行，是不符合框架设计的。因此让业务代码调度到线程池执行\n                    IpcContext.TaskPool.Run(() => task.SetResult(new IpcMessageBody(responseMessageByteList)));\n                }\n                finally\n                {\n                    message.Position = currentPosition;\n                }\n            }\n        }\n\n        private object Locker => TaskList;\n\n        private ulong CurrentMessageId { set; get; }\n\n        public override string ToString()\n        {\n            return $\"[{nameof(IpcMessageRequestManager)}] PeerName={IpcContext.PipeName} CurrentMessageId={CurrentMessageId} WaitingResponseCount={WaitingResponseCount}\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcMessageResponseManager.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    /// 响应管理器\n    /// </summary>\n    /// 这个类还没设计好，和 <see cref=\"IpcMessageRequestManager\"/> 是重复的\n    /// 完全可以删除\n    class IpcMessageResponseManager : IpcMessageManagerBase\n    {\n        public IpcBufferMessageContext CreateResponseMessage(IpcClientRequestMessageId messageId,\n           in IpcMessage response)\n            => CreateResponseMessageInner(messageId, in response);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcProvider.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Context.LoggingContext;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Utils;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    ///     对等通讯，每个都是服务器端，每个都是客户端\n    /// </summary>\n    /// 这是这个程序集最顶层的类\n    /// 这里有两个概念，一个是对方，另一个是本地\n    /// 对方就是其他的开启的Ipc服务的端，可以在相同的进程内。而本地是指此Ipc服务\n    public class IpcProvider : IIpcProvider, IDisposable\n    {\n        /// <summary>\n        /// 创建对等通讯\n        /// </summary>\n        public IpcProvider() : this(Guid.NewGuid().ToString(\"N\"))\n        {\n        }\n\n        /// <summary>\n        /// 创建对等通讯\n        /// </summary>\n        /// <param name=\"pipeName\">本地服务名，将作为管道名，管道服务端名</param>\n        /// <param name=\"ipcConfiguration\"></param>\n        public IpcProvider(string pipeName, IpcConfiguration? ipcConfiguration = null)\n        {\n            IpcContext = new IpcContext(this, pipeName, ipcConfiguration);\n            IpcContext.IpcConfiguration.AddFrameworkRequestHandler(IpcContext.GeneratedProxyJointIpcContext.RequestHandler);\n            IpcContext.Logger.Trace($\"[IpcProvider] 本地服务名 {pipeName}\");\n\n            PeerManagerInternal = new PeerManager(this);\n        }\n\n        /// <inheritdoc />\n        public IpcContext IpcContext { get; }\n\n        /// <summary>\n        /// 管理所有连接方\n        /// </summary>\n        public IPeerManager PeerManager => PeerManagerInternal;\n\n        private PeerManager PeerManagerInternal { get; }\n\n        /// <summary>\n        /// 是否启动了\n        /// </summary>\n        public bool IsStarted => _ipcServerService != null;\n\n        /// <summary>\n        /// 开启的管道服务端，用于接收消息\n        /// </summary>\n        public IpcServerService IpcServerService\n        {\n            get\n            {\n                if (!IsStarted)\n                {\n                    throw new InvalidOperationException($\"未启动之前，不能获取 IpcServerService 属性的值\");\n                }\n\n                return _ipcServerService!;\n            }\n        }\n\n        /// <summary>\n        /// 启动服务，启动之后将可以被对方连接\n        /// </summary>\n        /// <returns></returns>\n        public async void StartServer()\n        {\n            try\n            {\n                if (IsStarted) return;\n\n                var ipcServerService = new IpcServerService(IpcContext);\n                _ipcServerService = ipcServerService;\n\n                ipcServerService.PeerConnected += IpcServerService_OnPeerConnected;\n\n                // 以下的 Start 是一个循环，不会返回的\n                await ipcServerService.Start().ConfigureAwait(false);\n            }\n            catch (Exception)\n            {\n                // 当前是后台线程了，不能接受任何的抛出\n#if DEBUG\n                // 理论上框架层不会在这里抛出任何异常\n                Debugger.Break();\n#endif\n            }\n        }\n\n        /// <summary>\n        /// 对方连接过来的时候，需要反过来连接对方的服务器端\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        private async void IpcServerService_OnPeerConnected(object? sender, IpcInternalPeerConnectedArgs e)\n        {\n            try\n            {\n                IpcContext.Logger.Debug($\"[OnPeerConnected]IpcProvider.OnPeerConnected PeerName={e.PeerName};CurrentName={IpcContext.PipeName}\");\n\n                // 也许是对方反过来连接\n                if (PeerManagerInternal.TryGetValue(e.PeerName, out var peerProxy))\n                {\n                    IpcContext.Logger.Debug($\"[OnPeerConnected]PeerManager.TryGetValue Success. PeerName={e.PeerName};CurrentName={IpcContext.PipeName}\");\n\n                    // 如果当前的 Peer 已断开且不需要重新连接，那么重新创建 Peer 反过来连接对方的服务器端\n                    if (peerProxy.IsBroken && !IpcContext.IpcConfiguration.AutoReconnectPeers)\n                    {\n                        // 理论上不会进入这个分支，因为如果 IsBroken 将会自动去清理，除非刚好一个断开，然后立刻连接\n                        PeerManagerInternal.RemovePeerProxy(peerProxy);\n                        await ConnectBackToPeer(e);\n                    }\n                    else\n                    {\n                        peerProxy.Update(e);\n                    }\n                }\n                else\n                {\n                    IpcContext.Logger.Debug($\"[OnPeerConnected]PeerManager.TryGetValue Fail. ConnectBackToPeer. PeerName={e.PeerName};CurrentName={IpcContext.PipeName}\");\n\n                    // 其他客户端连接，需要反过来连接对方的服务器端\n                    await ConnectBackToPeer(e);\n                }\n            }\n            catch (Exception exception)\n            {\n                // 当前是后台线程了，不能接受任何的抛出\n                IpcContext.Logger.Error(exception);\n            }\n        }\n\n        private async Task ConnectBackToPeer(IpcInternalPeerConnectedArgs e)\n        {\n            try\n            {\n                IpcContext.Logger.Debug($\"[OnPeerConnected] ConnectBackToPeer. PeerName={e.PeerName};CurrentName={IpcContext.PipeName}\");\n\n                await ConnectBackToPeerCore(e);\n            }\n            catch (ObjectDisposedException)\n            {\n                // 对方刚刚连接过来，然后对方立刻被释放\n                // 这是符合预期的，就不需要抛出异常了\n                IpcContext.Logger.Information(\"ConnectBackToPeer But Peer Disposed.\");\n\n                /*\n                ExceptionName: System.ObjectDisposedException; \n                ExceptionMessage: 无法访问已释放的对象。\n                对象名:“IpcClientService”。; \n                ExceptionStackTrace:    在 dotnetCampus.Ipc.Pipes.IpcClientService.VerifyNotDisposed()\n                   在 dotnetCampus.Ipc.Pipes.IpcClientService.<WriteMessageAsync>d__22.MoveNext()\n                --- 引发异常的上一位置中堆栈跟踪的末尾 ---\n                   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\n                   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n                   在 dotnetCampus.Ipc.Pipes.IpcClientService.<RegisterToPeer>d__20.MoveNext()\n                --- 引发异常的上一位置中堆栈跟踪的末尾 ---\n                   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\n                   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n                   在 dotnetCampus.Ipc.Pipes.IpcClientService.<Start>d__19.MoveNext()\n                --- 引发异常的上一位置中堆栈跟踪的末尾 ---\n                   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\n                   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n                   在 dotnetCampus.Ipc.Pipes.IpcProvider.<ConnectBackToPeer>d__14.MoveNext()\n                --- 引发异常的上一位置中堆栈跟踪的末尾 ---\n                   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\n                   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\n                   在 dotnetCampus.Ipc.Pipes.IpcProvider.<NamedPipeServerStreamPoolPeerConnected>d__13.MoveNext()\n                --- 引发异常的上一位置中堆栈跟踪的末尾 ---\n                   在 System.Runtime.CompilerServices.AsyncMethodBuilderCore.<>c.<ThrowAsync>b__6_1(Object state)\n                   在 System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)\n                   在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\n                   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)\n                   在 System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()\n                   在 System.Threading.ThreadPoolWorkQueue.Dispatch()\n                   在 System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(); \n                 */\n            }\n            catch (IOException)\n            {\n                // 对方刚刚连接过来\n                // 进行注册到对方时，写入到一半，对方挂掉了\n                // 这是符合预期的，就不需要抛出异常了\n                IpcContext.Logger.Information(\"ConnectBackToPeer But Peer IOException.\");\n            }\n            catch (IpcRemoteException ipcRemoteException)\n            {\n                if (ipcRemoteException.InnerException is InvalidOperationException)\n                {\n                    // 这是在 DoubleBufferTask 写入的锅，后续将会换掉\n                    // 符合预期，对方断开\n                    return;\n                }\n\n                // 其他逻辑的对方的锅，记录日志\n                IpcContext.Logger.Information($\"ConnectBackToPeer IpcRemoteException {ipcRemoteException}\");\n            }\n        }\n        private async Task ConnectBackToPeerCore(IpcInternalPeerConnectedArgs e)\n        {\n            var peerName = e.PeerName;\n            //var receivedAck = e.Ack;\n\n            if (PeerManagerInternal.TryGetValue(peerName, out _))\n            {\n                // 预期不会进入此分支，也就是之前没有连接过才对\n                Debug.Assert(false, \"对方连接之前没有记录对方\");\n            }\n            else\n            {\n                // 无须再次启动本地的服务器端，因为有对方连接过来，此时一定开启了本地的服务器端\n                var ipcClientService = CreateIpcClientService(peerName);\n\n                // 此时向对方注册，让对方重新触发逻辑\n                var shouldRegisterToPeer = true;\n                var task = ipcClientService.Start(shouldRegisterToPeer: shouldRegisterToPeer);\n\n                // 此时就建立完成了链接\n                var peer = CreatePeerProxy(ipcClientService);\n                // 先建立链接再继续发送注册，解决多进程同时注册\n                await task;\n\n                // 通知有其他客户端连接过来\n                NotifyPeerConnected(peer);\n\n                /*\n                SendAckAndRegisterToPeer();\n\n                // 发送 ack 同时注册自身\n                async void SendAckAndRegisterToPeer()\n                {\n                    IpcContext.Logger.Debug($\"[{nameof(SendAckAndRegisterToPeer)}] Start SendAckAndRegisterToPeer\");\n                    var ackMessage = IpcContext.AckManager.BuildAckMessage(receivedAck);\n                    var peerRegisterMessage =\n                        IpcContext.PeerRegisterProvider.BuildPeerRegisterMessage(IpcContext.PipeName);\n                    const string summary = nameof(SendAckAndRegisterToPeer);\n\n                    // 消息的顺序是有要求的，先发送注册消息，然后加上回复 Ack 的消息\n                    // 在收到对方的连接的时候，需要去连接对方，而在连接的时候需要有两个步骤\n                    // 1. 回复对方的连接消息，需要发送 Ack 回复\n                    // 2. 连接对方，需要发送注册消息\n                    // 以下将上面两个步骤合并为一条消息，这一条消息包含了注册消息和 Ack 回复的消息\n                    // 为什么注册消息在前面，而回复 Ack 在后面？原因是为了在解析的时候，可以先了解是哪个服务进行连接\n                    // 而且回复 Ack 需要两个信息，一个是 Ack 的值，另一个是连接的设备名。因此让注册消息在前面就能\n                    // 先读取设备名，用于后续回复 Ack 了解是哪个设备回复\n                    var ackAndPeerRegisterMessage =\n                        peerRegisterMessage.BuildWithCombine(summary, IpcMessageCommandType.SendAckAndRegisterToPeer,\n                            mergeBefore: false,\n                            new IpcBufferMessage(ackMessage));\n                    await ipcClientService.WriteMessageAsync(ackAndPeerRegisterMessage);\n\n                    // 此时就建立完成了链接\n                    CreatePeerProxy(ipcClientService);\n                }\n                */\n            }\n\n            PeerProxy CreatePeerProxy(IpcClientService ipcClientService)\n            {\n                var peerProxy = new PeerProxy(e.PeerName, ipcClientService, e, IpcContext);\n\n                if (PeerManagerInternal.TryAdd(peerProxy))\n                {\n                    // 理论上会进入此分支，除非是此时收到了多次的发送\n                }\n                else\n                {\n                    // 后续需要处理，并发收到对方的多次连接\n                    Debug.Assert(false, \"对方的连接并发进入，此时也许会存在多次重复连接对方的服务器端\");\n                }\n\n                return peerProxy;\n            }\n        }\n\n        /// <summary>\n        /// 通知有其他客户端连接过来\n        /// </summary>\n        /// <param name=\"peer\"></param>\n        /// 拆分方法，优化调试\n        private void NotifyPeerConnected(PeerProxy peer)\n        {\n            _ = IpcContext.TaskPool.Run(() => PeerConnected?.Invoke(this, new PeerConnectedArgs(peer)), IpcContext.Logger);\n        }\n\n        /// <summary>\n        /// 本机作为服务端，有对方连接过来时触发\n        /// </summary>\n        /// <remarks>\n        /// 仅被动连接（被对方连接过来）时触发。主动去连接对方时，不会触发此事件。如需要获取无论是主动还是被动连接过来的事件，请使用 <see cref=\"IPeerManager.PeerConnected\"/> 事件\n        /// </remarks>\n        public event EventHandler<PeerConnectedArgs>? PeerConnected;\n\n        /// <summary>\n        /// 获取一个连接到指定 <paramref name=\"peerName\"/> 的客户端。如没有连接过，则需要等待连接。如已建立连接则不需要重新建立连接\n        /// </summary>\n        /// <remarks>\n        /// 禁止在此方法返回之后所在的线程执行将等待 IPC 响应的逻辑的异步做同步等待，否则 IPC 将会停止。例如以下代码是禁止使用的是\n        /// <para/>\n        /// <code>\n        /// var peer = await ipcProvider.GetAndConnectToPeerAsync(\"xxx\");\n        /// var result = peer.GetResponseAsync(xxx).Result; // 这是被禁止的，禁止将等待 IPC 响应的逻辑的异步做同步等待\n        /// </code>\n        /// <para/>\n        /// 此方法的返回之后，如无线程同步上下文，将调度到 IPC 的接收消息端所在线程进行执行。也就是说在此方法返回值之后的逻辑，可以卡住接收消息端，以此解决获取到 Peer 之后，对 Peer 加等事件之前，就接收了 Peer 的消息，从而在事件加等之前由于消息已被处理而漏掉消息。由于接收消息端所在线程需要等待此方法返回之后的同步逻辑执行完成，才能继续接收消息，从而解决了对 Peer 加等事件比消息处理慢的问题\n        /// <para/>\n        /// 但与此也引入另外的问题，那就是如果在此方法调用后的同步逻辑里面，编写了等待此 IPC 的响应的逻辑的异步做同步等待方法，将会导致 IPC 停止工作。其原因是 IPC 的响应需要由接收消息端接收到对方的响应才能完成，然而接收消息端所在线程在等待同步锁，此同步锁的释放需要等待 IPC 的响应完成\n        /// </remarks>\n        /// <param name=\"peerName\">对方</param>\n        /// <returns></returns>\n        public async Task<PeerProxy> GetAndConnectToPeerAsync(string peerName)\n        {\n            var peerProxy = await GetOrCreatePeerProxyAsync(peerName);\n\n            await PeerManagerInternal.WaitForPeerConnectFinishedAsync(peerProxy);\n\n            return peerProxy;\n        }\n\n        /// <summary>\n        /// 尝试获取或连接到已经存在的 Peer 上。如果当前的 Peer 还没起来，则不等待连接，直接返回失败\n        /// </summary>\n        /// <param name=\"peerName\">对方</param>\n        /// <param name=\"shouldWaitPeerConnectFinished\">是否应该等待对方连接回来完成，完全完成双向连接。如设置为 false 则需要自己通过 <see cref=\"ConnectToExistingPeerResult.PeerConnectFinishedTask\"/> 进行等待。默认为 true 表示等待所有准备完成再返回</param>\n        /// <returns></returns>\n        /// 为什么会存在 <paramref name=\"shouldWaitPeerConnectFinished\"/> 参数，这是为了解决极端情况下，刚好本进程能连接到对方，连接完成瞬间，对方挂了，无法反向连接回来的情况。正常不需要设置此参数\n        public async Task<ConnectToExistingPeerResult> TryConnectToExistingPeerAsync(string peerName, bool shouldWaitPeerConnectFinished = true)\n        {\n            if (PeerManagerInternal.TryGetValue(peerName, out var peerProxy))\n            {\n\n            }\n            else\n            {\n                // 如果之前没有建立过连接，则尝试连接\n\n                // 这里无视多次加入，这里的多线程问题也可以忽略\n                StartServer();\n\n                var ipcClientService = CreateIpcClientService(peerName);\n\n                // 尝试连接对方\n                // 连接的时候不会立刻向对方注册自己，只是建立连接关系而已\n                // 这样是因为一旦向对方注册自己，那对方将会反过来向自己注册。然而此时存在多线程安全问题，此时的 PeerManager 还没加入对方。导致以下代码里面的 PeerManager.WaitForPeerConnectFinishedAsync 无法完成等待，导致单元测试失败\n                var result = await ipcClientService.TryConnectToExistingPeerAsync().ConfigureAwait(false);\n                if (!result)\n                {\n                    // 对方不存在\n                    return ConnectToExistingPeerResult.Fail();\n                }\n\n                // 需要确定能连接上对方了，才能加入到 PeerManager 里面。确保不会在下次进来的时候，拿到了一个无法建立连接的 Peer 对象。这里的添加顺序是先确保连接再添加，这就意味着在并行的时候，可能会多次尝试连接。这是符合预期的，本身连接也没有多少损耗，最多只会多创建一个管道而已\n                peerProxy = new PeerProxy(peerName, ipcClientService, IpcContext);\n                PeerManagerInternal.TryAdd(peerProxy);\n\n                // 在 PeerProxy 加入到管理之后，才能向对方注册自己，确保对方收到注册之后，反过来向自己注册时，可以从管理里面拿到注册的对方信息，从而让 PeerManager.WaitForPeerConnectFinishedAsync 能够完成\n                await ipcClientService.RegisterToPeerAsync();\n            }\n\n            // 等待对方回连，建立双向连接\n            Task peerConnectFinishedTask = PeerManagerInternal.WaitForPeerConnectFinishedAsync(peerProxy);\n            if (shouldWaitPeerConnectFinished)\n            {\n                try\n                {\n#if NET6_0_OR_GREATER\n                    // 正常预期就是瞬间连接上的，不会说要等待5秒这么久的，除非系统卡住了，没有执行线程调度。或进入调试断点状态\n                    await peerConnectFinishedTask.WaitAsync(TimeSpan.FromSeconds(5));\n#else\n                    await peerConnectFinishedTask;\n#endif\n                }\n                catch (IpcPeerConnectionBrokenException e)\n                {\n                    // 对方连接断开了\n                    return ConnectToExistingPeerResult.Fail();\n                }\n            }\n\n            return new ConnectToExistingPeerResult(peerProxy, peerConnectFinishedTask);\n        }\n\n        /// <summary>\n        /// 连接其他客户端\n        /// </summary>\n        /// <param name=\"peerName\">对方</param>\n        /// <returns></returns>\n        internal async Task<PeerProxy> GetOrCreatePeerProxyAsync(string peerName)\n        {\n            if (PeerManagerInternal.TryGetValue(peerName, out var peerProxy))\n            {\n            }\n            else\n            {\n                // 这里无视多次加入，这里的多线程问题也可以忽略\n                StartServer();\n\n                peerProxy = await CreatePeerProxyAsync(peerName);\n            }\n\n            return peerProxy;\n        }\n\n        private async Task<PeerProxy> CreatePeerProxyAsync(string peerName)\n        {\n            var ipcClientService = CreateIpcClientService(peerName);\n\n            var peerProxy = new PeerProxy(peerName, ipcClientService, IpcContext);\n            PeerManagerInternal.TryAdd(peerProxy);\n\n            await ipcClientService.Start().ConfigureAwait(false);\n\n            return peerProxy;\n        }\n\n        internal IpcClientService CreateIpcClientService(string peerName) => new IpcClientService(IpcContext, peerName);\n\n        /// <inheritdoc />\n        public void Dispose()\n        {\n            //Debugger.Launch();\n            IpcContext.IsDisposing = true;\n            IpcContext.Logger.Trace($\"[IpcProvider][Dispose] {IpcContext.PipeName}\");\n            IpcServerService.Dispose();\n            PeerManagerInternal.Dispose();\n            IpcContext.IsDisposed = true;\n        }\n\n        private IpcServerService? _ipcServerService;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcRequestHandlerProvider.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Diagnostics;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    /// 关联 <see cref=\"IpcMessageRequestManager\"/> 和 <see cref=\"IIpcRequestHandler\"/> 的联系\n    /// </summary>\n    class IpcRequestHandlerProvider\n    {\n        public IpcRequestHandlerProvider(IpcContext ipcContext)\n        {\n            IpcContext = ipcContext;\n        }\n\n        public IpcContext IpcContext { get; }\n\n        /// <summary>\n        /// 处理请求消息\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"args\"></param>\n        /// 有三步\n        /// 1. 取出消息和上下文里面带的 <see cref=\"IIpcRequestHandler\"/> 用于处理消息\n        /// 2. 构建出 <see cref=\"IIpcRequestContext\"/> 传入到 <see cref=\"IIpcRequestHandler\"/> 处理\n        /// 3. 将 <see cref=\"IIpcRequestHandler\"/> 的返回值发送给到客户端\n        public async void HandleRequest(PeerProxy sender, IpcClientRequestArgs args)\n        {\n            try\n            {\n                var requestMessage = args.IpcMessageBody;\n                var peerProxy = sender;\n\n                IpcMessage ipcMessage = new IpcMessage($\"[{peerProxy.PeerName}][{args.MessageId}]\", requestMessage);\n                var ipcRequestContext = new IpcRequestMessageContext(ipcMessage, peerProxy, args.MessageCommandType.ToCoreMessageType());\n\n                // 处理消息\n                // 优先从 Peer 里面找处理的方法，这样上层可以对某个特定的 Peer 做不同的处理\n                // Todo 需要设计这部分 API 现在因为没有 API 的设计，先全部走 DefaultIpcRequestHandler 的逻辑\n                var receiveRequestTracker = new IpcMessageTracker<IpcRequestMessageContext>(\n                    peerProxy.IpcContext.PipeName,\n                    peerProxy.PeerName,\n                    ipcRequestContext,\n                    \"HandleRequest\",\n                    IpcContext.Logger);\n                IIpcResponseMessage result;\n\n                try\n                {\n                    result = await HandleRequestAsync(receiveRequestTracker, peerProxy.PeerName).ConfigureAwait(false);\n                }\n                catch (Exception e)\n                {\n                    IpcContext.Logger.Error(e, \"业务端 HandleRequestAsync 抛出异常\");\n                    result = new IpcHandleRequestMessageResult(new IpcMessage(\"Error\", new byte[0]));\n                }\n\n                // 构建信息回复，发送回客户端。\n                // 由于这里是通用的回复逻辑，所以只对需要回复的业务进行回复（不需要回复的业务就直接忽略）。\n                var responseManager = IpcContext.IpcMessageResponseManager;\n                var responseMessage = responseManager.CreateResponseMessage(args.MessageId, result.ResponseMessage);\n                var sendResponseTracker = new IpcMessageTracker<IpcBufferMessageContext>(\n                    peerProxy.IpcContext.PipeName,\n                    peerProxy.PeerName,\n                    responseMessage,\n                    \"HandleRequest\",\n                    IpcContext.Logger);\n                sendResponseTracker.CriticalStep(\"Send\", null, requestMessage);\n                await WriteResponseMessageAsync(peerProxy, sendResponseTracker).ConfigureAwait(false);\n            }\n            catch (Exception e)\n            {\n                // 当前是后台线程了，不能接受任何的抛出\n                IpcContext.Logger.Error(e);\n            }\n        }\n\n        private async Task<IIpcResponseMessage> HandleRequestAsync(IpcMessageTracker<IpcRequestMessageContext> context, string remotePeerName)\n        {\n            IIpcResponseMessage? result = null;\n            IIpcResponseMessage? customCanNotHandleRequestResult = null;\n\n            context.CriticalStep(\"ReceiveCore\", null, context.Message.IpcBufferMessage.Body);\n            var handlers = IpcContext.IpcConfiguration.GetIpcRequestHandlers();\n            foreach (var ipcRequestHandler in handlers)\n            {\n                result = await HandleRequestCoreAsync(context.Message, ipcRequestHandler).ConfigureAwait(false);\n                if (result is null)\n                {\n                    var errorMessage = $\"在实现 {nameof(IIpcRequestHandler)}.{nameof(IIpcRequestHandler.HandleRequest)} 时，必须返回非 null 的响应。如果不知道如何处理此消息，请返回 {nameof(KnownIpcResponseMessages)}.{nameof(KnownIpcResponseMessages.CannotHandle)}\";\n                    IpcContext.Logger.Error(errorMessage);\n#if DEBUG\n                    throw new InvalidOperationException(errorMessage);\n#endif\n                }\n\n                // 如果非不能响应，则代表已经处理完了\n                if (!KnownIpcResponseMessages.IsCanNotHandleResponseMessage(result))\n                {\n                    break;\n                }\n                else\n                {\n                    // 如果当前的 Handler 不能处理消息，则继续到下一个 Handler 来尝试处理\n                    // 如果当前的 Handler 返回了自定义的不能处理消息，则保存下来\n                    if (KnownIpcResponseMessages.IsCustomCanNotHandleResponseMessage(result))\n                    {\n                        customCanNotHandleRequestResult = result;\n                    }\n                }\n            }\n\n            if (result == null || KnownIpcResponseMessages.IsCanNotHandleResponseMessage(result) || result.ResponseMessage.Body.Length <= 0)\n            {\n                var possibleMessageContent = Encoding.UTF8.GetString(context.Message.IpcBufferMessage.Body.Buffer, context.Message.IpcBufferMessage.Body.Start, context.Message.IpcBufferMessage.Body.Length);\n                var errorMessage = $\"IPC 端 {remotePeerName} 正在等待返回，因此必须至少有一个 IPC 处理器正常处理此消息返回。出现此异常代表代码编写出现了错误，必须修复。\";\n                // 重新再拿一次，防止枚举遍历不正确\n                handlers = IpcContext.IpcConfiguration.GetIpcRequestHandlers();\n                var errorHandlers = string.Join(\"\\r\\n\", handlers.Select(FormatHandlerAsErrorMessage));\n                var logMessage = $\"{errorMessage}\\r\\n消息内容猜测为：\\r\\n{possibleMessageContent}\\r\\n消息处理器有：\\r\\n{errorHandlers}\\r\\n说明所有这些消息处理器都没有处理此条消息，请添加更多的消息处理器。\";\n                IpcContext.Logger.Error(logMessage);\n                // 由于业务代码肯定引用的是 IPC 库，以下的 DEBUG 异常一定不会在业务代码中抛出，因此除了开发过程中提示 IPC 库的开发者外，生产环境中不会有什么作用。现在 IPC 库已经稳定，以下代码先注释，方便编写单元测试\n                //#if DEBUG\n                //                // 这里一定说明业务代码写错了，缺少对应的 Handler。\n                //                throw new InvalidOperationException(logMessage);\n                //#endif\n\n                if (customCanNotHandleRequestResult != null)\n                {\n                    result = customCanNotHandleRequestResult;\n                }\n\n                // 如果没有自定义的不能处理消息，则返回一个默认的不能处理消息\n                result ??= KnownIpcResponseMessages.CannotHandle;\n            }\n\n            return result;\n        }\n\n        private string FormatHandlerAsErrorMessage(IIpcRequestHandler handler) => handler switch\n        {\n            GeneratedProxyJointIpcRequestHandler gpjirh => string.Join(\", \", gpjirh.Owner.JointManager.EnumerateJointNames()),\n            DelegateIpcRequestHandler dirh => nameof(DelegateIpcRequestHandler),\n            EmptyIpcRequestHandler eirh => nameof(EmptyIpcRequestHandler),\n            null => \"null\",\n            _ => handler.GetType().FullName!,\n        };\n\n        private static async Task<IIpcResponseMessage> HandleRequestCoreAsync(IpcRequestMessageContext message, IIpcRequestHandler ipcRequestHandler)\n        {\n            var result = await ipcRequestHandler.HandleRequest(message).ConfigureAwait(false);\n            return result;\n        }\n\n        private async Task WriteResponseMessageAsync(PeerProxy peerProxy, IpcMessageTracker<IpcBufferMessageContext> sendResponseTracker)\n        {\n            try\n            {\n                await peerProxy.IpcClientService.WriteMessageAsync(sendResponseTracker).ConfigureAwait(false);\n            }\n            catch (IOException)\n            {\n                // [正常现象] 在对方发完消息等待我方回复时退出。\n            }\n            catch (ObjectDisposedException)\n            {\n                // Cannot access a closed pipe.\n                // [正常现象] 在对方发完消息等待我方回复时退出。\n            }\n            catch (IpcRemoteException)\n            {\n                // [正常现象] 因为 IPC 对方已断开连接，所以已无法回复。\n            }\n            catch (Exception ex)\n            {\n                IpcContext.Logger.Error(ex.ToString());\n#if DEBUG\n                // 这里一定说明业务代码写错了，缺少对应的 Handler。\n                throw;\n#endif\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcRequestMessageContext.cs",
    "content": "﻿using System.Diagnostics;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    class IpcRequestMessageContext : ICoreIpcRequestContext, IIpcRequestContext\n    {\n        [DebuggerStepThrough]\n        internal IpcRequestMessageContext(IpcMessage ipcBufferMessage, IPeerProxy peer, CoreMessageType coreMessageType)\n        {\n            IpcBufferMessage = ipcBufferMessage;\n            Peer = peer;\n            CoreMessageType = coreMessageType;\n        }\n\n        /// <inheritdoc />\n        public bool Handled { get; set; }\n\n        /// <inheritdoc />\n        public IpcMessage IpcBufferMessage { get; }\n\n        public IPeerProxy Peer { get; }\n\n        public CoreMessageType CoreMessageType { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/IpcServerService.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    /// 管道服务端，用于接收消息\n    /// </summary>\n    /// 采用两个半工的管道做到双向通讯，这里的管道服务端用于接收\n    public class IpcServerService : IDisposable\n    {\n        /// <summary>\n        /// 管道服务端\n        /// </summary>\n        /// <param name=\"ipcContext\"></param>\n        public IpcServerService(IpcContext ipcContext)\n        {\n            IpcContext = ipcContext;\n        }\n\n        /// <summary>\n        /// 上下文\n        /// </summary>\n        public IpcContext IpcContext { get; }\n\n        private ILogger Logger => IpcContext.Logger;\n\n        /// <summary>\n        /// 用于解决对象被回收\n        /// </summary>\n        private List<IpcPipeServerMessageProvider> IpcPipeServerMessageProviderList { get; } =\n            new List<IpcPipeServerMessageProvider>();\n\n#if DEBUG\n        /// <summary>\n        /// 只是调试使用的列表，仅仅用来调试\n        /// </summary>\n        private List<WeakReference<IpcPipeServerMessageProvider>> IpcPipeServerMessageProviderDebugList { get; } =\n            new List<WeakReference<IpcPipeServerMessageProvider>>();\n#endif\n\n        /// <summary>\n        /// 启动服务\n        /// </summary>\n        /// <returns></returns>\n        internal async Task Start()\n        {\n            while (!_isDisposed)\n            {\n                var pipeServerMessage = new IpcPipeServerMessageProvider(IpcContext, this);\n                lock (IpcPipeServerMessageProviderList)\n                {\n                    IpcPipeServerMessageProviderList.Add(pipeServerMessage);\n#if DEBUG\n                    IpcPipeServerMessageProviderDebugList.Add(new WeakReference<IpcPipeServerMessageProvider>(pipeServerMessage));\n#endif\n                }\n                pipeServerMessage.PeerConnectBroke += PipeServerMessage_PeerConnectBroke;\n                await pipeServerMessage.Start().ConfigureAwait(false);\n            }\n        }\n\n        private void PipeServerMessage_PeerConnectBroke(object? sender, IpcPipeServerMessageProviderPeerConnectionBrokenArgs e)\n        {\n            lock (IpcPipeServerMessageProviderList)\n            {\n                IpcPipeServerMessageProviderList.Remove(e.IpcPipeServerMessageProvider);\n            }\n\n            try\n            {\n                e.IpcPipeServerMessageProvider.Dispose();\n            }\n            catch (Exception)\n            {\n                // 释放过程挂了，忽略吧\n            }\n        }\n\n        /// <summary>\n        /// 当收到消息时触发。无论是哪个 Peer 收到消息，都会引发此事件\n        /// </summary>\n        public event EventHandler<PeerMessageArgs>? MessageReceived;\n\n        /// <summary>\n        /// 当有对方连接时触发\n        /// </summary>\n        internal event EventHandler<IpcInternalPeerConnectedArgs>? PeerConnected;\n\n        /// <summary>\n        /// 当接收到消息时触发。无论是哪个 Peer 收到消息，都会引发此事件\n        /// </summary>\n        /// <param name=\"sender\"></param>\n        /// <param name=\"e\"></param>\n        internal void OnMessageReceived(object? sender, PeerStreamMessageArgs e)\n        {\n            string? debugMessageBody = null;\n#if DEBUG\n            debugMessageBody = e.ToDebugMessageText();\n#endif\n            Logger.Trace($\"[{nameof(IpcServerService)}] MessageReceived PeerName={e.PeerName} {e.Ack}{(debugMessageBody is null ? \"\" : $\" {debugMessageBody}\")}\");\n            MessageReceived?.Invoke(sender, e.ToPeerMessageArgs());\n        }\n\n        internal void OnPeerConnected(object? sender, IpcInternalPeerConnectedArgs e)\n        {\n            Logger.Trace($\"[{nameof(IpcServerService)}] PeerConnected PeerName={e.PeerName} {e.Ack}\");\n\n            PeerConnected?.Invoke(sender, e);\n        }\n\n        private void Dispose(bool disposing)\n        {\n            if (disposing)\n            {\n            }\n\n            _isDisposed = true;\n\n            List<IpcPipeServerMessageProvider> ipcPipeServerMessageProviderList;\n\n            lock (IpcPipeServerMessageProviderList)\n            {\n                ipcPipeServerMessageProviderList = IpcPipeServerMessageProviderList.ToList();\n            }\n\n            foreach (var ipcPipeServerMessageProvider in ipcPipeServerMessageProviderList)\n            {\n                ipcPipeServerMessageProvider.Dispose();\n            }\n        }\n\n        private bool _isDisposed;\n\n        /// <inheritdoc />\n        public void Dispose()\n        {\n            Dispose(true);\n            GC.SuppressFinalize(this);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/PeerProxy.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Diagnostics;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.Pipes\n{\n    /// <summary>\n    /// 用于表示远程的对方\n    /// </summary>\n    public class PeerProxy : IPeerProxy\n    // 为什么 PeerProxy 不加上 IDisposable 方法\n    // 因为这个类在上层业务使用，如果被上层业务调释放了，框架层就没得玩\n    //, IDisposable\n    {\n        internal PeerProxy(string peerName, IpcClientService ipcClientService, IpcContext ipcContext)\n        {\n            PeerName = peerName;\n            IpcClientService = ipcClientService;\n\n            IpcContext = ipcContext;\n\n            IpcMessageRequestManager = new IpcMessageRequestManager(ipcContext);\n            IpcMessageRequestManager.OnIpcClientRequestReceived += ResponseManager_OnIpcClientRequestReceived;\n        }\n\n        internal PeerProxy(string peerName, IpcClientService ipcClientService, IpcInternalPeerConnectedArgs ipcInternalPeerConnectedArgs, IpcContext ipcContext) :\n            this(peerName, ipcClientService, ipcContext)\n        {\n            Update(ipcInternalPeerConnectedArgs);\n        }\n\n        /// <summary>\n        /// 对方的服务器名\n        /// </summary>\n        public string PeerName { get; }\n\n        internal TaskCompletionSource<bool> WaitForFinishedTaskCompletionSource { private set; get; } =\n            new TaskCompletionSource<bool>();\n\n        internal IpcContext IpcContext { get; }\n\n        /// <summary>\n        /// 当收到消息时触发\n        /// </summary>\n        public event EventHandler<IPeerMessageArgs>? MessageReceived;\n\n        internal IpcMessageRequestManager IpcMessageRequestManager { get; }\n\n        /// <inheritdoc />\n        public async Task NotifyAsync(IpcMessage request)\n        {\n            if (request.IpcMessageHeader == 0)\n            {\n                // 追踪业务消息。\n                var requestTracker = new IpcMessageTracker<IpcMessage>(IpcContext.PipeName, PeerName, request, request.Tag, IpcContext.Logger);\n                requestTracker.CriticalStep(\"Send\", null, request.Body);\n\n                await WaitConnectAsync(requestTracker).ConfigureAwait(false);\n\n                try\n                {\n                    // 发送带有追踪的请求。\n                    await IpcClientService.WriteMessageAsync(requestTracker).ConfigureAwait(false);\n                }\n                catch (Exception e)\n                {\n                    throw new IpcRemoteException($\"[{nameof(NotifyAsync)}] Tag:'{request.Tag}'; LocalPeer:'{IpcContext.PipeName}'; RemotePeer:'{PeerName}'; ExceptionMessage:'{e.Message}'\", e);\n                }\n            }\n            else\n            {\n                var ipcBufferMessageContext = request.ToIpcBufferMessageContextWithMessageHeader(IpcMessageCommandType.Business);\n\n                var requestTracker = new IpcMessageTracker<IpcBufferMessageContext>(IpcContext.PipeName, PeerName, ipcBufferMessageContext, ipcBufferMessageContext.Tag, IpcContext.Logger);\n\n                requestTracker.CriticalStep(\"Send\", null, request.Body);\n\n                await WaitConnectAsync(requestTracker).ConfigureAwait(false);\n\n                try\n                {\n                    // 发送带有追踪的请求。\n                    await IpcClientService.WriteMessageAsync(requestTracker).ConfigureAwait(false);\n                }\n                catch (Exception e)\n                {\n                    throw new IpcRemoteException($\"[{nameof(NotifyAsync)}] Tag:'{request.Tag}'; LocalPeer:'{IpcContext.PipeName}'; RemotePeer:'{PeerName}'; ExceptionMessage:'{e.Message}'\", e);\n                }\n            }\n        }\n\n        /// <inheritdoc />\n        public async Task<IpcMessage> GetResponseAsync(IpcMessage request)\n        {\n            // 追踪业务消息。\n            var requestTracker = new IpcMessageTracker<IpcMessage>(IpcContext.PipeName, PeerName, request, request.Tag, IpcContext.Logger);\n            requestTracker.CriticalStep(\"Send\", null, request.Body);\n            await WaitConnectAsync(requestTracker).ConfigureAwait(false);\n\n            try\n            {\n                // 将业务消息封装成请求消息，并追踪。\n                var ipcClientRequestMessage = IpcMessageRequestManager.CreateRequestMessage(request);\n                var ipcBufferMessageContextTracker = requestTracker.TrackNext(ipcClientRequestMessage.IpcBufferMessageContext);\n\n                // 发送带有追踪的请求。\n                await IpcClientService.WriteMessageAsync(ipcBufferMessageContextTracker).ConfigureAwait(false);\n\n                // 等待响应，并追踪。\n                var messageBody = await ipcClientRequestMessage.Task.ConfigureAwait(false);\n                return new IpcMessage($\"[{PeerName}]\", messageBody);\n            }\n            catch (Exception e)\n            {\n                throw new IpcRemoteException($\"[{nameof(GetResponseAsync)}] Tag:'{request.Tag}'; LocalPeer:'{IpcContext.PipeName}'; RemotePeer:'{PeerName}'; ExceptionMessage:'{e.Message}'\", e);\n            }\n        }\n\n        /// <inheritdoc />\n        public event EventHandler<IPeerConnectionBrokenArgs>? PeerConnectionBroken;\n\n        /// <inheritdoc />\n        public event EventHandler<IPeerReconnectedArgs>? PeerReconnected;\n\n        #region 框架\n\n        /// <summary>\n        /// 用于写入裸数据\n        /// </summary>\n        /// 框架内使用\n        internal IpcMessageWriter IpcMessageWriter { private set; get; }\n        // 由构造函数初始化 IpcClientService 时，自动初始化此属性，因此不为空\n            = null!;\n\n        /// <summary>\n        /// 表示作为客户端和对方连接\n        /// </summary>\n        /// 框架内使用\n        internal IpcClientService IpcClientService\n        {\n            private set\n            {\n                IpcMessageWriter = new IpcMessageWriter(value);\n                _ipcClientService = value;\n            }\n            get\n            {\n                return _ipcClientService;\n            }\n        }\n\n        private IpcClientService _ipcClientService = null!;\n\n        /// <summary>\n        /// 是否已断开\n        /// </summary>\n        /// 特别和 <see cref=\"IsConnectedFinished\"/> 分开两个属性，一个对外，一个给框架内使用\n        /// 核心是用来实现重连的逻辑\n        internal bool IsBroken { get; private set; }\n\n        /// <summary>\n        /// 获取是否连接完成，也就是可以读取，可以发送\n        /// </summary>\n        /// 连接过程中，断开，此时依然是 true 的值。在此时进行写入的内容，都会加入到缓存里面\n        public bool IsConnectedFinished { get; private set; }\n\n        /// <summary>\n        /// 用于记录数据，记录是否附加上重新连接\n        /// </summary>\n        internal PeerReConnector? PeerReConnector { set; get; }\n\n        private bool AutoReconnectPeers => _ipcClientService.IpcContext.IpcConfiguration.AutoReconnectPeers;\n\n        /// <summary>\n        /// 被对方连回来时调用，此方法被调用意味着准备已完成\n        /// </summary>\n        /// <param name=\"ipcInternalPeerConnectedArgs\"></param>\n        internal void Update(IpcInternalPeerConnectedArgs ipcInternalPeerConnectedArgs)\n        {\n            Debug.Assert(ipcInternalPeerConnectedArgs.PeerName == PeerName);\n\n            var serverStreamMessageReader = ipcInternalPeerConnectedArgs.ServerStreamMessageReader;\n\n            serverStreamMessageReader.MessageReceived -= ServerStreamMessageReader_MessageReceived;\n            serverStreamMessageReader.MessageReceived += ServerStreamMessageReader_MessageReceived;\n\n            // 连接断开\n            serverStreamMessageReader.PeerConnectBroke -= ServerStreamMessageReader_PeerConnectBroke;\n            serverStreamMessageReader.PeerConnectBroke += ServerStreamMessageReader_PeerConnectBroke;\n\n            IsConnectedFinished = true;\n            IsBroken = false;\n\n            if (WaitForFinishedTaskCompletionSource.TrySetResult(true))\n            {\n            }\n            else\n            {\n                // Debug.Assert(false, \"重复调用\");\n            }\n        }\n\n        /// <summary>\n        /// 重新连接\n        /// </summary>\n        internal async void Reconnect(IpcClientService ipcClientService)\n        {\n            try\n            {\n                Debug.Assert(ipcClientService.PeerName == PeerName);\n\n                IpcClientService = ipcClientService;\n\n                // 等待完成更新之后，再进行通知，否则将会在收到事件时，还在准备完成所有逻辑\n                await WaitForFinishedTaskCompletionSource.Task;\n\n                PeerReconnected?.Invoke(this, new PeerReconnectedArgs());\n            }\n            catch (Exception e)\n            {\n                // 线程顶层，不能再抛出异常\n                IpcContext.Logger.Error(e, $\"[PeerProxy] Reconnect Fail. PeerName={PeerName}\");\n            }\n        }\n\n        private void ServerStreamMessageReader_PeerConnectBroke(object? sender, PeerConnectionBrokenArgs e)\n        {\n            OnPeerConnectionBroken(e);\n        }\n\n        private void ServerStreamMessageReader_MessageReceived(object? sender, PeerStreamMessageArgs e)\n        {\n            IpcMessageRequestManager.OnReceiveMessage(e);\n            var args = e.ToPeerMessageArgs();\n\n            var messageTracker = new IpcMessageTracker<IpcMessage>(\n                IpcContext.PipeName,\n                PeerName,\n                args.Message,\n                \"MessageReceived\",\n                IpcContext.Logger);\n            messageTracker.CriticalStep(\"Receive\", e.Ack, messageTracker.Message.Body);\n            IpcContext.TaskPool.Run(() => MessageReceived?.Invoke(this, args), IpcContext.Logger);\n        }\n\n        private void ResponseManager_OnIpcClientRequestReceived(object? sender, IpcClientRequestArgs e)\n        {\n            var ipcRequestHandlerProvider = IpcContext.IpcRequestHandlerProvider;\n            ipcRequestHandlerProvider.HandleRequest(this, e);\n        }\n\n        private void OnPeerConnectionBroken(IPeerConnectionBrokenArgs e)\n        {\n            IsBroken = true;\n\n            // 需要将旧的设置为断开状态，返回监听 WaitForFinishedTaskCompletionSource 的逻辑无限等待\n            WaitForFinishedTaskCompletionSource.TrySetException(new IpcPeerConnectionBrokenException());\n\n            if (AutoReconnectPeers)\n            {\n                WaitForFinishedTaskCompletionSource = new TaskCompletionSource<bool>();\n            }\n\n            IpcClientService.Dispose();\n\n            PeerConnectionBroken?.Invoke(this, e);\n\n            IpcMessageRequestManager.BreakAllRequestTaskByIpcBroken();\n        }\n\n        private async Task WaitConnectAsync(IIpcMessageTracker requestTracker)\n        {\n            if (IsBroken)\n            {\n                if (IpcContext.IsDisposing || IpcContext.IsDisposed)\n                {\n                    throw new ObjectDisposedException(nameof(PeerProxy),\n                        $\"当前服务已被释放，服务名: LocalPeerName={IpcContext.PipeName}; RemotePeerName={PeerName}; MessageTag={requestTracker.Tag}\");\n                }\n\n                if (AutoReconnectPeers)\n                {\n#if DEBUG\n                    requestTracker.Debug(\"[Reconnect] Waiting FinishedTaskCompletion\");\n#endif\n\n                    // 如果等待连接完成任务已经完成，且断开，且需要自动连接\n                    // 那么是不符合预期的。因为预期的是断开且需要自动连接时，应该有等待连接完成的任务正在执行\n                    // 但是由于有多线程访问，这就导致了会出现如上的条件满足\n                    // 只需要做很短的等待，即可进入符合预期的逻辑\n                    // 下面实现了简单的自旋，理论上是无伤的\n                    while (WaitForFinishedTaskCompletionSource.Task.IsCompleted && IsBroken)\n                    {\n                        // 如果只执行到设置 IsBroken=true 还没有创建新 WaitForFinishedTaskCompletionSource 对象\n                        // 那么此时 IsCompleted=true 且 IsBroken=true 满足\n                        // 进入等待\n\n                        // 如果特别快，进入 IsBroken=true 之后立刻创建 WaitForFinishedTaskCompletionSource 对象\n                        // 自然此时的 IsCompleted=false 进入下面的等待\n\n                        // 如果特别特别快，断开后立刻连接\n                        // 那么 IsCompleted=true 满足，但是 IsBroken=false 了\n                        // 进入下面的等待\n                        await Task.Yield();\n                    }\n                    await WaitForFinishedTaskCompletionSource.Task;\n#if DEBUG\n                    requestTracker.Debug(\"[Reconnect] Finish Waiting FinishedTaskCompletion\");\n#endif\n                }\n                else\n                {\n                    throw new IpcPeerConnectionBrokenException($\"PeerConnectionBroken. LocalPeerName={IpcContext.PipeName}; RemotePeerName={PeerName};  MessageTag={requestTracker.Tag}\");\n                }\n            }\n        }\n\n        #endregion\n\n        /// <summary>\n        /// 释放当前的对象，必须由框架内调用\n        /// </summary>\n        internal void DisposePeer()\n        {\n            if (!IpcContext.IsDisposing)\n            {\n                throw new InvalidOperationException($\"仅允许在 IpcProvider 的 Dispose 进行调用\");\n            }\n\n            IsBroken = true;\n            // 不管此状态\n            //IsConnectedFinished = true;\n            _ipcClientService.Dispose();\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/PipeConnectors/IIpcClientPipeConnector.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.Pipes.PipeConnectors;\n\n/// <summary>\n/// 配置客户端的管道连接\n/// </summary>\npublic interface IIpcClientPipeConnector\n{\n    /// <summary>\n    /// 进行管道连接，默认行为是调用 <code>await Task.Run(namedPipeClientStream.Connect)</code> 连接\n    /// </summary>\n    /// <param name=\"ipcClientPipeConnectionContext\"></param>\n    /// <returns></returns>\n    Task<IpcClientNamedPipeConnectResult> ConnectNamedPipeAsync(IpcClientPipeConnectionContext ipcClientPipeConnectionContext);\n}\n\n/// <summary>\n/// 客户端的管道连接结果\n/// </summary>\npublic readonly struct IpcClientNamedPipeConnectResult\n{\n    /// <summary>\n    /// 创建客户端的管道连接结果\n    /// </summary>\n    /// <param name=\"success\"></param>\n    public IpcClientNamedPipeConnectResult(bool success, string? reason = null)\n    {\n        Success = success;\n        Reason = reason;\n    }\n\n    /// <summary>\n    /// 连接是否成功\n    /// </summary>\n    public bool Success { get; }\n\n    /// <summary>\n    /// 连接成功或失败的原因\n    /// </summary>\n    public string? Reason { get; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/PipeConnectors/IpcClientPipeConnectionContext.cs",
    "content": "﻿using System.IO.Pipes;\nusing System.Threading;\n\nnamespace dotnetCampus.Ipc.Pipes.PipeConnectors;\n\n/// <summary>\n/// 用于传递客户端的管道连接参数\n/// </summary>\npublic readonly struct IpcClientPipeConnectionContext\n{\n    /// <summary>\n    /// 用于传递客户端的管道连接参数\n    /// </summary>\n    public IpcClientPipeConnectionContext(string peerName, NamedPipeClientStream namedPipeClientStream,\n        CancellationToken cancellationToken, bool isReConnect)\n    {\n        PeerName = peerName;\n        NamedPipeClientStream = namedPipeClientStream;\n        CancellationToken = cancellationToken;\n        IsReConnect = isReConnect;\n    }\n\n    /// <summary>\n    /// 准备连接的管道名\n    /// </summary>\n    public string PeerName { get; }\n\n    /// <summary>\n    /// 用来连接的管道，需要调用 Connect 方法进行连接\n    /// </summary>\n    public NamedPipeClientStream NamedPipeClientStream { get; }\n\n    /// <summary>\n    /// 连接取消标记\n    /// </summary>\n    public CancellationToken CancellationToken { get; }\n\n    /// <summary>\n    /// 是否属于重新连接。如 Peer 断开之后的重新连接\n    /// </summary>\n    public bool IsReConnect { get; }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Pipes/PipeConnectors/IpcClientPipeConnector.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.Pipes.PipeConnectors;\n\n/// <summary>\n/// 默认的配置客户端连接方法\n/// </summary>\npublic class IpcClientPipeConnector : IIpcClientPipeConnector\n{\n    /// <summary>\n    /// 创建默认的配置客户端连接方法\n    /// </summary>\n    /// <param name=\"canContinue\">是否能继续连接的判断委托</param>\n    /// <param name=\"stepTimeout\">每一次连接的等待超时时间</param>\n    /// <param name=\"stepSleepTimeGetter\">等待超时之后，下一次连接的延迟时间。连接的之间间隔时间。可以根据连接次数，不断延长延迟时间</param>\n    public IpcClientPipeConnector(CanContinueDelegate canContinue, TimeSpan? stepTimeout = null,\n        GetStepSleepTimeDelegate? stepSleepTimeGetter = null)\n    {\n        CanContinue = canContinue;\n        StepTimeout = stepTimeout ?? DefaultStepTimeout;\n        StepSleepTimeGetter = stepSleepTimeGetter ?? DefaultGetStepSleepTime;\n    }\n\n    /// <inheritdoc />\n    public async Task<IpcClientNamedPipeConnectResult> ConnectNamedPipeAsync(IpcClientPipeConnectionContext ipcClientPipeConnectionContext)\n    {\n        if (ipcClientPipeConnectionContext.IsReConnect)\n        {\n            // 如果是重新连接的，先获取上层业务端，询问是否可以继续连接\n            if (CanContinue(ipcClientPipeConnectionContext) is false)\n            {\n                // 如果上层业务端返回说不能继续连接了，那就不继续了\n                return new IpcClientNamedPipeConnectResult(false, \"CanContinue return false. IsReConnect=true\");\n            }\n        }\n\n        var namedPipeClientStream = ipcClientPipeConnectionContext.NamedPipeClientStream;\n\n        int stepCount = 0;\n        while (!ipcClientPipeConnectionContext.CancellationToken.IsCancellationRequested)\n        {\n            try\n            {\n                // 由于 namedPipeClientStream.ConnectAsync 底层也是使用 Task.Run 执行 Connect 的逻辑\n                // 且 ConnectAsync 在 .NET Framework 4.5 不存在\n                // 因此这里就使用 Task.Run 执行\n                await Task.Run(() => namedPipeClientStream.Connect((int) StepTimeout.TotalMilliseconds))\n                    .ConfigureAwait(false);\n                return new IpcClientNamedPipeConnectResult(true);\n            }\n            catch (TimeoutException)\n            {\n                // 连接超时了，继续执行下面的逻辑\n                // 如果没有连接超时，连接成功了，那就会进入上面的 return 分支，方法结束\n                // 如果抛出其他异常了，那就不接住，继续向上抛出\n            }\n\n            stepCount++;\n\n            if (CanContinue(ipcClientPipeConnectionContext))\n            {\n                var sleepTime = StepSleepTimeGetter(ipcClientPipeConnectionContext, stepCount);\n                await Task.Delay(sleepTime)\n                    .ConfigureAwait(false);\n            }\n            else\n            {\n                return new IpcClientNamedPipeConnectResult(false, \"CanContinue return false\");\n            }\n        }\n\n        return new IpcClientNamedPipeConnectResult(false, \"Timeout\");\n    }\n\n    /// <summary>\n    /// 一次连接的超时时间\n    /// </summary>\n    public TimeSpan StepTimeout { get; }\n\n    /// <summary>\n    /// 默认一次连接的超时时间\n    /// </summary>\n    public static TimeSpan DefaultStepTimeout => TimeSpan.FromSeconds(5);\n\n    /// <summary>\n    /// 默认连接的之间间隔时间\n    /// </summary>\n    public static TimeSpan DefaultStepSleepTime => TimeSpan.FromSeconds(1);\n\n    /// <summary>\n    /// 获取连接的之间间隔时间\n    /// </summary>\n    public GetStepSleepTimeDelegate StepSleepTimeGetter { get; }\n\n    /// <summary>\n    /// 默认连接的之间间隔时间\n    /// </summary>\n    /// <param name=\"ipcClientPipeConnectionContext\"></param>\n    /// <param name=\"stepCount\"></param>\n    /// <returns></returns>\n    public static TimeSpan DefaultGetStepSleepTime(IpcClientPipeConnectionContext ipcClientPipeConnectionContext,\n        int stepCount)\n    {\n        return DefaultStepSleepTime;\n    }\n\n    /// <summary>\n    /// 是否还能继续连接\n    /// </summary>\n    /// <remarks>\n    /// 每次连接超时之后，将会调用此方法，判断是否可以继续下一次连接\n    /// </remarks>\n    public CanContinueDelegate CanContinue { get; }\n\n    /// <summary>\n    /// 获取连接的之间间隔时间\n    /// </summary>\n    /// <param name=\"ipcClientPipeConnectionContext\"></param>\n    /// <param name=\"stepCount\">第几次连接</param>\n    /// <returns></returns>\n    public delegate TimeSpan GetStepSleepTimeDelegate(IpcClientPipeConnectionContext ipcClientPipeConnectionContext,\n        int stepCount);\n\n    /// <inheritdoc cref=\"CanContinue\"/>\n    public delegate bool CanContinueDelegate(IpcClientPipeConnectionContext ipcClientPipeConnectionContext);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Properties/.gitignore",
    "content": "﻿launchSettings.json\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Serialization/IIpcObjectSerializer.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\nnamespace dotnetCampus.Ipc.Serialization;\n\n/// <summary>\n/// 支持 IPC 对象序列化和反序列化的接口。\n/// </summary>\npublic interface IIpcObjectSerializer\n{\n    /// <summary>\n    /// 序列化对象为二进制数据。\n    /// </summary>\n    /// <param name=\"value\">要序列化的对象。</param>\n    /// <returns>序列化后的二进制数据。</returns>\n    byte[] Serialize(object value);\n\n    /// <summary>\n    /// 序列化对象到流。\n    /// </summary>\n    /// <param name=\"stream\">序列化到此流。</param>\n    /// <param name=\"value\">要序列化的对象。</param>\n    void Serialize(Stream stream, object? value);\n\n    /// <summary>\n    /// 序列化为 IPC 中间 JSON 对象。此中间对象将经过进一步处理在再进一步序列化以进行传输。\n    /// </summary>\n    /// <param name=\"value\">要序列化的对象。</param>\n    /// <returns>IPC 中间 JSON 对象。</returns>\n    IpcJsonElement SerializeToElement(object? value);\n\n    /// <summary>\n    /// 从二进制数据反序列化为对象。\n    /// </summary>\n    /// <typeparam name=\"T\">要反序列化的对象类型。</typeparam>\n    /// <param name=\"data\">二进制数据。</param>\n    /// <param name=\"start\">所需数据在 <paramref name=\"data\"/> 中的起始位置。</param>\n    /// <param name=\"length\">所需数据的长度。</param>\n    /// <returns>反序列化得到的对象。</returns>\n    T? Deserialize<T>(byte[] data, int start, int length);\n\n    /// <summary>\n    /// 从流反序列化为对象。\n    /// </summary>\n    /// <param name=\"stream\">要从中反序列化的流。</param>\n    /// <typeparam name=\"T\">要反序列化的对象类型。</typeparam>\n    /// <returns>反序列化得到的对象。</returns>\n    T? Deserialize<T>(Stream stream);\n\n    /// <summary>\n    /// 从 IPC 中间 JSON 对象反序列化为对象。\n    /// </summary>\n    /// <typeparam name=\"T\">要反序列化的对象类型。</typeparam>\n    /// <param name=\"jsonElement\">IPC 中间 JSON 对象。</param>\n    /// <returns>反序列化得到的对象。</returns>\n    T? Deserialize<T>(IpcJsonElement jsonElement);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Serialization/IpcObjectJsonSerializer.cs",
    "content": "﻿using System.Text;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\nusing Newtonsoft.Json.Linq;\nusing JsonSerializer = Newtonsoft.Json.JsonSerializer;\n#endif\n\nnamespace dotnetCampus.Ipc.Serialization;\n\n#if UseNewtonsoftJson\n/// <summary>\n/// 以 <see cref=\"Newtonsoft.Json\"/> 作为底层机制支持 IPC 对象传输。\n/// </summary>\n[Obsolete(\"此类型已改名为 NewtonsoftJsonIpcObjectSerializer，并已在新的 .NET 框架中移除。\")]\npublic class IpcObjectJsonSerializer : NewtonsoftJsonIpcObjectSerializer\n{\n    /// <summary>\n    /// 创建 <see cref=\"IpcObjectJsonSerializer\"/> 的新实例。\n    /// </summary>\n    public IpcObjectJsonSerializer()\n    {\n    }\n\n    /// <summary>\n    /// 创建 <see cref=\"IpcObjectJsonSerializer\"/> 的新实例。\n    /// </summary>\n    /// <param name=\"jsonSerializer\">用于序列化和反序列化的 <see cref=\"JsonSerializer\"/> 实例。</param>\n    public IpcObjectJsonSerializer(JsonSerializer jsonSerializer) : base(jsonSerializer)\n    {\n    }\n}\n\n/// <summary>\n/// 以 <see cref=\"Newtonsoft.Json\"/> 作为底层机制支持 IPC 对象传输。\n/// </summary>\npublic class NewtonsoftJsonIpcObjectSerializer : IIpcObjectSerializer\n{\n    private static readonly Dictionary<Type, (Func<object, JValue> serializer, Func<JValue, object> deserializer)> JValueConverters = new()\n    {\n        { typeof(IntPtr), (x => new JValue(((IntPtr) x).ToInt64()), x => new IntPtr(x.ToObject<long>())) },\n    };\n\n    /// <summary>\n    /// 创建 <see cref=\"NewtonsoftJsonIpcObjectSerializer\"/> 的新实例。\n    /// </summary>\n    public NewtonsoftJsonIpcObjectSerializer()\n    {\n        JsonSerializer = JsonSerializer.CreateDefault();\n    }\n\n    /// <summary>\n    /// 创建 <see cref=\"NewtonsoftJsonIpcObjectSerializer\"/> 的新实例。\n    /// </summary>\n    /// <param name=\"jsonSerializer\">用于序列化和反序列化的 <see cref=\"JsonSerializer\"/> 实例。</param>\n    public NewtonsoftJsonIpcObjectSerializer(JsonSerializer jsonSerializer)\n    {\n        JsonSerializer = jsonSerializer;\n    }\n\n    /// <summary>\n    /// 获取 JSON 序列化器。\n    /// </summary>\n    private JsonSerializer JsonSerializer { get; }\n\n    /// <inheritdoc />\n    public byte[] Serialize(object value)\n    {\n        var json = JsonConvert.SerializeObject(value);\n        return Encoding.UTF8.GetBytes(json);\n    }\n\n    /// <inheritdoc />\n    public void Serialize(Stream stream, object? value)\n    {\n        using var textWriter = new StreamWriter(stream, Encoding.UTF8, leaveOpen: true, bufferSize: 2048);\n        JsonSerializer.Serialize(textWriter, value);\n    }\n\n    /// <inheritdoc />\n    public IpcJsonElement SerializeToElement(object? value) => new IpcJsonElement\n    {\n        RawValueOnNewtonsoftJson = value switch\n        {\n            // null。\n            null => JValue.CreateNull(),\n\n            // dotnetCampus.Ipc 支持的类型。\n            IntPtr pointer => new JValue(pointer.ToInt64()),\n\n            // 默认类型。\n            _ => JToken.FromObject(value),\n        },\n    };\n\n    /// <inheritdoc />\n    public T? Deserialize<T>(byte[] byteList, int start, int length)\n    {\n#if NETCOREAPP3_0_OR_GREATER\n        var data = byteList.AsSpan(start, length);\n#else\n        var data = new byte[length];\n        Array.Copy(byteList, start, data, 0, length);\n#endif\n        var json = Encoding.UTF8.GetString(data);\n        return JsonConvert.DeserializeObject<T>(json);\n    }\n\n    /// <inheritdoc />\n    public T? Deserialize<T>(Stream stream)\n    {\n        using StreamReader reader = new StreamReader\n        (\n            stream,\n#if !NETCOREAPP\n            Encoding.UTF8,\n            detectEncodingFromByteOrderMarks: false,\n            bufferSize: 2048,\n#endif\n            leaveOpen: true\n        );\n        JsonReader jsonReader = new JsonTextReader(reader);\n        return JsonSerializer.Deserialize<T>(jsonReader);\n    }\n\n    /// <inheritdoc />\n    public T? Deserialize<T>(IpcJsonElement jsonElement) => jsonElement.RawValueOnNewtonsoftJson switch\n    {\n        null => default!,\n        JValue jValue => jValue.ToObject<T>(),\n        JObject jObject => jObject.ToObject<T>(),\n        JArray jArray => jArray.ToObject<T>(),\n        _ => throw new NotSupportedException(\"不支持将其他 JToken 类型转换成 IPC 业务类型。\")\n    };\n}\n#else\n/// <summary>\n/// 新的 .NET 框架中已不再支持 Newtonsoft.Json 依赖，请更换成基于 System.Text.Json 的 SystemTextJsonIpcObjectSerializer 实现。\n/// </summary>\n[Obsolete(\"新的 .NET 框架中已不再支持 Newtonsoft.Json 依赖，请更换成基于 System.Text.Json 的 SystemTextJsonIpcObjectSerializer 实现。\", true)]\npublic class IpcObjectJsonSerializer : IIpcObjectSerializer\n{\n    byte[] IIpcObjectSerializer.Serialize(object value) => throw null!;\n    void IIpcObjectSerializer.Serialize(Stream stream, object? value) => throw null!;\n    IpcJsonElement IIpcObjectSerializer.SerializeToElement(object? value) => throw null!;\n    T? IIpcObjectSerializer.Deserialize<T>(byte[] data, int start, int length) where T : default => throw null!;\n    T? IIpcObjectSerializer.Deserialize<T>(Stream stream) where T : default => throw null!;\n    T? IIpcObjectSerializer.Deserialize<T>(IpcJsonElement jsonElement) where T : default => throw null!;\n}\n\n#endif\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Serialization/JsonIpcMessageSerializer.cs",
    "content": "﻿using System.Diagnostics.CodeAnalysis;\nusing System.Text;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\n#if UseNewtonsoftJson\nusing Newtonsoft.Json;\n#else\nusing System.Text.Json;\n#endif\n\nnamespace dotnetCampus.Ipc.Serialization;\n\n/// <summary>\n/// 如果某 IPC 消息计划以 JSON 格式传输，那么可使用此类型来序列化和反序列化。\n/// </summary>\npublic static class JsonIpcMessageSerializer\n{\n#if UseNewtonsoftJson\n    /// <summary>\n    /// 将 IPC 模块自动生成的内部模型序列化成可供跨进程传输的 IPC 消息。\n    /// </summary>\n    /// <param name=\"tag\"></param>\n    /// <param name=\"model\"></param>\n    /// <returns></returns>\n    [Obsolete(\"因为 AOT 需要，此方法已计划删除。请自行序列化后，通过 new IpcMessage(tag, new IpcMessageBody(data), header) 创建 IPC 消息。\")]\n    public static IpcMessage Serialize(string tag, object model)\n    {\n        return SerializeToIpcMessage(IpcConfiguration.DefaultNewtonsoftJsonSerializer, 0, model, tag);\n    }\n\n    /// <summary>\n    /// 尝试将跨进程传输过来的 IPC 消息反序列化成 IPC 模块自动生成的内部模型。\n    /// </summary>\n    /// <param name=\"message\">IPC 消息。</param>\n    /// <param name=\"model\"></param>\n    /// <returns></returns>\n    [Obsolete(\"因为 AOT 需要，此方法已计划删除。请自行序列化后，通过 new IpcMessage(tag, new IpcMessageBody(data), header) 创建 IPC 消息。\")]\n    public static bool TryDeserialize<T>(IpcMessage message, [NotNullWhen(true)] out T? model) where T : class, new()\n    {\n        return IpcConfiguration.DefaultNewtonsoftJsonSerializer.TryDeserializeFromIpcMessage(message, 0, out model);\n    }\n#else\n    /// <summary>\n    /// 将 IPC 模块自动生成的内部模型序列化成可供跨进程传输的 IPC 消息。\n    /// </summary>\n    /// <param name=\"tag\"></param>\n    /// <param name=\"model\"></param>\n    /// <returns></returns>\n    [Obsolete(\"因为 AOT 需要，此方法已删除。请自行序列化后，通过 new IpcMessage(tag, new IpcMessageBody(data), header) 创建 IPC 消息。\", true)]\n    public static IpcMessage Serialize(string tag, object model)\n    {\n        return SerializeToIpcMessage(IpcConfiguration.DefaultSystemTextJsonIpcObjectSerializer, 0, model, tag);\n    }\n\n    /// <summary>\n    /// 尝试将跨进程传输过来的 IPC 消息反序列化成 IPC 模块自动生成的内部模型。\n    /// </summary>\n    /// <param name=\"message\">IPC 消息。</param>\n    /// <param name=\"model\"></param>\n    /// <returns></returns>\n    [Obsolete(\"因为 AOT 需要，此方法已删除。请自行序列化后，通过 new IpcMessage(tag, new IpcMessageBody(data), header) 创建 IPC 消息。\", true)]\n    public static bool TryDeserialize<T>(IpcMessage message, [NotNullWhen(true)] out T? model) where T : class, new()\n    {\n        return IpcConfiguration.DefaultSystemTextJsonIpcObjectSerializer.TryDeserializeFromIpcMessage(message, 0, out model);\n    }\n#endif\n\n    /// <summary>\n    /// 将 IPC 内部模型序列化成可供跨进程传输的 IPC 消息。\n    /// </summary>\n    /// <param name=\"serializer\">IPC 对象序列化器。</param>\n    /// <param name=\"header\">消息头。</param>\n    /// <param name=\"model\">要序列化的 IPC 内部模型。</param>\n    /// <param name=\"tag\">用于追踪调试的消息标签。</param>\n    /// <returns>用于 IPC 传输的消息。</returns>\n    internal static IpcMessage SerializeToIpcMessage(\n        this IIpcObjectSerializer serializer,\n        ulong header,\n        object model,\n        string tag)\n    {\n        var data = serializer.Serialize(model);\n        var message = header is 0\n            ? new IpcMessage(tag, new IpcMessageBody(data))\n            : new IpcMessage(tag, new IpcMessageBody(data), header);\n        return message;\n    }\n\n    /// <summary>\n    /// 尝试将跨进程传输过来的 IPC 消息反序列化成 IPC 模块自动生成的内部模型。\n    /// </summary>\n    /// <param name=\"serializer\"></param>\n    /// <param name=\"message\">IPC 消息。</param>\n    /// <param name=\"header\">消息头。</param>\n    /// <param name=\"model\"></param>\n    /// <returns></returns>\n    internal static bool TryDeserializeFromIpcMessage<T>(this IIpcObjectSerializer serializer,\n        IpcMessage message, ulong header, [NotNullWhen(true)] out T? model)\n        where T : class, new()\n    {\n        IpcMessageBody body;\n        if (header is 0)\n        {\n            body = message.Body;\n        }\n        else if (!message.TryGetPayload(header, out var deserializeMessage))\n        {\n            // 如果业务头不对，那就不需要解析了。\n            model = null;\n            return false;\n        }\n        else\n        {\n            body = deserializeMessage.Body;\n        }\n\n        try\n        {\n            model = serializer.Deserialize<T>(body.Buffer, body.Start, body.Length);\n            return model != null;\n        }\n#if UseNewtonsoftJson\n        catch (JsonSerializationException)\n        {\n            model = null;\n            return false;\n        }\n        catch (JsonReaderException)\n        {\n            // Newtonsoft.Json.JsonReaderException\n            //     Unexpected character encountered while parsing value: {0}.\n            // JSON 字符串中包含不符合格式的字符。典型情况如下：\n            //  * IPC 消息头被意外合入了消息体\n            //  * 待发现……\n            model = null;\n            return false;\n        }\n#endif\n#if NET6_0_OR_GREATER\n        catch (JsonException)\n        {\n            model = null;\n            return false;\n        }\n#endif\n        catch\n        {\n            // 此反序列化过程抛出异常是合理行为（毕竟 IPC 谁都能发，但应该主要是 JsonSerializationException）。\n            // 目前来看，还不知道有没有一些合理的正常的情况下会抛出其他异常，因此暂时在 !DEBUG 下不处理消息。\n#if DEBUG\n            throw;\n#else\n            model = null;\n            return false;\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Serialization/SystemTextJsonIpcObjectSerializer.cs",
    "content": "﻿#if NET6_0_OR_GREATER\n\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\nusing System.Text.Json.Serialization.Metadata;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies.Models;\nusing dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\n\nnamespace dotnetCampus.Ipc.Serialization;\n\n/// <summary>\n/// 以 <see cref=\"System.Text.Json\"/> 作为底层机制支持 IPC 对象传输。\n/// </summary>\npublic class SystemTextJsonIpcObjectSerializer : IIpcObjectSerializer\n{\n    internal SystemTextJsonIpcObjectSerializer()\n    {\n        // 完全走反射的方式\n        //JsonSerializerContext = IpcInternalJsonSerializerContext.Default;\n    }\n\n    /// <summary>\n    /// 创建 <see cref=\"SystemTextJsonIpcObjectSerializer\"/> 的新实例。\n    /// </summary>\n    /// <param name=\"jsonSerializerContext\">业务端用于业务对象序列化和反序列化的 JSON 序列化上下文。可由源生成器生成。</param>\n    public SystemTextJsonIpcObjectSerializer(JsonSerializerContext jsonSerializerContext)\n    {\n        JsonSerializerContext = new IpcCompositeJsonSerializerContext(jsonSerializerContext);\n    }\n\n    /// <summary>\n    /// 获取 JSON 序列化上下文。\n    /// </summary>\n    public JsonSerializerContext? JsonSerializerContext { get; }\n\n    /// <inheritdoc />\n    public byte[] Serialize(object? value)\n    {\n        if (value is null)\n        {\n            return \"{}\"u8.ToArray();\n        }\n\n        string json;\n        if (JsonSerializerContext is null)\n        {\n            json = JsonSerializer.Serialize(value);\n        }\n        else\n        {\n            json = JsonSerializer.Serialize(value, value.GetType(), JsonSerializerContext);\n        }\n\n        return Encoding.UTF8.GetBytes(json);\n    }\n\n    /// <inheritdoc />\n    public void Serialize(Stream stream, object? value)\n    {\n        if (value is null)\n        {\n            stream.Write(\"{}\"u8);\n            return;\n        }\n\n        if (JsonSerializerContext is null)\n        {\n            JsonSerializer.Serialize(stream, value);\n        }\n        else\n        {\n            JsonSerializer.Serialize(stream, value, value.GetType(), JsonSerializerContext);\n        }\n    }\n\n    /// <inheritdoc />\n    public IpcJsonElement SerializeToElement(object? value)\n    {\n        if (value is null)\n        {\n            return default;\n        }\n\n        JsonElement jsonElement;\n        if (JsonSerializerContext is null)\n        {\n            jsonElement = JsonSerializer.SerializeToElement(value);\n        }\n        else\n        {\n            jsonElement = JsonSerializer.SerializeToElement(value, value.GetType(), JsonSerializerContext);\n        }\n\n        return new IpcJsonElement { RawValueOnSystemTextJson = jsonElement, };\n    }\n\n    /// <inheritdoc />\n    public T? Deserialize<T>(byte[] data, int start, int length)\n    {\n        var span = data.AsSpan(start, length);\n\n        if (JsonSerializerContext is null)\n        {\n            return JsonSerializer.Deserialize<T>(span);\n        }\n        else\n        {\n            return JsonSerializer.Deserialize<T>(span, (JsonTypeInfo<T>) JsonSerializerContext.GetTypeInfo(typeof(T))!);\n        }\n    }\n\n    /// <inheritdoc />\n    public T? Deserialize<T>(Stream stream)\n    {\n        if (JsonSerializerContext is null)\n        {\n            return JsonSerializer.Deserialize<T>(stream);\n        }\n        else\n        {\n            return JsonSerializer.Deserialize<T>(stream, (JsonTypeInfo<T>) JsonSerializerContext.GetTypeInfo(typeof(T))!);\n        }\n    }\n\n    /// <inheritdoc />\n    public T? Deserialize<T>(IpcJsonElement jsonElement)\n    {\n        if (jsonElement.RawValueOnSystemTextJson is not { } element)\n        {\n            return default;\n        }\n\n        if (JsonSerializerContext is null)\n        {\n            return element.Deserialize<T>();\n        }\n        else\n        {\n            return element.Deserialize<T>((JsonTypeInfo<T>) JsonSerializerContext.GetTypeInfo(typeof(T))!);\n        }\n    }\n}\n\ninternal class IpcCompositeJsonSerializerContext(JsonSerializerContext businessContext) : JsonSerializerContext(null)\n{\n    public override JsonTypeInfo? GetTypeInfo(Type type)\n    {\n        return businessContext.GetTypeInfo(type) ?? IpcInternalJsonSerializerContext.Default.GetTypeInfo(type);\n    }\n\n    protected override JsonSerializerOptions GeneratedSerializerOptions => businessContext.Options;\n}\n\n// 基础类型\n[JsonSerializable(typeof(bool))]\n[JsonSerializable(typeof(byte))]\n[JsonSerializable(typeof(char))]\n[JsonSerializable(typeof(decimal))]\n[JsonSerializable(typeof(double))]\n[JsonSerializable(typeof(float))]\n[JsonSerializable(typeof(int))]\n[JsonSerializable(typeof(long))]\n[JsonSerializable(typeof(sbyte))]\n[JsonSerializable(typeof(short))]\n[JsonSerializable(typeof(string))]\n[JsonSerializable(typeof(uint))]\n[JsonSerializable(typeof(ulong))]\n[JsonSerializable(typeof(ushort))]\n// 远程对象\n[JsonSerializable(typeof(GeneratedProxyExceptionModel))]\n[JsonSerializable(typeof(GeneratedProxyMemberInvokeModel))]\n[JsonSerializable(typeof(GeneratedProxyMemberReturnModel))]\n[JsonSerializable(typeof(GeneratedProxyObjectModel))]\n// 路由\n[JsonSerializable(typeof(JsonIpcDirectRoutedParameterlessType))]\n[JsonSerializable(typeof(JsonIpcDirectRoutedHandleRequestExceptionResponse))]\n[JsonSerializable(typeof(JsonIpcDirectRoutedHandleRequestExceptionResponse.JsonIpcDirectRoutedHandleRequestExceptionInfo))]\ninternal partial class IpcInternalJsonSerializerContext : JsonSerializerContext;\n\n#endif\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/IIpcThreadPool.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Threading\n{\n    /// <summary>\n    /// 提供给 IPC 框架内部使用的线程池。\n    /// </summary>\n    internal interface IIpcThreadPool\n    {\n        /// <summary>\n        /// 在线程池挑选一个线程执行指定代码。\n        /// 当任务确定已经开始执行之后就会返回第一层 <see cref=\"Task\"/>，\n        /// 在任务和超时时间先完成者会再次返回第二层 <see cref=\"Task\"/>。\n        /// </summary>\n        /// <param name=\"action\"></param>\n        /// <param name=\"logger\"></param>\n        /// <returns></returns>\n        /// <remarks>\n        /// <para>特别注意！！！</para>\n        /// 此方法不是线程安全的，调用方必须确保此方法的调用处于临界区。\n        /// </remarks>\n        Task<Task> Run(Action action, ILogger? logger);\n    }\n\n    /// <summary>\n    /// 自定义的 IPC 所采用的线程池\n    /// </summary>\n    public abstract class CustomIpcThreadPoolBase : IIpcThreadPool\n    {\n        Task<Task> IIpcThreadPool.Run(Action action, ILogger? logger)\n        {\n            return Run(action);\n        }\n\n        /// <inheritdoc cref=\"IIpcThreadPool.Run\"/>\n        protected abstract Task<Task> Run(Action action);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/IpcSingleThreadPool.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Threading\n{\n    /// <summary>\n    /// 提供给 IPC 框架内部使用的线程池。\n    /// 所有的调度都将确定地按顺序执行，一个执行完毕后才会执行下一个。\n    /// </summary>\n    internal class IpcSingleThreadPool : IIpcThreadPool\n    {\n        public Task<Task> Run(Action action, ILogger? logger)\n        {\n            action();\n            // 因为此方法的调用方能保证依次执行而不并发，所以这里直接 Task.Run 也不会浪费线程。\n            //await Task.Run(action).ConfigureAwait(false);\n            return Task.FromResult<Task>(Task.FromResult(0));\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/IpcTaskScheduling.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Threading\n{\n    /// <summary>\n    /// 决定 IPC 消息抵达时，以何种调度方式通知给业务代码。\n    /// </summary>\n    public enum IpcTaskScheduling\n    {\n        /// <summary>\n        /// 所有的 IPC 共享同一个调度线程池。以大体上按顺序（但不保证）的并发方式通知。\n        /// </summary>\n        GlobalConcurrent = 0,\n\n        /// <summary>\n        /// 按顺序依次通知，并且与其他 IPC 实例互不影响。\n        /// </summary>\n        /// <remarks>\n        /// <para>使用前请避免在业务中创造死锁条件：</para>\n        /// <list type=\"bullet\">\n        /// <item>A 向 B 发送消息，但 B 为了回 A 的这条消息，需要先向 A 请求某种值。</item>\n        /// </list>\n        /// <para>在这种情况下，按顺序的发送方式将导致死锁。</para>\n        /// </remarks>\n        LocalOneByOne = 1,\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/IpcThreadPool.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Globalization;\nusing System.Runtime.CompilerServices;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Utils.Extensions;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nusing Producer = System.Collections.Concurrent.BlockingCollection<int>;\n\nnamespace dotnetCampus.Ipc.Threading\n{\n    /// <summary>\n    /// 提供给 IPC 框架内部使用的线程池。\n    /// 专为调用时长不确定的业务代码而设计。\n    /// 此类型的所有公共方法都不是线程安全的，因此你需要确保在临界区调用这些代码。\n    /// 但内部方法是线程安全的。\n    /// </summary>\n    internal sealed class IpcThreadPool : IIpcThreadPool\n    {\n        /// <summary>\n        /// 为每一个创建的线程名称准备一个序号。\n        /// </summary>\n        private volatile int _threadIndex;\n\n        /// <summary>\n        /// 目前正在执行 IPC 任务的线程总数（含正在执行任务和和正在等待执行任务的）。\n        /// </summary>\n        private volatile int _threadCount;\n\n        /// <summary>\n        /// 目前正在等待分配任务的线程数。\n        /// </summary>\n        private volatile int _availableCount;\n\n        /// <summary>\n        /// 最近一次回收线程的时间，如果短时间内大量触发则不回收。\n        /// </summary>\n        private DateTime _lastRecycleTime;\n\n        /// <summary>\n        /// 防止回收重入。\n        /// </summary>\n        private volatile int _isRecycling;\n\n        /// <summary>\n        /// 当前正在等待延时启动线程的任务。\n        /// </summary>\n        private readonly ConcurrentQueue<CancellationTokenSource> _delayStartWaitings = new();\n\n        /// <summary>\n        /// 任务队列，与 <see cref=\"_workingThreads\"/> 配合使用。\n        /// <para>这里放真实的任务。</para>\n        /// <list type=\"bullet\">\n        /// <item>为什么不放到 <see cref=\"_workingThreads\"/> 里呢？</item>\n        /// <item>是因为那按时间顺序放的任务，到那里会等线程调度，真正执行的时候可能顺序就乱了。</item>\n        /// <item>于是到真正调度到的时候再取任务，可以更大概率避免调度时间差导致的顺序问题。</item>\n        /// </list>\n        /// </summary>\n        private readonly ConcurrentQueue<IpcStartEndTaskItem> _taskQueue = new();\n\n        /// <summary>\n        /// 线程队列，与 <see cref=\"_taskQueue\"/> 配合使用。\n        /// <para>这里放此刻可供调度的线程资源。</para>\n        /// <list type=\"bullet\">\n        /// <item>存一个线程和其对应的调度器，不放具体的任务。</item>\n        /// <item>不放具体任务是因为按时间顺序调度的任务，真正执行的时候可能顺序会乱了。</item>\n        /// </list>\n        /// </summary>\n        private readonly ConcurrentQueue<(Thread thread, Producer producer)> _workingThreads = new();\n\n        /// <summary>\n        /// 在线程池挑选一个线程执行指定代码。\n        /// 当任务确定已经开始执行之后就会返回第一层 <see cref=\"Task\"/>，\n        /// 在任务和超时时间先完成者会再次返回第二层 <see cref=\"Task\"/>。\n        /// 注意，当线程池中线程数量不足时，第一层 <see cref=\"Task\"/> 的返回会延迟，直到空出新的可供执行的线程资源。\n        /// </summary>\n        /// <param name=\"action\"></param>\n        /// <param name=\"logger\"></param>\n        /// <returns></returns>\n        /// <remarks>\n        /// <para>特别注意！！！</para>\n        /// 此方法不是线程安全的，调用方必须确保此方法的调用处于临界区。\n        /// </remarks>\n        public Task<Task> Run(Action action, ILogger? logger)\n        {\n            var taskItem = new IpcStartEndTaskItem(action);\n            RunRecursively(taskItem, logger);\n            return taskItem.AsTask();\n        }\n\n        private async void RunRecursively(IpcStartEndTaskItem taskItem, ILogger? logger)\n        {\n            // 如果打算开始一个任务，则从线程池中取一个线程。\n            if (_workingThreads.TryDequeue(out var result))\n            {\n                // 线程池中有空闲线程。\n                var producer = result.producer;\n                _taskQueue.Enqueue(taskItem);\n                producer.Add(0);\n            }\n            else\n            {\n                // 线程池中所有线程繁忙。\n                var count = _threadCount;\n                var delayTime = GetCurrentStartThreadDelayTime(count);\n                if (delayTime == TimeSpan.Zero)\n                {\n                    // 线程池中虽没有空闲线程，但打算立即启动新线程。\n                    var producer = new Producer();\n                    StartThread(producer, logger);\n                    _taskQueue.Enqueue(taskItem);\n                    producer.Add(0);\n                }\n                else\n                {\n                    // 线程池中没有空闲线程，因此等待一段时间后重试。\n                    Log(logger, $\"因为线程资源紧张（{count} 个），延迟 {delayTime.TotalMilliseconds}ms 后重新调度。\");\n                    await DelayOrAnyThreadAvailable(delayTime);\n                    // 等待结束后重跑线程调度。\n                    RunRecursively(taskItem, logger);\n                }\n            }\n        }\n\n        private void StartThread(Producer c, ILogger? logger)\n        {\n            var index = Interlocked.Increment(ref _threadIndex);\n            var t = new Thread(OnThreadStart)\n            {\n                Name = $\"IPC-{index.ToString(CultureInfo.InvariantCulture).PadLeft(2, '0')}\",\n                IsBackground = true,\n            };\n            var count = Interlocked.Increment(ref _threadCount);\n            t.Start((c, logger));\n        }\n\n        private void OnThreadStart(object? arg)\n        {\n            var thread = Thread.CurrentThread;\n            var (producer, logger) = ((Producer, ILogger?)) arg!;\n            Interlocked.Increment(ref _availableCount);\n            Log(logger, $\"线程启动 {thread.Name}\");\n            foreach (var _ in producer.GetConsumingEnumerable())\n            {\n                if (!_taskQueue.TryDequeue(out var taskItem))\n                {\n                    throw new InvalidOperationException(\"100% BUG。先加入的任务，后面必定可以取到。\");\n                }\n                var action = taskItem.Action;\n                try\n                {\n                    // 开始执行任务。\n                    Interlocked.Decrement(ref _availableCount);\n                    taskItem.Start();\n                    action();\n                }\n                catch (Exception ex)\n                {\n                    throw new InvalidOperationException(\"调用 IpcThreadPool.Run 方法时传入的动作必须完全自行处理好异常，不允许让任何异常泄漏出来。\", ex);\n                }\n                finally\n                {\n                    // 完成执行任务。\n                    taskItem.End();\n                    // 回收线程（先回收线程，以防把本线程回收掉，导致任何任务都新开启线程执行）。\n                    RecycleUselessThreads(logger);\n                    // 重新回到线程池。\n                    _workingThreads.Enqueue((thread, producer));\n                    // 可用线程数 +1。\n                    Interlocked.Increment(ref _availableCount);\n                    // 如果有等待线程启动，则立即结束等待。\n                    ClearWaitings();\n                }\n            }\n            producer.Dispose();\n            Log(logger, $\"线程退出 {thread.Name}\");\n        }\n\n        private void ClearWaitings()\n        {\n            while (_delayStartWaitings.TryDequeue(out var tokenSource))\n            {\n                tokenSource.Cancel();\n            }\n        }\n\n        /// <summary>\n        /// 等待一个指定的时间。但如果有任何一个线程空闲，则等待立即完成。\n        /// </summary>\n        /// <param name=\"delayTime\"></param>\n        /// <returns></returns>\n        private async Task DelayOrAnyThreadAvailable(TimeSpan delayTime)\n        {\n            var taskSource = new CancellationTokenSource();\n            _delayStartWaitings.Enqueue(taskSource);\n            try\n            {\n                while (!taskSource.IsCancellationRequested && delayTime > TimeSpan.Zero)\n                {\n                    await Task.Delay(15, taskSource.Token);\n                    delayTime -= TimeSpan.FromMilliseconds(15);\n                }\n            }\n            catch (OperationCanceledException)\n            {\n                // 等待已结束。\n            }\n        }\n\n        private TimeSpan GetCurrentStartThreadDelayTime(int count)\n        {\n            if (_availableCount > 0)\n            {\n                // 如果当前存在可供使用的线程资源，则直接开启线程。\n                return TimeSpan.Zero;\n            }\n\n            var delay = ThreadCountToDelayTime(count);\n            return delay;\n        }\n\n        /// <summary>\n        /// 根据当前已有的线程总数来决定新启动下一个线程的等待时间。\n        /// </summary>\n        /// <param name=\"count\"></param>\n        /// <returns></returns>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private TimeSpan ThreadCountToDelayTime(int count) => count switch\n        {\n            0 => TimeSpan.Zero,\n            1 => TimeSpan.Zero,\n            2 => TimeSpan.Zero,\n            3 => TimeSpan.Zero,\n            4 => TimeSpan.Zero,\n            5 => TimeSpan.FromMilliseconds(250),\n            6 => TimeSpan.FromMilliseconds(500),\n            7 => TimeSpan.FromSeconds(1),\n            8 => TimeSpan.FromSeconds(2),\n            9 => TimeSpan.FromSeconds(4),\n            10 => TimeSpan.FromSeconds(10),\n            _ => TimeSpan.FromSeconds(30),\n        };\n\n        /// <summary>\n        /// 回收线程\n        /// </summary>\n        /// <param name=\"logger\"></param>\n        private void RecycleUselessThreads(ILogger? logger)\n        {\n            var now = DateTime.Now;\n            var elapsed = now - _lastRecycleTime;\n            // 1 秒内最多回收一次线程。\n            if (elapsed > TimeSpan.FromSeconds(1))\n            {\n                var isRecycling = Interlocked.CompareExchange(ref _isRecycling, 1, 0);\n                if (isRecycling is 1)\n                {\n                    return;\n                }\n\n                _lastRecycleTime = now;\n                // 期望回收数为当前空闲线程数的一半。\n\n                try\n                {\n                    RecycleCore();\n                }\n                finally\n                {\n                    _isRecycling = 0;\n                }\n            }\n\n            void RecycleCore()\n            {\n                var desiredDisposeCount = _workingThreads.Count / 2;\n                while (desiredDisposeCount > 0)\n                {\n                    desiredDisposeCount--;\n                    if (_workingThreads.TryDequeue(out var result))\n                    {\n                        var (thread, producer) = result;\n                        Log(logger, $\"回收线程 {thread.Name}\");\n                        producer.CompleteAdding();\n                    }\n                }\n            }\n        }\n\n        private void Log(ILogger? logger, string message)\n        {\n            logger.Information($\"[{nameof(IpcThreadPool)}] {message}\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/Tasks/IpcStartEndTaskItem.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.Threading\n{\n    internal sealed class IpcStartEndTaskItem\n    {\n        private readonly TaskCompletionSource<TaskCompletionSource<bool>> _actionStartedTaskSource = new();\n        private readonly TaskCompletionSource<bool> _actionEndedTaskSource = new();\n\n        public IpcStartEndTaskItem(Action action)\n        {\n            Action = action;\n        }\n\n        public Action Action { get; }\n\n        public async Task<Task> AsTask()\n        {\n            var endTask = await _actionStartedTaskSource.Task.ConfigureAwait(false);\n            return endTask.Task;\n        }\n\n        internal void Start()\n        {\n            _actionStartedTaskSource.SetResult(_actionEndedTaskSource);\n        }\n\n        internal void End()\n        {\n            _actionEndedTaskSource.SetResult(true);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/Tasks/IpcTask.cs",
    "content": "﻿using System;\nusing System.Collections.Concurrent;\nusing System.Diagnostics;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Threading.Tasks\n{\n    /// <summary>\n    /// 管理一组 IPC 任务。\n    /// 加入此 <see cref=\"IpcTask\"/> 的任务严格按照先到先执行的原则依次执行；\n    /// 但与普通队列不同的是，一旦任务开始执行后，便可根据配置决定是否并发执行后续任务（而不必等待任务完全执行完成）；\n    /// 并且支持限制并发数量（以避免潜在的性能影响）。\n    /// </summary>\n    internal sealed class IpcTask\n    {\n        private volatile int _isRunning;\n        private readonly ConcurrentQueue<TaskItem> _queue = new();\n        private readonly IIpcThreadPool _threadPool;\n\n        /// <summary>\n        /// 由于原子操作仅提供高性能的并发处理而不保证准确性，因此需要一个锁来同步 <see cref=\"_isRunning\"/> 中值为 0 时所指的不确定情况。\n        /// 不能使用一个锁来同步所有情况是因为在锁中使用 async/await 是不安全的，因此避免在锁中执行异步任务；我们使用原子操作来判断异步任务的执行条件。\n        /// </summary>\n        private readonly object _locker = new();\n\n        public IpcTask(IIpcThreadPool threadPool)\n        {\n            _threadPool = threadPool;\n        }\n\n        /// <summary>\n        /// 支持并发进入的 IPC 任务。\n        /// 被此 <see cref=\"IpcTask\"/> 管理的异步任务将按调用此方法的顺序依次开始执行，至于开始后多少个任务可以同时运行或者执行多长时间后算超时而执行下一个取决于此 <see cref=\"IpcTask\"/> 的配置。\n        /// </summary>\n        /// <param name=\"action\"></param>\n        /// <param name=\"logger\"></param>\n        /// <returns></returns>\n        public Task Run(Action action, ILogger? logger = null) => Run(() =>\n        {\n            action();\n            return Task.FromResult(0);\n        }, logger);\n\n        /// <summary>\n        /// 支持并发进入的 IPC 任务。\n        /// 被此 <see cref=\"IpcTask\"/> 管理的异步任务将按调用此方法的顺序依次开始执行，至于开始后多少个任务可以同时运行或者执行多长时间后算超时而执行下一个取决于此 <see cref=\"IpcTask\"/> 的配置。\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"task\"></param>\n        /// <param name=\"logger\"></param>\n        /// <returns></returns>\n        public Task<T> Run<T>(Func<Task<T>> task, ILogger? logger = null)\n        {\n            var item = new TaskItem<T>(task, logger);\n            _queue.Enqueue(item);\n            ResumeRunning();\n            return item.AsTask();\n        }\n\n        private async void ResumeRunning()\n        {\n            try\n            {\n                var isRunning = Interlocked.CompareExchange(ref _isRunning, 1, 0);\n                if (isRunning is 1)\n                {\n                    lock (_locker)\n                    {\n                        if (_isRunning is 1)\n                        {\n                            // 当前已经在执行队列，因此无需继续执行。\n                            return;\n                        }\n                    }\n                }\n\n                var hasTask = true;\n                while (hasTask)\n                {\n                    // 当前还没有任何队列开始执行，因此需要开始执行队列。\n                    while (_queue.TryDequeue(out var taskItem))\n                    {\n                        // 内部已包含异常处理，因此外面可以无需捕获或者清理。\n                        await ConsumeTaskItemAsync(taskItem).ConfigureAwait(false);\n                    }\n\n                    lock (_locker)\n                    {\n                        hasTask = _queue.TryPeek(out _);\n                        if (!hasTask)\n                        {\n                            _isRunning = 0;\n                        }\n                    }\n                }\n            }\n            catch (Exception)\n            {\n                // 当前是后台线程了，不能接受任何的抛出\n                // 理论上这里框架层是不会抛出任何异常的\n#if DEBUG\n                Debugger.Break();\n#endif\n            }\n        }\n\n        /// <summary>\n        /// 运行在其中一个调用线程中的临界区。\n        /// 请勿执行耗时操作。\n        /// </summary>\n        /// <param name=\"taskItem\"></param>\n        private async Task ConsumeTaskItemAsync(TaskItem taskItem)\n        {\n            // 第一层等待，非常必要，这可以确保任务一定按顺序开始执行。\n            var timeout = await _threadPool.Run(() => taskItem.Run(), taskItem.Logger).ConfigureAwait(false);\n            // timeout 是第二层等待，即任务完全执行完成（而这里我们并不关心，所以无视）。\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Threading/Tasks/TaskItem.cs",
    "content": "﻿using System;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Threading.Tasks\n{\n    internal abstract class TaskItem\n    {\n        protected TaskItem(ILogger? logger)\n        {\n            Logger = logger;\n        }\n\n        public ILogger? Logger { get; }\n\n        internal abstract void Run();\n    }\n\n    internal sealed class TaskItem<T> : TaskItem\n    {\n        private readonly TaskCompletionSource<T> _source;\n\n        public TaskItem(Func<Task<T>> func, ILogger? logger)\n            : base(logger)\n        {\n            _source = new TaskCompletionSource<T>();\n            Func = func;\n        }\n\n        public Func<Task<T>> Func { get; }\n\n        public Task<T> AsTask() => _source.Task;\n\n        internal override async void Run()\n        {\n            try\n            {\n                var value = await Func().ConfigureAwait(false);\n                _source.SetResult(value);\n            }\n            catch (Exception ex)\n            {\n                _source.SetException(ex);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Buffers/ISharedArrayPool.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Utils.Buffers\n{\n    /// <summary>\n    ///     提供共享的数组\n    /// </summary>\n    // 用于后续支持 .NET 4.5 版本，此版本没有 ArrayPool 类\n    public interface ISharedArrayPool\n    {\n        /// <summary>\n        ///     租借数组，要求返回的数组长度最小是 <paramref name=\"minLength\" /> 长度\n        /// </summary>\n        /// <param name=\"minLength\"></param>\n        /// <returns></returns>\n        byte[] Rent(int minLength);\n\n        /// <summary>\n        ///     将租借的数组返回，将会被下次租借使用\n        /// </summary>\n        /// <param name=\"array\"></param>\n        void Return(byte[] array);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Buffers/SharedArrayPool.cs",
    "content": "﻿using System;\nusing System.Threading;\n\nnamespace dotnetCampus.Ipc.Utils.Buffers\n{\n#if NETCOREAPP\n    using System.Buffers;\n    /// <summary>\n    ///     共享数组内存，底层使用 ArrayPool 实现\n    /// </summary>\n    public class SharedArrayPool : ISharedArrayPool\n    {\n        /// <summary>\n        ///     创建共享数组\n        /// </summary>\n        /// <param name=\"arrayPool\"></param>\n        public SharedArrayPool(ArrayPool<byte>? arrayPool = null)\n        {\n            ArrayPool = arrayPool ?? ArrayPool<byte>.Shared;\n        }\n\n        /// <summary>\n        ///     使用的数组池\n        /// </summary>\n        public ArrayPool<byte> ArrayPool { get; }\n\n        /// <inheritdoc />\n        public byte[] Rent(int minLength)\n        {\n            return ArrayPool.Rent(minLength);\n        }\n\n        /// <inheritdoc />\n        public void Return(byte[] array)\n        {\n            ArrayPool.Return(array);\n        }\n    }\n#else\n    /// <summary>\n    /// 共享数组内存\n    /// </summary>\n    /// 低配版\n    public class SharedArrayPool : ISharedArrayPool\n    {\n        /// <inheritdoc />\n        public byte[] Rent(int minLength)\n        {\n            var cacheByteList = _cacheByteList;\n\n            if (cacheByteList != null && cacheByteList.Length >= minLength)\n            {\n                _cacheByteList = null;\n                return cacheByteList;\n            }\n\n            return new byte[minLength];\n        }\n\n        /// <inheritdoc />\n        public void Return(byte[] array)\n        {\n            var cacheByteList = _cacheByteList;\n            if (cacheByteList == null || cacheByteList.Length < array.Length)\n            {\n                _cacheByteList = cacheByteList;\n            }\n        }\n\n        [ThreadStatic]\n        private static byte[]? _cacheByteList;\n    }\n#endif\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Caching/CachePool.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace dotnetCampus.Ipc.Utils.Caching;\n\n/// <summary>\n/// 如果获取 <typeparamref name=\"TSource\"/> 对应信息的过程比较耗时（例如反射），\n/// 则可使用 <see cref=\"CachePool{TSource,TCache}\"/> 对此过程进行缓存。\n/// </summary>\n/// <typeparam name=\"TSource\">为了获取信息所需的源对象。</typeparam>\n/// <typeparam name=\"TCache\">获取的信息将会储存在此类型的缓存对象中。</typeparam>\ninternal sealed class CachePool<TSource, TCache> where TSource : notnull\n{\n    /// <summary>\n    /// 使用特定的转换器创建 <see cref=\"CachePool{TSource,TCache}\"/> 的新实例。\n    /// </summary>\n    /// <param name=\"conversion\">从源对象到目标对象的转换方法，此方法仅执行一次。</param>\n    /// <param name=\"threadSafe\">如果获取缓存的过程可能在不同线程，则设置此值为 true，以便让缓存过程是线程安全的。</param>\n    public CachePool(Func<TSource, TCache> conversion, bool threadSafe = false)\n        : this(new CachePoolValueMap<TSource, TCache>(conversion), threadSafe)\n    {\n    }\n\n    /// <summary>\n    /// 使用特定的转换器创建 <see cref=\"CachePool{TSource,TCache}\"/> 的新实例。<para />\n    /// 你也可以使用隐式转换创建新实例：<para />\n    /// <code>\n    /// <see cref=\"CachePool{TSource,TCache}\"/> pool = <see cref=\"CachePoolValueMap{TSource,TCache}\"/>(key => \"\")<para />\n    /// {<para />\n    ///     [1] = o => \"1\",<para />\n    ///     [2] = o => \"2\",<para />\n    /// };<para />\n    /// </code>\n    /// </summary>\n    /// <param name=\"conversion\">从源对象到目标对象的转换器。</param>\n    /// <param name=\"threadSafe\">如果获取缓存的过程可能在不同线程，则设置此值为 true，以便让缓存过程是线程安全的。</param>\n    public CachePool(CachePoolValueMap<TSource, TCache> conversion, bool threadSafe = false)\n    {\n        _converter = conversion ?? throw new ArgumentNullException(nameof(conversion));\n        _locker = threadSafe ? new object() : null;\n    }\n\n    /// <summary>\n    /// 从缓存池中获取缓存的信息，如果从未获取过信息，则将会执行一次\n    /// 从 <typeparamref name=\"TSource\"/> 到 <typeparamref name=\"TCache\"/> 的转换。\n    /// </summary>\n    /// <param name=\"source\">为了获取信息所需的源对象。</param>\n    /// <returns>缓存的对象。</returns>\n    public TCache this[TSource source]\n    {\n        get => GetOrCacheValue(source);\n    }\n\n    /// <summary>\n    /// 获取锁，如果此值为 null，说明无需加锁。\n    /// </summary>\n    private readonly object? _locker;\n\n    /// <summary>\n    /// 获取转换对象的方法。\n    /// </summary>\n    private readonly CachePoolValueMap<TSource, TCache> _converter;\n\n    /// <summary>\n    /// 获取缓存了 <typeparamref name=\"TCache\"/> 的字典。\n    /// </summary>\n    private readonly Dictionary<TSource, TCache> _cacheDictionary = new();\n\n    /// <summary>\n    /// 从缓存池中获取缓存的信息，如果从未获取过信息，则将会执行一次\n    /// 从 <typeparamref name=\"TSource\"/> 到 <typeparamref name=\"TCache\"/> 的转换。\n    /// </summary>\n    /// <param name=\"source\"></param>\n    private TCache GetOrCacheValue(TSource source)\n    {\n        // 如果不需要加锁，则直接返回值。\n        if (_locker == null)\n        {\n            return GetOrCacheValue();\n        }\n\n        // 如果需要加锁，则加锁后返回值。\n        lock (_locker)\n        {\n            return GetOrCacheValue();\n        }\n\n        // 如果存在缓存，则获取缓存；否则从源值转换成缓存。\n        TCache GetOrCacheValue()\n        {\n            if (!_cacheDictionary.TryGetValue(source, out var cache))\n            {\n                // 转换值并放入字典缓存。\n                cache = _converter.Convert(source);\n                _cacheDictionary[source] = cache;\n            }\n\n            return cache;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Caching/CachePoolValueMap.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Runtime.CompilerServices;\n\nnamespace dotnetCampus.Ipc.Utils.Caching;\n\n/// <summary>\n/// 为 <see cref=\"CachePool{TSource,TCache}\"/> 的多值转换提供转换器。\n/// </summary>\n/// <typeparam name=\"TSource\">转换所需的源对象。</typeparam>\n/// <typeparam name=\"TCache\">转换的值将会储存在此类型的缓存对象中。</typeparam>\ninternal sealed class CachePoolValueMap<TSource, TCache> where TSource : notnull\n{\n    /// <summary>\n    /// 创建 <see cref=\"CachePoolValueMap{TSource,TCache}\"/> 类型的新实例，\n    /// 用于为 <see cref=\"CachePool{TSource,TCache}\"/> 提供多值转换。\n    /// </summary>\n    /// <param name=\"defaultConverter\">默认的转换函数。</param>\n    public CachePoolValueMap(Func<TSource, TCache> defaultConverter)\n    {\n        _converter = defaultConverter ?? throw new ArgumentNullException(nameof(defaultConverter));\n    }\n\n    /// <summary>\n    /// 获取或设置值转换方法。可以通过此实例额外设置特殊值的转换规则。\n    /// </summary>\n    /// <param name=\"source\">转换的源对象。</param>\n    public Func<TSource, TCache> this[TSource source]\n    {\n        get => _additionalConverters.TryGetValue(source, out var converter) ? converter : _converter;\n        set => _additionalConverters[source] = value ?? throw new ArgumentNullException(nameof(value));\n    }\n\n    /// <summary>\n    /// 转换一个值。\n    /// </summary>\n    /// <param name=\"source\">转换的源对象。</param>\n    /// <returns>缓存后的对象。</returns>\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    internal TCache Convert(TSource source) => this[source](source);\n\n    /// <summary>\n    /// 获取转换对象的方法。\n    /// </summary>\n    private readonly Func<TSource, TCache> _converter;\n\n    /// <summary>\n    /// 获取特殊指定的获取 <typeparamref name=\"TCache\"/> 方法的字典。\n    /// 此字典中的转换器优先级高于 <see cref=\"_converter\"/> 方法。\n    /// </summary>\n    private readonly Dictionary<TSource, Func<TSource, TCache>> _additionalConverters\n        = new();\n\n    /// <summary>\n    /// 从 <see cref=\"CachePoolValueMap{TSource,TCache}\"/> 转到 <see cref=\"CachePool{TSource,TCache}\"/> 的实例。\n    /// </summary>\n    /// <param name=\"converter\"><see cref=\"CachePoolValueMap{TSource,TCache}\"/> 的实例。</param>\n    [return: NotNullIfNotNull(\"converter\")]\n    public static implicit operator CachePool<TSource, TCache>?(\n        CachePoolValueMap<TSource, TCache>? converter)\n        => converter is null ? null : new CachePool<TSource, TCache>(converter);\n\n    /// <summary>\n    /// 转到 <see cref=\"CachePool{TSource,TCache}\"/> 的实例。\n    /// </summary>\n    public CachePool<TSource, TCache> ToCachePool() => new(this);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/BinaryArrayExtensions.cs",
    "content": "﻿using System;\nusing System.Linq;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions\n{\n    class BinaryArrayExtensions\n    {\n        public static byte[] Combine(params byte[][] arrays)\n        {\n            var ret = new byte[arrays.Sum(x => x.Length)];\n            var offset = 0;\n            foreach (var data in arrays)\n            {\n                Buffer.BlockCopy(data, 0, ret, offset, data.Length);\n                offset += data.Length;\n            }\n            return ret;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/ByteListExtensions.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Utils.Extensions\n{\n    internal static class ByteListExtensions\n    {\n        public static bool Equals(byte[] a, byte[] b, int length)\n        {\n            for (var i = 0; i < length; i++)\n            {\n                if (a[i] != b[i])\n                {\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        public static bool Equals(byte[] a, byte[] b)\n        {\n            if (ReferenceEquals(a, b)) return true;\n\n            if (a.Length != b.Length) return false;\n\n            return Equals(a, b, a.Length);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/DoubleBufferTaskExtensions.cs",
    "content": "﻿using System;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Threading;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions\n{\n    static class DoubleBufferTaskExtensions\n    {\n        private static volatile int _count = 0;\n\n        public static async Task AddTaskAsync(this DoubleBufferTask<Func<Task>> doubleBufferTask, Func<Task> task)\n        {\n            var count = Interlocked.Increment(ref _count);\n\n            LoggerExtensions.Trace(null, $\"[{count}] [AddTaskAsync]\");\n            var taskCompletionSource = new TaskCompletionSource<bool>();\n\n            doubleBufferTask.AddTask(async () =>\n            {\n                LoggerExtensions.Trace(null, $\"[{count}] [doubleBufferTask.AddTask]\");\n                try\n                {\n                    await task().ConfigureAwait(false);\n                    LoggerExtensions.Trace(null, $\"[{count}] [doubleBufferTask.AddTask] Completed\");\n                    taskCompletionSource.SetResult(true);\n                }\n                catch (Exception e)\n                {\n                    LoggerExtensions.Trace(null, $\"[{count}] [doubleBufferTask.AddTask] Exception\");\n                    taskCompletionSource.SetException(e);\n                }\n            });\n\n            await taskCompletionSource.Task.ConfigureAwait(false);\n            LoggerExtensions.Trace(null, $\"[{count}] [taskCompletionSource.Task] Completed\");\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/IpcMessageCommandExtensions.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Linq;\nusing System.Text;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions\n{\n    /// <summary>\n    /// 对 <see cref=\"IpcMessageCommandType\"/> 提供扩展方法。\n    /// </summary>\n    internal static class IpcMessageCommandExtensions\n    {\n        /// <summary>\n        /// 将 <see cref=\"CoreMessageType\"/> 转换为可与 <see cref=\"IpcMessageCommandType\"/> 进行位运算的标识位枚举。\n        /// </summary>\n        /// <param name=\"coreMessageType\">要转换的 <see cref=\"CoreMessageType\"/>。</param>\n        /// <returns>转换后的 <see cref=\"IpcMessageCommandType\"/>。</returns>\n        public static IpcMessageCommandType AsMessageCommandTypeFlags(this CoreMessageType coreMessageType)\n        {\n            return (IpcMessageCommandType) ((int) coreMessageType << 3);\n        }\n\n        /// <summary>\n        /// 将 <see cref=\"IpcMessageCommandType\"/> 进行位运算以得出 <see cref=\"CoreMessageType\"/> 消息类型。\n        /// </summary>\n        /// <param name=\"ipcMessageCommandType\">要转换的 <see cref=\"IpcMessageCommandType\"/>。</param>\n        /// <returns>转换后的 <see cref=\"CoreMessageType\"/>。</returns>\n        public static CoreMessageType ToCoreMessageType(this IpcMessageCommandType ipcMessageCommandType)\n        {\n            return (0b_0000_0000_0011_1000 & (short) ipcMessageCommandType) switch\n            {\n                0b_0000_0000_0000_1000 => CoreMessageType.Raw,\n                0b_0000_0000_0001_0000 => CoreMessageType.String,\n                0b_0000_0000_0010_0000 => CoreMessageType.JsonObject,\n                _ => CoreMessageType.NotMessageBody,\n            };\n        }\n\n#if DEBUG\n        internal static string? ToDebugMessageText(this IpcMessageBody body, IpcMessageCommandType ipcMessageCommandType)\n        {\n            return ipcMessageCommandType.ToCoreMessageType() switch\n            {\n                CoreMessageType.JsonObject => Encoding.UTF8.GetString(body.Buffer, body.Start, body.Length),\n                CoreMessageType.String => Encoding.UTF8.GetString(body.Buffer, body.Start, body.Length),\n                _ => Encoding.UTF8.GetString(body.Buffer, body.Start, body.Length),\n            };\n        }\n\n        internal static string? ToDebugMessageText(this IpcBufferMessageContext context)\n        {\n            string? debugMessageBody = string.Join(Environment.NewLine, context.IpcBufferMessageList.Select(x =>\n                x.ToDebugMessageText(context.IpcMessageCommandType) ?? \"\"));\n            return string.IsNullOrEmpty(debugMessageBody) ? null : debugMessageBody;\n        }\n\n        internal static string? ToDebugMessageText(this PeerStreamMessageArgs args)\n        {\n            return args.ToPeerMessageArgs().Message.Body.ToDebugMessageText(args.MessageCommandType);\n        }\n#endif\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/IpcMessageContextExtensions.cs",
    "content": "﻿using dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.IO;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions\n{\n    static class IpcMessageContextExtensions\n    {\n        public static ByteListMessageStream ToStream(this in IpcMessageContext ipcMessageContext)\n        {\n            return new ByteListMessageStream(ipcMessageContext);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/IpcMessageExtension.cs",
    "content": "﻿using System;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions;\n\nstatic class IpcMessageExtension\n{\n    public static IpcMessage Skip(this IpcMessage ipcMessage, int length)\n    {\n        return new IpcMessage(ipcMessage.Tag, new IpcMessageBody(ipcMessage.Body.Buffer,\n            ipcMessage.Body.Start + length,\n            ipcMessage.Body.Length - length));\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/LoggerExtensions.cs",
    "content": "﻿using System;\n\nusing dotnetCampus.Ipc.Context.LoggingContext;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions\n{\n    internal static class LoggerExtensions\n    {\n        public static void Trace(this ILogger? logger, string message)\n        {\n            logger?.Log(LogLevel.Trace, default, message, null, FormatOnlyMessage);\n        }\n\n        public static void Debug(this ILogger? logger, string message)\n        {\n            logger?.Log(LogLevel.Debug, LoggerEventIds.CommonDebugEventId, message, null, FormatOnlyMessage);\n        }\n\n        public static void Information(this ILogger? logger, string message)\n        {\n            logger?.Log(LogLevel.Information, default, message, null, FormatOnlyMessage);\n        }\n\n        public static void Warning(this ILogger? logger, string message)\n        {\n            if (logger is null)\n            {\n                System.Diagnostics.Debug.WriteLine($\"[{DateTime.Now:hh:mm:ss.fff}] [IPC] [Warning] {message}\");\n            }\n            else\n            {\n                logger?.Log(LogLevel.Warning, default, message, null, FormatOnlyMessage);\n            }\n        }\n\n        public static void Error(this ILogger? logger, string message)\n        {\n            if (logger is null)\n            {\n                System.Diagnostics.Debug.WriteLine($\"[{DateTime.Now:hh:mm:ss.fff}] [IPC] [Error] {message}\");\n            }\n            else\n            {\n                logger?.Log(LogLevel.Error, default, message, null, FormatOnlyMessage);\n            }\n        }\n\n        public static void Error(this ILogger? logger, Exception exception, string? message = null)\n        {\n            logger?.Log(LogLevel.Error, default, message, exception, StandardFormatMessage);\n        }\n\n        private static Func<string, Exception?, string> FormatOnlyMessage => static (s, _) => s;\n\n        private static Func<string?, Exception?, string> StandardFormatMessage => static (s, e) =>\n        {\n            if (s is null && e != null)\n            {\n                return e.ToString();\n            }\n            return $\"[IPC Error] {s} {e}\";\n        };\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/PeerMessageArgsExtension.cs",
    "content": "﻿using System;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions;\n\n/// <summary>\n/// 对 <see cref=\"IPeerMessageArgs\"/> 的扩展\n/// </summary>\npublic static class PeerMessageArgsExtension\n{\n    internal static bool TryGetPayload(IPeerMessageArgs args, byte[] requiredHeader, out IpcMessage subMessage)\n    {\n        var ipcMessage = args.Message;\n\n        return ipcMessage.TryGetPayload(requiredHeader, out subMessage);\n    }\n\n    /// <summary>\n    /// 尝试根据 <paramref name=\"requiredHeader\"/> 获取有效负载内容。如果当前的 <paramref cref=\"args\"/> 不包含 <paramref name=\"requiredHeader\"/> 头信息，将返回 false 值。如包含，则将 <paramref cref=\"args\"/> 去掉 <paramref name=\"requiredHeader\"/> 长度之后作为 <paramref name=\"subMessage\"/> 返回，同时返回 true 值\n    /// </summary>\n    /// <param name=\"args\"></param>\n    /// <param name=\"requiredHeader\"></param>\n    /// <param name=\"subMessage\"></param>\n    /// <returns></returns>\n    public static bool TryGetPayload(this IPeerMessageArgs args, ulong requiredHeader, out IpcMessage subMessage)\n    {\n        var ipcMessage = args.Message;\n\n        return ipcMessage.TryGetPayload(requiredHeader, out subMessage);\n    }\n\n    /// <summary>\n    /// 尝试根据 <paramref name=\"requiredHeader\"/> 获取有效负载内容。如果当前的 <paramref cref=\"ipcMessage\"/> 不包含 <paramref name=\"requiredHeader\"/> 头信息，将返回 false 值。如包含，则将 <paramref cref=\"ipcMessage\"/> 去掉 <paramref name=\"requiredHeader\"/> 长度之后作为 <paramref name=\"subMessage\"/> 返回，同时返回 true 值\n    /// </summary>\n    /// <param name=\"ipcMessage\"></param>\n    /// <param name=\"requiredHeader\"></param>\n    /// <param name=\"subMessage\"></param>\n    /// <returns></returns>\n    public static bool TryGetPayload(in this IpcMessage ipcMessage, byte[] requiredHeader, out IpcMessage subMessage)\n    {\n        var length = requiredHeader.Length;\n        if (ipcMessage.Body.Length >= length)\n        {\n            // 反正 requiredHeader 没多长，遍历即可\n            for (int i = 0; i < length; i++)\n            {\n                var start = ipcMessage.Body.Start + i;\n                if (ipcMessage.Body.Buffer[start] != requiredHeader[i])\n                {\n                    subMessage = default;\n                    return false;\n                }\n            }\n\n            subMessage = ipcMessage.Skip(length);\n            return true;\n        }\n\n        subMessage = default;\n        return false;\n    }\n\n    /// <summary>\n    /// 尝试根据 <paramref name=\"requiredHeader\"/> 获取有效负载内容。如果当前的 <see cref=\"ipcMessage\"/> 不包含 <paramref name=\"requiredHeader\"/> 头信息，将返回 false 值。如包含，则将 <see cref=\"ipcMessage\"/> 去掉 <paramref name=\"requiredHeader\"/> 长度之后作为 <paramref name=\"subMessage\"/> 返回，同时返回 true 值\n    /// </summary>\n    /// <param name=\"ipcMessage\"></param>\n    /// <param name=\"requiredHeader\"></param>\n    /// <param name=\"subMessage\"></param>\n    /// <returns></returns>\n    public static bool TryGetPayload(in this IpcMessage ipcMessage, ulong requiredHeader, out IpcMessage subMessage)\n    {\n        const int length = sizeof(ulong);\n        if (ipcMessage.Body.Length >= length)\n        {\n            var header = BitConverter.ToUInt64(ipcMessage.Body.Buffer, ipcMessage.Body.Start);\n            if (header == requiredHeader)\n            {\n                subMessage = ipcMessage.Skip(length);\n                return true;\n            }\n        }\n\n        subMessage = default;\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Extensions/StreamExtensions.cs",
    "content": "﻿using System.IO;\nusing System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.Utils.Extensions\n{\n#if NETFRAMEWORK\n    static class StreamExtensions\n    {\n        /// <summary>\n        /// 将字节序列异步写入\n        /// </summary>\n        /// <remarks>\n        /// 在 .NET Framework 4.5 不支持 WriteAsync 写入 byte[] 而是需要传入开始和长度\n        /// </remarks>\n        /// <param name=\"stream\"></param>\n        /// <param name=\"data\"></param>\n        /// <returns></returns>\n        public static Task WriteAsync(this Stream stream, byte[] data)\n        {\n            return stream.WriteAsync(data, 0, data.Length);\n        }\n    }\n#endif\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/IO/AsyncBinaryReader.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Threading.Tasks;\nusing dotnetCampus.Ipc.Utils.Buffers;\n\nnamespace dotnetCampus.Ipc.Utils.IO\n{\n    class AsyncBinaryReader\n    {\n        public AsyncBinaryReader(Stream stream, ISharedArrayPool sharedArrayPool)\n        {\n            _sharedArrayPool = sharedArrayPool;\n            Stream = stream;\n        }\n\n        private Stream Stream { get; }\n        private readonly ISharedArrayPool _sharedArrayPool;\n\n        public Task<StreamReadResult<ushort>> ReadUInt16Async()\n        {\n            return ReadAsync(2, byteList => BitConverter.ToUInt16(byteList, 0));\n        }\n\n        public Task<StreamReadResult<ulong>> ReadReadUInt64Async()\n        {\n            return ReadAsync(sizeof(ulong), byteList => BitConverter.ToUInt64(byteList, 0));\n        }\n\n        public Task<StreamReadResult<uint>> ReadUInt32Async()\n        {\n            return ReadAsync(sizeof(uint), byteList => BitConverter.ToUInt32(byteList, 0));\n        }\n\n        private async Task<StreamReadResult<T>> ReadAsync<T>(int byteCount, Func<byte[], T> converter)\n        {\n            var readResult = await InternalReadAsync(byteCount);\n            if (readResult.IsEndOfStream)\n            {\n                return StreamReadResult<T>.EndOfStream;\n            }\n\n            var byteList = readResult.Result;\n\n            var result = converter(byteList);\n            _sharedArrayPool.Return(byteList);\n            return new StreamReadResult<T>(result);\n        }\n\n        private async Task<StreamReadResult<byte[]>> InternalReadAsync(int numBytes)\n        {\n            var byteList = _sharedArrayPool.Rent(numBytes);\n            var bytesRead = 0;\n\n            do\n            {\n                var n = await Stream.ReadAsync(byteList, bytesRead, numBytes - bytesRead).ConfigureAwait(false);\n                if (n == 0)\n                {\n                    return StreamReadResult<byte[]>.EndOfStream;\n                }\n\n                bytesRead += n;\n            } while (bytesRead < numBytes);\n\n            return new StreamReadResult<byte[]>(byteList);\n        }\n    }\n\n    readonly struct StreamReadResult<T>\n    {\n        public StreamReadResult(T result, bool isEndOfStream = false)\n        {\n            Result = result;\n\n            IsEndOfStream = isEndOfStream;\n        }\n\n        public static StreamReadResult<T> EndOfStream =>\n            new StreamReadResult<T>(result: default!, isEndOfStream: true);\n\n        public bool IsEndOfStream { get; }\n\n        public T Result { get; }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/IO/AsyncBinaryWriter.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nnamespace dotnetCampus.Ipc.Utils.IO\n{\n    class AsyncBinaryWriter\n    {\n        public AsyncBinaryWriter(Stream stream)\n        {\n            Stream = stream;\n        }\n\n        private Stream Stream { get; }\n\n        public async Task WriteAsync(ushort value)\n        {\n            await Stream.WriteAsync(BitConverter.GetBytes(value)).ConfigureAwait(false);\n        }\n\n        public async Task WriteAsync(uint value)\n        {\n            await Stream.WriteAsync(BitConverter.GetBytes(value)).ConfigureAwait(false);\n        }\n\n        public async Task WriteAsync(ulong value)\n        {\n            await Stream.WriteAsync(BitConverter.GetBytes(value)).ConfigureAwait(false);\n        }\n\n        public async Task WriteAsync(int value)\n        {\n            await Stream.WriteAsync(BitConverter.GetBytes(value)).ConfigureAwait(false);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/IO/ByteListMessageStream.cs",
    "content": "﻿using System.IO;\n\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Buffers;\n\nnamespace dotnetCampus.Ipc.Utils.IO\n{\n    /// <summary>\n    /// 数组列表的消息，特别定义类型，方便内存分析\n    /// </summary>\n    internal sealed class ByteListMessageStream : MemoryStream\n    {\n        private ByteListMessageStream(byte[] buffer, int count) : base(buffer, 0,\n            count, writable: false,\n            // 设置 publiclyVisible 属性，防止 GetBuffer 抛出 UnauthorizedAccessException 异常\n            publiclyVisible: true)\n        {\n        }\n\n        public ByteListMessageStream(in IpcMessageContext ipcMessageContext) : this(ipcMessageContext.MessageBuffer,\n            (int) ipcMessageContext.MessageLength)\n        {\n            IpcMessageContext = ipcMessageContext;\n        }\n\n        public IpcMessageContext IpcMessageContext { get; }\n\n        protected override void Dispose(bool disposing)\n        {\n            base.Dispose(disposing);\n            IpcMessageContext.SharedArrayPool.Return(IpcMessageContext.MessageBuffer);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/IO/PipeHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nusing Microsoft.Win32.SafeHandles;\n\nnamespace dotnetCampus.Ipc.Utils.IO;\n\ninternal static class PipeHelper\n{\n    /// <summary>\n    /// 判断传入的管道是否存在。只有明确能判断不存在时，才返回 false 值，其他情况返回 true 值\n    /// </summary>\n    /// <param name=\"pipeName\"></param>\n    /// <returns>只有返回 false 是可信的。存在或不支持都返回 true 值</returns>\n    public static bool IsPipeExists(string pipeName)\n    {\n        bool isWindows;\n#if NETFRAMEWORK\n        // 对于 .NET Framework 来说，那就一定是 Windows 系统了\n        isWindows = true;\n#elif NETCOREAPP3_1_OR_GREATER\n        // 对于 .NET Core 3.1 和 .NET 5 来说，Windows 和 Linux 都可以\n        isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n#endif\n        if (isWindows)\n        {\n            var isPipeExists = IsPipeExistsForWindows(pipeName);\n            if (!isPipeExists)\n            {\n                // 如果连接的时候不存在，则返回失败\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n#if NET6_0_OR_GREATER\n    [System.Runtime.Versioning.SupportedOSPlatform(\"Windows\")]\n#endif\n    private static bool IsPipeExistsForWindows(string pipeName)\n    {\n        try\n        {\n            // 不要用 File.Exists 判断，内部会调用 GetFileAttributes 导致管道无法被连接\n\n            unsafe\n            {\n                // 这里是一个结构体，但是不关心内容，直接栈上分配点空间给它\n                var findFileData = stackalloc byte[604];\n\n                var file = FindFirstFile(@\"\\\\.\\pipe\\\" + pipeName, (IntPtr) findFileData);\n\n                const nint INVALID_HANDLE_VALUE = -1;\n\n                if (file.DangerousGetHandle() != INVALID_HANDLE_VALUE)\n                {\n                    FindClose(file.DangerousGetHandle());\n                    return true;\n                }\n            }\n        }\n        catch\n        {\n\n        }\n        return false;\n    }\n\n\n    [DllImport(\"kernel32.dll\", CharSet = CharSet.Unicode, EntryPoint = \"FindFirstFileW\", ExactSpelling = true)]\n    private static extern SafeFileHandle FindFirstFile([In] string lpFileName, [In] IntPtr lpFindFileData);\n\n    [DllImport(\"kernel32.dll\", CharSet = CharSet.Unicode, EntryPoint = \"FindClose\", ExactSpelling = true)]\n    [return: MarshalAs(UnmanagedType.Bool)]\n    private static extern bool FindClose([In] IntPtr hFindFile);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Logging/EventId.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Utils.Logging\n{\n    internal readonly struct EventId\n    {\n        /// <summary>\n        /// Implicitly creates an EventId from the given <see cref=\"int\"/>.\n        /// </summary>\n        /// <param name=\"i\">The <see cref=\"int\"/> to convert to an EventId.</param>\n        public static implicit operator EventId(int i)\n        {\n            return new EventId(i);\n        }\n\n        /// <summary>\n        /// Checks if two specified <see cref=\"EventId\"/> instances have the same value. They are equal if they have the same Id.\n        /// </summary>\n        /// <param name=\"left\">The first <see cref=\"EventId\"/>.</param>\n        /// <param name=\"right\">The second <see cref=\"EventId\"/>.</param>\n        /// <returns><see langword=\"true\" /> if the objects are equal.</returns>\n        public static bool operator ==(EventId left, EventId right)\n        {\n            return left.Equals(right);\n        }\n\n        /// <summary>\n        /// Checks if two specified <see cref=\"EventId\"/> instances have different values.\n        /// </summary>\n        /// <param name=\"left\">The first <see cref=\"EventId\"/>.</param>\n        /// <param name=\"right\">The second <see cref=\"EventId\"/>.</param>\n        /// <returns><see langword=\"true\" /> if the objects are not equal.</returns>\n        public static bool operator !=(EventId left, EventId right)\n        {\n            return !left.Equals(right);\n        }\n\n        /// <summary>\n        /// Initializes an instance of the <see cref=\"EventId\"/> struct.\n        /// </summary>\n        /// <param name=\"id\">The numeric identifier for this event.</param>\n        /// <param name=\"name\">The name of this event.</param>\n        public EventId(int id, string? name = null)\n        {\n            Id = id;\n            Name = name;\n        }\n\n        /// <summary>\n        /// Gets the numeric identifier for this event.\n        /// </summary>\n        public int Id { get; }\n\n        /// <summary>\n        /// Gets the name of this event.\n        /// </summary>\n        public string? Name { get; }\n\n        /// <inheritdoc />\n        public override string ToString()\n        {\n            return Name ?? Id.ToString();\n        }\n\n        /// <summary>\n        /// Indicates whether the current object is equal to another object of the same type. Two events are equal if they have the same id.\n        /// </summary>\n        /// <param name=\"other\">An object to compare with this object.</param>\n        /// <returns><see langword=\"true\" /> if the current object is equal to the other parameter; otherwise, <see langword=\"false\" />.</returns>\n        public bool Equals(EventId other)\n        {\n            return Id == other.Id;\n        }\n\n        /// <inheritdoc />\n        public override bool Equals(object? obj)\n        {\n            if (obj is null)\n            {\n                return false;\n            }\n\n            return obj is EventId eventId && Equals(eventId);\n        }\n\n        /// <inheritdoc />\n        public override int GetHashCode()\n        {\n            return Id;\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Logging/ILogger.cs",
    "content": "﻿using System;\n\nnamespace dotnetCampus.Ipc.Utils.Logging\n{\n    internal interface ILogger\n    {\n        /// <summary>\n        /// Writes a log entry.\n        /// </summary>\n        /// <param name=\"logLevel\">Entry will be written on this level.</param>\n        /// <param name=\"eventId\">Id of the event.</param>\n        /// <param name=\"state\">The entry to be written. Can be also an object.</param>\n        /// <param name=\"exception\">The exception related to this entry.</param>\n        /// <param name=\"formatter\">Function to create a <see cref=\"string\"/> message of the <paramref name=\"state\"/> and <paramref name=\"exception\"/>.</param>\n        /// <typeparam name=\"TState\">The type of the object to be written.</typeparam>\n        void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);\n\n        /// <summary>\n        /// Checks if the given <paramref name=\"logLevel\"/> is enabled.\n        /// </summary>\n        /// <param name=\"logLevel\">Level to be checked.</param>\n        /// <returns><c>true</c> if enabled.</returns>\n        bool IsEnabled(LogLevel logLevel);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Logging/IpcLogger.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\n\nnamespace dotnetCampus.Ipc.Utils.Logging\n{\n    /// <summary>\n    /// 为 IPC 提供日志输出。\n    /// </summary>\n    public class IpcLogger : ILogger\n    {\n        /// <summary>\n        /// 创建为 IPC 提供的日志\n        /// </summary>\n        /// <param name=\"name\">当前的 Ipc 名。等同于管道名</param>\n        public IpcLogger(string name)\n        {\n            Name = name;\n        }\n\n        private string Name { get; }\n\n        void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n        {\n            Log(logLevel, state, exception, formatter);\n        }\n\n        bool ILogger.IsEnabled(LogLevel logLevel)\n        {\n            return IsEnabled(logLevel);\n        }\n\n        /// <summary>\n        /// 设置或获取最低的日志等级，只有大于此等级的日志才会被记录到 IpcLogger 里\n        /// </summary>\n        public LogLevel MinLogLevel { get; set; } = LogLevel.Information;\n\n        /// <summary>\n        /// 判断当前的日志等级是否可记\n        /// </summary>\n        /// <param name=\"logLevel\"></param>\n        /// <returns></returns>\n        protected virtual bool IsEnabled(LogLevel logLevel)\n        {\n            return logLevel >= MinLogLevel;\n        }\n\n        /// <summary>\n        /// 记录日志内容\n        /// </summary>\n        /// <typeparam name=\"TState\"></typeparam>\n        /// <param name=\"logLevel\"></param>\n        /// <param name=\"state\"></param>\n        /// <param name=\"exception\"></param>\n        /// <param name=\"formatter\"></param>\n        protected virtual void Log<TState>(LogLevel logLevel, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n        {\n            if (!IsEnabled(logLevel))\n            {\n                return;\n            }\n\n            Debug.WriteLine($\"[IPC][{logLevel}]{formatter(state, exception)}\");\n        }\n\n        /// <summary>\n        /// 返回此日志的名字。\n        /// </summary>\n        /// <returns></returns>\n        public override string ToString()\n        {\n            return $\"[{Name}]\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/Logging/LogLevel.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Utils.Logging\n{\n    public enum LogLevel\n    {\n        /// <summary>\n        /// Logs that contain the most detailed messages. These messages may contain sensitive application data.\n        /// These messages are disabled by default and should never be enabled in a production environment.\n        /// </summary>\n        Trace = 0,\n\n        /// <summary>\n        /// Logs that are used for interactive investigation during development.  These logs should primarily contain\n        /// information useful for debugging and have no long-term value.\n        /// </summary>\n        Debug = 1,\n\n        /// <summary>\n        /// Logs that track the general flow of the application. These logs should have long-term value.\n        /// </summary>\n        Information = 2,\n\n        /// <summary>\n        /// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the\n        /// application execution to stop.\n        /// </summary>\n        Warning = 3,\n\n        /// <summary>\n        /// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a\n        /// failure in the current activity, not an application-wide failure.\n        /// </summary>\n        Error = 4,\n\n        /// <summary>\n        /// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires\n        /// immediate attention.\n        /// </summary>\n        Critical = 5,\n\n        /// <summary>\n        /// Not used for writing log messages. Specifies that a logging category should not write any messages.\n        /// </summary>\n        None = 6,\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/NullableBooleans.cs",
    "content": "﻿using System.Diagnostics.Contracts;\n\nnamespace dotnetCampus.Ipc.Utils;\n\n/// <summary>\n/// 可存储 8 个可空布尔值和 16 个布尔值的结构。使用 2 位来存储一个可空布尔值。共 32 位。\n/// </summary>\ninternal struct NullableBooleans\n{\n    /// <summary>\n    /// 高 16 位存储 16 个布尔值；低 16 位来存储 8 个可空布尔值。\n    /// </summary>\n    private uint _booleans;\n\n    /// <summary>\n    /// 获取或设置指定索引处的可空布尔值，值范围 [0, 7]。\n    /// </summary>\n    /// <param name=\"index\">索引，值范围 [0, 7]。</param>\n    public bool? this[int index]\n    {\n        get => ((_booleans & (1 << (index * 2 + 1))) >> (index * 2 + 1), (_booleans & (1 << (index * 2))) >> (index * 2)) switch\n        {\n            // 高位 1 表示非 null，0 表示 null；低位 1 表示 true，0 表示 false\n            (1, 0) => false,\n            (1, 1) => true,\n            _ => null,\n        };\n        set => _booleans = value switch\n        {\n            // 设置为 null：将索引处的两位都清零\n            null => _booleans & (uint)~(3 << (index * 2)),\n            // 设置为 true：将高位置 1，低位置 1\n            true => _booleans | (uint)(3 << (index * 2)),\n            // 设置为 false：将高位置 1，低位置清零\n            false => (_booleans & (uint)~(1 << (index * 2 + 1))) | (uint)(1 << (index * 2)),\n        };\n    }\n\n    [Pure]\n    public bool GetBooleanAt(int indexFromEnd)\n    {\n        var index = 31 - indexFromEnd;\n        return (_booleans & (1 << index)) != 0;\n    }\n\n    public void SetBooleanAt(int indexFromEnd, bool value)\n    {\n        var index = 31 - indexFromEnd;\n        _booleans = value\n            ? _booleans | (uint)(1 << index)\n            : _booleans & (uint)~(1 << index);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/Utils/TaskUtils.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace dotnetCampus.Ipc.Utils\n{\n    internal static class TaskUtils\n    {\n        public static async Task<TTarget> As<TSource, TTarget>(this Task<TSource> sourceTask) where TSource : TTarget\n        {\n            return await sourceTask.ConfigureAwait(false);\n        }\n\n#if NET45\n        private static readonly Task TheCompletedTask = Task.FromResult(0);\n#endif\n\n        public static Task CompletedTask\n        {\n            get\n            {\n#if NET45\n                return TheCompletedTask;\n#else\n                return Task.CompletedTask;\n#endif\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/dotnetCampus.Ipc.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <!--\n      We support:\n      1. The latest .NET version (net9)\n      2. Long-term support .NET version (net8, net6, netcoreapp3.1)\n      3. Legacy .NET Framework (net45)\n    -->\n    <TargetFrameworks>net9.0;net8.0;net6.0;netcoreapp3.1;net45</TargetFrameworks>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <Nullable>enable</Nullable>\n    <GeneratePackageOnBuild>True</GeneratePackageOnBuild>\n    <DefineConstants>$(DefineConstants)TRACE;PublicAsInternal</DefineConstants>\n    <IsTrimmable Condition=\"'$(TargetFramework)'=='net6.0'\">true</IsTrimmable>\n    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <CanUseNewtonsoftJson>true</CanUseNewtonsoftJson>\n    <CanUseNewtonsoftJson Condition=\"$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))\">false</CanUseNewtonsoftJson>\n    <DefineConstants Condition=\"'$(CanUseNewtonsoftJson)'=='true'\">$(DefineConstants);UseNewtonsoftJson</DefineConstants>\n    <IsAotCompatible Condition=\"$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))\">true</IsAotCompatible>\n    <IsIpcLegacyFramework Condition=\"'$(TargetFramework)'=='net45'\">true</IsIpcLegacyFramework>\n  </PropertyGroup>\n\n  <!-- 在 GitHub 的 Action 构建会添加 GITHUB_ACTIONS 变量 -->\n  <!-- 下面进行自动构建，自动添加源代码链接等 -->\n  <!-- 详细请看 https://github.com/dotnet/sourcelink -->\n  <PropertyGroup Condition=\"'$(GITHUB_ACTIONS)' == 'true'\">\n    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>\n\n    <!-- Optional: Publish the repository URL in the built .nupkg (in the NuSpec <Repository> element) -->\n    <PublishRepositoryUrl>true</PublishRepositoryUrl>\n\n    <!-- 只有在 GitHub 的 Action 构建才能使用源代码链接 -->\n    <!-- 源代码链接需要使用 commit 号，而在 GitHub 的 Action 构建的 commit 才是对的 -->\n    <!-- 本地构建，也许没有记得 commit 就构建，此时的 nuget 包的源代码是不对的，上传上去会让调试诡异 -->\n    <!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->\n    <EmbedUntrackedSources>true</EmbedUntrackedSources>\n\n    <!-- 本地等不需要创建符号文件 -->\n    <!-- Optional: Build symbol package (.snupkg) to distribute the PDB containing Source Link -->\n    <IncludeSymbols>true</IncludeSymbols>\n    <SymbolPackageFormat>snupkg</SymbolPackageFormat>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Compile Include=\"..\\..\\tests\\dotnetCampus.Ipc.Tests\\Attributes.cs\" Link=\"Properties\\Attributes.cs\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"'$(GITHUB_ACTIONS)' == 'true'\">\n    <!-- 链接源代码到 GitHub 仓库，方便调试 -->\n    <PackageReference Include=\"Microsoft.SourceLink.GitHub\" PrivateAssets=\"All\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"dotnetCampus.LatestCSharpFeatures\" PrivateAssets=\"all\" />\n    <PackageReference Include=\"dotnetCampus.AsyncWorkerCollection.Source\" PrivateAssets=\"all\" />\n    <PackageReference Include=\"Newtonsoft.Json\" Condition=\"'$(CanUseNewtonsoftJson)'=='true'\" />\n    <PackageReference Include=\"System.ValueTuple\" Condition=\"'$(IsIpcLegacyFramework)'=='true'\" />\n    <PackageReference Include=\"Microsoft.CSharp\" Condition=\"'$(IsIpcLegacyFramework)'=='true'\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'net6.0'\">\n    <!-- .NET 6/7 版本的 System.Text.Json 功能很弱，也有一些不可接受的 bug，所以强制使用新版 -->\n    <!-- 比如，标记了 JsonIgnore 的属性也会生成反序列化代码，导致对 Newtonsoft.Json.JToken 被迫生成了必然 StackOverflow 的代码 -->\n    <PackageReference Include=\"System.Text.Json\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"'$(TargetFramework)' == 'netcoreapp3.1'\">\n    <PackageReference Include=\"System.IO.Pipes.AccessControl\" />\n  </ItemGroup>\n\n  <!-- 生成 NuGet 包。 -->\n  <Target Name=\"_IncludeAllDependencies\" BeforeTargets=\"_GetPackageFiles\">\n    <ItemGroup>\n      <None Include=\"$(RepositoryRoot)README.md\" Pack=\"true\" PackagePath=\"\\\" />\n      <None Include=\"Package\\build\\Package.props\" Pack=\"True\" PackagePath=\"build\\$(PackageId).props\" />\n      <None Include=\"$(ArtifactsPath)\\bin\\dotnetCampus.Ipc.Analyzers\\$(Configuration)\\**\\*.dll\" Pack=\"True\" PackagePath=\"analyzers\\dotnet\\cs\" />\n    </ItemGroup>\n  </Target>\n\n</Project>\n"
  },
  {
    "path": "src/dotnetCampus.Ipc/dotnetCampus.Ipc.csproj.DotSettings",
    "content": "﻿<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namespace:System;assembly=mscorlib\" xmlns:ss=\"urn:shemas-jetbrains-com:settings-storage-xaml\" xmlns:wpf=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=compilerservices_005Cgeneratedproxies_005Ccontexts/@EntryIndexedValue\">False</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=compilerservices_005Cgeneratedproxies_005Cgarms/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=compilerservices_005Cgeneratedproxies_005Cmodels/@EntryIndexedValue\">False</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ipcrouteds_005Cdirectrouteds_005Cbase1_005F/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ipcrouteds_005Cdirectrouteds_005Cbase_005F/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ipcrouteds_005Cdirectrouteds_005Cjson_005F/@EntryIndexedValue\">True</s:Boolean>\n\t<s:Boolean x:Key=\"/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=ipcrouteds_005Cdirectrouteds_005Crawbyte_005F/@EntryIndexedValue\">True</s:Boolean></wpf:ResourceDictionary>"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/AddIpcProxyConfigsAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\npublic class AddIpcProxyConfigsAnalyzer : DiagnosticAnalyzer\n{\n    public AddIpcProxyConfigsAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(\n            IPC301_CreateIpcProxy_AddIpcProxyConfigs,\n            IPC303_CreateIpcProxy_AddIpcProxyConfigs);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeIpcPublicAttributes, SyntaxKind.SimpleMemberAccessExpression);\n    }\n\n    private void AnalyzeIpcPublicAttributes(SyntaxNodeAnalysisContext context)\n    {\n        if (context.Node is MemberAccessExpressionSyntax memberAccessNode\n            && IpcProxyInvokingInfo.TryCreateIpcProxyInvokingInfo(\n                context.SemanticModel,\n                memberAccessNode,\n                context.CancellationToken) is { } invokingInfo)\n        {\n            if (invokingInfo.ShapeType is null)\n            {\n                context.ReportDiagnostic(\n                    Diagnostic.Create(IPC301_CreateIpcProxy_AddIpcProxyConfigs,\n                    memberAccessNode.GetLocation()));\n                context.ReportDiagnostic(\n                    Diagnostic.Create(IPC303_CreateIpcProxy_AddIpcProxyConfigs,\n                    memberAccessNode.GetLocation()));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/AddIpcShapeAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\npublic class AddIpcShapeAnalyzer : DiagnosticAnalyzer\n{\n    public AddIpcShapeAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(IPC302_CreateIpcProxy_AddIpcShape);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeIpcPublicAttributes, SyntaxKind.SimpleMemberAccessExpression);\n    }\n\n    private void AnalyzeIpcPublicAttributes(SyntaxNodeAnalysisContext context)\n    {\n        if (context.Node is MemberAccessExpressionSyntax memberAccessNode\n            && IpcProxyInvokingInfo.TryCreateIpcProxyInvokingInfo(\n                context.SemanticModel,\n                memberAccessNode,\n                context.CancellationToken) is { } invokingInfo)\n        {\n            if (invokingInfo.ShapeType is null)\n            {\n                context.ReportDiagnostic(\n                    Diagnostic.Create(IPC302_CreateIpcProxy_AddIpcShape,\n                    memberAccessNode.GetLocation()));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/Compiling/IpcAttributeHelper.cs",
    "content": "﻿using dotnetCampus.Ipc.CodeAnalysis.Models;\nusing dotnetCampus.Ipc.Generators.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers.Compiling;\n\ninternal static class IpcAttributeHelper\n{\n    public static IEnumerable<(AttributeSyntax attributeNode, IpcPublicAttributeNamedValues namedValues)> TryFindIpcPublicAttributes(\n        SemanticModel semanticModel, InterfaceDeclarationSyntax typeDeclarationNode)\n    {\n        var typeSymbol = semanticModel.GetDeclaredSymbol(typeDeclarationNode);\n        if (typeSymbol is null)\n        {\n            yield break;\n        }\n\n        if (typeDeclarationNode.AttributeLists.SelectMany(x => x.Attributes).FirstOrDefault(x =>\n        {\n            string? attributeName = x.Name switch\n            {\n                IdentifierNameSyntax identifierName => identifierName.ToString(),\n                QualifiedNameSyntax qualifiedName => qualifiedName.ChildNodes().OfType<IdentifierNameSyntax>().LastOrDefault()?.ToString(),\n                _ => null,\n            };\n            return attributeName is not null &&\n                (attributeName.Equals(nameof(IpcPublicAttribute), StringComparison.Ordinal)\n                || attributeName.Equals(GetAttributeName(nameof(IpcPublicAttribute)), StringComparison.Ordinal));\n        }) is { } attributeNode)\n        {\n            var namedValues = typeSymbol.GetIpcPublicNamedValues();\n            yield return (attributeNode, namedValues);\n        }\n    }\n\n    public static IEnumerable<(AttributeSyntax attributeNode, IpcShapeAttributeNamedValues namedValues)> TryFindIpcShapeAttributes(\n        SemanticModel semanticModel, ClassDeclarationSyntax typeDeclarationNode)\n    {\n        var typeSymbol = semanticModel.GetDeclaredSymbol(typeDeclarationNode);\n        if (typeSymbol is null)\n        {\n            yield break;\n        }\n\n        if (typeDeclarationNode.AttributeLists.SelectMany(x => x.Attributes).FirstOrDefault(x =>\n        {\n            string? attributeName = x.Name switch\n            {\n                IdentifierNameSyntax identifierName => identifierName.ToString(),\n                QualifiedNameSyntax qualifiedName => qualifiedName.ChildNodes().OfType<IdentifierNameSyntax>().LastOrDefault()?.ToString(),\n                _ => null,\n            };\n            return attributeName is not null &&\n                (attributeName.Equals(nameof(IpcShapeAttribute), StringComparison.Ordinal)\n                || attributeName.Equals(GetAttributeName(nameof(IpcShapeAttribute)), StringComparison.Ordinal));\n        }) is { } attributeNode)\n        {\n            var namedValues = typeSymbol.GetIpcShapeNamedValues();\n            yield return (attributeNode, namedValues);\n        }\n    }\n\n    public static IEnumerable<(AttributeSyntax? attributeNode, IpcPublicAttributeNamedValues namedValues)> TryFindMemberAttributes(\n        SemanticModel semanticModel, TypeDeclarationSyntax typeDeclarationNode)\n    {\n        if (TryFindIpcPublicType(semanticModel, typeDeclarationNode, out var compilation))\n        {\n            foreach (var (contractType, member) in compilation.EnumerateMembers())\n            {\n                var namedValues = member switch\n                {\n                    IPropertySymbol propertySymbol => propertySymbol.GetIpcNamedValues(contractType),\n                    IMethodSymbol methodSymbol => methodSymbol.GetIpcNamedValues(null, contractType),\n                    _ => null,\n                };\n                if (namedValues is null)\n                {\n                    continue;\n                }\n\n                var memberNode = typeDeclarationNode.DescendantNodes()\n                    .Where(x => x is PropertyDeclarationSyntax or MethodDeclarationSyntax)\n                    .FirstOrDefault(x => x switch\n                    {\n                        PropertyDeclarationSyntax propertyDeclarationSyntax => SymbolEqualityComparer.Default.Equals(member, semanticModel.GetDeclaredSymbol(propertyDeclarationSyntax)),\n                        MethodDeclarationSyntax methodDeclarationSyntax => SymbolEqualityComparer.Default.Equals(member, semanticModel.GetDeclaredSymbol(methodDeclarationSyntax)),\n                        _ => false,\n                    }) as MemberDeclarationSyntax;\n                if (memberNode is not null && memberNode.AttributeLists.SelectMany(x => x.Attributes).FirstOrDefault(x =>\n                {\n                    string? attributeName = x.Name switch\n                    {\n                        IdentifierNameSyntax identifierName => identifierName.ToString(),\n                        QualifiedNameSyntax qualifiedName => qualifiedName.ChildNodes().OfType<IdentifierNameSyntax>().LastOrDefault()?.ToString(),\n                        _ => null,\n                    };\n                    return attributeName is not null &&\n                        (attributeName.Equals(nameof(IpcPropertyAttribute), StringComparison.Ordinal)\n                        || attributeName.Equals(GetAttributeName(nameof(IpcPropertyAttribute)), StringComparison.Ordinal)\n                        || attributeName.Equals(nameof(IpcMethodAttribute), StringComparison.Ordinal)\n                        || attributeName.Equals(GetAttributeName(nameof(IpcMethodAttribute)), StringComparison.Ordinal));\n                }) is { } attributeNode)\n                {\n                    yield return (attributeNode, namedValues);\n                }\n                else\n                {\n                    yield return (null, namedValues);\n                }\n            }\n        }\n    }\n\n    private static bool TryFindIpcPublicType(SemanticModel semanticModel, TypeDeclarationSyntax typeDeclarationNode,\n        [NotNullWhen(true)] out IpcPublicCompilation? publicIpcObjectCompilation)\n    {\n        if (semanticModel.GetDeclaredSymbol(typeDeclarationNode) is { } typeDeclarationSymbol\n            && typeDeclarationSymbol.GetAttributes().FirstOrDefault(x => string.Equals(\n                x.AttributeClass?.ToString(),\n                typeof(IpcPublicAttribute).FullName,\n                StringComparison.Ordinal)) is { } ipcPublicAttribute)\n        {\n            publicIpcObjectCompilation = new(typeDeclarationNode.SyntaxTree, semanticModel, typeDeclarationSymbol);\n            return true;\n        }\n        publicIpcObjectCompilation = null;\n        return false;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/Compiling/IpcProxyInvokingInfo.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Analyzers.Compiling;\n\n/// <summary>\n/// 用语法节点和语义模型描述一次方法调用。\n/// </summary>\n[DebuggerDisplay(\"{MemberAccessNode.GetLocation()?.SourceSpan.Start,nq}: {MemberAccessNode.ToFullString(),nq}\")]\ninternal struct IpcProxyInvokingInfo\n{\n    private IpcProxyInvokingInfo(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessNode,\n        IMethodSymbol methodSymbol, INamedTypeSymbol contractType)\n    {\n        SemanticModel = semanticModel;\n        MemberAccessNode = memberAccessNode ?? throw new ArgumentNullException(nameof(memberAccessNode));\n        MethodSymbol = methodSymbol ?? throw new ArgumentNullException(nameof(methodSymbol));\n        ContractType = contractType ?? throw new ArgumentNullException(nameof(contractType));\n        ShapeType = null;\n    }\n\n    private IpcProxyInvokingInfo(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessNode,\n        IMethodSymbol methodSymbol, INamedTypeSymbol contractType, INamedTypeSymbol shapeType)\n    {\n        SemanticModel = semanticModel;\n        MemberAccessNode = memberAccessNode ?? throw new ArgumentNullException(nameof(memberAccessNode));\n        MethodSymbol = methodSymbol ?? throw new ArgumentNullException(nameof(methodSymbol));\n        ContractType = contractType ?? throw new ArgumentNullException(nameof(contractType));\n        ShapeType = shapeType ?? throw new ArgumentNullException(nameof(shapeType));\n    }\n\n    /// <summary>\n    /// 能用来解读本语法节点的语义模型。\n    /// </summary>\n    public SemanticModel SemanticModel { get; }\n\n    /// <summary>\n    /// 本次调用这个方法时，调用的这一句语法节点。\n    /// </summary>\n    public MemberAccessExpressionSyntax MemberAccessNode { get; }\n\n    /// <summary>\n    /// 本次所调用的方法的语义符号。\n    /// </summary>\n    public IMethodSymbol MethodSymbol { get; }\n\n    /// <summary>\n    /// 本次调用的契约类型。\n    /// </summary>\n    public INamedTypeSymbol ContractType { get; }\n\n    /// <summary>\n    /// 如果本次调用使用到了 IPC 形状代理，那么此属性是 IPC 形状代理的类型。\n    /// </summary>\n    public INamedTypeSymbol? ShapeType { get; }\n\n    /// <inheritdoc />\n    public override int GetHashCode()\n    {\n        var hashCode = -25060616;\n        hashCode = hashCode * -1521134295 + EqualityComparer<SemanticModel>.Default.GetHashCode(SemanticModel);\n        hashCode = hashCode * -1521134295 + EqualityComparer<MemberAccessExpressionSyntax>.Default.GetHashCode(MemberAccessNode);\n        hashCode = hashCode * -1521134295 + EqualityComparer<IMethodSymbol>.Default.GetHashCode(MethodSymbol);\n        return hashCode;\n    }\n\n    public static IpcProxyInvokingInfo? TryCreateIpcProxyInvokingInfo(SemanticModel semanticModel, MemberAccessExpressionSyntax memberAccessNode, CancellationToken cancellationToken)\n    {\n        var methodName = memberAccessNode.Name.Identifier.ValueText;\n        if (methodName != \"CreateIpcProxy\")\n        {\n            // 初筛。\n            return null;\n        }\n\n        if (semanticModel.GetSymbolInfo(memberAccessNode, cancellationToken).Symbol is IMethodSymbol methodSymbol)\n        {\n            // 当前正在调用的方法是一个正常的方法。\n            if (memberAccessNode.Name is GenericNameSyntax genericNameNode)\n            {\n                if (genericNameNode.TypeArgumentList.Arguments.Count == 1)\n                {\n                    if (genericNameNode.TypeArgumentList.Arguments[0] is TypeSyntax typeArgumentNode\n                        && semanticModel.GetSymbolInfo(typeArgumentNode, cancellationToken).Symbol is INamedTypeSymbol typeArgumentSymbol)\n                    {\n                        return new(semanticModel, memberAccessNode, methodSymbol, typeArgumentSymbol);\n                    }\n                }\n                else if (genericNameNode.TypeArgumentList.Arguments.Count == 2)\n                {\n                    if (genericNameNode.TypeArgumentList.Arguments[0] is TypeSyntax contractTypeArgumentNode\n                        && semanticModel.GetSymbolInfo(contractTypeArgumentNode, cancellationToken).Symbol is INamedTypeSymbol contractTypeArgumentSymbol\n                        && genericNameNode.TypeArgumentList.Arguments[1] is TypeSyntax shapeTypeArgumentNode\n                        && semanticModel.GetSymbolInfo(shapeTypeArgumentNode, cancellationToken).Symbol is INamedTypeSymbol shapeTypeArgumentSymbol)\n                    {\n                        return new(semanticModel, memberAccessNode, methodSymbol, contractTypeArgumentSymbol, shapeTypeArgumentSymbol);\n                    }\n                }\n            }\n            else\n            {\n                // 无法解析。\n            }\n        }\n        else\n        {\n            // 当前正在调用的方法无法找到对应的符号（编译不通过）。\n        }\n        return null;\n    }\n\n    public static IEqualityComparer<IpcProxyInvokingInfo> ContractTypeEqualityComparer { get; } = new EqualityComparerByContractType();\n\n    private sealed class EqualityComparerByContractType : IEqualityComparer<IpcProxyInvokingInfo>\n    {\n        public bool Equals(IpcProxyInvokingInfo x, IpcProxyInvokingInfo y)\n        {\n            return false;\n        }\n\n        public int GetHashCode(IpcProxyInvokingInfo info)\n        {\n            return info.GetHashCode();\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/ContractTypeDismatchWithInterfaceAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\ninternal class ContractTypeDismatchWithInterfaceAnalyzer : DiagnosticAnalyzer\n{\n    public ContractTypeDismatchWithInterfaceAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(IPC161_IpcShape_ContractTypeDismatchWithInterface);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeTypeIpcAttributes, SyntaxKind.ClassDeclaration);\n    }\n\n    private void AnalyzeTypeIpcAttributes(SyntaxNodeAnalysisContext context)\n    {\n        foreach (var (attributeNode, namedValues) in IpcAttributeHelper.TryFindIpcShapeAttributes(context.SemanticModel, (ClassDeclarationSyntax) context.Node))\n        {\n            var contractType = namedValues.ContractType;\n            var realType = namedValues.IpcType;\n            if (contractType is null || realType is null)\n            {\n                // 无需报告诊断，因为缺少构造函数参数必然编译不通过。\n                return;\n            }\n            if (contractType.TypeKind != TypeKind.Interface)\n            {\n                // 由 ContractTypeMustBeAnInterfaceAnalyzer 报告。\n                return;\n            }\n\n            if (realType.AllInterfaces.Length is 0\n                || realType.AllInterfaces.All(x => !SymbolEqualityComparer.Default.Equals(x, contractType)))\n            {\n                // 在契约类型上报告。\n                var typeLocation = (attributeNode.ArgumentList?.Arguments.FirstOrDefault()?.Expression as TypeOfExpressionSyntax)?.Type.GetLocation();\n                context.ReportDiagnostic(\n                    Diagnostic.Create(IPC161_IpcShape_ContractTypeDismatchWithInterface,\n                    typeLocation,\n                    realType.Name, contractType.Name));\n\n                // 在形状代理类型上报告。\n                if (attributeNode.Parent is AttributeListSyntax attributeListNode\n                    && attributeListNode.Parent is ClassDeclarationSyntax classDeclarationNode)\n                {\n                    context.ReportDiagnostic(\n                        Diagnostic.Create(IPC161_IpcShape_ContractTypeDismatchWithInterface,\n                        classDeclarationNode.Identifier.GetLocation(),\n                        realType.Name, contractType.Name));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/ContractTypeMustBeAnInterfaceAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\ninternal class ContractTypeMustBeAnInterfaceAnalyzer : DiagnosticAnalyzer\n{\n    public ContractTypeMustBeAnInterfaceAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(IPC160_IpcShape_ContractTypeMustBeAnInterface);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeTypeIpcAttributes, SyntaxKind.ClassDeclaration);\n    }\n\n    private void AnalyzeTypeIpcAttributes(SyntaxNodeAnalysisContext context)\n    {\n        foreach (var (attribute, namedValues) in IpcAttributeHelper.TryFindIpcShapeAttributes(context.SemanticModel, (ClassDeclarationSyntax) context.Node))\n        {\n            var contractType = namedValues.ContractType;\n            if (contractType == null)\n            {\n                // 无需报告诊断，因为缺少构造函数参数必然编译不通过。\n                return;\n            }\n\n            var typeLocation = (attribute.ArgumentList?.Arguments.FirstOrDefault()?.Expression as TypeOfExpressionSyntax)?.Type.GetLocation();\n            if (contractType.TypeKind != TypeKind.Interface)\n            {\n                context.ReportDiagnostic(\n                    Diagnostic.Create(IPC160_IpcShape_ContractTypeMustBeAnInterface,\n                    typeLocation,\n                    contractType.Name));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/DefaultReturnDependsOnIgnoresIpcExceptionAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\npublic class DefaultReturnDependsOnIgnoresIpcExceptionAnalyzer : DiagnosticAnalyzer\n{\n    public DefaultReturnDependsOnIgnoresIpcExceptionAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(IPC242_IpcProperty_DefaultReturnDependsOnIgnoresIpcException);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeIpcTypeAttributes, SyntaxKind.InterfaceDeclaration);\n        context.RegisterSyntaxNodeAction(AnalyzeIpcTypeAttributes, SyntaxKind.ClassDeclaration);\n    }\n\n    private void AnalyzeIpcTypeAttributes(SyntaxNodeAnalysisContext context)\n    {\n        var typeDeclarationNode = (TypeDeclarationSyntax) context.Node;\n        var memberSymbol = context.SemanticModel.GetDeclaredSymbol(typeDeclarationNode);\n        if (memberSymbol is null)\n        {\n            return;\n        }\n\n        foreach (var (attributeNode, namedValues) in IpcAttributeHelper.TryFindMemberAttributes(context.SemanticModel, typeDeclarationNode))\n        {\n            // 设置了默认值却没有忽略异常。\n            if (!namedValues.IgnoresIpcException && namedValues.DefaultReturn is not null)\n            {\n                if (attributeNode?.ArgumentList?.Arguments.FirstOrDefault(x =>\n                    x.NameEquals?.Name.ToString() == nameof(IpcMethodAttribute.DefaultReturn)) is { } attributeArgumentNode)\n                {\n                    context.ReportDiagnostic(Diagnostic.Create(IPC242_IpcProperty_DefaultReturnDependsOnIgnoresIpcException, attributeArgumentNode.GetLocation()));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/EmptyIpcMemberAttributeIsUnnecessaryAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\npublic class EmptyIpcMemberAttributeIsUnnecessaryAnalyzer : DiagnosticAnalyzer\n{\n    public EmptyIpcMemberAttributeIsUnnecessaryAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(IPC201_IpcMember_EmptyIpcMemberAttributeIsUnnecessary);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeIpcPublicAttributes, SyntaxKind.InterfaceDeclaration);\n    }\n\n    private void AnalyzeIpcPublicAttributes(SyntaxNodeAnalysisContext context)\n    {\n        var interfaceDeclarationNode = (InterfaceDeclarationSyntax) context.Node;\n        var typeSymbol = context.SemanticModel.GetDeclaredSymbol(interfaceDeclarationNode);\n        if (typeSymbol is null)\n        {\n            return;\n        }\n\n        foreach (var (attributeNode, _) in IpcAttributeHelper.TryFindMemberAttributes(context.SemanticModel, interfaceDeclarationNode))\n        {\n            if (attributeNode?.Parent is AttributeListSyntax attributeList)\n            {\n                // 没有设置任何参数（即连括号都没打），或者设置了 0 个参数（即打了括号但括号里没内容）。\n                if (attributeNode.ArgumentList is null || attributeNode.ArgumentList.Arguments.Count is 0)\n                {\n                    context.ReportDiagnostic(\n                        Diagnostic.Create(IPC201_IpcMember_EmptyIpcMemberAttributeIsUnnecessary,\n                        attributeList.Attributes.Count is 1\n                            ? attributeList.GetLocation()\n                            : attributeNode.GetLocation(),\n                        attributeNode.Name));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Analyzers/IgnoresIpcExceptionIsRecommendedAnalyzer.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\n\nnamespace dotnetCampus.Ipc.Analyzers;\n\n[DiagnosticAnalyzer(LanguageNames.CSharp)]\npublic class IgnoresIpcExceptionIsRecommendedAnalyzer : DiagnosticAnalyzer\n{\n    public IgnoresIpcExceptionIsRecommendedAnalyzer()\n    {\n        SupportedDiagnostics = ImmutableArray.Create(IPC131_IpcMembers_IgnoresIpcExceptionIsRecommended);\n    }\n\n    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }\n\n    public override void Initialize(AnalysisContext context)\n    {\n        context.EnableConcurrentExecution();\n        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);\n\n        context.RegisterSyntaxNodeAction(AnalyzeIpcPublicAttributes, SyntaxKind.InterfaceDeclaration);\n        context.RegisterSyntaxNodeAction(AnalyzeIpcShapeAttributes, SyntaxKind.ClassDeclaration);\n    }\n\n    private void AnalyzeIpcPublicAttributes(SyntaxNodeAnalysisContext context)\n    {\n        if (context.Node is InterfaceDeclarationSyntax interfaceDeclarationNode)\n        {\n            foreach (var (attributeNode, namedValues) in IpcAttributeHelper.TryFindIpcPublicAttributes(context.SemanticModel, interfaceDeclarationNode))\n            {\n                if (namedValues.IgnoresIpcException is null)\n                {\n                    context.ReportDiagnostic(Diagnostic.Create(IPC131_IpcMembers_IgnoresIpcExceptionIsRecommended, attributeNode.GetLocation()));\n                }\n            }\n        }\n    }\n\n    private void AnalyzeIpcShapeAttributes(SyntaxNodeAnalysisContext context)\n    {\n        if (context.Node is ClassDeclarationSyntax classDeclarationNode)\n        {\n            foreach (var (attributeNode, namedValues) in IpcAttributeHelper.TryFindIpcShapeAttributes(context.SemanticModel, classDeclarationNode))\n            {\n                if (namedValues.IgnoresIpcException is null)\n                {\n                    context.ReportDiagnostic(Diagnostic.Create(IPC131_IpcMembers_IgnoresIpcExceptionIsRecommended, attributeNode.GetLocation()));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Core/DiagnosticException.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CodeAnalysis.Core;\n\n/// <summary>\n/// 当出现错误时，通过抛出此异常来报告编译错误。\n/// </summary>\ninternal class DiagnosticException : Exception\n{\n    private readonly object?[]? _messageArgs;\n\n    public DiagnosticException(DiagnosticDescriptor diagnostic)\n    {\n        Diagnostic = diagnostic;\n        Location = null;\n        _messageArgs = null;\n    }\n\n    public DiagnosticException(DiagnosticDescriptor diagnostic, Location? location, params object?[]? messageArgs)\n    {\n        Diagnostic = diagnostic;\n        Location = location;\n        _messageArgs = messageArgs;\n    }\n\n    public DiagnosticDescriptor Diagnostic { get; }\n\n    public Location? Location { get; }\n\n    public Diagnostic ToDiagnostic()\n    {\n        return Microsoft.CodeAnalysis.Diagnostic.Create(Diagnostic, Location, _messageArgs);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Core/Diagnostics.cs",
    "content": "﻿using dotnetCampus.Ipc.Properties;\n\nusing static dotnetCampus.Ipc.Properties.Localizations;\nusing static Microsoft.CodeAnalysis.WellKnownDiagnosticTags;\n\nnamespace dotnetCampus.Ipc.CodeAnalysis.Core;\n\n/// <summary>\n/// 包含 IPC 框架分析器中的所有诊断。\n/// </summary>\ninternal static class Diagnostics\n{\n    /// <summary>\n    /// 生成代码时出现未知错误。\n    /// </summary>\n    public static DiagnosticDescriptor IPC000_UnknownError { get; } = new(\n        nameof(IPC000),\n        Localize(nameof(IPC000)),\n        Localize(nameof(IPC000_Message)),\n        Categories.Compiler,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [AnalyzerException, NotConfigurable]);\n\n    /// <summary>\n    /// 生成代码时出现已知错误。本生成器不会报告此错误，因为后续编译器会准确报告之。\n    /// </summary>\n    public static DiagnosticDescriptor IPC001_KnownCompilerError { get; } = new(\n        nameof(IPC001),\n        Localize(nameof(IPC001)),\n        Localize(nameof(IPC001_Message)),\n        Categories.Compiler,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [NotConfigurable]);\n\n    /// <summary>\n    /// 生成代码时出现已知错误。本生成器不会报告此错误，因为分析器会准确报告之。\n    /// </summary>\n    public static DiagnosticDescriptor IPC002_KnownDiagnosticError { get; } = new(\n        nameof(IPC002),\n        Localize(nameof(IPC002)),\n        Localize(nameof(IPC002_Message)),\n        Categories.Compiler,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC101_IpcType_TimeoutCannotBeNegative { get; } = new(\n        nameof(IPC101),\n        Localize(nameof(IPC101)),\n        Localize(nameof(IPC101_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC102_IpcType_TimeoutZeroIsUnnecessary { get; } = new(\n        nameof(IPC102),\n        Localize(nameof(IPC102)),\n        Localize(nameof(IPC102_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC131_IpcMembers_IgnoresIpcExceptionIsRecommended { get; } = new(\n        nameof(IPC131),\n        Localize(nameof(IPC131)),\n        Localize(nameof(IPC131_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Info,\n        true);\n\n    public static DiagnosticDescriptor IPC160_IpcShape_ContractTypeMustBeAnInterface { get; } = new(\n        nameof(IPC160),\n        Localize(nameof(IPC160)),\n        Localize(nameof(IPC160_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC161_IpcShape_ContractTypeDismatchWithInterface { get; } = new(\n        nameof(IPC161),\n        Localize(nameof(IPC161)),\n        Localize(nameof(IPC161_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC162_IpcShape_AllMembersShouldBeMarkedAsIpcMembers { get; } = new(\n        nameof(IPC162),\n        Localize(nameof(IPC162)),\n        Localize(nameof(IPC162_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Warning,\n        true);\n\n    public static DiagnosticDescriptor IPC200_IpcMembers_OnlyPropertiesMethodsAndEventsAreSupported { get; } = new(\n        nameof(IPC200),\n        Localize(nameof(IPC200)),\n        Localize(nameof(IPC200_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC201_IpcMember_EmptyIpcMemberAttributeIsUnnecessary { get; } = new(\n        nameof(IPC201),\n        Localize(nameof(IPC201)),\n        Localize(nameof(IPC201_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC202_IpcMember_AllMembersShouldBeMarkedAsIpcMembers { get; } = new(\n        nameof(IPC202),\n        Localize(nameof(IPC202)),\n        Localize(nameof(IPC202_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Warning,\n        true);\n\n    public static DiagnosticDescriptor IPC240_IpcProperty_IpcPropertyIsNotRecommended { get; } = new(\n        nameof(IPC240),\n        Localize(nameof(IPC240)),\n        Localize(nameof(IPC240_Message)),\n        Categories.AvoidBugs,\n        DiagnosticSeverity.Info,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC241_IpcProperty_SetOnlyPropertyIsNotSupported { get; } = new(\n        nameof(IPC241),\n        Localize(nameof(IPC241)),\n        Localize(nameof(IPC241_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC242_IpcProperty_DefaultReturnDependsOnIgnoresIpcException { get; } = new(\n        nameof(IPC242),\n        Localize(nameof(IPC242)),\n        Localize(nameof(IPC242_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC243_IpcProperty_IsReadonlyFalseIsUnnecessary { get; } = new(\n        nameof(IPC243),\n        Localize(nameof(IPC243)),\n        Localize(nameof(IPC243_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC244_IpcProperty_DefaultReturnDismatchWithPropertyType { get; } = new(\n        nameof(IPC244),\n        Localize(nameof(IPC244)),\n        Localize(nameof(IPC244_Message)),\n        Categories.RuntimeException,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC245_IpcProperty_DefaultReturnsStringForAnObjectType { get; } = new(\n        nameof(IPC245),\n        Localize(nameof(IPC245)),\n        Localize(nameof(IPC245_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Warning,\n        true);\n\n    public static DiagnosticDescriptor IPC246_IpcProperty_DefaultReturnsStringForANonStringType { get; } = new(\n        nameof(IPC246),\n        Localize(nameof(IPC246)),\n        Localize(nameof(IPC246_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Hidden,\n        true);\n\n    public static DiagnosticDescriptor IPC247_IpcProperty_DefaultReturnsStringCannotBeCompiledAsACodeSnippet { get; } = new(\n        nameof(IPC247),\n        Localize(nameof(IPC247)),\n        Localize(nameof(IPC247_Message)),\n        Categories.Compiler,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC248_IpcProperty_PropertyTypeIsNotSupportedForIpc { get; } = new(\n        nameof(IPC248),\n        Localize(nameof(IPC248)),\n        Localize(nameof(IPC248_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC260_IpcMethod_SyncMethodIsNotRecommended { get; } = new(\n        nameof(IPC260),\n        Localize(nameof(IPC260)),\n        Localize(nameof(IPC260_Message)),\n        Categories.AvoidBugs,\n        DiagnosticSeverity.Info,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC261_IpcMethod_DefaultReturnDependsOnIgnoresIpcException { get; } = new(\n        nameof(IPC261),\n        Localize(nameof(IPC261)),\n        Localize(nameof(IPC261_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC262_IpcMethod_WaitsVoidIsRecommended { get; } = new(\n        nameof(IPC262),\n        Localize(nameof(IPC262)),\n        Localize(nameof(IPC262_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Warning,\n        true);\n\n    public static DiagnosticDescriptor IPC263_IpcMethod_WaitsVoidIsUseless { get; } = new(\n        nameof(IPC263),\n        Localize(nameof(IPC263)),\n        Localize(nameof(IPC263_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC264_IpcMethod_DefaultReturnDismatchWithMethodReturnType { get; } = new(\n        nameof(IPC264),\n        Localize(nameof(IPC264)),\n        Localize(nameof(IPC264_Message)),\n        Categories.RuntimeException,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC265_IpcMethod_DefaultReturnIsUselessForAVoidMethod { get; } = new(\n        nameof(IPC265),\n        Localize(nameof(IPC265)),\n        Localize(nameof(IPC265_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC266_IpcMethod_DefaultReturnIsUselessForATaskMethod { get; } = new(\n        nameof(IPC266),\n        Localize(nameof(IPC266)),\n        Localize(nameof(IPC266_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC267_IpcMethod_DefaultReturnsStringForAnObjectType { get; } = new(\n        nameof(IPC267),\n        Localize(nameof(IPC267)),\n        Localize(nameof(IPC267_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Warning,\n        true);\n\n    public static DiagnosticDescriptor IPC268_IpcMethod_DefaultReturnsStringForANonStringType { get; } = new(\n        nameof(IPC268),\n        Localize(nameof(IPC268)),\n        Localize(nameof(IPC268_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Hidden,\n        true);\n\n    public static DiagnosticDescriptor IPC269_IpcMethod_DefaultReturnsStringCannotBeCompiledAsACodeSnippet { get; } = new(\n        nameof(IPC269),\n        Localize(nameof(IPC269)),\n        Localize(nameof(IPC269_Message)),\n        Categories.Compiler,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC270_IpcMethod_GenericMethodIsNotSupportedForIpc { get; } = new(\n        nameof(IPC270),\n        Localize(nameof(IPC270)),\n        Localize(nameof(IPC270_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC271_IpcMethod_MethodParameterTypeIsNotSupportedForIpc { get; } = new(\n        nameof(IPC271),\n        Localize(nameof(IPC271)),\n        Localize(nameof(IPC271_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC272_IpcMethod_MethodReturnTypeIsNotSupportedForIpc { get; } = new(\n        nameof(IPC272),\n        Localize(nameof(IPC272)),\n        Localize(nameof(IPC272_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true);\n\n    public static DiagnosticDescriptor IPC301_CreateIpcProxy_AddIpcProxyConfigs { get; } = new(\n        nameof(IPC301),\n        Localize(nameof(IPC301)),\n        Localize(nameof(IPC301_Message)),\n        Categories.CodeFixOnly,\n        DiagnosticSeverity.Hidden,\n        true);\n\n    public static DiagnosticDescriptor IPC302_CreateIpcProxy_AddIpcShape { get; } = new(\n        nameof(IPC302),\n        Localize(nameof(IPC302)),\n        Localize(nameof(IPC302_Message)),\n        Categories.CodeFixOnly,\n        DiagnosticSeverity.Hidden,\n        true);\n\n    public static DiagnosticDescriptor IPC303_CreateIpcProxy_AddIpcProxyConfigs { get; } = new(\n        nameof(IPC303),\n        Localize(nameof(IPC303)),\n        Localize(nameof(IPC303_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Info,\n        true);\n\n    public static DiagnosticDescriptor IPC304_CreateIpcProxy_RemoveIpcProxyConfigs { get; } = new(\n        nameof(IPC304),\n        Localize(nameof(IPC304)),\n        Localize(nameof(IPC304_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC305_CreateIpcProxy_IgnoresIpcExceptionIsRecommended { get; } = new(\n        nameof(IPC305),\n        Localize(nameof(IPC305)),\n        Localize(nameof(IPC305_Message)),\n        Categories.Readable,\n        DiagnosticSeverity.Info,\n        true);\n\n    public static DiagnosticDescriptor IPC306_CreateIpcProxy_IgnoresIpcExceptionIsUseless { get; } = new(\n        nameof(IPC306),\n        Localize(nameof(IPC306)),\n        Localize(nameof(IPC306_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC307_CreateIpcProxy_TimeoutIsRecommended { get; } = new(\n        nameof(IPC307),\n        Localize(nameof(IPC307)),\n        Localize(nameof(IPC307_Message)),\n        Categories.CodeFixOnly,\n        DiagnosticSeverity.Hidden,\n        true);\n\n    public static DiagnosticDescriptor IPC308_CreateIpcProxy_TimeoutIsUseless { get; } = new(\n        nameof(IPC308),\n        Localize(nameof(IPC308)),\n        Localize(nameof(IPC308_Message)),\n        Categories.Useless,\n        DiagnosticSeverity.Hidden,\n        true,\n        customTags: [Unnecessary]);\n\n    public static DiagnosticDescriptor IPC309_CreateIpcProxy_IpcShapeIsNotValid { get; } = new(\n        nameof(IPC309),\n        Localize(nameof(IPC309)),\n        Localize(nameof(IPC309_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPC310_CreateIpcProxy_IpcShapeDismatchWithContractType { get; } = new(\n        nameof(IPC310),\n        Localize(nameof(IPC310)),\n        Localize(nameof(IPC310_Message)),\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    public static DiagnosticDescriptor IPCTMP1_IpcMembers_EventIsNotSupported { get; } = new(\n        \"IPCTMP1\",\n        \"IPC 成员暂不支持事件\",\n        \"IPC 成员暂不支持事件\",\n        Categories.Mechanism,\n        DiagnosticSeverity.Error,\n        true,\n        customTags: [NotConfigurable]);\n\n    private static class Categories\n    {\n        /// <summary>\n        /// 可能产生 bug，则报告此诊断。\n        /// </summary>\n        public const string AvoidBugs = \"dotnetCampus.AvoidBugs\";\n\n        /// <summary>\n        /// 为了提供代码生成能力，则报告此诊断。\n        /// </summary>\n        public const string CodeFixOnly = \"dotnetCampus.CodeFixOnly\";\n\n        /// <summary>\n        /// 因编译要求而必须满足的条件没有满足，则报告此诊断。\n        /// </summary>\n        public const string Compiler = \"dotnetCampus.Compiler\";\n\n        /// <summary>\n        /// 因 IPC 库内的机制限制，必须满足此要求 IPC 才可正常工作，则报告此诊断。\n        /// </summary>\n        public const string Mechanism = \"dotnetCampus.Mechanism\";\n\n        /// <summary>\n        /// 为了代码可读性，使之更易于理解、方便调试，则报告此诊断。\n        /// </summary>\n        public const string Readable = \"dotnetCampus.Readable\";\n\n        /// <summary>\n        /// 能写得出来正常编译，但会引发运行时异常，则报告此诊断。\n        /// </summary>\n        public const string RuntimeException = \"dotnetCampus.RuntimeException\";\n\n        /// <summary>\n        /// 编写了无法生效的代码，则报告此诊断。\n        /// </summary>\n        public const string Useless = \"dotnetCampus.Useless\";\n    }\n\n    private static LocalizableString Localize(string key) => new LocalizableResourceString(key, ResourceManager, typeof(Localizations));\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Models/IpcPublicAttributeNamedValues.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CodeAnalysis.Models;\n\n/// <summary>\n/// 仅供 GeneratedIpcProxy 的自动生成的派生类与基类传递参数使用，包含参数传递所需的各种个性化需求。\n/// </summary>\ninternal class IpcPublicAttributeNamedValues\n{\n    public IpcPublicAttributeNamedValues(INamedTypeSymbol? ipcType)\n    {\n        IpcType = ipcType;\n    }\n\n    public IpcPublicAttributeNamedValues(INamedTypeSymbol? ipcType, ISymbol? member, ITypeSymbol? memberReturnType)\n    {\n        IpcType = ipcType;\n        Member = member;\n        MemberReturnType = memberReturnType;\n    }\n\n    public INamedTypeSymbol? IpcType { get; }\n\n    public ISymbol? Member { get; }\n\n    public ITypeSymbol? MemberReturnType { get; }\n\n    public Assignable<string?>? DefaultReturn { get; init; }\n\n    public Assignable<bool>? IgnoresIpcException { get; init; }\n\n    public Assignable<bool>? IsReadonly { get; init; }\n\n    public Assignable<int>? Timeout { get; init; }\n\n    public Assignable<bool>? WaitsVoid { get; init; }\n\n    public override string ToString() => ToIndentString(\"    \", 0);\n\n    public string ToIndentString(string indent, int baseIndentLevel = 1)\n    {\n        var baseIndent = string.Concat(Enumerable.Repeat(indent, baseIndentLevel));\n        List<string> assignments =\n        [\n            Format(nameof(DefaultReturn), DefaultReturn, x => x ?? \"null\"),\n            Format(nameof(Timeout), Timeout),\n            Format(nameof(IgnoresIpcException), IgnoresIpcException),\n            Format(nameof(IsReadonly), IsReadonly),\n            Format(nameof(WaitsVoid), WaitsVoid),\n        ];\n        assignments.RemoveAll(string.IsNullOrEmpty);\n        if (assignments.Count is 0)\n        {\n            return \"default\";\n        }\n\n        var builder = new StringBuilder();\n        builder.AppendLine(\"new()\");\n        builder.Append(baseIndent).AppendLine(\"{\");\n        foreach (var assignment in assignments)\n        {\n            builder.Append(baseIndent).Append(indent).AppendLine(assignment);\n        }\n        builder.Append(baseIndent).Append(\"}\");\n        return builder.ToString();\n    }\n\n    protected string Format<T>(string name, Assignable<T>? assignable, Func<T?, string>? customFormatter = null)\n    {\n        if (assignable is null)\n        {\n            // 未赋值。\n            return \"\";\n        }\n\n        var value = assignable.Value.Value;\n        return customFormatter is null\n            ? $\"{name} = {Format(value, typeof(T))},\"\n            : $\"{name} = {customFormatter(value)},\";\n    }\n\n    /// <summary>\n    /// 将 Attribute 里设置的值转为字符串。\n    /// </summary>\n    /// <param name=\"value\">值。</param>\n    /// <param name=\"valueType\">值的类型。</param>\n    /// <returns></returns>\n    protected static string Format(object? value, Type valueType)\n    {\n        if (value is null)\n        {\n            return \"null\";\n        }\n        else if (valueType == typeof(string))\n        {\n            return $@\"\"\"{value}\"\"\";\n        }\n        if (valueType == typeof(bool) && value is bool booleanValue)\n        {\n            return booleanValue ? \"true\" : \"false\";\n        }\n        if (value is int int32Value)\n        {\n            // 可能会出现隐式转换，所以难以判断目标类型。\n            return int32Value.ToString(CultureInfo.InvariantCulture);\n        }\n        else\n        {\n            return value.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Models/IpcShapeAttributeNamedValues.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CodeAnalysis.Models;\n\n/// <summary>\n/// 仅供 GeneratedIpcProxy 的自动生成的派生类与基类传递参数使用，包含参数传递所需的各种个性化需求。\n/// </summary>\ninternal class IpcShapeAttributeNamedValues : IpcPublicAttributeNamedValues\n{\n    public IpcShapeAttributeNamedValues(INamedTypeSymbol? contractType, INamedTypeSymbol? ipcType)\n        : base(ipcType)\n    {\n        ContractType = contractType;\n    }\n\n    public IpcShapeAttributeNamedValues(INamedTypeSymbol? contractType, INamedTypeSymbol? ipcType, ISymbol? member, ITypeSymbol? memberReturnType)\n        : base(ipcType, member, memberReturnType)\n    {\n        ContractType = contractType;\n    }\n\n    public INamedTypeSymbol? ContractType { get; }\n\n    public override string ToString()\n    {\n        return $@\"new()\n{{\n    {Format(nameof(DefaultReturn), DefaultReturn, x => x ?? \"null\")}\n    {Format(nameof(Timeout), Timeout)}\n    {Format(nameof(IgnoresIpcException), IgnoresIpcException)}\n    {Format(nameof(IsReadonly), IsReadonly)}\n    {Format(nameof(WaitsVoid), WaitsVoid)}\n}}\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Utils/IpcSemanticAttributeHelper.cs",
    "content": "﻿using dotnetCampus.Ipc.CodeAnalysis.Models;\n\nnamespace dotnetCampus.Ipc.CodeAnalysis.Utils;\n\n/// <summary>\n/// 包含 <see cref=\"IpcMemberAttribute\"/> 相关语义分析的辅助扩展方法。\n/// </summary>\ninternal static class IpcSemanticAttributeHelper\n{\n    /// <summary>\n    /// 如果类型是 IPC 类型，则返回 true；否则返回 false。\n    /// <para>IPC 类型为标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型。</para>\n    /// </summary>\n    /// <param name=\"type\">要检查的类型。</param>\n    /// <returns></returns>\n    internal static bool GetIsIpcType(this ITypeSymbol type)\n    {\n        return type.GetIsDefined<IpcPublicAttribute>() || type.GetIsDefined<IpcShapeAttribute>();\n    }\n\n    /// <summary>\n    /// 检查此类型上标记的 <see cref=\"IpcPublicAttribute\"/> 并将其转换为传入 IPC 代理的类型。\n    /// </summary>\n    /// <param name=\"type\"></param>\n    /// <returns></returns>\n    internal static IpcPublicAttributeNamedValues GetIpcPublicNamedValues(this INamedTypeSymbol type)\n    {\n        var ignoresIpcException = type.GetAttributeValue<IpcPublicAttribute, bool>(nameof(IpcPublicAttribute.IgnoresIpcException));\n        var timeout = type.GetAttributeValue<IpcPublicAttribute, int>(nameof(IpcPublicAttribute.Timeout));\n        return new IpcPublicAttributeNamedValues(type)\n        {\n            Timeout = timeout,\n            IgnoresIpcException = ignoresIpcException,\n        };\n    }\n\n    /// <summary>\n    /// 检查此类型上标记的 <see cref=\"IpcShapeAttribute\"/> 并将其转换为传入 IPC 代理的类型。\n    /// </summary>\n    /// <param name=\"type\"></param>\n    /// <returns></returns>\n    internal static IpcShapeAttributeNamedValues GetIpcShapeNamedValues(this INamedTypeSymbol type)\n    {\n        var contractType = type.GetAttributedContractType<IpcShapeAttribute>();\n        var ignoresIpcException = type.GetAttributeValue<IpcShapeAttribute, bool>(nameof(IpcShapeAttribute.IgnoresIpcException));\n        var timeout = type.GetAttributeValue<IpcPublicAttribute, int>(nameof(IpcShapeAttribute.Timeout));\n        return new IpcShapeAttributeNamedValues(contractType, type)\n        {\n            Timeout = timeout,\n            IgnoresIpcException = ignoresIpcException,\n        };\n    }\n\n    /// <summary>\n    /// 检查此方法上标记的 <see cref=\"IpcMethodAttribute\"/> 并将其转换为传入 IPC 代理的类型。\n    /// </summary>\n    /// <param name=\"method\"></param>\n    /// <param name=\"returnTypeSymbol\">返回值类型，依此来决定如何将 Attribute 里的对象转为字符串。</param>\n    /// <param name=\"containingType\">要查找标记的类型。注意，当此属性来自于继承类时，属性所在的类型和真实要分析的类型不相同。</param>\n    /// <returns></returns>\n    internal static IpcPublicAttributeNamedValues GetIpcNamedValues(this IMethodSymbol method, ITypeSymbol? returnTypeSymbol, INamedTypeSymbol containingType)\n    {\n        var type = containingType;\n        var waitsVoid = method.GetAttributeValue<IpcMethodAttribute, bool>(nameof(IpcMethodAttribute.WaitsVoid));\n        var ignoresIpcException = method.GetAttributeValue<IpcMethodAttribute, bool>(nameof(IpcMethodAttribute.IgnoresIpcException))\n            ?? type.GetAttributeValue<IpcPublicAttribute, bool>(nameof(IpcPublicAttribute.IgnoresIpcException));\n        var defaultReturn = method.GetAttributeValue<IpcMethodAttribute, string?>(nameof(IpcMethodAttribute.DefaultReturn));\n        var timeout = method.GetAttributeValue<IpcMethodAttribute, int>(nameof(IpcMethodAttribute.Timeout))\n            ?? type.GetAttributeValue<IpcPublicAttribute, int>(nameof(IpcPublicAttribute.Timeout));\n        return new IpcPublicAttributeNamedValues(type, method, returnTypeSymbol)\n        {\n            DefaultReturn = defaultReturn,\n            IgnoresIpcException = ignoresIpcException,\n            Timeout = timeout,\n            WaitsVoid = waitsVoid\n        };\n    }\n\n    /// <summary>\n    /// 检查此方法上标记的 <see cref=\"IpcMethodAttribute\"/> 并将其转换为传入 IPC 代理的类型。\n    /// </summary>\n    /// <param name=\"property\">要查找标记的属性。</param>\n    /// <param name=\"containingType\">要查找标记的类型。注意，当此属性来自于继承类时，属性所在的类型和真实要分析的类型不相同。</param>\n    /// <returns></returns>\n    public static IpcPublicAttributeNamedValues GetIpcNamedValues(this IPropertySymbol property, INamedTypeSymbol containingType)\n    {\n        var type = containingType;\n        var isReadonly = property.GetAttributeValue<IpcPropertyAttribute, bool>(nameof(IpcPropertyAttribute.IsReadonly));\n        var ignoresIpcException = property.GetAttributeValue<IpcPropertyAttribute, bool>(nameof(IpcPropertyAttribute.IgnoresIpcException))\n            ?? type.GetAttributeValue<IpcPublicAttribute, bool>(nameof(IpcPublicAttribute.IgnoresIpcException));\n        var defaultReturn = property.GetAttributeValue<IpcPropertyAttribute, string?>(nameof(IpcPropertyAttribute.DefaultReturn));\n        var timeout = property.GetAttributeValue<IpcPropertyAttribute, int>(nameof(IpcPropertyAttribute.Timeout))\n            ?? type.GetAttributeValue<IpcPublicAttribute, int>(nameof(IpcPublicAttribute.Timeout));\n        return new IpcPublicAttributeNamedValues(type, property, property.Type)\n        {\n            DefaultReturn = defaultReturn,\n            IgnoresIpcException = ignoresIpcException,\n            IsReadonly = isReadonly,\n            Timeout = timeout,\n        };\n    }\n\n    private static INamedTypeSymbol? GetAttributedContractType<TAttribute>(this ISymbol symbol)\n    {\n        var attributeTypeName = typeof(TAttribute).FullName;\n        if (symbol.GetAttributes().FirstOrDefault(x => string.Equals(\n             x.AttributeClass?.ToString(),\n             attributeTypeName,\n             StringComparison.Ordinal)) is { } ipcMethodAttribute)\n        {\n            var typedConstant = ipcMethodAttribute.ConstructorArguments.FirstOrDefault();\n            if (typedConstant.Kind is TypedConstantKind.Type)\n            {\n                return typedConstant.Value as INamedTypeSymbol;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Utils/IpcSemanticSyntaxHelper.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\nusing Microsoft.CodeAnalysis.CSharp.Syntax;\n\nnamespace dotnetCampus.Ipc.CodeAnalysis.Utils;\n\ninternal static class IpcSemanticSyntaxHelper\n{\n    /// <summary>\n    /// 判断一个语义类型是否是 IPC 契约类型，如果是则返回其 IPC 特性语法。\n    /// </summary>\n    /// <param name=\"typeSymbol\"></param>\n    /// <param name=\"semanticModel\"></param>\n    /// <returns></returns>\n    public static AttributeSyntax? TryGetClassDeclarationWithIpcAttribute(this INamedTypeSymbol typeSymbol, SemanticModel semanticModel)\n    {\n        if (typeSymbol.TryGetTypeDeclaration() is InterfaceDeclarationSyntax interfaceDeclarationNode)\n        {\n            var (attribute, _) = IpcAttributeHelper.TryFindIpcPublicAttributes(semanticModel, interfaceDeclarationNode).FirstOrDefault();\n            if (attribute is not null)\n            {\n                return attribute;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Utils/SemanticAttributeHelper.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CodeAnalysis.Utils;\n\n/// <summary>\n/// 包含语义分析的辅助扩展方法。\n/// </summary>\ninternal static class SemanticAttributeHelper\n{\n    /// <summary>\n    /// 检查此成员的 <typeparamref name=\"TAttribute\"/> 类型特性的 <paramref name=\"namedArgumentName\"/> 名字的参数的值。\n    /// </summary>\n    /// <typeparam name=\"TAttribute\">特性类型。</typeparam>\n    /// <typeparam name=\"T\">参数类型。</typeparam>\n    /// <param name=\"symbol\">要查找特性的语义符号。</param>\n    /// <param name=\"namedArgumentName\">参数名称。</param>\n    /// <returns>参数的值。</returns>\n    public static Assignable<T>? GetAttributeValue<TAttribute, T>(this ISymbol symbol, string namedArgumentName)\n    {\n        var assignable = GetAttributeValue(symbol, typeof(TAttribute).FullName!, namedArgumentName);\n\n        // 未赋值。\n        if (assignable is null)\n        {\n            return null;\n        }\n\n        // 引用类型已赋值。\n        if (typeof(T) == typeof(object))\n        {\n            return new((T?) assignable.Value);\n        }\n        else if (typeof(T) == typeof(string))\n        {\n            return new((T?) assignable.Value);\n        }\n\n        // null 已赋值。\n        var value = assignable.Value.Value;\n        if (value is null)\n        {\n            return new(default);\n        }\n\n        // 值类型已复制。\n        if (typeof(T) == typeof(bool))\n        {\n            return new((T) (object) Convert.ToBoolean(value));\n        }\n        else if (typeof(T) == typeof(int))\n        {\n            return new((T) (object) Convert.ToInt32(value));\n        }\n\n        // 其他已赋值。\n        throw new NotSupportedException($\"尚不支持读取 {typeof(T).Name} 类型的特性。\");\n    }\n\n    /// <summary>\n    /// 检查此成员是否已定义了 <typeparamref name=\"TAttribute\"/> 类型的特性。\n    /// </summary>\n    /// <typeparam name=\"TAttribute\">要检查的类型。</typeparam>\n    /// <param name=\"symbol\">要查找特性的语义符号。</param>\n    /// <returns>如果定义了 <typeparamref name=\"TAttribute\"/>，则返回 true；否则返回 false。</returns>\n    internal static bool GetIsDefined<TAttribute>(this ISymbol symbol)\n    {\n        return symbol.GetAttributes().FirstOrDefault(x => string.Equals(\n            x.AttributeClass?.ToString(),\n            typeof(TAttribute).FullName,\n            StringComparison.Ordinal)) is not null;\n    }\n\n    /// <summary>\n    /// 检查此成员的 <paramref name=\"attributeTypeName\"/> 类型特性的 <paramref name=\"namedArgumentName\"/> 名字的参数的值。\n    /// </summary>\n    /// <param name=\"symbol\">要查找特性的语义符号。</param>\n    /// <param name=\"attributeTypeName\">特性类型的名称。</param>\n    /// <param name=\"namedArgumentName\">参数名称。</param>\n    /// <returns>参数的值。</returns>\n    private static Assignable<object?>? GetAttributeValue(ISymbol symbol, string attributeTypeName, string namedArgumentName)\n    {\n        if (symbol.GetAttributes().FirstOrDefault(x => string.Equals(\n             x.AttributeClass?.ToString(),\n             attributeTypeName,\n             StringComparison.Ordinal)) is { } ipcMethodAttribute)\n        {\n            var argumentPair = ipcMethodAttribute.NamedArguments\n                .FirstOrDefault(x => x.Key == namedArgumentName);\n            if (argumentPair.Key is not null)\n            {\n                return new(argumentPair.Value.Value);\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Utils/SemanticModelsHelper.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CSharp.Syntax;\n\nusing SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;\n\nnamespace dotnetCampus.Ipc.CodeAnalysis.Utils;\n\ninternal static class SemanticModelsHelper\n{\n    public static string ToFullyQualifiedName(this INamedTypeSymbol typeSymbol)\n    {\n        var symbolDisplayFormat = new SymbolDisplayFormat(\n            typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);\n        return typeSymbol.ToDisplayString(symbolDisplayFormat);\n    }\n\n    public static SyntaxNode ReplaceNodeWithUsings(this SyntaxNode rootSyntaxNode,\n        SyntaxNode oldSyntaxNode, SyntaxNode newSyntaxNode,\n        params INamedTypeSymbol[] namespaceProviders)\n    {\n        // 追踪多个语法节点的变更。\n        var annotation = new SyntaxAnnotation();\n\n        // 追踪即将被替换的语法节点。\n        var newRoot = rootSyntaxNode.ReplaceNode(\n             oldSyntaxNode,\n             oldSyntaxNode.WithAdditionalAnnotations(annotation));\n\n        // 增加文件级命名空间。\n        var fileNamespace = rootSyntaxNode.ChildNodes().OfType<FileScopedNamespaceDeclarationSyntax>().FirstOrDefault()?.Name.ToString()\n            ?? rootSyntaxNode.ChildNodes().OfType<NamespaceDeclarationSyntax>().FirstOrDefault()?.Name.ToString();\n        if (newRoot is CompilationUnitSyntax compilation)\n        {\n            foreach (var namespaceProvider in namespaceProviders)\n            {\n                var newNamespace = namespaceProvider.ContainingNamespace.ToString();\n                if (!string.Equals(fileNamespace, newNamespace)\n                    && compilation.Usings.All(u => !string.Equals(newNamespace, u.Name.ToString(), StringComparison.Ordinal)))\n                {\n                    var @namespace = SF.UsingDirective(SF.ParseName(newNamespace));\n                    newRoot = newRoot.InsertNodesAfter(\n                        compilation.Usings.Last(),\n                        new[] { @namespace });\n                }\n            }\n        }\n\n        // 替换节点。\n        var annotatedOldSyntaxNode = newRoot.GetAnnotatedNodes(annotation).First();\n        newRoot = newRoot.ReplaceNode(annotatedOldSyntaxNode, newSyntaxNode.WithoutAnnotations());\n\n        // 返回替换后的节点。\n        return newRoot;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Utils/SemanticSyntaxHelper.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CSharp.Syntax;\n\nnamespace dotnetCampus.Ipc.CodeAnalysis.Utils;\n\ninternal static class SemanticSyntaxHelper\n{\n    public static TypeDeclarationSyntax? TryGetTypeDeclaration(this INamedTypeSymbol type)\n    {\n        return type.Locations.FirstOrDefault() is Location location\n            ? location.SourceTree?.GetRoot()?.FindNode(location.SourceSpan) as TypeDeclarationSyntax\n            : null;\n    }\n\n    public static EventDeclarationSyntax? TryGetMemberDeclaration(this IEventSymbol @event)\n    {\n        return @event.Locations.FirstOrDefault() is Location location\n            ? location.SourceTree?.GetRoot()?.FindNode(location.SourceSpan) as EventDeclarationSyntax\n            : null;\n    }\n\n    public static PropertyDeclarationSyntax? TryGetMemberDeclaration(this IPropertySymbol @event)\n    {\n        return @event.Locations.FirstOrDefault() is Location location\n            ? location.SourceTree?.GetRoot()?.FindNode(location.SourceSpan) as PropertyDeclarationSyntax\n            : null;\n    }\n\n    public static MethodDeclarationSyntax? TryGetMemberDeclaration(this IMethodSymbol @event)\n    {\n        return @event.Locations.FirstOrDefault() is Location location\n            ? location.SourceTree?.GetRoot()?.FindNode(location.SourceSpan) as MethodDeclarationSyntax\n            : null;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeAnalysis/Utils/SyntaxNameGuesser.cs",
    "content": "﻿namespace dotnetCampus.Ipc.CodeAnalysis.Utils;\n\ninternal static class SyntaxNameGuesser\n{\n    /// <summary>\n    /// 获取一个类型作为 Attribute 编写时的编写名称，即去掉末尾的 Attribute 字符串。\n    /// </summary>\n    /// <param name=\"typeName\">类型名称。</param>\n    /// <returns>去掉末尾 Attribute 后的字符串。</returns>\n    public static string GetAttributeName(string typeName)\n    {\n        const string attributePostfix = \"Attribute\";\n        if (typeName.EndsWith(attributePostfix, StringComparison.Ordinal))\n        {\n            return typeName.Substring(0, typeName.Length - attributePostfix.Length);\n        }\n        return typeName;\n    }\n\n    /// <summary>\n    /// 去掉接口前面的“I”以生成一个类型名称。\n    /// </summary>\n    /// <param name=\"interfaceName\">接口名称。</param>\n    /// <returns>类型名称</returns>\n    public static string GenerateClassNameByInterfaceName(string interfaceName)\n    {\n        const string interfacePrefix = \"I\";\n        if (interfaceName.StartsWith(interfacePrefix, StringComparison.Ordinal))\n        {\n            return interfaceName.Substring(1);\n        }\n        return interfaceName;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeFixes/AddIpcShapeCodeFixProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\nusing dotnetCampus.Ipc.Generators.Compiling;\nusing dotnetCampus.Ipc.Properties;\nusing Microsoft.CodeAnalysis.CodeActions;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing static dotnetCampus.Ipc.Generators.Utils.GeneratorHelper;\nusing SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;\n\nnamespace dotnetCampus.Ipc.CodeFixes;\n\n[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IgnoresIpcExceptionIsRecommendedCodeFixProvider)), Shared]\npublic class AddIpcShapeCodeFixProvider : CodeFixProvider\n{\n    public AddIpcShapeCodeFixProvider()\n    {\n        FixableDiagnosticIds = ImmutableArray.Create(IPC302_CreateIpcProxy_AddIpcShape.Id);\n    }\n\n    public override ImmutableArray<string> FixableDiagnosticIds { get; }\n\n    public override FixAllProvider? GetFixAllProvider()\n    {\n        // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers\n        return WellKnownFixAllProviders.BatchFixer;\n    }\n\n    public override async Task RegisterCodeFixesAsync(CodeFixContext context)\n    {\n        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return;\n        }\n\n        var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);\n        if (semanticModel is null)\n        {\n            return;\n        }\n\n        foreach (var diagnostic in context.Diagnostics)\n        {\n            var diagnosticSpan = diagnostic.Location.SourceSpan;\n            var node = root.FindNode(diagnosticSpan);\n            if (node is MemberAccessExpressionSyntax memberAccessNode\n                && IpcProxyInvokingInfo.TryCreateIpcProxyInvokingInfo(\n                    semanticModel,\n                    memberAccessNode,\n                    context.CancellationToken) is { } invokingInfo)\n            {\n                if (invokingInfo.ShapeType is null)\n                {\n                    // 在新文件中生成 IPC 形状代理。（在当前文件中生成的这一个，因为字符串拼接的代码很难和语法节点的代码保持格式统一，所以就不做了。）\n                    var fix2 = string.Format(Localizations.IPC302_Fix2, invokingInfo.ContractType.Name);\n                    context.RegisterCodeFix(\n                        CodeAction.Create(\n                            title: fix2,\n                            createChangedSolution: c => GenerateIpcShapeInNewFileAsync(context.Document, invokingInfo, root, c),\n                            equivalenceKey: fix2),\n                        diagnostic);\n                }\n            }\n        }\n    }\n\n    private async Task<Solution> GenerateIpcShapeInNewFileAsync(Document document, IpcProxyInvokingInfo invokingInfo, SyntaxNode root, CancellationToken cancellationToken)\n    {\n        var newTypeName = GenerateClassNameByInterfaceName($\"{invokingInfo.ContractType.Name}IpcShape\");\n\n        if (invokingInfo.MemberAccessNode is { } memberAccessNode\n            && memberAccessNode.Name is GenericNameSyntax genericNameNode\n            && genericNameNode.TypeArgumentList is { } typeArgumentListNode\n            && typeArgumentListNode.Arguments.Count == 1\n            && typeArgumentListNode.Arguments[0] is TypeSyntax contractTypeArgumentNode)\n        {\n            // 在调用处的泛型参数列表中传入新生成的类型。\n            var newRoot = root.ReplaceNode(\n                typeArgumentListNode,\n                SF.TypeArgumentList(SF.SeparatedList(new[]\n                {\n                    contractTypeArgumentNode,\n                    SF.ParseTypeName(newTypeName),\n                })));\n            document = document.WithSyntaxRoot(newRoot);\n\n            var contractTypeDeclarationNode = invokingInfo.ContractType.TryGetTypeDeclaration();\n            if (contractTypeDeclarationNode is not null)\n            {\n                // 新生成一个 IPC 形状代理类型。\n                var ipcPublicCompilation = new IpcPublicCompilation(contractTypeDeclarationNode.SyntaxTree, invokingInfo.SemanticModel, invokingInfo.ContractType);\n                var shapeSource = GenerateShapeSource(ipcPublicCompilation, newTypeName, GetNamespace(newRoot));\n                var newDocumentRoot = SF.ParseSyntaxTree(shapeSource).GetRoot();\n\n                var folders = new List<string>(document.Folders.Count + 1) { document.Project.Name };\n                folders.AddRange(document.Folders);\n\n                var newDocument = document.Project.AddDocument(\n                    newTypeName,\n                    newDocumentRoot,\n                    ImmutableArray.CreateRange(folders));\n                return newDocument.Project.Solution.WithDocumentSyntaxRoot(document.Id, newRoot);\n            }\n        }\n\n        return document.Project.Solution;\n    }\n\n    private static string? GetNamespace(SyntaxNode root)\n    {\n        return root.ChildNodes().OfType<FileScopedNamespaceDeclarationSyntax>().FirstOrDefault()?.Name.ToString()\n            ?? root.ChildNodes().OfType<NamespaceDeclarationSyntax>().FirstOrDefault()?.Name.ToString();\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeFixes/ChangeClassContractTypeCodeFixProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\nusing dotnetCampus.Ipc.Properties;\nusing Microsoft.CodeAnalysis.CodeActions;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;\n\nnamespace dotnetCampus.Ipc.CodeFixes;\n\n[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IgnoresIpcExceptionIsRecommendedCodeFixProvider)), Shared]\npublic class ChangeClassContractTypeCodeFixProvider : CodeFixProvider\n{\n    public ChangeClassContractTypeCodeFixProvider()\n    {\n        FixableDiagnosticIds = ImmutableArray.Create(\n            IPC160_IpcShape_ContractTypeMustBeAnInterface.Id,\n            IPC161_IpcShape_ContractTypeDismatchWithInterface.Id);\n    }\n\n    public override ImmutableArray<string> FixableDiagnosticIds { get; }\n\n    public override FixAllProvider? GetFixAllProvider()\n    {\n        // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers\n        return WellKnownFixAllProviders.BatchFixer;\n    }\n\n    public override async Task RegisterCodeFixesAsync(CodeFixContext context)\n    {\n        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return;\n        }\n        var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);\n        if (semanticModel is null)\n        {\n            return;\n        }\n\n        foreach (var diagnostic in context.Diagnostics)\n        {\n            var (classDeclarationNode, contractTypeNode) = FindClassDeclarationNodeFromDiagnostic(root, diagnostic);\n            if (classDeclarationNode is not null && contractTypeNode is not null)\n            {\n                var (_, namedValues) = IpcAttributeHelper.TryFindIpcShapeAttributes(semanticModel, classDeclarationNode).FirstOrDefault();\n                if (namedValues.IpcType is { } ipcType)\n                {\n                    if (ipcType.AllInterfaces.Length is 0)\n                    {\n                        // 没有实现任何接口，此修改器无法给出任何建议。\n                        continue;\n                    }\n\n                    foreach (var @interface in ipcType.AllInterfaces)\n                    {\n                        var fix = string.Format(Localizations.IPC161_Fix1, @interface.Name);\n                        context.RegisterCodeFix(\n                            CodeAction.Create(\n                                title: fix,\n                                createChangedDocument: c => ChangeContractType(context.Document, contractTypeNode, @interface, c),\n                                equivalenceKey: fix),\n                            diagnostic);\n                    }\n                }\n            }\n        }\n    }\n\n    private async Task<Document> ChangeContractType(Document document,\n        TypeSyntax contractTypeNode, INamedTypeSymbol interfaceSymbol,\n        CancellationToken cancellationToken)\n    {\n        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return document;\n        }\n\n        var newContractTypeNode = SF.ParseTypeName(interfaceSymbol.Name);\n        var newRoot = root.ReplaceNodeWithUsings(\n            contractTypeNode, newContractTypeNode,\n            interfaceSymbol);\n\n        return document.WithSyntaxRoot(newRoot);\n    }\n\n    private (ClassDeclarationSyntax? classDeclarationNode, TypeSyntax? typeNode) FindClassDeclarationNodeFromDiagnostic(\n        SyntaxNode root, Diagnostic diagnostic)\n    {\n        var diagnosticSpan = diagnostic.Location.SourceSpan;\n        if (root.FindNode(diagnosticSpan) is TypeSyntax typeNode\n            && typeNode.Parent is TypeOfExpressionSyntax typeOfExpressionNode\n            && typeOfExpressionNode.Parent is AttributeArgumentSyntax attributeArgumentNode\n            && attributeArgumentNode.Parent is AttributeArgumentListSyntax attributeArgumentListNode\n            && attributeArgumentListNode.Parent is AttributeSyntax attributeNode\n            && attributeNode.Parent is AttributeListSyntax attributeListNode\n            && attributeListNode.Parent is ClassDeclarationSyntax classDeclarationNode1)\n        {\n            return (classDeclarationNode1, typeNode);\n        }\n        return (null, null);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeFixes/DefaultReturnDependsOnIgnoresIpcExceptionCodeFixProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Properties;\nusing Microsoft.CodeAnalysis.CodeActions;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;\n\nnamespace dotnetCampus.Ipc.CodeFixes;\n\n[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IgnoresIpcExceptionIsRecommendedCodeFixProvider)), Shared]\npublic class DefaultReturnDependsOnIgnoresIpcExceptionCodeFixProvider : CodeFixProvider\n{\n    public DefaultReturnDependsOnIgnoresIpcExceptionCodeFixProvider()\n    {\n        FixableDiagnosticIds = ImmutableArray.Create(IPC242_IpcProperty_DefaultReturnDependsOnIgnoresIpcException.Id);\n    }\n\n    public override ImmutableArray<string> FixableDiagnosticIds { get; }\n\n    public override FixAllProvider? GetFixAllProvider()\n    {\n        // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers\n        return WellKnownFixAllProviders.BatchFixer;\n    }\n\n    public override async Task RegisterCodeFixesAsync(CodeFixContext context)\n    {\n        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return;\n        }\n\n        foreach (var diagnostic in context.Diagnostics)\n        {\n            var diagnosticSpan = diagnostic.Location.SourceSpan;\n            if (root.FindNode(diagnosticSpan) is AttributeArgumentSyntax argumentNode\n                && argumentNode.Parent?.Parent is AttributeSyntax attributeNode)\n            {\n                context.RegisterCodeFix(\n                    CodeAction.Create(\n                        title: Localizations.IPC242_Fix1,\n                        createChangedDocument: c => RemoveDefaultReturn(context.Document, attributeNode, c),\n                        equivalenceKey: Localizations.IPC242_Fix1),\n                    diagnostic);\n                context.RegisterCodeFix(\n                    CodeAction.Create(\n                        title: Localizations.IPC242_Fix2,\n                        createChangedDocument: c => SetIgnoresIpcException(context.Document, attributeNode, c),\n                        equivalenceKey: Localizations.IPC242_Fix2),\n                    diagnostic);\n            }\n        }\n    }\n\n    private async Task<Document> RemoveDefaultReturn(Document document, AttributeSyntax attributeNode, CancellationToken cancellationToken)\n    {\n        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);\n        if (root is null || attributeNode.ArgumentList is null)\n        {\n            return document;\n        }\n\n        if (attributeNode.ArgumentList.Arguments.Count <= 1)\n        {\n            // 只设了这一个属性。\n            var newAttributeNode = attributeNode.RemoveNode(attributeNode.ArgumentList, SyntaxRemoveOptions.KeepNoTrivia);\n            if (newAttributeNode is not null)\n            {\n                var newRoot = root.ReplaceNode(attributeNode, newAttributeNode);\n                return document.WithSyntaxRoot(newRoot);\n            }\n        }\n        else\n        {\n            // 还设了其他属性。\n            var argumentNode = attributeNode.ArgumentList.Arguments.FirstOrDefault(x =>\n                x.NameEquals?.Name.ToString() == nameof(IpcMethodAttribute.DefaultReturn));\n\n            var newAttributeNode = argumentNode is null\n                ? null\n                : attributeNode.ArgumentList.RemoveNode(argumentNode, SyntaxRemoveOptions.KeepNoTrivia);\n\n            if (newAttributeNode is not null)\n            {\n                var newRoot = root.ReplaceNode(attributeNode.ArgumentList, newAttributeNode);\n                return document.WithSyntaxRoot(newRoot);\n            }\n        }\n\n        return document;\n    }\n\n    private async Task<Document> SetIgnoresIpcException(Document document, AttributeSyntax syntax, CancellationToken cancellationToken)\n    {\n        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);\n        if (root is null || syntax.ArgumentList is null)\n        {\n            return document;\n        }\n\n        var argumentNode = syntax.ArgumentList.Arguments.FirstOrDefault(x =>\n            x.NameEquals?.Name.ToString() == nameof(IpcMemberAttribute.IgnoresIpcException));\n\n        var newAttributeNode = argumentNode is null\n            ? syntax.ArgumentList.AddArguments(\n                // IgnoresIpcException = true/false\n                SF.AttributeArgument(\n                    SF.NameEquals(\n                        SF.IdentifierName(nameof(IpcPublicAttribute.IgnoresIpcException))),\n                    null,\n                    SF.LiteralExpression(SyntaxKind.TrueLiteralExpression)))\n            : syntax.ArgumentList.RemoveNode(argumentNode, SyntaxRemoveOptions.KeepNoTrivia);\n\n        if (newAttributeNode is not null)\n        {\n            var newRoot = root.ReplaceNode(syntax.ArgumentList, newAttributeNode);\n            return document.WithSyntaxRoot(newRoot);\n        }\n\n        return document;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeFixes/EmptyIpcMemberAttributeIsUnnecessaryCodeFixProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Properties;\nusing Microsoft.CodeAnalysis.CodeActions;\nusing Microsoft.CodeAnalysis.CodeFixes;\n\nnamespace dotnetCampus.Ipc.CodeFixes;\n\n[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IgnoresIpcExceptionIsRecommendedCodeFixProvider)), Shared]\npublic class EmptyIpcMemberAttributeIsUnnecessaryCodeFixProvider : CodeFixProvider\n{\n    public EmptyIpcMemberAttributeIsUnnecessaryCodeFixProvider()\n    {\n        FixableDiagnosticIds = ImmutableArray.Create(IPC201_IpcMember_EmptyIpcMemberAttributeIsUnnecessary.Id);\n    }\n\n    public override ImmutableArray<string> FixableDiagnosticIds { get; }\n\n    public override FixAllProvider? GetFixAllProvider()\n    {\n        // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers\n        return WellKnownFixAllProviders.BatchFixer;\n    }\n\n    public override async Task RegisterCodeFixesAsync(CodeFixContext context)\n    {\n        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return;\n        }\n\n        foreach (var diagnostic in context.Diagnostics)\n        {\n            var diagnosticSpan = diagnostic.Location.SourceSpan;\n            var node = root.FindNode(diagnosticSpan);\n            if (node is AttributeSyntax attributeNode)\n            {\n                var fix = string.Format(Localizations.IPC201_Fix, attributeNode.Name);\n                context.RegisterCodeFix(\n                    CodeAction.Create(\n                        title: fix,\n                        createChangedDocument: c => RemoveAttribute(context.Document, attributeNode, c),\n                        equivalenceKey: fix),\n                    diagnostic);\n            }\n            else if (node is AttributeListSyntax attributeListNode)\n            {\n                var fix = string.Format(Localizations.IPC201_Fix, attributeListNode.Attributes[0].Name);\n                context.RegisterCodeFix(\n                    CodeAction.Create(\n                        title: fix,\n                        createChangedDocument: c => RemoveAttribute(context.Document, attributeListNode, c),\n                        equivalenceKey: fix),\n                    diagnostic);\n            }\n        }\n    }\n\n    private async Task<Document> RemoveAttribute(Document document, SyntaxNode syntax, CancellationToken cancellationToken)\n    {\n        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);\n        if (root is null || syntax.Parent is null)\n        {\n            return document;\n        }\n\n        var newAttributeNode = syntax.Parent.RemoveNode(syntax, SyntaxRemoveOptions.KeepUnbalancedDirectives);\n\n        if (newAttributeNode is not null)\n        {\n            var newRoot = root.ReplaceNode(syntax.Parent, newAttributeNode);\n            return document.WithSyntaxRoot(newRoot);\n        }\n\n        return document;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeFixes/IgnoresIpcExceptionIsRecommendedCodeFixProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Properties;\nusing Microsoft.CodeAnalysis.CodeActions;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;\n\nnamespace dotnetCampus.Ipc.CodeFixes;\n\n[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IgnoresIpcExceptionIsRecommendedCodeFixProvider)), Shared]\npublic class IgnoresIpcExceptionIsRecommendedCodeFixProvider : CodeFixProvider\n{\n    public IgnoresIpcExceptionIsRecommendedCodeFixProvider()\n    {\n        FixableDiagnosticIds = ImmutableArray.Create(IPC131_IpcMembers_IgnoresIpcExceptionIsRecommended.Id);\n    }\n\n    public override ImmutableArray<string> FixableDiagnosticIds { get; }\n\n    public override FixAllProvider? GetFixAllProvider()\n    {\n        // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers\n        return WellKnownFixAllProviders.BatchFixer;\n    }\n\n    public override async Task RegisterCodeFixesAsync(CodeFixContext context)\n    {\n        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return;\n        }\n\n        foreach (var diagnostic in context.Diagnostics)\n        {\n            var diagnosticSpan = diagnostic.Location.SourceSpan;\n            if (root.FindNode(diagnosticSpan) is AttributeSyntax attributeNode)\n            {\n                context.RegisterCodeFix(\n                    CodeAction.Create(\n                        title: Localizations.IPC131_Fix1,\n                        createChangedDocument: c => SetIgnoresIpcException(context.Document, attributeNode, true, c),\n                        equivalenceKey: Localizations.IPC131_Fix1),\n                    diagnostic);\n                context.RegisterCodeFix(\n                    CodeAction.Create(\n                        title: Localizations.IPC131_Fix2,\n                        createChangedDocument: c => SetIgnoresIpcException(context.Document, attributeNode, false, c),\n                        equivalenceKey: Localizations.IPC131_Fix2),\n                    diagnostic);\n            }\n        }\n    }\n\n    private async Task<Document> SetIgnoresIpcException(Document document, AttributeSyntax attributeNode, bool value, CancellationToken cancellationToken)\n    {\n        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return document;\n        }\n\n        var newArgumentNode = SF.AttributeArgument(\n            SF.NameEquals(\n                SF.IdentifierName(nameof(IpcPublicAttribute.IgnoresIpcException))),\n            null,\n            SF.LiteralExpression(value ? SyntaxKind.TrueLiteralExpression : SyntaxKind.FalseLiteralExpression));\n\n        if (attributeNode.ArgumentList is { } argumentList)\n        {\n            var newArgumentListNode = argumentList.AddArguments(newArgumentNode);\n            var newRoot = root.ReplaceNode(argumentList, newArgumentListNode);\n            return document.WithSyntaxRoot(newRoot);\n        }\n        else\n        {\n            var newAttributeNode = attributeNode.AddArgumentListArguments(newArgumentNode);\n            var newRoot = root.ReplaceNode(attributeNode, newAttributeNode);\n            return document.WithSyntaxRoot(newRoot);\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/CodeFixes/LetClassImplementInterfaceCodeFixProvider.cs",
    "content": "﻿using dotnetCampus.Ipc.Analyzers.Compiling;\nusing dotnetCampus.Ipc.Properties;\nusing Microsoft.CodeAnalysis.CodeActions;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;\n\nnamespace dotnetCampus.Ipc.CodeFixes;\n\n[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(IgnoresIpcExceptionIsRecommendedCodeFixProvider)), Shared]\npublic class LetClassImplementInterfaceCodeFixProvider : CodeFixProvider\n{\n    public LetClassImplementInterfaceCodeFixProvider()\n    {\n        FixableDiagnosticIds = ImmutableArray.Create(IPC161_IpcShape_ContractTypeDismatchWithInterface.Id);\n    }\n\n    public override ImmutableArray<string> FixableDiagnosticIds { get; }\n\n    public override FixAllProvider? GetFixAllProvider()\n    {\n        // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers\n        return WellKnownFixAllProviders.BatchFixer;\n    }\n\n    public override async Task RegisterCodeFixesAsync(CodeFixContext context)\n    {\n        var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return;\n        }\n        var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);\n        if (semanticModel is null)\n        {\n            return;\n        }\n\n        foreach (var diagnostic in context.Diagnostics)\n        {\n            if (FindClassDeclarationSyntaxFromDiagnostic(root, diagnostic) is { } classDeclarationNode)\n            {\n                var (_, namedValues) = IpcAttributeHelper.TryFindIpcShapeAttributes(semanticModel, classDeclarationNode).FirstOrDefault();\n                if (namedValues.IpcType is { } realType && namedValues.ContractType is { } contractType)\n                {\n                    var fix = string.Format(Localizations.IPC161_Fix2, realType.Name, contractType.Name);\n                    context.RegisterCodeFix(\n                        CodeAction.Create(\n                            title: fix,\n                            createChangedDocument: c => ImplementInterface(context.Document, classDeclarationNode, contractType, c),\n                            equivalenceKey: fix),\n                        diagnostic);\n                }\n            }\n        }\n    }\n\n    private async Task<Document> ImplementInterface(Document document,\n        ClassDeclarationSyntax classDeclarationNode, INamedTypeSymbol interfaceSymbol, CancellationToken cancellationToken)\n    {\n        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);\n        if (root is null)\n        {\n            return document;\n        }\n\n        var newClassDeclarationNode = classDeclarationNode.AddBaseListTypes(SF.SimpleBaseType(SF.ParseTypeName(interfaceSymbol.Name)));\n        var newRoot = root.ReplaceNodeWithUsings(\n            classDeclarationNode, newClassDeclarationNode,\n            interfaceSymbol);\n        return document.WithSyntaxRoot(newRoot);\n    }\n\n    private ClassDeclarationSyntax? FindClassDeclarationSyntaxFromDiagnostic(SyntaxNode root, Diagnostic diagnostic)\n    {\n        var diagnosticSpan = diagnostic.Location.SourceSpan;\n        if (root.FindNode(diagnosticSpan) is TypeSyntax typeNode\n            && typeNode.Parent is TypeOfExpressionSyntax typeOfExpressionNode\n            && typeOfExpressionNode.Parent is AttributeArgumentSyntax attributeArgumentNode\n            && attributeArgumentNode.Parent is AttributeArgumentListSyntax attributeArgumentListNode\n            && attributeArgumentListNode.Parent is AttributeSyntax attributeNode\n            && attributeNode.Parent is AttributeListSyntax attributeListNode\n            && attributeListNode.Parent is ClassDeclarationSyntax classDeclarationNode1)\n        {\n            return classDeclarationNode1;\n        }\n        else if (root.FindNode(diagnosticSpan) is ClassDeclarationSyntax classDeclarationNode2)\n        {\n            return classDeclarationNode2;\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Core/ComponentModels/Assignable.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Core.ComponentModels;\n\n/// <summary>\n/// 表示一个可被赋值的量，对于值类型，含已赋值和未赋值两种状态；对于引用类型，含已赋值为 null、已赋值为非 null 和未赋值三种状态。\n/// <para>你不应该直接使用这个类型本身，而是应该结合 Nullable 一起用，即 Assignable&lt;T&gt;?。其中 null 表示未赋值过，而非 null 表示已赋值过。</para>\n/// </summary>\n/// <typeparam name=\"T\">值类型或引用类型。</typeparam>\ninternal readonly struct Assignable<T>\n{\n    public Assignable()\n    {\n        Value = default;\n    }\n\n    public Assignable(T? value)\n    {\n        Value = value;\n    }\n\n    public T? Value { get; }\n\n    public static explicit operator Assignable<T>?(T value)\n    {\n        return new Assignable<T>(value);\n    }\n\n    public static implicit operator T?(Assignable<T>? assignable)\n    {\n        return assignable is null ? default : assignable.Value.Value;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/IpcPublicCompilation.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Generators.Compiling;\n/// <summary>\n/// 提供 IPC 对象（契约接口）的语法和语义分析。\n/// </summary>\n[DebuggerDisplay(\"IpcPublic : {IpcType.Name,nq}\")]\ninternal class IpcPublicCompilation : IEquatable<IpcPublicCompilation?>\n{\n    /// <summary>\n    /// IPC 对象文件的编译信息。\n    /// </summary>\n    private readonly CompilationUnitSyntax _compilationUnitSyntax;\n\n    /// <summary>\n    /// 创建 IPC 对象的语法和语义分析。\n    /// </summary>\n    /// <param name=\"syntaxTree\">IPC 对象所在整个文件的语法树。</param>\n    /// <param name=\"semanticModel\">语义模型。</param>\n    /// <param name=\"ipcType\">IPC 对象的语义符号。</param>\n    public IpcPublicCompilation(SyntaxTree syntaxTree, SemanticModel semanticModel, INamedTypeSymbol ipcType)\n    {\n        _compilationUnitSyntax = syntaxTree.GetCompilationUnitRoot();\n        SemanticModel = semanticModel ?? throw new ArgumentNullException(nameof(semanticModel));\n        IpcType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n    }\n\n    /// <summary>\n    /// 整个项目的语义模型。\n    /// </summary>\n    public SemanticModel SemanticModel { get; }\n\n    /// <summary>\n    /// IPC 对象（即标记了 <see cref=\"IpcPublicAttribute\"/> 的接口类型）的语义符号。\n    /// </summary>\n    public INamedTypeSymbol IpcType { get; }\n\n    /// <summary>\n    /// 获取 IPC 对象所在文件的全部 using。\n    /// </summary>\n    /// <returns>全部 using 组成的字符串。</returns>\n    public string GetUsing()\n    {\n        var usingsSyntax = _compilationUnitSyntax.Usings;\n        var usings = string.Join(\"\\r\\n\", usingsSyntax.Select(x => x.ToString()));\n        return usings;\n    }\n\n    /// <summary>\n    /// 获取 IPC 对象文件的命名空间。\n    /// </summary>\n    /// <returns>IPC 真实类型的命名空间</returns>\n    public string GetNamespace()\n    {\n        return IpcType.ContainingNamespace.ToString();\n    }\n\n    /// <summary>\n    /// 查找 IPC 对象的所有成员。\n    /// </summary>\n    /// <returns>所有成员信息。</returns>\n    public virtual IEnumerable<(INamedTypeSymbol IpcType, ISymbol Member)> EnumerateMembers()\n    {\n        var members = IpcType.AllInterfaces.SelectMany(x => x.GetMembers())\n            .Concat(IpcType.GetMembers());\n        foreach (var member in members)\n        {\n            if (member is IMethodSymbol method && method.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet)\n            {\n                // 属性内的方法忽略。\n                continue;\n            }\n\n            if (member is INamedTypeSymbol)\n            {\n                // 接口中如果定义有其他嵌套类型/枚举/接口/结构，也忽略。\n                continue;\n            }\n\n            if (member is IEventSymbol eventSymbol)\n            {\n                // IPC 不支持事件。\n                var eventSyntax = eventSymbol.TryGetMemberDeclaration();\n                throw new DiagnosticException(\n                    IPCTMP1_IpcMembers_EventIsNotSupported,\n                    eventSyntax?.GetLocation(),\n                    eventSymbol.Name);\n            }\n\n            yield return new(IpcType, member);\n        }\n    }\n\n    /// <summary>\n    /// 试图解析一个接口定义语法节点并创建 IPC 对象（契约接口）。\n    /// </summary>\n    /// <param name=\"syntaxNode\">接口定义语法节点。</param>\n    /// <param name=\"semanticModel\">此接口定义语法节点的语义模型。</param>\n    /// <param name=\"ipcPublicCompilation\">如果找到了 IPC 对象，则此参数为此语法树中的所有 IPC 对象；如果没有找到，则为空集合。</param>\n    /// <returns>如果找到了 IPC 类型，则返回 true；如果没有找到，则返回 false。</returns>\n    public static bool TryCreateIpcPublicCompilation(InterfaceDeclarationSyntax syntaxNode, SemanticModel semanticModel,\n        [NotNullWhen(true)] out IpcPublicCompilation? ipcPublicCompilation)\n    {\n        var syntaxTree = syntaxNode.SyntaxTree;\n        if (semanticModel.GetDeclaredSymbol(syntaxNode) is { } typeSymbol\n            && typeSymbol.GetAttributes().FirstOrDefault(x => string.Equals(\n                x.AttributeClass?.ToString(),\n                typeof(IpcPublicAttribute).FullName,\n                StringComparison.Ordinal)) is { } ipcPublicAttribute)\n        {\n            ipcPublicCompilation = new IpcPublicCompilation(syntaxTree, semanticModel, typeSymbol);\n            return true;\n        }\n\n        ipcPublicCompilation = null;\n        return false;\n    }\n\n    /// <summary>\n    /// 在一个语法树（单个文件）中查找所有的 IPC 对象（契约接口）。\n    /// </summary>\n    /// <param name=\"compilation\">整个项目的编译信息。</param>\n    /// <param name=\"syntaxTree\">单个文件的语法树。</param>\n    /// <param name=\"publicIpcObjectCompilations\">如果找到了 IPC 对象，则此参数为此语法树中的所有 IPC 对象；如果没有找到，则为空集合。</param>\n    /// <returns>如果找到了 IPC 类型，则返回 true；如果没有找到，则返回 false。</returns>\n    public static bool TryFindIpcPublicCompilations(Compilation compilation, SyntaxTree syntaxTree,\n        out IReadOnlyList<IpcPublicCompilation> publicIpcObjectCompilations)\n    {\n        var result = new List<IpcPublicCompilation>();\n        var typeDeclarationSyntaxes = from node in syntaxTree.GetRoot().DescendantNodes()\n                                      where node.IsKind(SyntaxKind.InterfaceDeclaration)\n                                      select (InterfaceDeclarationSyntax) node;\n\n        var semanticModel = compilation.GetSemanticModel(syntaxTree);\n        foreach (var typeDeclarationSyntax in typeDeclarationSyntaxes)\n        {\n            if (TryCreateIpcPublicCompilation(typeDeclarationSyntax, semanticModel, out var publicIpcObjectCompilation))\n            {\n                result.Add(publicIpcObjectCompilation);\n            }\n        }\n\n        publicIpcObjectCompilations = result;\n        return publicIpcObjectCompilations.Count > 0;\n    }\n\n    public override bool Equals(object? obj)\n    {\n        return Equals(obj as IpcPublicCompilation);\n    }\n\n    public bool Equals(IpcPublicCompilation? other)\n    {\n        return other is not null &&\n               SymbolEqualityComparer.Default.Equals(IpcType, other.IpcType); ;\n    }\n\n    public override int GetHashCode()\n    {\n        return 1998130605 + EqualityComparer<INamedTypeSymbol>.Default.GetHashCode(IpcType);\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/IpcPublicMemberProxyJointGenerator.cs",
    "content": "﻿using dotnetCampus.Ipc.Generators.Compiling.Members;\n\nnamespace dotnetCampus.Ipc.Generators.Compiling;\n\n/// <summary>\n/// 辅助生成契约接口中每一个成员对应的 IPC 代理和对接。\n/// </summary>\ninternal class IpcPublicMemberProxyJointGenerator\n{\n    private readonly IPublicIpcObjectProxyMemberGenerator _proxyMemberGenerator;\n    private readonly IPublicIpcObjectShapeMemberGenerator _shapeMemberGenerator;\n    private readonly IPublicIpcObjectJointMatchGenerator _jointMatchGenerator;\n\n    /// <summary>\n    /// 创建 IPC 对象的其中一个成员信息。\n    /// </summary>\n    /// <param name=\"ipcType\">IPC 类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）的语义符号。（可能与 <paramref name=\"ipcType\"/> 是相同类型。）</param>\n    /// <param name=\"member\"><paramref name=\"ipcType\"/> 中成员的语义符号。</param>\n    public IpcPublicMemberProxyJointGenerator(INamedTypeSymbol ipcType, ISymbol member)\n    {\n        ipcType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n        member = member ?? throw new ArgumentNullException(nameof(member));\n\n        _proxyMemberGenerator = member switch\n        {\n            IMethodSymbol methodSymbol => new IpcPublicMethodInfo(ipcType, ipcType, methodSymbol, methodSymbol),\n            IPropertySymbol propertySymbol => new IpcPublicPropertyInfo(ipcType, ipcType, propertySymbol, propertySymbol),\n            _ => throw new DiagnosticException(\n                IPC200_IpcMembers_OnlyPropertiesMethodsAndEventsAreSupported,\n                member.Locations.FirstOrDefault(),\n                member.Name),\n        };\n        _shapeMemberGenerator = (IPublicIpcObjectShapeMemberGenerator) _proxyMemberGenerator;\n        _jointMatchGenerator = (IPublicIpcObjectJointMatchGenerator) _proxyMemberGenerator;\n    }\n\n    /// <summary>\n    /// 创建 IPC 对象的其中一个成员信息。\n    /// </summary>\n    /// <param name=\"contractType\">契约接口类型的语义符号。</param>\n    /// <param name=\"ipcType\">IPC 类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）的语义符号。（可能与 <paramref name=\"contractType\"/> 是相同类型。）</param>\n    /// <param name=\"contractMember\">此成员原始定义（即在 <paramref name=\"contractType\"/> 中所定义的方法）的语义符号。</param>\n    /// <param name=\"ipcMember\">标记了 <see cref=\"IpcMemberAttribute\"/> 的此成员实现的语义符号。（可能与 <paramref name=\"contractMember\"/> 是相同实例。）</param>\n    public IpcPublicMemberProxyJointGenerator(INamedTypeSymbol contractType, INamedTypeSymbol ipcType, ISymbol contractMember, ISymbol ipcMember)\n    {\n        contractType = contractType ?? throw new ArgumentNullException(nameof(contractType));\n        contractMember = contractMember ?? throw new ArgumentNullException(nameof(contractMember));\n\n        _proxyMemberGenerator = contractMember switch\n        {\n            IMethodSymbol methodSymbol => new IpcPublicMethodInfo(contractType, ipcType, methodSymbol, (IMethodSymbol) ipcMember),\n            IPropertySymbol propertySymbol => new IpcPublicPropertyInfo(contractType, ipcType, propertySymbol, (IPropertySymbol) ipcMember),\n            _ => throw new DiagnosticException(\n                IPC200_IpcMembers_OnlyPropertiesMethodsAndEventsAreSupported,\n                contractMember.Locations.FirstOrDefault(),\n                contractMember.Name),\n        };\n        _shapeMemberGenerator = (IPublicIpcObjectShapeMemberGenerator) _proxyMemberGenerator;\n        _jointMatchGenerator = (IPublicIpcObjectJointMatchGenerator) _proxyMemberGenerator;\n    }\n\n    /// <summary>\n    /// 生成此成员在 IPC 代理中的源代码。\n    /// </summary>\n    /// <returns>成员源代码。</returns>\n    public string GenerateProxyMember() => _proxyMemberGenerator.GenerateProxyMember();\n\n    /// <summary>\n    /// 生成此成员在 IPC 形状代理中的源代码。\n    /// </summary>\n    /// <returns>成员源代码。</returns>\n    internal string GenerateShapeMember() => _shapeMemberGenerator.GenerateShapeMember();\n\n    /// <summary>\n    /// 生成此成员在 IPC 对接中的源代码。\n    /// </summary>\n    /// <param name=\"realInstanceVariableName\">IPC 对接方法中真实实例的实参名称。</param>\n    /// <returns>成员源代码。</returns>\n    public string GenerateJointMatch(string realInstanceVariableName) => _jointMatchGenerator.GenerateJointMatch(realInstanceVariableName);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/IpcShapeCompilation.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Generators.Compiling;\n/// <summary>\n/// 提供 IPC 对象（形状代理类）的语法和语义分析。\n/// </summary>\n[DebuggerDisplay(\"{ShapeType} : {ContractType.Name,nq}\")]\ninternal class IpcShapeCompilation : IpcPublicCompilation, IEquatable<IpcShapeCompilation?>\n{\n    /// <summary>\n    /// 创建 IPC 对象的语法和语义分析。\n    /// </summary>\n    /// <param name=\"syntaxTree\">IPC 对象所在整个文件的语法树。</param>\n    /// <param name=\"semanticModel\">语义模型。</param>\n    /// <param name=\"ipcType\">IPC 形状代理的语义符号。</param>\n    /// <param name=\"contractType\">IPC 契约类型的语义符号。</param>\n    public IpcShapeCompilation(SyntaxTree syntaxTree, SemanticModel semanticModel,\n        INamedTypeSymbol ipcType, INamedTypeSymbol contractType)\n        : base(syntaxTree, semanticModel, ipcType)\n    {\n        ContractType = contractType;\n    }\n\n    /// <summary>\n    /// IPC 契约类型的语义符号。\n    /// </summary>\n    public INamedTypeSymbol ContractType { get; }\n\n    /// <summary>\n    /// 查找 IPC 对象的所有成员。\n    /// </summary>\n    /// <returns>所有成员信息。</returns>\n    public IEnumerable<(INamedTypeSymbol contractType, INamedTypeSymbol shapeType, ISymbol member, ISymbol shapeMember)> EnumerateMembersByContractType()\n    {\n        var members = ContractType.AllInterfaces.SelectMany(x => x.GetMembers())\n            .Concat(ContractType.GetMembers());\n        foreach (var member in members)\n        {\n            if (member is IMethodSymbol method && method.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet)\n            {\n                // 属性内的方法忽略。\n                continue;\n            }\n\n            if (member is INamedTypeSymbol)\n            {\n                // 接口中如果定义有其他嵌套类型/枚举/接口/结构，也忽略。\n                continue;\n            }\n\n            if (member is IEventSymbol eventSymbol)\n            {\n                // IPC 不支持事件。\n                var eventSyntax = eventSymbol.TryGetMemberDeclaration();\n                throw new DiagnosticException(\n                    IPCTMP1_IpcMembers_EventIsNotSupported,\n                    eventSyntax?.GetLocation(),\n                    IpcType.Name,\n                    ContractType.Name);\n            }\n\n            if (IpcType.FindImplementationForInterfaceMember(member) is ISymbol implementationMember)\n            {\n                yield return new(ContractType, IpcType, member, implementationMember);\n            }\n            else\n            {\n                var attribute = IpcType.TryGetClassDeclarationWithIpcAttribute(SemanticModel);\n                throw new DiagnosticException(\n                    IPC161_IpcShape_ContractTypeDismatchWithInterface,\n                    attribute?.ArgumentList?.Arguments.FirstOrDefault()?.GetLocation(),\n                    IpcType.Name,\n                    ContractType.Name);\n            }\n        }\n    }\n\n    /// <summary>\n    /// 试图解析一个类型定义语法节点并创建 IPC 对象（契约接口）。\n    /// </summary>\n    /// <param name=\"syntaxNode\">类型定义语法节点。</param>\n    /// <param name=\"semanticModel\">此类型定义语法节点的语义模型。</param>\n    /// <param name=\"ipcShapeCompilation\">如果找到了 IPC 对象，则此参数为此语法树中的所有 IPC 对象；如果没有找到，则为空集合。</param>\n    /// <returns>如果找到了 IPC 类型，则返回 true；如果没有找到，则返回 false。</returns>\n    public static bool TryCreateIpcShapeCompilation(ClassDeclarationSyntax syntaxNode, SemanticModel semanticModel,\n        [NotNullWhen(true)] out IpcShapeCompilation? ipcShapeCompilation)\n    {\n        var syntaxTree = syntaxNode.SyntaxTree;\n        if (semanticModel.GetDeclaredSymbol(syntaxNode) is { } typeSymbol\n            && typeSymbol.GetAttributes().FirstOrDefault(x => string.Equals(\n                 x.AttributeClass?.ToString(),\n                 typeof(IpcShapeAttribute).FullName,\n                 StringComparison.Ordinal)) is { } ipcPublicAttribute)\n        {\n            if (ipcPublicAttribute.ConstructorArguments.Length == 1)\n            {\n                if (ipcPublicAttribute.ConstructorArguments[0] is TypedConstant typedConstant\n                    && typedConstant.Value is INamedTypeSymbol contractType)\n                {\n                    if (contractType.TypeKind == TypeKind.Interface)\n                    {\n                        ipcShapeCompilation = new IpcShapeCompilation(syntaxTree, semanticModel, typeSymbol, contractType);\n                        return true;\n                    }\n                    else\n                    {\n                        throw new DiagnosticException(IPC002_KnownDiagnosticError);\n                    }\n                }\n                else\n                {\n                    throw new DiagnosticException(IPC001_KnownCompilerError);\n                }\n            }\n            else\n            {\n                throw new DiagnosticException(IPC001_KnownCompilerError);\n            }\n        }\n\n        ipcShapeCompilation = null;\n        return false;\n    }\n\n    /// <summary>\n    /// 在一个语法树（单个文件）中查找所有的 IPC 对象（契约接口）。\n    /// </summary>\n    /// <param name=\"compilation\">整个项目的编译信息。</param>\n    /// <param name=\"syntaxTree\">单个文件的语法树。</param>\n    /// <param name=\"publicIpcObjectCompilations\">如果找到了 IPC 对象，则此参数为此语法树中的所有 IPC 对象；如果没有找到，则为空集合。</param>\n    /// <returns>如果找到了 IPC 类型，则返回 true；如果没有找到，则返回 false。</returns>\n    public static bool TryFindIpcShapeCompilations(Compilation compilation, SyntaxTree syntaxTree,\n        out IReadOnlyList<IpcShapeCompilation> publicIpcObjectCompilations)\n    {\n        var result = new List<IpcShapeCompilation>();\n        var typeDeclarationSyntaxes = from node in syntaxTree.GetRoot().DescendantNodes()\n                                      where node.IsKind(SyntaxKind.ClassDeclaration)\n                                      select (ClassDeclarationSyntax) node;\n\n        var semanticModel = compilation.GetSemanticModel(syntaxTree);\n        foreach (var typeDeclarationSyntax in typeDeclarationSyntaxes)\n        {\n            if (TryCreateIpcShapeCompilation(typeDeclarationSyntax, semanticModel, out var publicIpcObjectCompilation))\n            {\n                result.Add(publicIpcObjectCompilation);\n            }\n        }\n\n        publicIpcObjectCompilations = result;\n        return publicIpcObjectCompilations.Count > 0;\n    }\n\n    public override bool Equals(object? obj)\n    {\n        return Equals(obj as IpcShapeCompilation);\n    }\n\n    public bool Equals(IpcShapeCompilation? other)\n    {\n        return other is not null &&\n               SymbolEqualityComparer.Default.Equals(IpcType, other.IpcType) &&\n               SymbolEqualityComparer.Default.Equals(ContractType, other.ContractType);\n    }\n\n    public override int GetHashCode()\n    {\n        var hashCode = -1723556882;\n        hashCode = hashCode * -1521134295 + base.GetHashCode();\n        hashCode = hashCode * -1521134295 + EqualityComparer<INamedTypeSymbol>.Default.GetHashCode(IpcType);\n        hashCode = hashCode * -1521134295 + EqualityComparer<INamedTypeSymbol>.Default.GetHashCode(ContractType);\n        return hashCode;\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/Members/IPublicIpcObjectMemberGenerator.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Generators.Compiling.Members;\n\ninternal interface IPublicIpcObjectProxyMemberGenerator\n{\n    string GenerateProxyMember();\n}\n\ninternal interface IPublicIpcObjectShapeMemberGenerator\n{\n    string GenerateShapeMember();\n}\n\ninternal interface IPublicIpcObjectJointMatchGenerator\n{\n    string GenerateJointMatch(string real);\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/Members/IpcPublicMethodInfo.cs",
    "content": "﻿using dotnetCampus.Ipc.Generators.Builders;\n\nnamespace dotnetCampus.Ipc.Generators.Compiling.Members;\n\ninternal class IpcPublicMethodInfo : IPublicIpcObjectProxyMemberGenerator, IPublicIpcObjectShapeMemberGenerator, IPublicIpcObjectJointMatchGenerator\n{\n    /// <summary>\n    /// 契约接口类型的语义符号。\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取接口定义相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly INamedTypeSymbol _contractType;\n\n    /// <summary>\n    /// IPC 类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）的语义符号。（可能与 <see cref=\"_ipcType\"/> 是相同类型。）\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取接口特性（<see cref=\"Attribute\"/>）相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly INamedTypeSymbol _ipcType;\n\n    /// <summary>\n    /// 此方法原始定义（即在 <see cref=\"_contractType\"/> 中所定义的方法）的语义符号。\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取方法签名相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly IMethodSymbol _contractMethod;\n\n    /// <summary>\n    /// 标记了 <see cref=\"IpcMemberAttribute\"/> 的此方法实现的语义符号。（可能与 <see cref=\"_contractMethod\"/> 是相同实例。）\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取方法特性（<see cref=\"Attribute\"/>）相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly IMethodSymbol _ipcMethod;\n\n    /// <summary>\n    /// 如果此成员是一个异步方法，则此值为 true；否则为 false。\n    /// </summary>\n    private readonly bool _isAsyncMethod;\n\n    /// <summary>\n    /// 创建 IPC 对象的其中一个成员信息。\n    /// </summary>\n    /// <param name=\"contractType\">契约接口类型的语义符号。</param>\n    /// <param name=\"ipcType\">IPC 类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）的语义符号。（可能与 <paramref name=\"contractType\"/> 是相同类型。）</param>\n    /// <param name=\"contractMethod\">此成员原始定义（即在 <paramref name=\"contractType\"/> 中所定义的方法）的语义符号。</param>\n    /// <param name=\"ipcMethod\">标记了 <see cref=\"IpcMemberAttribute\"/> 的此成员实现的语义符号。（可能与 <paramref name=\"contractMethod\"/> 是相同实例。）</param>\n    public IpcPublicMethodInfo(INamedTypeSymbol contractType, INamedTypeSymbol ipcType, IMethodSymbol contractMethod, IMethodSymbol ipcMethod)\n    {\n        _contractType = contractType ?? throw new ArgumentNullException(nameof(contractType));\n        _ipcType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n        _contractMethod = contractMethod ?? throw new ArgumentNullException(nameof(contractMethod));\n        _ipcMethod = ipcMethod ?? throw new ArgumentNullException(nameof(ipcMethod));\n        var returnType = contractMethod.ReturnType.OriginalDefinition.ToString();\n        _isAsyncMethod = returnType is \"System.Threading.Tasks.Task\" or \"System.Threading.Tasks.Task<TResult>\";\n    }\n\n    /// <summary>\n    /// 生成此方法在 IPC 代理中的源代码。\n    /// </summary>\n    /// <returns>方法源代码。</returns>\n    public string GenerateProxyMember()\n    {\n        var methodId = MemberIdGenerator.GenerateMethodId(_contractMethod);\n        var parameters = GenerateMethodParameters(_contractMethod.Parameters);\n        var arguments = GenerateGarmArguments(_contractMethod.Parameters) is { Length: > 0 } args ? $\"new global::dotnetCampus.Ipc.CompilerServices.GeneratedProxies.IGarmObject[] {{ {args} }}\" : \"null\";\n        var asyncReturnType = GetAsyncReturnType(_contractMethod.ReturnType);\n        var returnTypeName = asyncReturnType is null ? \"void\" : asyncReturnType.ToUsingString();\n        var methodContainingTypeName = _contractMethod.ContainingType.ToUsingString();\n        var isAsync = _isAsyncMethod;\n        var returnsVoid = _contractMethod.ReturnsVoid || asyncReturnType is null;\n        var namedValues = _ipcMethod.GetIpcNamedValues(asyncReturnType, _ipcType);\n\n        return (isAsync, returnsVoid) switch\n        {\n            // 异步 Task 方法。\n            (true, true) => $$\"\"\"\n                Task {{methodContainingTypeName}}.{{_contractMethod.Name}}({{parameters}})\n                {\n                    return CallMethodAsync({{methodId}}, {{arguments}}, {{namedValues.ToIndentString(\"    \")}} );\n                }\n                \"\"\",\n            // 异步 Task<T> 方法。\n            (true, _) => $$\"\"\"\n                Task<{{returnTypeName}}> {{methodContainingTypeName}}.{{_contractMethod.Name}}({{parameters}})\n                {\n                    return CallMethodAsync<{{returnTypeName}}>({{methodId}}, {{arguments}}, {{namedValues.ToIndentString(\"    \")}});\n                }\n                \"\"\",\n            // 同步 void 方法。\n            (false, true) => namedValues.WaitsVoid\n                ? $$\"\"\"\n                    void {{methodContainingTypeName}}.{{_contractMethod.Name}}({{parameters}})\n                    {\n                        CallMethod({{methodId}}, {{arguments}}, {{namedValues.ToIndentString(\"    \")}}).Wait();\n                    }\n                    \"\"\"\n                : $$\"\"\"\n                    void {{methodContainingTypeName}}.{{_contractMethod.Name}}({{parameters}})\n                    {\n                        _ = CallMethod({{methodId}}, {{arguments}}, {{namedValues.ToIndentString(\"    \")}});\n                    }\n                    \"\"\",\n            // 同步 T 方法。\n            (false, _) => $$\"\"\"\n                {{returnTypeName}} {{methodContainingTypeName}}.{{_contractMethod.Name}}({{parameters}})\n                {\n                    return CallMethod<{{returnTypeName}}>({{methodId}}, {{arguments}}, {{namedValues.ToIndentString(\"    \")}}).Result;\n                }\n                \"\"\",\n        };\n    }\n\n    /// <summary>\n    /// 生成此成员在 IPC 形状代理中的源代码。\n    /// </summary>\n    /// <returns>成员源代码。</returns>\n    public string GenerateShapeMember()\n    {\n        var parameters = GenerateMethodParameters(_contractMethod.Parameters);\n        var returnTypeName = _contractMethod.ReturnType.ToUsingString();\n        var methodContainingTypeName = _contractMethod.ContainingType.ToUsingString();\n        return $$\"\"\"\n            [IpcMethod]\n            {{returnTypeName}} {{methodContainingTypeName}}.{{_contractMethod.Name}}({{parameters}})\n            {\n                throw null;\n            }\n            \"\"\";\n    }\n\n    /// <summary>\n    /// 生成此方法在 IPC 对接中的源代码。\n    /// </summary>\n    /// <param name=\"real\">IPC 对接方法中真实实例的实参名称。</param>\n    /// <returns>方法源代码。</returns>\n    public string GenerateJointMatch(string real)\n    {\n        var methodId = MemberIdGenerator.GenerateMethodId(_contractMethod);\n        var containingTypeName = _contractMethod.ContainingType.ToUsingString();\n        var parameterTypes = GenerateMethodParameterTypes(_contractMethod.Parameters);\n        var arguments = GenerateMethodArguments(_contractMethod.Parameters);\n        var asyncReturnType = GetAsyncReturnType(_contractMethod.ReturnType);\n        var returnTypeName = asyncReturnType is null ? \"void\" : asyncReturnType.ToUsingString();\n        var isAsync = _isAsyncMethod;\n        var returnsVoid = _contractMethod.ReturnsVoid || asyncReturnType is null;\n\n        if (isAsync && returnsVoid)\n        {\n            // 异步 Task 方法。\n            var call = $\"{real}.{_contractMethod.Name}({arguments})\";\n            var sourceCode = string.IsNullOrWhiteSpace(arguments)\n                ? $\"MatchMethod({methodId}, new Func<Task>(() => {call}));\"\n                : $\"MatchMethod({methodId}, new Func<{parameterTypes}, Task>(({arguments}) => {call}));\";\n            return sourceCode;\n        }\n        else if (isAsync && !returnsVoid)\n        {\n            // 异步 Task<T> 方法。\n            var @return = $\"Task<Garm<{returnTypeName}>>\";\n            var call = GenerateGarmReturn(asyncReturnType!, $\"await {real}.{_contractMethod.Name}({arguments}).ConfigureAwait(false)\");\n            var sourceCode = string.IsNullOrWhiteSpace(arguments)\n                ? $\"MatchMethod({methodId}, new Func<{@return}>(async () => {call}));\"\n                : $\"MatchMethod({methodId}, new Func<{parameterTypes}, {@return}>(async ({arguments}) => {call}));\";\n            return sourceCode;\n        }\n        else if (!isAsync && returnsVoid)\n        {\n            // 同步 void 方法。\n            var call = $\"{real}.{_contractMethod.Name}({arguments})\";\n            var sourceCode = string.IsNullOrWhiteSpace(arguments)\n                ? $\"MatchMethod({methodId}, new Action(() => {call}));\"\n                : $\"MatchMethod({methodId}, new Action<{parameterTypes}>(({arguments}) => {call}));\";\n            return sourceCode;\n        }\n        else\n        {\n            // 同步 T 方法。\n            var @return = $\"Garm<{returnTypeName}>\";\n            var call = GenerateGarmReturn(_contractMethod.ReturnType, $\"{real}.{_contractMethod.Name}({arguments})\");\n            var sourceCode = string.IsNullOrWhiteSpace(arguments)\n                ? $\"MatchMethod({methodId}, new Func<{@return}>(() => {call}));\"\n                : $\"MatchMethod({methodId}, new Func<{parameterTypes}, {@return}>(({arguments}) => {call}));\";\n            return sourceCode;\n        }\n    }\n\n    /// <summary>\n    /// 根据参数列表生成方法形参列表字符串。\n    /// </summary>\n    /// <param name=\"parameters\">方法参数列表。</param>\n    /// <returns>方法形参列表字符串。</returns>\n    private string GenerateMethodParameters(ImmutableArray<IParameterSymbol> parameters)\n    {\n        return string.Join(\n            \", \",\n            parameters.Select(x => $\"{x.Type.ToUsingString()} {x.Name}\"));\n    }\n\n    /// <summary>\n    /// 根据参数列表生成方法参数类型列表字符串。\n    /// </summary>\n    /// <param name=\"parameters\">方法参数列表。</param>\n    /// <returns>方法参数类型列表字符串。</returns>\n    private string GenerateMethodParameterTypes(ImmutableArray<IParameterSymbol> parameters)\n    {\n        return string.Join(\n            \", \",\n            parameters.Select(x => x.Type.ToUsingString()));\n    }\n\n    /// <summary>\n    /// 根据参数列表生成方法实参列表字符串。\n    /// </summary>\n    /// <param name=\"parameters\">方法参数列表。</param>\n    /// <returns>方法实参列表字符串。</returns>\n    private string GenerateMethodArguments(ImmutableArray<IParameterSymbol> parameters)\n    {\n        return string.Join(\n            \", \",\n            parameters.Select(x => $\"{x.Name}\"));\n    }\n\n    /// <summary>\n    /// 根据参数列表生成方法实参列表字符串，同时将原参数改为 Garm 类型的参数，以支持 IPC 对象的跨进程传输。\n    /// <para>这么做是因为以下语句在 instance 实例为接口时因无法隐式转换而编译不通过，在其他情况下却可以：</para>\n    /// <c>new Garm&lt;object?&gt;[] { instance }</c>\n    /// </summary>\n    /// <param name=\"parameters\">方法参数列表。</param>\n    /// <returns>方法实参列表字符串。</returns>\n    private string GenerateGarmArguments(ImmutableArray<IParameterSymbol> parameters)\n    {\n        return string.Join(\n            \", \",\n            parameters.Select(x => x.Type.GetIsIpcType()\n                ? $\"new Garm<{x.Type.ToUsingString()}>({x.Name}, typeof({x.Type.ToNotNullGlobalDisplayString()}))\"\n                : $\"new Garm<{x.Type.ToUsingString()}>({x.Name})\"));\n    }\n\n    /// <summary>\n    /// 如果方法是异步方法，则返回泛型 Task 的内部类型或非泛型 Task 的 null；如果是同步方法，则返回原类型。\n    /// </summary>\n    /// <param name=\"returnType\">此方法返回类型的语义符号。</param>\n    /// <returns>返回类型的源代码。</returns>\n    private ITypeSymbol? GetAsyncReturnType(ITypeSymbol returnType)\n    {\n        if (returnType is INamedTypeSymbol namedReturnType)\n        {\n            if (_isAsyncMethod)\n            {\n                if (namedReturnType.TypeArguments.FirstOrDefault() is { } returnArgument)\n                {\n                    // Task<TResult>\n                    return returnArgument;\n                }\n                else\n                {\n                    // Task\n                    return null;\n                }\n            }\n            else\n            {\n                return returnType;\n            }\n        }\n        else\n        {\n            return returnType;\n        }\n    }\n\n    /// <summary>\n    /// 根据方法返回值生成返回值字符串，同时将原参数改为 Garm 类型的参数，以支持 IPC 对象的跨进程传输。\n    /// <para>这么做是因为以下语句在 instance 实例为接口时因无法隐式转换而编译不通过，在其他情况下却可以：</para>\n    /// <c>new Garm&lt;object?&gt;[] { instance }</c>\n    /// </summary>\n    /// <param name=\"return\">方法返回值类型。</param>\n    /// <param name=\"value\">方法返回值。</param>\n    /// <returns>方法实参列表字符串。</returns>\n    private string GenerateGarmReturn(ITypeSymbol @return, string value)\n    {\n        return @return.GetIsIpcType()\n            ? $\"new Garm<{@return.ToUsingString()}>({value}, typeof({@return.ToNotNullGlobalDisplayString()}))\"\n            : $\"new Garm<{@return.ToUsingString()}>({value})\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/Members/IpcPublicPropertyInfo.cs",
    "content": "﻿using dotnetCampus.Ipc.Generators.Builders;\n\nnamespace dotnetCampus.Ipc.Generators.Compiling.Members;\n\ninternal class IpcPublicPropertyInfo : IPublicIpcObjectProxyMemberGenerator, IPublicIpcObjectShapeMemberGenerator, IPublicIpcObjectJointMatchGenerator\n{\n    /// <summary>\n    /// 契约接口类型的语义符号。\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取接口定义相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly INamedTypeSymbol _contractType;\n\n    /// <summary>\n    /// IPC 类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）的语义符号。（可能与 <see cref=\"_ipcType\"/> 是相同类型。）\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取接口特性（<see cref=\"Attribute\"/>）相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly INamedTypeSymbol _ipcType;\n\n    /// <summary>\n    /// 此属性原始定义（即在 <see cref=\"_contractType\"/> 中所定义的方法）的语义符号。\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取属性签名相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly IPropertySymbol _contractProperty;\n\n    /// <summary>\n    /// 标记了 <see cref=\"IpcMemberAttribute\"/> 的此属性实现的语义符号。（可能与 <see cref=\"_contractProperty\"/> 是相同实例。）\n    /// </summary>\n    /// <remarks>\n    /// 如果需要获取属性特性（<see cref=\"Attribute\"/>）相关的信息，应从此属性中获取。\n    /// </remarks>\n    private readonly IPropertySymbol _ipcProperty;\n\n    /// <summary>\n    /// 创建 IPC 对象的其中一个成员信息。\n    /// </summary>\n    /// <param name=\"contractType\">契约接口类型的语义符号。</param>\n    /// <param name=\"ipcType\">IPC 类型（即标记了 <see cref=\"IpcPublicAttribute\"/> 或 <see cref=\"IpcShapeAttribute\"/> 的类型）的语义符号。（可能与 <paramref name=\"contractType\"/> 是相同类型。）</param>\n    /// <param name=\"contractProperty\">此成员原始定义（即在 <paramref name=\"contractType\"/> 中所定义的方法）的语义符号。</param>\n    /// <param name=\"ipcProperty\">标记了 <see cref=\"IpcMemberAttribute\"/> 的此成员实现的语义符号。（可能与 <paramref name=\"contractProperty\"/> 是相同实例。）</param>\n    public IpcPublicPropertyInfo(INamedTypeSymbol contractType, INamedTypeSymbol ipcType, IPropertySymbol contractProperty, IPropertySymbol ipcProperty)\n    {\n        _contractType = contractType ?? throw new ArgumentNullException(nameof(contractType));\n        _ipcType = ipcType ?? throw new ArgumentNullException(nameof(ipcType));\n        _contractProperty = contractProperty ?? throw new ArgumentNullException(nameof(contractProperty));\n        _ipcProperty = ipcProperty ?? throw new ArgumentNullException(nameof(ipcProperty));\n    }\n\n    /// <summary>\n    /// 生成此属性在 IPC 代理中的源代码。\n    /// </summary>\n    /// <returns>属性源代码。</returns>\n    public string GenerateProxyMember()\n    {\n        var propertyTypeName = _contractProperty.Type.ToUsingString();\n        var containingTypeName = _contractProperty.ContainingType.ToUsingString();\n        var getMemberId = MemberIdGenerator.GeneratePropertyId(\"get\", _ipcProperty.Name);\n        var setMemberId = MemberIdGenerator.GeneratePropertyId(\"set\", _ipcProperty.Name);\n        var valueArgumentName = GenerateGarmArgument(_contractProperty.Type, \"value\");\n        var namedValues = _ipcProperty.GetIpcNamedValues(_ipcType);\n        var (hasGet, hasSet) = (_contractProperty.GetMethod is not null, _contractProperty.SetMethod is not null);\n        return (hasGet, hasSet) switch\n        {\n            // get/set 属性。\n            (true, true) => $$\"\"\"\n                    {{propertyTypeName}} {{containingTypeName}}.{{_contractProperty.Name}}\n                    {\n                        get => GetValueAsync<{{propertyTypeName}}>({{getMemberId}}, {{namedValues.ToIndentString(\"    \")}}).Result;\n                        set => SetValueAsync<{{propertyTypeName}}>({{setMemberId}}, {{valueArgumentName}}, {{namedValues.ToIndentString(\"    \")}}).Wait();\n                    }\n                    \"\"\",\n            // get 属性。\n            (true, false) => $\"\"\"\n                    {propertyTypeName} {containingTypeName}.{_contractProperty.Name} => GetValueAsync<{propertyTypeName}>({getMemberId}, {namedValues}).Result;\n                    \"\"\",\n            // 不支持 set 属性。\n            _ => throw new DiagnosticException(IPC002_KnownDiagnosticError),\n        };\n    }\n\n    /// <summary>\n    /// 生成此成员在 IPC 形状代理中的源代码。\n    /// </summary>\n    /// <returns>成员源代码。</returns>\n    public string GenerateShapeMember()\n    {\n        var propertyTypeName = _contractProperty.Type.ToUsingString();\n        var containingTypeName = _contractProperty.ContainingType.ToUsingString();\n        var (hasGet, hasSet) = (_contractProperty.GetMethod is not null, _contractProperty.SetMethod is not null);\n        return (hasGet, hasSet) switch\n        {\n            // get/set 属性。\n            (true, true) => $$\"\"\"\n                [IpcProperty]\n                {{propertyTypeName}} {{containingTypeName}}.{{_contractProperty.Name}} { get; set; }\n                \"\"\",\n            // get 属性。\n            (true, false) => $$\"\"\"\n                [IpcProperty]\n                {{propertyTypeName}} {{containingTypeName}}.{{_contractProperty.Name}} { get; }\n                \"\"\",\n            // 不支持 set 属性。\n            _ => throw new DiagnosticException(IPC002_KnownDiagnosticError),\n        };\n    }\n\n    /// <summary>\n    /// 生成此属性在 IPC 对接中的源代码。\n    /// </summary>\n    /// <param name=\"real\">IPC 对接方法中真实实例的实参名称。</param>\n    /// <returns>属性源代码。</returns>\n    public string GenerateJointMatch(string real)\n    {\n        var containingTypeName = _contractProperty.ContainingType.ToUsingString();\n        var propertyTypeName = _contractProperty.Type.ToUsingString();\n        var getMemberId = MemberIdGenerator.GeneratePropertyId(\"get\", _ipcProperty.Name);\n        var setMemberId = MemberIdGenerator.GeneratePropertyId(\"set\", _ipcProperty.Name);\n        var garmPropertyTypeName = $\"Garm<{propertyTypeName}>\";\n        var garmPropertyArgumentName = GenerateGarmArgument(_contractProperty.Type, $\"{real}.{_contractProperty.Name}\");\n        var (hasGet, hasSet) = (_contractProperty.GetMethod is not null, _contractProperty.SetMethod is not null);\n        if (hasGet && hasSet)\n        {\n            var sourceCode =\n                $\"MatchProperty({getMemberId}, {setMemberId}, new Func<{garmPropertyTypeName}>(() => {garmPropertyArgumentName}), new Action<{propertyTypeName}>(value => {real}.{_contractProperty.Name} = value));\";\n            return sourceCode;\n        }\n        else if (hasGet)\n        {\n            var sourceCode = $\"MatchProperty({getMemberId}, new Func<{garmPropertyTypeName}>(() => {garmPropertyArgumentName}));\";\n            return sourceCode;\n        }\n        else\n        {\n            // 不支持只写属性。\n            throw new DiagnosticException(IPC002_KnownDiagnosticError);\n        }\n    }\n\n    /// <summary>\n    /// 使用原参数生成 Garm 类型的参数，以支持 IPC 对象的跨进程传输。\n    /// </summary>\n    /// <param name=\"parameterType\"></param>\n    /// <param name=\"argumentName\"></param>\n    /// <returns></returns>\n    private string GenerateGarmArgument(ITypeSymbol parameterType, string argumentName)\n    {\n        return parameterType.GetIsIpcType()\n            ? $\"new Garm<{parameterType.ToUsingString()}>({argumentName}, typeof({parameterType.ToNotNullGlobalDisplayString()}))\"\n            : $\"new Garm<{parameterType.ToUsingString()}>({argumentName})\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Compiling/Members/MemberIdGenerator.cs",
    "content": "﻿using System.Security.Cryptography;\n\nnamespace dotnetCampus.Ipc.Generators.Compiling.Members;\n\n/// <summary>\n/// 为成员签名生成唯一 ID，用于 IPC 对象的跨进程通信和跨版本兼容。\n/// </summary>\npublic static class MemberIdGenerator\n{\n    /// <summary>\n    /// 生成属性的 get 或 set 方法的唯一 ID。\n    /// </summary>\n    /// <param name=\"getSet\">只能传 get 或 set 这两种字符串。</param>\n    /// <param name=\"propertyName\">属性名称。</param>\n    /// <returns>属性的唯一 ID。</returns>\n    public static string GeneratePropertyId(string getSet, string propertyName)\n        => CalculateHash($\"{getSet}_{propertyName}()\");\n\n    /// <summary>\n    /// 生成方法的唯一 ID。\n    /// </summary>\n    /// <param name=\"method\">方法。</param>\n    /// <returns>方法的唯一 ID。</returns>\n    public static string GenerateMethodId(IMethodSymbol method)\n        => GenerateMethodId(method.Name, method.Parameters.Select(x => x.Type.ToString()));\n\n    /// <summary>\n    /// 生成方法的唯一 ID。\n    /// </summary>\n    /// <param name=\"methodName\">方法名称。</param>\n    /// <param name=\"parameterTypeNames\">参数类型名称集合。</param>\n    /// <returns>方法的唯一 ID。</returns>\n    public static string GenerateMethodId(string methodName, IEnumerable<string> parameterTypeNames)\n        => CalculateHash($\"{methodName}({string.Join(\",\", parameterTypeNames)})\");\n\n    /// <summary>\n    /// 根据方法签名计算哈希值，作为唯一 ID。\n    /// </summary>\n    /// <param name=\"text\">方法签名文本。</param>\n    /// <returns>唯一 ID。</returns>\n    private static string CalculateHash(string text)\n    {\n        // **请勿修改此方法的实现，否则会导致跨版本兼容性问题。**\n        using var sha256 = SHA256.Create();\n\n        // **请勿修改此方法的实现，否则会导致跨版本兼容性问题。**\n        var inputBytes = Encoding.UTF8.GetBytes(text);\n        var hashBytes = sha256.ComputeHash(inputBytes);\n\n        // **请勿修改此方法的实现，否则会导致跨版本兼容性问题。**\n        return $\"0x{BitConverter.ToInt64(hashBytes, 0):X16}\";\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/IpcPublicGenerator.cs",
    "content": "﻿using dotnetCampus.Ipc.Generators.Compiling;\nusing Microsoft.CodeAnalysis.Text;\nusing static dotnetCampus.Ipc.Generators.Utils.GeneratorHelper;\n\nnamespace dotnetCampus.Ipc.Generators;\n\n/// <summary>\n/// 为 IPC 接口生成对应的代理（Proxy）和对接（Joint）。\n/// </summary>\n[Generator(LanguageNames.CSharp)]\npublic class IpcPublicGenerator : IIncrementalGenerator\n{\n    public void Initialize(IncrementalGeneratorInitializationContext context)\n    {\n        var ipcPublic = context.SyntaxProvider.CreateSyntaxProvider(\n                // 基本过滤：有特性的接口。\n                (syntaxNode, ct) => syntaxNode is InterfaceDeclarationSyntax { AttributeLists.Count: > 0 },\n                // 语义解析：确定是否真的是感兴趣的 IPC 接口。\n                (generatorSyntaxContext, ct) => IpcPublicCompilation.TryCreateIpcPublicCompilation(\n                    (InterfaceDeclarationSyntax)generatorSyntaxContext.Node,\n                    generatorSyntaxContext.SemanticModel,\n                    out var ipcPublicCompilation)\n                    ? ipcPublicCompilation\n                    : null)\n            .Where(x => x is not null)\n            .Select((x, ct) => x!);\n\n        var ipcShape = context.SyntaxProvider.CreateSyntaxProvider(\n                // 基本过滤：有特性的接口。\n                (syntaxNode, ct) => syntaxNode is ClassDeclarationSyntax { AttributeLists.Count: > 0 },\n                // 语义解析：确定是否真的是感兴趣的 IPC 接口。\n                (generatorSyntaxContext, ct) => IpcShapeCompilation.TryCreateIpcShapeCompilation(\n                    (ClassDeclarationSyntax)generatorSyntaxContext.Node,\n                    generatorSyntaxContext.SemanticModel,\n                    out var ipcPublicCompilation)\n                    ? ipcPublicCompilation\n                    : null)\n            .Where(x => x is not null)\n            .Select((x, ct) => x!);\n\n        context.RegisterSourceOutput(ipcPublic, Execute);\n        context.RegisterSourceOutput(ipcShape, Execute);\n        context.RegisterSourceOutput(ipcPublic.Collect().Combine(ipcShape.Collect()), Execute);\n    }\n\n    private void Execute(SourceProductionContext context, IpcPublicCompilation ipcPublicCompilation)\n    {\n        var ipcType = ipcPublicCompilation.IpcType;\n        var proxySource = GenerateProxySource(ipcPublicCompilation);\n        var jointSource = GenerateJointSource(ipcPublicCompilation);\n        context.AddSource($\"{ipcType.Name}.proxy.cs\", SourceText.From(proxySource, Encoding.UTF8));\n        context.AddSource($\"{ipcType.Name}.joint.cs\", SourceText.From(jointSource, Encoding.UTF8));\n    }\n\n    private void Execute(SourceProductionContext context, IpcShapeCompilation ipcShapeCompilation)\n    {\n        var contractType = ipcShapeCompilation.ContractType;\n        var ipcType = ipcShapeCompilation.IpcType;\n        var proxySource = GenerateProxySource(ipcShapeCompilation);\n        context.AddSource($\"{contractType.Name}.{ipcType.Name}.shape.cs\", SourceText.From(proxySource, Encoding.UTF8));\n    }\n\n    private void Execute(SourceProductionContext context,\n        (ImmutableArray<IpcPublicCompilation> IpcPublics, ImmutableArray<IpcShapeCompilation> IpcShapes) compilations)\n    {\n        var moduleInitializerSource = GenerateModuleInitializerSource(compilations.IpcPublics, compilations.IpcShapes);\n        context.AddSource(\"_ModuleInitializer.cs\", SourceText.From(moduleInitializerSource, Encoding.UTF8));\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Generators/Utils/GeneratorHelper.cs",
    "content": "﻿using dotnetCampus.Ipc.Generators.Compiling;\nusing dotnetCampus.Ipc.Generators.Builders;\n\nnamespace dotnetCampus.Ipc.Generators.Utils;\n\ninternal static class GeneratorHelper\n{\n    /// <summary>\n    /// 生成代理类。\n    /// </summary>\n    /// <param name=\"ipc\">IPC 接口类型的编译信息。</param>\n    /// <returns>代理类的源代码。</returns>\n    internal static string GenerateProxySource(IpcPublicCompilation ipc)\n    {\n        using var builder = new SourceTextBuilder(ipc.GetNamespace())\n            .Using(\"System.Threading.Tasks\")\n            .Using(\"dotnetCampus.Ipc.CompilerServices.GeneratedProxies\")\n            .AddTypeDeclaration($\"internal sealed class __{ipc.IpcType.Name}IpcProxy\", t => t\n                .AddBaseTypes($\"GeneratedIpcProxy<{ipc.IpcType.ToUsingString()}>\", ipc.IpcType.ToUsingString())\n                .AddGeneratedToolAndEditorBrowsingAttributes()\n                .AddRawMembers(ipc.EnumerateMembers()\n                    .Select(x => new IpcPublicMemberProxyJointGenerator(x.IpcType, x.Member))\n                    .Select(x => x.GenerateProxyMember())));\n        return builder.ToString();\n    }\n\n    /// <summary>\n    /// 生成代理类。\n    /// </summary>\n    /// <param name=\"ipc\">IPC 接口类型的编译信息。</param>\n    /// <returns>代理类的源代码。</returns>\n    internal static string GenerateProxySource(IpcShapeCompilation ipc)\n    {\n        using var builder = new SourceTextBuilder(ipc.GetNamespace())\n            .Using(\"System.Threading.Tasks\")\n            .Using(\"dotnetCampus.Ipc.CompilerServices.GeneratedProxies\")\n            .AddTypeDeclaration($\"internal sealed class __{ipc.IpcType.Name}IpcProxy\", t => t\n                .AddBaseTypes($\"GeneratedIpcProxy<{ipc.ContractType.ToUsingString()}>\", ipc.ContractType.ToUsingString())\n                .AddGeneratedToolAndEditorBrowsingAttributes()\n                .AddRawMembers(ipc.EnumerateMembersByContractType()\n                    .Select(x => new IpcPublicMemberProxyJointGenerator(x.contractType, x.shapeType, x.member, x.shapeMember))\n                    .Select(x => x.GenerateProxyMember())));\n        return builder.ToString();\n    }\n\n    /// <summary>\n    /// 生成形状代理类。\n    /// </summary>\n    /// <param name=\"ipc\">真实对象的编译信息。</param>\n    /// <param name=\"typeName\">类型名称。</param>\n    /// <param name=\"namespace\">命名空间。</param>\n    /// <returns>代理类的源代码。</returns>\n    internal static string GenerateShapeSource(IpcPublicCompilation ipc, string? typeName, string? @namespace)\n    {\n        using var builder = new SourceTextBuilder(@namespace ?? ipc.IpcType.ContainingNamespace.ToString())\n            {\n                SimplifyTypeNamesByUsingNamespace = true,\n                ShouldPrependGlobal = false,\n            }\n            .Using(\"dotnetCampus.Ipc.CompilerServices.Attributes\")\n            .Using(\"dotnetCampus.Ipc.CompilerServices.GeneratedProxies\")\n            .AddTypeDeclaration($\"internal sealed class {typeName ?? $\"{ipc.IpcType.Name}IpcShape\"}\", t => t\n                .AddBaseTypes(ipc.IpcType.ToUsingString())\n                .AddAttribute($\"[IpcShape(typeof({ipc.IpcType.ToUsingString()}))]\")\n                .AddRawMembers(ipc.EnumerateMembers()\n                    .Select(x => new IpcPublicMemberProxyJointGenerator(x.IpcType, x.Member))\n                    .Select(x => x.GenerateShapeMember())));\n        return builder.ToString();\n    }\n\n    /// <summary>\n    /// 生成对接类。\n    /// </summary>\n    /// <param name=\"ipc\">真实对象的编译信息。</param>\n    /// <returns>对接类的源代码。</returns>\n    internal static string GenerateJointSource(IpcPublicCompilation ipc)\n    {\n        const string realInstanceName = \"real\";\n        using var builder = new SourceTextBuilder(ipc.GetNamespace())\n            .Using(\"System\")\n            .Using(\"System.Threading.Tasks\")\n            .Using(\"dotnetCampus.Ipc.CompilerServices.GeneratedProxies\")\n            .AddTypeDeclaration($\"internal sealed class __{ipc.IpcType.Name}IpcJoint\", t => t\n                .AddBaseTypes($\"GeneratedIpcJoint<{ipc.IpcType.ToUsingString()}>\")\n                .AddGeneratedToolAndEditorBrowsingAttributes()\n                .AddMethodDeclaration($\"protected override void MatchMembers({ipc.IpcType.ToUsingString()} {realInstanceName})\", m => m\n                    .AddRawStatements(ipc.EnumerateMembers()\n                        .Select(x => new IpcPublicMemberProxyJointGenerator(x.IpcType, x.Member))\n                        .Select(x => x.GenerateJointMatch(realInstanceName)))));\n        return builder.ToString();\n    }\n\n    /// <summary>\n    /// 生成代理对接关系信息。\n    /// </summary>\n    /// <param name=\"ipcPublicCompilations\">真实对象的编译信息。</param>\n    /// <param name=\"ipcShapeCompilations\">形状代理类型的编译信息。</param>\n    /// <returns>程序集特性的源代码。</returns>\n    internal static string GenerateModuleInitializerSource(\n        IReadOnlyList<IpcPublicCompilation> ipcPublicCompilations,\n        IReadOnlyList<IpcShapeCompilation> ipcShapeCompilations)\n    {\n        using var builder = new SourceTextBuilder()\n            .AddRawText(\"#if NET5_0_OR_GREATER\")\n            .AddRawText(\"using static global::dotnetCampus.Ipc.CompilerServices.GeneratedProxies.GeneratedIpcFactory;\")\n            .AddTypeDeclaration(\"file static class DotNetCampusIpcModuleInitializer\", t => t\n                .AddGeneratedToolAndEditorBrowsingAttributes()\n                .AddMethodDeclaration(\"internal static void Initialize()\", m => m\n                    .AddAttribute(\"[global::System.Runtime.CompilerServices.ModuleInitializerAttribute]\")\n                    .AddRawStatements(ipcPublicCompilations.Select(GenerateIpcPublicRegistration))\n                    .AddLineSeparator()\n                    .AddRawStatements(ipcShapeCompilations.Select(GenerateIpcPublicRegistration))))\n            .AddRawText(\"#else\")\n            .AddRawStatements(ipcPublicCompilations.Select(GenerateIpcPublicAssemblyAttribute))\n            .AddRawStatements(ipcShapeCompilations.Select(GenerateIpcPublicAssemblyAttribute))\n            .AddRawText(\"#endif\");\n        return builder.ToString();\n    }\n\n    private static string GenerateIpcPublicRegistration(IpcPublicCompilation ipc) => $\"\"\"\n        RegisterIpcPublic<{ipc.IpcType.ToUsingString()}>(\n            static () => new global::{ipc.GetNamespace()}.__{ipc.IpcType.Name}IpcProxy(),\n            static () => new global::{ipc.GetNamespace()}.__{ipc.IpcType.Name}IpcJoint());\n        \"\"\";\n\n    private static string GenerateIpcPublicRegistration(IpcShapeCompilation ipc) => $\"\"\"\n        RegisterIpcShape<{ipc.ContractType.ToUsingString()}, {ipc.IpcType.ToUsingString()}>(\n            static () => new global::{ipc.GetNamespace()}.__{ipc.IpcType.Name}IpcProxy());\n        \"\"\";\n\n    private static string GenerateIpcPublicAssemblyAttribute(IpcPublicCompilation ipc) => $\"\"\"\n        [assembly: global::dotnetCampus.Ipc.CompilerServices.Attributes.AssemblyIpcProxyJointAttribute(\n            typeof({ipc.IpcType.ToUsingString()}),\n            typeof(global::{ipc.GetNamespace()}.__{ipc.IpcType.Name}IpcProxy),\n            typeof(global::{ipc.GetNamespace()}.__{ipc.IpcType.Name}IpcJoint))]\n        \"\"\";\n\n    private static string GenerateIpcPublicAssemblyAttribute(IpcShapeCompilation ipc) => $\"\"\"\n        [assembly: global::dotnetCampus.Ipc.CompilerServices.Attributes.AssemblyIpcProxyAttribute(\n            typeof({ipc.ContractType.ToUsingString()}),\n            typeof({ipc.IpcType.ToUsingString()}),\n            typeof(global::{ipc.GetNamespace()}.__{ipc.IpcType.Name}IpcProxy))]\n        \"\"\";\n\n    /// <summary>\n    /// 在代码生成器中报告那些分析器中没有报告的编译错误。\n    /// <para>注意：虽然代码生成器和分析器都能报告编译错误，但只有分析器才能在 Visual Studio 中画波浪线。所以我们会考虑将一些需要立即觉察的错误放到分析器中报告。</para>\n    /// <para>因此，在这个代码生成器项目中：</para>\n    /// <list type=\"bullet\">\n    /// <item>如果某个错误后续一定会报编译错误，则抛出 <see cref=\"IPC001_KnownCompilerError\"/>。</item>\n    /// <item>如果某个错误已经写了分析器，就报 <see cref=\"IPC002_KnownDiagnosticError\"/>。</item>\n    /// <item>如果未来会写某个分析器，但现在还没完成，则报具体的分析器错误。</item>\n    /// <item>其他情况，该抛什么异常就抛什么异常，不局限于诊断异常。</item>\n    /// </list>\n    /// </summary>\n    /// <param name=\"context\"></param>\n    /// <param name=\"ex\"></param>\n    internal static void ReportDiagnosticsThatHaveNotBeenReported(SourceProductionContext context, DiagnosticException ex)\n    {\n        var diagnosticsThatWillBeReported = new List<DiagnosticDescriptor>\n        {\n            // 这些诊断将仅在分析器中报告，凡在生成器中发生的这些诊断都将自动忽略。\n            IPC001_KnownCompilerError,\n            IPC002_KnownDiagnosticError,\n        };\n\n        if (diagnosticsThatWillBeReported.Find(x => x.Id == ex.Diagnostic.Id) is not null)\n        {\n            return;\n        }\n\n        // 报告所有非已知诊断。\n        context.ReportDiagnostic(ex.ToDiagnostic());\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"dotnetCampus.Ipc.Analyzers.Tests\")]\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Properties/GlobalUsings.cs",
    "content": "﻿global using System;\nglobal using System.Collections.Generic;\nglobal using System.Collections.Immutable;\nglobal using System.Composition;\nglobal using System.Diagnostics;\nglobal using System.Diagnostics.CodeAnalysis;\nglobal using System.Globalization;\nglobal using System.Linq;\nglobal using System.Text;\nglobal using System.Threading;\nglobal using System.Threading.Tasks;\n\nglobal using dotnetCampus.Ipc.CodeAnalysis.Core;\nglobal using dotnetCampus.Ipc.CodeAnalysis.Utils;\nglobal using dotnetCampus.Ipc.CompilerServices.Attributes;\nglobal using dotnetCampus.Ipc.Core.ComponentModels;\nglobal using Microsoft.CodeAnalysis;\nglobal using Microsoft.CodeAnalysis.CSharp;\nglobal using Microsoft.CodeAnalysis.CSharp.Syntax;\nglobal using Microsoft.CodeAnalysis.Diagnostics;\n\nglobal using static dotnetCampus.Ipc.CodeAnalysis.Core.Diagnostics;\nglobal using static dotnetCampus.Ipc.CodeAnalysis.Utils.SyntaxNameGuesser;\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Properties/Localizations.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     此代码由工具生成。\n//     运行时版本:4.0.30319.42000\n//\n//     对此文件的更改可能会导致不正确的行为，并且如果\n//     重新生成代码，这些更改将会丢失。\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace dotnetCampus.Ipc.Properties {\n    using System;\n\n\n    /// <summary>\n    ///   一个强类型的资源类，用于查找本地化的字符串等。\n    /// </summary>\n    // 此类是由 StronglyTypedResourceBuilder\n    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。\n    // 若要添加或移除成员，请编辑 .ResX 文件，然后重新运行 ResGen\n    // (以 /str 作为命令选项)，或重新生成 VS 项目。\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"17.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Localizations {\n\n        private static global::System.Resources.ResourceManager resourceMan;\n\n        private static global::System.Globalization.CultureInfo resourceCulture;\n\n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Localizations() {\n        }\n\n        /// <summary>\n        ///   返回此类使用的缓存的 ResourceManager 实例。\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"dotnetCampus.Ipc.Properties.Localizations\", typeof(Localizations).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n\n        /// <summary>\n        ///   重写当前线程的 CurrentUICulture 属性，对\n        ///   使用此强类型资源类的所有资源查找执行重写。\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC unknown error 的本地化字符串。\n        /// </summary>\n        internal static string IPC000 {\n            get {\n                return ResourceManager.GetString(\"IPC000\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 An unknown error occurred when generating IPC types. The error is: {0} 的本地化字符串。\n        /// </summary>\n        internal static string IPC000_Message {\n            get {\n                return ResourceManager.GetString(\"IPC000_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC known compiler error 的本地化字符串。\n        /// </summary>\n        internal static string IPC001 {\n            get {\n                return ResourceManager.GetString(\"IPC001\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 An known compiler error occurred when generating IPC types. We&apos;ll not report it because the compiler will do this. 的本地化字符串。\n        /// </summary>\n        internal static string IPC001_Message {\n            get {\n                return ResourceManager.GetString(\"IPC001_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC known diagnostic error 的本地化字符串。\n        /// </summary>\n        internal static string IPC002 {\n            get {\n                return ResourceManager.GetString(\"IPC002\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 An known diagnostic error occurred when generating IPC types. We&apos;ll not report it because the analyzer will do this. 的本地化字符串。\n        /// </summary>\n        internal static string IPC002_Message {\n            get {\n                return ResourceManager.GetString(\"IPC002_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC timeout can&apos;t be negative 的本地化字符串。\n        /// </summary>\n        internal static string IPC101 {\n            get {\n                return ResourceManager.GetString(\"IPC101\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC timeout can&apos;t be negative, but it is set to {0}ms. 的本地化字符串。\n        /// </summary>\n        internal static string IPC101_Message {\n            get {\n                return ResourceManager.GetString(\"IPC101_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The timeout is zero 的本地化字符串。\n        /// </summary>\n        internal static string IPC102 {\n            get {\n                return ResourceManager.GetString(\"IPC102\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 A timeout with zero milliseconds does nothing so there is no need to assign this property. 的本地化字符串。\n        /// </summary>\n        internal static string IPC102_Message {\n            get {\n                return ResourceManager.GetString(\"IPC102_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IgnoresIpcException is recommended 的本地化字符串。\n        /// </summary>\n        internal static string IPC131 {\n            get {\n                return ResourceManager.GetString(\"IPC131\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set IgnoresIpcException to true 的本地化字符串。\n        /// </summary>\n        internal static string IPC131_Fix1 {\n            get {\n                return ResourceManager.GetString(\"IPC131_Fix1\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set IgnoresIpcException to false 的本地化字符串。\n        /// </summary>\n        internal static string IPC131_Fix2 {\n            get {\n                return ResourceManager.GetString(\"IPC131_Fix2\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 For better readability, IgnoresIpcException is recommended to set so that the developers know that there may have been some IPC exceptions here. 的本地化字符串。\n        /// </summary>\n        internal static string IPC131_Message {\n            get {\n                return ResourceManager.GetString(\"IPC131_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC contract type must be an interface 的本地化字符串。\n        /// </summary>\n        internal static string IPC160 {\n            get {\n                return ResourceManager.GetString(\"IPC160\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC contract type must be an interface but {0} is not. 的本地化字符串。\n        /// </summary>\n        internal static string IPC160_Message {\n            get {\n                return ResourceManager.GetString(\"IPC160_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC contract type dismatches with interface 的本地化字符串。\n        /// </summary>\n        internal static string IPC161 {\n            get {\n                return ResourceManager.GetString(\"IPC161\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Change the contract type to {0} 的本地化字符串。\n        /// </summary>\n        internal static string IPC161_Fix1 {\n            get {\n                return ResourceManager.GetString(\"IPC161_Fix1\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Let {0} implement {1} 的本地化字符串。\n        /// </summary>\n        internal static string IPC161_Fix2 {\n            get {\n                return ResourceManager.GetString(\"IPC161_Fix2\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 {0} does not fully implement the contract type {1} that the IpcShape marked. 的本地化字符串。\n        /// </summary>\n        internal static string IPC161_Message {\n            get {\n                return ResourceManager.GetString(\"IPC161_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Mark all members as IPC members 的本地化字符串。\n        /// </summary>\n        internal static string IPC162 {\n            get {\n                return ResourceManager.GetString(\"IPC162\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 An IPC shape should mark all the members as IPC members, because it cannot use any behaviors from the original contract type. 的本地化字符串。\n        /// </summary>\n        internal static string IPC162_Message {\n            get {\n                return ResourceManager.GetString(\"IPC162_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Only properties, methods and events are supported 的本地化字符串。\n        /// </summary>\n        internal static string IPC200 {\n            get {\n                return ResourceManager.GetString(\"IPC200\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Unknown IPC member {0}. IPC object only supports properties, methods and events. 的本地化字符串。\n        /// </summary>\n        internal static string IPC200_Message {\n            get {\n                return ResourceManager.GetString(\"IPC200_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Empty IPC member attribute is not needed 的本地化字符串。\n        /// </summary>\n        internal static string IPC201 {\n            get {\n                return ResourceManager.GetString(\"IPC201\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Remove the {0} 的本地化字符串。\n        /// </summary>\n        internal static string IPC201_Fix {\n            get {\n                return ResourceManager.GetString(\"IPC201_Fix\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Empty {0} does nothing, so there is no need to add it. 的本地化字符串。\n        /// </summary>\n        internal static string IPC201_Message {\n            get {\n                return ResourceManager.GetString(\"IPC201_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Mark the member as an IPC member 的本地化字符串。\n        /// </summary>\n        internal static string IPC202 {\n            get {\n                return ResourceManager.GetString(\"IPC202\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 As an IPC shape, it is recommended to mark {0} as an IPC member. 的本地化字符串。\n        /// </summary>\n        internal static string IPC202_Message {\n            get {\n                return ResourceManager.GetString(\"IPC202_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC property is not recommended 的本地化字符串。\n        /// </summary>\n        internal static string IPC240 {\n            get {\n                return ResourceManager.GetString(\"IPC240\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 It&apos;s better to use an async method instead of a property as an IPC member to avoid potential UI frozen or deadlocks. 的本地化字符串。\n        /// </summary>\n        internal static string IPC240_Message {\n            get {\n                return ResourceManager.GetString(\"IPC240_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set-only property is not supported 的本地化字符串。\n        /// </summary>\n        internal static string IPC241 {\n            get {\n                return ResourceManager.GetString(\"IPC241\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Unknown IPC property {0}. Only get or get-set property is supported. 的本地化字符串。\n        /// </summary>\n        internal static string IPC241_Message {\n            get {\n                return ResourceManager.GetString(\"IPC241_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn is useless 的本地化字符串。\n        /// </summary>\n        internal static string IPC242 {\n            get {\n                return ResourceManager.GetString(\"IPC242\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Remove the DefaultReturn 的本地化字符串。\n        /// </summary>\n        internal static string IPC242_Fix1 {\n            get {\n                return ResourceManager.GetString(\"IPC242_Fix1\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set IgnoresIpcException to true 的本地化字符串。\n        /// </summary>\n        internal static string IPC242_Fix2 {\n            get {\n                return ResourceManager.GetString(\"IPC242_Fix2\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn only works with the situation in which IgnoresIpcException is set to true. 的本地化字符串。\n        /// </summary>\n        internal static string IPC242_Message {\n            get {\n                return ResourceManager.GetString(\"IPC242_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IsReadonly is unnecessary 的本地化字符串。\n        /// </summary>\n        internal static string IPC243 {\n            get {\n                return ResourceManager.GetString(\"IPC243\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IsReadonly is set to false by default, so there is no need to assign it to false manully. 的本地化字符串。\n        /// </summary>\n        internal static string IPC243_Message {\n            get {\n                return ResourceManager.GetString(\"IPC243_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn type does not match the property type 的本地化字符串。\n        /// </summary>\n        internal static string IPC244 {\n            get {\n                return ResourceManager.GetString(\"IPC244\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The IPC DefaultReturn type {0} does not match the property type {1}. 的本地化字符串。\n        /// </summary>\n        internal static string IPC244_Message {\n            get {\n                return ResourceManager.GetString(\"IPC244_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string is treated as a code snippet 的本地化字符串。\n        /// </summary>\n        internal static string IPC245 {\n            get {\n                return ResourceManager.GetString(\"IPC245\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string {0} is treated as a code snippet and will be set to an object type. If you want to set the original string to it, use @&quot;&quot;&quot;{0}&quot;&quot;&quot; instead. 的本地化字符串。\n        /// </summary>\n        internal static string IPC245_Message {\n            get {\n                return ResourceManager.GetString(\"IPC245_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string is treated as a code snippet 的本地化字符串。\n        /// </summary>\n        internal static string IPC246 {\n            get {\n                return ResourceManager.GetString(\"IPC246\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string {0} is treated as a code snippet and will be set to a {1} type. 的本地化字符串。\n        /// </summary>\n        internal static string IPC246_Message {\n            get {\n                return ResourceManager.GetString(\"IPC246_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string is treated as a code snippet but cannot be compiled 的本地化字符串。\n        /// </summary>\n        internal static string IPC247 {\n            get {\n                return ResourceManager.GetString(\"IPC247\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string {0} is treated as a code snippet but cannot be compiled. 的本地化字符串。\n        /// </summary>\n        internal static string IPC247_Message {\n            get {\n                return ResourceManager.GetString(\"IPC247_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The property type is not supported 的本地化字符串。\n        /// </summary>\n        internal static string IPC248 {\n            get {\n                return ResourceManager.GetString(\"IPC248\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The type {1} of the property {0} is not supported for an IPC type. Only the primary types, the types that can be serialized via JSON, or the types that are marked with the IpcPublic can be IPC property types. 的本地化字符串。\n        /// </summary>\n        internal static string IPC248_Message {\n            get {\n                return ResourceManager.GetString(\"IPC248_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IPC sync method is not recommended 的本地化字符串。\n        /// </summary>\n        internal static string IPC260 {\n            get {\n                return ResourceManager.GetString(\"IPC260\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 It&apos;s better to use an async method instead of a sync one as an IPC member to avoid potential UI frozen or deadlocks. 的本地化字符串。\n        /// </summary>\n        internal static string IPC260_Message {\n            get {\n                return ResourceManager.GetString(\"IPC260_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn is useless 的本地化字符串。\n        /// </summary>\n        internal static string IPC261 {\n            get {\n                return ResourceManager.GetString(\"IPC261\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Remove the DefaultReturn 的本地化字符串。\n        /// </summary>\n        internal static string IPC261_Fix1 {\n            get {\n                return ResourceManager.GetString(\"IPC261_Fix1\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set IgnoresIpcException to true 的本地化字符串。\n        /// </summary>\n        internal static string IPC261_Fix2 {\n            get {\n                return ResourceManager.GetString(\"IPC261_Fix2\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn only works with the situation in which IgnoresIpcException is set to true. 的本地化字符串。\n        /// </summary>\n        internal static string IPC261_Message {\n            get {\n                return ResourceManager.GetString(\"IPC261_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 WaitsVoid is recommended 的本地化字符串。\n        /// </summary>\n        internal static string IPC262 {\n            get {\n                return ResourceManager.GetString(\"IPC262\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Commonly, developers can hardly determin whether a waiting is happened during an IPC call if a method returns void. So it&apos;s recommended to assign WaitsVoid to the value you want instead of keeping it as false by default. 的本地化字符串。\n        /// </summary>\n        internal static string IPC262_Message {\n            get {\n                return ResourceManager.GetString(\"IPC262_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The WaitsVoid is useless 的本地化字符串。\n        /// </summary>\n        internal static string IPC263 {\n            get {\n                return ResourceManager.GetString(\"IPC263\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 This method has a return value so WaitsVoid does not work for it. 的本地化字符串。\n        /// </summary>\n        internal static string IPC263_Message {\n            get {\n                return ResourceManager.GetString(\"IPC263_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn type does not match the method return type 的本地化字符串。\n        /// </summary>\n        internal static string IPC264 {\n            get {\n                return ResourceManager.GetString(\"IPC264\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The IPC DefaultReturn type {0} does not match the method return type {1}. 的本地化字符串。\n        /// </summary>\n        internal static string IPC264_Message {\n            get {\n                return ResourceManager.GetString(\"IPC264_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn value for a void method is useless 的本地化字符串。\n        /// </summary>\n        internal static string IPC265 {\n            get {\n                return ResourceManager.GetString(\"IPC265\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn value for a void method {0} is useless. 的本地化字符串。\n        /// </summary>\n        internal static string IPC265_Message {\n            get {\n                return ResourceManager.GetString(\"IPC265_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn value for an async Task method is useless 的本地化字符串。\n        /// </summary>\n        internal static string IPC266 {\n            get {\n                return ResourceManager.GetString(\"IPC266\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The DefaultReturn value for an async Task method {0} is useless. 的本地化字符串。\n        /// </summary>\n        internal static string IPC266_Message {\n            get {\n                return ResourceManager.GetString(\"IPC266_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string is treated as a code snippet 的本地化字符串。\n        /// </summary>\n        internal static string IPC267 {\n            get {\n                return ResourceManager.GetString(\"IPC267\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string {0} is treated as a code snippet and will be set to an object type. If you want to set the original string to it, use @&quot;&quot;&quot;{0}&quot;&quot;&quot; instead. 的本地化字符串。\n        /// </summary>\n        internal static string IPC267_Message {\n            get {\n                return ResourceManager.GetString(\"IPC267_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string is treated as a code snippet 的本地化字符串。\n        /// </summary>\n        internal static string IPC268 {\n            get {\n                return ResourceManager.GetString(\"IPC268\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string {0} is treated as a code snippet and will be set to a {1} type. 的本地化字符串。\n        /// </summary>\n        internal static string IPC268_Message {\n            get {\n                return ResourceManager.GetString(\"IPC268_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string is treated as a code snippet but cannot be compiled 的本地化字符串。\n        /// </summary>\n        internal static string IPC269 {\n            get {\n                return ResourceManager.GetString(\"IPC269\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The string {0} is treated as a code snippet but cannot be compiled. 的本地化字符串。\n        /// </summary>\n        internal static string IPC269_Message {\n            get {\n                return ResourceManager.GetString(\"IPC269_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Generic methods are not supported 的本地化字符串。\n        /// </summary>\n        internal static string IPC270 {\n            get {\n                return ResourceManager.GetString(\"IPC270\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Generic methods are not supported, because we cannot determine a generic type at runtime which should have been determined at compile time. 的本地化字符串。\n        /// </summary>\n        internal static string IPC270_Message {\n            get {\n                return ResourceManager.GetString(\"IPC270_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The method parameter type is not supported 的本地化字符串。\n        /// </summary>\n        internal static string IPC271 {\n            get {\n                return ResourceManager.GetString(\"IPC271\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The type {2} of the method {0} pamameter {1} is not supported for an IPC type. Only the primary types, the types that can be serialized via JSON, or the types that are marked with the IpcPublic can be IPC types. 的本地化字符串。\n        /// </summary>\n        internal static string IPC271_Message {\n            get {\n                return ResourceManager.GetString(\"IPC271_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The method return type is not supported 的本地化字符串。\n        /// </summary>\n        internal static string IPC272 {\n            get {\n                return ResourceManager.GetString(\"IPC272\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The type {1} of the method {0} return is not supported for an IPC type. Only the primary types, the types that can be serialized via JSON, or the types that are marked with the IpcPublic can be IPC types. 的本地化字符串。\n        /// </summary>\n        internal static string IPC272_Message {\n            get {\n                return ResourceManager.GetString(\"IPC272_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Add IpcProxyConfigs 的本地化字符串。\n        /// </summary>\n        internal static string IPC301 {\n            get {\n                return ResourceManager.GetString(\"IPC301\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Configure the IPC behaviors by adding IpcProxyConfigs. 的本地化字符串。\n        /// </summary>\n        internal static string IPC301_Message {\n            get {\n                return ResourceManager.GetString(\"IPC301_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Add an IpcShape 的本地化字符串。\n        /// </summary>\n        internal static string IPC302 {\n            get {\n                return ResourceManager.GetString(\"IPC302\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Generate an IpcShape for {0} 的本地化字符串。\n        /// </summary>\n        internal static string IPC302_Fix1 {\n            get {\n                return ResourceManager.GetString(\"IPC302_Fix1\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Generate an IpcShape for {0} in a new file 的本地化字符串。\n        /// </summary>\n        internal static string IPC302_Fix2 {\n            get {\n                return ResourceManager.GetString(\"IPC302_Fix2\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Configure the IPC behaviors by adding an IpcShape. 的本地化字符串。\n        /// </summary>\n        internal static string IPC302_Message {\n            get {\n                return ResourceManager.GetString(\"IPC302_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Add IpcProxyConfigs 的本地化字符串。\n        /// </summary>\n        internal static string IPC303 {\n            get {\n                return ResourceManager.GetString(\"IPC303\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Configure the IPC behaviors by adding IpcProxyConfigs because no behaviors are specified on the IPC contract type. 的本地化字符串。\n        /// </summary>\n        internal static string IPC303_Message {\n            get {\n                return ResourceManager.GetString(\"IPC303_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IpcProxyConfigs are not needed 的本地化字符串。\n        /// </summary>\n        internal static string IPC304 {\n            get {\n                return ResourceManager.GetString(\"IPC304\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 All the behaviors are specified by the IPC contract type, so every value here does not work. 的本地化字符串。\n        /// </summary>\n        internal static string IPC304_Message {\n            get {\n                return ResourceManager.GetString(\"IPC304_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set IgnoresIpcException 的本地化字符串。\n        /// </summary>\n        internal static string IPC305 {\n            get {\n                return ResourceManager.GetString(\"IPC305\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 For better readability, IgnoresIpcException is recommended to set so that the developers know that there may have been some IPC exceptions here. 的本地化字符串。\n        /// </summary>\n        internal static string IPC305_Message {\n            get {\n                return ResourceManager.GetString(\"IPC305_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IgnoresIpcException is not needed 的本地化字符串。\n        /// </summary>\n        internal static string IPC306 {\n            get {\n                return ResourceManager.GetString(\"IPC306\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 IgnoresIpcException is not needed because it has been set on the IPC contract type. 的本地化字符串。\n        /// </summary>\n        internal static string IPC306_Message {\n            get {\n                return ResourceManager.GetString(\"IPC306_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set Timeout 的本地化字符串。\n        /// </summary>\n        internal static string IPC307 {\n            get {\n                return ResourceManager.GetString(\"IPC307\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Set timeout explicitly. 的本地化字符串。\n        /// </summary>\n        internal static string IPC307_Message {\n            get {\n                return ResourceManager.GetString(\"IPC307_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Timeout is not needed 的本地化字符串。\n        /// </summary>\n        internal static string IPC308 {\n            get {\n                return ResourceManager.GetString(\"IPC308\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 Timeout is not needed because it has been set on the IPC contract type. 的本地化字符串。\n        /// </summary>\n        internal static string IPC308_Message {\n            get {\n                return ResourceManager.GetString(\"IPC308_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The type argument is not an IpcShape 的本地化字符串。\n        /// </summary>\n        internal static string IPC309 {\n            get {\n                return ResourceManager.GetString(\"IPC309\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The type argument {0} is not an IpcShape. 的本地化字符串。\n        /// </summary>\n        internal static string IPC309_Message {\n            get {\n                return ResourceManager.GetString(\"IPC309_Message\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The IPC proxy type does not match the IPC contract type 的本地化字符串。\n        /// </summary>\n        internal static string IPC310 {\n            get {\n                return ResourceManager.GetString(\"IPC310\", resourceCulture);\n            }\n        }\n\n        /// <summary>\n        ///   查找类似 The IPC proxy type {0} does not match the IPC contract type {1}. 的本地化字符串。\n        /// </summary>\n        internal static string IPC310_Message {\n            get {\n                return ResourceManager.GetString(\"IPC310_Message\", resourceCulture);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Properties/Localizations.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"IPC000\" xml:space=\"preserve\">\n    <value>IPC unknown error</value>\n  </data>\n  <data name=\"IPC000_Message\" xml:space=\"preserve\">\n    <value>An unknown error occurred when generating IPC types. The error is: {0}</value>\n  </data>\n  <data name=\"IPC001\" xml:space=\"preserve\">\n    <value>IPC known compiler error</value>\n  </data>\n  <data name=\"IPC001_Message\" xml:space=\"preserve\">\n    <value>An known compiler error occurred when generating IPC types. We'll not report it because the compiler will do this.</value>\n  </data>\n  <data name=\"IPC002\" xml:space=\"preserve\">\n    <value>IPC known diagnostic error</value>\n  </data>\n  <data name=\"IPC002_Message\" xml:space=\"preserve\">\n    <value>An known diagnostic error occurred when generating IPC types. We'll not report it because the analyzer will do this.</value>\n  </data>\n  <data name=\"IPC101\" xml:space=\"preserve\">\n    <value>IPC timeout can't be negative</value>\n  </data>\n  <data name=\"IPC101_Message\" xml:space=\"preserve\">\n    <value>IPC timeout can't be negative, but it is set to {0}ms.</value>\n  </data>\n  <data name=\"IPC102\" xml:space=\"preserve\">\n    <value>The timeout is zero</value>\n  </data>\n  <data name=\"IPC102_Message\" xml:space=\"preserve\">\n    <value>A timeout with zero milliseconds does nothing so there is no need to assign this property.</value>\n  </data>\n  <data name=\"IPC131\" xml:space=\"preserve\">\n    <value>IgnoresIpcException is recommended</value>\n  </data>\n  <data name=\"IPC131_Fix1\" xml:space=\"preserve\">\n    <value>Set IgnoresIpcException to true</value>\n  </data>\n  <data name=\"IPC131_Fix2\" xml:space=\"preserve\">\n    <value>Set IgnoresIpcException to false</value>\n  </data>\n  <data name=\"IPC131_Message\" xml:space=\"preserve\">\n    <value>For better readability, IgnoresIpcException is recommended to set so that the developers know that there may have been some IPC exceptions here.</value>\n  </data>\n  <data name=\"IPC160\" xml:space=\"preserve\">\n    <value>IPC contract type must be an interface</value>\n  </data>\n  <data name=\"IPC160_Message\" xml:space=\"preserve\">\n    <value>IPC contract type must be an interface but {0} is not.</value>\n  </data>\n  <data name=\"IPC161\" xml:space=\"preserve\">\n    <value>IPC contract type dismatches with interface</value>\n  </data>\n  <data name=\"IPC161_Fix1\" xml:space=\"preserve\">\n    <value>Change the contract type to {0}</value>\n  </data>\n  <data name=\"IPC161_Fix2\" xml:space=\"preserve\">\n    <value>Let {0} implement {1}</value>\n  </data>\n  <data name=\"IPC161_Message\" xml:space=\"preserve\">\n    <value>{0} does not fully implement the contract type {1} that the IpcShape marked.</value>\n  </data>\n  <data name=\"IPC162\" xml:space=\"preserve\">\n    <value>Mark all members as IPC members</value>\n  </data>\n  <data name=\"IPC162_Message\" xml:space=\"preserve\">\n    <value>An IPC shape should mark all the members as IPC members, because it cannot use any behaviors from the original contract type.</value>\n  </data>\n  <data name=\"IPC200\" xml:space=\"preserve\">\n    <value>Only properties, methods and events are supported</value>\n  </data>\n  <data name=\"IPC200_Message\" xml:space=\"preserve\">\n    <value>Unknown IPC member {0}. IPC object only supports properties, methods and events.</value>\n  </data>\n  <data name=\"IPC201\" xml:space=\"preserve\">\n    <value>Empty IPC member attribute is not needed</value>\n  </data>\n  <data name=\"IPC201_Fix\" xml:space=\"preserve\">\n    <value>Remove the {0}</value>\n  </data>\n  <data name=\"IPC201_Message\" xml:space=\"preserve\">\n    <value>Empty {0} does nothing, so there is no need to add it.</value>\n  </data>\n  <data name=\"IPC202\" xml:space=\"preserve\">\n    <value>Mark the member as an IPC member</value>\n  </data>\n  <data name=\"IPC202_Message\" xml:space=\"preserve\">\n    <value>As an IPC shape, it is recommended to mark {0} as an IPC member.</value>\n  </data>\n  <data name=\"IPC240\" xml:space=\"preserve\">\n    <value>IPC property is not recommended</value>\n  </data>\n  <data name=\"IPC240_Message\" xml:space=\"preserve\">\n    <value>It's better to use an async method instead of a property as an IPC member to avoid potential UI frozen or deadlocks.</value>\n  </data>\n  <data name=\"IPC241\" xml:space=\"preserve\">\n    <value>Set-only property is not supported</value>\n  </data>\n  <data name=\"IPC241_Message\" xml:space=\"preserve\">\n    <value>Unknown IPC property {0}. Only get or get-set property is supported.</value>\n  </data>\n  <data name=\"IPC242\" xml:space=\"preserve\">\n    <value>The DefaultReturn is useless</value>\n  </data>\n  <data name=\"IPC242_Fix1\" xml:space=\"preserve\">\n    <value>Remove the DefaultReturn</value>\n  </data>\n  <data name=\"IPC242_Fix2\" xml:space=\"preserve\">\n    <value>Set IgnoresIpcException to true</value>\n  </data>\n  <data name=\"IPC242_Message\" xml:space=\"preserve\">\n    <value>The DefaultReturn only works with the situation in which IgnoresIpcException is set to true.</value>\n  </data>\n  <data name=\"IPC243\" xml:space=\"preserve\">\n    <value>IsReadonly is unnecessary</value>\n  </data>\n  <data name=\"IPC243_Message\" xml:space=\"preserve\">\n    <value>IsReadonly is set to false by default, so there is no need to assign it to false manully.</value>\n  </data>\n  <data name=\"IPC244\" xml:space=\"preserve\">\n    <value>The DefaultReturn type does not match the property type</value>\n  </data>\n  <data name=\"IPC244_Message\" xml:space=\"preserve\">\n    <value>The IPC DefaultReturn type {0} does not match the property type {1}.</value>\n  </data>\n  <data name=\"IPC245\" xml:space=\"preserve\">\n    <value>The string is treated as a code snippet</value>\n  </data>\n  <data name=\"IPC245_Message\" xml:space=\"preserve\">\n    <value>The string {0} is treated as a code snippet and will be set to an object type. If you want to set the original string to it, use @\"\"\"{0}\"\"\" instead.</value>\n  </data>\n  <data name=\"IPC246\" xml:space=\"preserve\">\n    <value>The string is treated as a code snippet</value>\n  </data>\n  <data name=\"IPC246_Message\" xml:space=\"preserve\">\n    <value>The string {0} is treated as a code snippet and will be set to a {1} type.</value>\n  </data>\n  <data name=\"IPC247\" xml:space=\"preserve\">\n    <value>The string is treated as a code snippet but cannot be compiled</value>\n  </data>\n  <data name=\"IPC247_Message\" xml:space=\"preserve\">\n    <value>The string {0} is treated as a code snippet but cannot be compiled.</value>\n  </data>\n  <data name=\"IPC248\" xml:space=\"preserve\">\n    <value>The property type is not supported</value>\n  </data>\n  <data name=\"IPC248_Message\" xml:space=\"preserve\">\n    <value>The type {1} of the property {0} is not supported for an IPC type. Only the primary types, the types that can be serialized via JSON, or the types that are marked with the IpcPublic can be IPC property types.</value>\n  </data>\n  <data name=\"IPC260\" xml:space=\"preserve\">\n    <value>IPC sync method is not recommended</value>\n  </data>\n  <data name=\"IPC260_Message\" xml:space=\"preserve\">\n    <value>It's better to use an async method instead of a sync one as an IPC member to avoid potential UI frozen or deadlocks.</value>\n  </data>\n  <data name=\"IPC261\" xml:space=\"preserve\">\n    <value>The DefaultReturn is useless</value>\n  </data>\n  <data name=\"IPC261_Fix1\" xml:space=\"preserve\">\n    <value>Remove the DefaultReturn</value>\n  </data>\n  <data name=\"IPC261_Fix2\" xml:space=\"preserve\">\n    <value>Set IgnoresIpcException to true</value>\n  </data>\n  <data name=\"IPC261_Message\" xml:space=\"preserve\">\n    <value>The DefaultReturn only works with the situation in which IgnoresIpcException is set to true.</value>\n  </data>\n  <data name=\"IPC262\" xml:space=\"preserve\">\n    <value>WaitsVoid is recommended</value>\n  </data>\n  <data name=\"IPC262_Message\" xml:space=\"preserve\">\n    <value>Commonly, developers can hardly determin whether a waiting is happened during an IPC call if a method returns void. So it's recommended to assign WaitsVoid to the value you want instead of keeping it as false by default.</value>\n  </data>\n  <data name=\"IPC263\" xml:space=\"preserve\">\n    <value>The WaitsVoid is useless</value>\n  </data>\n  <data name=\"IPC263_Message\" xml:space=\"preserve\">\n    <value>This method has a return value so WaitsVoid does not work for it.</value>\n  </data>\n  <data name=\"IPC264\" xml:space=\"preserve\">\n    <value>The DefaultReturn type does not match the method return type</value>\n  </data>\n  <data name=\"IPC264_Message\" xml:space=\"preserve\">\n    <value>The IPC DefaultReturn type {0} does not match the method return type {1}.</value>\n  </data>\n  <data name=\"IPC265\" xml:space=\"preserve\">\n    <value>The DefaultReturn value for a void method is useless</value>\n  </data>\n  <data name=\"IPC265_Message\" xml:space=\"preserve\">\n    <value>The DefaultReturn value for a void method {0} is useless.</value>\n  </data>\n  <data name=\"IPC266\" xml:space=\"preserve\">\n    <value>The DefaultReturn value for an async Task method is useless</value>\n  </data>\n  <data name=\"IPC266_Message\" xml:space=\"preserve\">\n    <value>The DefaultReturn value for an async Task method {0} is useless.</value>\n  </data>\n  <data name=\"IPC267\" xml:space=\"preserve\">\n    <value>The string is treated as a code snippet</value>\n  </data>\n  <data name=\"IPC267_Message\" xml:space=\"preserve\">\n    <value>The string {0} is treated as a code snippet and will be set to an object type. If you want to set the original string to it, use @\"\"\"{0}\"\"\" instead.</value>\n  </data>\n  <data name=\"IPC268\" xml:space=\"preserve\">\n    <value>The string is treated as a code snippet</value>\n  </data>\n  <data name=\"IPC268_Message\" xml:space=\"preserve\">\n    <value>The string {0} is treated as a code snippet and will be set to a {1} type.</value>\n  </data>\n  <data name=\"IPC269\" xml:space=\"preserve\">\n    <value>The string is treated as a code snippet but cannot be compiled</value>\n  </data>\n  <data name=\"IPC269_Message\" xml:space=\"preserve\">\n    <value>The string {0} is treated as a code snippet but cannot be compiled.</value>\n  </data>\n  <data name=\"IPC270\" xml:space=\"preserve\">\n    <value>Generic methods are not supported</value>\n  </data>\n  <data name=\"IPC270_Message\" xml:space=\"preserve\">\n    <value>Generic methods are not supported, because we cannot determine a generic type at runtime which should have been determined at compile time.</value>\n  </data>\n  <data name=\"IPC271\" xml:space=\"preserve\">\n    <value>The method parameter type is not supported</value>\n  </data>\n  <data name=\"IPC271_Message\" xml:space=\"preserve\">\n    <value>The type {2} of the method {0} pamameter {1} is not supported for an IPC type. Only the primary types, the types that can be serialized via JSON, or the types that are marked with the IpcPublic can be IPC types.</value>\n  </data>\n  <data name=\"IPC272\" xml:space=\"preserve\">\n    <value>The method return type is not supported</value>\n  </data>\n  <data name=\"IPC272_Message\" xml:space=\"preserve\">\n    <value>The type {1} of the method {0} return is not supported for an IPC type. Only the primary types, the types that can be serialized via JSON, or the types that are marked with the IpcPublic can be IPC types.</value>\n  </data>\n  <data name=\"IPC301\" xml:space=\"preserve\">\n    <value>Add IpcProxyConfigs</value>\n  </data>\n  <data name=\"IPC301_Message\" xml:space=\"preserve\">\n    <value>Configure the IPC behaviors by adding IpcProxyConfigs.</value>\n  </data>\n  <data name=\"IPC302\" xml:space=\"preserve\">\n    <value>Add an IpcShape</value>\n  </data>\n  <data name=\"IPC302_Fix1\" xml:space=\"preserve\">\n    <value>Generate an IpcShape for {0}</value>\n  </data>\n  <data name=\"IPC302_Fix2\" xml:space=\"preserve\">\n    <value>Generate an IpcShape for {0} in a new file</value>\n  </data>\n  <data name=\"IPC302_Message\" xml:space=\"preserve\">\n    <value>Configure the IPC behaviors by adding an IpcShape.</value>\n  </data>\n  <data name=\"IPC303\" xml:space=\"preserve\">\n    <value>Add IpcProxyConfigs</value>\n  </data>\n  <data name=\"IPC303_Message\" xml:space=\"preserve\">\n    <value>Configure the IPC behaviors by adding IpcProxyConfigs because no behaviors are specified on the IPC contract type.</value>\n  </data>\n  <data name=\"IPC304\" xml:space=\"preserve\">\n    <value>IpcProxyConfigs are not needed</value>\n  </data>\n  <data name=\"IPC304_Message\" xml:space=\"preserve\">\n    <value>All the behaviors are specified by the IPC contract type, so every value here does not work.</value>\n  </data>\n  <data name=\"IPC305\" xml:space=\"preserve\">\n    <value>Set IgnoresIpcException</value>\n  </data>\n  <data name=\"IPC305_Message\" xml:space=\"preserve\">\n    <value>For better readability, IgnoresIpcException is recommended to set so that the developers know that there may have been some IPC exceptions here.</value>\n  </data>\n  <data name=\"IPC306\" xml:space=\"preserve\">\n    <value>IgnoresIpcException is not needed</value>\n  </data>\n  <data name=\"IPC306_Message\" xml:space=\"preserve\">\n    <value>IgnoresIpcException is not needed because it has been set on the IPC contract type.</value>\n  </data>\n  <data name=\"IPC307\" xml:space=\"preserve\">\n    <value>Set Timeout</value>\n  </data>\n  <data name=\"IPC307_Message\" xml:space=\"preserve\">\n    <value>Set timeout explicitly.</value>\n  </data>\n  <data name=\"IPC308\" xml:space=\"preserve\">\n    <value>Timeout is not needed</value>\n  </data>\n  <data name=\"IPC308_Message\" xml:space=\"preserve\">\n    <value>Timeout is not needed because it has been set on the IPC contract type.</value>\n  </data>\n  <data name=\"IPC309\" xml:space=\"preserve\">\n    <value>The type argument is not an IpcShape</value>\n  </data>\n  <data name=\"IPC309_Message\" xml:space=\"preserve\">\n    <value>The type argument {0} is not an IpcShape.</value>\n  </data>\n  <data name=\"IPC310\" xml:space=\"preserve\">\n    <value>The IPC proxy type does not match the IPC contract type</value>\n  </data>\n  <data name=\"IPC310_Message\" xml:space=\"preserve\">\n    <value>The IPC proxy type {0} does not match the IPC contract type {1}.</value>\n  </data>\n</root>"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Properties/Localizations.zh-hans.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!--\n    Microsoft ResX Schema\n\n    Version 2.0\n\n    The primary goals of this format is to allow a simple XML format\n    that is mostly human readable. The generation and parsing of the\n    various data types are done through the TypeConverter classes\n    associated with the data types.\n\n    Example:\n\n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n\n    There are any number of \"resheader\" rows that contain simple\n    name/value pairs.\n\n    Each data row contains a name, and value. The row also contains a\n    type or mimetype. Type corresponds to a .NET class that support\n    text/value conversion through the TypeConverter architecture.\n    Classes that don't support this are serialized and stored with the\n    mimetype set.\n\n    The mimetype is used for serialized objects, and tells the\n    ResXResourceReader how to depersist the object. This is currently not\n    extensible. For a given mimetype the value must be set accordingly:\n\n    Note - application/x-microsoft.net.object.binary.base64 is the format\n    that the ResXResourceWriter will generate, however the reader can\n    read any of the formats listed below.\n\n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with\n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array\n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n              <xsd:attribute ref=\"xml:space\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <data name=\"IPC000\" xml:space=\"preserve\">\n    <value>IPC 未知错误</value>\n  </data>\n  <data name=\"IPC000_Message\" xml:space=\"preserve\">\n    <value>生成 IPC 代理和对接代码时遇到未知错误：{0}</value>\n  </data>\n  <data name=\"IPC001\" xml:space=\"preserve\">\n    <value>IPC 已知编译错误</value>\n  </data>\n  <data name=\"IPC001_Message\" xml:space=\"preserve\">\n    <value>生成代码时出现已知错误。本生成器不会报告此错误，因为后续编译器会准确报告之。</value>\n  </data>\n  <data name=\"IPC002\" xml:space=\"preserve\">\n    <value>IPC 已知诊断错误</value>\n  </data>\n  <data name=\"IPC002_Message\" xml:space=\"preserve\">\n    <value>生成代码时出现已知错误。本生成器不会报告此错误，因为分析器会准确报告之。</value>\n  </data>\n  <data name=\"IPC101\" xml:space=\"preserve\">\n    <value>IPC 超时时间不可设为负数</value>\n  </data>\n  <data name=\"IPC101_Message\" xml:space=\"preserve\">\n    <value>IPC 超时时间不可设为负数，但这里被设为 {0} 毫秒。</value>\n  </data>\n  <data name=\"IPC102\" xml:space=\"preserve\">\n    <value>超时时间为零</value>\n  </data>\n  <data name=\"IPC102_Message\" xml:space=\"preserve\">\n    <value>0 毫秒的超时时间相当于不设置超时，因此不需要设置此属性。</value>\n  </data>\n  <data name=\"IPC131\" xml:space=\"preserve\">\n    <value>显式允许或忽略 IPC 异常</value>\n  </data>\n  <data name=\"IPC131_Fix1\" xml:space=\"preserve\">\n    <value>忽略 IPC 连接和超时异常</value>\n  </data>\n  <data name=\"IPC131_Fix2\" xml:space=\"preserve\">\n    <value>允许抛出 IPC 连接和超时异常</value>\n  </data>\n  <data name=\"IPC131_Message\" xml:space=\"preserve\">\n    <value>建议显式设置 IgnoresIpcException 以明确处理这里可能出现的 IPC 连接和超时异常。</value>\n  </data>\n  <data name=\"IPC160\" xml:space=\"preserve\">\n    <value>IPC 契约类型必须是接口</value>\n  </data>\n  <data name=\"IPC160_Message\" xml:space=\"preserve\">\n    <value>IPC 契约类型必须是接口，但 {0} 不是。</value>\n  </data>\n  <data name=\"IPC161\" xml:space=\"preserve\">\n    <value>IPC 契约类型与实现的接口不匹配</value>\n  </data>\n  <data name=\"IPC161_Fix1\" xml:space=\"preserve\">\n    <value>修改契约类型为 {0}</value>\n  </data>\n  <data name=\"IPC161_Fix2\" xml:space=\"preserve\">\n    <value>使 {0} 实现 {1} 接口</value>\n  </data>\n  <data name=\"IPC161_Message\" xml:space=\"preserve\">\n    <value>{0} 标记了 IPC 形状代理契约类型是 {1}，却并没有（完全）实现这个接口。</value>\n  </data>\n  <data name=\"IPC162\" xml:space=\"preserve\">\n    <value>显式标记所有 IPC 成员</value>\n  </data>\n  <data name=\"IPC162_Message\" xml:space=\"preserve\">\n    <value>IPC 形状代理应显式标记所有成员的 IPC 行为，因为其无法继承契约接口的代理配置。</value>\n  </data>\n  <data name=\"IPC200\" xml:space=\"preserve\">\n    <value>IPC 成员仅支持属性、方法和事件</value>\n  </data>\n  <data name=\"IPC200_Message\" xml:space=\"preserve\">\n    <value>不支持的 IPC 成员 {0}。IPC 成员仅支持属性、方法和事件，不支持其他类型。</value>\n  </data>\n  <data name=\"IPC201\" xml:space=\"preserve\">\n    <value>空的 IPC 特性没有任何用途</value>\n  </data>\n  <data name=\"IPC201_Fix\" xml:space=\"preserve\">\n    <value>删除 {0}</value>\n  </data>\n  <data name=\"IPC201_Message\" xml:space=\"preserve\">\n    <value>空的 {0} 没有任何用途，因此完全可以将其删除。</value>\n  </data>\n  <data name=\"IPC202\" xml:space=\"preserve\">\n    <value>标记 IPC 成员</value>\n  </data>\n  <data name=\"IPC202_Message\" xml:space=\"preserve\">\n    <value>建议为 IPC 形状代理的 {0} 成员配置代理行为。</value>\n  </data>\n  <data name=\"IPC240\" xml:space=\"preserve\">\n    <value>不建议使用 IPC 属性</value>\n  </data>\n  <data name=\"IPC240_Message\" xml:space=\"preserve\">\n    <value>不建议在 IPC 类型中使用属性。应改用异步方法，以避免同步访问属性可能引发的卡顿和死锁风险。</value>\n  </data>\n  <data name=\"IPC241\" xml:space=\"preserve\">\n    <value>IPC 类型不支持只写属性</value>\n  </data>\n  <data name=\"IPC241_Message\" xml:space=\"preserve\">\n    <value>不支持只写属性 {0}。IPC 类型只支持读写属性或只读属性。</value>\n  </data>\n  <data name=\"IPC242\" xml:space=\"preserve\">\n    <value>IPC 默认值不起作用</value>\n  </data>\n  <data name=\"IPC242_Fix1\" xml:space=\"preserve\">\n    <value>去掉此成员的 IPC 默认值</value>\n  </data>\n  <data name=\"IPC242_Fix2\" xml:space=\"preserve\">\n    <value>忽略此成员抛出的 IPC 连接和超时异常</value>\n  </data>\n  <data name=\"IPC242_Message\" xml:space=\"preserve\">\n    <value>不需要设置默认值。IPC 默认值仅在忽略了 IPC 连接和超时异常的情况下才会生效。</value>\n  </data>\n  <data name=\"IPC243\" xml:space=\"preserve\">\n    <value>不需要设置 IsReadonly 属性</value>\n  </data>\n  <data name=\"IPC243_Message\" xml:space=\"preserve\">\n    <value>不需要设置 IsReadonly 属性为 false，因为默认如此。</value>\n  </data>\n  <data name=\"IPC244\" xml:space=\"preserve\">\n    <value>默认值类型与属性类型不匹配</value>\n  </data>\n  <data name=\"IPC244_Message\" xml:space=\"preserve\">\n    <value>IPC 默认值 {0} 与 {1} 属性的类型不匹配。</value>\n  </data>\n  <data name=\"IPC245\" xml:space=\"preserve\">\n    <value>字符串将被编译为代码片段</value>\n  </data>\n  <data name=\"IPC245_Message\" xml:space=\"preserve\">\n    <value>试图将 object 类型的默认值设置为代码片段 {0}。请注意，这里的字符串将被编译为代码片段，如需使用字符串，请修改为“@\"\"\"{0}\"\"\"”。</value>\n  </data>\n  <data name=\"IPC246\" xml:space=\"preserve\">\n    <value>字符串将被编译为代码片段</value>\n  </data>\n  <data name=\"IPC246_Message\" xml:space=\"preserve\">\n    <value>试图将 {1} 类型的默认值设置为代码片段 {0}。请注意，这里的字符串将被编译为代码片段。</value>\n  </data>\n  <data name=\"IPC247\" xml:space=\"preserve\">\n    <value>字符串编译的代码片段无法被解析</value>\n  </data>\n  <data name=\"IPC247_Message\" xml:space=\"preserve\">\n    <value>字符串中的代码片段 {0} 在当前上下文下无法被解析。</value>\n  </data>\n  <data name=\"IPC248\" xml:space=\"preserve\">\n    <value>不支持的 IPC 属性类型</value>\n  </data>\n  <data name=\"IPC248_Message\" xml:space=\"preserve\">\n    <value>属性 {0} 的类型 {1} 不支持通过 IPC 传输。请确保类型是基础类型、可被 Json 序列化的类型或标记了 IpcPublic 的 IPC 公开类型。</value>\n  </data>\n  <data name=\"IPC260\" xml:space=\"preserve\">\n    <value>不建议使用 IPC 同步方法</value>\n  </data>\n  <data name=\"IPC260_Message\" xml:space=\"preserve\">\n    <value>不建议在 IPC 类型中使用同步。应改用异步方法，以避免同步调用可能引发的卡顿和死锁风险。</value>\n  </data>\n  <data name=\"IPC261\" xml:space=\"preserve\">\n    <value>IPC 默认值不起作用</value>\n  </data>\n  <data name=\"IPC261_Fix1\" xml:space=\"preserve\">\n    <value>去掉此成员的 IPC 默认值</value>\n  </data>\n  <data name=\"IPC261_Fix2\" xml:space=\"preserve\">\n    <value>忽略此成员抛出的 IPC 连接和超时异常</value>\n  </data>\n  <data name=\"IPC261_Message\" xml:space=\"preserve\">\n    <value>不需要设置默认值。IPC 默认值仅在忽略了 IPC 连接和超时异常的情况下才会生效。</value>\n  </data>\n  <data name=\"IPC262\" xml:space=\"preserve\">\n    <value>推荐设置 WaitsVoid 属性</value>\n  </data>\n  <data name=\"IPC262_Message\" xml:space=\"preserve\">\n    <value>在 IPC 访问中难以得知 void 方法调用时是否会等待其返回。虽然默认是不等待，但仍建议显式设置 WaitsVoid 属性以增强此代码的可读性。</value>\n  </data>\n  <data name=\"IPC263\" xml:space=\"preserve\">\n    <value>WaitsVoid 不起作用</value>\n  </data>\n  <data name=\"IPC263_Message\" xml:space=\"preserve\">\n    <value>方法 {0} 有返回值，因此 WaitsVoid 对其不起作用。</value>\n  </data>\n  <data name=\"IPC264\" xml:space=\"preserve\">\n    <value>默认值类型与方法返回值类型不匹配</value>\n  </data>\n  <data name=\"IPC264_Message\" xml:space=\"preserve\">\n    <value>IPC 默认值 {0} 与 {1} 方法的返回值类型不匹配。</value>\n  </data>\n  <data name=\"IPC265\" xml:space=\"preserve\">\n    <value>void 方法的 IPC 默认值不起作用</value>\n  </data>\n  <data name=\"IPC265_Message\" xml:space=\"preserve\">\n    <value>方法 {0} 没有返回值，因此无需设置其 IPC 默认值。</value>\n  </data>\n  <data name=\"IPC266\" xml:space=\"preserve\">\n    <value>异步方法 {0} 没有返回值，因此无需设置其 IPC 默认值。</value>\n  </data>\n  <data name=\"IPC266_Message\" xml:space=\"preserve\">\n    <value>Task 返回值方法的 IPC 默认值不起作用</value>\n  </data>\n  <data name=\"IPC267\" xml:space=\"preserve\">\n    <value>字符串将被编译为代码片段</value>\n  </data>\n  <data name=\"IPC267_Message\" xml:space=\"preserve\">\n    <value>试图将 object 类型的默认值设置为代码片段 {0}。请注意，这里的字符串将被编译为代码片段，如需使用字符串，请修改为“@\"\"\"{0}\"\"\"”。</value>\n  </data>\n  <data name=\"IPC268\" xml:space=\"preserve\">\n    <value>字符串将被编译为代码片段</value>\n  </data>\n  <data name=\"IPC268_Message\" xml:space=\"preserve\">\n    <value>试图将 {1} 类型的默认值设置为代码片段 {0}。请注意，这里的字符串将被编译为代码片段。</value>\n  </data>\n  <data name=\"IPC269\" xml:space=\"preserve\">\n    <value>字符串编译的代码片段无法被解析</value>\n  </data>\n  <data name=\"IPC269_Message\" xml:space=\"preserve\">\n    <value>字符串中的代码片段 {0} 在当前上下文下无法被解析。</value>\n  </data>\n  <data name=\"IPC270\" xml:space=\"preserve\">\n    <value>不支持泛型 IPC 方法</value>\n  </data>\n  <data name=\"IPC270_Message\" xml:space=\"preserve\">\n    <value>泛型方法无法成为 IPC 成员，因为无法在运行时协商原本应该在编译时确定的泛型类型。</value>\n  </data>\n  <data name=\"IPC271\" xml:space=\"preserve\">\n    <value>不支持的 IPC 方法参数类型</value>\n  </data>\n  <data name=\"IPC271_Message\" xml:space=\"preserve\">\n    <value>方法 {0} 参数 {1} 的类型 {2} 不支持通过 IPC 传输。请确保类型是基础类型、可被 Json 序列化的类型或标记了 IpcPublic 的 IPC 公开类型。</value>\n  </data>\n  <data name=\"IPC272\" xml:space=\"preserve\">\n    <value>不支持的 IPC 方法返回值类型</value>\n  </data>\n  <data name=\"IPC272_Message\" xml:space=\"preserve\">\n    <value>方法 {0} 的返回值类型 {1} 不支持通过 IPC 传输。请确保类型是基础类型、可被 Json 序列化的类型或标记了 IpcPublic 的 IPC 公开类型。</value>\n  </data>\n  <data name=\"IPC301\" xml:space=\"preserve\">\n    <value>通过 IPC 配置项配置 IPC 代理行为</value>\n  </data>\n  <data name=\"IPC301_Message\" xml:space=\"preserve\">\n    <value>通过 IPC 配置项配置 IPC 代理行为。</value>\n  </data>\n  <data name=\"IPC302\" xml:space=\"preserve\">\n    <value>通过 IPC 形状代理配置代理行为</value>\n  </data>\n  <data name=\"IPC302_Fix1\" xml:space=\"preserve\">\n    <value>为 {0} 生成 IPC 形状代理</value>\n  </data>\n  <data name=\"IPC302_Fix2\" xml:space=\"preserve\">\n    <value>在新文件中为 {0} 生成 IPC 形状代理</value>\n  </data>\n  <data name=\"IPC302_Message\" xml:space=\"preserve\">\n    <value>通过 IPC 形状代理配置代理行为。</value>\n  </data>\n  <data name=\"IPC303\" xml:space=\"preserve\">\n    <value>传入 IPC 配置项来配置 IPC 代理行为</value>\n  </data>\n  <data name=\"IPC303_Message\" xml:space=\"preserve\">\n    <value>IPC 契约类型上没有指定代理行为，可以通过在这里传入 IPC 配置项来配置。</value>\n  </data>\n  <data name=\"IPC304\" xml:space=\"preserve\">\n    <value>无需传入 IPC 配置项</value>\n  </data>\n  <data name=\"IPC304_Message\" xml:space=\"preserve\">\n    <value>IPC 契约类型上指定的代理行为已经覆盖了这里的所有 IPC 配置项，因此这里无需额外传入。</value>\n  </data>\n  <data name=\"IPC305\" xml:space=\"preserve\">\n    <value>建议显式设置 IgnoresIpcException</value>\n  </data>\n  <data name=\"IPC305_Message\" xml:space=\"preserve\">\n    <value>建议显式设置 IgnoresIpcException 以明确处理这里可能出现的 IPC 连接和超时异常。</value>\n  </data>\n  <data name=\"IPC306\" xml:space=\"preserve\">\n    <value>无需设置 IgnoresIpcException</value>\n  </data>\n  <data name=\"IPC306_Message\" xml:space=\"preserve\">\n    <value>无需设置 IgnoresIpcException，因为 IPC 契约类型上已经设置了此属性。</value>\n  </data>\n  <data name=\"IPC307\" xml:space=\"preserve\">\n    <value>显式设置 Timeout</value>\n  </data>\n  <data name=\"IPC307_Message\" xml:space=\"preserve\">\n    <value>显式设置 Timeout。</value>\n  </data>\n  <data name=\"IPC308\" xml:space=\"preserve\">\n    <value>无需设置 Timeout</value>\n  </data>\n  <data name=\"IPC308_Message\" xml:space=\"preserve\">\n    <value>无需设置 Timeout，因为 IPC 契约类型上已经设置了此属性。</value>\n  </data>\n  <data name=\"IPC309\" xml:space=\"preserve\">\n    <value>不是有效的 IPC 形状代理</value>\n  </data>\n  <data name=\"IPC309_Message\" xml:space=\"preserve\">\n    <value>指定的 IpcShape 参数 {0} 不是有效的 IPC 形状代理。</value>\n  </data>\n  <data name=\"IPC310\" xml:space=\"preserve\">\n    <value>契约接口与形状代理不匹配</value>\n  </data>\n  <data name=\"IPC310_Message\" xml:space=\"preserve\">\n    <value>指定的 IpcShape 参数 {0} 不是针对 {1} 的 IPC 形状代理。</value>\n  </data>\n</root>\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/Properties/launchSettings.json",
    "content": "﻿{\n  \"$schema\": \"https://json.schemastore.org/launchsettings.json\",\n  \"profiles\": {\n    \"DotNetCampus.CommandLine.Tests\": {\n      \"commandName\": \"DebugRoslynComponent\",\n      \"targetProject\": \"../../tests/dotnetCampus.Ipc.Tests/dotnetCampus.Ipc.Tests.csproj\"\n    },\n    \"DotNetCampus.CommandLine.Performance\": {\n      \"commandName\": \"DebugRoslynComponent\",\n      \"targetProject\": \"../../demo/IpcRemotingObjectDemo/IpcRemotingObjectServerDemo/IpcRemotingObjectServerDemo.csproj\"\n    }\n  }\n}\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.Analyzers/dotnetCampus.Ipc.Analyzers.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netstandard2.0</TargetFramework>\n    <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n    <Nullable>enable</Nullable>\n    <RootNamespace>dotnetCampus.Ipc</RootNamespace>\n    <DefineConstants>$(DefineConstants);IPC_ANALYZER</DefineConstants>\n    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"DotNetCampus.CodeAnalysisUtils\" />\n    <PackageReference Include=\"dotnetCampus.LatestCSharpFeatures\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.Analyzers\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.Workspaces\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Include=\"..\\dotnetCampus.Ipc\\CompilerServices\\Attributes\\**\\*.cs\" Link=\"Attributes\\%(RecursiveDir)%(FileName)%(Extension)\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Remove=\"SourceProject\\**\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Update=\"Properties\\Localizations.Designer.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>Localizations.resx</DependentUpon>\n    </Compile>\n    <EmbeddedResource Update=\"Properties\\Localizations.resx\">\n      <Generator>PublicResXFileCodeGenerator</Generator>\n      <LastGenOutput>Localizations.Designer.cs</LastGenOutput>\n    </EmbeddedResource>\n    <EmbeddedResource Update=\"Properties\\Localizations.*.resx\">\n      <DependentUpon>Localizations.resx</DependentUpon>\n    </EmbeddedResource>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/dotnetCampus.Ipc.PipeMvc/dotnetCampus.Ipc.PipeMvc.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <Description>Pipe</Description>\n    <TargetFramework>net5.0</TargetFramework>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <Nullable>enable</Nullable>\n    <OutputType>Library</OutputType>\n    <IsPackable>true</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.AspNetCore.Hosting\" Version=\"2.2.7\" />\n    <PackageReference Include=\"System.IO.Pipelines\" Version=\"6.0.0\" />\n    <!-- <PackageReference Include=\"Microsoft.Extensions.HostFactoryResolver.Sources\" /> -->\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/IgnoresIpcExceptionAnalyzerTests.cs",
    "content": "﻿using System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.CodeAnalysis.Core;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nusing VerifyCS = dotnetCampus.Ipc.Analyzers.Tests.CSharpCodeFixVerifier<\n    dotnetCampus.Ipc.Analyzers.IgnoresIpcExceptionIsRecommendedAnalyzer,\n    dotnetCampus.Ipc.CodeFixes.IgnoresIpcExceptionIsRecommendedCodeFixProvider>;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\n\n[TestClass]\npublic class IgnoresIpcExceptionAnalyzerTests\n{\n    //    [TestMethod]\n    //    public async Task TestMethod1()\n    //    {\n    //        var test = @\"\";\n\n    //        await VerifyCS.VerifyAnalyzerAsync(test);\n    //    }\n\n    //    [TestMethod]\n    //    public async Task TestMethod2()\n    //    {\n    //        var test = @\"\n    //using System;\n    //using System.Reflection;\n    //using System.Threading;\n    //using System.Threading.Tasks;\n\n    //using dotnetCampus.Ipc.CompilerServices.Attributes;\n\n    //namespace ConsoleApplication1\n    //{\n    //    [IpcPublic(typeof(IFakeIpcObject))]\n    //    internal class FakeIpcObject : IFakeIpcObject\n    //    {\n    //    }\n    //}\";\n\n    //        var fixtest = @\"\n    //using System;\n    //using System.Reflection;\n    //using System.Threading;\n    //using System.Threading.Tasks;\n\n    //using dotnetCampus.Ipc.CompilerServices.Attributes;\n\n    //namespace ConsoleApplication1\n    //{\n    //    [IpcPublic(typeof(IFakeIpcObject), IgnoresIpcException = true)]\n    //    internal class FakeIpcObject : IFakeIpcObject\n    //    {\n    //    }\n    //}\";\n\n    //        var expected = VerifyCS.Diagnostic(Diagnostics.DIPC101_IgnoresIpcExceptionIsRecommended);\n    //        await VerifyCS.VerifyCodeFixAsync(test, expected, fixtest);\n    //    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpAnalyzerVerifier`1+Test.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CSharp.Testing;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class CSharpAnalyzerVerifier<TAnalyzer>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n{\n    public class Test : CSharpAnalyzerTest<TAnalyzer, MSTestVerifier>\n    {\n        public Test()\n        {\n            SolutionTransforms.Add((solution, projectId) =>\n            {\n                var compilationOptions = solution.GetProject(projectId).CompilationOptions;\n                compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(\n                    compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));\n                solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);\n\n                return solution;\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Testing;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class CSharpAnalyzerVerifier<TAnalyzer>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n{\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic()\"/>\n    public static DiagnosticResult Diagnostic()\n        => CSharpAnalyzerVerifier<TAnalyzer, MSTestVerifier>.Diagnostic();\n\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic(string)\"/>\n    public static DiagnosticResult Diagnostic(string diagnosticId)\n        => CSharpAnalyzerVerifier<TAnalyzer, MSTestVerifier>.Diagnostic(diagnosticId);\n\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic(DiagnosticDescriptor)\"/>\n    public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)\n        => CSharpAnalyzerVerifier<TAnalyzer, MSTestVerifier>.Diagnostic(descriptor);\n\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.VerifyAnalyzerAsync(string, DiagnosticResult[])\"/>\n    public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpCodeFixVerifier`2+Test.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CodeFixes;\nusing Microsoft.CodeAnalysis.CSharp.Testing;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class CSharpCodeFixVerifier<TAnalyzer, TCodeFix>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n    where TCodeFix : CodeFixProvider, new()\n{\n    public class Test : CSharpCodeFixTest<TAnalyzer, TCodeFix, MSTestVerifier>\n    {\n        public Test()\n        {\n            SolutionTransforms.Add((solution, projectId) =>\n            {\n                var compilationOptions = solution.GetProject(projectId).CompilationOptions;\n                compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(\n                    compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));\n                solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);\n\n                return solution;\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpCodeFixVerifier`2.cs",
    "content": "﻿using System.Collections.Immutable;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing Microsoft.CodeAnalysis.CSharp.Testing;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class CSharpCodeFixVerifier<TAnalyzer, TCodeFix>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n    where TCodeFix : CodeFixProvider, new()\n{\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.Diagnostic()\"/>\n    public static DiagnosticResult Diagnostic()\n        => CSharpCodeFixVerifier<TAnalyzer, TCodeFix, MSTestVerifier>.Diagnostic();\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.Diagnostic(string)\"/>\n    public static DiagnosticResult Diagnostic(string diagnosticId)\n        => CSharpCodeFixVerifier<TAnalyzer, TCodeFix, MSTestVerifier>.Diagnostic(diagnosticId);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.Diagnostic(DiagnosticDescriptor)\"/>\n    public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)\n        => CSharpCodeFixVerifier<TAnalyzer, TCodeFix, MSTestVerifier>.Diagnostic(descriptor);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyAnalyzerAsync(string, DiagnosticResult[])\"/>\n    public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, string)\"/>\n    public static async Task VerifyCodeFixAsync(string source, string fixedSource)\n        => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult, string)\"/>\n    public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)\n        => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult[], string)\"/>\n    public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n            FixedCode = fixedSource,\n            ReferenceAssemblies = ReferenceAssemblies.Default.AddAssemblies(\n                ImmutableArray.Create(typeof(IpcPublicAttribute).Assembly.Location)),\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CodeRefactorings;\nusing Microsoft.CodeAnalysis.CSharp.Testing;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class CSharpCodeRefactoringVerifier<TCodeRefactoring>\n    where TCodeRefactoring : CodeRefactoringProvider, new()\n{\n    public class Test : CSharpCodeRefactoringTest<TCodeRefactoring, MSTestVerifier>\n    {\n        public Test()\n        {\n            SolutionTransforms.Add((solution, projectId) =>\n            {\n                var compilationOptions = solution.GetProject(projectId).CompilationOptions;\n                compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(\n                    compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));\n                solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);\n\n                return solution;\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpCodeRefactoringVerifier`1.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\n\nusing Microsoft.CodeAnalysis.CodeRefactorings;\nusing Microsoft.CodeAnalysis.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class CSharpCodeRefactoringVerifier<TCodeRefactoring>\n    where TCodeRefactoring : CodeRefactoringProvider, new()\n{\n    /// <inheritdoc cref=\"CodeRefactoringVerifier{TCodeRefactoring, TTest, TVerifier}.VerifyRefactoringAsync(string, string)\"/>\n    public static async Task VerifyRefactoringAsync(string source, string fixedSource)\n    {\n        await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);\n    }\n\n    /// <inheritdoc cref=\"CodeRefactoringVerifier{TCodeRefactoring, TTest, TVerifier}.VerifyRefactoringAsync(string, DiagnosticResult, string)\"/>\n    public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource)\n    {\n        await VerifyRefactoringAsync(source, new[] { expected }, fixedSource);\n    }\n\n    /// <inheritdoc cref=\"CodeRefactoringVerifier{TCodeRefactoring, TTest, TVerifier}.VerifyRefactoringAsync(string, DiagnosticResult[], string)\"/>\n    public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n            FixedCode = fixedSource,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/CSharpVerifierHelper.cs",
    "content": "﻿using System;\nusing System.Collections.Immutable;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\ninternal static class CSharpVerifierHelper\n{\n    /// <summary>\n    /// By default, the compiler reports diagnostics for nullable reference types at\n    /// <see cref=\"DiagnosticSeverity.Warning\"/>, and the analyzer test framework defaults to only validating\n    /// diagnostics at <see cref=\"DiagnosticSeverity.Error\"/>. This map contains all compiler diagnostic IDs\n    /// related to nullability mapped to <see cref=\"ReportDiagnostic.Error\"/>, which is then used to enable all\n    /// of these warnings for default validation during analyzer and code fix tests.\n    /// </summary>\n    internal static ImmutableDictionary<string, ReportDiagnostic> NullableWarnings { get; } = GetNullableWarningsFromCompiler();\n\n    private static ImmutableDictionary<string, ReportDiagnostic> GetNullableWarningsFromCompiler()\n    {\n        string[] args = { \"/warnaserror:nullable\" };\n        var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);\n        var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;\n\n        // Workaround for https://github.com/dotnet/roslyn/issues/41610\n        nullableWarnings = nullableWarnings\n            .SetItem(\"CS8632\", ReportDiagnostic.Error)\n            .SetItem(\"CS8669\", ReportDiagnostic.Error);\n\n        return nullableWarnings;\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs",
    "content": "﻿using Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\nusing Microsoft.CodeAnalysis.VisualBasic.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class VisualBasicAnalyzerVerifier<TAnalyzer>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n{\n    public class Test : VisualBasicAnalyzerTest<TAnalyzer, MSTestVerifier>\n    {\n        public Test()\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/VisualBasicAnalyzerVerifier`1.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\nusing Microsoft.CodeAnalysis.VisualBasic.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class VisualBasicAnalyzerVerifier<TAnalyzer>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n{\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic()\"/>\n    public static DiagnosticResult Diagnostic()\n        => VisualBasicAnalyzerVerifier<TAnalyzer, MSTestVerifier>.Diagnostic();\n\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic(string)\"/>\n    public static DiagnosticResult Diagnostic(string diagnosticId)\n        => VisualBasicAnalyzerVerifier<TAnalyzer, MSTestVerifier>.Diagnostic(diagnosticId);\n\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.Diagnostic(DiagnosticDescriptor)\"/>\n    public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)\n        => VisualBasicAnalyzerVerifier<TAnalyzer, MSTestVerifier>.Diagnostic(descriptor);\n\n    /// <inheritdoc cref=\"AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.VerifyAnalyzerAsync(string, DiagnosticResult[])\"/>\n    public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CodeFixes;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\nusing Microsoft.CodeAnalysis.VisualBasic.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n    where TCodeFix : CodeFixProvider, new()\n{\n    public class Test : VisualBasicCodeFixTest<TAnalyzer, TCodeFix, MSTestVerifier>\n    {\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/VisualBasicCodeFixVerifier`2.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\n\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CodeFixes;\nusing Microsoft.CodeAnalysis.Diagnostics;\nusing Microsoft.CodeAnalysis.Testing;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\nusing Microsoft.CodeAnalysis.VisualBasic.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix>\n    where TAnalyzer : DiagnosticAnalyzer, new()\n    where TCodeFix : CodeFixProvider, new()\n{\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.Diagnostic()\"/>\n    public static DiagnosticResult Diagnostic()\n        => VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, MSTestVerifier>.Diagnostic();\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.Diagnostic(string)\"/>\n    public static DiagnosticResult Diagnostic(string diagnosticId)\n        => VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, MSTestVerifier>.Diagnostic(diagnosticId);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.Diagnostic(DiagnosticDescriptor)\"/>\n    public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)\n        => VisualBasicCodeFixVerifier<TAnalyzer, TCodeFix, MSTestVerifier>.Diagnostic(descriptor);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyAnalyzerAsync(string, DiagnosticResult[])\"/>\n    public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, string)\"/>\n    public static async Task VerifyCodeFixAsync(string source, string fixedSource)\n        => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult, string)\"/>\n    public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)\n        => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource);\n\n    /// <inheritdoc cref=\"CodeFixVerifier{TAnalyzer, TCodeFix, TTest, TVerifier}.VerifyCodeFixAsync(string, DiagnosticResult[], string)\"/>\n    public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n            FixedCode = fixedSource,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs",
    "content": "﻿using Microsoft.CodeAnalysis.CodeRefactorings;\nusing Microsoft.CodeAnalysis.Testing.Verifiers;\nusing Microsoft.CodeAnalysis.VisualBasic.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class VisualBasicCodeRefactoringVerifier<TCodeRefactoring>\n    where TCodeRefactoring : CodeRefactoringProvider, new()\n{\n    public class Test : VisualBasicCodeRefactoringTest<TCodeRefactoring, MSTestVerifier>\n    {\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs",
    "content": "﻿using System.Threading;\nusing System.Threading.Tasks;\n\nusing Microsoft.CodeAnalysis.CodeRefactorings;\nusing Microsoft.CodeAnalysis.Testing;\n\nnamespace dotnetCampus.Ipc.Analyzers.Tests;\npublic static partial class VisualBasicCodeRefactoringVerifier<TCodeRefactoring>\n    where TCodeRefactoring : CodeRefactoringProvider, new()\n{\n    /// <inheritdoc cref=\"CodeRefactoringVerifier{TCodeRefactoring, TTest, TVerifier}.VerifyRefactoringAsync(string, string)\"/>\n    public static async Task VerifyRefactoringAsync(string source, string fixedSource)\n    {\n        await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);\n    }\n\n    /// <inheritdoc cref=\"CodeRefactoringVerifier{TCodeRefactoring, TTest, TVerifier}.VerifyRefactoringAsync(string, DiagnosticResult, string)\"/>\n    public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource)\n    {\n        await VerifyRefactoringAsync(source, new[] { expected }, fixedSource);\n    }\n\n    /// <inheritdoc cref=\"CodeRefactoringVerifier{TCodeRefactoring, TTest, TVerifier}.VerifyRefactoringAsync(string, DiagnosticResult[], string)\"/>\n    public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource)\n    {\n        var test = new Test\n        {\n            TestCode = source,\n            FixedCode = fixedSource,\n        };\n\n        test.ExpectedDiagnostics.AddRange(expected);\n        await test.RunAsync(CancellationToken.None);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Analyzers.Tests/dotnetCampus.Ipc.Analyzers.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netcoreapp3.1</TargetFramework>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.CodeAnalysis\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.MSTest\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.MSTest\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.CodeRefactoring.Testing.MSTest\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.VisualBasic.Analyzer.Testing.MSTest\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.VisualBasic.CodeFix.Testing.MSTest\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.VisualBasic.CodeRefactoring.Testing.MSTest\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" />\n    <PackageReference Include=\"MSTest.TestAdapter\" />\n    <PackageReference Include=\"MSTest.TestFramework\" />\n    <PackageReference Include=\"coverlet.collector\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Moq\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc.Analyzers\\dotnetCampus.Ipc.Analyzers.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.FakeTests/FakeApis/IRemoteFakeIpcArgumentOrReturn.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.FakeTests.FakeApis;\n[IpcPublic]\npublic interface IRemoteFakeIpcArgumentOrReturn\n{\n    string Value { get; set; }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.FakeTests/FakeApis/IRemoteFakeIpcObject.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.FakeTests.FakeApis;\n[IpcPublic]\npublic interface IRemoteFakeIpcObject\n{\n    [IpcMethod(IgnoresIpcException = false)]\n    Task<IRemoteFakeIpcArgumentOrReturn> MethodWithIpcParameterAsync(IRemoteFakeIpcArgumentOrReturn ipcArgument, string changeValue);\n\n    [IpcMethod(IgnoresIpcException = true, Timeout = 2000)]\n    Task ShutdownAsync();\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.FakeTests/FakeApis/RemoteFakeIpcObject.cs",
    "content": "﻿using dotnetCampus.Ipc.Tests.CompilerServices.FakeRemote;\n\nnamespace dotnetCampus.Ipc.FakeTests.FakeApis;\npublic class RemoteFakeIpcObject : IRemoteFakeIpcObject\n{\n    private readonly TaskCompletionSource _source = new();\n    private readonly IRemoteFakeIpcArgumentOrReturn? _privateReturn = new RemoteIpcReturn(\"private\");\n\n    public async Task<IRemoteFakeIpcArgumentOrReturn> MethodWithIpcParameterAsync(IRemoteFakeIpcArgumentOrReturn ipcArgument, string changeValue)\n    {\n        // 修改来自参数所在进程的 IPC 对象的值。\n        await Task.Run(() =>\n        {\n            ipcArgument.Value = changeValue;\n        });\n\n        // 返回自己进程的值给对方进程。\n        return _privateReturn;\n    }\n\n    Task IRemoteFakeIpcObject.ShutdownAsync()\n    {\n        _source.SetResult();\n        return Task.CompletedTask;\n    }\n\n    internal Task WaitForShutdownAsync()\n    {\n        return _source.Task;\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.FakeTests/FakeApis/RemoteIpcReturn.cs",
    "content": "﻿using dotnetCampus.Ipc.FakeTests.FakeApis;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices.FakeRemote;\n\npublic class RemoteIpcReturn : IRemoteFakeIpcArgumentOrReturn\n{\n    public RemoteIpcReturn(string value)\n    {\n        Value = value;\n    }\n\n    public string Value { get; set; }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.FakeTests/Program.cs",
    "content": "﻿using DotNetCampus.Cli;\nusing DotNetCampus.Cli.Compiler;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.FakeTests.FakeApis;\nusing dotnetCampus.Ipc.Pipes;\n\nnamespace dotnetCampus.Ipc.FakeTests;\n\ninternal static class Program\n{\n    private static async Task Main(string[] args)\n    {\n        Console.WriteLine(\"【IPC 远程进程单元测试】\");\n        var ipcPeerName = CommandLine.Parse(args).As<CommandLineOptions>().IpcPeerName;\n        var ipcProvider = new IpcProvider(ipcPeerName);\n        Console.WriteLine($\"IPC 服务启动中 [{ipcPeerName}] ...\");\n        ipcProvider.StartServer();\n        Console.CursorTop--;\n        Console.WriteLine(\"IPC 服务已启动\");\n        var jointObject = new RemoteFakeIpcObject();\n        Console.WriteLine(\"IPC 对接创建中...\");\n        ipcProvider.CreateIpcJoint<IRemoteFakeIpcObject>(jointObject);\n        Console.CursorTop--;\n        Console.WriteLine(\"IPC 对接已创建\");\n        Console.ForegroundColor = ConsoleColor.Yellow;\n        Console.WriteLine(\"IPC 交互中...\");\n        Console.ResetColor();\n        await jointObject.WaitForShutdownAsync().WaitAsync(TimeSpan.FromSeconds(3));\n        Console.WriteLine(\"IPC 收到退出信号\");\n        Console.WriteLine(\"IPC 远程进程单元测试已退出\");\n    }\n}\n\ninternal record CommandLineOptions\n{\n    [Option(\"peer-name\")]\n    public required string IpcPeerName { get; init; }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.FakeTests/dotnetCampus.Ipc.FakeTests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <!--\n      请保持 net6.0 不变，不可升级到 net8.0：\n      1. 在 net6.0 下我们将使用 Newtonsoft.Json 作为 IPC 的底层传输机制；\n      2. 在 net8.0 下我们将使用 System.Text.Json 作为 IPC 的底层传输机制；\n      3. 主测试项目已经是双框架的了，但不能测试这两种机制之间的兼容性，所以需要一个旧框架的测试项目来专门测试。\n    -->\n    <TargetFramework>net6.0</TargetFramework>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <Nullable>enable</Nullable>\n  </PropertyGroup>\n\n  <PropertyGroup>\n    <!--\n      在绝大多数项目里面，我们希望使用最新的 C# 版本；但源生成器必须支持旧版本的 C# 语法以支持旧的 IDE；\n      所以我们选了一个普通代码最少的项目降低 C# 版本，以确保源生成器可以生成旧语法代码的同时，不影响其他项目使用最新的 C# 版本。\n\n      有一台构建机是 Windows Server 2016，最高只能安装 Visual Studio 17.6，最高支持到 .NET 7：\n      https://learn.microsoft.com/en-us/dotnet/core/install/windows#net-versions-and-visual-studio\n\n      .NET 7 对应的 C# 版本是 11.0：\n      https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-versioning\n\n      从 C# 12.0 开始，才可使用集合表达式：\n      https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12#collection-expressions\n    -->\n    <LangVersion>11.0</LangVersion>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"DotNetCampus.CommandLine\" />\n    <PackageReference Include=\"DotNetCampus.LatestCSharpFeatures\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc.Analyzers\\dotnetCampus.Ipc.Analyzers.csproj\" OutputItemType=\"Analyzer\" ReferenceOutputAssembly=\"false\" />\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/AckManagerTest.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Threading.Tasks;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.PipeCore\n{\n    /*\n     当前不使用 ACK 啦，所以这个单元测试跑不过\n    [TestClass]\n    public class AckManagerTest\n    {\n        [ContractTestCase]\n        public void RegisterAckTask()\n        {\n            \"重复注册相同编号的消息，提示错误\".Test(() =>\n            {\n                var clientName = \"lindexi\";\n                Ack ack = 20;\n                var taskCompletionSource = new TaskCompletionSource<bool>();\n\n                var ackTask = new AckTask(clientName, ack, taskCompletionSource, \"调试\");\n                AckManager.RegisterAckTask(ackTask);\n                Assert.ThrowsException<ArgumentException>(() => { AckManager.RegisterAckTask(ackTask); });\n            });\n\n            \"将消息注册，如果没有收到回复，那么注册的任务依然没有完成\".Test(() =>\n            {\n                // 注册的消息可以完成\n                var clientName = \"lindexi\";\n                Ack ack = 2;\n                var taskCompletionSource = new TaskCompletionSource<bool>();\n\n                var ackTask = new AckTask(clientName, ack, taskCompletionSource, \"调试\");\n                AckManager.RegisterAckTask(ackTask);\n                Assert.AreEqual(false, taskCompletionSource.Task.IsCompleted);\n            });\n\n            \"将消息注册，此时设置收到回复，注册的消息可以完成\".Test(() =>\n            {\n                // 注册的消息可以完成\n                var clientName = \"lindexi\";\n                Ack ack = 2;\n                var taskCompletionSource = new TaskCompletionSource<bool>();\n\n                var ackTask = new AckTask(clientName, ack, taskCompletionSource, \"调试\");\n                AckManager.RegisterAckTask(ackTask);\n                AckManager.OnAckReceived(this, new AckArgs(clientName, ack));\n                //Debug.Assert(taskCompletionSource.Task.IsCompleted);\n                Assert.AreEqual(true, taskCompletionSource.Task.IsCompleted);\n            });\n        }\n\n        [ContractTestCase]\n        public void BuildAckMessage()\n        {\n            \"创建的Ack信息可以被解析出创建传入的Ack值\".Test(() =>\n            {\n                // 返回的信息包含当前的 ack 值\n                var buildAckMessage = AckManager.BuildAckMessage(100);\n                using var memoryStream = new MemoryStream(buildAckMessage, false);\n                var isAck = AckManager.IsAckMessage(memoryStream, out var ack);\n\n                Assert.AreEqual(true, isAck);\n                // 100ul 就是 ulong 100 的意思，我担心你看懂，所以特别加了 ulong 的转换，让你以为 ul 是一个有趣的后缀\n                Assert.AreEqual((ulong) 100ul, ack.Value);\n            });\n\n            \"传入不属于 Ack 的信息，可以返回不是 Ack 信息\".Test(() =>\n            {\n                using var memoryStream = new MemoryStream();\n                var streamWriter = new StreamWriter(memoryStream);\n                streamWriter.WriteLine(\"林德熙是逗比\");\n                const long position = 2;\n                memoryStream.Position = position;\n\n                var isAck = AckManager.IsAckMessage(memoryStream, out var ack);\n                Assert.AreEqual(false, isAck);\n                // 如果读取返回不是 Ack 那么将 Stream 设置回传入的 Position 的值\n                Assert.AreEqual(position, memoryStream.Position);\n            });\n        }\n\n        private AckManager AckManager { get; } = new AckManager(new IpcContext(new IpcProvider(), \"123123\"));\n    }\n    */\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/Attributes.cs",
    "content": "﻿using System.Runtime.CompilerServices;\n\n[assembly: InternalsVisibleTo(\"dotnetCampus.Ipc.Tests\")]\n[assembly: InternalsVisibleTo(\"dotnetCampus.Ipc.Demo\")]\n[assembly: InternalsVisibleTo(\"dotnetCampus.Ipc.WpfDemo\")]\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/Fake/FakeIpcObject.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\nusing dotnetCampus.Ipc.Tests.CompilerServices.Fake;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices\n{\n    internal class FakeIpcObject : IFakeIpcObject\n    {\n        private BindingFlags _enumProperty = BindingFlags.Public;\n        private bool _ipcReadonlyProperty = true;\n#nullable enable\n        private INestedFakeIpcArgumentOrReturn? _nestedIpcObject;\n#nullable restore\n\n        public FakeIpcObject()\n        {\n        }\n\n        public FakeIpcObject(INestedFakeIpcArgumentOrReturn jointSideObject)\n        {\n            _nestedIpcObject = jointSideObject;\n        }\n\n#nullable enable\n        public string? NullableStringProperty { get; set; } = \"Title\";\n#nullable restore\n\n#nullable enable\n        public string? NonNullableStringProperty { get; set; } = \"Description\";\n#nullable restore\n\n        public BindingFlags EnumProperty\n        {\n            get => _enumProperty;\n            set => _enumProperty = value;\n        }\n\n        public bool IpcReadonlyProperty => _ipcReadonlyProperty;\n\n        public void SetIpcReadonlyProperty(bool value)\n        {\n            _ipcReadonlyProperty = value;\n        }\n\n        public IntPtr IntPtrProperty { get; } = new IntPtr(1);\n\n        public IntPtr? NullableIntPtrProperty { get; }\n\n        public INestedFakeIpcArgumentOrReturn NestedIpcProperty\n        {\n            get => _nestedIpcObject;\n            set => _nestedIpcObject = value;\n        }\n\n        public List<string>? ListProperty { get; set; } = new List<string>() { \"List1\", \"List2\" };\n\n        public IList<string>? CollectionProperty { get; set; } = new List<string>() { \"Collection1\", \"Collection2\" };\n\n        public string[]? ArrayProperty { get; set; } = new string[] { \"Array1\", \"Array2\" };\n\n        public void WaitsVoidMethod()\n        {\n            Thread.Sleep(100);\n            EnumProperty = BindingFlags.Public | BindingFlags.Instance;\n        }\n\n        public void NonWaitsVoidMethod()\n        {\n            Thread.Sleep(100);\n            EnumProperty = BindingFlags.Public | BindingFlags.Instance;\n        }\n\n        public Task MethodThatIgnoresIpcException()\n        {\n            Thread.Sleep(100);\n            return Task.CompletedTask;\n        }\n\n        public Task MethodThatThrowsIpcException()\n        {\n            Thread.Sleep(100);\n            return Task.CompletedTask;\n        }\n\n        public Task MethodThatHasTimeout()\n        {\n            return Task.Delay(150);\n        }\n\n#nullable enable\n        public async Task<string?> MethodThatHasAsyncNullableReturn()\n        {\n            return null;\n        }\n#nullable restore\n\n#nullable enable\n        public async Task<FakeIpcObjectSubModelA?> MethodThatHasAsyncNullableComplexReturn()\n        {\n            return null;\n        }\n#nullable restore\n\n        public async Task<string> MethodThatHasDefaultReturn()\n        {\n            await Task.Delay(250);\n            return \"xxx\";\n        }\n\n        public async Task<object> MethodThatHasObjectWithObjectDefaultReturn()\n        {\n            await Task.Delay(250);\n            return \"xxx\";\n        }\n\n        public async Task<object> MethodThatHasObjectWithStringDefaultReturn()\n        {\n            await Task.Delay(250);\n            return \"xxx\";\n        }\n\n        // 请不要将这里的 String 改为 string，这是为了测试代码生成器能否处理类型而非关键字。\n        public async Task<String> MethodThatHasStringDefaultReturn()\n        {\n            await Task.Delay(250);\n            return \"xxx\";\n        }\n\n        public async Task<IntPtr> MethodThatHasCustomDefaultReturn()\n        {\n            await Task.Delay(150);\n            return IntPtr.Zero;\n        }\n\n        public async Task<string> MethodThatCannotBeCompiled_MustSetOtherAttributes()\n        {\n            await Task.Delay(150);\n            return \"xxx\";\n        }\n\n        public async Task<List<string>> MethodWithListParametersAndListReturn(List<string> a, List<string> b)\n        {\n            await Task.Delay(1);\n            return a.Concat(b).ToList();\n        }\n\n        public async Task<IList<string>> MethodWithCollectionParametersAndCollectionReturn(IList<string> a, IList<string> b)\n        {\n            await Task.Delay(1);\n            return a.Concat(b).ToList();\n        }\n\n        public async Task<string[]> MethodWithArrayParametersAndArrayReturn(string[] a, string[] b)\n        {\n            await Task.Delay(1);\n            return a.Concat(b).ToArray();\n        }\n\n        public void MethodWithStructParameters(BindingFlags flags)\n        {\n        }\n\n        public bool MethodWithStructReturn()\n        {\n            return IpcReadonlyProperty;\n        }\n\n        public int MethodWithSameParameterCountOverloading(int a, int b)\n        {\n            return a + b;\n        }\n\n        public long MethodWithSameParameterCountOverloading(long a, long b)\n        {\n            return a * b;\n        }\n\n        public IFakeIpcObject.NestedEnum MethodWithNestedEnumReturn()\n        {\n            return IFakeIpcObject.NestedEnum.None;\n        }\n\n        public Task<IFakeIpcObject.NestedEnum> AsyncMethodWithNestedEnumReturn()\n        {\n            return Task.FromResult(IFakeIpcObject.NestedEnum.None);\n        }\n\n        public async Task AsyncMethod()\n        {\n            await Task.Delay(2000);\n        }\n\n        public async Task<INestedFakeIpcArgumentOrReturn> AsyncMethodWithIpcPublicObjectParametersAndIpcPublicObjectReturn(INestedFakeIpcArgumentOrReturn nested, string changeValue)\n        {\n            // 修改来自参数所在进程的 IPC 对象的值。\n            await Task.Run(() =>\n            {\n                nested.Value = changeValue;\n            });\n\n            // 返回自己进程的值给对方进程。\n            return _nestedIpcObject;\n        }\n\n#nullable enable\n        public Task<(double a, uint b, int? c, byte d)> AsyncMethodWithStructParametersAndStructReturn(double a, uint b, int? c, byte d)\n        {\n            return Task.FromResult((a, b, c, d));\n        }\n#nullable restore\n\n#nullable enable\n        public Task<(FakeIpcObjectSubModelA a, FakeIpcObjectSubModelA? b, IntPtr c, IntPtr? d)> AsyncMethodWithComplexValueTupleParametersAndComplexValueTupleReturn(FakeIpcObjectSubModelA a, FakeIpcObjectSubModelA? b, IntPtr c, IntPtr? d)\n        {\n            return Task.FromResult((new FakeIpcObjectSubModelA(), (FakeIpcObjectSubModelA?) null, IntPtr.Zero, (IntPtr?) null));\n        }\n#nullable restore\n\n        public Task<FakeIpcObjectSubModelA> AsyncMethodWithComplexParametersAndComplexReturn(FakeIpcObjectSubModelA model)\n        {\n            return Task.FromResult(new FakeIpcObjectSubModelA(model.A, model.B, model.C, model.D));\n        }\n\n        public Task<string> AsyncMethodWithPrimaryParametersAndPrimaryReturn(string source)\n        {\n            return Task.FromResult(source);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/Fake/FakeIpcObjectSubModelA.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Tests.CompilerServices\n{\n    internal class FakeIpcObjectSubModelA\n    {\n        public FakeIpcObjectSubModelA()\n        {\n        }\n\n        public FakeIpcObjectSubModelA(double a, uint b, int c, byte d)\n        {\n            A = a;\n            B = b;\n            C = c;\n            D = d;\n        }\n\n        public double A { get; set; }\n        public uint B { get; set; }\n        public int C { get; set; }\n        public byte D { get; set; }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/Fake/FakeIpcObjectWithTypeAttributes.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices.Fake;\n\n[IpcPublic(IgnoresIpcException = true, Timeout = 100)]\ninternal interface IFakeIpcObjectWithTypeAttributes : IFakeIpcObject\n{\n}\n\ninternal class FakeIpcObjectWithTypeAttributes : FakeIpcObject, IFakeIpcObjectWithTypeAttributes\n{\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/Fake/IFakeIpcObject.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Threading.Tasks;\n\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\nusing dotnetCampus.Ipc.Tests.CompilerServices.Fake;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices\n{\n    [IpcPublic]\n    internal interface IFakeIpcObject : IFakeIpcObjectBase\n    {\n        BindingFlags EnumProperty { get; set; }\n\n        [IpcProperty(IsReadonly = true)]\n        bool IpcReadonlyProperty { get; }\n\n        IntPtr IntPtrProperty { get; }\n\n        IntPtr? NullableIntPtrProperty { get; }\n\n        List<string>? ListProperty { get; set; }\n\n        IList<string>? CollectionProperty { get; set; }\n\n        string[]? ArrayProperty { get; set; }\n\n        INestedFakeIpcArgumentOrReturn NestedIpcProperty { get; set; }\n\n        [IpcMethod(WaitsVoid = true)]\n        void WaitsVoidMethod();\n\n        [IpcMethod(WaitsVoid = false)]\n        void NonWaitsVoidMethod();\n\n        [IpcMethod(IgnoresIpcException = true)]\n        Task MethodThatIgnoresIpcException();\n\n        Task MethodThatThrowsIpcException();\n\n        [IpcMethod(Timeout = 100)]\n        Task MethodThatHasTimeout();\n\n#nullable enable\n        Task<string?> MethodThatHasAsyncNullableReturn();\n#nullable restore\n\n#nullable enable\n        Task<FakeIpcObjectSubModelA?> MethodThatHasAsyncNullableComplexReturn();\n#nullable restore\n\n        [IpcMethod(DefaultReturn = \"\\\"default1\\\"\", IgnoresIpcException = true, Timeout = 100)]\n        Task<string> MethodThatHasDefaultReturn();\n\n        [IpcMethod(DefaultReturn = \"default\", IgnoresIpcException = true, Timeout = 100)]\n        Task<object> MethodThatHasObjectWithObjectDefaultReturn();\n\n        [IpcMethod(DefaultReturn = \"\\\"default1\\\"\", IgnoresIpcException = true, Timeout = 100)]\n        Task<object> MethodThatHasObjectWithStringDefaultReturn();\n\n        // 请不要将这里的 String 改为 string，这是为了测试代码生成器能否处理类型而非关键字。\n        [IpcMethod(DefaultReturn = \"\\\"default1\\\"\", IgnoresIpcException = true, Timeout = 100)]\n        Task<String> MethodThatHasStringDefaultReturn();\n\n        [IpcMethod(DefaultReturn = \"new System.IntPtr(1)\", IgnoresIpcException = true, Timeout = 100)]\n        Task<IntPtr> MethodThatHasCustomDefaultReturn();\n\n        [IpcMethod(DefaultReturn = \"\\\"default1\\\"\")]\n        Task<string> MethodThatCannotBeCompiled_MustSetOtherAttributes();\n\n        Task<List<string>> MethodWithListParametersAndListReturn(List<string> a, List<string> b);\n\n        Task<IList<string>> MethodWithCollectionParametersAndCollectionReturn(IList<string> a, IList<string> b);\n\n        Task<string[]> MethodWithArrayParametersAndArrayReturn(string[] a, string[] b);\n\n        void MethodWithStructParameters(BindingFlags flags);\n\n        bool MethodWithStructReturn();\n\n        int MethodWithSameParameterCountOverloading(int a, int b);\n\n        long MethodWithSameParameterCountOverloading(long a, long b);\n\n        NestedEnum MethodWithNestedEnumReturn();\n\n        Task<IFakeIpcObject.NestedEnum> AsyncMethodWithNestedEnumReturn();\n\n        Task AsyncMethod();\n\n        Task<INestedFakeIpcArgumentOrReturn> AsyncMethodWithIpcPublicObjectParametersAndIpcPublicObjectReturn(INestedFakeIpcArgumentOrReturn nested, string changeValue);\n\n#nullable enable\n        Task<(double a, uint b, int? c, byte d)> AsyncMethodWithStructParametersAndStructReturn(double a, uint b, int? c, byte d);\n#nullable restore\n\n#nullable enable\n        Task<(FakeIpcObjectSubModelA a, FakeIpcObjectSubModelA? b, IntPtr c, IntPtr? d)> AsyncMethodWithComplexValueTupleParametersAndComplexValueTupleReturn(FakeIpcObjectSubModelA a, FakeIpcObjectSubModelA? b, IntPtr c, IntPtr? d);\n#nullable restore\n\n        Task<FakeIpcObjectSubModelA> AsyncMethodWithComplexParametersAndComplexReturn(FakeIpcObjectSubModelA model);\n\n        Task<string> AsyncMethodWithPrimaryParametersAndPrimaryReturn(string source);\n\n        public enum NestedEnum\n        {\n            None,\n            Tested,\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/Fake/IFakeIpcObjectBase.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Tests.CompilerServices\n{\n    internal interface IFakeIpcObjectBase\n    {\n        string? NullableStringProperty { get; set; }\n\n        string? NonNullableStringProperty { get; set; }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/Fake/INestedFakeIpcArgumentOrReturn.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.Attributes;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices.Fake;\n\n[IpcPublic]\ninternal interface INestedFakeIpcArgumentOrReturn\n{\n    string Value { get; set; }\n}\n\ninternal sealed class FakeNestedIpcArgumentOrReturn : INestedFakeIpcArgumentOrReturn\n{\n    public FakeNestedIpcArgumentOrReturn(string value)\n    {\n        Value = value;\n    }\n\n    public string Value { get; set; }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/FakeRemote/RemoteIpcArgument.cs",
    "content": "﻿using dotnetCampus.Ipc.FakeTests.FakeApis;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices.FakeRemote;\npublic class RemoteIpcArgument : IRemoteFakeIpcArgumentOrReturn\n{\n    public RemoteIpcArgument(string value)\n    {\n        Value = value;\n    }\n\n    public string Value { get; set; }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/GeneratedProxies/IpcObjectTests.cs",
    "content": "﻿#nullable enable\nusing System.Collections;\nusing System.Diagnostics;\nusing System.Reflection;\nusing dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.FakeTests.FakeApis;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Tests.CompilerServices.Fake;\nusing dotnetCampus.Ipc.Tests.CompilerServices.FakeRemote;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices.GeneratedProxies;\n\n[TestClass]\npublic class IpcObjectTests\n{\n    [TestMethod(\"IPC 代理生成：可空字符串属性\")]\n    public async Task IpcPropertyTests1()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.NullableStringProperty));\n\n        // 安放。\n        var result = proxy.NullableStringProperty;\n\n        // 植物。\n        Assert.AreEqual(\"Title\", result);\n    }\n\n    [TestMethod(\"IPC 代理生成：非可空字符串属性\")]\n    public async Task IpcPropertyTests2()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.NonNullableStringProperty));\n\n        // 安放。\n        var result = proxy.NonNullableStringProperty;\n\n        // 植物。\n        Assert.AreEqual(\"Description\", result);\n    }\n\n    [TestMethod(\"IPC 代理生成：枚举属性\")]\n    public async Task IpcPropertyTests3()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.EnumProperty));\n\n        // 安放。\n        var result = proxy.EnumProperty;\n\n        // 植物。\n        Assert.AreEqual(BindingFlags.Public, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：IPC 只读属性\")]\n    public async Task IpcPropertyTests4()\n    {\n        // 准备。\n        var instance = new FakeIpcObject();\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.IpcReadonlyProperty), instance);\n\n        // 安放。\n        var result1 = proxy.IpcReadonlyProperty;\n        instance.SetIpcReadonlyProperty(false);\n        var result2 = proxy.IpcReadonlyProperty;\n\n        // 植物。\n        Assert.AreEqual(true, result1);\n        Assert.AreEqual(true, result2);\n    }\n\n#if !NET8_0_OR_GREATER\n    [TestMethod(\"IPC 代理生成：没有原生序列化的属性（以指针属性为例，仅支持 Newtonsoft.Json）\")]\n    public async Task IpcPropertyTests5()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.IntPtrProperty));\n\n        // 安放。\n        var result = proxy.IntPtrProperty;\n\n        // 植物。\n        Assert.AreEqual(new IntPtr(1), result);\n    }\n#endif\n\n    [TestMethod(\"IPC 代理生成：要等待完成的 void 方法\")]\n    public async Task IpcMethodsTests1()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.WaitsVoidMethod));\n\n        // 安放。\n        proxy.WaitsVoidMethod();\n        var result = proxy.EnumProperty;\n\n        // 植物。\n        Assert.AreEqual(BindingFlags.Public | BindingFlags.Instance, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：不等待完成的 void 方法\")]\n    public async Task IpcMethodsTests2()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.NonWaitsVoidMethod));\n\n        // 安放。\n        proxy.NonWaitsVoidMethod();\n        var result = proxy.EnumProperty;\n\n        // 植物。\n        Assert.AreEqual(BindingFlags.Public, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：会 IPC 超时的方法\")]\n    public async Task IpcMethodsTests3()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodThatHasTimeout));\n\n        // 安放植物。\n        await Assert.ThrowsExceptionAsync<IpcInvokingTimeoutException>(async () =>\n        {\n            await proxy.MethodThatHasTimeout();\n        });\n    }\n\n    [TestMethod(\"IPC 代理生成：返回默认值的方法\")]\n    public async Task IpcMethodsTests4()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodThatHasDefaultReturn));\n\n        // 安放。\n        var result = await proxy.MethodThatHasDefaultReturn();\n\n        // 植物。\n        Assert.AreEqual(\"default1\", result);\n    }\n\n    [TestMethod(\"IPC 代理生成：返回默认表达式默认值的方法\")]\n    public async Task IpcMethodsTests5()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodThatHasObjectWithObjectDefaultReturn));\n\n        // 安放。\n        var result = await proxy.MethodThatHasObjectWithObjectDefaultReturn();\n\n        // 植物。\n        Assert.AreEqual(null, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：返回字符串表达式默认值的方法\")]\n    public async Task IpcMethodsTests6()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodThatHasObjectWithStringDefaultReturn));\n\n        // 安放。\n        var result = await proxy.MethodThatHasObjectWithStringDefaultReturn();\n\n        // 植物。\n        Assert.AreEqual(\"default1\", result);\n    }\n\n    [TestMethod(\"IPC 代理生成：返回大写字符串表达式默认值的方法\")]\n    public async Task IpcMethodsTests7()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodThatHasStringDefaultReturn));\n\n        // 安放。\n        var result = await proxy.MethodThatHasStringDefaultReturn();\n\n        // 植物。\n        Assert.AreEqual(\"default1\", result);\n    }\n\n    [TestMethod(\"IPC 代理生成：返回自定义表达式默认值的方法\")]\n    public async Task IpcMethodsTests8()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodThatHasCustomDefaultReturn));\n\n        // 安放。\n        var result = await proxy.MethodThatHasCustomDefaultReturn();\n\n        // 植物。\n        Assert.AreEqual(new IntPtr(1), result);\n    }\n\n    [TestMethod(\"IPC 代理生成：枚举参数\")]\n    public async Task IpcParametersAndReturnsTests1()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodWithStructParameters));\n\n        // 安放植物。\n        proxy.MethodWithStructParameters(BindingFlags.Public);\n    }\n\n    [TestMethod(\"IPC 代理生成：布尔返回值\")]\n    public async Task IpcParametersAndReturnsTests2()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodWithStructReturn));\n\n        // 安放。\n        var result = proxy.MethodWithStructReturn();\n\n        // 植物。\n        Assert.AreEqual(true, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：同数量的参数组成的重载方法组。\")]\n    public async Task IpcParametersAndReturnsTests3()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodWithSameParameterCountOverloading));\n\n        // 安放。\n        var int32Result = proxy.MethodWithSameParameterCountOverloading(1, 2);\n        var int64Result = proxy.MethodWithSameParameterCountOverloading(1L, 2L);\n\n        // 植物。\n        Assert.AreEqual(3, int32Result);\n        Assert.AreEqual(2L, int64Result);\n    }\n\n    [TestMethod(\"IPC 代理生成：异步返回值\")]\n    public async Task IpcParametersAndReturnsTests4()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.AsyncMethod));\n\n        // 安放植物。\n        await proxy.AsyncMethod();\n    }\n\n    [TestMethod(\"IPC 代理生成：多参数和异步结构体返回值。\")]\n    public async Task IpcParametersAndReturnsTests5()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.AsyncMethodWithStructParametersAndStructReturn));\n\n        // 安放。\n        var result = await proxy.AsyncMethodWithStructParametersAndStructReturn(1, 2, 3, 4);\n\n        // 植物。\n        Assert.AreEqual(new ValueTuple<double, uint, int, byte>(1, 2, 3, 4), result);\n    }\n\n    [TestMethod(\"IPC 代理生成：复杂参数和异步复杂返回值\")]\n    public async Task IpcParametersAndReturnsTests6()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.AsyncMethodWithComplexParametersAndComplexReturn));\n\n        // 安放。\n        var result = await proxy.AsyncMethodWithComplexParametersAndComplexReturn(new FakeIpcObjectSubModelA(1, 2, 3, 4));\n\n        // 植物。\n        Assert.AreEqual((double) 1, result.A);\n        Assert.AreEqual((uint) 2, result.B);\n        Assert.AreEqual((int) 3, result.C);\n        Assert.AreEqual((byte) 4, result.D);\n    }\n\n    [TestMethod(\"IPC 代理生成：字符串参数和异步字符串返回值。\")]\n    public async Task IpcParametersAndReturnsTests7()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.AsyncMethodWithPrimaryParametersAndPrimaryReturn));\n\n        // 安放。\n        var result = await proxy.AsyncMethodWithPrimaryParametersAndPrimaryReturn(\"Test\");\n\n        // 植物。\n        Assert.AreEqual(\"Test\", result);\n    }\n\n    [TestMethod(\"IPC 代理生成：IPC 参数和异步 IPC 返回值\")]\n    public async Task IpcParametersAndReturnsTests8()\n    {\n        // 准备。\n        var proxySideObject = new FakeNestedIpcArgumentOrReturn(\"test on proxy side\");\n        var jointSideObject = new FakeNestedIpcArgumentOrReturn(\"test on joint side\");\n        var (aProvider, _, peer, proxy) =\n            await CreateIpcPairWithProvidersAsync(nameof(FakeIpcObject.AsyncMethodWithIpcPublicObjectParametersAndIpcPublicObjectReturn),\n                new FakeIpcObject(jointSideObject));\n\n        // 安放。\n        var result = await proxy.AsyncMethodWithIpcPublicObjectParametersAndIpcPublicObjectReturn(proxySideObject, \"change on joint side\");\n\n        // 植物。\n        // 代理端对象的值被对接端修改。\n        Assert.AreEqual(\"change on joint side\", proxySideObject.Value);\n        // 对接端的值保持原样。\n        Assert.AreEqual(\"test on joint side\", jointSideObject.Value);\n        Assert.AreEqual(\"test on joint side\", result.Value);\n\n        // 安放。\n        result.Value = \"test changed from proxy side\";\n\n        // 植物。\n        // 代理端对象的值保持原样。\n        Assert.AreEqual(\"change on joint side\", proxySideObject.Value);\n        // 对接端的值被代理端修改。\n        Assert.AreEqual(\"test changed from proxy side\", jointSideObject.Value);\n        Assert.AreEqual(\"test changed from proxy side\", result.Value);\n    }\n\n    [TestMethod(\"IPC 代理生成：不同程序集中的同名 IPC 参数和异步 IPC 返回值\")]\n    public async Task DifferentAssembliesIpcParametersAndReturnsTests()\n    {\n        // 准备。\n        var remoteExecutablePath = Path.Combine(\n            Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!,\n            \"..\", \"..\",\n            @\"dotnetCampus.Ipc.FakeTests\",\n            Assembly.GetExecutingAssembly().GetCustomAttribute<AssemblyConfigurationAttribute>()!.Configuration.ToLowerInvariant(),\n            \"dotnetCampus.Ipc.FakeTests.dll\");\n\n        if (!File.Exists(remoteExecutablePath))\n        {\n            throw new FileNotFoundException($\"在执行真正跨进程 IPC 通信时，目标程序集未找到，请确认代码中编写的路径是否已更新到最新路径。路径为：{remoteExecutablePath}\");\n        }\n\n#if NET8_0_OR_GREATER\n        var ipcPeerName = $\"IpcObjectTests.IpcTests.RemoteFakeIpcObject.net8\";\n#else\n        var ipcPeerName = $\"IpcObjectTests.IpcTests.RemoteFakeIpcObject.net6\";\n#endif\n        var process = Process.Start(new ProcessStartInfo(\"dotnet\")\n        {\n            UseShellExecute = true,\n            ArgumentList =\n            {\n                remoteExecutablePath,\n                \"--peer-name\",\n                ipcPeerName,\n            },\n            WorkingDirectory = Path.GetDirectoryName(remoteExecutablePath),\n        })!;\n        try\n        {\n            var ipcProvider = new IpcProvider(ipcPeerName + \".Local\");\n            ipcProvider.StartServer();\n            var ipcPeer = await ipcProvider.GetAndConnectToPeerAsync(ipcPeerName);\n            var remoteObject = ipcProvider.CreateIpcProxy<IRemoteFakeIpcObject>(ipcPeer);\n            var ipcArgument = new RemoteIpcArgument(\"argument\");\n\n            // 安放。\n            var ipcReturn = await remoteObject.MethodWithIpcParameterAsync(ipcArgument, \"changed ipc argument\");\n\n            try\n            {\n                // 植物。\n                // 本地值被远端修改。\n                Assert.AreEqual(\"changed ipc argument\", ipcArgument.Value);\n                // 远端的值保持默认。\n                Assert.AreEqual(\"private\", ipcReturn.Value);\n            }\n            finally\n            {\n                // 清理。\n                await remoteObject.ShutdownAsync();\n            }\n        }\n        finally\n        {\n            try\n            {\n                // 清理。\n                process.Kill();\n            }\n            catch (Exception)\n            {\n                // ignored\n            }\n        }\n    }\n\n    [TestMethod(\"IPC 代理生成：集合（列表）属性\")]\n    public async Task IpcCollectionTests1()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.ListProperty));\n\n        // 安放。\n        var result = proxy.ListProperty;\n\n        // 植物。\n        CollectionAssert.AreEqual(new string[] { \"List1\", \"List2\" }, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：集合（接口）属性\")]\n    public async Task IpcCollectionTests2()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.CollectionProperty));\n\n        // 安放。\n        var result = proxy.CollectionProperty;\n\n        // 植物。\n        CollectionAssert.AreEqual(new string[] { \"Collection1\", \"Collection2\" }, (ICollection?) result);\n    }\n\n    [TestMethod(\"IPC 代理生成：集合（数组）属性\")]\n    public async Task IpcCollectionTests3()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.ArrayProperty));\n\n        // 安放。\n        var result = proxy.ArrayProperty;\n\n        // 植物。\n        CollectionAssert.AreEqual(new string[] { \"Array1\", \"Array2\" }, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：集合（列表）异步方法\")]\n    public async Task IpcCollectionTests4()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodWithListParametersAndListReturn));\n\n        // 安放。\n        var result = await proxy.MethodWithListParametersAndListReturn(\n            new List<string> { \"a\", \"b\" },\n            new List<string> { \"c\", \"d\" });\n\n        // 植物。\n        CollectionAssert.AreEqual(new string[] { \"a\", \"b\", \"c\", \"d\" }, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：集合（接口）异步方法\")]\n    public async Task IpcCollectionTests5()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodWithCollectionParametersAndCollectionReturn));\n\n        // 安放。\n        var result = await proxy.MethodWithCollectionParametersAndCollectionReturn(\n            new List<string> { \"a\", \"b\" },\n            new List<string> { \"c\", \"d\" });\n\n        // 植物。\n        CollectionAssert.AreEqual(new string[] { \"a\", \"b\", \"c\", \"d\" }, (ICollection) result);\n    }\n\n    [TestMethod(\"IPC 代理生成：集合（数组）异步方法\")]\n    public async Task IpcCollectionTests6()\n    {\n        // 准备。\n        var (peer, proxy) = await CreateIpcPairAsync(nameof(FakeIpcObject.MethodWithArrayParametersAndArrayReturn));\n\n        // 安放。\n        var result = await proxy.MethodWithArrayParametersAndArrayReturn(\n            new string[] { \"a\", \"b\" },\n            new string[] { \"c\", \"d\" });\n\n        // 植物。\n        CollectionAssert.AreEqual(new string[] { \"a\", \"b\", \"c\", \"d\" }, result);\n    }\n\n    [TestMethod(\"IPC 代理生成：忽略异常的方法\")]\n    public async Task IpcMethodExceptionTests1()\n    {\n        // 准备。\n        var name = nameof(FakeIpcObject.MethodThatIgnoresIpcException);\n        var aName = $\"IpcObjectTests.IpcTests.{name}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.{name}.B\";\n        var aProvider = new IpcProvider(aName);\n        var bProvider = new IpcProvider(bName);\n        aProvider.StartServer();\n        bProvider.StartServer();\n        var aJoint = aProvider.CreateIpcJoint<IFakeIpcObject>(new FakeIpcObject());\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n        var bProxy = bProvider.CreateIpcProxy<IFakeIpcObject>(aPeer);\n\n        // 安放植物。\n        // 没有发生异常。\n        var task = bProxy.MethodThatIgnoresIpcException();\n        await Task.Run(async () =>\n        {\n            await Task.Delay(20);\n            aProvider.Dispose();\n        });\n        await task;\n    }\n\n    [TestMethod(\"IPC 代理生成：没有忽略异常的方法\")]\n    public async Task IpcMethodExceptionTests2()\n    {\n        // 准备。\n        var name = nameof(FakeIpcObject.MethodThatThrowsIpcException);\n        var aName = $\"IpcObjectTests.IpcTests.{name}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.{name}.B\";\n        var aProvider = new IpcProvider(aName);\n        var bProvider = new IpcProvider(bName);\n        aProvider.StartServer();\n        bProvider.StartServer();\n        var aJoint = aProvider.CreateIpcJoint<IFakeIpcObject>(new FakeIpcObject());\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n        var bProxy = bProvider.CreateIpcProxy<IFakeIpcObject>(aPeer);\n\n        // 安放。\n        var task = bProxy.MethodThatThrowsIpcException();\n        await Task.Run(async () =>\n        {\n            await Task.Delay(20);\n            aProvider.Dispose();\n        });\n\n        // 植物。\n        try\n        {\n            await task;\n        }\n        catch (IpcRemoteException e)\n        {\n            if (e.InnerException is IpcPeerConnectionBrokenException)\n            {\n                // 期望的异常是外层是 IpcRemoteException 异常\n                // 里层是 IpcPeerConnectionBrokenException 异常\n                // 外层的异常将包括发送的消息的调试使用的 Tag 信息\n                return;\n            }\n        }\n\n        Assert.Fail($\"期望的异常没有被抛出\");\n    }\n\n    [TestMethod(\"IPC 代理生成：成员上没有标记忽略异常，但是类型上标记了，也要忽略异常\")]\n    public async Task IpcMethodExceptionTests3()\n    {\n        // 准备。\n        var name = $\"{nameof(FakeIpcObjectWithTypeAttributes)}.{nameof(FakeIpcObject.MethodThatThrowsIpcException)}\";\n        var aName = $\"IpcObjectTests.IpcTests.{name}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.{name}.B\";\n        var aProvider = new IpcProvider(aName);\n        var bProvider = new IpcProvider(bName);\n        aProvider.StartServer();\n        bProvider.StartServer();\n        var aJoint = aProvider.CreateIpcJoint<IFakeIpcObject>(new FakeIpcObjectWithTypeAttributes());\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n        var bProxy = bProvider.CreateIpcProxy<IFakeIpcObject>(aPeer, new IpcProxyConfigs { IgnoresIpcException = true, });\n\n        // 安放植物。\n        // 没有发生异常。\n        var task = bProxy.MethodThatThrowsIpcException();\n        await Task.Run(async () =>\n        {\n            await Task.Delay(20);\n            aProvider.Dispose();\n        });\n        await task;\n    }\n\n    private async Task<(IPeerProxy peer, IFakeIpcObject proxy)> CreateIpcPairAsync(string name, FakeIpcObject? instance = null)\n    {\n        var aName = $\"IpcObjectTests.IpcTests.{name}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.{name}.B\";\n        var aProvider = new IpcProvider(aName, TestJsonContext.CreateIpcConfiguration());\n        var bProvider = new IpcProvider(bName, TestJsonContext.CreateIpcConfiguration());\n        aProvider.StartServer();\n        bProvider.StartServer();\n        var aJoint = aProvider.CreateIpcJoint<IFakeIpcObject>(instance ?? new FakeIpcObject());\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n        var bProxy = bProvider.CreateIpcProxy<IFakeIpcObject>(aPeer);\n        // 这里的延迟是为了暂时缓解死锁 bug @lindexi\n        // 这个问题其实是因为 IpcObject 这一套可能存在异步转同步等待的问题\n        // 问题原因如下：\n        // 从 GetAndConnectToPeerAsync 返回的时，是消息接受端 DispatchMessage 所在线程调用过来的\n        // 如果此线程卡住了，那就意味着不再能够接收到消息\n        // 那为什么存在锁的问题？因为如果在接下来一句话是走 IpcObject 框架获取远程对象的属性值类似的代码\n        // 将会在获取属性时，进入异步转同步等待，需要等待到什么时候才会继续执行？需要等待消息接受端 DispatchMessage 接收到远程对象返回的消息\n        // 然而消息接受端 DispatchMessage 所在线程已经进入异步转同步等待，导致无法接收到消息，进而导致 DispatchMessage 所在线程无法释放\n        // 那为什么设计上要让 GetAndConnectToPeerAsync 返回的线程是消息接受端 DispatchMessage 所在线程？原因是在主动发起对 Peer 连接时，也许需要进行一些事件加等处理等，如果在另一个线程，那可能出现在获取到 Peer 的同时也接收到 Peer 发送过来的消息。这会存在由于事件加等处理在另一个线程，没有及时执行，导致丢失消息\n        await Task.Delay(100);\n        return (aPeer, bProxy);\n    }\n\n    private async Task<(IIpcProvider aProvider, IIpcProvider bProvider, IPeerProxy peer, IFakeIpcObject proxy)> CreateIpcPairWithProvidersAsync(string name,\n        FakeIpcObject? instance = null)\n    {\n        var aName = $\"IpcObjectTests.IpcTests.{name}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.{name}.B\";\n        var aProvider = new IpcProvider(aName);\n        var bProvider = new IpcProvider(bName);\n        aProvider.StartServer();\n        bProvider.StartServer();\n        var aJoint = aProvider.CreateIpcJoint<IFakeIpcObject>(instance ?? new FakeIpcObject());\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n        var bProxy = bProvider.CreateIpcProxy<IFakeIpcObject>(aPeer);\n        // 这里的延迟是为了暂时缓解死锁 bug @lindexi\n        await Task.Delay(100);\n        return (aProvider, bProvider, aPeer, bProxy);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/CompilerServices/GeneratedProxies/NotGeneratedTestOnlyFakeIpcObjectIpcShape.cs",
    "content": "﻿#nullable disable\nusing System.Reflection;\nusing dotnetCampus.Ipc.CompilerServices.Attributes;\nusing dotnetCampus.Ipc.Tests.CompilerServices.Fake;\n\nnamespace dotnetCampus.Ipc.Tests.CompilerServices.GeneratedProxies\n{\n    [IpcShape(typeof(IFakeIpcObject))]\n    internal class NotGeneratedTestOnlyFakeIpcObjectIpcShape : IFakeIpcObject\n    {\n        [IpcProperty]\n        string? IFakeIpcObjectBase.NullableStringProperty { get; set; }\n\n        [IpcProperty]\n        string? IFakeIpcObjectBase.NonNullableStringProperty { get; set; }\n\n        [IpcProperty]\n        BindingFlags IFakeIpcObject.EnumProperty { get; set; }\n\n        [IpcProperty]\n        bool IFakeIpcObject.IpcReadonlyProperty { get; }\n\n        [IpcProperty]\n        IntPtr IFakeIpcObject.IntPtrProperty { get; }\n\n        [IpcProperty]\n        IntPtr? IFakeIpcObject.NullableIntPtrProperty { get; }\n\n        [IpcProperty]\n        INestedFakeIpcArgumentOrReturn IFakeIpcObject.NestedIpcProperty { get; set; }\n\n        [IpcProperty]\n        public List<string> ListProperty { get; set; }\n\n        [IpcProperty]\n        public IList<string> CollectionProperty { get; set; }\n\n        [IpcProperty]\n        public string[] ArrayProperty { get; set; }\n\n        [IpcMethod]\n        void IFakeIpcObject.WaitsVoidMethod()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        void IFakeIpcObject.NonWaitsVoidMethod()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task IFakeIpcObject.MethodThatIgnoresIpcException()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task IFakeIpcObject.MethodThatThrowsIpcException()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task IFakeIpcObject.MethodThatHasTimeout()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<string?> IFakeIpcObject.MethodThatHasAsyncNullableReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<FakeIpcObjectSubModelA?> IFakeIpcObject.MethodThatHasAsyncNullableComplexReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<string> IFakeIpcObject.MethodThatHasDefaultReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<object> IFakeIpcObject.MethodThatHasObjectWithObjectDefaultReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<object> IFakeIpcObject.MethodThatHasObjectWithStringDefaultReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<string> IFakeIpcObject.MethodThatHasStringDefaultReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<IntPtr> IFakeIpcObject.MethodThatHasCustomDefaultReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<string> IFakeIpcObject.MethodThatCannotBeCompiled_MustSetOtherAttributes()\n        {\n            throw null;\n        }\n\n        public Task<List<string>> MethodWithListParametersAndListReturn(List<string> a, List<string> b)\n        {\n            throw null;\n        }\n\n        public Task<IList<string>> MethodWithCollectionParametersAndCollectionReturn(IList<string> a, IList<string> b)\n        {\n            throw null;\n        }\n\n        public Task<string[]> MethodWithArrayParametersAndArrayReturn(string[] a, string[] b)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        void IFakeIpcObject.MethodWithStructParameters(BindingFlags flags)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        bool IFakeIpcObject.MethodWithStructReturn()\n        {\n            throw null;\n        }\n\n        public int MethodWithSameParameterCountOverloading(int a, int b)\n        {\n            throw null;\n        }\n\n        public long MethodWithSameParameterCountOverloading(long a, long b)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        IFakeIpcObject.NestedEnum IFakeIpcObject.MethodWithNestedEnumReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<IFakeIpcObject.NestedEnum> IFakeIpcObject.AsyncMethodWithNestedEnumReturn()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task IFakeIpcObject.AsyncMethod()\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<INestedFakeIpcArgumentOrReturn> IFakeIpcObject.AsyncMethodWithIpcPublicObjectParametersAndIpcPublicObjectReturn(INestedFakeIpcArgumentOrReturn nested, string changeValue)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<(double a, uint b, int? c, byte d)> IFakeIpcObject.AsyncMethodWithStructParametersAndStructReturn(double a, uint b, int? c, byte d)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<(FakeIpcObjectSubModelA a, FakeIpcObjectSubModelA? b, IntPtr c, IntPtr? d)> IFakeIpcObject.AsyncMethodWithComplexValueTupleParametersAndComplexValueTupleReturn(FakeIpcObjectSubModelA a, FakeIpcObjectSubModelA? b, IntPtr c, IntPtr? d)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<FakeIpcObjectSubModelA> IFakeIpcObject.AsyncMethodWithComplexParametersAndComplexReturn(FakeIpcObjectSubModelA model)\n        {\n            throw null;\n        }\n\n        [IpcMethod]\n        Task<string> IFakeIpcObject.AsyncMethodWithPrimaryParametersAndPrimaryReturn(string source)\n        {\n            throw null;\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/IpcClientServiceTests.cs",
    "content": "﻿using dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Threading;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests\n{\n    [TestClass]\n    public class IpcClientServiceTests\n    {\n        [TestMethod(\"连接断开之后持续发送消息，将会收到 ObjectDisposedException 异常\")]\n        public async Task SendWhenDisposed()\n        {\n            var ipcProviderA = new IpcProvider();\n            var ipcProviderB = new IpcProvider();\n\n            ipcProviderA.StartServer();\n            ipcProviderB.StartServer();\n\n            var peer = await ipcProviderA.GetAndConnectToPeerAsync(ipcProviderB.IpcContext.PipeName);\n            var n = 100;\n            var buffer = new byte[1024];\n            var autoResetEvent = new AsyncAutoResetEvent(false);\n            var sendTask = Task.Run(async () =>\n            {\n                for (int i = 0; i < n; i++)\n                {\n                    await peer.IpcMessageWriter.WriteMessageAsync(new IpcMessage(\"Test\", buffer));\n\n                    if (i == n / 2)\n                    {\n                        // 让另一个线程去释放 ipcProviderB 对象\n                        autoResetEvent.Set();\n                    }\n\n                    await Task.Yield();\n                }\n            });\n\n            var disposeTask = Task.Run(async () =>\n            {\n                await autoResetEvent.WaitOneAsync();\n                ipcProviderB.Dispose();\n            });\n\n            try\n            {\n                // 等待发送和释放任务完成\n                // 预期是一定会在发送任务抛出异常，异常是 ObjectDisposedException 或 IOException 这两个\n                await Task.WhenAll(sendTask, disposeTask);\n            }\n            catch (Exception e)\n            {\n                // 期望抛出异常\n                // 在调用一次 WriteMessageAsync 之前，被释放，那么抛出 ObjectDisposedException 异常\n                // 在写入过程，被释放，那么写入 PipeStream.WriteCore 抛出 IO 异常\n                Assert.AreEqual(true, e is ObjectDisposedException or IOException);\n                return;\n            }\n\n            // 如果没有抛出异常，那么进入此分支\n            Assert.Fail(\"预期抛出的是 ObjectDisposedException 或 IOException 异常\");\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/IpcMessageConverterTest.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Buffers;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests\n{\n    [TestClass]\n    public class IpcMessageConverterTest\n    {\n        [TestMethod(\"读取消息头不对的数据，可以返回读取失败\")]\n        public async Task IpcMessageConverterWriteAsync1()\n        {\n            using var memoryStream = new MemoryStream();\n\n            ulong ack = 10;\n            var buffer = new byte[] { 0x12, 0x12, 0x00 };\n            var messageHeader = new byte[] { 0x00, 0x00 };\n            var breakMessageHeader = new byte[] { 0x00, 0x01 };\n\n            var ipcBufferMessageContext = new IpcBufferMessageContext(\"test\", IpcMessageCommandType.Unknown, new IpcMessageBody(buffer));\n\n            await IpcMessageConverter.WriteAsync(memoryStream, messageHeader, ack, ipcBufferMessageContext, new SharedArrayPool());\n\n            memoryStream.Position = 0;\n            var ipcMessageResult = (await IpcMessageConverter.ReadAsync(memoryStream,\n                breakMessageHeader, new SharedArrayPool())).Result;\n            var success = ipcMessageResult.Success;\n            var ipcMessageCommandType = ipcMessageResult.IpcMessageCommandType;\n\n            Assert.AreEqual(false, success);\n            Assert.AreEqual(IpcMessageCommandType.Unknown, ipcMessageCommandType);\n        }\n\n        [TestMethod(\"读取消息头长度不对的数据，可以返回读取失败\")]\n        public async Task IpcMessageConverterWriteAsync2()\n        {\n            using var memoryStream = new MemoryStream();\n\n            var ipcConfiguration = new IpcConfiguration();\n            ulong ack = 10;\n            var buffer = new byte[] { 0x12, 0x12, 0x00 };\n            var messageHeader = new byte[] { 0x00, 0x00 };\n\n            var ipcBufferMessageContext = new IpcBufferMessageContext(\"test\", IpcMessageCommandType.Unknown, new IpcMessageBody(buffer));\n\n            await IpcMessageConverter.WriteAsync(memoryStream, messageHeader, ack, ipcBufferMessageContext, new SharedArrayPool());\n\n            memoryStream.Position = 0;\n            var ipcMessageResult = (await IpcMessageConverter.ReadAsync(memoryStream,\n                ipcConfiguration.MessageHeader, new SharedArrayPool())).Result;\n            var success = ipcMessageResult.Success;\n            var ipcMessageCommandType = ipcMessageResult.IpcMessageCommandType;\n\n            Assert.AreEqual(false, success);\n            Assert.AreEqual(IpcMessageCommandType.Unknown, ipcMessageCommandType);\n        }\n\n        [TestMethod(\"写入的数据和读取的相同，可以读取到写入的数据\")]\n        public async Task IpcMessageConverterWriteAsync3()\n        {\n\n            // 写入的数据和读取的相同\n            using var memoryStream = new MemoryStream();\n\n            var ipcConfiguration = new IpcConfiguration();\n            ulong ack = 10;\n            var buffer = new byte[] { 0x12, 0x12, 0x00 };\n            var ipcBufferMessageContext = new IpcBufferMessageContext(\"test\", IpcMessageCommandType.Unknown, new IpcMessageBody(buffer));\n            await IpcMessageConverter.WriteAsync(memoryStream, ipcConfiguration.MessageHeader, ack, ipcBufferMessageContext, new SharedArrayPool());\n\n            memoryStream.Position = 0;\n            var (success, ipcMessageContext) = (await IpcMessageConverter.ReadAsync(memoryStream,\n                ipcConfiguration.MessageHeader, new SharedArrayPool())).Result;\n\n            Assert.AreEqual(true, success);\n            Assert.AreEqual(ack, ipcMessageContext.Ack.Value);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/IpcObjectJsonSerializerTests.cs",
    "content": "﻿using System.Text.Json.Serialization;\nusing dotnetCampus.Ipc.Serialization;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests;\n\n[TestClass]\npublic class IpcObjectJsonSerializerTests\n{\n    [TestMethod(\"序列化对象之后，能通过二进制反序列化回对象\")]\n    public void Serialize()\n    {\n        // Arrange\n        IIpcObjectSerializer ipcObjectSerializer =\n#if NET8_0_OR_GREATER\n            new SystemTextJsonIpcObjectSerializer(FooJsonContext.Default);\n#else\n            new NewtonsoftJsonIpcObjectSerializer();\n#endif\n        var foo = new Foo() { Name = \"林德熙是逗比\" };\n\n        // Action\n        var byteList = ipcObjectSerializer.Serialize(foo);\n        var deserializeFoo = ipcObjectSerializer.Deserialize<Foo>(byteList, 0, byteList.Length);\n\n        // Assert\n        Assert.AreEqual(foo.Name, deserializeFoo?.Name);\n    }\n\n    public class Foo\n    {\n        public string Name { set; get; } = \"\";\n    }\n}\n\n#if NET8_0_OR_GREATER\n[JsonSerializable(typeof(IpcObjectJsonSerializerTests.Foo))]\ninternal partial class FooJsonContext : JsonSerializerContext;\n#endif\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/IpcProviderTests.cs",
    "content": "﻿using System.Diagnostics;\n\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Pipes;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests;\n\n[TestClass]\npublic class IpcProviderTests\n{\n    [TestMethod(\"使用 TryConnectToExistingPeerAsync 尝试连接不存在的对方，可以立刻返回连接失败\")]\n    public async Task TestTryConnectToExistingPeerAsync1()\n    {\n        var ipcProvider = new IpcProvider();\n        var result = await ipcProvider.TryConnectToExistingPeerAsync(\"The_Not_Exists_Peer_Name_E6EE8975-EF9A-480B-912D-B3C4530294E0\");\n        Assert.IsFalse(result.IsSuccess);\n    }\n\n    [TestMethod(\"使用 TryConnectToExistingPeerAsync 尝试连接存在的对方，可以返回连接成功\")]\n    public async Task TestTryConnectToExistingPeerAsync2()\n    {\n        var peerName = \"IpcProvider1_The_Exists_Peer_Name_E6EE8975-EF9A-480B-912D-B3C4530294E0\";\n        var testLogger = new TestLogger();\n        var ipcProvider1 = new IpcProvider(peerName, new IpcConfiguration()\n        {\n            IpcLoggerProvider = _ => testLogger\n        });\n        ipcProvider1.StartServer();\n        var ipcProvider2 = new IpcProvider(peerName.Replace(\"IpcProvider1\", \"IpcProvider2\"), new IpcConfiguration()\n        {\n            IpcLoggerProvider = _ => testLogger\n        });\n        ipcProvider2.StartServer();\n\n        try\n        {\n            var result = await ipcProvider2.TryConnectToExistingPeerAsync(peerName).WaitAsync(TimeSpan.FromSeconds(5));\n\n            Assert.IsTrue(result.IsSuccess);\n            Assert.IsNotNull(result.PeerProxy);\n        }\n        catch\n        {\n            Console.WriteLine(testLogger.GetAllLogMessage());\n            throw;\n        }\n    }\n\n    // 预期这一条是可能过也可能不过的，取决于时机，于是默认不加入单元测试去跑了\n    // 尽管提供了 TryConnectToExistingPeerAsync 方法，但仍然有一些边界的情况。正常也没有开发者会想着去连接自己吧\n    //[TestMethod(\"使用 TryConnectToExistingPeerAsync 尝试错误地连接自身，可能出现无限等待\")]\n    public async Task TestTryConnectToExistingPeerAsync3()\n    {\n        var peerName = \"The_Self_IpcProvider_E6EE8975-EF9A-480B-912D-B3C4530294E0\";\n        var ipcProvider1 = new IpcProvider(peerName);\n        ipcProvider1.StartServer();\n\n        var result = await ipcProvider1.TryConnectToExistingPeerAsync(peerName).WaitAsync(TimeSpan.FromSeconds(5));\n        _ = result;\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/IpcRouteds/DirectRouteds/JsonIpcDirectRoutedProviderSystemJsonSerializerTest.cs",
    "content": "﻿using System.Text.Json.Serialization;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.IpcRouteds.DirectRouteds;\n\n[TestClass]\npublic class JsonIpcDirectRoutedProviderSystemJsonSerializerTest\n{\n    [TestMethod(\"测试直接路由匹配到不符合预期的响应时的异常\")]\n    public async Task TestRequestNotMatchResponse()\n    {\n        var name = \"JsonIpcDirectRoutedProviderSystemJsonSerializerTest_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(name, new IpcConfiguration()\n#if NET8_0_OR_GREATER\n            .UseSystemTextJsonIpcObjectSerializer(JsonIpcDirectRoutedSystemJsonSerializerTestGenerationContext.Default)\n#endif\n        );\n\n        var requestPath = \"RequestPath1\";\n        var requestValue = \"请求的内容\";\n        serverProvider.AddRequestHandler(requestPath, (JsonIpcDirectRoutedSystemJsonSerializerTestRequest1 request) =>\n        {\n            Assert.AreEqual(requestValue, request.Value);\n            // 特意返回不符合预期的内容，预期是 JsonIpcDirectRoutedSystemJsonSerializerTestResponse1 类型，实际返回是字符串\n            return \"错误的返回内容\";\n        });\n        serverProvider.StartServer();\n\n        var clientProvider = new JsonIpcDirectRoutedProvider(ipcConfiguration: new IpcConfiguration()\n#if NET8_0_OR_GREATER\n            .UseSystemTextJsonIpcObjectSerializer(JsonIpcDirectRoutedSystemJsonSerializerTestGenerationContext.Default)\n#endif\n        );\n\n        JsonIpcDirectRoutedClientProxy jsonIpcDirectRoutedClientProxy = await clientProvider.GetAndConnectClientAsync(name);\n\n        try\n        {\n            var request = new JsonIpcDirectRoutedSystemJsonSerializerTestRequest1() { Value = requestValue };\n            var response =\n                await jsonIpcDirectRoutedClientProxy\n                    .GetResponseAsync<JsonIpcDirectRoutedSystemJsonSerializerTestResponse1>(requestPath,\n                        request);\n            _ = response; // 只是让分析器开森而已\n        }\n        catch (Exception e)\n        {\n            var jsonIpcDirectRouteSerializeLocalException = e as JsonIpcDirectRouteSerializeLocalException;\n            Assert.IsNotNull(jsonIpcDirectRouteSerializeLocalException);\n            // Json Ipc DirectRoute Serialize Exception. ResponseType=dotnetCampus.Ipc.Tests.IpcRouteds.DirectRouteds.JsonIpcDirectRoutedSystemJsonSerializerTestResponse1 ResponseMessage=[IpcMessage] Header=0;Tag=[JsonIpcDirectRoutedProviderSystemJsonSerializerTest_1];Body=[22 5C 75 39 35 31 39 5C 75 38 42 45 46 5C 75 37 36 38 34 5C 75 38 46 44 34 5C 75 35 36 44 45 5C 75 35 31 38 35 5C 75 35 42 42 39 22](GuessText=\"\\u9519\\u8BEF\\u7684\\u8FD4\\u56DE\\u5185\\u5BB9\") The JSON value could not be converted to dotnetCampus.Ipc.Tests.IpcRouteds.DirectRouteds.JsonIpcDirectRoutedSystemJsonSerializerTestResponse1. Path: $ | LineNumber: 0 | BytePositionInLine: 44.\n            Assert.AreEqual(typeof(JsonIpcDirectRoutedSystemJsonSerializerTestResponse1),\n                jsonIpcDirectRouteSerializeLocalException.ResponseType);\n            // 在此返回，证明进入了异常。不想用 Assert.ThrowsException 方法，因为这个方法不能进一步调试异常里面的信息\n            return;\n        }\n\n        Assert.Fail(\"预期一定会抛出异常，在 catch 分支返回\");\n    }\n}\n\n#if NET8_0_OR_GREATER\n[JsonSerializable(typeof(JsonIpcDirectRoutedSystemJsonSerializerTestRequest1))]\n[JsonSerializable(typeof(JsonIpcDirectRoutedSystemJsonSerializerTestResponse1))]\ninternal partial class JsonIpcDirectRoutedSystemJsonSerializerTestGenerationContext : JsonSerializerContext\n{\n}\n#endif\n\npublic record JsonIpcDirectRoutedSystemJsonSerializerTestRequest1\n{\n    public string? Value { get; set; }\n}\n\npublic record JsonIpcDirectRoutedSystemJsonSerializerTestResponse1\n{\n    public string? Result { get; set; }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/IpcRouteds/DirectRouteds/JsonIpcDirectRoutedProviderTest.cs",
    "content": "﻿using dotnetCampus.Ipc.CompilerServices.GeneratedProxies;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.IpcRouteds.DirectRouteds;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Tests.CompilerServices;\nusing dotnetCampus.Ipc.Threading;\nusing dotnetCampus.Ipc.Utils.Logging;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.IpcRouteds.DirectRouteds;\n\n[TestClass]\npublic class JsonIpcDirectRoutedProviderTest\n{\n    [TestMethod(\"多个通讯框架共用相同的 IpcProvider 对象，相互之间不受影响\")]\n    public async Task TestShared()\n    {\n        var name = \"JsonIpcDirectRoutedProviderTest_1\";\n        var aName = $\"IpcObjectTests.IpcTests.{name}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.{name}.B\";\n        var aProvider = new IpcProvider(aName);\n        var bProvider = new IpcProvider(bName);\n\n        var serverProvider = new JsonIpcDirectRoutedProvider(aProvider);\n        var clientProvider = new JsonIpcDirectRoutedProvider(bProvider);\n\n        const string routedPath = \"Foo1\";\n        serverProvider.AddRequestHandler(routedPath, (FakeArgument argument) =>\n        {\n            return new FakeResult(\"Ok\");\n        });\n\n        serverProvider.StartServer();\n        clientProvider.StartServer();\n\n        // 有上面的 StartServer 其实就可以不需要有下面的启动\n        aProvider.StartServer();\n        bProvider.StartServer();\n\n        var aJoint = aProvider.CreateIpcJoint<IFakeIpcObject>(new FakeIpcObject());\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n        var bProxy = bProvider.CreateIpcProxy<IFakeIpcObject>(aPeer);\n\n        // 防止获取属性异步转同步卡住\n        await Task.Yield();\n\n        // 两个框架发送的内容，都经过相同的一个管道，但是相互不影响\n        var result = bProxy.NullableStringProperty;\n        Assert.IsNotNull(result);\n\n        // 发送直接路由的请求\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(aName);\n        var fakeResult = await clientProxy.GetResponseAsync<FakeResult>(routedPath, new FakeArgument(\"Name\", 1));\n        Assert.AreEqual(\"Ok\", fakeResult.Name);\n    }\n\n    [TestMethod(\"客户端请求服务端，可以在服务端收到客户端请求的内容\")]\n    public async Task TestRequest1()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Request_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName,\n            new IpcConfiguration() { IpcLoggerProvider = name => new IpcLogger(name) { MinLogLevel = LogLevel.Debug, } }\n                .UseTestFrameworkJsonSerializer()\n            );\n        var argument = new FakeArgument(\"TestName\", 1);\n\n        var responseText = $\"OK_{Guid.NewGuid().ToString()}\";\n\n        int enterCount = 0;\n        serverProvider.AddRequestHandler(\"Foo1\", (FakeArgument arg) =>\n        {\n            // 没有任何逻辑请求，不能处理\n            enterCount++;\n            Assert.AreEqual(argument.Name, arg.Name);\n            Assert.AreEqual(argument.Count, arg.Count);\n\n            return new FakeResult(responseText);\n        });\n\n        serverProvider.AddRequestHandler(\"Foo2\", (FakeArgument arg, JsonIpcDirectRoutedContext context) =>\n        {\n            // 没有任何逻辑请求，不能处理\n            Assert.Fail();\n            return new FakeResult(\"Ok\");\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        // 允许管道名无参数，如果只是做客户端使用的话\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: new IpcConfiguration()\n        {\n            IpcLoggerProvider = name => new IpcLogger(name) { MinLogLevel = LogLevel.Debug, }\n        }.UseTestFrameworkJsonSerializer());\n        // 对于 clientProvider 来说，可选调用 StartServer 方法\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        var result = await clientProxy.GetResponseAsync<FakeResult>(\"Foo1\", argument);\n\n        // 可以获取到响应内容\n        Assert.AreEqual(responseText, result!.Name);\n\n        // 要求只进入一次\n        Assert.AreEqual(1, enterCount);\n    }\n\n    [TestMethod(\"客户端到服务端的请求，可以获取到服务端的响应\")]\n    public async Task TestRequest2()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Request_2\";\n        var clientName = \"JsonIpcDirectRoutedProviderTest_Request_Client_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var argument = new FakeArgument(\"TestName\", 1);\n\n        var responseText = Guid.NewGuid().ToString();\n\n        int enterCount = 0;\n        serverProvider.AddRequestHandler(\"Foo1\", (FakeArgument arg) =>\n        {\n            // 没有任何逻辑请求，不能处理\n            Assert.Fail();\n            return new FakeResult(\"Ok\");\n        });\n\n        serverProvider.AddRequestHandler(\"Foo2\", (FakeArgument arg, JsonIpcDirectRoutedContext context) =>\n        {\n            // 可以获取到客户端名\n            Assert.AreEqual(clientName, context.PeerName);\n\n            enterCount++;\n\n            return new FakeResult(responseText);\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        // 允许无参数，如果只是做客户端使用的话\n        JsonIpcDirectRoutedProvider clientProvider = new(clientName, TestJsonContext.CreateIpcConfiguration());\n        // 对于 clientProvider 来说，可选调用 StartServer 方法\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        var result = await clientProxy.GetResponseAsync<FakeResult>(\"Foo2\", argument);\n\n        // 可以获取到响应内容\n        Assert.AreEqual(responseText, result.Name);\n\n        // 要求只进入一次\n        Assert.AreEqual(1, enterCount);\n    }\n\n    [TestMethod(\"允许创建多个服务端实例共用相同的 IpcProvider 对象，但是如果多个服务端对相同的路由进行处理，只有先添加的才能收到消息\")]\n    public async Task TestRequest3()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Request_3\";\n        // 这样的创建方式也是对 IPC 连接最高定制的方式\n        IpcProvider ipcProvider = new(serverName, TestJsonContext.CreateIpcConfiguration());\n        var serverProvider1 = new JsonIpcDirectRoutedProvider(ipcProvider);\n        var argument = new FakeArgument(\"TestName\", 1);\n        int enterCount = 0;\n        const string routedPath = \"Foo1\";\n        var responseText = Guid.NewGuid().ToString();\n        TaskCompletionSource taskCompletionSource = new();\n\n        serverProvider1.AddRequestHandler(routedPath, (FakeArgument arg) =>\n        {\n            enterCount++;\n            return new FakeResult(responseText);\n        });\n\n        // 再次开启一个服务，共用相同的 IpcProvider 对象\n        var serverProvider2 = new JsonIpcDirectRoutedProvider(ipcProvider);\n\n        serverProvider2.AddRequestHandler(routedPath, (FakeArgument arg) =>\n        {\n            // 第二个服务收不到消息\n            Assert.Fail();\n\n            return new FakeResult(responseText);\n        });\n\n        // 多服务使用相同的 IpcProvider 对象存在一个问题，那就是如果是原先 IpcProvider 就启动过的，那这里添加处理一定会抛出异常\n        // 意味着需要所有的 JsonIpcDirectRoutedProvider 都创建和添加处理，才能一起调用 StartServer 开始\n        serverProvider1.StartServer();\n        serverProvider2.StartServer();\n\n        // 创建客户端\n        // 允许无参数，如果只是做客户端使用的话\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        var result = await clientProxy.GetResponseAsync<FakeResult>(\"Foo1\", argument);\n\n        // 可以获取到响应内容\n        Assert.IsNotNull(result);\n        Assert.AreEqual(responseText, result.Name);\n\n        // 要求只进入一次\n        Assert.AreEqual(1, enterCount);\n    }\n\n    [TestMethod(\"请求不存在的路径，能收到异常\")]\n    public async Task TestRequest4()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Request_4\";\n\n        IpcProvider ipcProvider = new(serverName, TestJsonContext.CreateIpcConfiguration());\n        var serverProvider1 = new JsonIpcDirectRoutedProvider(ipcProvider);\n\n        serverProvider1.AddRequestHandler(\"F1\", (FakeArgument arg) =>\n        {\n            return new FakeResult(\"F123\");\n        });\n        serverProvider1.StartServer();\n\n        // 创建客户端\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n        var argument = new FakeArgument(\"TestName\", 1);\n\n        await Assert.ThrowsExceptionAsync<JsonIpcDirectRoutedCanNotFindRequestHandlerException>(async () =>\n        {\n            var result = await clientProxy.GetResponseAsync<FakeResult>(\"不存在\", argument);\n            _ = result;\n        });\n    }\n\n    [TestMethod(\"如果请求的对象出现了异常，可以正确收到请求响应结束和具体的远端异常信息，而不会进入无限等待\")]\n    public async Task TestException1()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_TestException_1\";\n        var jsonIpcDirectRoutedProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var path = \"Foo\";\n        jsonIpcDirectRoutedProvider.AddRequestHandler(path, (FakeArgument fakeArgument) =>\n        {\n            if (!string.IsNullOrEmpty(fakeArgument.Name))\n            {\n                throw new FooException1(\"FooExceptionInfo\");\n            }\n\n            return new FakeResult(\"xx\");\n        });\n        jsonIpcDirectRoutedProvider.StartServer();\n\n        var t = new JsonIpcDirectRoutedProvider(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        var client = await t.GetAndConnectClientAsync(serverName);\n        try\n        {\n            var response = await client.GetResponseAsync<FakeResult>(path, new FakeArgument(\"xx\", 1));\n            _ = response;\n        }\n        catch (JsonIpcDirectRoutedHandleRequestRemoteException e)\n        {\n            // 不要使用 Assert.ThrowsException 方法，这个方法不适合同时将单元测试作为调试程序，不方便看到具体异常信息内容\n            // e.RemoteExceptionType 是 dotnetCampus.Ipc.Tests.IpcRouteds.DirectRouteds.JsonIpcDirectRoutedProviderTest+FooException1\n            Assert.IsTrue(e.RemoteExceptionType.Contains(nameof(FooException1)));\n            Assert.IsTrue(e.RemoteExceptionMessage == \"FooExceptionInfo\");\n\n            return;\n        }\n        // 预期能进入到 catch 分支进行返回\n        Assert.Fail(\"必定能进入到 catch 分支\");\n    }\n\n    [TestMethod(\"重复调用 JsonIpcDirectRoutedProvider 添加通知处理相同的消息，将会抛出异常\")]\n    public void AddNotifyHandler()\n    {\n        JsonIpcDirectRoutedProvider provider = new();\n\n        var routedPath = \"FooPath\";\n\n        provider.AddNotifyHandler(routedPath, (FakeArgument arg) =>\n        {\n        });\n\n        Assert.ThrowsException<InvalidOperationException>(() =>\n        {\n            provider.AddNotifyHandler(routedPath, (FakeArgument arg) =>\n            {\n            });\n        });\n    }\n\n    [TestMethod(\"重复调用 AddRequestHandler 添加请求处理相同的消息，将会抛出异常\")]\n    public void AddRequestHandler()\n    {\n        JsonIpcDirectRoutedProvider provider = new();\n\n        var routedPath = \"FooPath\";\n\n        provider.AddRequestHandler(routedPath, (FakeArgument argument) =>\n        {\n            return \"Ok\";\n        });\n\n        Assert.ThrowsException<InvalidOperationException>(() =>\n        {\n            provider.AddRequestHandler(routedPath, (FakeArgument argument) =>\n            {\n                return \"Ok\";\n            });\n        });\n    }\n\n    [TestMethod(\"允许创建多个服务端实例共用相同的 IpcProvider 对象，从而每个服务端接收不同的通知\")]\n    public async Task TestNotify1()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Notify_3\";\n        // 这样的创建方式也是对 IPC 连接最高定制的方式\n        IpcProvider ipcProvider = new(serverName, TestJsonContext.CreateIpcConfiguration());\n        var serverProvider1 = new JsonIpcDirectRoutedProvider(ipcProvider);\n        var argument = new FakeArgument(\"TestName\", 1);\n        int enterCount = 0;\n        TaskCompletionSource taskCompletionSource = new();\n\n        serverProvider1.AddNotifyHandler(\"Foo1\", (FakeArgument arg) =>\n        {\n            Interlocked.Increment(ref enterCount);\n            Assert.AreEqual(argument.Name, arg.Name);\n            Assert.AreEqual(argument.Count, arg.Count);\n\n            if (enterCount == 2)\n            {\n                taskCompletionSource.TrySetResult();\n            }\n        });\n\n        // 再次开启一个服务，共用相同的 IpcProvider 对象\n        var serverProvider2 = new JsonIpcDirectRoutedProvider(ipcProvider);\n\n        serverProvider2.AddNotifyHandler(\"Foo2\", (FakeArgument arg) =>\n        {\n            Interlocked.Increment(ref enterCount);\n            Assert.AreEqual(argument.Name, arg.Name);\n            Assert.AreEqual(argument.Count, arg.Count);\n\n            if (enterCount == 2)\n            {\n                taskCompletionSource.TrySetResult();\n            }\n        });\n        serverProvider1.StartServer();\n        serverProvider2.StartServer();\n\n        // 创建客户端\n        // 允许无参数，如果只是做客户端使用的话\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n        await clientProxy.NotifyAsync(\"Foo1\", argument);\n        // 预期这条消息是在第二个服务处理的\n        await clientProxy.NotifyAsync(\"Foo2\", argument);\n\n        // 等待接收完成，以上的 await 返回仅仅只是发送出去，不代表对方已接收到\n        await taskCompletionSource.Task.WaitTimeout(TimeSpan.FromSeconds(5));\n        // 两个服务都进入\n        Assert.AreEqual(2, enterCount);\n    }\n\n    [TestMethod(\"从客户端通知到服务端，可以在服务端获取到通知的客户端名\")]\n    public async Task TestNotify2()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Notify_2\";\n        var clientName = \"JsonIpcDirectRoutedProviderTest_Notify_Client_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var argument = new FakeArgument(\"TestName\", 1);\n\n        int enterCount = 0;\n        TaskCompletionSource taskCompletionSource = new();\n        serverProvider.AddNotifyHandler(\"Foo1\", (FakeArgument arg) =>\n        {\n            // 没有任何逻辑请求，不能处理\n            Assert.Fail();\n        });\n\n        serverProvider.AddNotifyHandler<FakeArgument>(\"Foo2\", (arg, context) =>\n        {\n            // 可以获取到客户端名\n            Assert.AreEqual(clientName, context.PeerName);\n\n            enterCount++;\n\n            taskCompletionSource.TrySetResult();\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        // 允许无参数，如果只是做客户端使用的话\n        JsonIpcDirectRoutedProvider clientProvider = new(clientName, TestJsonContext.CreateIpcConfiguration());\n        // 对于 clientProvider 来说，可选调用 StartServer 方法\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        await clientProxy.NotifyAsync(\"Foo2\", argument);\n\n        // 等待接收完成，以上的 await 返回仅仅只是发送出去，不代表对方已接收到\n        await taskCompletionSource.Task.WaitTimeout(TimeSpan.FromSeconds(5));\n        // 要求只进入一次\n        Assert.AreEqual(1, enterCount);\n    }\n\n    [TestMethod(\"客户端的通知可以成功发送到服务端\")]\n    public async Task TestNotify3()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Notify_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var routedPath = \"Foo1\";\n        var argument = new FakeArgument(\"TestName\", 1);\n\n        int enterCount = 0;\n        TaskCompletionSource taskCompletionSource = new();\n        serverProvider.AddNotifyHandler(routedPath, (FakeArgument arg) =>\n        {\n            enterCount++;\n            Assert.AreEqual(argument.Name, arg.Name);\n            Assert.AreEqual(argument.Count, arg.Count);\n\n            taskCompletionSource.TrySetResult();\n        });\n\n        serverProvider.AddNotifyHandler<FakeArgument>(\"Foo2\", (arg, context) =>\n        {\n            // 没有任何逻辑请求，不能处理\n            Assert.Fail();\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        // 允许无参数，如果只是做客户端使用的话\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        clientProvider.StartServer();\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        await clientProxy.NotifyAsync(routedPath, argument);\n\n        // 等待接收完成，以上的 await 返回仅仅只是发送出去，不代表对方已接收到\n        await taskCompletionSource.Task.WaitTimeout(TimeSpan.FromSeconds(5));\n        // 要求只进入一次\n        Assert.AreEqual(1, enterCount);\n    }\n\n    [TestMethod(\"配置 LocalOneByOne 即可让服务端收到的通知消息是一条条按照顺序接收的\")]\n    public async Task TestNotifyLocalOneByOne()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Test_NotifyLocalOneByOne_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, new IpcConfiguration() { IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne, });\n\n        var count = 0;\n        for (int i = 0; i < 10; i++)\n        {\n            var n = i;\n            serverProvider.AddNotifyHandler($\"Foo{n}\", async () =>\n            {\n                // 如果是按照顺序进来的，那就是按照数字顺序\n                Assert.AreEqual(n, count);\n                count++;\n                // 模拟异步处理\n                await Task.Delay(100);\n            });\n        }\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        JsonIpcDirectRoutedProvider clientProvider = new();\n        clientProvider.StartServer();\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        // 发送 10 条通知\n        for (int i = 0; i < 10; i++)\n        {\n            await clientProxy.NotifyAsync($\"Foo{i}\");\n        }\n\n        // 等待接收完成，以上的 await 返回仅仅只是发送出去，不代表对方已接收到\n        for (int i = 0; i < 1000 && count != 10; i++)\n        {\n            await Task.Delay(100);\n        }\n    }\n\n    /// <summary>\n    /// 测试无参版本\n    /// </summary>\n    [TestMethod(\"发送无参请求，可以让服务端收到请求\")]\n    public async Task TestParameterless()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Test_Parameterless_1\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var routedPath = \"Foo1\";\n\n        // 注册无参数请求处理\n        serverProvider.AddRequestHandler(routedPath, () =>\n        {\n            return new FakeResult(nameof(TestParameterless));\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        clientProvider.StartServer();\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        // 请求无参数\n        var result = await clientProxy.GetResponseAsync<FakeResult>(routedPath);\n\n        // 如果能收到服务端返回值，证明请求成功\n        Assert.IsNotNull(result);\n        Assert.AreEqual(nameof(TestParameterless), result.Name);\n    }\n\n    [TestMethod(\"发送无参通知，可以让服务端收到通知\")]\n    public async Task TestParameterless2()\n    {\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Test_Parameterless_2\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName);\n        var routedPath = \"Foo1\";\n\n        var taskCompletionSource = new TaskCompletionSource();\n        serverProvider.AddNotifyHandler(routedPath, () =>\n        {\n            taskCompletionSource.SetResult();\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        JsonIpcDirectRoutedProvider clientProvider = new();\n        clientProvider.StartServer();\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n        await clientProxy.NotifyAsync(routedPath);\n\n        // 再等待一下，让服务端处理完成\n        await taskCompletionSource.Task.WaitTimeout(TimeSpan.FromSeconds(1));\n        // 预期是服务端能够执行完成\n        Assert.AreEqual(true, taskCompletionSource.Task.IsCompleted);\n    }\n\n    [TestMethod(\"发送无参请求，服务端订阅有参，依然可以让服务端收到请求\")]\n    public async Task TestParameterless3()\n    {\n        // 可能是版本兼容，一个客户端版本软件是旧版本发送时不带参数，后续的服务端新版本写了带参数处理\n        // 期望这样的情况服务端依然能够收到请求，达成兼容\n        // 初始化服务端\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Test_Parameterless_3\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var routedPath = \"Foo1\";\n\n        // 服务端订阅有参\n        serverProvider.AddRequestHandler(routedPath, (FakeArgument arg) =>\n        {\n            Assert.IsNotNull(arg);\n\n            return new FakeResult(nameof(TestParameterless));\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        clientProvider.StartServer();\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        // 请求无参数\n        var result = await clientProxy.GetResponseAsync<FakeResult>(routedPath);\n\n        // 如果能收到服务端返回值，证明请求成功\n        Assert.IsNotNull(result);\n        Assert.AreEqual(nameof(TestParameterless), result.Name);\n    }\n\n    [TestMethod(\"客户端发送有参请求，服务端订阅无参，依然可以让服务端收到请求\")]\n    public async Task TestParameterless4()\n    {\n        var serverName = \"JsonIpcDirectRoutedProviderTest_Test_Parameterless_4\";\n        var serverProvider = new JsonIpcDirectRoutedProvider(serverName, TestJsonContext.CreateIpcConfiguration());\n        var routedPath = \"Foo1\";\n\n        // 服务端订阅无参\n        serverProvider.AddRequestHandler(routedPath, () =>\n        {\n            return new FakeResult(nameof(TestParameterless));\n        });\n\n        serverProvider.StartServer();\n\n        // 创建客户端\n        JsonIpcDirectRoutedProvider clientProvider = new(ipcConfiguration: TestJsonContext.CreateIpcConfiguration());\n        clientProvider.StartServer();\n        var clientProxy = await clientProvider.GetAndConnectClientAsync(serverName);\n\n        // 发送有参请求\n        var result = await clientProxy.GetResponseAsync<FakeResult>(routedPath, new FakeArgument(\"foo\", 2));\n\n        // 如果能收到服务端返回值，证明请求成功\n        Assert.IsNotNull(result);\n        Assert.AreEqual(nameof(TestParameterless), result.Name);\n    }\n\n    internal record class FakeArgument(string Name, int Count)\n    {\n    }\n\n    internal record class FakeResult(string Name);\n\n    private class FooException1 : Exception\n    {\n        public FooException1(string? message) : base(message)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/NotifyTest.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Threading;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests;\n\n[TestClass]\npublic class NotifyTest\n{\n    [TestMethod(\"使用 LocalOneByOne 的线程调度，按照顺序调用 NotifyAsync 方法，但是不等待 NotifyAsync 方法执行完成，可以按照顺序接收\")]\n    public async Task Notify1()\n    {\n        var name = \"B_PeerNotifyTest_01\";\n\n        // 让 a 发送给 b 接收\n        var a = new IpcProvider(\"A_PeerNotifyTest_01\");\n        var b = new IpcProvider(name, new IpcConfiguration()\n        {\n            // 使用 LocalOneByOne 的线程调度\n            IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne\n        });\n\n        a.StartServer();\n        b.StartServer();\n\n        int currentCount = 0;\n        var taskCompletionSource = new TaskCompletionSource<bool>();\n        const int count = byte.MaxValue - 10;\n\n        b.PeerConnected += (sender, args) =>\n        {\n            var aPeer = args.Peer;\n            aPeer.MessageReceived += (_, e) =>\n            {\n                // 按照顺序调用 Notify 方法，可以按照顺序接收\n                // 发送的顺序就是首个不断加一，于是判断收到是否连续即可\n                Assert.AreEqual(currentCount, e.Message.Body.Buffer[0]);\n                Interlocked.Increment(ref currentCount);\n\n                if (currentCount == count)\n                {\n                    // 全部执行完成\n                    taskCompletionSource.SetResult(true);\n                }\n            };\n        };\n\n        var peer = await a.GetAndConnectToPeerAsync(name);\n\n        // 按照顺序调用 NotifyAsync 方法，但是不等待 NotifyAsync 方法执行完成\n        for (int i = 0; i < count; i++)\n        {\n            _ = peer.NotifyAsync(new IpcMessage(\"测试使用 LocalOneByOne 的线程调度，按照顺序调用 Notify 方法，可以按照顺序接收\",\n                new byte[] { (byte) i, 0xF1, 0xF2 }));\n        }\n\n        // 等待全部执行完成\n        await Task.WhenAny(taskCompletionSource.Task, Task.Delay(TimeSpan.FromMinutes(5)));\n        Assert.AreEqual(true, taskCompletionSource.Task.IsCompleted);\n    }\n\n    [TestMethod(\"使用 LocalOneByOne 的线程调度，按照顺序调用 Notify 方法，可以按照顺序接收\")]\n    public async Task Notify2()\n    {\n        var name = \"B_PeerNotifyTest\";\n\n        // 让 a 发送给 b 接收\n        var a = new IpcProvider(\"A_PeerNotifyTest\");\n        var b = new IpcProvider(name, new IpcConfiguration()\n        {\n            // 使用 LocalOneByOne 的线程调度\n            IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne\n        });\n\n        a.StartServer();\n        b.StartServer();\n\n        int currentCount = 0;\n\n        b.PeerConnected += (sender, args) =>\n        {\n            var aPeer = args.Peer;\n            aPeer.MessageReceived += (_, e) =>\n            {\n                // 按照顺序调用 Notify 方法，可以按照顺序接收\n                // 发送的顺序就是首个不断加一，于是判断收到是否连续即可\n                Assert.AreEqual(currentCount, e.Message.Body.Buffer[0]);\n                Interlocked.Increment(ref currentCount);\n            };\n        };\n\n        var peer = await a.GetAndConnectToPeerAsync(name);\n\n        // 按照顺序调用 Notify 方法\n        for (int i = 0; i < byte.MaxValue - 10; i++)\n        {\n            await peer.NotifyAsync(new IpcMessage(\"测试使用 LocalOneByOne 的线程调度，按照顺序调用 Notify 方法，可以按照顺序接收\",\n                new byte[] { (byte) i, 0xF1, 0xF2 }));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/PeerManagerTest.cs",
    "content": "﻿using dotnetCampus.Ipc.Pipes;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests;\n\n[TestClass]\npublic class PeerManagerTest\n{\n    [TestMethod(\"无论是主动连接还是被动连接，都能触发 PeerManager.PeerConnected 事件\")]\n    public async Task TestPeerManager1()\n    {\n        var nameA = \"PeerManagerTest_1\";\n        var nameB = \"PeerManagerTest_2\";\n\n        var a = new IpcProvider(nameA);\n        var b = new IpcProvider(nameB);\n\n        var aPeerManagerConnectedCount = 0;\n        var bPeerManagerConnectedCount = 0;\n\n        a.PeerManager.PeerConnected += (sender, args) =>\n        {\n            aPeerManagerConnectedCount++;\n        };\n\n        b.PeerManager.PeerConnected += (sender, args) =>\n        {\n            bPeerManagerConnectedCount++;\n        };\n\n        var aIpcProviderConnectedCount = 0;\n        var bIpcProviderConnectedCount = 0;\n\n        a.PeerConnected += (sender, args) =>\n        {\n            aIpcProviderConnectedCount++;\n        };\n        var bConnectedTaskCompletionSource = new TaskCompletionSource();\n        b.PeerConnected += (sender, args) =>\n        {\n            bIpcProviderConnectedCount++;\n            bConnectedTaskCompletionSource.SetResult();\n        };\n\n        a.StartServer();\n\n        Task<PeerProxy> connectTask = a.GetAndConnectToPeerAsync(nameB);\n\n        Assert.IsFalse(connectTask.IsCompleted, \"由于此时 B 服务还没启动，必定现在还没连接完成\");\n        // 这里有一个小争议点，那就是事件名为 PeerConnected 但实际上可能没有完全完成连接的建立\n        Assert.AreEqual(1, aPeerManagerConnectedCount, \"已经主动在 a 发起连接，此时有一个记录\");\n        Assert.AreEqual(0, bPeerManagerConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n\n        Assert.AreEqual(0, aIpcProviderConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n        Assert.AreEqual(0, bIpcProviderConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n\n        // 开启 B 服务了，预期现在能够完成连接\n        b.StartServer();\n\n        // 等待连接\n        await connectTask;\n\n        // 由于 b 是在另一个线程\\进程跑的，不是在当前单元测试所在的线程跑的。需要等待一下，避免多线程执行顺序，导致单元测试概率不通过\n        await Task.WhenAny(bConnectedTaskCompletionSource.Task, Task.Delay(TimeSpan.FromSeconds(3)));\n\n        // 连接完成之后，预期现在无论是 a 还是 b 的 PeerManager 都有一次连接触发。但 IpcProvider 的 PeerConnected 只有 b 被动连接的一次触发\n        Assert.AreEqual(1, aPeerManagerConnectedCount, \"连接完成之后，无论主动连接，都能让 PeerManager.PeerConnected 事件触发\");\n        Assert.AreEqual(1, bPeerManagerConnectedCount, \"连接完成之后，无论主动连接，都能让 PeerManager.PeerConnected 事件触发\");\n\n        Assert.AreEqual(0, aIpcProviderConnectedCount, \"连接完成之后，只有被动连接才能让 IpcProvider.PeerConnected 事件触发。由于是 a 主动连接 b 的，因此 a 的 IpcProvider.PeerConnected 事件没有触发\");\n        Assert.AreEqual(1, bIpcProviderConnectedCount, \"连接完成之后，只有被动连接才能让 IpcProvider.PeerConnected 事件触发。由于是 a 主动连接 b 的，因此 b 的 IpcProvider.PeerConnected 事件触发\");\n\n        // 当前连接只有一项\n        Assert.AreEqual(1, a.PeerManager.CurrentConnectedPeerProxyCount);\n        Assert.AreEqual(1, b.PeerManager.CurrentConnectedPeerProxyCount);\n\n        Assert.AreEqual(1, a.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n        Assert.AreEqual(1, b.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n\n        // 现在 a 所连接的就是 b 的\n        Assert.AreEqual(nameB, a.PeerManager.GetCurrentConnectedPeerProxyList()[0].PeerName);\n        // 现在 b 所连接的就是 a 的\n        Assert.AreEqual(nameA, b.PeerManager.GetCurrentConnectedPeerProxyList()[0].PeerName);\n    }\n\n    [TestMethod(\"a 主动连接 b 和 c，测试三端的 PeerManager.PeerConnected 事件和连接数量\")]\n    public async Task TestPeerManager2()\n    {\n        var nameA = \"PeerManagerTest2_A\";\n        var nameB = \"PeerManagerTest2_B\";\n        var nameC = \"PeerManagerTest2_C\";\n\n        var a = new IpcProvider(nameA);\n        var b = new IpcProvider(nameB);\n        var c = new IpcProvider(nameC);\n\n        var aPeerManagerConnectedCount = 0;\n        var bPeerManagerConnectedCount = 0;\n        var cPeerManagerConnectedCount = 0;\n\n        a.PeerManager.PeerConnected += (sender, args) =>\n        {\n            aPeerManagerConnectedCount++;\n        };\n\n        b.PeerManager.PeerConnected += (sender, args) =>\n        {\n            bPeerManagerConnectedCount++;\n        };\n\n        c.PeerManager.PeerConnected += (sender, args) =>\n        {\n            cPeerManagerConnectedCount++;\n        };\n\n        var aIpcProviderConnectedCount = 0;\n        var bIpcProviderConnectedCount = 0;\n        var cIpcProviderConnectedCount = 0;\n\n        a.PeerConnected += (sender, args) =>\n        {\n            aIpcProviderConnectedCount++;\n        };\n\n        var bConnectedTaskCompletionSource = new TaskCompletionSource();\n        b.PeerConnected += (sender, args) =>\n        {\n            bIpcProviderConnectedCount++;\n            bConnectedTaskCompletionSource.SetResult();\n        };\n\n        var cConnectedTaskCompletionSource = new TaskCompletionSource();\n        c.PeerConnected += (sender, args) =>\n        {\n            cIpcProviderConnectedCount++;\n            cConnectedTaskCompletionSource.SetResult();\n        };\n\n        a.StartServer();\n\n        Task<PeerProxy> connectToBTask = a.GetAndConnectToPeerAsync(nameB);\n        Task<PeerProxy> connectToCTask = a.GetAndConnectToPeerAsync(nameC);\n\n        Assert.IsFalse(connectToBTask.IsCompleted, \"由于此时 B 服务还没启动，必定现在还没连接完成\");\n        Assert.IsFalse(connectToCTask.IsCompleted, \"由于此时 C 服务还没启动，必定现在还没连接完成\");\n        // 这里有一个小争议点，那就是事件名为 PeerConnected 但实际上可能没有完全完成连接的建立\n        Assert.AreEqual(2, aPeerManagerConnectedCount, \"a 主动发起连接 b 和 c，此时已有两个记录\");\n        Assert.AreEqual(0, bPeerManagerConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n        Assert.AreEqual(0, cPeerManagerConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n\n        Assert.AreEqual(0, aIpcProviderConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n        Assert.AreEqual(0, bIpcProviderConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n        Assert.AreEqual(0, cIpcProviderConnectedCount, \"还没有完成建立连接，必定现在是 0 的值\");\n\n        // 开启 B 和 C 服务了，预期现在能够完成连接\n        b.StartServer();\n        c.StartServer();\n\n        // 等待连接\n        await Task.WhenAll(connectToBTask, connectToCTask);\n\n        // 由于 b 和 c 是在另一个线程跑的，需要等待一下，避免多线程执行顺序，导致单元测试概率不通过\n        await Task.WhenAny(\n            Task.WhenAll(bConnectedTaskCompletionSource.Task, cConnectedTaskCompletionSource.Task),\n            Task.Delay(TimeSpan.FromSeconds(3)));\n\n        Assert.AreEqual(2, aPeerManagerConnectedCount, \"连接完成之后，a 主动连接 b 和 c，PeerManager.PeerConnected 触发两次\");\n        Assert.AreEqual(1, bPeerManagerConnectedCount, \"连接完成之后，b 被 a 连接，PeerManager.PeerConnected 触发一次\");\n        Assert.AreEqual(1, cPeerManagerConnectedCount, \"连接完成之后，c 被 a 连接，PeerManager.PeerConnected 触发一次\");\n\n        Assert.AreEqual(0, aIpcProviderConnectedCount, \"a 是主动连接方，IpcProvider.PeerConnected 事件不触发\");\n        Assert.AreEqual(1, bIpcProviderConnectedCount, \"b 是被动连接方，IpcProvider.PeerConnected 事件触发一次\");\n        Assert.AreEqual(1, cIpcProviderConnectedCount, \"c 是被动连接方，IpcProvider.PeerConnected 事件触发一次\");\n\n        Assert.AreEqual(2, a.PeerManager.CurrentConnectedPeerProxyCount);\n        Assert.AreEqual(1, b.PeerManager.CurrentConnectedPeerProxyCount);\n        Assert.AreEqual(1, c.PeerManager.CurrentConnectedPeerProxyCount);\n\n        Assert.AreEqual(2, a.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n        Assert.AreEqual(1, b.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n        Assert.AreEqual(1, c.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n\n        // a 连接了 b 和 c，顺序不保证，用名称集合验证\n        var aConnectedPeerNames = a.PeerManager.GetCurrentConnectedPeerProxyList().Select(p => p.PeerName).ToHashSet();\n        Assert.IsTrue(aConnectedPeerNames.Contains(nameB), \"a 连接的列表中应包含 b\");\n        Assert.IsTrue(aConnectedPeerNames.Contains(nameC), \"a 连接的列表中应包含 c\");\n\n        // b 和 c 各自只连了 a\n        Assert.AreEqual(nameA, b.PeerManager.GetCurrentConnectedPeerProxyList()[0].PeerName);\n        Assert.AreEqual(nameA, c.PeerManager.GetCurrentConnectedPeerProxyList()[0].PeerName);\n    }\n\n    [TestMethod(\"b 和 c 主动连接 a，测试三端的 PeerManager.PeerConnected 事件和连接数量\")]\n    public async Task TestPeerManager3()\n    {\n        var nameA = \"PeerManagerTest3_A\";\n        var nameB = \"PeerManagerTest3_B\";\n        var nameC = \"PeerManagerTest3_C\";\n\n        var a = new IpcProvider(nameA);\n        var b = new IpcProvider(nameB);\n        var c = new IpcProvider(nameC);\n\n        var aPeerManagerConnectedCount = 0;\n        var bPeerManagerConnectedCount = 0;\n        var cPeerManagerConnectedCount = 0;\n\n        a.PeerManager.PeerConnected += (sender, args) =>\n        {\n            aPeerManagerConnectedCount++;\n        };\n\n        b.PeerManager.PeerConnected += (sender, args) =>\n        {\n            bPeerManagerConnectedCount++;\n        };\n\n        c.PeerManager.PeerConnected += (sender, args) =>\n        {\n            cPeerManagerConnectedCount++;\n        };\n\n        var aIpcProviderConnectedCount = 0;\n        var bIpcProviderConnectedCount = 0;\n        var cIpcProviderConnectedCount = 0;\n\n        var aAllConnectedTaskCompletionSource = new TaskCompletionSource();\n        a.PeerConnected += (sender, args) =>\n        {\n            aIpcProviderConnectedCount++;\n            if (aIpcProviderConnectedCount >= 2)\n            {\n                aAllConnectedTaskCompletionSource.TrySetResult();\n            }\n        };\n\n        b.PeerConnected += (sender, args) =>\n        {\n            bIpcProviderConnectedCount++;\n        };\n\n        c.PeerConnected += (sender, args) =>\n        {\n            cIpcProviderConnectedCount++;\n        };\n\n        a.StartServer();\n        b.StartServer();\n        c.StartServer();\n\n        Task<PeerProxy> bConnectToATask = b.GetAndConnectToPeerAsync(nameA);\n        Task<PeerProxy> cConnectToATask = c.GetAndConnectToPeerAsync(nameA);\n\n        // 这里有一个小争议点，那就是事件名为 PeerConnected 但实际上可能没有完全完成连接的建立\n        Assert.AreEqual(1, bPeerManagerConnectedCount, \"b 主动发起连接 a，此时 b 有一个记录\");\n        Assert.AreEqual(1, cPeerManagerConnectedCount, \"c 主动发起连接 a，此时 c 有一个记录\");\n\n        Assert.AreEqual(0, bIpcProviderConnectedCount, \"b 是主动连接方，IpcProvider.PeerConnected 事件不触发\");\n        Assert.AreEqual(0, cIpcProviderConnectedCount, \"c 是主动连接方，IpcProvider.PeerConnected 事件不触发\");\n\n        // 等待连接\n        await Task.WhenAll(bConnectToATask, cConnectToATask);\n\n        // 由于 a 是在另一个线程跑的，需要等待一下，避免多线程执行顺序，导致单元测试概率不通过\n        await Task.WhenAny(aAllConnectedTaskCompletionSource.Task, Task.Delay(TimeSpan.FromSeconds(3)));\n\n        Assert.AreEqual(2, aPeerManagerConnectedCount, \"连接完成之后，a 被 b 和 c 连接，PeerManager.PeerConnected 触发两次\");\n        Assert.AreEqual(1, bPeerManagerConnectedCount, \"b 主动连接 a，PeerManager.PeerConnected 触发一次\");\n        Assert.AreEqual(1, cPeerManagerConnectedCount, \"c 主动连接 a，PeerManager.PeerConnected 触发一次\");\n\n        Assert.AreEqual(2, aIpcProviderConnectedCount, \"a 是被动连接方，IpcProvider.PeerConnected 事件触发两次\");\n        Assert.AreEqual(0, bIpcProviderConnectedCount, \"b 是主动连接方，IpcProvider.PeerConnected 事件不触发\");\n        Assert.AreEqual(0, cIpcProviderConnectedCount, \"c 是主动连接方，IpcProvider.PeerConnected 事件不触发\");\n\n        Assert.AreEqual(2, a.PeerManager.CurrentConnectedPeerProxyCount);\n        Assert.AreEqual(1, b.PeerManager.CurrentConnectedPeerProxyCount);\n        Assert.AreEqual(1, c.PeerManager.CurrentConnectedPeerProxyCount);\n\n        Assert.AreEqual(2, a.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n        Assert.AreEqual(1, b.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n        Assert.AreEqual(1, c.PeerManager.GetCurrentConnectedPeerProxyList().Count);\n\n        // a 被 b 和 c 连接，顺序不保证，用名称集合验证\n        var aConnectedPeerNames = a.PeerManager.GetCurrentConnectedPeerProxyList().Select(p => p.PeerName).ToHashSet();\n        Assert.IsTrue(aConnectedPeerNames.Contains(nameB), \"a 连接的列表中应包含 b\");\n        Assert.IsTrue(aConnectedPeerNames.Contains(nameC), \"a 连接的列表中应包含 c\");\n\n        // b 和 c 各自只连了 a\n        Assert.AreEqual(nameA, b.PeerManager.GetCurrentConnectedPeerProxyList()[0].PeerName);\n        Assert.AreEqual(nameA, c.PeerManager.GetCurrentConnectedPeerProxyList()[0].PeerName);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/PeerProxyTest.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Threading;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests\n{\n    [TestClass]\n    public class PeerProxyTest\n    {\n        [TestMethod(\"断开连接过程中，所有请求响应，都可以在重连之后请求成功\")]\n        public async Task SendWithReconnect1()\n        {\n            var name = \"B_PeerReconnected\";\n            var aRequest = new byte[] { 0xF1 };\n            var cResponse = new byte[] { 0xF1, 0xF2 };\n\n            var a = new IpcProvider(\"A_PeerReconnected\", new IpcConfiguration() { AutoReconnectPeers = true, });\n            // 毕竟一会就要挂了，啥都不需要配置\n            var b = new IpcProvider(name);\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n\n            // 连接成功了，那么就让 b 凉凉\n            b.Dispose();\n\n            // 等待后台所有断开成功\n            await Task.Delay(TimeSpan.FromSeconds(2));\n\n            // 预期状态是 Peer 是断开的，等待重新连接\n            Assert.AreEqual(true, peer.IsBroken);\n            Assert.AreEqual(false, peer.WaitForFinishedTaskCompletionSource.Task.IsCompleted);\n\n            // 开始请求响应，预期进入等待，没有任何响应\n            var requestTask = peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n\n            // 重新启动 b 服务，用法是再新建一个 c 用了 b 的 name 从而假装是 b 重启\n            var c = new IpcProvider(name, new IpcConfiguration()\n            {\n                DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                    new IpcHandleRequestMessageResult(new IpcMessage(\"C回复\", cResponse)))\n            });\n            c.StartServer();\n\n            // 预期可以收到\n            await requestTask.WaitTimeout(TimeSpan.FromSeconds(3));\n            var ipcMessage = await requestTask;\n            Assert.AreEqual(true, ipcMessage.Body.AsSpan().SequenceEqual(cResponse));\n        }\n\n        [TestMethod(\"断开连接过程中，发送的所有消息，都可以在重连之后发送\")]\n        public async Task SendWithReconnect2()\n        {\n            var name = \"B_PeerReconnected_F2\";\n            var aRequest = new byte[] { 0xF1 };\n\n            var a = new IpcProvider(\"A_PeerReconnected_F2\", new IpcConfiguration() { AutoReconnectPeers = true, });\n            // 毕竟一会就要挂了，啥都不需要配置\n            var b = new IpcProvider(name);\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n\n            // 连接成功了，那么就让 b 凉凉\n            b.Dispose();\n\n            // 等待后台所有断开成功\n            await Task.Delay(TimeSpan.FromSeconds(2));\n\n            // 预期状态是 Peer 是断开的，等待重新连接\n            Assert.AreEqual(true, peer.IsBroken);\n            Assert.AreEqual(false, peer.WaitForFinishedTaskCompletionSource.Task.IsCompleted);\n\n            // 开始发送消息，此时发送消息的任务都在进入等待\n            var notifyTask = peer.NotifyAsync(new IpcMessage(\"A发送\", aRequest));\n\n            // 稍微等一下，此时预期还是没有发送完成\n            await Task.WhenAny(notifyTask, Task.Delay(TimeSpan.FromMilliseconds(100)));\n            Assert.AreEqual(false, notifyTask.IsCompleted);\n\n            // 重新启动 b 服务，用法是再新建一个 c 用了 b 的 name 从而假装是 b 重启\n            var c = new IpcProvider(name);\n\n            // 是否可以收到重新发送消息\n            var receiveANotifyTask = new TaskCompletionSource<bool>();\n            c.PeerConnected += (s, e) =>\n            {\n                // 预期这里是 A 连接过来\n                e.Peer.MessageReceived += (sender, args) =>\n                {\n                    if (args.Message.Body.AsSpan().SequenceEqual(aRequest))\n                    {\n                        receiveANotifyTask.SetResult(true);\n                    }\n                };\n            };\n\n            c.StartServer();\n\n            var receiveAFromGlobalMessageReceived = new TaskCompletionSource<bool>();\n            c.IpcServerService.MessageReceived += (s, e) =>\n            {\n                if (e.Message.Body.AsSpan().SequenceEqual(aRequest))\n                {\n                    receiveAFromGlobalMessageReceived.SetResult(true);\n                }\n            };\n\n            await receiveANotifyTask.Task.WaitTimeout(TimeSpan.FromSeconds(5));\n            await notifyTask.WaitTimeout(TimeSpan.FromSeconds(5));\n\n            // 发送成功\n            Assert.AreEqual(true, notifyTask.IsCompleted);\n            if (receiveANotifyTask.Task.IsCompleted)\n            {\n                // 和平，能收到重新连接发过来的消息\n            }\n            else\n            {\n                if (receiveAFromGlobalMessageReceived.Task.IsCompleted)\n                {\n                    // 如果被全局收了，那也是预期的，因为连接过来之后，立刻收到消息，此时的 e.Peer.MessageReceived+=xx 的代码还没跑\n                }\n                else\n                {\n                    Assert.Fail(\"没有收到重连的消息\");\n                }\n            }\n        }\n\n        [TestMethod(\"向对方请求响应，可以拿到对方的回复\")]\n        public async Task GetResponseAsync()\n        {\n            var name = \"B_PeerReconnected_GetResponseAsync\";\n            var aRequest = new byte[] { 0xF1 };\n            var bResponse = new byte[] { 0xF1, 0xF2 };\n\n            var a = new IpcProvider(\"A_PeerReconnected_GetResponseAsync\");\n            var b = new IpcProvider(name, new IpcConfiguration()\n            {\n                DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                    new IpcHandleRequestMessageResult(new IpcMessage(\"B回复\", bResponse)))\n            });\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n            var request1 = await peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n            Assert.AreEqual(true, bResponse.AsSpan().SequenceEqual(request1.Body.AsSpan()));\n\n            // 多次发送消息测试一下\n            var request2 = await peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n            Assert.AreEqual(true, bResponse.AsSpan().SequenceEqual(request2.Body.AsSpan()));\n        }\n\n        [TestMethod(\"连接过程中，对方断掉重连，可以收到重连事件\")]\n        public async Task PeerReconnected()\n        {\n            var name = \"B_PeerReconnected_Main\";\n            var a = new IpcProvider(\"A_PeerReconnected_Main\", new IpcConfiguration() { AutoReconnectPeers = true });\n            var b = new IpcProvider(name);\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n            var peerReconnectedTask = new TaskCompletionSource<bool>();\n            peer.PeerReconnected += delegate\n            {\n                peerReconnectedTask.SetResult(true);\n            };\n\n            // 断开 b 此时只会收到断开消息，不会收到重连消息\n            b.Dispose();\n\n            // 等待2秒，预计此时是不会收到重新连接消息，也就是 peerReconnectedTask 任务还没完成\n            await Task.Delay(TimeSpan.FromSeconds(2));\n            // 判断此时是否收到重连消息\n            Assert.AreEqual(false, peerReconnectedTask.Task.IsCompleted, \"还没有重启 b 服务，但是已收到重连消息\");\n\n            // 重新启动 b 服务，用法是再新建一个 c 用了 b 的 name 从而假装是 b 重启\n            var c = new IpcProvider(name);\n            c.StartServer();\n\n            // 多线程，需要等待一下，等待连接\n            await peerReconnectedTask.Task.WaitTimeout(TimeSpan.FromSeconds(3));\n\n            Assert.AreEqual(true, peerReconnectedTask.Task.IsCompleted);\n            Assert.AreEqual(true, peerReconnectedTask.Task.Result);\n\n            Assert.AreEqual(true, peer.IsConnectedFinished);\n            Assert.AreEqual(false, peer.IsBroken);\n        }\n\n        [TestMethod(\"发送请求响应，对方断掉，请求将会抛出异常\")]\n        public async Task PeerConnectionBroken1()\n        {\n            var name = \"B_BreakAllRequestTaskByIpcBroken\";\n            var aRequest = new byte[] { 0xF2 };\n            var asyncManualResetEvent = new AsyncManualResetEvent(false);\n            var a = new IpcProvider(\"A_BreakAllRequestTaskByIpcBroken\");\n            var b = new IpcProvider(name,\n                new IpcConfiguration()\n                {\n                    DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                        Task.Run<IIpcResponseMessage>(async () =>\n                        {\n                            await asyncManualResetEvent.WaitOneAsync();\n                            return (IIpcResponseMessage) null;\n                        }))\n                });\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n\n            // 发送请求，预期这些请求都没有收到回复\n            var taskList = new List<Task<IpcMessage>>();\n            for (int i = 0; i < 10; i++)\n            {\n                Task<IpcMessage> responseTask = peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n                taskList.Add(responseTask);\n            }\n\n            await Task.Yield();\n\n            foreach (var task in taskList)\n            {\n                Assert.AreEqual(false, task.IsCompleted);\n            }\n\n            // 让消息写入一下\n            await Task.Delay(TimeSpan.FromSeconds(2));\n\n            b.Dispose();\n\n            // 等待断开\n            await Task.Delay(TimeSpan.FromSeconds(5));\n            foreach (var task in taskList)\n            {\n                Assert.AreEqual(true, task.IsCompleted);\n\n                // 这里的异常也许是 连接断开异常， 也许是写入过程中，对方已断开异常\n                Assert.IsNotNull(task.Exception?.InnerExceptions[0] as Exception);\n            }\n\n            // 所有请求都炸掉\n            Assert.AreEqual(0, peer.IpcMessageRequestManager.WaitingResponseCount);\n        }\n\n        [TestMethod(\"连接过程中，对方断掉，可以收到对方断掉的消息\")]\n        public async Task PeerConnectionBroken2()\n        {\n            // 让 a 去连接 b 然后聊聊天\n            // 接着将 b 结束，此时 a 的 peer 将会断开连接\n            var name = \"B_PeerConnectionBroken_PeerConnectionBroken\";\n            var aRequest = new byte[] { 0xF1 };\n            var bResponse = new byte[] { 0xF1, 0xF2 };\n\n            var a = new IpcProvider(\"A_PeerConnectionBroken_PeerConnectionBroken\");\n            var b = new IpcProvider(name,\n                new IpcConfiguration()\n                {\n                    DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                        new IpcHandleRequestMessageResult(new IpcMessage(\"B回复\", bResponse)))\n                });\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n            var request1 = await peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n            Assert.AreEqual(true, bResponse.AsSpan().SequenceEqual(request1.Body.AsSpan()));\n            await Task.Yield();\n\n            var peerBrokenTask = new TaskCompletionSource<bool>();\n            peer.PeerConnectionBroken += delegate { peerBrokenTask.TrySetResult(true); };\n\n            b.Dispose();\n\n            // 预期 b 结束时，能收到 PeerConnectionBroken 事件\n            await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromSeconds(2)));\n\n            if (!peerBrokenTask.Task.IsCompleted)\n            {\n#if DEBUG\n                // 进入断点，也许上面的时间太短\n                await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromMinutes(5)));\n#endif\n            }\n\n            // 判断是否能收到对方断开的消息\n            Assert.AreEqual(true, peerBrokenTask.Task.IsCompleted);\n            Assert.AreEqual(true, peerBrokenTask.Task.Result);\n\n            Assert.AreEqual(true, peer.IsBroken);\n        }\n\n        [TestMethod(\"使用释放的服务发送消息，将会提示对象释放\")]\n        public async Task Dispose1()\n        {\n            var name = \"B_PeerConnectionBroken_Dispose\";\n\n            var aRequest = new byte[] { 0xF1 };\n            var a = new IpcProvider(\"A_PeerConnectionBroken_Dispose\", new IpcConfiguration() { AutoReconnectPeers = true });\n            var b = new IpcProvider(name);\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n\n            // 设置为自动重连的服务，释放\n            a.Dispose();\n\n            // 等待资源的释放\n            await Task.Delay(TimeSpan.FromSeconds(2));\n\n            await Assert.ThrowsExceptionAsync<ObjectDisposedException>(async () =>\n            {\n                await peer.NotifyAsync(new IpcMessage(\"A发送\", aRequest));\n            });\n        }\n\n        [TestMethod(\"设置为自动重连的服务，释放之后，不会有任何资源进入等待\")]\n        public async Task Dispose()\n        {\n            var name = \"B_PeerConnectionBroken_Dispose2\";\n\n            var a = new IpcProvider(\"A_PeerConnectionBroken_Dispose2\", new IpcConfiguration() { AutoReconnectPeers = true });\n            var b = new IpcProvider(name);\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n\n            // 设置为自动重连的服务，释放\n            a.Dispose();\n\n            // 等待资源的释放\n            await Task.WhenAny(peer.WaitForFinishedTaskCompletionSource.Task, Task.Delay(TimeSpan.FromSeconds(10)));\n\n            Assert.AreEqual(true, peer.IsBroken);\n            Assert.AreEqual(true, peer.WaitForFinishedTaskCompletionSource.Task.IsCompleted);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/PeerReConnectorTest.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Pipes.PipeConnectors;\nusing dotnetCampus.Ipc.Threading;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests\n{\n    [TestClass]\n    public class PeerReConnectorTest\n    {\n        [TestMethod(\"设置自动重新连接，但是重新连接器里面永远返回不继续连接。在对方结束之后，重新再开始，可以被重复连接\")]\n        public async Task IpcClientPipeConnectorTest()\n        {\n            // 先启动 a 和 b 两个\n            // 让 b 主动连接 a 然后聊聊天\n            // 接着将 b 结束，此时 a 的 peer 将会断开连接\n            // 然后启动 c 让 c 用原本 b 的 name 从而假装是 b 重新再开始\n            // 让 c 主动连接 a 然后聊聊天\n            // 预期可以让 a 获取到 c 的连接事件\n\n            var aName = \"A_PeerConnectorTest_02\";\n            var bName = \"B_PeerConnectorTest_02\";\n            var aResponse = new byte[] { 0xF1, 0xF3 };\n            var bRequest = new byte[] { 0xF1, 0xF2, 0xF3 };\n            var cRequest = new byte[] { 0x01, 0x05, 0xF3 };\n\n            var a = new IpcProvider(aName, new IpcConfiguration()\n            {\n                AutoReconnectPeers = true, // 设置自动重新连接\n                // 但是重新连接器里面永远返回不继续连接\n                IpcClientPipeConnector =\n                    new IpcClientPipeConnector(context => false,\n                        // 设置每一步都是快速超时\n                        stepTimeout: TimeSpan.FromMilliseconds(100)),\n                DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                    new IpcHandleRequestMessageResult(new IpcMessage(\"B回复\", aResponse))),\n                IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne,\n            });\n\n            var connectCount = 0;\n            var peerBrokenTask = new TaskCompletionSource<bool>();\n            a.PeerConnected += (sender, args) =>\n            {\n                connectCount++;\n\n                args.Peer.PeerConnectionBroken += (o, brokenArgs) =>\n                {\n                    peerBrokenTask.TrySetResult(true);\n                };\n            };\n\n            var b = new IpcProvider(bName, new IpcConfiguration()\n            {\n                AutoReconnectPeers = true, // 设置自动重新连接\n                // 但是重新连接器里面永远返回不继续连接\n                IpcClientPipeConnector = new IpcClientPipeConnector(context => false),\n                IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne,\n            });\n\n            a.StartServer();\n            b.StartServer();\n\n            // 让 b 主动连接 a 然后聊聊天\n            var peer1 = await b.GetOrCreatePeerProxyAsync(aName);\n            var request1 = await peer1.GetResponseAsync(new IpcMessage(\"Test\", bRequest));\n            Assert.AreEqual(true, aResponse.AsSpan().SequenceEqual(request1.Body.AsSpan()));\n            await Task.Yield();\n\n            // 能收到一次连接。这是预期的\n            Assert.AreEqual(1, connectCount);\n\n            var peerReconnectedCount = 0;\n            peer1.PeerReconnected += (sender, args) =>\n            {\n                peerReconnectedCount++;\n            };\n\n            // 将 b 结束，此时 a 的 peer 将会断开连接\n            b.Dispose();\n\n            // 预期 b 结束时，能收到 PeerConnectionBroken 事件\n            await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromSeconds(2)));\n            if (!peerBrokenTask.Task.IsCompleted)\n            {\n#if DEBUG\n                // 进入断点，也许上面的时间太短\n                await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromMinutes(5)));\n#endif\n            }\n            // 判断是否能收到对方断开的消息\n            Assert.AreEqual(true, peerBrokenTask.Task.IsCompleted);\n            Assert.AreEqual(true, peerBrokenTask.Task.Result);\n\n            // 等待重新连接失败\n            await Task.Delay(TimeSpan.FromSeconds(5));\n\n            // 确定 b 断开，再启动 c 去主动连接\n            var c = new IpcProvider(bName, new IpcConfiguration()\n            {\n                AutoReconnectPeers = true, // 设置自动重新连接\n                // 但是重新连接器里面永远返回不继续连接\n                IpcClientPipeConnector = new IpcClientPipeConnector(context => false),\n                IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne,\n            });\n            c.StartServer();\n\n            // 启动 c 去主动连接\n            var peer2 = await c.GetOrCreatePeerProxyAsync(aName);\n            // 发送一条请求获取响应，可以证明连接到符合预期的。同时也等待对方完成连接\n            var request2 = await peer2.GetResponseAsync(new IpcMessage(\"Test\", cRequest));\n            Assert.AreEqual(true, aResponse.AsSpan().SequenceEqual(request2.Body.AsSpan()));\n\n            // 可以被重复连接\n            // 也就是会被连接两次\n            // 不存在被重复连接\n            Assert.AreEqual(2, connectCount);\n            Assert.AreEqual(0, peerReconnectedCount);\n        }\n\n        [TestMethod(\"不自动重新连接，对方结束之后，重新再开始，可以被重复连接\")]\n        public async Task Connect()\n        {\n            // 先启动 a 和 b 两个\n            // 让 b 主动连接 a 然后聊聊天\n            // 接着将 b 结束，此时 a 的 peer 将会断开连接\n            // 然后启动 c 让 c 用原本 b 的 name 从而假装是 b 重新再开始\n            // 让 c 主动连接 a 然后聊聊天\n            // 预期可以让 a 获取到 c 的连接事件\n\n            var aName = \"A_PeerConnectorTest_01\";\n            var bName = \"B_PeerConnectorTest_01\";\n            var aResponse = new byte[] { 0xF1, 0xF3 };\n            var bRequest = new byte[] { 0xF1, 0xF2, 0xF3 };\n            var cRequest = new byte[] { 0x01, 0x05, 0xF3 };\n\n            var a = new IpcProvider(aName, new IpcConfiguration()\n            {\n                AutoReconnectPeers = false, // 不自动重新连接\n                DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                    new IpcHandleRequestMessageResult(new IpcMessage(\"B回复\", aResponse))),\n                IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne,\n            });\n\n            var connectCount = 0;\n            var peerBrokenTask = new TaskCompletionSource<bool>();\n            a.PeerConnected += (sender, args) =>\n            {\n                connectCount++;\n\n                args.Peer.PeerConnectionBroken += (o, brokenArgs) =>\n                {\n                    peerBrokenTask.TrySetResult(true);\n                };\n            };\n\n            var b = new IpcProvider(bName, new IpcConfiguration()\n            {\n                AutoReconnectPeers = false, // 不自动重新连接\n                IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne,\n            });\n\n            a.StartServer();\n            b.StartServer();\n\n            // 让 b 主动连接 a 然后聊聊天\n            var peer1 = await b.GetOrCreatePeerProxyAsync(aName);\n            var request1 = await peer1.GetResponseAsync(new IpcMessage(\"Test\", bRequest));\n            Assert.AreEqual(true, aResponse.AsSpan().SequenceEqual(request1.Body.AsSpan()));\n            await Task.Yield();\n\n            // 能收到一次连接。这是预期的\n            Assert.AreEqual(1, connectCount);\n\n            var peerReconnectedCount = 0;\n            peer1.PeerReconnected += (sender, args) =>\n            {\n                peerReconnectedCount++;\n            };\n\n            // 将 b 结束，此时 a 的 peer 将会断开连接\n            b.Dispose();\n\n            // 预期 b 结束时，能收到 PeerConnectionBroken 事件\n            await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromSeconds(2)));\n            if (!peerBrokenTask.Task.IsCompleted)\n            {\n#if DEBUG\n                // 进入断点，也许上面的时间太短\n                await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromMinutes(5)));\n#endif\n            }\n            // 判断是否能收到对方断开的消息\n            Assert.AreEqual(true, peerBrokenTask.Task.IsCompleted);\n            Assert.AreEqual(true, peerBrokenTask.Task.Result);\n\n            // 确定 b 断开，再启动 c 去主动连接\n            var c = new IpcProvider(bName, new IpcConfiguration()\n            {\n                AutoReconnectPeers = false, // 不自动重新连接\n                IpcTaskScheduling = IpcTaskScheduling.LocalOneByOne,\n            });\n            c.StartServer();\n\n            // 启动 c 去主动连接\n            var peer2 = await c.GetOrCreatePeerProxyAsync(aName);\n            // 发送一条请求获取响应，可以证明连接到符合预期的。同时也等待对方完成连接\n            var request2 = await peer2.GetResponseAsync(new IpcMessage(\"Test\", cRequest));\n            Assert.AreEqual(true, aResponse.AsSpan().SequenceEqual(request2.Body.AsSpan()));\n\n            // 可以被重复连接\n            // 也就是会被连接两次\n            // 不存在被重复连接\n            Assert.AreEqual(2, connectCount);\n            Assert.AreEqual(0, peerReconnectedCount);\n        }\n\n        [TestMethod(\"连接过程中，对方断掉，可以自动重新连接对方\")]\n        public async Task Reconnect()\n        {\n            // 让 a 去连接 b 然后聊聊天\n            // 接着将 b 结束，此时 a 的 peer 将会断开连接\n            // 然后启动 c 让 c 用原本 b 的 name 从而假装是 b 重启\n            // 预期是 a 会重新连接到 \"b\" 继续聊天\n            var name = \"B_PeerReConnectorTest\";\n            var aRequest = new byte[] { 0xF1 };\n            var bResponse = new byte[] { 0xF1, 0xF2 };\n            var cResponse = new byte[] { 0x01, 0x05 };\n\n            var a = new IpcProvider(\"A_PeerReConnectorTest\", new IpcConfiguration() { AutoReconnectPeers = true });\n            var b = new IpcProvider(name,\n                new IpcConfiguration()\n                {\n                    DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                        new IpcHandleRequestMessageResult(new IpcMessage(\"B回复\", bResponse)))\n                });\n\n            a.StartServer();\n            b.StartServer();\n\n            var peer = await a.GetAndConnectToPeerAsync(name);\n            var request1 = await peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n            Assert.AreEqual(true, bResponse.AsSpan().SequenceEqual(request1.Body.AsSpan()));\n            await Task.Yield();\n\n            var peerBrokenTask = new TaskCompletionSource<bool>();\n            peer.PeerConnectionBroken += delegate { peerBrokenTask.TrySetResult(true); };\n\n            b.Dispose();\n\n            // 预期 b 结束时，能收到 PeerConnectionBroken 事件\n            await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromSeconds(2)));\n\n            if (!peerBrokenTask.Task.IsCompleted)\n            {\n#if DEBUG\n                // 进入断点，也许上面的时间太短\n                await Task.WhenAny(peerBrokenTask.Task, Task.Delay(TimeSpan.FromMinutes(5)));\n#endif\n            }\n\n            // 判断是否能收到对方断开的消息\n            Assert.AreEqual(true, peerBrokenTask.Task.IsCompleted);\n            Assert.AreEqual(true, peerBrokenTask.Task.Result);\n\n            Assert.AreEqual(true, peer.IsBroken);\n            Assert.AreEqual(false, peer.WaitForFinishedTaskCompletionSource.Task.IsCompleted);\n\n            // 启动 c 用来假装 b 重启，能让 a 自动用原先的 Peer 连接\n            var c = new IpcProvider(name,\n                new IpcConfiguration()\n                {\n                    DefaultIpcRequestHandler = new DelegateIpcRequestHandler(context =>\n                        new IpcHandleRequestMessageResult(new IpcMessage(\"C回复\", cResponse)))\n                });\n            c.StartServer();\n\n            // 等待 a 重新连接\n            await Task.WhenAny(peer.WaitForFinishedTaskCompletionSource.Task, Task.Delay(TimeSpan.FromSeconds(2)));\n            if (!peer.WaitForFinishedTaskCompletionSource.Task.IsCompleted)\n            {\n#if DEBUG\n                // 进入断点，也许上面的时间太短\n                await Task.WhenAny(peer.WaitForFinishedTaskCompletionSource.Task, Task.Delay(TimeSpan.FromMinutes(2)));\n#endif\n            }\n\n            var request2 = await peer.GetResponseAsync(new IpcMessage(\"A发送\", aRequest));\n            Assert.AreEqual(true, cResponse.AsSpan().SequenceEqual(request2.Body.AsSpan()));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/PeerRegisterProviderTests.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Utils.Buffers;\nusing dotnetCampus.Ipc.Utils.IO;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests\n{\n    [TestClass]\n    public class PeerRegisterProviderTests\n    {\n        [TestMethod(\"如果注册消息的内容添加了其他内容，不会读取到不属于注册消息的内容\")]\n        public void BuildPeerRegisterMessage1()\n        {\n            // 创建的内容可以序列化\n            var peerRegisterProvider = new PeerRegisterProvider();\n            var pipeName = \"123\";\n            var bufferMessageContext = peerRegisterProvider.BuildPeerRegisterMessage(pipeName);\n            var memoryStream = new MemoryStream(bufferMessageContext.Length);\n\n            foreach (var ipcBufferMessage in bufferMessageContext.IpcBufferMessageList)\n            {\n                memoryStream.Write(ipcBufferMessage.Buffer, ipcBufferMessage.Start, ipcBufferMessage.Length);\n            }\n\n            // 写入其他内容\n            var streamWriter = new StreamWriter(memoryStream);\n            streamWriter.Write(\"林德熙是逗比\");\n            streamWriter.Flush();\n\n            memoryStream.Position = 0;\n\n            var success = peerRegisterProvider.TryParsePeerRegisterMessage(memoryStream, out var peerName);\n\n            Assert.AreEqual(true, success);\n            Assert.AreEqual(pipeName, peerName);\n        }\n\n        [TestMethod(\"如果消息不是对方的注册消息，那么将不修改Stream的起始\")]\n        public void BuildPeerRegisterMessage2()\n        {\n            var peerRegisterProvider = new PeerRegisterProvider();\n            var memoryStream = new MemoryStream();\n            for (int i = 0; i < 1000; i++)\n            {\n                memoryStream.WriteByte(0x00);\n            }\n\n            const int position = 10;\n            memoryStream.Position = position;\n            var isPeerRegisterMessage = peerRegisterProvider.TryParsePeerRegisterMessage(memoryStream, out _);\n            Assert.AreEqual(false, isPeerRegisterMessage);\n            Assert.AreEqual(position, memoryStream.Position);\n        }\n\n        [TestMethod(\"使用发送端之后，能序列化之前的字符串\")]\n        public async Task BuildPeerRegisterMessage3()\n        {\n            var peerRegisterProvider = new PeerRegisterProvider();\n            var pipeName = \"123\";\n            var bufferMessageContext = peerRegisterProvider.BuildPeerRegisterMessage(pipeName);\n            var memoryStream = new MemoryStream(bufferMessageContext.Length);\n            var ipcConfiguration = new IpcConfiguration();\n\n            await IpcMessageConverter.WriteAsync(memoryStream, ipcConfiguration.MessageHeader, ack: 10,\n                bufferMessageContext, new SharedArrayPool());\n\n            memoryStream.Position = 0;\n            var (success, ipcMessageContext) = (await IpcMessageConverter.ReadAsync(memoryStream,\n                ipcConfiguration.MessageHeader, new SharedArrayPool())).Result;\n\n            Assert.AreEqual(true, success);\n\n            var stream = new ByteListMessageStream(ipcMessageContext);\n            success = peerRegisterProvider.TryParsePeerRegisterMessage(stream, out var peerName);\n\n            Assert.AreEqual(true, success);\n\n            Assert.AreEqual(pipeName, peerName);\n        }\n\n        [TestMethod(\"创建的注册服务器名内容可以序列化，序列化之后可以反序列化出服务器名\")]\n        public void BuildPeerRegisterMessage4()\n        {\n            // 创建的内容可以序列化\n            var peerRegisterProvider = new PeerRegisterProvider();\n            var pipeName = \"123\";\n            var bufferMessageContext = peerRegisterProvider.BuildPeerRegisterMessage(pipeName);\n            var memoryStream = new MemoryStream(bufferMessageContext.Length);\n\n            foreach (var ipcBufferMessage in bufferMessageContext.IpcBufferMessageList)\n            {\n                memoryStream.Write(ipcBufferMessage.Buffer, ipcBufferMessage.Start, ipcBufferMessage.Length);\n            }\n\n            memoryStream.Position = 0;\n\n            var success = peerRegisterProvider.TryParsePeerRegisterMessage(memoryStream, out var peerName);\n\n            Assert.AreEqual(true, success);\n            Assert.AreEqual(pipeName, peerName);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/Pipes/PipeConnectors/IpcClientPipeConnectorTest.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Exceptions;\nusing dotnetCampus.Ipc.Pipes;\nusing dotnetCampus.Ipc.Pipes.PipeConnectors;\nusing dotnetCampus.Threading;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.Pipes.PipeConnectors;\n\n[TestClass]\npublic class IpcClientPipeConnectorTest\n{\n    [TestMethod(\"重连接时，调用 CanContinue 方法返回不支持再次重新连接，则不再次重新连接\")]\n    public async Task ReConnectBreak()\n    {\n        int callCanContinueCount = 0;\n        var asyncAutoResetEvent = new AsyncAutoResetEvent(false);\n        var ipcConfiguration = new IpcConfiguration()\n        {\n            AutoReconnectPeers = true,\n            IpcClientPipeConnector = new IpcClientPipeConnector(c =>\n            {\n                // 调用 CanContinue 方法返回不支持\n                callCanContinueCount++;\n                asyncAutoResetEvent.Set();\n                return false;\n            }, stepTimeout: TimeSpan.FromMilliseconds(100)),\n        };\n\n        var ipcProviderA = new IpcProvider(Guid.NewGuid().ToString(\"N\"), ipcConfiguration);\n        var ipcProviderB = new IpcProvider();\n        ipcProviderA.StartServer();\n        ipcProviderB.StartServer();\n\n        var peer = await ipcProviderA.GetAndConnectToPeerAsync(ipcProviderB.IpcContext.PipeName);\n        Assert.AreEqual(0, callCanContinueCount);\n        Assert.IsNotNull(peer);\n\n        // 断开，预期此时将会重新连接\n        ipcProviderB.Dispose();\n\n        await asyncAutoResetEvent.WaitOneAsync();\n        await Task.Delay(TimeSpan.FromSeconds(1));\n        Assert.AreEqual(true, peer.IsBroken);\n        Assert.AreEqual(1, callCanContinueCount);\n    }\n\n    [TestMethod(\"连接一个不存在的服务，会调用到 CanContinue 方法判断是否可以再次重新连接。如 CanContinue 返回不能继续连接，将抛出连接失败异常\")]\n    public async Task ConnectNamedPipeAsync1()\n    {\n        int callCanContinueCount = 0;\n        var ipcConfiguration = new IpcConfiguration()\n        {\n            IpcClientPipeConnector = new IpcClientPipeConnector(c =>\n            {\n                callCanContinueCount++;\n                // 第一次返回可以继续连接，预期进来第二次\n                return callCanContinueCount == 1;\n            }, stepTimeout: TimeSpan.FromMilliseconds(100)),\n        };\n\n        var ipcProviderA = new IpcProvider(Guid.NewGuid().ToString(\"N\"), ipcConfiguration);\n        ipcProviderA.StartServer();\n\n        await Assert.ThrowsExceptionAsync<IpcClientPipeConnectionException>(async () =>\n        {\n            // 连接一个不存在的服务\n            var peer = await ipcProviderA.GetAndConnectToPeerAsync(\"NotExists_\" + Guid.NewGuid().ToString(\"N\"));\n            Assert.IsNull(peer);\n        });\n\n        // 调用两次，第一次返回可以继续连接\n        Assert.AreEqual(2, callCanContinueCount);\n    }\n\n    [TestMethod(\"连接能立刻连上的服务，不会调用到 CanContinue 方法判断是否可以再次重新连接\")]\n    public async Task ConnectNamedPipeAsync2()\n    {\n        int callCanContinueCount = 0;\n        var ipcConfiguration = new IpcConfiguration()\n        {\n            IpcClientPipeConnector = new IpcClientPipeConnector(c =>\n            {\n                callCanContinueCount++;\n                return true;\n            }),\n        };\n\n        var ipcProviderA = new IpcProvider(Guid.NewGuid().ToString(\"N\"), ipcConfiguration);\n        var ipcProviderB = new IpcProvider();\n        ipcProviderA.StartServer();\n        ipcProviderB.StartServer();\n\n        var peer = await ipcProviderA.GetAndConnectToPeerAsync(ipcProviderB.IpcContext.PipeName);\n\n        // 不会调用到 CanContinue 方法判断是否可以再次重新连接\n        Assert.AreEqual(0, callCanContinueCount);\n        Assert.IsNotNull(peer);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/Properties/Compatibility.cs",
    "content": "﻿global using dotnetCampus.Ipc.Tests.Properties;\n\nnamespace dotnetCampus.Ipc.Tests.Properties;\n\npublic static class Compatibility\n{\n#if !NET5_0_OR_GREATER\n    public static async Task<T> WaitAsync<T>(this Task<T> task, TimeSpan timeout)\n    {\n        var timeoutTask = Task.Delay(timeout);\n        var completedTask = await Task.WhenAny(task, timeoutTask);\n        if (completedTask == timeoutTask)\n        {\n            throw new TimeoutException(\"The operation has timed out.\");\n        }\n\n        return await task;\n    }\n#endif\n}\n\n#if !NET5_0_OR_GREATER\npublic sealed class TaskCompletionSource\n{\n    private readonly TaskCompletionSource<bool> _tcs = new();\n\n    public Task Task => _tcs.Task;\n\n    public void SetResult() => _tcs.SetResult(true);\n\n    public void TrySetResult() => _tcs.TrySetResult(true);\n}\n#endif\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/ResponseManagerTests.cs",
    "content": "﻿using dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests\n{\n    [TestClass]\n    public class ResponseManagerTests\n    {\n        [TestMethod(\"发送消息到另一个 IPC 服务，可以等待收到对方的返回值\")]\n        public async Task SendAndGetResponse()\n        {\n            var ipcAName = Guid.NewGuid().ToString(\"N\");\n            var ipcBName = Guid.NewGuid().ToString(\"N\");\n            var requestByteList = new byte[] { 0xFF, 0xFE };\n            var responseByteList = new byte[] { 0xF1, 0xF2 };\n\n            using var ipcA = new IpcProvider(ipcAName);\n            using var ipcB = new IpcProvider(ipcBName, new IpcConfiguration()\n            {\n                DefaultIpcRequestHandler = new DelegateIpcRequestHandler(c =>\n                {\n                    Assert.AreEqual(ipcAName, c.Peer.PeerName);\n                    c.Handled = true;\n                    var span = c.IpcBufferMessage.Body.AsSpan();\n                    Assert.AreEqual(true, span.SequenceEqual(requestByteList));\n\n                    return new IpcHandleRequestMessageResult(new IpcMessage(\"Return\",\n                        new IpcMessageBody(responseByteList)));\n                })\n            });\n            ipcA.StartServer();\n            ipcB.StartServer();\n\n            var bPeer = await ipcA.GetAndConnectToPeerAsync(ipcBName);\n            // 从 A 发送消息给到 B 然后可以收到从 B 返回的消息\n            var response =\n                await bPeer.GetResponseAsync(new IpcMessage(\"发送\", new IpcMessageBody(requestByteList)));\n            Assert.AreEqual(true, response.Body.AsSpan().SequenceEqual(responseByteList));\n        }\n\n        [TestMethod(\"发送消息之后，能等待收到对应的回复\")]\n        public async Task GetResponseAsync()\n        {\n            var ipcMessageRequestManager = new IpcMessageRequestManager(new IpcProvider().IpcContext);\n            var requestByteList = new byte[] { 0xFF, 0xFE };\n            var request = new IpcMessage(\"Tests\", new IpcMessageBody(requestByteList));\n            var ipcClientRequestMessage = ipcMessageRequestManager.CreateRequestMessage(request);\n            Assert.AreEqual(false, ipcClientRequestMessage.Task.IsCompleted);\n\n            var requestStream = IpcBufferMessageContextToStream(ipcClientRequestMessage.IpcBufferMessageContext);\n\n            IpcClientRequestArgs ipcClientRequestArgs = null;\n            ipcMessageRequestManager.OnIpcClientRequestReceived += (sender, args) =>\n            {\n                ipcClientRequestArgs = args;\n            };\n\n            Assert.IsNotNull(requestStream);\n            ipcMessageRequestManager.OnReceiveMessage(new PeerStreamMessageArgs(new IpcMessageContext(), \"Foo\", requestStream, ack: 100,\n                IpcMessageCommandType.RequestMessage));\n\n            Assert.IsNotNull(ipcClientRequestArgs);\n            var responseByteList = new byte[] { 0xF1, 0xF2 };\n            var ipcMessageResponseManager = new IpcMessageResponseManager();\n            var responseMessageContext = ipcMessageResponseManager.CreateResponseMessage(\n                ipcClientRequestArgs.MessageId,\n                new IpcMessage(\"Tests\", new IpcMessageBody(responseByteList)));\n            var responseStream = IpcBufferMessageContextToStream(responseMessageContext);\n            ipcMessageRequestManager.OnReceiveMessage(new PeerStreamMessageArgs(new IpcMessageContext(), \"Foo\", responseStream, ack: 100,\n                IpcMessageCommandType.ResponseMessage));\n\n            // 在 OnReceiveMessage 收到消息，不是立刻释放 ipcClientRequestMessage 的，需要调度到线程池进行释放\n            await ipcClientRequestMessage.Task.WaitTimeout(TimeSpan.FromSeconds(5));\n            Assert.AreEqual(true, ipcClientRequestMessage.Task.IsCompleted);\n        }\n\n        [TestMethod(\"所有发送消息都收到回复后，将清空等待响应的数量\")]\n        public async Task WaitingResponseCount()\n        {\n            // 请求的顺序是\n            // A: 生成请求消息\n            // A: 发送请求消息\n            // B: 收到请求消息\n            // B: 生成回复消息\n            // B: 发送回复消息\n            // A: 收到回复消息\n            // A: 完成请求\n            var aIpcMessageRequestManager = new IpcMessageRequestManager(new IpcProvider().IpcContext);\n            var requestByteList = new byte[] { 0xFF, 0xFE };\n            var request = new IpcMessage(\"Tests\", new IpcMessageBody(requestByteList));\n\n            var ipcClientRequestMessageList = new List<IpcClientRequestMessage>();\n\n            for (int i = 0; i < 10; i++)\n            {\n                // 创建请求消息\n                IpcClientRequestMessage ipcClientRequestMessage = aIpcMessageRequestManager.CreateRequestMessage(request);\n                ipcClientRequestMessageList.Add(ipcClientRequestMessage);\n\n                Assert.AreEqual(i + 1, aIpcMessageRequestManager.WaitingResponseCount);\n            }\n\n            // 创建的请求消息还没发送出去，需要进行发送\n            // 发送的做法就是往 B 里面调用接收方法\n            // 在测试里面不引入 IPC 的发送逻辑，因此 A 的发送就是调用 B 的接收\n            var bIpcMessageRequestManager = new IpcMessageRequestManager(new IpcProvider().IpcContext);\n            var bIpcMessageResponseManager = new IpcMessageResponseManager();\n\n            // 接收 B 的消息，用的是事件\n            var ipcClientRequestArgsList = new List<IpcClientRequestArgs>();\n            bIpcMessageRequestManager.OnIpcClientRequestReceived += (sender, args) =>\n            {\n                ipcClientRequestArgsList.Add(args);\n            };\n\n            // 开始发送消息\n            foreach (var ipcClientRequestMessage in ipcClientRequestMessageList)\n            {\n                var requestStream = IpcBufferMessageContextToStream(ipcClientRequestMessage.IpcBufferMessageContext);\n                var args = new PeerStreamMessageArgs(new IpcMessageContext(), \"Foo\", requestStream, ack: 100,\n                    IpcMessageCommandType.RequestMessage);\n\n                bIpcMessageRequestManager.OnReceiveMessage(args);\n            }\n\n            // 因为 A 发送了 10 条消息，因此 B 需要接收到 10 条\n            Assert.AreEqual(ipcClientRequestMessageList.Count, ipcClientRequestArgsList.Count);\n\n            // 逐条消息回复\n            foreach (var ipcClientRequestArgs in ipcClientRequestArgsList)\n            {\n                var responseByteList = new byte[] { 0xF1, 0xF2 };\n                var responseMessageContext = bIpcMessageResponseManager.CreateResponseMessage(\n                    ipcClientRequestArgs.MessageId,\n                    new IpcMessage(\"Tests\", new IpcMessageBody(responseByteList)));\n                var responseStream = IpcBufferMessageContextToStream(responseMessageContext);\n\n                // 回复就是发送消息给 A 相当于让 A 接收消息\n                aIpcMessageRequestManager.OnReceiveMessage(new PeerStreamMessageArgs(new IpcMessageContext(), \"Foo\", responseStream, ack: 100,\n                    IpcMessageCommandType.ResponseMessage));\n            }\n\n            // 此时 A 没有等待回复的消息\n            Assert.AreEqual(0, aIpcMessageRequestManager.WaitingResponseCount);\n            // 所有发送的消息都收到回复\n\n            foreach (var ipcClientRequestMessage in ipcClientRequestMessageList)\n            {\n                // 在 OnReceiveMessage 收到消息，不是立刻释放 ipcClientRequestMessage 的，需要调度到线程池进行释放\n                await ipcClientRequestMessage.Task.WaitTimeout();\n\n                Assert.AreEqual(true, ipcClientRequestMessage.Task.IsCompleted);\n            }\n        }\n\n        private static MemoryStream IpcBufferMessageContextToStream(IpcBufferMessageContext ipcBufferMessageContext)\n        {\n            var stream = new MemoryStream();\n            foreach (var ipcBufferMessage in ipcBufferMessageContext.IpcBufferMessageList)\n            {\n                stream.Write(ipcBufferMessage.Buffer, ipcBufferMessage.Start, ipcBufferMessage.Length);\n            }\n\n            stream.Position = 0;\n            return stream;\n        }\n\n        class FakeClientMessageWriter : IClientMessageWriter\n        {\n            public Task WriteMessageAsync(byte[] buffer, int offset, int count, string summary = null)\n            {\n                throw new System.NotImplementedException();\n            }\n\n            public Stream Stream { set; get; }\n\n            public Task WriteMessageAsync(in IpcBufferMessageContext ipcBufferMessageContext)\n            {\n                Stream = IpcBufferMessageContextToStream(ipcBufferMessageContext);\n\n                return Task.CompletedTask;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/TaskExtension.cs",
    "content": "﻿namespace dotnetCampus.Ipc.Tests\n{\n    static class TaskExtension\n    {\n        public static async Task WaitTimeout(this Task task, TimeSpan? timeout = null)\n        {\n            timeout ??= TimeSpan.FromSeconds(2);\n            await Task.WhenAny(task, Task.Delay(timeout.Value));\n\n#if DEBUG\n            // 进入断点，也许上面的时间太短\n            for (int i = 0; i < 100 && !task.IsCompleted; i++)\n            {\n                await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(1)));\n            }\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/TestJsonContext.cs",
    "content": "﻿using System.Reflection;\nusing System.Text.Json.Serialization;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Tests.CompilerServices;\nusing dotnetCampus.Ipc.Tests.IpcRouteds.DirectRouteds;\n\nnamespace dotnetCampus.Ipc.Tests;\n\n#if NET8_0_OR_GREATER\n\n[JsonSerializable(typeof(BindingFlags))]\n[JsonSerializable(typeof(List<string>))]\n[JsonSerializable(typeof(IList<string>))]\n[JsonSerializable(typeof(string[]))]\n[JsonSerializable(typeof(FakeIpcObjectSubModelA))]\n[JsonSerializable(typeof(IFakeIpcObject.NestedEnum))]\n[JsonSerializable(typeof((double a, uint b, int? c, byte d)))]\n[JsonSerializable(typeof(JsonIpcDirectRoutedProviderTest.FakeArgument))]\n[JsonSerializable(typeof(JsonIpcDirectRoutedProviderTest.FakeResult))]\n[JsonSourceGenerationOptions(\n    PropertyNameCaseInsensitive = false,\n    IncludeFields = true,\n    UseStringEnumConverter = true)]\ninternal partial class TestJsonContext : JsonSerializerContext\n{\n    public static IpcConfiguration CreateIpcConfiguration() => new IpcConfiguration()\n        .UseSystemTextJsonIpcObjectSerializer(Default);\n}\n\n#else\n\ninternal static class TestJsonContext\n{\n    public static IpcConfiguration CreateIpcConfiguration() => new IpcConfiguration()\n        .UseNewtonsoftJsonIpcObjectSerializer(null);\n}\n\n#endif\n\ninternal static class IpcConfigurationExtensions\n{\n    public static IpcConfiguration UseTestFrameworkJsonSerializer(this IpcConfiguration ipcConfiguration)\n    {\n#if NET8_0_OR_GREATER\n        // 在 .NET 8.0 及更高版本中，测试 System.Text.Json 作为底层 IPC 传输机制。\n        return ipcConfiguration.UseSystemTextJsonIpcObjectSerializer(TestJsonContext.Default);\n#else\n        // 在旧版本的 .NET 中，测试 Newtonsoft.Json 作为底层 IPC 传输机制。\n        return ipcConfiguration.UseNewtonsoftJsonIpcObjectSerializer(null);\n#endif\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/TestLogger.cs",
    "content": "﻿using dotnetCampus.Ipc.Utils.Logging;\n\nnamespace dotnetCampus.Ipc.Tests;\n\nclass TestLogger : IpcLogger\n{\n    public TestLogger() : base(nameof(TestLogger))\n    {\n    }\n\n    protected override bool IsEnabled(LogLevel logLevel)\n    {\n        return true; // 这里为了测试，全部都开启\n    }\n\n    protected override void Log<TState>(LogLevel logLevel, TState state, Exception? exception, Func<TState, Exception?, string> formatter)\n    {\n        lock (LogMessage)\n        {\n            LogMessage.Add($\"[{DateTime.Now:HH:mm:ss,fff}] [IPC][{logLevel}]{formatter(state, exception)}\");\n        }\n    }\n\n    public List<string> LogMessage { get; } = [];\n\n    public string GetAllLogMessage()\n    {\n        lock (LogMessage)\n        {\n            return string.Join(\"\\n\", LogMessage);\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/Threading/IpcThreadPoolTests.cs",
    "content": "﻿using System.Text;\nusing dotnetCampus.Ipc.Context;\nusing dotnetCampus.Ipc.Internals;\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Pipes;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.Threading;\n\n[TestClass]\npublic class IpcThreadPoolTests : IIpcRequestHandler\n{\n    [TestMethod(\"线程池满，等待新任务调度时，本递归调用不应出现 StackOverflowException。\")]\n    public async Task RunRecursively()\n    {\n        var aName = $\"IpcObjectTests.IpcTests.IpcThreadPoolTests.{nameof(RunRecursively)}.A\";\n        var bName = $\"IpcObjectTests.IpcTests.IpcThreadPoolTests.{nameof(RunRecursively)}.B\";\n        var aProvider = new IpcProvider(aName, new IpcConfiguration { DefaultIpcRequestHandler = this, });\n        var bProvider = new IpcProvider(bName);\n        aProvider.StartServer();\n        bProvider.StartServer();\n        var aPeer = await bProvider.GetAndConnectToPeerAsync(aName);\n\n        // 占满线程池，以便有机会等待新任务调度。\n        var task1 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n        var task2 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n        var task3 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n        var task4 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n        var task5 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n        var task6 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n        var task7 = aPeer.GetResponseAsync(new IpcMessage(\"Test\", Encoding.UTF8.GetBytes(\"Test\")));\n\n        await Task.WhenAll(task1, task2, task3, task4, task5);\n    }\n\n    public Task<IIpcResponseMessage> HandleRequest(IIpcRequestContext requestContext)\n    {\n        Thread.Sleep(100);\n        return Task.FromResult<IIpcResponseMessage>(\n            new IpcHandleRequestMessageResult(\n                new IpcMessage(\n                    \"TestR\",\n                    Encoding.UTF8.GetBytes(\"TestR\")\n                )\n            )\n        );\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/Utils/Extensions/PeerMessageArgsExtensionTest.cs",
    "content": "﻿using System.Text;\n\nusing dotnetCampus.Ipc.Messages;\nusing dotnetCampus.Ipc.Utils.Extensions;\n\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.Utils.Extensions;\n\n[TestClass]\npublic class PeerMessageArgsExtensionTest\n{\n    [TestMethod(\"给定的 IpcMessage 不包含正确 byte 数组的有效负载，获取有效负载失败\")]\n    public void GetPayload1()\n    {\n        var header = new byte[] { 0xF1, 0xC1, 0xAA, (byte) 'a' };\n        var text = \"abc\";\n        using var memoryStream = new MemoryStream();\n        using (var binaryWriter = new BinaryWriter(memoryStream))\n        {\n            binaryWriter.Write(header);\n\n            // 再写入一些测试数据\n            var textByteList = Encoding.UTF8.GetBytes(text);\n            binaryWriter.Write(textByteList);\n        }\n\n        var ipcMessage = new IpcMessage(\"test\", new IpcMessageBody(memoryStream.ToArray()));\n\n        // 要求的负载和传入的不相同\n        var result = ipcMessage.TryGetPayload(new byte[] { 0xF1, 0xC1, 0xAA, (byte) 'b' }, out var subMessage);\n\n        // 可以成功获取到有效负载\n        Assert.AreEqual(false, result);\n    }\n\n    [TestMethod(\"给定的 IpcMessage 存在偏移，但包含正确 byte 数组的有效负载，可以成功获取到有效负载\")]\n    public void GetPayload2()\n    {\n        var header = new byte[] { 0xF1, 0xC1, 0xAA, (byte) 'a' };\n        var text = \"abc\";\n        using var memoryStream = new MemoryStream();\n        using (var binaryWriter = new BinaryWriter(memoryStream))\n        {\n            // 写入一个 byte 垃圾，用于后续测试存在偏移\n            binaryWriter.Write((byte) 0);\n\n            binaryWriter.Write(header);\n\n            // 再写入一些测试数据\n            var textByteList = Encoding.UTF8.GetBytes(text);\n            binaryWriter.Write(textByteList);\n        }\n        // sizeof(header) + sizeof(text) = 4 + 3 = 7\n        var ipcMessage = new IpcMessage(\"test\", new IpcMessageBody(memoryStream.ToArray(), 1, 7));\n\n        var result = ipcMessage.TryGetPayload(header, out var subMessage);\n\n        // 可以成功获取到有效负载\n        Assert.AreEqual(true, result, \"没有成功获取到有效负载\");\n\n        var resultText = Encoding.UTF8.GetString(subMessage.Body.Buffer, subMessage.Body.Start, subMessage.Body.Length);\n        Assert.AreEqual(text, resultText);\n    }\n\n    [TestMethod(\"给定 IpcMessage 包含正确 byte 数组的有效负载，可以成功获取到有效负载\")]\n    public void GetPayload3()\n    {\n        var header = new byte[] { 0xF1, 0xC1, 0xAA, (byte) 'a' };\n        var text = \"abc\";\n        using var memoryStream = new MemoryStream();\n        using (var binaryWriter = new BinaryWriter(memoryStream))\n        {\n            binaryWriter.Write(header);\n\n            // 再写入一些测试数据\n            var textByteList = Encoding.UTF8.GetBytes(text);\n            binaryWriter.Write(textByteList);\n        }\n\n        var ipcMessage = new IpcMessage(\"test\", new IpcMessageBody(memoryStream.ToArray()));\n\n        var result = ipcMessage.TryGetPayload(header, out var subMessage);\n\n        // 可以成功获取到有效负载\n        Assert.AreEqual(true, result, \"没有成功获取到有效负载\");\n\n        var resultText = Encoding.UTF8.GetString(subMessage.Body.Buffer, subMessage.Body.Start, subMessage.Body.Length);\n        Assert.AreEqual(text, resultText);\n    }\n\n    [TestMethod(\"给定的 IpcMessage 不包含正确 ulong 的有效负载，获取有效负载失败\")]\n    public void GetPayload4()\n    {\n        ulong header = 0x12312;\n        var text = \"abc\";\n        using var memoryStream = new MemoryStream();\n        using (var binaryWriter = new BinaryWriter(memoryStream))\n        {\n            binaryWriter.Write(header);\n\n            // 再写入一些测试数据\n            var textByteList = Encoding.UTF8.GetBytes(text);\n            binaryWriter.Write(textByteList);\n        }\n\n        var ipcMessage = new IpcMessage(\"test\", new IpcMessageBody(memoryStream.ToArray()));\n\n        // 要求的负载和传入的不相同\n        var result = ipcMessage.TryGetPayload(0xFF, out var subMessage);\n\n        // 可以成功获取到有效负载\n        Assert.AreEqual(false, result);\n    }\n\n    [TestMethod(\"给定的 IpcMessage 存在偏移，但包含正确 ulong 的有效负载，可以成功获取到有效负载\")]\n    public void GetPayload5()\n    {\n        ulong header = 0x12312;\n        var text = \"abc\";\n        using var memoryStream = new MemoryStream();\n        using (var binaryWriter = new BinaryWriter(memoryStream))\n        {\n            // 写入一个 byte 垃圾，用于后续测试存在偏移\n            binaryWriter.Write((byte) 0);\n\n            binaryWriter.Write(header);\n\n            // 再写入一些测试数据\n            var textByteList = Encoding.UTF8.GetBytes(text);\n            binaryWriter.Write(textByteList);\n        }\n        // sizeof(ulong) + sizeof(text) = 8 + 3 = 11\n        var ipcMessage = new IpcMessage(\"test\", new IpcMessageBody(memoryStream.ToArray(), 1, 11));\n\n        var result = ipcMessage.TryGetPayload(header, out var subMessage);\n\n        // 可以成功获取到有效负载\n        Assert.AreEqual(true, result, \"没有成功获取到有效负载\");\n\n        var resultText = Encoding.UTF8.GetString(subMessage.Body.Buffer, subMessage.Body.Start, subMessage.Body.Length);\n        Assert.AreEqual(text, resultText);\n    }\n\n    [TestMethod(\"给定 IpcMessage 包含正确 ulong 的有效负载，可以成功获取到有效负载\")]\n    public void GetPayload6()\n    {\n        ulong header = 0x12312;\n        var text = \"abc\";\n        using var memoryStream = new MemoryStream();\n        using (var binaryWriter = new BinaryWriter(memoryStream))\n        {\n            binaryWriter.Write(header);\n\n            // 再写入一些测试数据\n            var textByteList = Encoding.UTF8.GetBytes(text);\n            binaryWriter.Write(textByteList);\n        }\n\n        var ipcMessage = new IpcMessage(\"test\", new IpcMessageBody(memoryStream.ToArray()));\n\n        var result = ipcMessage.TryGetPayload(header, out var subMessage);\n\n        // 可以成功获取到有效负载\n        Assert.AreEqual(true, result, \"没有成功获取到有效负载\");\n\n        var resultText = Encoding.UTF8.GetString(subMessage.Body.Buffer, subMessage.Body.Start, subMessage.Body.Length);\n        Assert.AreEqual(text, resultText);\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/Utils/IO/AsyncBinaryReaderTests.cs",
    "content": "﻿using dotnetCampus.Ipc.Utils.Buffers;\nusing dotnetCampus.Ipc.Utils.IO;\nusing Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace dotnetCampus.Ipc.Tests.Utils.IO;\n\n[TestClass]\npublic class AsyncBinaryReaderTests\n{\n    [TestMethod(\"给定的共享数组远远超过所需长度，可以成功读取正确的数值\")]\n    public async Task AsyncBinaryReaderTest()\n    {\n        var memoryStream = new MemoryStream();\n        var streamWriter = new BinaryWriter(memoryStream);\n        ushort n1 = 15;\n        streamWriter.Write(n1);\n        ulong n2 = 65521;\n        streamWriter.Write(n2);\n        uint n3 = 0;\n        streamWriter.Write(n3);\n        streamWriter.Flush();\n        memoryStream.Position = 0;\n\n        var asyncBinaryReader = new AsyncBinaryReader(memoryStream, new FakeSharedArrayPool());\n        var r1 = await asyncBinaryReader.ReadUInt16Async();\n        Assert.AreEqual(n1, r1.Result);\n        var r2 = await asyncBinaryReader.ReadReadUInt64Async();\n        Assert.AreEqual(n2, r2.Result);\n        var r3 = await asyncBinaryReader.ReadUInt32Async();\n        Assert.AreEqual(n3, r3.Result);\n    }\n\n    class FakeSharedArrayPool : ISharedArrayPool\n    {\n        public byte[] Rent(int minLength)\n        {\n            return new byte[1024];\n        }\n\n        public void Return(byte[] array)\n        {\n        }\n    }\n}\n"
  },
  {
    "path": "tests/dotnetCampus.Ipc.Tests/dotnetCampus.Ipc.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <!--\n      使用两个框架：\n      1. 在 net8.0 下我们将使用 System.Text.Json 作为 IPC 的底层传输机制；\n      2. 在 net6.0 下我们将使用 Newtonsoft.Json 作为 IPC 的底层传输机制；\n      3. 可以分别测试这两种机制单独是否能正常工作。\n      4. 额外配合 dotnetCampus.Ipc.FakeTests 测试项目，可以测试两个机制之间的兼容性。\n      5. netcoreapp3.1 用于测试利用反射（而不是模块初始化器）注册 IPC 服务的功能。\n    -->\n    <TargetFrameworks>net9.0;net6.0;netcoreapp3.1</TargetFrameworks>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" />\n    <PackageReference Include=\"MSTest.TestAdapter\" />\n    <PackageReference Include=\"MSTest.TestFramework\" />\n    <PackageReference Include=\"coverlet.collector\">\n      <PrivateAssets>all</PrivateAssets>\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n    </PackageReference>\n    <PackageReference Include=\"Moq\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc.Analyzers\\dotnetCampus.Ipc.Analyzers.csproj\" OutputItemType=\"Analyzer\" ReferenceOutputAssembly=\"false\" />\n    <ProjectReference Include=\"..\\..\\src\\dotnetCampus.Ipc\\dotnetCampus.Ipc.csproj\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Include=\"..\\dotnetCampus.Ipc.FakeTests\\FakeApis\\**\\*.cs\" Link=\"CompilerServices\\FakeRemote\\%(RecursiveDir)\\%(Filename)%(Extension)\" />\n  </ItemGroup>\n\n</Project>\n"
  }
]