Files
isphere-ai-bridge/native/ISphereWinHelper/RuntimeProbe.cs
2026-07-10 10:59:48 +08:00

333 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ISphereWinHelper
{
internal static class RuntimeProbe
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process);
public static Dictionary<string, object> ProbeClientRuntime(Dictionary<string, object> args)
{
var processNames = GetStringList(args, "process_names", new[] { "IMPlatformClient", "IMPP.ISphere", "iSphere", "importal" });
var moduleHints = GetStringList(args, "module_hints", new[]
{
"IMPlatformClient.exe",
"smack.dll",
"IMPP.Interface.dll",
"IMPP.ServiceBase.dll",
"IMPP.Service.dll",
"IMPP.Common.dll",
"IMPP.Helper.dll",
"IMPP.Model.dll",
"IMPP.Util.dll",
"IMPP.UI.dll",
"TcpFileTransfer.dll",
"HYHC.IMPP.DAL.dll",
"INetwork.dll",
"IOClientNetwork.dll"
});
bool includeModules = HelperProtocol.GetBool(args, "include_modules", true);
int maxModules = HelperProtocol.GetInt(args, "max_modules", 120);
if (maxModules < 0)
{
maxModules = 0;
}
var targets = new List<Dictionary<string, object>>();
foreach (Process process in Process.GetProcesses())
{
using (process)
{
string processName = SafeProcessName(process);
if (!MatchesAny(processName, processNames))
{
continue;
}
targets.Add(DescribeProcess(process, moduleHints, includeModules, maxModules));
}
}
targets.Sort(delegate(Dictionary<string, object> left, Dictionary<string, object> right)
{
int leftScore = Convert.ToInt32(left["score"]);
int rightScore = Convert.ToInt32(right["score"]);
int scoreCompare = rightScore.CompareTo(leftScore);
if (scoreCompare != 0)
{
return scoreCompare;
}
return Convert.ToInt32(left["pid"]).CompareTo(Convert.ToInt32(right["pid"]));
});
return new Dictionary<string, object>
{
{ "probe_mode", "read_only_process_module_inventory" },
{ "host", new Dictionary<string, object>
{
{ "is_64_bit_os", Environment.Is64BitOperatingSystem },
{ "is_64_bit_process", Environment.Is64BitProcess },
{ "machine_name", Environment.MachineName }
}
},
{ "process_names", processNames },
{ "module_hints", moduleHints },
{ "target_count", targets.Count },
{ "targets", targets }
};
}
private static Dictionary<string, object> DescribeProcess(Process process, List<string> moduleHints, bool includeModules, int maxModules)
{
string executablePath = "";
string mainModuleName = "";
string mainModuleVersion = "";
string mainModuleError = "";
try
{
ProcessModule mainModule = process.MainModule;
if (mainModule != null)
{
executablePath = mainModule.FileName ?? "";
mainModuleName = mainModule.ModuleName ?? "";
FileVersionInfo versionInfo = mainModule.FileVersionInfo;
if (versionInfo != null)
{
mainModuleVersion = versionInfo.FileVersion ?? "";
}
}
}
catch (Exception ex)
{
mainModuleError = ex.GetType().Name + ": " + ex.Message;
}
bool? isWow64 = GetIsWow64(process);
string bitness = "unknown";
if (Environment.Is64BitOperatingSystem && isWow64.HasValue)
{
bitness = isWow64.Value ? "x86" : "x64";
}
else if (!Environment.Is64BitOperatingSystem)
{
bitness = "x86";
}
string moduleProbeError = "";
var modules = new List<Dictionary<string, object>>();
var assemblyPresence = NewPresenceMap(moduleHints);
if (includeModules)
{
try
{
int count = 0;
foreach (ProcessModule module in process.Modules)
{
if (count >= maxModules)
{
break;
}
string moduleName = module.ModuleName ?? "";
string modulePath = module.FileName ?? "";
string matchedHint = FindMatchedHint(moduleName, modulePath, moduleHints);
if (!string.IsNullOrEmpty(matchedHint))
{
assemblyPresence[matchedHint] = true;
}
modules.Add(new Dictionary<string, object>
{
{ "name", moduleName },
{ "path", modulePath },
{ "matched_hint", matchedHint }
});
count++;
}
}
catch (Exception ex)
{
moduleProbeError = ex.GetType().Name + ": " + ex.Message;
}
}
if (!string.IsNullOrEmpty(mainModuleName))
{
string matchedMain = FindMatchedHint(mainModuleName, executablePath, moduleHints);
if (!string.IsNullOrEmpty(matchedMain))
{
assemblyPresence[matchedMain] = true;
}
}
int score = Score(process.ProcessName, executablePath, assemblyPresence);
return new Dictionary<string, object>
{
{ "pid", process.Id },
{ "process_name", process.ProcessName },
{ "main_window_title", SafeMainWindowTitle(process) },
{ "main_window_handle", WindowScanner.FormatHwnd(process.MainWindowHandle) },
{ "has_main_window", process.MainWindowHandle != IntPtr.Zero },
{ "executable_path", executablePath },
{ "main_module_name", mainModuleName },
{ "main_module_version", mainModuleVersion },
{ "main_module_error", mainModuleError },
{ "bitness", bitness },
{ "is_wow64", isWow64.HasValue ? (object)isWow64.Value : null },
{ "module_probe_ok", string.IsNullOrEmpty(moduleProbeError) },
{ "module_probe_error", moduleProbeError },
{ "assembly_presence", assemblyPresence },
{ "modules", modules },
{ "score", score }
};
}
private static Dictionary<string, object> NewPresenceMap(List<string> moduleHints)
{
var presence = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (string hint in moduleHints)
{
if (!presence.ContainsKey(hint))
{
presence.Add(hint, false);
}
}
return presence;
}
private static int Score(string processName, string executablePath, Dictionary<string, object> assemblyPresence)
{
int score = 0;
string combined = ((processName ?? "") + " " + (executablePath ?? "")).ToLowerInvariant();
if (combined.Contains("implatformclient")) score += 80;
if (combined.Contains("isphere")) score += 40;
if (combined.Contains("impp")) score += 20;
if (combined.Contains("importal")) score += 10;
foreach (object value in assemblyPresence.Values)
{
if (value is bool && (bool)value)
{
score += 5;
}
}
return score;
}
private static bool? GetIsWow64(Process process)
{
try
{
bool isWow64;
if (IsWow64Process(process.Handle, out isWow64))
{
return isWow64;
}
}
catch
{
}
return null;
}
private static string SafeProcessName(Process process)
{
try
{
return process.ProcessName ?? "";
}
catch
{
return "";
}
}
private static string SafeMainWindowTitle(Process process)
{
try
{
return process.MainWindowTitle ?? "";
}
catch
{
return "";
}
}
private static bool MatchesAny(string processName, List<string> candidates)
{
foreach (string candidate in candidates)
{
if (string.Equals(processName, candidate, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static string FindMatchedHint(string moduleName, string modulePath, List<string> hints)
{
foreach (string hint in hints)
{
if (string.Equals(moduleName, hint, StringComparison.OrdinalIgnoreCase))
{
return hint;
}
if (!string.IsNullOrEmpty(modulePath) &&
modulePath.EndsWith("\\" + hint, StringComparison.OrdinalIgnoreCase))
{
return hint;
}
}
return "";
}
private static List<string> GetStringList(Dictionary<string, object> source, string key, string[] defaults)
{
var values = new List<string>();
object raw;
if (source != null && source.TryGetValue(key, out raw) && raw != null)
{
object[] rawArray = raw as object[];
if (rawArray != null)
{
foreach (object item in rawArray)
{
string value = Convert.ToString(item);
if (!string.IsNullOrWhiteSpace(value))
{
values.Add(value.Trim());
}
}
}
else
{
string single = Convert.ToString(raw);
if (!string.IsNullOrWhiteSpace(single))
{
foreach (string part in single.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
string value = part.Trim();
if (!string.IsNullOrWhiteSpace(value))
{
values.Add(value);
}
}
}
}
}
if (values.Count == 0)
{
values.AddRange(defaults);
}
return values;
}
}
}