คอมไพเลอร์ C # ตรวจจับประเภท COM ได้อย่างไร


168

แก้ไข:ฉันได้เขียนผลขึ้นเป็นบล็อกโพสต์


คอมไพเลอร์ C # ใช้ COM ประเภทค่อนข้างน่าอัศจรรย์ ตัวอย่างเช่นคำสั่งนี้ดูเหมือนปกติ ...

Word.Application app = new Word.Application();

... จนกว่าคุณจะรู้ว่านั่นApplicationคือส่วนต่อประสาน การเรียกตัวสร้างบนอินเทอร์เฟซหรือไม่ Yoiks! นี้จริงได้รับการแปลเป็นโทรไปType.GetTypeFromCLSID()และอื่น ๆ Activator.CreateInstanceเพื่อ

นอกจากนี้ใน C # 4 คุณสามารถใช้อาร์กิวเมนต์ที่ไม่อ้างอิงสำหรับrefพารามิเตอร์และคอมไพเลอร์เพิ่งเพิ่มตัวแปรท้องถิ่นที่จะผ่านการอ้างอิงโดยยกเลิกผลลัพธ์:

// FileName parameter is *really* a ref parameter
app.ActiveDocument.SaveAs(FileName: "test.doc");

(ใช่มีข้อโต้แย้งจำนวนมากหายไปพารามิเตอร์ตัวเลือกไม่ดีใช่ไหม :)

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

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

[ComImport, GuidAttribute("00012345-0000-0000-0000-000000000011")]
public interface Dummy
{
    void Foo(ref int x);
}

class Test
{
    static void Main()
    {
        Dummy dummy = null;
        dummy.Foo(10);
    }
}

ฉันต้องการเขียน:

Dummy dummy = new Dummy();

แม้ เห็นได้ชัดว่ามันจะทำงานได้อย่างราบรื่นในเวลาดำเนินการ แต่ก็ไม่เป็นไร ฉันแค่ทดลอง

คุณลักษณะอื่น ๆ ที่คอมไพเลอร์เพิ่มเข้ามาสำหรับ COM PIA ที่เชื่อมโยง ( CompilerGeneratedและTypeIdentifier) ดูเหมือนจะไม่หลอกลวง ... อะไรคือเวทย์มนตร์


11
พารามิเตอร์ทางเลือกไม่ดีใช่ไหม IMO, ไม่พวกเขาไม่ดี Microsoft กำลังพยายามแก้ไขข้อบกพร่องในอินเทอร์เฟซ Office COM ด้วยการเพิ่ม bloat ใน C #
Mehrdad Afshari

18
@ Mehrdad: พารามิเตอร์ตัวเลือกมีประโยชน์เกินกว่า COM แน่นอน คุณต้องระวังด้วยค่าเริ่มต้น แต่ระหว่างอาร์กิวเมนต์เหล่านี้กับชื่ออาร์กิวเมนต์มันง่ายกว่ามากในการสร้างประเภทที่เปลี่ยนไม่ได้ที่ใช้งานได้
Jon Skeet

1
จริง โดยเฉพาะอย่างยิ่งพารามิเตอร์ที่มีชื่อสามารถใช้งานได้จริงสำหรับการทำงานร่วมกันในสภาพแวดล้อมแบบไดนามิกบางอย่าง แน่นอนว่ามันเป็นคุณสมบัติที่มีประโยชน์ แต่นั่นไม่ได้หมายความว่ามันฟรี ค่าใช้จ่ายเรียบง่าย (เป้าหมายการออกแบบที่ระบุไว้อย่างชัดเจน) โดยส่วนตัวแล้วฉันคิดว่า C # นั้นยอดเยี่ยมสำหรับฟีเจอร์ที่ทีมทิ้งไว้ (ไม่เช่นนั้นอาจเป็น C ++ โคลน) ทีม C # นั้นยอดเยี่ยม แต่สภาพแวดล้อมขององค์กรแทบจะไม่มีการเมือง ฉันเดาว่าตัวเอง Anders ไม่มีความสุขกับสิ่งนี้ตามที่เขากล่าวไว้ในการพูดคุยของ PDC'08: "เราใช้เวลาสิบปีเพื่อกลับไปยังที่ที่เราอยู่"
Mehrdad Afshari

