ใช้การอ้างอิง 32/64 บิตตามเงื่อนไขเมื่อสร้างใน Visual Studio


124

ฉันมีโปรเจ็กต์ที่สร้างแบบ 32/64-bit และมีการอ้างอิง 32/64-bit ที่สอดคล้องกัน ฉันต้องการเปลี่ยนการกำหนดค่าและใช้การอ้างอิงที่ถูกต้อง แต่ฉันไม่รู้ว่าจะบอกให้ Visual Studio ใช้การอ้างอิงที่เหมาะสมกับสถาปัตยกรรมได้อย่างไร

บางทีฉันอาจจะเข้าใจผิด แต่ฉันต้องการสลับระหว่าง x86 และ x64 ในเมนูแบบเลื่อนลงการกำหนดค่าและให้ DLL อ้างอิงเป็นบิตเนสที่ถูกต้อง


ไม่ชัดเจนมากนี่คือภาษาอะไร? โครงการ DLL อยู่ในโซลูชันหรือไม่
Hans Passant

ขออภัยนี่คือ. NET ฉันเขียนด้วยภาษา C #
Jonathan Yee

4
ตกลงฉันแก้ไขด้วยวิธีการโง่ ๆ : สร้างไฟล์ csproj เพิ่มเติมที่อ้างอิงเฉพาะ x64 DLL (และลบการกำหนดค่า x86 ออกจาก csproj) มันใช้งานได้ แต่ถ้าใครมีวิธีแก้ปัญหาที่หรูหรากว่านี้ซึ่งไม่ได้เกี่ยวข้องกับ csproj เพิ่มเติมฉันก็อยากเห็นมัน
Jonathan Yee

คำตอบ:


99

นี่คือสิ่งที่ฉันได้ทำในโครงการก่อนหน้านี้ซึ่งจะต้องใช้ไฟล์. csproj รุ่นคู่มือ นอกจากนี้คุณยังต้องมีไดเร็กทอรีแยกต่างหากสำหรับไบนารีที่แตกต่างกันโดยเป็นพี่น้องกันและมีชื่อเดียวกันกับแพลตฟอร์มที่คุณกำหนดเป้าหมาย

หลังจากเพิ่มการอ้างอิงของแพลตฟอร์มเดียวในโปรเจ็กต์แล้วให้เปิด. csproj ในโปรแกรมแก้ไขข้อความ ก่อน<ItemGroup>องค์ประกอบแรกภายใน<Project>องค์ประกอบให้เพิ่มรหัสต่อไปนี้ซึ่งจะช่วยระบุว่าคุณกำลังใช้งาน (และสร้าง) บนแพลตฟอร์มใด

<!-- Properties group for Determining 64bit Architecture -->
<PropertyGroup>
  <CurrentPlatform>x86</CurrentPlatform>
  <CurrentPlatform Condition="'$(PROCESSOR_ARCHITECTURE)'=='AMD64' or '$(PROCESSOR_ARCHITEW6432)'=='AMD64'">AMD64</CurrentPlatform>
</PropertyGroup>

จากนั้นสำหรับการอ้างอิงเฉพาะแพลตฟอร์มของคุณคุณทำการเปลี่ยนแปลงดังต่อไปนี้:

<ItemGroup>
  <Reference Include="Leadtools, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.dll</HintPath>
  </Reference>
  <Reference Include="Leadtools.Codecs, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.Codecs.dll</HintPath>
  </Reference>
  <Reference Include="Leadtools.ImageProcessing.Core, Version=16.5.0.0, Culture=neutral, PublicKeyToken=9cf889f53ea9b907, processorArchitecture=x86">
    <SpecificVersion>False</SpecificVersion>
    <HintPath>..\..\Lib\Leadtools\$(CurrentPlatform)\Leadtools.ImageProcessing.Core.dll</HintPath>
  </Reference>
  <Reference Include="System" />
  <Reference Include="System.Core" />
  <Reference Include="System.Data.Entity" />
  <!--  Other project references -->
</ItemGroup>

