यह एक ऐसी समस्या है जो विशेष रूप से ARM पर होती है, x86 या x64 पर नहीं। मेरे पास एक उपयोगकर्ता द्वारा रिपोर्ट की गई यह समस्या थी और विंडोज़ IoT के माध्यम से रास्पबेरी पाई 2 पर UWP का उपयोग करके इसे पुन: उत्पन्न करने में सक्षम था। मैंने इस तरह की समस्या को बेमेल कॉलिंग कन्वेंशन से पहले देखा है, लेकिन मैं P / Invoke घोषणा में Cdecl को निर्दिष्ट कर रहा हूं और मैंने उसी परिणामों के साथ देशी पक्ष पर __cdecl को जोड़ने का स्पष्ट प्रयास किया। यहाँ कुछ जानकारी है:
पी / आह्वान घोषणा ( संदर्भ ):
[DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern FLSliceResult FLEncoder_Finish(FLEncoder* encoder, FLError* outError);
C # संरचना ( संदर्भ ):
internal unsafe partial struct FLSliceResult
{
public void* buf;
private UIntPtr _size;
public ulong size
{
get {
return _size.ToUInt64();
}
set {
_size = (UIntPtr)value;
}
}
}
internal enum FLError
{
NoError = 0,
MemoryError,
OutOfRange,
InvalidData,
EncodeError,
JSONError,
UnknownValue,
InternalError,
NotFound,
SharedKeysStateError,
}
internal unsafe struct FLEncoder
{
}
C हैडर में फ़ंक्शन ( संदर्भ )
FLSliceResult FLEncoder_Finish(FLEncoder, FLError*);
FLSliceResult कुछ समस्याओं का कारण हो सकता है क्योंकि यह मूल्य द्वारा लौटाया जाता है और देशी तरफ कुछ C ++ सामान है?
देशी पक्ष की संरचना में वास्तविक जानकारी होती है, लेकिन C API के लिए, FLEncoder को अपारदर्शी सूचक के रूप में परिभाषित किया गया है । X86 और x64 चीजों पर उपरोक्त विधि को सुचारू रूप से काम करते समय, लेकिन ARM पर, मैं निम्नलिखित निरीक्षण करता हूं। पहले तर्क का पता SECOND तर्क का पता है, और दूसरा तर्क अशक्त है (उदाहरण के लिए, जब मैं C # पक्ष के पते पर लॉग इन करता हूं, उदाहरण के लिए, 0x054f59b8 और 0x0583fbbc, लेकिन फिर मूल पक्ष में तर्क 0x0583f3bc और 0x00000000) हैं। इस तरह की आउट ऑफ ऑर्डर समस्या क्या हो सकती है? क्या किसी के पास कोई विचार है, क्योंकि मैं स्तब्ध हूं ...
यहां वह कोड है जिसे मैं पुन: पेश करने के लिए चलाता हूं:
unsafe {
var enc = Native.FLEncoder_New();
Native.FLEncoder_BeginDict(enc, 1);
Native.FLEncoder_WriteKey(enc, "answer");
Native.FLEncoder_WriteInt(enc, 42);
Native.FLEncoder_EndDict(enc);
FLError err;
NativeRaw.FLEncoder_Finish(enc, &err);
Native.FLEncoder_Free(enc);
}
निम्नलिखित कार्यों के साथ C ++ ऐप चलाना ठीक है:
auto enc = FLEncoder_New();
FLEncoder_BeginDict(enc, 1);
FLEncoder_WriteKey(enc, FLSTR("answer"));
FLEncoder_WriteInt(enc, 42);
FLEncoder_EndDict(enc);
FLError err;
auto result = FLEncoder_Finish(enc, &err);
FLEncoder_Free(enc);
यह तर्क नवीनतम डेवलपर बिल्ड के साथ क्रैश को ट्रिगर कर सकता हैलेकिन दुर्भाग्य से मुझे अभी तक यह पता नहीं चला है कि Nuget के माध्यम से देशी डिबग प्रतीकों को कैसे प्रदान किया जा सकता है, जैसे कि इसके माध्यम से कदम रखा जा सकता है (केवल स्रोत से सब कुछ ऐसा लगता है ...) इसलिए डिबगिंग थोड़ा अजीब है - दोनों मूल और प्रबंधित घटकों का निर्माण करने की आवश्यकता है। मैं सुझाव देने के लिए तैयार हूं कि यह कैसे आसान है, हालांकि अगर कोई कोशिश करना चाहता है। लेकिन अगर किसी ने पहले यह अनुभव किया है या ऐसा क्यों होता है इसके बारे में कोई विचार है, तो कृपया एक उत्तर जोड़ें, धन्यवाद! बेशक, अगर किसी को एक प्रजनन का मामला चाहिए (या तो एक निर्माण करने के लिए आसान है जो स्रोत को एक कदम नहीं प्रदान करता है या एक को बनाने के लिए कठिन है) तो एक टिप्पणी छोड़ दें लेकिन मैं एक बनाने की प्रक्रिया से नहीं गुजरना चाहता हूं अगर कोई इसका उपयोग करने वाला नहीं है (मुझे यकीन नहीं है कि वास्तविक एआरएम पर विंडोज सामान चलाना कितना लोकप्रिय है)
संपादित करें दिलचस्प अपडेट: यदि मैं C # में हस्ताक्षर "नकली" करता हूं और 2 पैरामीटर हटाता हूं, तो पहला ओके के माध्यम से आता है।
संपादित करें 2 दूसरा दिलचस्प अद्यतन: यदि मैं से आकार के सी # FLSliceResult परिभाषा बदलने UIntPtr
के लिए ulong
तो तर्क को सही ढंग से में आते हैं ... जो मतलब नहीं है के बाद से size_t
एआरएम पर अहस्ताक्षरित पूर्णांक होना चाहिए।
EDIT 3[StructLayout(LayoutKind.Sequential, Size = 12)]
C # में परिभाषा को जोड़ने से भी यह काम होता है, लेकिन क्यों? इस आर्किटेक्चर के लिए C / C ++ में sizeof (FLSliceResult) 8 जैसा चाहिए वैसा ही देता है। C # में समान आकार सेट करना क्रैश का कारण बनता है, लेकिन इसे 12 पर सेट करने से यह काम करता है।
EDIT 4 मैंने टेस्ट केस को कम से कम किया ताकि मैं C ++ टेस्ट केस भी लिख सकूं। C # UWP में यह विफल रहता है, लेकिन C ++ UWP में यह सफल होता है।
EDIT 5 यहाँ तुलना के लिए C ++ और C # दोनों के लिए अलग-अलग निर्देश दिए गए हैं (हालाँकि C # मुझे यकीन नहीं है कि कितना लेना है इसलिए मैंने बहुत अधिक लेने के पक्ष में मिटा दिया)
EDIT 6 आगे के विश्लेषण से पता चलता है कि "अच्छा" चलाने के दौरान जब मैं झूठ बोलता हूं और कहता हूं कि संरचना C # पर 12 बाइट्स है, तो r0 रजिस्टर करने के लिए रिटर्न वैल्यू पास हो जाती है, साथ ही दो अन्य args r1, r2 के माध्यम से आते हैं। हालाँकि, बुरे समय में, इसे इस तरह से स्थानांतरित कर दिया जाता है कि दो args r0, r1 के माध्यम से आ रहे हैं और रिटर्न वैल्यू कहीं और है (स्टैक पॉइंटर?)
EDIT 7 मैंने ARM आर्किटेक्चर के लिए प्रक्रिया कॉल मानक से परामर्श किया । मुझे यह उद्धरण मिला: "एक कम्पोजिट प्रकार 4 बाइट्स से बड़ा है, या जिसका आकार कॉलर और कैलली दोनों द्वारा सांख्यिकीय रूप से निर्धारित नहीं किया जा सकता है, एक मेमोरी में एक अतिरिक्त तर्क के रूप में पारित होने पर संग्रहीत किया जाता है जब फ़ंक्शन को बुलाया गया था (§5.5, रूल ए) .4)। परिणाम के लिए उपयोग की जाने वाली मेमोरी को फ़ंक्शन कॉल के दौरान किसी भी बिंदु पर संशोधित किया जा सकता है। " इसका तात्पर्य यह है कि r0 में गुजरना सही व्यवहार है क्योंकि अतिरिक्त तर्क का अर्थ है पहला (क्योंकि C कॉलिंग कन्वेंशन के पास तर्कों की संख्या निर्दिष्ट करने का कोई तरीका नहीं है)। मुझे आश्चर्य है कि क्या सीएलआर मूलभूत के बारे में एक और नियम के साथ भ्रमित कर रहा है 64-बिट डेटा प्रकार: "एक डबल-शब्द आकार का फंडामेंटल डेटा प्रकार (जैसे, लंबे लंबे, डबल और 64-बिट कंटेनरीकृत वैक्टर) r0 और r1 में वापस किया जाता है।"
EDIT 8 ओके यहाँ गलत काम करने के लिए CLR को इंगित करने के लिए बहुत सारे सबूत हैं, इसलिए मैंने एक बग रिपोर्ट दर्ज की । मुझे उम्मीद है कि कोई भी उस रेपो पर मुद्दों को पोस्ट करने वाले सभी स्वचालित बॉट के बीच इसे नोटिस करता है: -एस।