2009-03-09 22 views
6

एक पटकथा भाषा (सी में लिखा) से Win32 एपीआई के उपयोग की अनुमति के लिए, मैं निम्नलिखित जैसे एक समारोह लिखने के लिए करना चाहते हैं:Win32 फ़ंक्शन को कॉल करने के लिए मैं एक सामान्य सी फ़ंक्शन कैसे लिख सकता हूं?

void Call(LPCSTR DllName, LPCSTR FunctionName, 
    LPSTR ReturnValue, USHORT ArgumentCount, LPSTR Arguments[]) 

जो सामान्य रूप से, फोन करेगा, किसी भी Win32 एपीआई समारोह।

(एलपीस्ट्रेट पैरामीटर अनिवार्य रूप से बाइट एरे के रूप में उपयोग किए जा रहे हैं - मान लीजिए कि उन्हें सही डेटा प्रकार को सही करने के लिए सही ढंग से आकार दिया गया है। साथ ही मेरा मानना ​​है कि कुछ अतिरिक्त जटिलता को पॉइंटर और गैर- सूचक तर्क लेकिन मैं इस प्रश्न के प्रयोजनों के लिए इसे अनदेखा कर रहा हूं)।

मेरी समस्या यह है कि Win32 API फ़ंक्शंस में तर्क पारित कर रहा है। चूंकि ये stdcall हैं I varargs का उपयोग नहीं कर सकते हैं इसलिए 'कॉल' के कार्यान्वयन को पहले से तर्कों की संख्या के बारे में पता होना चाहिए और इसलिए यह सामान्य नहीं हो सकता है ...

मुझे लगता है कि मैं इसे असेंबली कोड (तर्कों पर लूपिंग, प्रत्येक को स्टैक पर धक्का देना) लेकिन क्या यह शुद्ध सी में संभव है?

अद्यतन: मैंने अभी तक स्वीकृत के रूप में 'नहीं यह संभव नहीं है' उत्तर चिह्नित किया है। यदि सी-आधारित समाधान प्रकाश में आता है तो मैं निश्चित रूप से इसे बदल दूंगा।

अद्यतन:ruby/dl ऐसा लगता है कि इसे उपयुक्त तंत्र का उपयोग करके कार्यान्वित किया जा सकता है। इस पर किसी भी विवरण की सराहना की जाएगी।

उत्तर

5

नहीं, मुझे नहीं लगता कि कुछ असेंबली लिखने के बिना ऐसा करना संभव नहीं है। कारण है कि आपको लक्ष्य फ़ंक्शन को कॉल करने से पहले स्टैक पर क्या सटीक नियंत्रण की आवश्यकता है, और शुद्ध सी में ऐसा करने का कोई वास्तविक तरीका नहीं है। हालांकि, यह असेंबली में करना आसान है।

इसके अलावा, आप इन सभी तर्कों के लिए पीसीस्ट्रेट का उपयोग कर रहे हैं, जो वास्तव में केवल const char * है। लेकिन चूंकि ये सभी तर्क तार नहीं हैं, आप वास्तव में वापसी मूल्य के लिए और तर्क के लिए उपयोग करना चाहते हैं [] void * या LPVOID है। यह वह प्रकार है जिसका उपयोग आप char * पर कास्टिंग करने के बजाय तर्कों के सही प्रकार को नहीं जानते हैं।

+0

मैं स्ट्रिंग के बजाए बाइट्स की सरणी के रूप में char * का उपयोग कर रहा हूं। धारणा यह है कि मैं 'कॉल' फ़ंक्शन में मेटाडेटा उपलब्ध कराता हूं जो वास्तविक प्रकारों (यानी बाइट्स की संख्या) के आकारों का वर्णन करता है जो आवश्यक हैं। उस अर्थ में * प्रकार * ज्ञात हैं - वे बाइट्स की एक ज्ञात संख्या हैं। –

+0