สังเกตการใช้$(CurrentPlatform)คุณสมบัติที่เรากำหนดไว้ข้างต้น คุณสามารถใช้เงื่อนไขสำหรับการประกอบที่จะรวมสำหรับแพลตฟอร์มใดแทนได้ คุณอาจต้อง:

  • แทนที่$(PROCESSOR_ARCHITEW6432)และ$(PROCESSOR_ARCHITECTURE)ด้วย$(Platform)เพื่อพิจารณาเฉพาะแพลตฟอร์มเป้าหมายของโครงการ
  • ปรับเปลี่ยนตรรกะการกำหนดแพลตฟอร์มเพื่อให้เหมาะสมกับเครื่องปัจจุบันเพื่อที่คุณจะไม่สร้าง / อ้างอิงไบนารี 64 บิตเพื่อดำเนินการบนแพลตฟอร์ม 32 บิต

แต่เดิมฉันเขียนขึ้นสำหรับ Wiki ภายในที่ทำงานอย่างไรก็ตามฉันได้แก้ไขและโพสต์กระบวนการทั้งหมดลงในบล็อกของฉันแล้วหากคุณสนใจคำแนะนำทีละขั้นตอนโดยละเอียด


1
ดี ฉันใช้เงื่อนไขใน ItemGroup ตามคำแนะนำด้านล่าง แต่ใช้ $ (PROCESSOR_ARCHITEW6432) และ $ (PROCESSOR_ARCHITECTURE) สำหรับเงื่อนไขตามที่นี่ ฉันพบหมายเหตุ $ (PROCESSOR_ARCHITECTURE) ส่งคืน x86 บนทั้งแพลตฟอร์ม 32 และ 64 บิต แต่ $ (PROCESSOR_ARCHITEW6432) ส่งคืน AMD64 เฉพาะบน 64 บิต สิ่งที่ควรทราบหากคุณพยายามทดสอบ x86 (เนื่องจาก AMD64 เป็นอนุพันธ์ของ x86 ฉันถือว่า)
tjmoore

ขอบคุณสำหรับข้อมูลนั้น @tjmoore O / S ใดที่คุณสังเกตเห็นสิ่งนี้ ฉันเพิ่งตรวจสอบของฉันอีกครั้ง (Win7SP1) และพูดว่า AMD64 สำหรับ $ (PROCESSOR_ARCHITECTURE) แต่ต้องการข้อมูลที่ครบถ้วนและละเอียดที่สุดเท่าที่จะเป็นไปได้
Hugo

7
ตลกดีการค้นหาของฉันทำให้ฉันมาที่นี่และฉันต้องการสิ่งนี้เพราะฉันใช้ LeadTools ด้วย ... +1
Ed S.

โซลูชันนี้ใช้ได้กับการกำหนดค่าเริ่มต้น แต่ไม่ใช่จากการทดสอบของฉันหากคุณเปลี่ยนการกำหนดค่าจากการกำหนดค่าจากรายการ Visual Studio (2012 ในกรณีของฉัน) การกำหนดค่าโซลูชัน
Sarah Weinberger

แทนที่จะใช้ $ (PROCESSOR_ARCHITEW6432) ฉันใช้ $ (แพลตฟอร์ม) ด้วยเหตุผลบางประการ $ (PROCESSOR_ARCHITEW6432) ไม่ทำงาน
Dzyann

60

AFAIK หากโครงการของคุณต้องการการอ้างอิงที่เฉพาะเจาะจง 32 บิตหรือ 64 บิต (เช่น COM-interop Assemblies) และคุณไม่สนใจที่จะแก้ไขไฟล์. csproj ด้วยตนเองคุณจะต้องสร้าง 32 บิตแยกต่างหากและ โครงการ 64 บิต

