144 lines
4.1 KiB
C#
144 lines
4.1 KiB
C#
![]() |
/*
|
||
|
Copyright (C) 2014-2019 de4dot@gmail.com
|
||
|
|
||
|
This file is part of dnSpy
|
||
|
|
||
|
dnSpy is free software: you can redistribute it and/or modify
|
||
|
it under the terms of the GNU General Public License as published by
|
||
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
(at your option) any later version.
|
||
|
|
||
|
dnSpy is distributed in the hope that it will be useful,
|
||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
using System;
|
||
|
using dnlib.DotNet;
|
||
|
|
||
|
namespace dnSpy.Analyzer.TreeNodes {
|
||
|
static class ComUtils {
|
||
|
public static bool IsComType(TypeDef type, out Guid guid) {
|
||
|
guid = default;
|
||
|
|
||
|
// type.IsImport (== ComImportAttribute) isn't checked since the CLR doesn't require
|
||
|
// it to be set. If it has a GuidAttribute, it's probably a COM type.
|
||
|
|
||
|
if (!type.IsInterface)
|
||
|
return false;
|
||
|
|
||
|
var ca = type.CustomAttributes.Find("System.Runtime.InteropServices.GuidAttribute");
|
||
|
if (ca is null || ca.ConstructorArguments.Count != 1)
|
||
|
return false;
|
||
|
if (!(ca.ConstructorArguments[0].Value is UTF8String guidStr) || !Guid.TryParse(guidStr, out guid))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public static bool ComEquals(TypeDef type, ref Guid guid) {
|
||
|
// type.IsImport (== ComImportAttribute) isn't checked since the CLR doesn't require
|
||
|
// it to be set. If it has a GuidAttribute, it's probably a COM type.
|
||
|
|
||
|
if (!type.IsInterface)
|
||
|
return false;
|
||
|
|
||
|
var ca = type.CustomAttributes.Find("System.Runtime.InteropServices.GuidAttribute");
|
||
|
if (ca is null || ca.ConstructorArguments.Count != 1)
|
||
|
return false;
|
||
|
if (!(ca.ConstructorArguments[0].Value is UTF8String guidStr) || !Guid.TryParse(guidStr, out var g))
|
||
|
return false;
|
||
|
|
||
|
return g == guid;
|
||
|
}
|
||
|
|
||
|
static bool IsVtblGap(MethodDef method) => IsVtblGap(method, out _);
|
||
|
|
||
|
public static bool IsVtblGap(MethodDef method, out int count) {
|
||
|
count = 0;
|
||
|
if (!(method.IsVirtual || method.IsAbstract))
|
||
|
return false;
|
||
|
if (!method.IsRuntimeSpecialName)
|
||
|
return false;
|
||
|
string name = method.Name;
|
||
|
const string vtblGapPrefix = "_VtblGap";
|
||
|
if (name is null || !name.StartsWith(vtblGapPrefix))
|
||
|
return false;
|
||
|
int i = vtblGapPrefix.Length;
|
||
|
while (i < name.Length) {
|
||
|
var c = name[i];
|
||
|
if (!('0' <= c && c <= '9'))
|
||
|
break;
|
||
|
i++;
|
||
|
}
|
||
|
if (i == name.Length)
|
||
|
count = 1;
|
||
|
else {
|
||
|
if (name[i++] != '_')
|
||
|
return false;
|
||
|
if (i == name.Length)
|
||
|
return false;
|
||
|
while (i < name.Length) {
|
||
|
var c = name[i];
|
||
|
if (!('0' <= c && c <= '9'))
|
||
|
break;
|
||
|
count = (ushort)((uint)count * 10 + c - '0');
|
||
|
i++;
|
||
|
}
|
||
|
if (i != name.Length)
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static int GetVtblIndex(MethodDef method) {
|
||
|
int vtblIndex = 0;
|
||
|
var methods = method.DeclaringType.Methods;
|
||
|
for (int i = 0; i < methods.Count; i++) {
|
||
|
var m = methods[i];
|
||
|
if (!(m.IsVirtual || m.IsAbstract))
|
||
|
continue;
|
||
|
if (m == method)
|
||
|
return vtblIndex;
|
||
|
if (IsVtblGap(m, out var count))
|
||
|
vtblIndex += count;
|
||
|
else
|
||
|
vtblIndex++;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
public static void GetMemberInfo(MethodDef method, out bool isComType, out Guid comGuid, out int vtblIndex) {
|
||
|
comGuid = default;
|
||
|
isComType = (method.IsVirtual || method.IsAbstract) && !IsVtblGap(method) && IsComType(method.DeclaringType, out comGuid);
|
||
|
if (isComType)
|
||
|
vtblIndex = GetVtblIndex(method);
|
||
|
else
|
||
|
vtblIndex = -1;
|
||
|
}
|
||
|
|
||
|
public static MethodDef? GetMethod(TypeDef type, int vtblIndex) {
|
||
|
int currentVtblIndex = 0;
|
||
|
var methods = type.Methods;
|
||
|
for (int i = 0; i < methods.Count; i++) {
|
||
|
var method = methods[i];
|
||
|
if (!(method.IsVirtual || method.IsAbstract))
|
||
|
continue;
|
||
|
if (!IsVtblGap(method, out int count)) {
|
||
|
if (currentVtblIndex == vtblIndex)
|
||
|
return method;
|
||
|
count = 1;
|
||
|
}
|
||
|
currentVtblIndex += count;
|
||
|
if (currentVtblIndex > vtblIndex)
|
||
|
break;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
}
|