सामान्य रूप से आपको कॉल करने के लिए बाइट्स की संख्या से अधिक की आवश्यकता होती है, क्योंकि उत्तीर्ण सम्मेलन विभिन्न प्रकार के डेटा (विशेष रूप से फ़्लोटिंग-पॉइंट तर्कों के लिए) के लिए भिन्न हो सकता है। लेकिन मैं सही हो सकता हूं कि win32 __stdcall बस सबकुछ ढेर पर रखता है, मैं इसे विस्तार से नहीं जानता। – puetzk

8

पहली चीजें पहले: आप सी में पैरामीटर के रूप में एक प्रकार को पास नहीं कर सकते हैं। आपके द्वारा छोड़ा गया एकमात्र विकल्प मैक्रोज़ है।

यह योजना बशर्ते आप एक LoadLibrary/GetProcAddress कर रहे हैं Win32 कार्यों कॉल करने के लिए, एक छोटे संशोधन (शून्य की सरणी तर्क के लिए *) से काम करता है। फ़ंक्शन नाम स्ट्रिंग होने के बावजूद अन्यथा इसका उपयोग नहीं किया जाएगा। सी में, एकमात्र तरीका जिसे आप फ़ंक्शन कहते हैं, उसके नाम (एक पहचानकर्ता) के माध्यम से होता है जो अधिकांश मामलों में फ़ंक्शन में पॉइंटर को क्षय करता है। आपको रिटर्न वैल्यू कास्टिंग करने का भी ख्याल रखना होगा।

मेरे सर्वोत्तम नंबर:

// define a function type to be passed on to the next macro 
#define Declare(ret, cc, fn_t, ...) typedef ret (cc *fn_t)(__VA_ARGS__) 

// for the time being doesn't work with UNICODE turned on 
#define Call(dll, fn, fn_t, ...) do {\ 
    HMODULE lib = LoadLibraryA(dll); \ 
    if (lib) { \ 
     fn_t pfn = (fn_t)GetProcAddress(lib, fn); \ 
     if (pfn) { \ 
      (pfn)(__VA_ARGS__); \ 
     } \ 
     FreeLibrary(lib); \ 
    } \ 
    } while(0) 

int main() { 
    Declare(int, __stdcall, MessageBoxProc, HWND, LPCSTR, LPCSTR, UINT); 

    Call("user32.dll", "MessageBoxA", MessageBoxProc, 
      NULL, ((LPCSTR)"?"), ((LPCSTR)"Details"), 
      (MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2)); 

    return 0; 
} 
+0

तो तुम कह रहे हैं कि यह * * सी में ऐसा करना संभव है - मुद्दा मेरे उदाहरण में 'कॉल' समारोह के प्रोटोटाइप Win32 कार्य करने के लिए वास्तविक कॉल (मैं पर कोई नियंत्रण नहीं है जो स्पष्ट रूप से) के बजाय जा रहा है? –

+0

क्या __VA_ARGS__ का उपयोग यहां नहीं लगता है कि कॉल stdcall की बजाय cdecl है (और इसलिए तर्क गलत क्रम में ढेर पर धकेल दिए जाएंगे)? –

+0

__VA_ARGS__ एक विविध मैक्रो है। प्री-प्रोसेसिंग चरण के बाद यह अस्तित्व में है। यह वैरिएडिक फ़ंक्शंस के लिए va_args जैसा नहीं है। ध्यान दें कि मैं फ़ंक्शन के प्रकार को मैक्रो पैरामीटर के रूप में पास कर रहा हूं। – dirkgently

0

होने एक बुरा विचार की तरह लगता है कि जैसे एक समारोह है, लेकिन आप यह कोशिश कर सकते हैं: एक 32-बिट सिस्टम पर

int Call(LPCSTR DllName, LPCSTR FunctionName, 
    USHORT ArgumentCount, int args[]) 
{ 
    void STDCALL (*foobar)()=lookupDLL(...); 

    switch(ArgumentCount) { 
     /* Note: If these give some compiler errors, you need to cast 
      each one to a func ptr type with suitable number of arguments. */ 
     case 0: return foobar(); 
     case 1: return foobar(args[0]); 
     ... 
    } 
} 

