2009-07-06 12 views
11

पर कनवर्ट करें मैं एक इंटिप्ट में आरईसीटी संरचना (नीचे दिया गया) की एक सरणी को कन्वर्ट करने की कोशिश कर रहा हूं, इसलिए मैं पोस्टमेसेज का उपयोग किसी अन्य एप्लिकेशन पर पॉइंटर भेज सकता हूं।structs को intPtr

[StructLayout(LayoutKind.Sequential)] 
public struct RECT 
{ 
    public int Left; 
    public int Top; 
    public int Right; 
    public int Bottom; 

    // lots of functions snipped here 
} 

// so we have something to send, in reality I have real data here 
// also, the length of the array is not constant 
RECT[] foo = new RECT[4]; 
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(foo[0]) * 4); 
Marshal.StructureToPtr(foo, ptr, true); // -- FAILS 

यह अंतिम पंक्ति पर एक ArgumentException देता है ("निर्दिष्ट संरचना blittable होना चाहिए या लेआउट जानकारी नहीं है।")। मुझे PostMessage का उपयोग करके किसी अन्य एप्लिकेशन पर RECTs की इस सरणी को किसी भी तरह से प्राप्त करने की आवश्यकता है, इसलिए मुझे वास्तव में इस डेटा के लिए एक पॉइंटर चाहिए।

यहां मेरे विकल्प क्या हैं?

अद्यतन: यह काम करने के लिए लगता है:

IntPtr result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.RECT)) * foo.Length); 
IntPtr c = new IntPtr(result.ToInt32()); 
for (i = 0; i < foo.Length; i++) 
{ 
    Marshal.StructureToPtr(foo[i], c, true); 
    c = new IntPtr(c.ToInt32() + Marshal.SizeOf(typeof(Win32.RECT))); 
} 

UPDATED फिर ठीक करने के लिए क्या मध्यस्थ पर टिप्पणी की।

+0

क्या संदेश आपको लगता है कि स्वचालित रूप से पोस्ट कर रहे हैं 4 rects की एक सरणी का करता पार प्रक्रिया marshaling? –

+0

मैं स्क्रीन के कुछ क्षेत्रों को अनदेखा करने के लिए एक डीएलएल (जिसे दूसरी प्रक्रिया में होस्ट किया गया है क्योंकि यह 64-बिट है) बताने का प्रयास कर रहा हूं। यह आवश्यक 4 आरईसीटी नहीं है। –

+0

