2009-02-05 6 views
8

मेरे पास एक डेल्फी डीएलएल है जिसे मैंने नहीं लिखा था, लेकिन सी # एएसपी.नेट 3.5 ऐप से कॉल करने की आवश्यकता है।सी # से डेल्फी डीएलएल को कॉल करना अप्रत्याशित परिणाम उत्पन्न करता है

function CreateCode(SerialID : String; 
    StartDateOfYear, YearOfStartDate, YearOfEndDate, DatePeriod : Word; 
    CodeType,RecordNumber,StartHour,EndHour : Byte) : PChar; 
    external 'CreateCodeDLL.dll'; 

और यहाँ मेरी सी # कोड है:: यहाँ समारोह परिभाषा मैं डेवलपर्स से मिल गया है

[DllImport("CreateCodeDLL.dll", 
    CallingConvention = CallingConvention.StdCall, 
    CharSet=CharSet.Ansi)] 
public static extern IntPtr CreateCode(string SerialID, 
             UInt16 StartDateOfYear, 
             UInt16 YearOfStartDate, 
             UInt16 YearOfEndDate, 
             UInt16 DatePeriod, 
             Byte CodeType, 
             Byte RecordNumber, 
             Byte StartHour, 
             Byte EndHour); 

और अंत में, इस विधि के लिए अपने कॉल:

//The Inputs 
String serialID = "92F00000B4FBE"; 
UInt16 StartDateOfYear = 20; 
UInt16 YearOfStartDate = 2009; 
UInt16 YearOfEndDate = 2009; 
UInt16 DatePeriod = 7; 
Byte CodeType = 1; 
Byte RecordNumber = 0; 
Byte StartHour = 15; 
Byte EndHour = 14;    

// The DLL call 
IntPtr codePtr = CodeGenerator.CreateCode(serialID, StartDateOfYear, 
       YearOfStartDate, YearOfEndDate, DatePeriod, CodeType, 
       RecordNumber, StartHour, EndHour); 

// Take the pointer and extract the code in a string 
String code = Marshal.PtrToStringAnsi(codePtr); 

हर बार मैं इस सटीक कोड को फिर से संकलित करता हूं और इसे चलाता हूं, यह एक अलग मूल्य देता है। अपेक्षित मूल्य 10 अंकों वाला कोड है जिसमें संख्याएं शामिल हैं। लौटा मूल्य वास्तव में 12 अंक है।

जानकारी का अंतिम महत्वपूर्ण भाग यह है कि मेरे पास एक परीक्षण है .EXE जिसमें एक जीयूआई है जो मुझे डीएलएल का परीक्षण करने की अनुमति देती है। .EXE का उपयोग करके प्रत्येक परीक्षण एक ही 10-अंकीय संख्या (अपेक्षित परिणाम) देता है।

तो, मुझे विश्वास करना है कि मैंने डीएलएल को गलत तरीके से कॉल किया है। विचार?

उत्तर

22

डेल्फी डिफ़ॉल्ट रूप से इतना fastcall बुलाया बुला सम्मेलन का उपयोग करता है। इसका मतलब यह है कि संकलक सीपीयू रजिस्टरों में किसी फ़ंक्शन पर पैरामीटर पास करने का प्रयास करता है और केवल फ्री रजिस्टरों की तुलना में अधिक पैरामीटर होने पर केवल स्टैक का उपयोग करता है। उदाहरण के लिए डेल्फी किसी फ़ंक्शन के पहले तीन पैरामीटर के लिए (ईएक्स, ईडीएक्स, ईसीएक्स) का उपयोग करता है।
अपने सी # कोड में आप वास्तव में stdcall बुला सम्मेलन है, जो संकलक ढेर के माध्यम से मानकों को पारित करने के लिए निर्देश देता है का उपयोग कर रहे (उलटे क्रम में, यानी पिछले परम पहले धकेल दिया जाता है) और कॉल प्राप्त करने वाला सफाई जाने के लिए ढेर ।
इसके विपरीत, cdecl सी/सी ++ कंपाइलर्स द्वारा उपयोग किए जाने वाले कॉलिंग कॉलर को स्टैक को साफ़ करने के लिए मजबूर करता है।
बस सुनिश्चित करें कि आप दोनों तरफ एक ही कॉलिंग सम्मेलन का उपयोग कर रहे हैं। Stdcall ज्यादातर उपयोग किया जाता है क्योंकि इसे लगभग हर जगह इस्तेमाल किया जा सकता है और प्रत्येक कंपाइलर द्वारा समर्थित है (Win32 एपीआई भी इस सम्मेलन का उपयोग करते हैं)।
ध्यान दें कि फास्टकॉल किसी भी तरह से .NET द्वारा समर्थित नहीं है।

