हम .NET प्रोग्राम में Unmanaged C ++ कोड का उपयोग करते हैं

छवि

आज, मैं (जैसे, शायद, कई अन्य प्रोग्रामर), मैं .NET प्लेटफ़ॉर्म और सी # भाषा का अधिक से अधिक उपयोग करता हूं, लेकिन फिर भी ऐसे कोने हैं जहां सी ++ का उपयोग उचित है। इससे उनके एकीकरण की आवश्यकता पैदा होती है।

क्यों?

ये सिर्फ मुख्य कारण हैं, सूची पूरी तरह से दूर है। लेकिन अगर जरूरत है, तो समाधान भी हैं।

हैबरकट से पहले, मैं तुरंत कहूंगा कि लेख एक को बताएगा और दिखाएगा, इस तरह की बातचीत का बिल्कुल क्लासिक तरीका नहीं है, जो कई वास्तविक अनुप्रयोगों में बेहद उपयोगी हो सकता है।

कैसे?

इनमें से प्रत्येक समाधान के अपने पेशेवरों और विपक्ष हैं। इस लेख का उद्देश्य इन दृष्टिकोणों के सभी फायदे और नुकसान का अवलोकन प्रदान करना नहीं था, लेकिन मैं उन पर संक्षेप में बात करूंगा।

पी / आह्वान - बहुत सरल लेकिन लचीला नहीं। ओओपी के लिए कोई समर्थन नहीं है, एक बार में कई तरीके बनाना सुविधाजनक नहीं है।

COM सुविधाजनक और काफी शक्तिशाली है, लेकिन COM पुस्तकालयों को सिस्टम रजिस्ट्री में पंजीकरण की आवश्यकता होती है, और इसके अलावा, स्थापना के लिए प्रशासक अधिकारों की आवश्यकता होती है (UPD: जैसा कि टिप्पणी में सही उल्लेख किया गया है, आवश्यक नहीं है)।

C ++ / CLI एक बहुत ही अच्छा एकीकरण है, एक बहुत अच्छा समाधान है। लेकिन अगर आप अपने C ++ कोड को / CLI कुंजी के साथ संकलित नहीं कर सकते हैं, तो आपको एक अतिरिक्त मध्यवर्ती C ++ / CLI लाइब्रेरी में प्रवेश करना होगा और इसमें प्रत्येक कॉल के लिए रैपर लिखना होगा। इसके अलावा, सीएलआई अक्सर कोड को जटिल करता है और इसकी कुछ सीमाएं हैं।

और कैसे?


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

चलो व्यापार के लिए नीचे उतरो।

हम दो प्रोजेक्ट बनाते हैं। एक है सी ++ (सीएलआर और एमएफसी का उपयोग किए बिना, यूनिकोड सक्षम करें)। इसे लिब के लिए उदाहरण के लिए कहा जाता है।

छवि
(नोट, डीबगिंग में आसानी के लिए, और आगे की प्रतिलिपि की आवश्यकता से बचने के लिए, आउटपुट लाइब्रेरी विंडोज फ़ोल्डर में जा रही है)।

उसके बाद हम C # प्रोजेक्ट - विंडोज फॉर्म बनाते हैं। चलो इसे net2c कहते हैं। हम परीक्षा फॉर्म जमा करते हैं:

छवि

इंटरफ़ेस से, मुझे लगता है कि यह पहले से ही स्पष्ट है कि हमारी सी ++ लाइब्रेरी लगभग क्या करेगी।

अब हम C ++ प्रोजेक्ट पर लौटते हैं और एक एकल फ़ंक्शन लिखते हैं जो हमारी वस्तुओं को बनाएगा:

//
enum EObjectType
{
Hello = 0,
GraphicAlgorithm
};
__declspec(dllexport) void * __stdcall Create(EObjectType AType)
{
if (AType == Hello) return new CHello();
if (AType == GraphicAlgorithm) return new CGraphicAlgorithm();
return NULL;
}



निर्यात किए गए कार्यों के नामों को सजाने से बचने के लिए, आपको एक और .DEF फ़ाइल बनाने और उसमें लिखने की आवश्यकता है:
EXPORTS
Create