आपके अपडेट के अनुसार, आप पर्याप्त स्थान आवंटित नहीं करते हैं (मार्शल.SizeOf (टाइपऑफ (आरईसीटी) के बजाय intptr.size)। और आप पॉइंटर अंकगणित x64 मशीनों पर असफल हो सकते हैं, मेरा जवाब देखें। – arbiter

उत्तर

12

संरचना ToPtr संरचना ऑब्जेक्ट की अपेक्षा करता है, और foo संरचना नहीं है यह सरणी है, यही कारण है कि अपवाद होता है।

मैं तुम्हें सुझाव है चक्र (दुर्भाग्य से, StructureToPtr सूचकांक के साथ अधिभार नहीं है) में संरचनाओं लिखने के लिए कर सकते हैं:

long LongPtr = ptr.ToInt64(); // Must work both on x86 and x64 
for (int I = 0; I < foo.Length; I++) 
{ 
    IntPtr RectPtr = new IntPtr(LongPtr); 
    Marshal.StructureToPtr(foo[I], RectPtr, false); // You do not need to erase struct in this case 
    LongPtr += Marshal.SizeOf(typeof(Rect)); 
} 

एक अन्य विकल्प Marshal.WriteInt32 चार पूर्णांक के रूप में संरचना लिखने के लिए, का उपयोग कर रहा है:

for (int I = 0; I < foo.Length; I++) 
{ 
    int Base = I * sizeof(int) * 4; 
    Marshal.WriteInt32(ptr, Base + 0, foo[I].Left); 
    Marshal.WriteInt32(ptr, Base + sizeof(int), foo[I].Top); 
    Marshal.WriteInt32(ptr, Base + sizeof(int) * 2, foo[I].Right); 
    Marshal.WriteInt32(ptr, Base + sizeof(int) * 3, foo[I].Bottom); 
} 

और आखिरी, आप असुरक्षित कीवर्ड का उपयोग कर सकते हैं, और सीधे पॉइंटर्स के साथ काम कर सकते हैं।

0

आप निम्न की कोशिश कर सकते:

RECT[] rects = new RECT[ 4 ]; 
IntPtr[] pointers = new IntPtr[4]; 
IntPtr result = Marshal.AllocHGlobal(IntPtr.Size * rects.Length); 
for (int i = 0; i < rects.Length; i++) 
{ 
    pointers[i] = Marshal.AllocHGlobal (IntPtr.Size); 
    Marshal.StructureToPtr(rects[i], pointers[i], true); 
    Marshal.WriteIntPtr(result, i * IntPtr.Size, pointers[i]); 
} 
// the array that you need is stored in result 

और बाद आप समाप्त कर सब कुछ खाली करने के लिए मत भूलना।

+3

सिवाय इसके कि यह आरईसीटी के लिए आरईसीटी की एक सरणी नहीं है। और आप प्रत्येक पॉइंटर्स के लिए स्मृति के पॉइंटर आकार के ब्लॉक आवंटित कर रहे हैं [i] लेकिन फिर उस स्थान पर 16 बाइट्स (आकार (आरईसीटी)) मार्शल कर रहे हैं। इस प्रकार का अतिप्रवाह आपदा के लिए एक नुस्खा है। –

1

आर्बिटर ने आपको structs के मार्शल सरणी के लिए एक अच्छा जवाब दिया है। इस तरह के ब्लिटेबल structs के लिए, व्यक्तिगत रूप से, प्रत्येक तत्व को मैन्युअल रूप से अप्रबंधित स्मृति में मार्शल करने के बजाय असुरक्षित कोड का उपयोग करेगा। कुछ ऐसा:

RECT[] foo = new RECT[4]; 
unsafe 
{ 
    fixed (RECT* pBuffer = foo) 
    { 
     //Do work with pointer 
    } 
} 

या आप एक जीसीएचंडल का उपयोग करके सरणी पिन कर सकते हैं।

दुर्भाग्यवश, आप कहते हैं कि आपको यह जानकारी किसी अन्य प्रक्रिया में भेजने की आवश्यकता है। यदि आप जिस संदेश को पोस्ट कर रहे हैं वह उन लोगों में से एक नहीं है जिनके लिए विंडोज स्वचालित मार्शलिंग प्रदान करता है तो आपको एक और समस्या है। चूंकि सूचक स्थानीय प्रक्रिया से संबंधित है, इसका मतलब है कि रिमोट प्रक्रिया में कुछ भी नहीं है और इस सूचक के साथ एक संदेश पोस्ट करने से अप्रत्याशित व्यवहार होगा, जिसमें संभावित प्रोग्राम क्रैश भी शामिल है। तो आपको जो करना है वह आरईसीटी सरणी को दूसरी प्रक्रिया 'मेमोरी' लिखना है। ऐसा करने के लिए आपको प्रक्रिया को संभालने के लिए ओपनप्रोसेस का उपयोग करने की आवश्यकता है, अन्य प्रक्रिया में स्मृति आवंटित करने के लिए VitualAllocEx और फिर अन्य प्रक्रिया 'वर्चुअल मेमोरी में सरणी लिखने के लिए WriteProcessMemory।

दुर्भाग्य से फिर से, यदि आप 32 बिट प्रक्रिया से 32 बिट प्रक्रिया में या 64 बिट प्रक्रिया से 64 बिट प्रक्रिया तक जा रहे हैं तो चीजें काफी सरल हैं लेकिन 32 बिट प्रक्रिया से 64 बिट प्रक्रिया तक चीजें थोड़ा बालों वाली हो सकती हैं। VirtualAllocEx और WriteProcessMemory वास्तव में 32 से 64 तक समर्थित नहीं हैं।आपको 64 बिट मेमोरी स्पेस के नीचे 4 जीबी में अपनी मेमोरी आवंटित करने के लिए VirtualAllocEx को मजबूर करने की कोशिश करके सफलता प्राप्त हो सकती है ताकि परिणामी सूचक 32 बिट प्रक्रिया एपीआई कॉल के लिए मान्य हो और फिर उस पॉइंटर के साथ लिखें। इसके अतिरिक्त, आपके पास दो प्रक्रिया प्रकारों के बीच संरचना आकार और पैकिंग अंतर हो सकते हैं। आरईसीटी के साथ कोई समस्या नहीं है लेकिन 64 बिट स्ट्रक्चर लेआउट से मेल खाने के लिए पैकिंग या संरेखण के मुद्दों के साथ कुछ अन्य structs को 64 बिट प्रक्रिया में क्षेत्र द्वारा मैन्युअल रूप से लिखे जाने की आवश्यकता हो सकती है।