, लगभग सभी मूल्यों फिट 32-बिट शब्द और छोटे मानों को फ़ंक्शन कॉल तर्कों के लिए 32-बिट शब्दों के रूप में स्टैक पर धकेल दिया जाता है, इसलिए आप लगभग सभी Win32 API फ़ंक्शंस को इस तरह कॉल करने में सक्षम होना चाहिए, केवल int से तर्क और int से वापसी मूल्य डालें उचित प्रकार के लिए।

+0

एक सामान्य कार्यान्वयन के लिए मैं नहीं चाहता कि वास्तव में स्विच करने के लिए (या अगर-बाकी), हालांकि मैं मानता हूँ तर्क मैं के लिए पूरा करने के लिए की आवश्यकता होगी की संख्या की एक व्यावहारिक सीमा है कि वहाँ चाहते तर्कों की अलग नंबरों के लिए कोड। –

+0

तुम भी cdecl कार्यों ... – RBerteig

0

मुझे यकीन नहीं है कि यह आपके लिए रूचिपूर्ण होगा, लेकिन एक विकल्प RunDll32.exe पर खोलना होगा और इसे आपके लिए फ़ंक्शन कॉल निष्पादित करना होगा। RunDll32 में कुछ सीमाएं हैं और मुझे विश्वास नहीं है कि आप रिटर्न वैल्यू का उपयोग कर सकते हैं लेकिन यदि आप कमांड लाइन तर्क सही तरीके से बनाते हैं तो यह फ़ंक्शन को कॉल करेगा।

यहाँ एक link

+0

लिंक के लिए धन्यवाद कॉल करने की आवश्यकता है, लेकिन यह स्पष्ट रूप से पता चलता है कि Rundll32 किसी भी Win32 एपीआई कार्यों ... –

0

सबसे पहले आप एक अतिरिक्त पैरामीटर के रूप में प्रत्येक तर्क के आकार जोड़ना चाहिए है,। अन्यथा, आपको स्टैक पर धक्का देने के लिए प्रत्येक फ़ंक्शन के लिए प्रत्येक पैरामीटर के आकार को दैवीय करने की आवश्यकता होती है, जो WinXX फ़ंक्शंस के लिए संभव है क्योंकि उन्हें दस्तावेजों के साथ संगत होना चाहिए, लेकिन वे थके हुए हैं।

दूसरा, एक varargs फ़ंक्शन को छोड़कर तर्कों को जानने के बिना एक समारोह को कॉल करने के लिए "शुद्ध सी" तरीका नहीं है, और डीएलएल में किसी फ़ंक्शन द्वारा उपयोग किए जाने वाले कॉलिंग सम्मेलन पर कोई बाधा नहीं है।

वास्तव में, दूसरा हिस्सा पहले से ज्यादा महत्वपूर्ण है।

सिद्धांत रूप में, आप 11 पैरामीटर के पैरामीटर प्रकारों के सभी संयोजनों को उत्पन्न करने के लिए एक प्रीप्रोसेसर मैक्रो/# संरचना संरचना स्थापित कर सकते हैं, लेकिन इसका तात्पर्य है कि आप समय से पहले जानते हैं कि आपके द्वारा किस प्रकार के कार्य पारित किए जाएंगे Call। यदि आप मुझसे पूछें तो कौन सा पागल है।

हालांकि, यदि आप वास्तव में यह असुरक्षित करना चाहते हैं, तो आप पैरामीटर के प्रकार निकालने के लिए C++ उलझन वाले नाम को पार कर सकते हैं और UnDecorateSymbolName का उपयोग कर सकते हैं। हालांकि, यह सी लिंकेज के साथ निर्यात किए गए कार्यों के लिए काम नहीं करेगा।

1

बहुत सारे Win32 एपीआई विशिष्ट लेआउट के साथ structs पर पॉइंटर्स लेते हैं। इनमें से, एक बड़ा सबसेट एक सामान्य पैटर्न का पालन करता है जहां पहले डीडब्ल्यूओआर को शुरू करने से पहले संरचना का आकार रखने के लिए प्रारंभ किया जाना चाहिए। कभी-कभी उन्हें पास करने के लिए स्मृति की एक ब्लॉक की आवश्यकता होती है, जिसमें वे एक संरचना लिखेंगे, और मेमोरी ब्लॉक एक आकार का होना चाहिए जो पहले एक ही एपीआई को एक नल पॉइंटर के साथ कॉल करके निर्धारित किया जाता है और सही मूल्य खोजने के लिए वापसी मूल्य पढ़ता है आकार। कुछ एपीआई एक संरचना आवंटित करते हैं और इसमें एक सूचक लौटाते हैं, जैसे कि पॉइंटर को दूसरे कॉल के साथ हटा दिया जाना चाहिए।