7
ฉันยอมรับว่าทีมจะต้องคอยจับตาดูความซับซ้อนอย่างใกล้ชิด สิ่งที่มีพลวัตนั้นเพิ่มความซับซ้อนมากมายสำหรับค่าเล็กน้อยสำหรับนักพัฒนาส่วนใหญ่แต่มูลค่าสูงสำหรับนักพัฒนาบางคน
Jon Skeet

1
ฉันเห็นผู้พัฒนาเฟรมเวิร์กเริ่มพูดคุยเกี่ยวกับการใช้งานในหลาย ๆ ที่ IMO ถึงเวลาแล้วจนกว่าเราจะพบว่ามีการใช้งานที่ดีสำหรับdynamic... เราแค่คุ้นเคยกับการพิมพ์แบบคงที่ / แข็งแรงเพื่อดูว่าทำไมมันถึงมีความสำคัญนอก COM
chakrit

คำตอบ:


145

ฉันไม่ใช่ผู้เชี่ยวชาญในเรื่องนี้ แต่ฉันเพิ่งพบสิ่งที่คุณต้องการ: คลาสแอตทริบิวต์ของCoClass

[System.Runtime.InteropServices.CoClass(typeof(Test))]
public interface Dummy { }

coclass ระบุการนำไปใช้อย่างเป็นรูปธรรมของอินเตอร์เฟสตั้งแต่หนึ่งอินเตอร์เฟสขึ้นไป ใน COM การใช้งานที่เป็นรูปธรรมดังกล่าวสามารถเขียนได้ในภาษาการเขียนโปรแกรมใด ๆ ที่รองรับการพัฒนาองค์ประกอบ COM เช่น Delphi, C ++, Visual Basic เป็นต้น

ดูคำตอบของฉันสำหรับคำถามที่คล้ายกันเกี่ยวกับ Microsoft Speech APIซึ่งคุณสามารถ "อินสแตนซ์" อินเทอร์เฟซSpVoice(แต่จริงๆแล้วคุณกำลังอินสแตนซ์SPVoiceClass)

[CoClass(typeof(SpVoiceClass))]
public interface SpVoice : ISpeechVoice, _ISpeechVoiceEvents_Event { }

2
น่าสนใจมาก - จะลองอีกครั้ง ประเภท PIA ที่เชื่อมโยงนั้นไม่มี CoClass บางทีมันอาจจะเป็นสิ่งที่จะทำอย่างไรกับกระบวนการเชื่อมโยง - ฉันจะได้ดูใน PIA เดิม ...
จอนสกีต

64
+1 ที่ยอดเยี่ยมด้วยการเขียนคำตอบที่ยอมรับเมื่อ Eric Lippert และ Jon Skeet ตอบด้วย;) ไม่จริง ๆ +1 สำหรับพูดถึง CoClass
OregonGhost

61

ระหว่างคุณกับไมเคิลคุณได้ชิ้นส่วนต่างๆมาประกอบกัน ฉันคิดว่านี่เป็นวิธีการทำงาน (ฉันไม่ได้เขียนรหัสดังนั้นฉันอาจจะระบุผิดเล็กน้อย แต่ฉันค่อนข้างแน่ใจว่านี่เป็นวิธีที่จะไป)

ถ้า:

  • คุณเป็น "ใหม่" ในประเภทอินเตอร์เฟสและ
  • ประเภทอินเตอร์เฟสมี coclass ที่รู้จักและ
  • คุณกำลังใช้คุณสมบัติ "no pia" สำหรับอินเทอร์เฟซนี้