ฉันควรทราบว่าโซลูชันต่อไปนี้ยังไม่ได้ทดลอง แต่ควรใช้งานได้ หากคุณยินดีที่จะแก้ไขไฟล์. csproj ด้วยตนเองคุณควรจะสามารถบรรลุผลลัพธ์ที่ต้องการได้ด้วยโปรเจ็กต์เดียว แฟ้ม .csproj เป็นเพียงสคริปต์ MSBuild ดังนั้นสำหรับการอ้างอิงเต็มดูที่นี่ เมื่อคุณเปิดไฟล์. csproj ในโปรแกรมแก้ไขให้ค้นหา<Reference>องค์ประกอบ คุณควรแยกองค์ประกอบเหล่านี้ออกเป็นกลุ่มสินค้าที่แตกต่างกันได้ 3 กลุ่มได้แก่ การอ้างอิงที่ไม่เจาะจงแพลตฟอร์มการอ้างอิงเฉพาะ x86 และการอ้างอิง x64 โดยเฉพาะ

นี่คือตัวอย่างที่ถือว่าโครงการของคุณได้รับการกำหนดค่าด้วยแพลตฟอร์มเป้าหมายชื่อ "x86" และ "x64"

<!-- this group contains references that are not platform specific -->
<ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <!-- any other references that aren't platform specific -->
</ItemGroup>

<!-- x86 specific references -->
<ItemGroup Condition=" '$(Platform)' == 'x86' ">
    <Reference Include="MyComAssembly.Interop">
        <HintPath>..\..\lib\x86\MyComAssembly.Interop.dll</HintPath>
    </Reference>

    <!-- any additional x86 specific references -->
</ItemGroup>

<!-- x64 specific referneces -->
<ItemGroup Condition=" '$(Platform)' == 'x64' ">
    <Reference Include="MyComAssembly.Interop">
        <HintPath>..\..\lib\x64\MyComAssembly.Interop.dll</HintPath>
    </Reference>

    <!-- any additional x64 specific references -->
</ItemGroup>

ตอนนี้เมื่อคุณตั้งค่าคอนฟิกบิลด์โปรเจ็กต์ / โซลูชันของคุณเพื่อกำหนดเป้าหมายแพลตฟอร์ม x86 หรือ x64 ควรมีการอ้างอิงที่เหมาะสมในแต่ละกรณี แน่นอนคุณจะต้องเล่นกับ<Reference>องค์ประกอบต่างๆ คุณสามารถตั้งค่าโปรเจ็กต์จำลองที่คุณเพิ่มการอ้างอิง x86 และ x64 จากนั้นคัดลอก<Reference>องค์ประกอบที่จำเป็นจากไฟล์โปรเจ็กต์จำลองเหล่านั้นไปยังไฟล์โปรเจ็กต์ "จริง" ของคุณ


แก้ไข 1
นี่คือลิงค์ไปยังรายการโครงการ MSBuild ทั่วไปซึ่งฉันเผลอทิ้งไว้จากโพสต์เดิม: http://msdn.microsoft.com/en-us/library/bb629388.aspx


ตอบดีเลิศ !! บันทึกวันของฉัน! ขอบคุณมาก.
hellodear

20

คุณสามารถใช้เงื่อนไขกับItemGroupสำหรับการอ้างอิง dll ในไฟล์โครงการ
สิ่งนี้จะทำให้ Visual Studio ตรวจสอบเงื่อนไขและการอ้างอิงอีกครั้งเมื่อใดก็ตามที่คุณเปลี่ยนการกำหนดค่าที่ใช้งานอยู่
เพียงเพิ่มเงื่อนไขสำหรับการกำหนดค่าแต่ละรายการ

ตัวอย่าง:

 <ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
    <Reference Include="DLLName">
      <HintPath>..\DLLName.dll</HintPath>
    </Reference>
    <ProjectReference Include="..\MyOtherProject.vcxproj">
      <Project>{AAAAAA-000000-BBBB-CCCC-TTTTTTTTTT}</Project>
      <Name>MyOtherProject</Name>
    </ProjectReference>
  </ItemGroup>

1
ขอบคุณมาก! นี่น่าจะเป็นทางออกที่ได้รับการยอมรับอย่างแน่นอน!
ManicBlowfish

อย่างจริงจังคำตอบนี้ดีกว่าและง่ายกว่ามากซึ่งเป็นที่ยอมรับ
Yandros

1
เป็นเรื่องปกติหรือไม่ที่จะมีรายการซ้ำในการอ้างอิงหลังจากทำสิ่งนี้
natenho

7

