การติดตามการอ้างอิงถึงปัญหาการอ้างอิงดั้งเดิม - C# ถึง C++/CLI ถึง C++ AccessViolationException

ฉันกำลังพยายามเขียน wrapper C++/CLI โดยใช้ VS2010 กับ C++ API ซึ่งฉันมีสิทธิ์เข้าถึงเฉพาะไฟล์ส่วนหัวและไฟล์ dll/lib เท่านั้น

ฉันประสบปัญหากับการสำรองข้อมูลการอ้างอิงเนทิฟจากโค้ดเนทีฟไปยังการอ้างอิงการติดตามในโค้ด C# ฉันได้รับ SystemAccessViolation รหัสมีดังนี้:

ส่วนหัว C++ ดั้งเดิมที่ไม่ได้รับการจัดการสำหรับฟังก์ชันดั้งเดิมที่ส่งคืนการอ้างอิงเพื่อสร้างและแก้ไข CustomObject:

class ManageCustomObject;
    _declspec(dllimport) errorCode CreateCustomObject(CustomObject& customObject);
    _declspec(dllimport) errorCode ModifyCustomObject(CustomObject& customObject);

สิ่งสำคัญที่ควรทราบเกี่ยวกับ CustomObject คือมีตัวสร้างส่วนตัวเท่านั้น:

CustomObject(const CustomObject&) { }
CustomObject& operator=(const CustomObject&) { return *this; }

ตอนนี้วิธีเลเยอร์ C++/CLI ของฉันมีลักษณะดังนี้:

public ref class WrapperManageCustomObject {
public:
        errorCode WrapperCreateCustomObject(CustomObject% customObject)
        {
            return CreateCustomObject(customObject);
       }

        errorCode WrapperModifyCustomObject(CustomObject% customObject)
        {
            return ModifyCustomObject(customObject);
        }
};

และ wrapper สำหรับ CustomObject มีลักษณะดังนี้:

public class WrapperCustomObject : public CustomObject {
    public:
        WrapperCustomObject(const CustomObject&) {};
};

ใน C# รหัสจะเป็นดังนี้:

WrapperCustomObject wrapperCustomObject;
WrapperManageCustomObject wrapperManageCustomObject = new WrapperManageCustomObject();

long result;

// this works great
result = wrapperManageCustomObject.CreateCustomObject(ref wrapperCustomObject);

// can utilize wrapperCustomObject here with other native functions no problem, so long as I don't try to modify it...
...

// this throws an AccessViolationException was unhandled message
// essentially I am modifying / returning a different native customObject (though I'm not 100% sure what it does as I do not have access to the code)
result = wrapperManageCustomObject.ModifyCustomObject(ref wrapperCustomObject);

ดังที่คุณเห็นในความคิดเห็นของฉัน ความพยายามที่จะแก้ไขวัตถุนั้นผ่านการอ้างอิงทำให้เกิดข้อผิดพลาด "AccessViolationException was unhandled : พยายามอ่านหรือเขียนหน่วยความจำที่ได้รับการป้องกัน นี่มักจะบ่งชี้ว่าหน่วยความจำอื่นเสียหาย"

ตอนนี้ ถ้าฉันลบ C# และการอ้างอิงการติดตามออกจากสมการนี้ และทำทั้งหมดนี้ภายใน C++ หรือ C++/CLI (ตราบใดที่ไม่มีการอ้างอิงการติดตาม) ก็ใช้งานได้ดี ฉันคิดว่านี่เกี่ยวข้องกับโค้ดเนทีฟที่พยายามเข้าถึงการอัปเดตข้อมูลอ้างอิงที่ขณะนี้อยู่ในพื้นที่ที่ได้รับการจัดการ

มีวิธีใดในการทำให้โค้ดนี้ "ใช้งานได้" จากภายใน C # หรือไม่ ไม่มีวิธีใดที่จะส่งผ่านการอ้างอิงดั้งเดิมไปยัง C# ที่ฉันทราบได้