ดังนั้นรหัสจะถูกสร้างขึ้นเป็น (IPIAINTERFACE) Activator.CreateInstance (Type.GetTypeFromClsid (GUID OF COCLASSTYPE)

ถ้า:

  • คุณเป็น "ใหม่" ในประเภทอินเตอร์เฟสและ
  • ประเภทอินเตอร์เฟสมี coclass ที่รู้จักและ
  • คุณไม่ได้ใช้คุณสมบัติ "no pia" สำหรับอินเทอร์เฟซนี้

รหัสจะถูกสร้างขึ้นราวกับว่าคุณได้พูดว่า "ใหม่ COCLASSTYPE ()"

จอนรู้สึกอิสระที่จะบั๊กฉันหรือแซมโดยตรงหากคุณมีคำถามเกี่ยวกับสิ่งนี้ FYI, Sam เป็นผู้เชี่ยวชาญในคุณสมบัตินี้


36

ตกลงนี่เป็นเพียงการเพิ่มเนื้อความอีกเล็กน้อยกับคำตอบของไมเคิล (เขายินดีที่จะเพิ่มเข้าไปในถ้าเขาต้องการซึ่งในกรณีนี้ฉันจะลบออก)

มองไปที่ PIA ดั้งเดิมสำหรับ Word.Application มีสามประเภทที่เกี่ยวข้อง (ละเว้นเหตุการณ์):

[ComImport, TypeLibType(...), Guid("..."), DefaultMember("Name")]
public interface _Application
{
     ...
}

[ComImport, Guid("..."), CoClass(typeof(ApplicationClass))]
public interface Application : _Application
{
}

[ComImport, ClassInterface(...), ComSourceInterfaces("..."), Guid("..."), 
 TypeLibType((short) 2), DefaultMember("Name")]
public class ApplicationClass : _Application, Application
{
}

มีสองอินเตอร์เฟซสำหรับเหตุผลที่เอริค Lippert พูดคุยเกี่ยวกับในคำตอบอื่น และอย่างที่คุณบอกว่าเป็นCoClass- ทั้งในแง่ของคลาสเองและแอ็ตทริบิวต์บนApplicationอินเตอร์เฟส

ตอนนี้ถ้าเราใช้การเชื่อมโยง PIA ใน C # 4 บางส่วนจะถูกฝังอยู่ในไบนารีผลลัพธ์ ... แต่ไม่ใช่ทั้งหมด แอปพลิเคชั่นที่เพิ่งสร้างอินสแตนซ์ของApplicationลงท้ายด้วยประเภทเหล่านี้:

[ComImport, TypeIdentifier, Guid("..."), CompilerGenerated]
public interface _Application

[ComImport, Guid("..."), CompilerGenerated, TypeIdentifier]
public interface Application : _Application

ไม่ApplicationClassน่าจะเป็นเพราะมันจะถูกโหลดแบบไดนามิกจากCOM จริงในเวลาที่ใช้งาน

อีกสิ่งที่น่าสนใจคือความแตกต่างของรหัสระหว่างรุ่นที่เชื่อมโยงกับรุ่นที่ไม่ได้เชื่อมโยง หากคุณถอดรหัสสาย

Word.Application application = new Word.Application();

ในรุ่นที่อ้างอิงมันจะสิ้นสุดลงเมื่อ:

Application application = new ApplicationClass();

ในขณะที่ในรุ่นที่เชื่อมโยงจะสิ้นสุดลงเป็น

Application application = (Application) 
    Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("...")));

ดังนั้นดูเหมือนว่า "ของจริง" PIA ต้องการCoClassแอตทริบิวต์ แต่รุ่นที่เชื่อมโยงไม่ได้เพราะมีไม่CoClassคอมไพเลอร์จริงสามารถอ้างอิง มันต้องทำแบบไดนามิก

ฉันอาจลองปลอมส่วนติดต่อ COM โดยใช้ข้อมูลนี้และดูว่าฉันจะได้รับรวบรวมเพื่อเชื่อมโยงมัน ...


27

เพียงเพิ่มการยืนยันเล็กน้อยกับคำตอบของ Michael:

รหัสต่อไปนี้รวบรวมและทำงาน:

public class Program
{
    public class Foo : IFoo
    {
    }

    [Guid("00000000-0000-0000-0000-000000000000")]
    [CoClass(typeof(Foo))]
    [ComImport]
    public interface IFoo
    {
    }

    static void Main(string[] args)
    {
        IFoo foo = new IFoo();
    }
}

คุณต้องการทั้งComImportAttributeและGuidAttributeสำหรับการทำงาน

นอกจากนี้ควรจดบันทึกข้อมูลเมื่อคุณวางเมาส์ไว้เหนือnew IFoo(): Intellisense จะรับข้อมูลอย่างถูกต้อง: ดีมาก!


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