2012-06-20 17 views
8

में एक सी # कक्षा ऑब्जेक्ट पास करना और मैं एक प्रोटोटाइप कोड एप्लिकेशन पर काम कर रहा हूं जो सी # में चलता है और पुराने सी ++ कोड (आयातित डीएलएल के रूप में) से कक्षाओं और कार्यों का उपयोग करता है। कोड आवश्यकता को क्लास ऑब्जेक्ट में अप्रबंधित सी ++ डीएलएल (सी # से) में पास करना है और इसे C# एप्लिकेशन द्वारा बाद में पुनर्प्राप्ति के लिए संग्रहीत/संशोधित किया जाना है।सी ++ डीएलएल कक्षा

class CClass : public CObject 
{ 
public: 
    int intTest1 
}; 

सी ++ DLL कार्य::

CClass *Holder = new CClass; 

extern "C" 
{ 
    // obj always comes in with a 0 value. 
    __declspec(dllexport) void SetDLLObj(CClass* obj) 
    { 
     Holder = obj; 
    } 

    // obj should leave with value of Holder (from SetDLLObj). 
    __declspec(dllexport) void GetDLLObj(__out CClass* &obj) 
    { 
     obj = Holder; 
    } 
} 

सी # क्लास और आवरण:

[StructureLayout(LayoutKind.Sequential)] 
public class CSObject 
{ 
    public int intTest2; 
} 

class LibWrapper 
{ 
    [DLLImport("CPPDLL.dll")] 
    public static extern void SetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
     CSObject csObj); 
    public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
     ref CSObject csObj); 
} 

सी # यहाँ कोड मैं अब तक है ...

सरल सी ++ DLL वर्ग है डीएलएल को फंक्शन कॉल:

class TestCall 
{ 
    public static void CallDLL() 
    { 
     ... 
     CSObject objIn = new CSObject(); 
     objIn.intTest2 = 1234; // Just so it contains something. 
     LibWrapper.SetDLLObj(objIn); 
     CSObject objOut = new CSObject(); 
     LibWrapper.GetDLLObj(ref objOut); 
     MessageBox.Show(objOut.intTest2.ToString()); // This only outputs "0". 
     ... 
    } 
} 