ฉันกำลังอ้างถึง x86 DLL ซึ่งอยู่ในเช่น \ component \ v3_NET4 ในโครงการของฉัน DLL เฉพาะสำหรับ x86 / x64 อยู่ในโฟลเดอร์ย่อยชื่อ "x86" และ "x64" resp

จากนั้นฉันใช้สคริปต์สร้างล่วงหน้าที่คัดลอก DLL ที่ไม่เหมาะสม (x86 / x64) ไปยังโฟลเดอร์ที่อ้างอิงโดยอ้างอิงจาก $ (PlatformName)

xcopy /s /e /y "$(SolutionDir)..\component\v3_NET4\$(PlatformName)\*" "$(SolutionDir)..\component\v3_NET4"

ใช้ได้ผลสำหรับฉัน


3

หนึ่ง. Net build ที่มีการอ้างอิง x86 / x64

ในขณะที่คำตอบอื่น ๆ ทั้งหมดให้วิธีแก้ปัญหาในการสร้างงานสร้างที่แตกต่างกันตามแพลตฟอร์ม แต่ฉันให้ตัวเลือกแก่คุณในการกำหนดค่า "AnyCPU" และสร้างบิลด์ที่ใช้งานได้กับ x86 และ x64 dll ของคุณ

ความละเอียดของ x86 / x64-dlls ที่ถูกต้องที่รันไทม์

ขั้นตอน:

  1. ใช้ AnyCPU ใน csproj
  2. ตัดสินใจว่าคุณอ้างอิงเฉพาะ x86 หรือ x64 dlls ใน csprojs ของคุณ ปรับการตั้งค่า UnitTests ให้เข้ากับการตั้งค่าสถาปัตยกรรมที่คุณเลือก สิ่งสำคัญสำหรับการดีบัก / เรียกใช้การทดสอบภายใน VisualStudio
  3. เมื่อวันที่อ้างอิง-คุณสมบัติตั้งคัดลอกท้องถิ่นและเฉพาะรุ่นที่จะเท็จ
  4. กำจัดคำเตือนสถาปัตยกรรมโดยเพิ่มบรรทัดนี้ในPropertyGroupแรกในไฟล์ csproj ทั้งหมดของคุณที่คุณอ้างอิง x86 / x64: <ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
  5. เพิ่มสคริปต์ postbuild นี้ในโปรเจ็กต์เริ่มต้นของคุณใช้และแก้ไขเส้นทางของสคริปต์นี้เพื่อให้คัดลอก x86 / x64 dlls ทั้งหมดของคุณในโฟลเดอร์ย่อยที่สอดคล้องกันของ build bin \ x86 \ bin \ x64 \

    xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX86Dlls $(TargetDir)\x86 xcopy /E /H /R /Y /I /D $(SolutionDir)\YourPathToX64Dlls $(TargetDir)\x64

    -> เมื่อคุณจะเริ่มแอปพลิเคชันตอนนี้คุณจะได้รับข้อยกเว้นว่าไม่พบชุดประกอบ

  6. ลงทะเบียนเหตุการณ์ AssemblyResolve ที่จุดเริ่มต้นของจุดเข้าแอปพลิเคชันของคุณ

    AppDomain.CurrentDomain.AssemblyResolve += TryResolveArchitectureDependency;

    ด้วยวิธีนี้:

    /// <summary>
    /// Event Handler for AppDomain.CurrentDomain.AssemblyResolve
    /// </summary>
    /// <param name="sender">The app domain</param>
    /// <param name="resolveEventArgs">The resolve event args</param>
    /// <returns>The architecture dependent assembly</returns>
    public static Assembly TryResolveArchitectureDependency(object sender, ResolveEventArgs resolveEventArgs)
    {
        var dllName = resolveEventArgs.Name.Substring(0, resolveEventArgs.Name.IndexOf(","));
    
        var anyCpuAssemblyPath = $".\\{dllName}.dll";
    
        var architectureName = System.Environment.Is64BitProcess ? "x64" : "x86";
    
        var assemblyPath = $".\\{architectureName}\\{dllName}.dll";
    
        if (File.Exists(assemblyPath))
        {
            return Assembly.LoadFrom(assemblyPath);
        }
    
        return null;
    }
  7. หากคุณมีการทดสอบหน่วยให้ทำ TestClass ด้วย Method ที่มี AssemblyInitializeAttribute และลงทะเบียน TryResolveArchitectureDependency-Handler ข้างต้นที่นั่น (สิ่งนี้จะไม่ถูกดำเนินการในบางครั้งหากคุณเรียกใช้การทดสอบเดี่ยวภายในวิชวลสตูดิโอการอ้างอิงจะได้รับการแก้ไขไม่ได้มาจากถังทดสอบ UnitTest ดังนั้นการตัดสินใจในขั้นตอนที่ 2 จึงมีความสำคัญ)

