जैसा कि ऊपर बताया गया है, सही उत्तर सब कुछ VS2015 के साथ संकलित करना है, लेकिन ब्याज के लिए निम्नलिखित समस्या का मेरा विश्लेषण है।
यह प्रतीक Microsoft द्वारा VS2015 के भाग के रूप में प्रदान की गई किसी भी स्थिर लाइब्रेरी में परिभाषित नहीं किया गया है, जो कि अन्य लोगों के मुकाबले अजीबोगरीब है। क्यों की खोज करने के लिए, हमें उस फ़ंक्शन की घोषणा को देखने की आवश्यकता है और, इससे भी महत्वपूर्ण बात यह है कि इसका उपयोग कैसे किया जाता है।
यहाँ दृश्य स्टूडियो 2008 हेडर से एक स्निपेट दिया गया है:
_CRTIMP FILE * __cdecl __iob_func(void);
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
इसलिए हम देख सकते हैं कि फ़ंक्शन का काम FILE ऑब्जेक्ट्स की एक सरणी की शुरुआत को वापस करना है (हैंडल नहीं, "FILE *" हैंडल है, FILE महत्वपूर्ण स्टेट गुडियों को संग्रहीत करने वाला अंतर्निहित अपारदर्शी डेटा संरचना है)। इस फ़ंक्शन के उपयोगकर्ता तीन मैक्रो स्टडिन, स्टडआउट और स्टेडर हैं जो कि विभिन्न fscanf, fprintf स्टाइल कॉल के लिए उपयोग किए जाते हैं।
अब आइए नजर डालते हैं कि विजुअल स्टूडियो 2015 में उन्हीं चीजों को कैसे परिभाषित किया गया है:
_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#define stdin (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))
इसलिए प्रतिस्थापन फ़ंक्शन के लिए दृष्टिकोण बदल गया है अब फ़ाइल ऑब्जेक्ट्स के सरणी के पते के बजाय फ़ाइल हैंडल को वापस लौटाएं, और मैक्रोज़ केवल एक पहचान संख्या में गुजरने वाले फ़ंक्शन को कॉल करने के लिए बदल गए हैं।
तो वे / हम संगत API क्यों नहीं दे सकते? दो प्रमुख नियम हैं जो Microsoft __iob_func के माध्यम से अपने मूल कार्यान्वयन के संदर्भ में उल्लंघन नहीं कर सकते हैं:
- तीन फ़ाइल संरचनाओं की एक सरणी होनी चाहिए जिसे पहले की तरह ही अनुक्रमित किया जा सकता है।
- FILE का संरचनात्मक लेआउट नहीं बदल सकता है।
उपरोक्त में से किसी में भी बदलाव का मतलब मौजूदा संकलित कोड से जुड़ा होगा, जो कि एपीआई कहलाता है तो बुरी तरह से गलत हो जाएगा।
आइए नजर डालते हैं कि FILE को कैसे परिभाषित किया गया है।
पहली VS2008 फ़ाइल परिभाषा:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
और अब VS2015 फ़ाइल परिभाषा:
typedef struct _iobuf
{
void* _Placeholder;
} FILE;
तो इसका क्रुक्स है: संरचना ने आकार बदल दिया है। मौजूदा संकलित कोड __iob_func का संदर्भ देते हुए इस तथ्य पर निर्भर करता है कि लौटाया गया डेटा दोनों एक सरणी है जिसे अनुक्रमित किया जा सकता है और उस सरणी में तत्व समान दूरी पर हैं।
इन रेखाओं के साथ ऊपर दिए गए उत्तर में वर्णित संभावित समाधान कुछ कारणों से काम नहीं करेंगे (यदि कहा जाता है):
FILE _iob[] = {*stdin, *stdout, *stderr};
extern "C" FILE * __cdecl __iob_func(void)
{
return _iob;
}
फ़ाइल सरणी _iob को VS2015 के साथ संकलित किया जाएगा और इसलिए इसे एक शून्य * संरचना वाले ब्लॉक के रूप में रखा जाएगा। 32-बिट संरेखण मानते हुए, ये तत्व 4 बाइट्स के अलावा होंगे। तो _iob [0] ऑफ़सेट 0 पर है, _iob [1] ऑफसेट 4 पर है और _iob [2] ऑफ़सेट 8 पर है। कॉलिंग कोड इसके बजाय FILE के अधिक लंबे समय तक बने रहने की उम्मीद करेगा, मेरे सिस्टम पर 32 बाइट्स में संरेखित है, और इसी तरह यह दिए गए ऐरे का पता लेगा और शून्य तत्व को प्राप्त करने के लिए 0 बाइट्स जोड़ देगा (जो कि ठीक है), लेकिन _iob [1] के लिए यह कटौती करेगा कि उसे 32 बाइट्स जोड़ने की जरूरत है और _iob [2] के लिए यह कटौती करेगा यह 64-बाइट्स जोड़ने की जरूरत है (क्योंकि यह कैसे VS2008 हेडर में देखा गया है)। और वास्तव में VS2008 के लिए असंतुष्ट कोड इसे प्रदर्शित करता है।
उपरोक्त समाधान के साथ एक द्वितीयक समस्या यह है कि यह FILE संरचना की सामग्री की प्रतिलिपि बनाता है (* stdin), न कि FILE * हैंडल। इसलिए कोई भी VS2008 कोड VS2015 के लिए एक अलग अंतर्निहित संरचना को देखेगा। यह काम कर सकता है यदि संरचना में केवल संकेत होते हैं, लेकिन यह एक बड़ा जोखिम है। किसी भी स्थिति में पहला मुद्दा इस अप्रासंगिक को प्रस्तुत करता है।
केवल वही हैक जो मैं सपने में देख पा रहा हूं, वह है जिसमें __iob_func कॉल स्टैक चलता है जो वास्तविक फ़ाइल हैंडल की तलाश में है जो वे खोज रहे हैं (लौटे पते पर जोड़े गए ऑफसेट के आधार पर) और एक कम्प्यूटेड मान लौटाता है जैसे कि सही उत्तर देता है। यह हर बिट पागल है जैसा कि यह लगता है, लेकिन केवल x86 के लिए प्रोटोटाइप (x64 नहीं) आपके मनोरंजन के लिए नीचे सूचीबद्ध है। इसने मेरे प्रयोगों में ठीक काम किया, लेकिन आपका लाभ भिन्न हो सकता है - उत्पादन उपयोग के लिए अनुशंसित नहीं!
#include <windows.h>
#include <stdio.h>
#include <dbghelp.h>
/* #define LOG */
#if defined(_M_IX86)
#define GET_CURRENT_CONTEXT(c, contextFlags) \
do { \
c.ContextFlags = contextFlags; \
__asm call x \
__asm x: pop eax \
__asm mov c.Eip, eax \
__asm mov c.Ebp, ebp \
__asm mov c.Esp, esp \
} while(0);
#else
/* This should work for 64-bit apps, but doesn't */
#define GET_CURRENT_CONTEXT(c, contextFlags) \
do { \
c.ContextFlags = contextFlags; \
RtlCaptureContext(&c); \
} while(0);
#endif
FILE * __cdecl __iob_func(void)
{
CONTEXT c = { 0 };
STACKFRAME64 s = { 0 };
DWORD imageType;
HANDLE hThread = GetCurrentThread();
HANDLE hProcess = GetCurrentProcess();
GET_CURRENT_CONTEXT(c, CONTEXT_FULL);
#ifdef _M_IX86
imageType = IMAGE_FILE_MACHINE_I386;
s.AddrPC.Offset = c.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Esp;
s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rsp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64;
s.AddrPC.Offset = c.StIIP;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.IntSp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrBStore.Offset = c.RsBSP;
s.AddrBStore.Mode = AddrModeFlat;
s.AddrStack.Offset = c.IntSp;
s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{
#ifdef LOG
printf("Error: 0x%08X (Address: %p)\n", GetLastError(), (LPVOID)s.AddrPC.Offset);
#endif
return NULL;
}
if (s.AddrReturn.Offset == 0)
{
return NULL;
}
{
unsigned char const * assembly = (unsigned char const *)(s.AddrReturn.Offset);
#ifdef LOG
printf("Code bytes proceeding call to __iob_func: %p: %02X,%02X,%02X\n", assembly, *assembly, *(assembly + 1), *(assembly + 2));
#endif
if (*assembly == 0x83 && *(assembly + 1) == 0xC0 && (*(assembly + 2) == 0x20 || *(assembly + 2) == 0x40))
{
if (*(assembly + 2) == 32)
{
return (FILE*)((unsigned char *)stdout - 32);
}
if (*(assembly + 2) == 64)
{
return (FILE*)((unsigned char *)stderr - 64);
}
}
else
{
return stdin;
}
}
return NULL;
}