person codemouse    schedule 05.08.2011    source แหล่งที่มา
comment
คุณควรสร้าง CustomObject ในที่ดิน C ++ อย่างไรหากมีตัวสร้างส่วนตัวและไม่มีฟังก์ชันจากโรงงาน คุณต้องมีอินสแตนซ์ CustomObject อยู่แล้วจึงจะสามารถเรียกใช้ฟังก์ชันตัวช่วย CreateCustomObject ได้   -  person Adam Rosenfield    schedule 06.08.2011
comment
คุณเอาสิ่งนี้มารวบรวมได้อย่างไร? คุณกำลังส่งคืนตัวชี้ออบเจ็กต์ native แต่โค้ด C# ถือว่ามันเป็น wrapper ที่มีการจัดการ ใช่ Kaboom ใหญ่เมื่อคุณใช้มัน ตัวชี้วัตถุดั้งเดิมควรเป็นสมาชิกส่วนตัวของ wrapper   -  person Hans Passant    schedule 06.08.2011
comment
สตู - ฉันสามารถทำสิ่งเหล่านี้เพื่อเป็นแนวทางได้เช่นกัน แต่ก็แค่นั้นแหละ พารามิเตอร์การอ้างอิงเป็นสิ่งที่ c #   -  person codemouse    schedule 06.08.2011
comment
Hans - C++/CLI อนุญาตให้มีการอ้างอิงการติดตาม เชื่อหรือไม่ว่ามันใช้งานได้จริง   -  person codemouse    schedule 06.08.2011
comment
อดัม - คุณไม่จำเป็นต้องสร้าง CustomObject ในพื้นที่ C ++ หากฟังก์ชันส่งคืนการอ้างอิงไปที่หนึ่งแล้ว - หมายความว่าฉันคิดว่าไฟล์ .cpp ที่ฉันไม่เห็น IS ที่ซึ่งอินสแตนซ์ถูกสร้างขึ้นและเก็บไว้ คุณเพียงแค่ต้องมีคอนเทนเนอร์เพื่อเก็บข้อมูลอ้างอิงนั้น เช่น: CustomObject customObject; ก่อนที่คุณจะเรียกใช้ฟังก์ชันนั้น   -  person codemouse    schedule 06.08.2011
comment
@Brian: ฉันเห็นด้วยกับ Hans ที่นี่; ฉันไม่รู้ว่าทำไมคุณถึงคิดว่าการมีอยู่ของการอ้างอิงการติดตามเปลี่ยนแปลงความถูกต้องของข้อความของเขา นอกจากนี้ เมื่อกล่าวถึงบุคคลอื่น โปรดใส่ชื่อพวกเขานำหน้าด้วย @ เพื่อให้พวกเขาได้รับการแจ้งเตือนในกล่องจดหมาย SO ของพวกเขา   -  person ildjarn    schedule 06.08.2011
comment
@ildjarn โอเค ตกลง อย่างไรก็ตามแม้ว่าฉันจะทำบางอย่างเช่น private: CustomObject _customObject; ภายในคลาส Public WrapperCustomObject (ไม่มีการสืบทอดคลาสจากประเภทพื้นฐานดั้งเดิม) ฉันยังคงได้รับข้อผิดพลาดเดียวกัน ในขณะที่ถ้าฉันทำสิ่งนี้ทั้งหมดใน C++ ที่ไม่มีการจัดการ มันก็ทำงานได้ดี การอ้างอิงการติดตามที่ฉันใช้เพื่อชี้ไปที่อินสแตนซ์ของคลาส wrapper ทำให้เกิด barf   -  person codemouse    schedule 06.08.2011
comment
@Brian: วิธีการที่ใช้สำนวนคือการทำให้ private: CustomObject* _customObject; และใช้การดำเนินการกำจัดและตัวสรุปเพื่อให้แน่ใจว่าได้รับการทำความสะอาด   -  person ildjarn    schedule 06.08.2011
comment
@ildjarn: เห็นด้วยอีกครั้ง นี่คือทุกสิ่งที่ฉันจะทำถ้าฉันมีสิทธิ์เข้าถึงซอร์ส C++ dll ที่รองรับอย่างเหมาะสม แต่ฉันทำไม่ได้ ในระหว่างนี้ วิธีแก้ไขทั้งหมดเหล่านี้ดีที่สุดเท่าที่จะทำได้เพื่อให้ทำงานได้สำเร็จ   -  person codemouse    schedule 09.08.2011