एक विकल्प के रूप में, आवश्यक वस्तुओं को बनाने के लिए, आप एक अलग एकल वर्ग बना सकते हैं जिसे इस फ़ंक्शन द्वारा वापस किया जाएगा, लेकिन मैं सबसे सरल तरीके से जाऊंगा।

अब CHello वर्ग के लिए कोड:
//
// {08356CFE-A3DD-43c2-980C-1393E37118B2}
static const GUID IID_IHello =
{ 0x8356cfe, 0xa3dd, 0x43c2, { 0x98, 0xc, 0x13, 0x93, 0xe3, 0x71, 0x18, 0xb2 } };
class IHello : public CBaseUnknown
{
public :
STDHRESULTMETHOD SetName(LPWSTR AName) = 0;
STDHRESULTMETHOD Say(HWND AParent) = 0;
};

class CHello :
public IHello
{
public :
STDHRESULTMETHOD QueryInterface( const CLSID &AId, void ** ARet)
{
__super::QueryInterface(AId, ARet);
if (AId == IID_IHello) *ARet = (IHello*) this ;
return (*ARet != NULL) ? S_OK : E_NOINTERFACE;
}
STDHRESULTMETHOD SetName(LPWSTR AName)
{
mName = AName;
return S_OK;
}
STDHRESULTMETHOD Say(HWND AParent)
{
wstring message = L "Hello, my friend " + mName;
MessageBox(AParent, message.c_str(), L "From C++" , MB_ICONINFORMATION);
return S_OK;
}
private :
wstring mName;
};


जैसा कि आप देख सकते हैं, सब कुछ बहुत सरल है। वर्ग एकल IHello इंटरफ़ेस का समर्थन करता है और प्राथमिक तरीकों की एक जोड़ी को लागू करता है।

अब मज़ेदार भाग के लिए, C # प्रोजेक्ट पर जाएँ और लिखें:
// enum C#
public enum EObjectType : int
{
Hello = 0,
GraphicAlgorithm
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid ( "08356CFE-A3DD-43c2-980C-1393E37118B2" )]
public interface IHello
{
void SetName([MarshalAs(UnmanagedType.LPWStr)] string AName);
void Say( IntPtr AParent);
}
public class Lib
{
//
[DllImport( "Lib.dll" )]
protected static extern IntPtr Create(EObjectType AType);
//
public static object CreateObject(EObjectType AType)
{
IntPtr ptr = Create(AType);
return Marshal.GetObjectForIUnknown(ptr);
}
}



फिर से, कुछ भी जटिल नहीं है, सिवाय शायद मार्शल केGetObjectForIUn परिचित । यह फ़ंक्शन किसी COM ऑब्जेक्ट की ओर इशारा करते हुए एक IntPtr लेता है और इसके लिए एक System.Object देता है। अलग से, मैं आपका ध्यान आकर्षित करूंगा कि IHello इंटरफ़ेस कैसे घोषित किया जाता है । यहां हम संकलक को बताते हैं कि यह एक COM इंटरफ़ेस है, और उसी GUID को C ++ प्रोग्राम में लिखें।

वह सब है! अब CHello क्लास के C ++ तरीकों को बिना किसी समस्या के C # से कॉल किया जा सकता है, और आपको यह भी याद नहीं है कि इसके पीछे C ++ की बुराई और डरावना है। अपने लिए देखें:

object hiObject = Lib.CreateObject(EObjectType.Hello);
IHello hello = hiObject as IHello;
hello.SetName(txtName.Text);
hello.Say(Handle);



छवि
(सब कुछ वास्तव में काम करता है)

मिष्ठान के लिए


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

