SHA256
Add CommonWarningDialogUI and RegistryUtil to Support NVIDIA Driver Warning
This commit is contained in:
7 files changed
+539
-10
No files matched your search
+42
-1
@@ -7,13 +7,28 @@ namespace ADOFAIRenderer.Core
|
||||
public class FirstUpdateHook : MonoBehaviour
|
||||
{
|
||||
private bool _checkedFFmpeg;
|
||||
private bool _checkedNvidiaDriver;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_checkedFFmpeg)
|
||||
{
|
||||
if (FFmpegDownloadUI.Instance) return;
|
||||
|
||||
if (!FFmpegDownloadUI.CheckFFmpegExists())
|
||||
{
|
||||
FFmpegDownloadUI.ShowDownloadUI();
|
||||
return;
|
||||
}
|
||||
_checkedFFmpeg = true;
|
||||
FFmpegDownloadUI.ShowDownloadUI();
|
||||
}
|
||||
|
||||
if (!_checkedNvidiaDriver)
|
||||
{
|
||||
if (CommonWarningDialogUI.Instance) return;
|
||||
|
||||
_checkedNvidiaDriver = true;
|
||||
CheckNvidiaDriverWarning();
|
||||
}
|
||||
|
||||
if (RenderManager.Instance)
|
||||
@@ -21,5 +36,31 @@ namespace ADOFAIRenderer.Core
|
||||
RenderManager.Instance.OnPreRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckNvidiaDriverWarning()
|
||||
{
|
||||
var driverVer = Util.GPUUtil.GetNvidiaDriverVersion();
|
||||
if (driverVer is null or >= 610.00f) return;
|
||||
|
||||
var currentVersion = driverVer.Value.ToString("F2");
|
||||
|
||||
CommonWarningDialogUI.ShowDialog(
|
||||
"NVIDIA 그래픽 드라이버 업데이트 권장",
|
||||
$"현재 감지된 NVIDIA 그래픽 드라이버 버전: <b><color=#ff5555>{currentVersion}</color></b>\n\n하드웨어 가속(NVENC)을 사용하여 영상을 렌더링하려면 드라이버 버전 <b><color=#55ff55>610.00 이상</color></b>이 필요합니다.\n\n드라이버 버전이 낮으면 인코딩이 실패하거나 소프트웨어(CPU) 인코딩으로 대체되어 렌더링이 매우 느려집니다. 드라이버 업데이트를 진행해 주시기 바랍니다.",
|
||||
new DialogButton("드라이버 업데이트", () =>
|
||||
{
|
||||
Application.OpenURL("https://www.nvidia.com/Download/index.aspx");
|
||||
Application.Quit();
|
||||
}, new Color(0.0f, 0.8f, 1.0f, 1.0f)),
|
||||
new DialogButton("기능 비활성화", () =>
|
||||
{
|
||||
RenderMod.IsFeatureDisabled = true;
|
||||
}),
|
||||
new DialogButton("이번만 그냥 진행", () =>
|
||||
{
|
||||
// 아무 동작 안 함
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ namespace ADOFAIRenderer.Patch.Game
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void AddButtonRender(scnEditor __instance)
|
||||
{
|
||||
if (RenderMod.IsFeatureDisabled) return;
|
||||
|
||||
var cloneTarget = __instance.buttonExit!.gameObject;
|
||||
var buttonContainer = cloneTarget.transform.parent!;
|
||||
|
||||
@@ -33,12 +35,11 @@ namespace ADOFAIRenderer.Patch.Game
|
||||
// ReSharper disable once Unity.UnknownResource
|
||||
// images[0] is the background on the root, images[1] is the icon on the child
|
||||
var sprite = Resources.Load<Sprite>("LevelEditor/LevelEvents/MoveCamera");
|
||||
if (sprite != null)
|
||||
{
|
||||
var iconImage = images[1];
|
||||
iconImage.sprite = sprite;
|
||||
iconImage.rectTransform.sizeDelta = new Vector2(24, 24);
|
||||
}
|
||||
if (sprite == null) return;
|
||||
|
||||
var iconImage = images[1];
|
||||
iconImage.sprite = sprite;
|
||||
iconImage.rectTransform.sizeDelta = new Vector2(24, 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@ namespace ADOFAIRenderer
|
||||
public static AssetBundle FontBundle = null!;
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
public static AssetBundle SceneBundle = null!;
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
public static AssetBundle ShaderBundle = null!;
|
||||
public static ComputeShader ColorConversionShader = null!;
|
||||
public static bool IsFeatureDisabled = false;
|
||||
|
||||
public override void OnInitializeMelon()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace ADOFAIRenderer.UI
|
||||
{
|
||||
public class DialogButton
|
||||
{
|
||||
public string Text;
|
||||
public Action OnClick;
|
||||
public Color? BackgroundColor;
|
||||
|
||||
public DialogButton(string text, Action onClick, Color? backgroundColor = null)
|
||||
{
|
||||
Text = text;
|
||||
OnClick = onClick;
|
||||
BackgroundColor = backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
public class CommonWarningDialogUI : MonoBehaviour
|
||||
{
|
||||
public static CommonWarningDialogUI? Instance { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static TMP_FontAsset GetNanumSquareNeoBdFontAsset()
|
||||
{
|
||||
TMP_FontAsset? font = null;
|
||||
if (RenderMod.FontBundle)
|
||||
{
|
||||
try
|
||||
{
|
||||
font = RenderMod.FontBundle.LoadAsset<TMP_FontAsset>("NanumSquareNeoOTF-Bd SDF");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 로딩 실패
|
||||
}
|
||||
}
|
||||
|
||||
// 폴백 처리
|
||||
if (!font)
|
||||
{
|
||||
try
|
||||
{
|
||||
font = RDString.fontData.fontTMP;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
if (!font)
|
||||
{
|
||||
// ReSharper disable once Unity.UnknownResource
|
||||
font = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
|
||||
}
|
||||
|
||||
if (font) return font;
|
||||
var fonts = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
|
||||
if (fonts.Length > 0) font = fonts[0];
|
||||
|
||||
return font!;
|
||||
}
|
||||
|
||||
public static void ShowDialog(string title, string description, params DialogButton[] buttons)
|
||||
{
|
||||
if (buttons.Length == 0) return;
|
||||
|
||||
var obj = new GameObject("CommonWarningDialogUI");
|
||||
DontDestroyOnLoad(obj);
|
||||
var dialog = obj.AddComponent<CommonWarningDialogUI>();
|
||||
dialog.Initialize(title, description, buttons);
|
||||
}
|
||||
|
||||
private void Initialize(string title, string description, DialogButton[] buttons)
|
||||
{
|
||||
// UI 레이어(5) 설정
|
||||
gameObject.layer = 5;
|
||||
|
||||
// Canvas 설정
|
||||
var canvas = gameObject.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 32766; // FFmpegDownloadUI(32767) 바로 아래
|
||||
|
||||
// 해상도 대응 캔버스 스케일러 설정
|
||||
var scaler = gameObject.AddComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920, 1080);
|
||||
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
|
||||
gameObject.AddComponent<GraphicRaycaster>();
|
||||
|
||||
// 1. 전체 화면 오버레이 배경 생성
|
||||
var bgPanelObj = new GameObject("BackgroundPanel");
|
||||
bgPanelObj.transform.SetParent(transform, false);
|
||||
bgPanelObj.layer = 5;
|
||||
|
||||
var bgImage = bgPanelObj.AddComponent<Image>();
|
||||
bgImage.color = new Color(0f, 0f, 0f, 0.65f); // 어두운 투명 오버레이
|
||||
bgImage.raycastTarget = true;
|
||||
|
||||
var bgRect = bgPanelObj.GetComponent<RectTransform>();
|
||||
bgRect.anchorMin = Vector2.zero;
|
||||
bgRect.anchorMax = Vector2.one;
|
||||
bgRect.offsetMin = Vector2.zero;
|
||||
bgRect.offsetMax = Vector2.zero;
|
||||
|
||||
var font = GetNanumSquareNeoBdFontAsset();
|
||||
|
||||
// 2. 모달 컨테이너 생성
|
||||
var modalContainerObj = new GameObject("ModalContainer");
|
||||
modalContainerObj.transform.SetParent(bgPanelObj.transform, false);
|
||||
modalContainerObj.layer = 5;
|
||||
|
||||
var modalImage = modalContainerObj.AddComponent<Image>();
|
||||
modalImage.color = new Color(0.08f, 0.08f, 0.1f, 0.88f); // 기존 UI의 배경 톤과 동일
|
||||
|
||||
var modalOutline = modalContainerObj.AddComponent<Outline>();
|
||||
modalOutline.effectColor = Color.white;
|
||||
modalOutline.effectDistance = new Vector2(3.0f, -3.0f); // 기존 UI 외곽선 스타일 일관성
|
||||
|
||||
var modalRect = modalContainerObj.GetComponent<RectTransform>();
|
||||
modalRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
modalRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
modalRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
modalRect.sizeDelta = new Vector2(650, 360);
|
||||
|
||||
// 3. 타이틀 텍스트 생성
|
||||
var titleObj = new GameObject("TitleText");
|
||||
titleObj.transform.SetParent(modalContainerObj.transform, false);
|
||||
titleObj.layer = 5;
|
||||
|
||||
var titleText = titleObj.AddComponent<TextMeshProUGUI>();
|
||||
titleText.text = title;
|
||||
titleText.fontSize = 24;
|
||||
titleText.font = font;
|
||||
titleText.color = new Color(1.0f, 0.8f, 0.2f, 1.0f); // 경고 테마에 맞는 주황/황색 계열
|
||||
titleText.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
var titleRect = titleObj.GetComponent<RectTransform>();
|
||||
titleRect.anchorMin = new Vector2(0, 1);
|
||||
titleRect.anchorMax = new Vector2(1, 1);
|
||||
titleRect.pivot = new Vector2(0.5f, 1);
|
||||
titleRect.anchoredPosition = new Vector2(0, -35);
|
||||
titleRect.sizeDelta = new Vector2(0, 40);
|
||||
|
||||
// 4. 본문 설명 텍스트 생성
|
||||
var descObj = new GameObject("DescriptionText");
|
||||
descObj.transform.SetParent(modalContainerObj.transform, false);
|
||||
descObj.layer = 5;
|
||||
|
||||
var descText = descObj.AddComponent<TextMeshProUGUI>();
|
||||
descText.text = description;
|
||||
descText.fontSize = 15;
|
||||
descText.font = font;
|
||||
descText.color = new Color(0.85f, 0.85f, 0.85f, 1f);
|
||||
descText.alignment = TextAlignmentOptions.Center;
|
||||
descText.lineSpacing = 8;
|
||||
|
||||
var descRect = descObj.GetComponent<RectTransform>();
|
||||
descRect.anchorMin = new Vector2(0, 0.5f);
|
||||
descRect.anchorMax = new Vector2(1, 1);
|
||||
descRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
descRect.anchoredPosition = new Vector2(0, -65);
|
||||
descRect.sizeDelta = new Vector2(-40, 120);
|
||||
|
||||
// 5. 버튼 컨테이너 생성
|
||||
var buttonContainerObj = new GameObject("ButtonContainer", typeof(RectTransform));
|
||||
buttonContainerObj.transform.SetParent(modalContainerObj.transform, false);
|
||||
buttonContainerObj.layer = 5;
|
||||
|
||||
var btnContainerRect = buttonContainerObj.GetComponent<RectTransform>();
|
||||
btnContainerRect.anchorMin = new Vector2(0, 0);
|
||||
btnContainerRect.anchorMax = new Vector2(1, 0);
|
||||
btnContainerRect.pivot = new Vector2(0.5f, 0);
|
||||
btnContainerRect.anchoredPosition = new Vector2(0, 35);
|
||||
btnContainerRect.sizeDelta = new Vector2(0, 50);
|
||||
|
||||
// 6. 가변 버튼 동적 배치 및 스타일링
|
||||
float totalWidth = 560f;
|
||||
float spacing = 15f;
|
||||
float buttonHeight = 45f;
|
||||
int numButtons = buttons.Length;
|
||||
|
||||
float buttonWidth = numButtons == 1
|
||||
? 200f
|
||||
: (totalWidth - (spacing * (numButtons - 1))) / numButtons;
|
||||
|
||||
float startX = numButtons == 1
|
||||
? 0
|
||||
: (-totalWidth / 2f) + (buttonWidth / 2f);
|
||||
|
||||
for (int i = 0; i < numButtons; i++)
|
||||
{
|
||||
var btnData = buttons[i];
|
||||
var btnObj = new GameObject($"Button_{i}");
|
||||
btnObj.transform.SetParent(buttonContainerObj.transform, false);
|
||||
btnObj.layer = 5;
|
||||
|
||||
// 기본 색상 정의
|
||||
Color baseColor = btnData.BackgroundColor ?? new Color(0.2f, 0.2f, 0.23f, 1f); // 커스텀 색상 또는 회색톤 기본값
|
||||
Color hoverColor = ApplyColorBrightness(baseColor, 1.2f);
|
||||
Color pressedColor = ApplyColorBrightness(baseColor, 0.8f);
|
||||
|
||||
var btnImage = btnObj.AddComponent<Image>();
|
||||
btnImage.color = baseColor;
|
||||
btnImage.raycastTarget = true;
|
||||
|
||||
var btn = btnObj.AddComponent<Button>();
|
||||
SetupButtonStyle(btn, baseColor, hoverColor, pressedColor);
|
||||
|
||||
// 클릭 콜백 등록
|
||||
btn.onClick.AddListener(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
btnData.OnClick?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MelonLoader.Melon<RenderMod>.Logger.Error($"Dialog Button Click Event Error: {ex}");
|
||||
}
|
||||
Destroy(gameObject);
|
||||
});
|
||||
|
||||
var btnRect = btnObj.GetComponent<RectTransform>();
|
||||
btnRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
btnRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
btnRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
|
||||
float posX = numButtons == 1
|
||||
? 0
|
||||
: startX + i * (buttonWidth + spacing);
|
||||
|
||||
btnRect.anchoredPosition = new Vector2(posX, 0);
|
||||
btnRect.sizeDelta = new Vector2(buttonWidth, buttonHeight);
|
||||
|
||||
// 텍스트 라벨 추가
|
||||
var txtObj = new GameObject("Text");
|
||||
txtObj.transform.SetParent(btnObj.transform, false);
|
||||
txtObj.layer = 5;
|
||||
|
||||
var text = txtObj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = btnData.Text;
|
||||
text.fontSize = 14;
|
||||
text.font = font;
|
||||
text.color = Color.white;
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
|
||||
var txtRect = txtObj.GetComponent<RectTransform>();
|
||||
txtRect.anchorMin = Vector2.zero;
|
||||
txtRect.anchorMax = Vector2.one;
|
||||
txtRect.offsetMin = Vector2.zero;
|
||||
txtRect.offsetMax = Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupButtonStyle(Button button, Color normalColor, Color hoverColor, Color pressedColor)
|
||||
{
|
||||
button.transition = Selectable.Transition.ColorTint;
|
||||
var colors = button.colors;
|
||||
colors.normalColor = normalColor;
|
||||
colors.highlightedColor = hoverColor;
|
||||
colors.pressedColor = pressedColor;
|
||||
colors.disabledColor = new Color(0.3f, 0.3f, 0.3f, 1f);
|
||||
button.colors = colors;
|
||||
}
|
||||
|
||||
private Color ApplyColorBrightness(Color color, float factor)
|
||||
{
|
||||
float r = Mathf.Clamp01(color.r * factor);
|
||||
float g = Mathf.Clamp01(color.g * factor);
|
||||
float b = Mathf.Clamp01(color.b * factor);
|
||||
return new Color(r, g, b, color.a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,12 @@ namespace ADOFAIRenderer.UI
|
||||
private System.Threading.CancellationTokenSource? _cts;
|
||||
private readonly System.Collections.Concurrent.ConcurrentQueue<Action> _mainThreadQueue = new();
|
||||
|
||||
private static bool CheckFFmpegExists()
|
||||
public static bool CheckFFmpegExists()
|
||||
{
|
||||
var userLibs = MelonEnvironment.UserLibsDirectory;
|
||||
if (!Directory.Exists(userLibs)) return false;
|
||||
|
||||
string searchPattern = Application.platform switch
|
||||
var searchPattern = Application.platform switch
|
||||
{
|
||||
RuntimePlatform.WindowsPlayer => "avcodec-*.dll",
|
||||
RuntimePlatform.OSXPlayer => "libavcodec.*.dylib",
|
||||
@@ -58,8 +58,11 @@ namespace ADOFAIRenderer.UI
|
||||
obj.AddComponent<FFmpegDownloadUI>();
|
||||
}
|
||||
|
||||
public static FFmpegDownloadUI? Instance { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
if (CheckFFmpegExists())
|
||||
{
|
||||
Destroy(gameObject);
|
||||
@@ -647,6 +650,10 @@ namespace ADOFAIRenderer.UI
|
||||
private void OnDestroy()
|
||||
{
|
||||
_cts?.Dispose();
|
||||
if (Instance == this)
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
-1
@@ -34,7 +34,7 @@ namespace ADOFAIRenderer.Util
|
||||
};
|
||||
|
||||
private static GPUVendor _cachedVendor = GPUVendor.Unknown;
|
||||
private static bool _hasCachedVendor = false;
|
||||
private static bool _hasCachedVendor;
|
||||
|
||||
private static GPUVendor GetActiveGPUVendor()
|
||||
{
|
||||
@@ -72,6 +72,64 @@ namespace ADOFAIRenderer.Util
|
||||
return _cachedVendor;
|
||||
}
|
||||
|
||||
private static double? _cachedNvidiaVersion;
|
||||
private static bool _hasCachedNvidiaVersion;
|
||||
|
||||
public static double? GetNvidiaDriverVersion()
|
||||
{
|
||||
if (_hasCachedNvidiaVersion) return _cachedNvidiaVersion;
|
||||
|
||||
_hasCachedNvidiaVersion = true;
|
||||
_cachedNvidiaVersion = null;
|
||||
|
||||
if (GetActiveGPUVendor() != GPUVendor.NVIDIA) return null;
|
||||
|
||||
// 1. Try parsing from UnityEngine.SystemInfo.graphicsDeviceVersion
|
||||
var deviceVersion = UnityEngine.SystemInfo.graphicsDeviceVersion;
|
||||
if (!string.IsNullOrEmpty(deviceVersion))
|
||||
{
|
||||
var match = Regex.Match(deviceVersion, @"\b(\d+)\.(\d+)\.(\d+)\.(\d+)\b");
|
||||
if (match.Success)
|
||||
{
|
||||
if (int.TryParse(match.Groups[3].Value, out int sub) && int.TryParse(match.Groups[4].Value, out int build))
|
||||
{
|
||||
var nvidiaVerInt = sub % 10 * 10000 + build;
|
||||
_cachedNvidiaVersion = nvidiaVerInt / 100.0;
|
||||
Melon<RenderMod>.Logger.Msg($"Parsed NVIDIA Driver Version from SystemInfo: {_cachedNvidiaVersion:F2}");
|
||||
return _cachedNvidiaVersion;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback to Windows Registry (only on Windows NT platforms via RegistryUtil)
|
||||
if (System.Environment.OSVersion.Platform != System.PlatformID.Win32NT) return null;
|
||||
const string classKey = @"SYSTEM\CurrentControlSet\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}";
|
||||
var subkeys = RegistryUtil.GetHKLMSubKeyNames(classKey);
|
||||
if (subkeys == null) return null;
|
||||
|
||||
foreach (var subkey in subkeys)
|
||||
{
|
||||
var subkeyPath = classKey + "\\" + subkey;
|
||||
var provider = RegistryUtil.GetHKLMValue(subkeyPath, "ProviderName");
|
||||
if (provider == null || !provider.Contains("NVIDIA")) continue;
|
||||
|
||||
var driverVersionStr = RegistryUtil.GetHKLMValue(subkeyPath, "DriverVersion");
|
||||
if (string.IsNullOrEmpty(driverVersionStr)) continue;
|
||||
|
||||
var regMatch = Regex.Match(driverVersionStr, @"\b(\d+)\.(\d+)\.(\d+)\.(\d+)\b");
|
||||
if (!regMatch.Success) continue;
|
||||
if (!int.TryParse(regMatch.Groups[3].Value, out var sub) ||
|
||||
!int.TryParse(regMatch.Groups[4].Value, out var build)) continue;
|
||||
|
||||
var nvidiaVerInt = sub % 10 * 10000 + build;
|
||||
_cachedNvidiaVersion = nvidiaVerInt / 100f;
|
||||
Melon<RenderMod>.Logger.Msg($"Parsed NVIDIA Driver Version from Registry (RegistryUtil): {_cachedNvidiaVersion:F2}");
|
||||
return _cachedNvidiaVersion;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<string> GetPrioritizedEncoderCandidates(Codec codec)
|
||||
{
|
||||
if (!CodecMap.TryGetValue(codec, out var list)) return new List<string>();
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
|
||||
namespace ADOFAIRenderer.Util
|
||||
{
|
||||
public static class RegistryUtil
|
||||
{
|
||||
private static readonly Type? RegistryKeyType;
|
||||
private static readonly Type? RegistryHiveType;
|
||||
private static readonly Type? RegistryViewType;
|
||||
private static readonly System.Reflection.MethodInfo? OpenBaseKeyMethod;
|
||||
private static readonly System.Reflection.MethodInfo? OpenSubKeyMethod;
|
||||
private static readonly System.Reflection.MethodInfo? GetSubKeyNamesMethod;
|
||||
private static readonly System.Reflection.MethodInfo? GetValueMethod;
|
||||
|
||||
static RegistryUtil()
|
||||
{
|
||||
try
|
||||
{
|
||||
RegistryKeyType = Type.GetType("Microsoft.Win32.RegistryKey");
|
||||
RegistryHiveType = Type.GetType("Microsoft.Win32.RegistryHive");
|
||||
RegistryViewType = Type.GetType("Microsoft.Win32.RegistryView");
|
||||
|
||||
if (RegistryKeyType == null || RegistryHiveType == null || RegistryViewType == null)
|
||||
{
|
||||
var mscorlib = typeof(object).Assembly;
|
||||
RegistryKeyType = mscorlib.GetType("Microsoft.Win32.RegistryKey");
|
||||
RegistryHiveType = mscorlib.GetType("Microsoft.Win32.RegistryHive");
|
||||
RegistryViewType = mscorlib.GetType("Microsoft.Win32.RegistryView");
|
||||
}
|
||||
|
||||
if (RegistryKeyType != null)
|
||||
{
|
||||
OpenBaseKeyMethod = RegistryKeyType.GetMethod("OpenBaseKey", new[] { RegistryHiveType!, RegistryViewType! });
|
||||
OpenSubKeyMethod = RegistryKeyType.GetMethod("OpenSubKey", new[] { typeof(string) });
|
||||
GetSubKeyNamesMethod = RegistryKeyType.GetMethod("GetSubKeyNames");
|
||||
GetValueMethod = RegistryKeyType.GetMethod("GetValue", new[] { typeof(string) });
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void CloseKey(object? key)
|
||||
{
|
||||
if (key == null || RegistryKeyType == null) return;
|
||||
try
|
||||
{
|
||||
if (key is IDisposable disposable)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
var closeMethod = RegistryKeyType.GetMethod("Close");
|
||||
closeMethod?.Invoke(key, null);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
public static string[]? GetHKLMSubKeyNames(string parentKeyPath)
|
||||
{
|
||||
if (RegistryKeyType == null || OpenBaseKeyMethod == null || OpenSubKeyMethod == null || GetSubKeyNamesMethod == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
object hiveLocalMachine = Enum.ToObject(RegistryHiveType!, 0x80000002);
|
||||
object viewRegistry64 = Enum.ToObject(RegistryViewType!, 256);
|
||||
|
||||
object? baseKey = OpenBaseKeyMethod.Invoke(null, new[] { hiveLocalMachine, viewRegistry64 });
|
||||
if (baseKey == null) return null;
|
||||
|
||||
object? key = OpenSubKeyMethod.Invoke(baseKey, new object[] { parentKeyPath });
|
||||
if (key == null)
|
||||
{
|
||||
CloseKey(baseKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
string[]? subkeyNames = GetSubKeyNamesMethod.Invoke(key, null) as string[];
|
||||
CloseKey(key);
|
||||
CloseKey(baseKey);
|
||||
return subkeyNames;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string? GetHKLMValue(string keyPath, string valueName)
|
||||
{
|
||||
if (RegistryKeyType == null || OpenBaseKeyMethod == null || OpenSubKeyMethod == null || GetValueMethod == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
object hiveLocalMachine = Enum.ToObject(RegistryHiveType!, 0x80000002);
|
||||
object viewRegistry64 = Enum.ToObject(RegistryViewType!, 256);
|
||||
|
||||
object? baseKey = OpenBaseKeyMethod.Invoke(null, new[] { hiveLocalMachine, viewRegistry64 });
|
||||
if (baseKey == null) return null;
|
||||
|
||||
object? key = OpenSubKeyMethod.Invoke(baseKey, new object[] { keyPath });
|
||||
if (key == null)
|
||||
{
|
||||
CloseKey(baseKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
string? value = GetValueMethod.Invoke(key, new object[] { valueName }) as string;
|
||||
CloseKey(key);
|
||||
CloseKey(baseKey);
|
||||
return value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user