+4

ध्यान दें कि "फास्टकॉल" का अर्थ विभिन्न संदर्भों में अलग-अलग चीजें हैं। माइक्रोसॉफ्ट का संस्करण Embarcadero के समान नहीं है, और मुझे संदेह है कि जीसीसी दोनों से अलग है। डेल्फी में, कॉलिंग सम्मेलन को "फास्टकॉल" भी नहीं कहा जाता है; यह "रजिस्टर" कॉलिंग सम्मेलन है। –

1

मैं इस कभी नहीं किया है, लेकिन करने के लिए अपने कोड बदलने का प्रयास किया है:

function CreateCode(SerialID : String; 
    StartDateOfYear, YearOfStartDate, YearOfEndDate, DatePeriod : Word; 
    CodeType,RecordNumber,StartHour,EndHour : Byte) : PChar; stdcall; 
    external 'CreateCodeDLL.dll'; 

नोट अतिरिक्त stdcall।

संपादित 2: जैसा कि आप अन्य उत्तरों से देख सकते हैं, आपको या तो ऊपर परिवर्तन करना है या एक रैपर डीएल लिखना है जो एक ही काम करता है।

+0

पास्कल कॉल प्रकार क्योंकि डेल्फी रजिस्टर कॉल (भी __fastcall जो __msfastcall नहीं है के रूप में जाना जाता है) का उपयोग करता है काम नहीं होगा। –

+0

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.callingconvention.aspx के अनुसार कोई भी फास्टकॉल .NET द्वारा समर्थित नहीं है। तो इसका मतलब है कि उन्हें उन्हें पास्कल कोड बदलने के लिए कहा है? – PetriW

16

जेएन सही है। दिए गए फ़ंक्शन प्रोटोटाइप को आसानी से सी # से सीधे नहीं कहा जा सकता है जब तक कि यह डेल्फी के register कॉलिंग सम्मेलन में है। इसके लिए आपको stdcall रैपर फ़ंक्शन लिखने की आवश्यकता है - शायद आपके पास कोई अन्य डीएलएल नहीं है यदि आपके पास स्रोत नहीं है - या आपको उन लोगों को प्राप्त करने की आवश्यकता है जो अपने कॉलिंग सम्मेलन को stdcall पर बदलने के लिए फ़ंक्शन को बनाए रखते हैं।

अद्यतन: मैं यह भी देखता हूं कि पहला तर्क डेल्फी स्ट्रिंग है। यह ऐसा कुछ नहीं है जो सी # या तो आपूर्ति कर सकता है। यह इसके बजाय एक पीसीहर होना चाहिए।साथ ही, यह स्पष्ट होना महत्वपूर्ण है कि फ़ंक्शन अंसी या यूनिकोड है; यदि डीएलएल डेल्फी 200 (या बाद में) के साथ लिखा गया है, तो यह यूनिकोड है, अन्यथा यह अंसी है।

0

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

2

वापसी मूल्य एक और समस्या हो सकती है। यह शायद या तो एक स्मृति रिसाव है (वे ढेर पर एक बफर आवंटित करते हैं और कभी भी इसे मुक्त नहीं करते हैं) या पहले से ही मुफ्त मेमोरी तक पहुंच (वे स्थानीय स्ट्रिंग वैरिएबल कास्ट पीसीहर में लौटते हैं)।

फ़ंक्शन स्ट्रिंग (या सामान्य रूप से परिवर्तनीय आकार का डेटा) किसी फ़ंक्शन से दूसरे मॉड्यूल में सामान्य रूप से समस्याग्रस्त है।

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

एक और संभावित समाधान बफर को फ़ंक्शन में ढेर से आवंटित करना और इसे वापस करना है। फिर आपको एक और फ़ंक्शन निर्यात करने की आवश्यकता है जिसे कॉलर को आवंटित स्मृति को फिर से मुक्त करने के लिए उपयोग करना चाहिए। यह सुनिश्चित करता है कि स्मृति को उसी रनटाइम द्वारा मुक्त किया जाता है जो इसे आवंटित करता है।

विभिन्न (बोर्लैंड) भाषाओं के बीच एक (डेल्फी) स्ट्रिंग पैरामीटर पास करना शायद असंभव है। और डेल्फी मॉड्यूल के बीच भी आप यह सुनिश्चित करने के लिए दोनों मॉड्यूल मेमोरी मैनेजर के एक ही उदाहरण का उपयोग करते हैं। आमतौर पर इसका मतलब है कि सभी मॉड्यूल के पहले उपयोग के रूप में "ShareMem का उपयोग करता है" जोड़ना। एक और अंतर कॉलिंग सम्मेलन "रजिस्टर" है जो एक फास्टकॉल सम्मेलन है, लेकिन फास्टकॉल एमएस कंपाइलर्स के उपयोग के समान नहीं है।

