উপরে উত্তর হিসাবে, সঠিক উত্তরটি হ'ল ভিএস ২০১৫ এর সাথে সমস্ত কিছু সংকলন করা, তবে আগ্রহের জন্য নিম্নলিখিতটি আমার সমস্যা বিশ্লেষণ।
এই প্রতীকটি মাইক্রোসফ্ট দ্বারা সরবরাহিত কোনও স্থির গ্রন্থাগারে VS2015 এর অংশ হিসাবে সংজ্ঞায়িত বলে মনে হয় না, যা অন্য সমস্ত কারণেই বরং অদ্ভুত। কেন তা আবিষ্কার করার জন্য আমাদের সেই ফাংশনটির ঘোষণাপত্র এবং আরও গুরুত্বপূর্ণভাবে এটি কীভাবে ব্যবহৃত হচ্ছে তা লক্ষ্য করা উচিত।
এখানে ভিজ্যুয়াল স্টুডিও ২০০ 2008 শিরোনামের একটি স্নিপেট রয়েছে:
_CRTIMP FILE * __cdecl __iob_func(void);
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
সুতরাং আমরা দেখতে পাচ্ছি যে ফাংশনটির কাজটি হ'ল ফাইলের একটি অ্যারের প্রারম্ভ ফিরিয়ে আনা (হ্যান্ডলগুলি নয়, "ফাইল *" হ্যান্ডেলটি, ফাইলে গুরুত্বপূর্ণ স্টেটের গুডিকে স্টোর করে অন্তর্নিহিত অস্বচ্ছ ডেটা স্ট্রাকচার)। এই ফাংশনটির ব্যবহারকারীরা হলেন তিনটি ম্যাক্রো স্টিডিন, স্টডআউট এবং স্টার্ডার যা বিভিন্ন 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 সরবরাহ করতে পারি না? মাইক্রোসফ্ট __iob_func এর মাধ্যমে তাদের মূল প্রয়োগের ক্ষেত্রে লঙ্ঘন করতে পারে না এমন দুটি মূল বিধি রয়েছে:
- সেখানে তিনটি ফাইল স্ট্রাকচারের অ্যারে থাকতে হবে যা পূর্বের মতো একই পদ্ধতিতে সূচকযুক্ত হতে পারে।
- 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, আইওব [1] অফসেট 4 এবং _iob [2] অফসেটে 8 রয়েছে The কলিং কোডটি তার পরিবর্তে আমার সিস্টেমে 32 বাইটে সারিবদ্ধভাবে ফাইলটি প্রত্যাশা করবে এবং তাই এটি ফিরে আসা অ্যারের ঠিকানা গ্রহণ করবে এবং মৌলিক শূন্যটি পেতে 0 বাইট যুক্ত করবে (এটি একটি ঠিক আছে) তবে _ আইওব [1] এর জন্য এটি ব্যয় করতে হবে যে 32 বাইট যুক্ত করতে হবে এবং _ আইওব [2] এর জন্য এটি হ্রাস পাবে এটিতে 64৪-বাইট যুক্ত করা দরকার (কারণ এটি ভিএস ২০০৮ শিরোলেখগুলিতে কেমন দেখায়)। এবং সত্যই VS2008 এর জন্য বিযুক্ত কোডটি এটি দেখায়।
উপরের সমাধান সহ একটি গৌণ সমস্যা হ'ল এটি ফাইলের কাঠামোর (* স্টিডিন) বিষয়বস্তু অনুলিপি করে, ফাইল * হ্যান্ডেলটি নয়। সুতরাং যে কোনও ভিএস ২০০৮ কোড ভিএস ২০১৫ এ অন্য অন্তর্নিহিত কাঠামোটির দিকে চেয়ে থাকবে। কাঠামোটিতে কেবলমাত্র পয়েন্টার থাকলে এটি কাজ করতে পারে তবে এটি একটি বড় ঝুঁকি। যে কোনও ক্ষেত্রে প্রথম ইস্যুটি এই অপ্রাসঙ্গিক উপস্থাপন করে।
আমি যে হ্যাকটি স্বপ্ন দেখতে পেরেছি তা হ'ল এটির মধ্যে __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;
}