คำตอบ (1)


ข้อมูลอ้างอิงที่ได้รับการจัดการนั้นไม่มีอะไรเหมือนกับข้อมูลอ้างอิงดั้งเดิมเลย การอ้างอิงที่มีการจัดการนั้นเหมือนกับพอยน์เตอร์ดั้งเดิมมากกว่า เท่าที่ฉันทราบ คุณไม่สามารถทำสิ่งที่ต้องการ public class WrapperCustomObject : public CustomObject (โดยที่ CustomObject เป็นคลาสเนทิฟ) และคาดหวังว่ามันจะได้ผล เมื่อคุณสร้างคลาสที่มีการจัดการ คุณกำลังบอก CLR ว่าคลาสของคุณจะมีพฤติกรรมที่ดีเมื่อเผชิญกับการเคลื่อนย้ายในหน่วยความจำ หาก CustomObject ทำอะไรโดยใช้พอยน์เตอร์เลย นั่นจะไม่เป็นจริงอีกต่อไป CustomObject ไม่ได้ใช้การอ้างอิงที่มีการจัดการ ดังนั้นรันไทม์จึงไม่มีทางรู้ว่าจะอัปเดตไคลเอ็นต์ได้ที่ไหนหาก/เมื่ออินสแตนซ์คลาสนั้นถูกย้าย

ที่แย่กว่านั้นคือ wrapper ของคุณยังอนุญาตให้คลาสนั้นถูกสร้างอินสแตนซ์โดยไม่ต้องผ่านวิธีการจากโรงงาน ดังนั้น หากวิธีการของโรงงาน CreateCustomObject ทำสิ่งใดที่สำคัญกับออบเจ็กต์ที่คุณได้รับกลับมา แสดงว่าคุณละเมิดสัญญาของโค้ดที่คุณเรียกใช้ ซึ่งขอให้คุณสร้างอินสแตนซ์ของออบเจ็กต์นั้นผ่านวิธีการของโรงงานเท่านั้น