डेल्फी डीएलएल में से एक के साथ डेल्फी डीएल को एक पूरी तरह से अलग समाधान का पुन: संकलित किया जा सकता है। यह कितना काम उनके कोड पर निर्भर करता है।

1

डेल्फी में एक COM wrapper बनाएं और इंटरप के माध्यम से अपने सी # कोड में कॉल करें। वोला .. सी # या किसी अन्य भविष्य के मंच से उपयोग करना आसान है।

1

मैं अन्य दिन कॉन्फ़्रेंसिंग के बारे में जानने की कोशिश कर रहा था और मैंने विभिन्न तरीकों के बीच परिवर्तित करने के लिए कुछ तरीकों को लिखा था। यहां StdCall-> FastCall के लिए एक है।

typedef struct 
{ 
    USHORT ParameterOneOffset; // The offset of the first parameter in dwords starting at one 
    USHORT ParameterTwoOffset; // The offset of the second parmaeter in dwords starting at one 
} FastCallParameterInfo; 



    __declspec(naked,dllexport) void __stdcall InvokeFast() 
{ 
    FastCallParameterInfo paramInfo; 
    int functionAddress; 
    int retAddress; 
    int paramOne, paramTwo; 
    __asm 
    { 
     // Pop the return address and parameter info. Store in memory. 
     pop retAddress; 
     pop paramInfo; 
     pop functionAddress; 

     // Check if any parameters should be stored in edx       
     movzx ecx, paramInfo.ParameterOneOffset;  
     cmp ecx,0; 
     je NoRegister; 

     // Calculate the offset for parameter one. 
     movzx ecx, paramInfo.ParameterOneOffset; // Move the parameter one offset to ecx 
     dec ecx;         // Decrement by 1 
     mov eax, 4;         // Put 4 in eax 
     mul ecx;         // Multiple offset by 4 

     // Copy the value from the stack on to the register. 
     mov ecx, esp;        // Move the stack pointer to ecx 
     add ecx, eax;        // Subtract the offset. 
     mov eax, ecx;        // Store in eax for later. 
     mov ecx, [ecx];        // Derefernce the value 
     mov paramOne, ecx;       // Store the value in memory. 

     // Fix up stack 
     add esp,4;         // Decrement the stack pointer 
     movzx edx, paramInfo.ParameterOneOffset; // Move the parameter one offset to edx 
     dec edx;         // Decrement by 1 
     cmp edx,0;         // Compare offset with zero 
     je ParamOneNoShift;       // If first parameter then no shift. 

    ParamOneShiftLoop: 
     mov ecx, eax; 
     sub ecx, 4; 
     mov ecx, [ecx] 
     mov [eax], ecx;        // Copy value over 
     sub eax, 4;         // Go to next 
     dec edx;         // decrement edx 
     jnz ParamOneShiftLoop;      // Loop 
    ParamOneNoShift: 
     // Check if any parameters should be stored in edx       
     movzx ecx, paramInfo.ParameterTwoOffset;  
     cmp ecx,0; 
     je NoRegister; 

     movzx ecx, paramInfo.ParameterTwoOffset; // Move the parameter two offset to ecx 
     sub ecx, 2;         // Increment the offset by two. One extra for since we already shifted for ecx 
     mov eax, 4;         // Put 4 in eax 
     mul ecx;         // Multiple by 4 

     // Copy the value from the stack on to the register. 
     mov ecx, esp;        // Move the stack pointer to ecx 
     add ecx, eax;        // Subtract the offset. 
     mov eax, ecx;        // Store in eax for later. 
     mov ecx, [ecx];        // Derefernce the value 
     mov paramTwo, ecx;       // Store the value in memory.   

     // Fix up stack 
     add esp,4;         // Decrement the stack pointer 
     movzx edx, paramInfo.ParameterTwoOffset; // Move the parameter two offset to ecx 
     dec edx;         // Decrement by 1 
     cmp edx,0;         // Compare offset with zero 
     je NoRegister;        // If first parameter then no shift. 
    ParamTwoShiftLoop: 
     mov ecx, eax; 
     sub ecx, 4; 
     mov ecx, [ecx] 
     mov [eax], ecx;        // Copy value over 
     sub eax, 4;         // Go to next 
     dec edx;         // decrement edx 
     jnz ParamTwoShiftLoop;      // Loop 


    NoRegister: 
     mov ecx, paramOne;       // Copy value from memory to ecx register 
     mov edx, paramTwo;       // 
     push retAddress; 
     jmp functionAddress; 
    } 
} 

}

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