C ++ फ़ाइल
STDMETHODIMP CGraphicAlgorithm::QueryInterface( const CLSID &AId, void ** ARet)
{
__super::QueryInterface(AId, ARet);
if (AId == IID_IGraphicAlgorithm) *ARet = (CBaseUnknown*) this ;
return (*ARet != NULL) ? S_OK : E_NOINTERFACE;
}
STDMETHODIMP CGraphicAlgorithm::MakeGrayScale( void * APointer, int AWidth, int AHeight)
{
RGBQUAD *p = (RGBQUAD*)APointer;
for ( int y = 0; y < AHeight; y++)
{
for ( int x = 0; x < AWidth; x++)
{
// :)
short mid = (( short )p->rgbBlue + p->rgbGreen + p->rgbRed) / 3;
if (mid > 255) mid = 255;
BYTE v = (BYTE)mid;
memset(p, v, 3);
p++;
}
}
return S_OK;
}
STDMETHODIMP CGraphicAlgorithm::MakeAlpha( void * APointer, int AWidth, int AHeight)
{
RGBQUAD *p = (RGBQUAD*)APointer;
for ( int y = 0; y < AHeight; y++)
{
for ( int x = 0; x < AWidth; x++)
{
// , :)
memset(p, p->rgbReserved, 4);
p++;
}
}
return S_OK;
}


ज फाइल
// {65ACBBC0-45D2-4622-A779-E67ED41D2F26}
static const GUID IID_IGraphicAlgorithm =
{ 0x65acbbc0, 0x45d2, 0x4622, { 0xa7, 0x79, 0xe6, 0x7e, 0xd4, 0x1d, 0x2f, 0x26 } };
class CGraphicAlgorithm : CBaseUnknown
{
public :
STDHRESULTMETHOD MakeGrayScale( void * APointer, int AWidth, int AHeight);
STDHRESULTMETHOD MakeAlpha( void * APointer, int AWidth, int AHeight);
STDHRESULTMETHOD QueryInterface( const CLSID &AId, void ** ARet);
};



अलग-अलग, मैं इस तथ्य पर आपका ध्यान आकर्षित करता हूं कि IID_IGraphicAl एल्गोरिदम है , लेकिन IGraphicAl एल्गोरिथम इंटरफ़ेस स्वयं , जैसे कि, नहीं है। ऐसा कैसे? यह आपके साथ हमारे काम को और सरल बनाने और कम कोड लिखने के उद्देश्य से किया गया है। केवल एक चीज जो यहां पर विचार करने के लिए महत्वपूर्ण है, वह यह है कि सभी वर्चुअल तरीके जो IGraphicAl एल्गोरिदम इंटरफ़ेस के हैं, उन्हें कक्षा की शुरुआत में और सख्त क्रम में जाना चाहिए। इसके अलावा, तब वर्ग कई अलग-अलग इंटरफेस प्रदान नहीं कर सकेगा (जिनकी हमें आवश्यकता नहीं है, यह एक और वर्ग बनाने के लिए अधिक सुविधाजनक और तार्किक है)।

तेज करें और इस कोड को लिखें:
IGraphicAlgorithm utils = Lib.CreateObject(EObjectType.GraphicAlgorithm) as IGraphicAlgorithm;

BitmapData data = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

if (optGray.Checked)
utils.MakeGrayScale(data.Scan0, data.Width, data.Height);
else
utils.MakeAlpha(data.Scan0, data.Width, data.Height);

bmp.UnlockBits(data);
picDest.Image = bmp;
picSrc.Refresh();
picDest.Refresh();


यहां, हम पहले मेमोरी को एक पॉइंटर प्राप्त करते हैं जहां बिटमैप डेटा संग्रहीत किया जाता है, और फिर हम आगे की प्रक्रिया के लिए इस सूचक को C ++ लाइब्रेरी को देते हैं। मैं आपको तुरंत चेतावनी देता हूं कि यह सबसे प्रभावी और सुविधाजनक तरीका नहीं है, लेकिन यदि आप विषय में रुचि रखते हैं, तो मैं आपको एक अलग लेख में स्मृति में छवियों के साथ सीधे काम करने के बारे में बता सकता हूं।

परिणामस्वरूप:
छवि

परिणाम


देखें हमें क्या मिला:
PS पूर्ण स्रोत कोड यहां डाउनलोड किए जा सकते हैं: http://66bit.ru/files/paper/net2c/net2c.zip
PPS अगला लेख उलटा समस्या के बारे में होगा - C ++ में C # पुस्तकालयों का प्रत्यक्ष उपयोग

युपीडी। भाग दो का विमोचन

Source: https://habr.com/ru/post/In101589/


All Articles