person Billy ONeal    schedule 05.08.2011
comment
น่าแปลกที่คุณจะได้รับ WrapperCustomObject คลาสสาธารณะเป็นคลาสที่ได้รับการจัดการเพื่อสืบทอดจาก Public CustomObject (คลาสเนทีฟที่ไม่ได้รับการจัดการ) และมันจะใช้งานได้จริง อย่างไรก็ตาม ตามที่คุณชี้ให้เห็นและฉันเห็นด้วย ข้อจำกัดสามารถสะสมได้อย่างรวดเร็วและฉันเห็นด้วยกับสิ่งที่คุณพูด - person codemouse; 06.08.2011
comment
บางทีสิ่งที่ฉันกำลังมองหาตอนนี้อาจเป็นวิธีที่เป็นไปได้ในการหลีกเลี่ยงวิธีการนี้ไปสู่สิ่งที่อาจทำงานได้อย่างถูกต้อง ดูเหมือนว่าฉันไม่สามารถหาวิธีทำสิ่งที่มีประโยชน์ได้เมื่อ CreateCustomObject ส่งคืนออบเจ็กต์นั้นโดยการอ้างอิงเท่านั้น และนั่นคือวิธีเดียวที่ฉันสามารถสร้างหรือระงับได้อย่างมีประสิทธิภาพ การทำงานกับข้อมูลอ้างอิงนั้นยากมาก คุณไม่สามารถจัดเก็บมันได้เลยยกเว้นอินสแตนซ์เอกพจน์คงที่ของ C++ หรือตามที่ฉันได้สาธิตไปแล้ว - การอ้างอิงการติดตามที่เก็บไว้ผ่าน Managed C# - person codemouse; 06.08.2011
comment
@Brian: ไม่ มันใช้งานไม่ได้จริง ๆ เพียงเพราะมันคอมไพล์ไม่ได้หมายความว่ามันจะไม่ล้มเหลวที่รันไทม์ (และในความเป็นจริงมันเป็นเช่นนั้น) มันคอมไพล์เพราะคลาส C++ อาจ ไม่มีสถานะ หรือ อาจ ไม่มีตัวชี้ ซึ่งในกรณีนี้จะทำงานได้ แต่เนื่องจากคุณไม่ทราบถึงการใช้งานคลาส จึงไม่ใช่สิ่งที่คุณสามารถไว้วางใจได้ สำหรับการทำงานกับการอ้างอิงเป็นเรื่องยาก จะยากหากคุณพยายามคิดว่าการอ้างอิงดั้งเดิมเป็นการอ้างอิงที่มีการจัดการ พวกเขาไม่มีอะไรเกี่ยวข้องกัน หากคุณต้องการข้อมูลอ้างอิงที่มีการจัดการ ให้สร้างข้อมูลอ้างอิง - person Billy ONeal; 06.08.2011
comment
@Brian: คุณไม่สามารถเปลี่ยนการอ้างอิงที่มีการจัดการเป็นการอ้างอิงดั้งเดิมหรือในทางกลับกัน ระยะเวลา. หากคุณต้องการ wrapper คุณจะต้องสร้าง wrapper และเปิดเผยการจัดการที่เทียบเท่าสำหรับวิธีการดั้งเดิม - person Billy ONeal; 06.08.2011
comment
เห็นด้วย งานวิจัยทั้งหมดของฉันชี้ให้เห็นว่าไม่มีทางที่จะดึงข้อมูลอ้างอิงดั้งเดิมจากข้อมูลอ้างอิงที่มีการจัดการ / ติดตามได้ อย่างไรก็ตาม ฉันยังคงทราบว่าเมื่อออกไปข้างนอก การอ้างอิงการติดตามจาก C++/CLI สามารถทำงานได้ - แม้จะกลับเข้าไปด้านในเป็นโค้ดที่ไม่มีการจัดการ ตราบใดที่ออบเจ็กต์อ้างอิงเป้าหมายไม่เปลี่ยนแปลงในโค้ดที่ไม่มีการจัดการ - เนื่องจากโค้ดที่ไม่มีการจัดการไม่สามารถเข้าถึงโค้ดที่มีการจัดการได้ ซ้อนกัน. ตกลงด้วยว่าเพิ่มเติมนั้น - ขึ้นอยู่กับอินสแตนซ์ไร้สัญชาติของคลาสนั้นที่สร้างขึ้น วิธีแก้ไขที่นี่คือการเก็บการอ้างอิงแบบเนทีฟภายในโค้ดแบบเนทีฟเท่านั้น - อย่าให้อ้างอิงการติดตามเข้ามาเกี่ยวข้อง - person codemouse; 09.08.2011
comment
ไม่สามารถเปลี่ยนโค้ดพื้นฐานได้ หากโค้ดที่คอมไพล์สร้างอินสแตนซ์ที่นั่นและส่งผ่านการอ้างอิง อืม... ฉันอยู่ภายใต้การเสนอราคาของโค้ดนั้น :) - person codemouse; 09.08.2011
comment
@Codemouse: คุณสามารถคัดลอกเป้าหมายของการอ้างอิงได้ หรือเก็บพอยน์เตอร์ไว้ หรือใช้เป็นข้อมูลอ้างอิงดั้งเดิมในวิธีอื่นใดที่สามารถใช้การอ้างอิงดั้งเดิมได้ คุณไม่สามารถทำงานประเภทนั้นจากภายใน C # ได้ - person Billy ONeal; 09.08.2011