मैं आश्चर्यचकित नहीं होगा अगर एपीआई का सेट जिसे एक शॉट में उपयोगी रूप से बुलाया जा सकता है, व्यक्तिगत तर्क एक साधारण स्ट्रिंग प्रस्तुति से परिवर्तनीय है, काफी छोटा है।

इस विचार आम तौर पर लागू करने के लिए, हम काफी एक चरम पर जाने के लिए होगा:

typedef void DynamicFunction(size_t argumentCount, const wchar_t *arguments[], 
          size_t maxReturnValueSize, wchar_t *returnValue); 

DynamicFunction *GenerateDynamicFunction(const wchar_t *code); 

आप GenerateDynamicFunction करने के लिए कोड का एक सरल टुकड़ा से होकर गुजरेगा, और यह कुछ मानक बॉयलरप्लेट में उस कोड लपेटो जाएगा और उसके बाद एक डीएलएल बनाने के लिए एक सी संकलक/लिंकर का आह्वान करें (इसमें कुछ मुफ्त विकल्प उपलब्ध हैं), जिसमें फ़ंक्शन शामिल है। यह LoadLibrary होगा कि DLL और फ़ंक्शन खोजने के लिए GetProcAddress का उपयोग करें, और फिर इसे वापस करें। यह महंगा होगा, लेकिन आप इसे बार-बार उपयोग करेंगे और परिणामी DynamicFunctionPtr को बार-बार उपयोग के लिए कैश करेंगे। आप पॉइंटर्स को हैशटेबल में रखकर गतिशील रूप से ऐसा कर सकते हैं, जो कि कोड स्निपेट्स द्वारा स्वयं की कुंजी है।

बॉयलरप्लेट हो सकता है:

#include <windows.h> 
// and anything else that might be handy 

void DynamicFunctionWrapper(size_t argumentCount, const wchar_t *arguments[], 
          size_t maxReturnValueSize, wchar_t *returnValue) 
{ 
    // --- insert code snipped here 
} 

तो इस प्रणाली का एक उदाहरण उपयोग होगा:

DynamicFunction *getUserName = GenerateDynamicFunction(
    "GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))"); 

wchar_t userName[100]; 
getUserName(0, NULL, sizeof(userName)/sizeof(wchar_t), userName); 

आप GenerateDynamicFunction बनाने तर्क गणना स्वीकार करके इस में वृद्धि कर सकता है, इसलिए यह एक उत्पन्न कर सकता है रैपर की शुरुआत में जांच करें कि सही संख्या में तर्क पारित किए गए हैं। और यदि आप प्रत्येक सामना कोडपिपेट के लिए फ़ंक्शंस को कैश करने के लिए वहां हैशटेबल डालते हैं, तो आप अपने मूल उदाहरण के करीब आ सकते हैं। कॉल फ़ंक्शन केवल एक एपीआई नाम की बजाय कोड स्निपेट लेगा, लेकिन अन्यथा वही होगा। यह हैशटेबल में कोड स्निपेट को देखेगा, और यदि मौजूद नहीं है, तो यह जेनरेट डायनामिक फ़ंक्शन को कॉल करेगा और परिणाम को अगली बार हैशटेबल में संग्रहीत करेगा। फिर यह समारोह पर कॉल करेगा। उदाहरण उपयोग:

wchar_t userName[100]; 

Call("GetUserNameW(returnValue, (LPDWORD)(&maxReturnValueSize))", 
    0, NULL, sizeof(userName)/sizeof(wchar_t), userName); 

