IDA has problems parsing C++ inherited classes

#define _X86_
#include <minwindef.h>
#include <basetyps.h>
#include <rpcsal.h>
#include <winerror.h>
#define interface __STRUCT__
typedef WCHAR OLECHAR;
typedef /* [string] */  __RPC_string OLECHAR* LPOLESTR;
DECLARE_HANDLE(HWND);
#define __RPC_FAR
struct IBindCtx;
struct IMoniker;

#define MIDL_INTERFACE(x)   struct DECLSPEC_UUID(x) DECLSPEC_NOVTABLE

extern "C"
{
	HWND
		WINAPI
		GetDesktopWindow(
			VOID);
	DWORD
		WINAPI
		GetTickCount(
			VOID
		);
}

#define NO_USE_ALIGN
#define NO_USE_PACK

#ifdef NO_USE_ALIGN
#undef DECLSPEC_ALIGN
#define DECLSPEC_ALIGN(x)
#endif


#ifndef NO_USE_PACK
#pragma pack(push, 2)
#endif
MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
CUnknown
{
public:
	virtual HRESULT STDMETHODCALLTYPE QueryInterface(
		/* [in] */ REFIID riid,
		/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR * __RPC_FAR * ppvObject) = 0;

	virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0;

	virtual ULONG STDMETHODCALLTYPE Release(void) = 0;
};
MIDL_INTERFACE("0000011a-0000-0000-C000-000000000046")
DECLSPEC_ALIGN(8) CParseDisplayName : public CUnknown
{
public:
	bool IParseDisplayName_Ok;
	int IParseDisplayName_ID;
	bool IParseDisplayName_Valid;
	int IParseDisplayName_Value;
	virtual HRESULT STDMETHODCALLTYPE ParseDisplayName(
		/* [unique][in] */ __RPC__in_opt IBindCtx* pbc,
		/* [in] */ __RPC__in LPOLESTR pszDisplayName,
		/* [out] */ __RPC__out ULONG* pchEaten,
		/* [out] */ __RPC__deref_out_opt IMoniker** ppmkOut) = 0;

};
#ifndef NO_USE_PACK
#pragma pack(pop)
#pragma pack(push, 1)
#endif
MIDL_INTERFACE("00000114-0000-0000-C000-000000000046")
DECLSPEC_ALIGN(16) COleWindow : public CUnknown
{
public:
	bool IOleWindow_Ok;
	int IOleWindow_ID;
	bool IOleWindow_Ok2;
	short IOleWindow_SValue;
	bool IOleWindow_Valid;
	int IOleWindow_Value;
	virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow(
		/* [out] */ __RPC__deref_out_opt HWND* phwnd) = 0;

	virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(
		/* [in] */ BOOL fEnterMode) = 0;

};
#ifndef NO_USE_PACK
#pragma pack(pop)
#pragma pack(push, 16)
#endif
MIDL_INTERFACE("0000010c-0000-0000-C000-000000000046")
DECLSPEC_ALIGN(8) CPersist : public CUnknown
{
public:
	bool IPersist_Ok;
	int IPersist_ID;
	short IPersist_SValue;
	int IPersist_Value;
	virtual HRESULT STDMETHODCALLTYPE GetClassID(
		/* [out] */ __RPC__out CLSID* pClassID) = 0;

};
#ifndef NO_USE_PACK
#pragma pack(pop)
#pragma pack(push, 1)
#endif
MIDL_INTERFACE("DF0B3D60-548F-101B-8E65-08002B2BD119")
DECLSPEC_ALIGN(16) CSupportErrorInfo : public CUnknown
{
public:
	int ISupportErrorInfo_ID;
	int ISupportErrorInfo_Value;
	virtual HRESULT STDMETHODCALLTYPE InterfaceSupportsErrorInfo(
		/* [in] */ __RPC__in REFIID riid) = 0;

};
#ifndef NO_USE_PACK
#pragma pack(pop)
#pragma pack(push, 8)
#endif
interface DECLSPEC_ALIGN(8) CGetFrameOptions : CUnknown
{
public:
	short IGetFrameOptions_ID;
	bool IGetFrameOptions_Ok;
	int IGetFrameOptions_Value;
	virtual HRESULT STDMETHODCALLTYPE GetFrameOptions(DWORD* pdwFlags) = 0;
};