कुछ भी नहीं, लेकिन डीएलएल के भीतर जंक वैल्यू उपलब्ध नहीं हैं (सी # ऑब्जेक्ट में पारित होने से)। मेरा मानना ​​है कि मुझे क्लास मार्शलिंग या मेमोरी/पॉइंटर इश्यू के साथ कुछ याद आ रहा है। मैं क्या खो रहा हूँ?

संपादित करें: मैंने बॉन्ड द्वारा सुझाए गए सी #/सी ++ में विधि/फ़ंक्शन परिभाषाओं में परिवर्तन को दर्शाने के लिए उपर्युक्त कोड बदल दिया। मूल्य (1234) पारित किया जा रहा है अब सी # कोड द्वारा ठीक से पुनर्प्राप्त किया गया है। इसने सी ++ डीएलएल में एक और मुद्दा उजागर किया है। 1234 मान C++ कोड के लिए उपलब्ध नहीं है। इसके बजाय ऑब्जेक्ट में डीएलएल के अंदर 0 का मान है। मैं डीएलएल के भीतर से ऑब्जेक्ट को संपादित करने के लिए पूर्व परिभाषित सी ++ फ़ंक्शंस का उपयोग करना चाहता हूं। किसी और मदद की बहुत सराहना की है। धन्यवाद!

+0

__declspec (dllexport) शून्य SetDLLObj (CClass obj) { धारक = &obj; } स्थानीय CClass के गले में सूचक ताकि आप उस के कबाड़ couse मिल सकता है जाएगा। – user629926

+0

धारक बाद में पुनर्प्राप्ति के लिए C++ कोड में आने वाले मान को संग्रहीत करता प्रतीत होता है। मैं इसे देख सकता हूं, लेकिन मेरी मुख्य चिंता अभी डीएलएल में पारित वस्तु से मूल्य खींच रही है। – notsodev

उत्तर

5

बॉन्ड सही था, मैं प्रबंधित और अप्रबंधित कोड के बीच एक ऑब्जेक्ट पास नहीं कर सकता और फिर भी यह अपनी संग्रहीत जानकारी को बरकरार रखता है।

मैंने ऑब्जेक्ट बनाने के लिए सी ++ फ़ंक्शंस को कॉल करना समाप्त कर दिया और पॉइंटर को वापस C# के IntPtr प्रकार में पास कर दिया। इसके बाद मैं सी # से किसी भी सी ++ फ़ंक्शन के लिए सूचक को पास कर सकता हूं (प्रदान किया गया यह बाहरी है) सी # से। यह उत्साहजनक नहीं था कि हम क्या करना चाहते थे, लेकिन यह उस उद्देश्य के लिए अपना उद्देश्य पूरा करेगा जो हमें चाहिए।

यहां सी # रैपर है जिसका मैं उदाहरण/संदर्भ के लिए उपयोग कर रहा हूं। (नोट: मैं उपरोक्त मेरे उदाहरण से 'int intestest' के बजाय स्ट्रिंगबिल्डर का उपयोग कर रहा हूं। यही वह है जो हम अपने प्रोटोटाइप के लिए चाहते थे। मैंने सादगी के लिए क्लास ऑब्जेक्ट में एक पूर्णांक का उपयोग किया।):

class LibWrapper 
{ 
    [DllImport("CPPDLL.dll")] 
    public static extern IntPtr CreateObject(); 
    [DllImport("CPPDLL.dll")] 
    public static extern void SetObjectData(IntPtr ptrObj, StringBuilder strInput); 
    [DllImport("CPPDLL.dll")] 
    public static extern StringBuilder GetObjectData(IntPtr ptrObj); 
    [DllImport("CPPDLL.dll")] 
    public static extern void DisposeObject(IntPtr ptrObj); 
} 

public static void CallDLL() 
{ 
    try 
    { 
     IntPtr ptrObj = Marshal.AllocHGlobal(4); 
     ptrObj = LibWrapper.CreateObject(); 
     StringBuilder strInput = new StringBuilder(); 
     strInput.Append("DLL Test"); 
     MessageBox.Show("Before DLL Call: " + strInput.ToString()); 
     LibWrapper.SetObjectData(ptrObj, strInput); 
     StringBuilder strOutput = new StringBuilder(); 
     strOutput = LibWrapper.GetObjectData(ptrObj); 
     MessageBox.Show("After DLL Call: " + strOutput.ToString()); 
     LibWrapper.DisposeObject(ptrObj); 
    } 
    ... 
} 

बेशक सी ++ सभी आवश्यक संशोधनों और के लिए एक ही रास्ता करता है सी # सामग्री का उपयोग करने के लिए है, और अधिक या कम, C++ के माध्यम से वांछित सामग्री अनुरोध करना होगा। सी # कोड में इस तरह से असंगत वर्ग सामग्री तक पहुंच नहीं है, जिससे यह दोनों सिरों पर कोड के लिए थोड़ा लंबा बना देता है। लेकिन यह मेरे लिए काम कर गया।

इस संदर्भ में मैं अपने समाधान के आधार के साथ आने के लिए प्रयोग किया जाता है: http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class

उम्मीद है कि इस मदद कर सकते हैं कुछ अन्य लोगों के लिए और अधिक समय की तुलना में मैं यह पता लगाने की कोशिश की थी बचाने!

0

तुम सिर्फ से सी # वर्ग का उपयोग कर रहे हैं, तो आप के लिए है कि http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx

संपादित GCHandle उपयोग करना चाहिए:

CClass *Holder; 

extern "C" 
{ 
    // obj always comes in with a 0 value. 
    __declspec(dllexport) void SetDLLObj(CClass* obj) 
    { 
     (*Holder) = (*obj); 
    } 

    // obj should leave with value of Holder (from SetDLLObj). 
    __declspec(dllexport) void GetDLLObj(__out CClass** &obj) 
    { 
     (**obj) = (*Holder); 
    } 
} 
+0

यह दिलचस्प है क्योंकि मुझे लगता है कि यह दोनों के बीच स्मृति पहुंच से संबंधित कुछ था और मैं GCHandle भर में नहीं चला है। लेकिन इसका उपयोग करके मुझे अपने CSObject को पास करने के स्थान पर एक इंटप्राट प्रकार का उपयोग करने की आवश्यकता होगी। क्या CSObject क्लास को IntPtr में परिवर्तित करने का कोई तरीका है? अन्यथा मैं कक्षा सामग्री में पास नहीं कर सकता। धन्यवाद! – notsodev

+0

आप सीसीएलएएस * या शून्य * के साथ सीसीएलएएस को प्रतिस्थापित करते हैं और आप इसे सी # पक्ष पर इंटप्राट के रूप में उपयोग कर सकते हैं। क्या आपने सी ++ पक्ष पर अपने मूल्यों को बदल दिया है? सी # क्लास CClass मार्शल स्वचालित रूप से CClass * के रूप में। आप अपनी कक्षा को संरचना के रूप में घोषित करने का प्रयास करते हैं और – user629926

+0

सार्वजनिक स्थैतिक बाहरी शून्य GetDLLObj ( CSObject csObj से बाहर) का उपयोग करते हैं; – user629926

4

मेरा मानना ​​है कि आप इस

__declspec(dllexport) void getDLLObj(__out CClass* &obj) 

की तरह अपने लौटने प्रणाली की घोषणा करनी चाहिए और क्रमशः सी # प्रोटोटाइप

public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)] 
     ref CSObject csObj); 