बेशक वहाँ इस के किसी भी कर ज्यादा बात जब तक विचार सामान्य सुरक्षा छेद के कुछ प्रकार को खोलने के लिए था नहीं होगा। जैसे एक webservice के रूप में Call का पर्दाफाश करने के लिए। आपके मूल विचार के लिए सुरक्षा प्रभाव मौजूद हैं, लेकिन केवल इसलिए स्पष्ट नहीं हैं क्योंकि आपके द्वारा सुझाए गए मूल दृष्टिकोण प्रभावी नहीं होंगे। जितना अधिक शक्तिशाली हम इसे बनाते हैं, उतना ही अधिक सुरक्षा समस्या होगी।

अद्यतन टिप्पणी के आधार पर:

.NET फ़्रेमवर्क एक विशेषता पी बुलाया/आह्वान है, जो वास्तव में मौजूद है अपनी समस्या को हल करने के लिए है। तो यदि आप इसे सामान के बारे में जानने के लिए एक परियोजना के रूप में कर रहे हैं, तो आप यह समझने के लिए पी/आवेक देख सकते हैं कि यह कितना जटिल है। आप संभवतः अपनी स्क्रिप्टिंग भाषा के साथ .NET ढांचे को लक्षित कर सकते हैं - वास्तविक समय में स्क्रिप्ट को समझने के बजाय, या उन्हें अपने बाइटकोड में संकलित करने के बजाय, आप उन्हें आईएल में संकलित कर सकते हैं। या आप .NET पर पहले से उपलब्ध कई लोगों से मौजूदा स्क्रिप्टिंग भाषा होस्ट कर सकते हैं।

+0

+1 बुला अनुमति नहीं देता है, लेकिन मेरी मंशा क्षमता मनमाने ढंग से Win32 एपीआई में कॉल करने के लिए जोड़ने के लिए है सी में लागू एक स्क्रिप्टिंग भाषा से (एक सुरक्षा छेद खोलने के बजाय!)। यदि संभव हो तो मैं कार्यान्वयन सी संकलक/लिंकर पर निर्भर नहीं था। –

+0

अद्यतन के लिए धन्यवाद (दुर्भाग्य से मैं आपको फिर से ऊपर नहीं उठा सकता!)। मैं एक सी-आधारित समाधान की तलाश में हूं, आदर्श रूप से लेकिन .NET विकल्प विचार के लिए भोजन है। –

+0

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

2

अन्य पोस्ट वास्तव में कॉल करने के लिए असेंबली या अन्य गैर मानक चाल के लिए लगभग निश्चित आवश्यकता के बारे में सही हैं, वास्तविक कॉलिंग सम्मेलनों के सभी विवरणों का उल्लेख न करें। stdcall और cdecl:

विंडोज DLLs कार्यों के लिए कम से कम दो अलग बुला सम्मेलनों का उपयोग करें। आपको दोनों को संभालने की आवश्यकता होगी, और यह भी पता लगाने की आवश्यकता हो सकती है कि किस का उपयोग करना है।

इससे निपटने का एक तरीका मौजूदा विवरणों को समाहित करने के लिए मौजूदा पुस्तकालय का उपयोग करना है। आश्चर्यजनक रूप से, एक है: libffi। एक स्क्रिप्टिंग पर्यावरण में इसके उपयोग का एक उदाहरण लुआ Alien का कार्यान्वयन है, जो लूआ मॉड्यूल है जो अल्युअन से अलग शुद्ध लुआ में मनमाने ढंग से डीएलएल बनाने के लिए इंटरफेस की अनुमति देता है।

1

आप कुछ इस तरह की कोशिश कर सकते हैं - यह Win32 एपीआई कार्यों के लिए अच्छी तरह से काम करता है: "ढेर" तर्क में

int CallFunction(int functionPtr, int* stack, int size) 
{ 
    if(!stack && size > 0) 
     return 0; 
    for(int i = 0; i < size; i++) { 
     int v = *stack; 
     __asm { 
      push v 
     } 
     stack++; 
    } 
    int r; 
    FARPROC fp = (FARPROC) functionPtr; 
    __asm { 
     call fp 
     mov dword ptr[r], eax 
    } 
    return r; 
} 

मापदंडों उलटे क्रम में होना चाहिए (के रूप में इस क्रम में वे पर पुश किए जाते हैं ढेर)।

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

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