#ifndef NO_USE_PACK
#pragma pack(pop)
#pragma pack(push, 16)
#endif
class DECLSPEC_ALIGN(16) CMyMI :public CParseDisplayName, COleWindow, CPersist, CSupportErrorInfo, CGetFrameOptions
{
private:
	long nRefs;
	HWND hWnd;
	CLSID clsid;
public:
	CMyMI()
	{
		nRefs = GetTickCount();
		hWnd = (HWND)0x12345678;
		clsid = __uuidof(CPersist);
		int base_size_think = sizeof(CParseDisplayName) + sizeof(COleWindow) + sizeof(CPersist) + sizeof(CSupportErrorInfo) + sizeof(CGetFrameOptions);
		int base_size_real = (char*)&nRefs - (char*)this;
		if (base_size_real != base_size_think)
		{
			__debugbreak();
			return;
		}
	}
protected:
	virtual void DeleteObject()
	{
		delete this;
	}
public:
	virtual HRESULT STDMETHODCALLTYPE QueryInterface(
		/* [in] */ REFIID riid,
		/* [iid_is][out] */ _COM_Outptr_ void __RPC_FAR * __RPC_FAR * ppvObject)
	{
		return (HRESULT)(DWORD_PTR)hWnd;
	}
	ULONG STDMETHODCALLTYPE AddRef()
	{
		//ATLASSERT(nRefs >= 0);
		return nRefs++;
	}
	ULONG STDMETHODCALLTYPE Release()
	{
		//ATLASSERT(nRefs != 0);

		long nRefs_Result = nRefs--;
		if (nRefs_Result <= 0)
		{
			DeleteObject();
		}
		return nRefs_Result;
	}
	//IParseDisplayName
public:
	virtual HRESULT STDMETHODCALLTYPE ParseDisplayName(
		/* [unique][in] */ __RPC__in_opt IBindCtx * pbc,
		/* [in] */ __RPC__in LPOLESTR pszDisplayName,
		/* [out] */ __RPC__out ULONG * pchEaten,
		/* [out] */ __RPC__deref_out_opt IMoniker * *ppmkOut)
	{
		IParseDisplayName_ID = GetTickCount();
		IOleWindow_ID = GetTickCount();
		IPersist_ID = GetTickCount();
		ISupportErrorInfo_ID = GetTickCount();
		IGetFrameOptions_ID = GetTickCount();

		hWnd = GetDesktopWindow();
		return E_NOTIMPL;
	}
	//IOleWindow
public:
	virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow(
		/* [out] */ __RPC__deref_out_opt HWND * phwnd)
	{
		*phwnd = hWnd;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(
		/* [in] */ BOOL fEnterMode)
	{
		return E_NOTIMPL;
	}
	//IPersist
public:
	virtual HRESULT STDMETHODCALLTYPE GetClassID(
		/* [out] */ __RPC__out CLSID * pClassID)
	{
		IParseDisplayName_ID = GetTickCount();
		IOleWindow_ID = GetTickCount();
		IPersist_ID = GetTickCount();
		ISupportErrorInfo_ID = GetTickCount();
		IGetFrameOptions_ID = GetTickCount();

		*pClassID = clsid;
		return S_OK;
	}
	//ISupportErrorInfo
public:
	HRESULT STDMETHODCALLTYPE InterfaceSupportsErrorInfo(
		/* [in] */ __RPC__in REFIID riid)
	{
		IParseDisplayName_ID = GetTickCount();
		IOleWindow_ID = GetTickCount();
		IPersist_ID = GetTickCount();
		ISupportErrorInfo_ID = GetTickCount();
		IGetFrameOptions_ID = GetTickCount();

		return riid == clsid ? S_OK : S_FALSE;
	}
	//IGetFrameOptions
public:
	HRESULT STDMETHODCALLTYPE GetFrameOptions(DWORD * pdwFlags)
	{
		IParseDisplayName_ID = GetTickCount();
		IOleWindow_ID = GetTickCount();
		IPersist_ID = GetTickCount();
		ISupportErrorInfo_ID = GetTickCount();
		IGetFrameOptions_ID = GetTickCount();

		nRefs += GetTickCount();
		*pdwFlags = nRefs;
		return S_OK;
	}
};
#ifndef NO_USE_PACK
#pragma pack(pop)
#endif
int main()
{
	CMyMI my;
	my.ParseDisplayName(0, 0, 0, 0);
	HWND hWnd;
	my.GetWindow(&hWnd);
	DWORD dwFlags;
	my.GetFrameOptions(&dwFlags);
	return (int)((DWORD_PTR)(hWnd)+dwFlags);
}

Compile the above code with vc x86, then load the generated exe with IDA and let IDA automatically load the pdb of this exe. You will see the following IDA decompiled code for CMyMI::GetWindow:

HRESULT __stdcall CMyMI::GetWindow(CMyMI *this, HWND__ **phwnd)
{
  *phwnd = (HWND__ *)this->ISupportErrorInfo_Value;
  return 0;
}

But its original code is:

	virtual /* [input_sync] */ HRESULT STDMETHODCALLTYPE GetWindow(
		/* [out] */ __RPC__deref_out_opt HWND * phwnd)
	{
		*phwnd = hWnd;
		return S_OK;
	}

The hWnd field is incorrectly identified as ISupportErrorInfo_Value. The reason for the error is that the type of the this parameter is incorrectly applied. Its type should be CMyMI[adjustor for COleWindow]*, which represents this + the offset of the base class where the current interface function is located , for example, as follows :

struct CMyMI_DataPart
{
   int nRefs;
  HWND__ *hWnd;
  _GUID clsid;
}
struct __cppobj CMyMI[adjustor for COleWindow] :COleWindow, CPersist, CSupportErrorInfo, CGetFrameOptions
{
  CMyMI_DataPart;
};

This is one problem. There is another problem. Comment out the

#define NO_USE_ALIGN
#define NO_USE_PACK

in the code, then compile and generate again and open it with IDA. You will see that the definition of CMyMI changes from

struct __cppobj CMyMI : CParseDisplayName, COleWindow, CPersist, CSupportErrorInfo, CGetFrameOptions
{
  int nRefs;
  HWND__ *hWnd;
  _GUID clsid;
};

to

struct __cppobj __declspec(align(16)) CMyMI : CParseDisplayName, COleWindow, CSupportErrorInfo, CGetFrameOptions
{
  _BYTE _bytes_30[16];
  HWND__ *hWnd;
  _GUID clsid;
};

. The member field nRefs is missing, and the base class CPersist is also missing. The reason for the error is that IDA mistakenly believes that the starting offset of the first field nRefs of CMyMI is equal to sizeof(CParseDisplayName) + sizeof(COleWindow) + sizeof(CPersist) + sizeof(CSupportErrorInfo) + sizeof(CGetFrameOptions). In fact, it is the sum of the sizes of each base class excluding the end padding field, The pseudo code is as follows:

sizeof(CParseDisplayName_NoTailPadding) + sizeof(COleWindow_NoTailPadding) + sizeof(CPersist_NoTailPadding) + sizeof(CSupportErrorInfo_NoTailPadding) + sizeof(CGetFrameOptions_NoTailPadding)

Thank you very much for the report and nice example!

Regarding the first issue, you can modify the prototype as follows:

int __stdcall CMyMI::GetWindow(COleWindow *__shifted(CMyMI,0x14) __hidden this, HWND *)

It may be possible to detect this automatically, but it doesn’t look trivial. We’ve added a ticket to investigate it.

The second issue seems to hit a corner case we have foreseen but had no code examples for. We will check if it can be handled.

Thank you very much for telling me that the __shifted modifier can solve the first problem. Based on your suggestion, I spent a few days to create a plug-in to batch fix this kind of pointer recognition error problem.
However, I have only dealt with the MSVC x86/x64 scenarios, not gcc and ARM. You are welcome to integrate the core logic of this plug-in into your product.

#include "pch.h"

//--------------------------------------------------------------------------
struct plugin_ctx_t : public plugmod_t
{
	~plugin_ctx_t()
	{

	}
	virtual bool idaapi run(size_t) override;
};

//--------------------------------------------------------------------------
static plugmod_t* idaapi init()
{
	return new plugin_ctx_t;
}

// 辅助函数:检查地址是否指向有效代码
bool is_valid_code_ea_cpp(ea_t ea) {
	if (ea == BADADDR) return false;
	flags64_t flags = get_flags(ea);
	if (is_code(flags))
	{
		return true;
	}
	func_t* f = get_func(ea);
	return f != nullptr;
}

// 辅助函数:获取函数名,如果不存在则返回 "sub_XXXX"
uint32 demangle_name__disable_mask;
qstring get_function_name_or_addr_cpp(ea_t ea) {
	qstring name_buf;
	if (get_func_name(&name_buf, ea) > 0)
	{
		qstring demangled_name = demangle_name(name_buf.c_str(), demangle_name__disable_mask);
		if (!demangled_name.empty())
		{
			return demangled_name;
		}
		return name_buf;
	}
	name_buf.sprnt("sub_%a", ea); // %a 是 IDA 的地址格式化
	return name_buf;
}

// 辅助函数:分析简单 thunk (sub 指令 + jmp 指令)
// 返回: std::pair<is_thunk, target_ea>
std::pair<bool, ea_t> analyze_thunk_cpp(ea_t func_ea) {
	func_t* pfn = get_func(func_ea);
	if (pfn)
	{
		qstring name = get_function_name_or_addr_cpp(func_ea);
		if (name.starts_with("[thunk]:"))
		{
			//目前只解析x86和x64
			insn_t insn;
			int insn_len = decode_insn(&insn, pfn->start_ea);
			if (insn_len > 0)
			{
				if (insn.itype == NN_sub)
				{
					auto adjustor_offset = insn.Op2.value;

					insn_len = decode_insn(&insn, pfn->start_ea + insn_len);
					if (insn_len > 0)
					{
						if (insn.itype == NN_jmp || insn.itype == NN_jmpni)
						{
							if ((insn.Op1.type == o_near || insn.Op1.type == o_far) && insn.Op1.addr != BADADDR)
							{
								if (get_func(insn.Op1.addr)) // 确认目标也是函数
								{
									return { true, insn.Op1.addr };
								}
							}
						}
					}
				}
			}
		}
	}
	return { false, func_ea };
}

// Helper function to fix the this pointer argument type
bool fix_this_ptr_arg_type(ea_t func_ea, tinfo_t& new_this_ptr_arg_tif) {
	func_t* pfn = get_func(func_ea);
	if (pfn == nullptr)
	{
		msg("Error: Could not find function at %a\n", func_ea);
		return false;
	}

	// 1. Get current function type info
	tinfo_t current_func_tif;
	if (!get_tinfo(&current_func_tif, func_ea))
	{
		std::pair<bool, ea_t> thunk_info = analyze_thunk_cpp(func_ea);
		if (thunk_info.first)
		{
			auto target_func_ea = thunk_info.second;
			get_tinfo(&current_func_tif, target_func_ea);
		}
		if (current_func_tif.empty())
		{
			ASSERT(!current_func_tif.empty());
			// If no type info exists, we might need to create one.
			// For simplicity, let's assume it exists or try to guess one.
			// Alternatively, you could use guess_func_tinfo(&current_func_tif, func_ea);
			// or idaapi::apply_cdecl(get_idati(), func_ea, "void default_name();", true); to set a default.
			// For this example, we'll try to guess if it's not set.
			if (!guess_tinfo(&current_func_tif, func_ea))
			{
				msg("Error: Could not get or guess tinfo for function at %a\n", func_ea);
				return false;
			}
		}
	}

	if (!current_func_tif.is_func())
	{
		msg("Error: Address %a does not have a function type.\n", func_ea);
		// You might want to parse a default function declaration and apply it first
		// e.g., tinfo_t default_func_type;
		// parse_decl(&default_func_type, get_idati(), "void placeholder_func(void);", PT_SILENT);
		// set_tinfo(func_ea, &default_func_type);
		// Then re-fetch it. For this example, we stop.
		return false;
	}

	// 2. Extract function details (func_type_data_t)
	func_type_data_t ftd;
	if (!current_func_tif.get_func_details(&ftd))
	{
		msg("Error: Could not get function details for %a\n", func_ea);
		return false;
	}

	if (ftd.empty())
	{
		// ftd is a qvector<funcarg_t>
		msg("Warning: Function %a has no arguments defined in its current type signature.\n", func_ea);
		return false;
	}


	// 3. Modify the func_type_data_t
	if (!ftd.empty())
	{
		// Arguments exist, modify the first one.
		ftd[0].type = new_this_ptr_arg_tif;
		// Optionally, you can also change the argument's name:
		if (ftd[0].name.empty())
		{
			ftd[0].name = "this";
		}
	}


	// 4. Create new function tinfo_t from modified func_type_data_t
	tinfo_t new_func_tif;
	if (!new_func_tif.create_func(ftd))
	{
		msg("Error: Failed to create new function tinfo from modified details for %a\n", func_ea);
		return false;
	}

	// 5. Apply the new function type to the database
	if (!apply_tinfo(func_ea, new_func_tif, TINFO_DEFINITE))
	{
		// Use apply_tinfo for better propagation
		// if (!set_tinfo(func_ea, &new_func_tif)) { // Old way
		msg("Error: Failed to set new tinfo for function at %a\n", func_ea);
		return false;
	}
	//msg("Successfully updated function signature in IDA database for %a.\n", func_ea);

	return true;
}

uint64 get_baseclass_offset(const tinfo_t& current_typeinfo, const tinfo_t& base_typeinfo)
{
	udt_type_data_t udt_details;
	if (!current_typeinfo.get_udt_details(&udt_details))
	{
		return -1;
	}
	for (const udm_t& member : udt_details)
	{
		if (member.is_baseclass())
		{
			if (member.type == base_typeinfo)
			{
				auto cur_class__offset = member.offset / 8;
				return cur_class__offset;
			}
			else
			{
				auto cur_class_in_its_parent__offset = get_baseclass_offset(member.type, base_typeinfo);
				if (cur_class_in_its_parent__offset != -1)
				{
					auto cur_class__offset = member.offset / 8;
					return cur_class__offset + cur_class_in_its_parent__offset;
				}
			}
		}
		else
		{
			break;
		}
	}
	return -1;
}

//--------------------------------------------------------------------------
bool idaapi plugin_ctx_t::run(size_t)
{
	demangle_name__disable_mask = inf_get_short_demnames();

	til_t* local_til = get_idati(); // Get the local type library
	if (local_til == nullptr)
	{
		warning("Could not get local TIL (idati).\n");
		return false;
	}

	//记录MIClass的各基类的虚表信息
	struct VTableInfo
	{
		int ItemCount;
		qstring DClass;
		qstring BaseClass;
	};
	std::map<ea_t, VTableInfo> mapBassClassVTableInfo;

	uint ptr_size = inf_get_app_bitness() / 8;
	if (ptr_size != 4 && ptr_size != 8)
	{
		warning("C++: Unsupported pointer size: %zu\n", ptr_size);
		return false;
	}

	size_t nlist_size = get_nlist_size();
	qstring name_buffer;

	for (size_t i = 0; i < nlist_size; ++i)
	{
		ea_t ea = get_nlist_ea(i);
		if (ea == BADADDR) continue;

		const char* nlist_raw_name = get_nlist_name(i);
		if (nlist_raw_name == nullptr || *nlist_raw_name == '\0')
		{
			if (!get_visible_name(&name_buffer, ea, GN_LOCAL))
			{
				continue;
			}
		}
		else
		{
			name_buffer = nlist_raw_name;
		}

		if (name_buffer.empty()) continue;

		// ... (MSVC 和 GCC vtable 名称检查逻辑) ...
		//MSVC: ??_7...@@6B@  (注意:'B' 代表 'const',也可能是 'E' 代表 'volatile const')
		//GCC / Clang: _ZTV...
		//AI说有@@6E这种情况,但是我没遇到过,所以暂时忽略
		bool is_potential_msvc_vtable = name_buffer.starts_with("??_7") && name_buffer.find("@@6B") != qstring::npos;
		bool is_potential_gcc_vtable = name_buffer.starts_with("_ZTV");

		if (is_potential_msvc_vtable || is_potential_gcc_vtable)
		{
			//目前只处理MSVC的情况
			qstring demangled_name = demangle_name(name_buffer.c_str(), MNG_NOPTRTYP);
			auto pos = demangled_name.find("::`vftable'{for `");
			if (pos == qstring::npos)
			{
				continue;
			}
			else
			{
				if (!demangled_name.starts_with("const ") || !demangled_name.ends_with("'}"))
				{
					ASSERT(false);
					continue;
				}
			}

			//msg("\n[+] C++: 发现潜在虚函数表: '%s' 地址: %a\n", demangled_name.c_str(), ea);
			ea_t current_entry_ea = ea;
			int entry_index = 0;
			bool gcc_parsing_metadata = is_potential_gcc_vtable;
			int gcc_metadata_parsed_count = 0;

			int ItemCount = 0;
			for (int k = 0; k < 512; ++k)// 安全限制
			{
				ea_t vfunc_ptr_val = (ptr_size == 8) ? get_qword(current_entry_ea) : get_dword(current_entry_ea);

				// ... (后续的条目处理逻辑和之前一样) ...
				if (vfunc_ptr_val == 0 || vfunc_ptr_val == BADADDR)
				{
					if (entry_index > 0) { /* ... */ }
					else if (entry_index == 0 && (is_potential_msvc_vtable || is_potential_gcc_vtable)) { /* ... */ }
					break;
				}
				// ... (thunk 解析, 函数名获取, 代码/数据判断) ...
				// (确保这里的逻辑也使用正确的 ptr_size)

				ea_t target_func_ea = vfunc_ptr_val;

				//qstring func_name_display = get_function_name_or_addr_cpp(vfunc_ptr_val);

				// Check if the *final* target (after thunk resolution) is __purecall
				qstring final_target_name = get_function_name_or_addr_cpp(target_func_ea);
				bool is_purecall = (final_target_name.find("purecall") != qstring::npos);

				if (is_purecall)
				{
					//msg("  [-] C++: 条目 %d: 地址 %a -> 指向 %a (%s) - 已跳过 (pure virtual)\n",
					//	entry_index, current_entry_ea, vfunc_ptr_val, func_name_display.c_str());
					// Still a valid vtable entry in terms of structure, just points to purecall
					// We just log it differently and don't pass it to is_valid_code_ea_cpp for "normal" processing
					gcc_parsing_metadata = false; // A purecall is still a function pointer, not metadata
					break;
				}
				else if (is_valid_code_ea_cpp(target_func_ea))
				{
					gcc_parsing_metadata = false;
					//msg("  [*] C++: 条目 %d: 地址 %a -> 指向函数 %a (%s)\n",
					//	entry_index, current_entry_ea, vfunc_ptr_val, func_name_display.c_str());
					ItemCount++;
				}
				else
				{
					if (gcc_parsing_metadata && gcc_metadata_parsed_count < 2)
					{
						qstring data_name;
						get_visible_name(&data_name, vfunc_ptr_val, GN_VISIBLE | GN_DEMANGLED);
						msg("  [i] C++: 条目 %d: 地址 %a -> 数据 %a (GCC VTable 元数据%s%s)\n",
							entry_index, current_entry_ea, vfunc_ptr_val,
							data_name.empty() ? "" : ": ", data_name.c_str());
						gcc_metadata_parsed_count++;
					}
					else if (is_potential_msvc_vtable && entry_index == 0)
					{
						qstring data_name;
						get_visible_name(&data_name, vfunc_ptr_val, GN_VISIBLE | GN_DEMANGLED);
						msg("  [i] C++: 条目 %d: 地址 %a -> 数据 %a (可能是MSVC RTTI COL 指针%s%s)\n",
							entry_index, current_entry_ea, vfunc_ptr_val,
							data_name.empty() ? "" : ": ", data_name.c_str());
					}
					else
					{
						//msg("  [!] C++: 条目 %d: 地址 %a -> 指向数据? %a (非代码) - 可能已到达函数列表末尾或遇到其他数据\n",
						//	entry_index, current_entry_ea, vfunc_ptr_val);
						if (entry_index > 0 && !gcc_parsing_metadata) { break; }
						if (entry_index == 0 && !(is_potential_msvc_vtable && gcc_metadata_parsed_count == 0)) break;
					}
				}
				current_entry_ea += ptr_size;
				entry_index++;
			}
			if (ItemCount > 0)
			{
				VTableInfo info;
				info.ItemCount = ItemCount;
				auto slen = std::size("const ") - 1;
				auto DClass = demangled_name.substr(slen, pos);
				DClass.replace("class ", "");
				info.DClass = DClass;
				auto mlen = std::size("::`vftable'{for `") - 1;
				auto BaseClass = demangled_name.substr(pos + mlen, demangled_name.length() - 2);
				BaseClass.replace("class ", "");
				info.BaseClass = BaseClass;
				mapBassClassVTableInfo[ea] = info;
			}
		}
	}

	//修正虚表中元素的个数
	for (auto i = mapBassClassVTableInfo.begin(); i != mapBassClassVTableInfo.end(); i++)
	{
		auto i_range_end = i->first + i->second.ItemCount * ptr_size;
		for (auto j = std::next(i); j != mapBassClassVTableInfo.end(); j++)
		{
			if (j->first < i_range_end)
			{
				i->second.ItemCount = (int)((j->first - i->first) / ptr_size);
				break;
			}
		}
	}

	for (auto i = mapBassClassVTableInfo.begin(); i != mapBassClassVTableInfo.end(); i++)
	{
		const auto& info = i->second;

		tinfo_t current_typeinfo;
		// Get type by name
		if (!current_typeinfo.get_named_type(local_til, info.DClass.c_str()))
		{
			ASSERT(false);
			continue;
		}
		tinfo_t base_typeinfo;
		if (!base_typeinfo.get_named_type(local_til, info.BaseClass.c_str()))
		{
			ASSERT(false);
			continue;
		}

		auto offset = get_baseclass_offset(current_typeinfo, base_typeinfo);
		if (offset == -1)
		{
			msg("Error: Could not found the base class offset record: '%s::%s'\n", info.DClass.c_str(), info.BaseClass.c_str());
			continue;
		}
		if (offset == 0)
		{
			//第一个基类没有必要处理
			continue;
		}

		// Create the new this pointer argument type
		tinfo_t new_this_ptr_arg_tif;
		ptr_type_data_t pi;
		pi.obj_type = base_typeinfo;
		pi.parent = current_typeinfo;
		pi.delta = (int32)offset;
		pi.taptr_bits = TAPTR_SHIFTED;
		if (!new_this_ptr_arg_tif.create_ptr(pi))
		{
			msg("Error: Could not create the new this pointer argument type: '%s::%s'\n", info.DClass.c_str(), info.BaseClass.c_str());
			continue;
		}

		ea_t current_entry_ea = i->first;
		for (int k = 0; k < info.ItemCount; k++)
		{
			ea_t vfunc_ptr_val = (ptr_size == 8) ? get_qword(current_entry_ea) : get_dword(current_entry_ea);
			fix_this_ptr_arg_type(vfunc_ptr_val, new_this_ptr_arg_tif);
			current_entry_ea += ptr_size;
		}
	}

	info("The type of the pThis pointer parameter of all interface functions of multiple inheritance classes has been fixed!");
	return true;
}

//--------------------------------------------------------------------------
static char comment[] = "Multiple Inheritance Class Fixer";

//--------------------------------------------------------------------------
//
//      PLUGIN DESCRIPTION BLOCK
//
//--------------------------------------------------------------------------
plugin_t PLUGIN =
{
  IDP_INTERFACE_VERSION,
  PLUGIN_MULTI,         // The plugin can work with multiple idbs in parallel
  init,                 // initialize
  nullptr,
  nullptr,
  comment,              // long comment about the plugin
  nullptr,              // multiline help about the plugin
  "Multiple Inheritance Class Fixer",		// the preferred short name of the plugin
  nullptr,              // the preferred hotkey to run the plugin
};


MultipleInheritanceClassFixer.7z