ประโยชน์ที่ได้รับ:

  • หนึ่งการติดตั้ง / สร้างสำหรับทั้งสองแพลตฟอร์ม

ข้อเสีย: - ไม่มีข้อผิดพลาดในเวลาคอมไพล์เมื่อ x86 / x64 dll ไม่ตรงกัน - คุณควรทำการทดสอบในทั้งสองโหมด!

เลือกที่จะสร้างไฟล์ปฏิบัติการที่สองซึ่งเป็นเอกสิทธิ์สำหรับสถาปัตยกรรม x64 ด้วย Corflags.exe ในสคริปต์ postbuild

ตัวแปรอื่น ๆ ที่ควรทดลองใช้: - คุณไม่จำเป็นต้องใช้ตัวจัดการเหตุการณ์ AssemblyResolve หากคุณมั่นใจเป็นอย่างอื่นว่า dlls จะถูกคัดลอกในโฟลเดอร์ไบนารีของคุณเมื่อเริ่มต้น (ประเมินสถาปัตยกรรมกระบวนการ -> ย้าย dll ที่เกี่ยวข้องจาก x64 / x86 ไปยังโฟลเดอร์ bin และย้อนกลับ) - ในโปรแกรมติดตั้งประเมินสถาปัตยกรรมและลบไบนารีสำหรับสถาปัตยกรรมที่ไม่ถูกต้องและย้ายสิ่งที่ถูกต้องไปยังโฟลเดอร์ถังขยะ


2

ฉันประสบปัญหาเดียวกันและใช้เวลาค่อนข้างนานในการค้นหาวิธีแก้ปัญหาที่เหมาะสม คนส่วนใหญ่เสนอการแก้ไขไฟล์โซลูชัน Visual Studio ด้วยตนเองซึ่งค่อนข้างน่าเบื่อเกิดข้อผิดพลาดและสับสนเมื่อสำรวจไฟล์ที่แก้ไขเหล่านี้ใน Visual Studio GUI ในภายหลัง เมื่อฉันยอมแพ้แล้ววิธีแก้ปัญหาก็เกิดขึ้นเอง คล้ายกับสิ่งที่ Micke แนะนำในคำตอบของเขาข้างต้น

ในตัวจัดการบัญชีฉันได้สร้างสองเป้าหมายการสร้างแยกกันสำหรับแพลตฟอร์ม x86 และ x64 ตามปกติ ต่อไปฉันเพิ่มการอ้างอิงถึงแอสเซมบลี x86 ในโครงการของฉัน ในจุดนี้ฉันเชื่อว่าโครงการได้รับการกำหนดค่าสำหรับ x86 build เท่านั้นและจะไม่สร้างสำหรับการกำหนดค่า x64 เว้นแต่ฉันจะทำการแก้ไขด้วยตนเองตามที่ Hugo แนะนำข้างต้น

หลังจากนั้นไม่นานในที่สุดฉันก็ลืมข้อ จำกัด และเริ่มสร้าง x64 โดยไม่ได้ตั้งใจ แน่นอนว่าการสร้างล้มเหลว แต่สิ่งสำคัญคือข้อความแสดงข้อผิดพลาดที่ฉันได้รับ ข้อความแสดงข้อผิดพลาดแจ้งว่าแอสเซมบลีชื่อตรงตามที่แอสเซมบลี x86 อ้างอิงของฉันหายไปในโฟลเดอร์ที่มีจุดประสงค์เพื่อสร้าง x64 สำหรับโซลูชันของฉัน