इनबाउंड विधि को सीसीएलएएस को भी पॉइंटर लेना चाहिए, सी # प्रोटोटाइप ठीक है।

+0

मुझे डीएलएल संकलित करने की कोशिश करते समय "अवैध संदर्भ देने के लिए सूचक" त्रुटि मिलती है। संदर्भ के रूप में सी ++ फ़ंक्शन को परिभाषित करने से सार्वजनिक वर्ग के लिए "क्लास में निजी सदस्य का निजी सदस्य" त्रुटि भी मिलती है। यदि मैं कक्षा में पॉइंटर के रूप में रिटर्निंग फ़ंक्शन को परिभाषित करता हूं तो यह सी # ऐप में आउटपुट बदलता है, हालांकि मैंने जो कक्षा जंक का उल्लेख किया है, वह डीएल में बदलकर 0 मानों में बदल गया है। क्या यह किसी अन्य मुद्दे से संबंधित हो सकता है? – notsodev

+0

मेरी गलती, यह पॉइंटर के लिए पॉइंटर या पॉइंटर का संदर्भ होना चाहिए - मैंने अपना नमूना कोड तय किया है। आपको अपने getDLLObj में पॉइंटर के संदर्भ का उपयोग करने और SetDLLObj में केवल एक पॉइंटर का उपयोग करने और पैरामीटर पर रेफ कीवर्ड जोड़कर getDLLObj के लिए अपने सी # प्रोटोटाइप को ठीक करने की आवश्यकता है। – Bond

+0

धन्यवाद बॉन्ड, आपके सुझावों ने डीएलएल के अंदर संग्रहीत जंक वैल्यू को हटाने में मदद की। हालांकि, मेरे पास अभी भी 0 मान का मेरा मुद्दा पारित किया जा रहा है, जहां यह 1234 होना चाहिए। मान लीजिए कि असम्बद्ध और प्रबंधित मेमोरी के बीच अभी भी कोई समस्या है @ user629926 लाया गया है। मैं आपकी मदद की सराहना करता हूं और कोई अन्य विचार बहुत अच्छा होगा! – notsodev

संबंधित मुद्दे