เมื่อสังเกตเห็นสิ่งนี้ฉันได้คัดลอกแอสเซมบลี x64 ที่เหมาะสมลงในไดเร็กทอรีนี้ด้วยตนเอง ความรุ่งโรจน์! งานสร้าง x64 ของฉันประสบความสำเร็จอย่างน่าอัศจรรย์โดยพบการประกอบที่เหมาะสมและเชื่อมโยงโดยปริยาย ใช้เวลาไม่กี่นาทีในการแก้ไขโซลูชันของฉันเพื่อตั้งค่าไดเร็กทอรี build target สำหรับ x64 assembly ไปยังโฟลเดอร์นี้ หลังจากขั้นตอนเหล่านี้โซลูชันจะสร้างขึ้นโดยอัตโนมัติสำหรับทั้ง x86 และ x64 โดยไม่มีการแก้ไขไฟล์ MSBuild ด้วยตนเอง

สรุป:

  1. สร้างเป้าหมาย x86 และ x64 ในโปรเจ็กต์เดียว
  2. เพิ่มการอ้างอิงโปรเจ็กต์ที่เหมาะสมทั้งหมดในชุดประกอบ x86
  3. ตั้งค่าไดเร็กทอรีเป้าหมายบิลด์ทั่วไปหนึ่งรายการสำหรับชุดประกอบ x64 ทั้งหมด
  4. ในกรณีที่คุณมีแอสเซมบลี x64 ที่พร้อมให้คัดลอกหนึ่งครั้งลงในไดเร็กทอรีเป้าหมายบิวด์ x64 ของคุณ

หลังจากเสร็จสิ้นขั้นตอนเหล่านี้โซลูชันของคุณจะสร้างอย่างเหมาะสมสำหรับทั้งการกำหนดค่า x86 และ x64

สิ่งนี้ใช้ได้ผลสำหรับฉันในโครงการ Visual Studio 2010 .NET 4.0 C # เห็นได้ชัดว่านี่เป็นลักษณะการทำงานภายในที่ไม่มีเอกสารของ Visual Studio ซึ่งอาจมีการเปลี่ยนแปลงในเวอร์ชัน 2012, 2013 และ 2015 หากมีใครจะลองรุ่นอื่นโปรดแบ่งปันประสบการณ์ของคุณ


-1

ฉันลงเอยด้วยการใช้สิ่งที่ฉันคิดว่าเป็นวิธีแก้ปัญหาที่ง่ายกว่านั่นคือการผกผันของ Micke โครงการนี้เป็นแอปรูปแบบ C # Visual Studio 2015 ที่มีเป้าหมาย x86 และ x64 ฉันอ้างถึงหนึ่งในแอสเซมบลี. NET ฉันใช้ 32 บิตหนึ่ง ในคุณสมบัติการอ้างอิงฉันตั้งค่า "Copy Local" เป็นเท็จ จากนั้นฉันก็ใส่แอสเซมบลี. Net ที่เหมาะสม (32 หรือ 64 บิต) ด้วยตนเองในแต่ละไดเร็กทอรีเป้าหมาย บิตเนสอ้างอิงที่แท้จริงไม่เกี่ยวข้องโดยสมมติว่ามีความสามารถเหมือนกันเนื่องจากเป็นเพียงการกำหนดอินเทอร์เฟซภายนอก คุณสามารถใส่ขั้นตอนการคัดลอกการสร้างโพสต์ได้หากคุณต้องการจินตนาการ โปรดทราบว่าโครงการนี้มีการอ้างอิง COM ด้วยเช่นกัน การอ้างอิงกำหนดอ็อบเจ็กต์ / อินเทอร์เฟซดังนั้นบิตเนสของ DLL อ้างอิงจึงไม่เกี่ยวข้อง หากลงทะเบียน COM DLL ทั้ง 32 บิตและ 64 บิตแล้ว แอปจะดูในตำแหน่งที่เหมาะสมในรีจิสทรีและสร้างวัตถุ COM 32 หรือ 64 บิตที่ถูกต้อง ใช้ได้ผลสำหรับฉัน!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.