Initial commit: Complete open-source Decal rebuild
All 5 phases of the open-source Decal rebuild: Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil) Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters - Decal.Core, DecalControls, DecalRender, D3DService Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL Phase 4: DenAgent WinForms tray application Phase 5: WiX installer and build script 25 C# projects building with 0 errors. Native C++ projects require VS 2022 + Windows SDK (x86). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
d1442e3747
1382 changed files with 170725 additions and 0 deletions
281
Managed/Decal.Core/ACHooksImpl.cs
Normal file
281
Managed/Decal.Core/ACHooksImpl.cs
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Core;
|
||||
|
||||
namespace Decal.Core
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("CB8875CD-ABC2-42AD-8175-8908706EED37")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces("Decal.Interop.Core.IACHooksEvents\0\0")]
|
||||
[ProgId("Decal.ACHooks")]
|
||||
public class ACHooksImpl : IACHooks
|
||||
{
|
||||
// COM events
|
||||
public event IACHooksEvents_ObjectDestroyedEventHandler ObjectDestroyed;
|
||||
public event IACHooksEvents_ChatTextInterceptEventHandler ChatTextIntercept;
|
||||
public event IACHooksEvents_ChatParserInterceptEventHandler ChatParserIntercept;
|
||||
public event IACHooksEvents_StatusTextInterceptEventHandler StatusTextIntercept;
|
||||
public event IACHooksEvents_ObjectSelectedEventHandler ObjectSelected;
|
||||
public event IACHooksEvents_MessageProcessedEventHandler MessageProcessed;
|
||||
public event IACHooksEvents_AC3DRegionChangedEventHandler AC3DRegionChanged;
|
||||
public event IACHooksEvents_ContainerOpenedEventHandler ContainerOpened;
|
||||
public event IACHooksEvents_ChatClickInterceptEventHandler ChatClickIntercept;
|
||||
public event IACHooksEvents_RenderPreUIEventHandler RenderPreUI;
|
||||
|
||||
private DecalCoreImpl _decal;
|
||||
private IIdentifyFilter _idFilter;
|
||||
|
||||
// State
|
||||
private int _currentSelection;
|
||||
private int _previousSelection;
|
||||
private int _selectedStackCount;
|
||||
private int _maxSelectedStackCount;
|
||||
private int _combatMode;
|
||||
private int _vendorId;
|
||||
private int _busyState;
|
||||
private int _busyStateId;
|
||||
private int _pointerState;
|
||||
private int _commandInterpreter;
|
||||
private int _openedContainer;
|
||||
private bool _chatState;
|
||||
private double _headingDegrees;
|
||||
private double _headingRadians;
|
||||
private int _landcell;
|
||||
private double _locationX, _locationY, _locationZ;
|
||||
private tagRECT _ac3dRegionRect;
|
||||
private tagRECT _acWindowRect;
|
||||
|
||||
public void SetIDFilter(IIdentifyFilter pIDFilter) => _idFilter = pIDFilter;
|
||||
|
||||
public void SetDecal(object pDecal)
|
||||
{
|
||||
_decal = pDecal as DecalCoreImpl;
|
||||
}
|
||||
|
||||
// Hook availability
|
||||
public int HooksAvail =>
|
||||
(int)(eAvailableHooks.eCurrentSelect | eAvailableHooks.ePrevSelect |
|
||||
eAvailableHooks.eCastSpell | eAvailableHooks.eMoveItem |
|
||||
eAvailableHooks.eSelectItem | eAvailableHooks.eUseItem |
|
||||
eAvailableHooks.eCombatMode | eAvailableHooks.eChatState |
|
||||
eAvailableHooks.eStackCount | eAvailableHooks.eMaxStackCount |
|
||||
eAvailableHooks.eVendorID | eAvailableHooks.eBusyState |
|
||||
eAvailableHooks.eBusyStateID | eAvailableHooks.ePointerState |
|
||||
eAvailableHooks.eMoveItemEx | eAvailableHooks.ePosition |
|
||||
eAvailableHooks.eFaceHeading | eAvailableHooks.eAC3DRegion |
|
||||
eAvailableHooks.eObjectDestroyed | eAvailableHooks.eSendTell |
|
||||
eAvailableHooks.eSetAutorun | eAvailableHooks.eSetCombatMode |
|
||||
eAvailableHooks.eGetMiscInt | eAvailableHooks.eGetAttribute |
|
||||
eAvailableHooks.eGetSkill | eAvailableHooks.eGetVital |
|
||||
eAvailableHooks.eHooksAvailEx);
|
||||
|
||||
public bool HooksAvailEx => true;
|
||||
|
||||
// Chat methods
|
||||
public void AddChatText(string szText, int lColor, int lTarget)
|
||||
{
|
||||
// In the open-source version, we fire this through the chat event system
|
||||
bool eat = false;
|
||||
ChatTextIntercept?.Invoke(szText, lColor, lTarget, ref eat);
|
||||
}
|
||||
|
||||
public void AddChatTextRaw(string szText, int lColor, int lTarget)
|
||||
{
|
||||
AddChatText(szText, lColor, lTarget);
|
||||
}
|
||||
|
||||
public void AddStatusText(string Text)
|
||||
{
|
||||
bool eat = false;
|
||||
StatusTextIntercept?.Invoke(Text, ref eat);
|
||||
}
|
||||
|
||||
public void InvokeChatParser(string Text)
|
||||
{
|
||||
bool eat = false;
|
||||
ChatParserIntercept?.Invoke(Text, ref eat);
|
||||
}
|
||||
|
||||
public void SetIdleTime(double dIdleTimeout) { }
|
||||
|
||||
public void Logout() { }
|
||||
|
||||
// Selection
|
||||
public int CurrentSelection { get => _currentSelection; set => _currentSelection = value; }
|
||||
public int PreviousSelection { get => _previousSelection; set => _previousSelection = value; }
|
||||
public int SelectedStackCount { get => _selectedStackCount; set => _selectedStackCount = value; }
|
||||
public int MaxSelectedStackCount => _maxSelectedStackCount;
|
||||
|
||||
public void SetCursorPosition(int lX, int lY) { }
|
||||
|
||||
// Window/region info
|
||||
public tagRECT AC3DRegionRect => _ac3dRegionRect;
|
||||
public IntPtr AC3DRegionRectPtr => IntPtr.Zero;
|
||||
public tagRECT ACWindowRect => _acWindowRect;
|
||||
|
||||
// State properties
|
||||
public bool ChatState => _chatState;
|
||||
public int BusyState => _busyState;
|
||||
public int BusyStateID => _busyStateId;
|
||||
public int PointerState => _pointerState;
|
||||
public int VendorID => _vendorId;
|
||||
|
||||
// Vendor operations
|
||||
public void VendorBuyListAdd(int lID, int lAmount) { }
|
||||
public void VendorBuyListClear() { }
|
||||
public void VendorBuyAll() { }
|
||||
public void VendorSellListAdd(int lID) { }
|
||||
public void VendorSellListClear() { }
|
||||
public void VendorSellAll() { }
|
||||
|
||||
// Combat
|
||||
public int CombatMode => _combatMode;
|
||||
public void SetCombatMode(int pVal) { _combatMode = pVal; }
|
||||
public void SetAutorun(bool bOnOff) { }
|
||||
public bool FaceHeading(float fHeading, bool bUnknown) { return true; }
|
||||
|
||||
public int CommandInterpreter => _commandInterpreter;
|
||||
|
||||
// Item operations
|
||||
public void SelectItem(int lObjectID)
|
||||
{
|
||||
_previousSelection = _currentSelection;
|
||||
_currentSelection = lObjectID;
|
||||
ObjectSelected?.Invoke(lObjectID);
|
||||
}
|
||||
|
||||
public void GiveItem(int lObject, int lDestination) { }
|
||||
public void ApplyItem(int UseThis, int OnThis) { }
|
||||
public void UseItem(int lObjectID, int lUseState) { }
|
||||
public void UseItemRaw(int lObjectID, int lUseState, int lUseMethod) { }
|
||||
public void MoveItem(int lObjectID, int lPackID, int lSlot, bool bStack) { }
|
||||
public void MoveItemEx(int lObjectID, int lDestinationID) { }
|
||||
public void MoveItemExRaw(int lObject, int lDestination, int lMoveFlags) { }
|
||||
public void DropItem(int lObjectID) { }
|
||||
|
||||
// Spell casting
|
||||
public void CastSpell(int lSpellID, int lObjectID) { }
|
||||
|
||||
// Object queries
|
||||
public bool IsValidObject(int lGUID) => false;
|
||||
public int GetWeenieObjectPtr(int lObjectID) => 0;
|
||||
public int GetPhysicsObjectPtr(int lObjectID) => 0;
|
||||
|
||||
// Position
|
||||
public double HeadingDegrees => _headingDegrees;
|
||||
public double HeadingRadians => _headingRadians;
|
||||
public int Landcell => _landcell;
|
||||
public double LocationX => _locationX;
|
||||
public double LocationY => _locationY;
|
||||
public double LocationZ => _locationZ;
|
||||
|
||||
// Character stats (parameterized properties — index-based access)
|
||||
public eTrainLevel SkillTrainLevel => eTrainLevel.eUntrained;
|
||||
public int SkillTotalXP => 0;
|
||||
public int SkillFreePoints => 0;
|
||||
public int SkillClicks => 0;
|
||||
public int AttributeTotalXP => 0;
|
||||
public int AttributeClicks => 0;
|
||||
public int AttributeStart => 0;
|
||||
public int VitalTotalXP => 0;
|
||||
public int VitalClicks => 0;
|
||||
public int Vital => 0;
|
||||
public int Attribute => 0;
|
||||
public int Skill => 0;
|
||||
public int Misc => 0;
|
||||
|
||||
// ID/object operations
|
||||
public void RequestID(int lObjectID) { _idFilter?.AddToQueue(lObjectID); }
|
||||
public void IDQueueAdd(int lObjectID) { _idFilter?.AddToQueue(lObjectID); }
|
||||
|
||||
// Spell bar
|
||||
public void SpellTabAdd(int lTab, int lIndex, int lSpellID) { }
|
||||
public void SpellTabDelete(int lTab, int lSpellID) { }
|
||||
|
||||
// Trade
|
||||
public void TradeAdd(int ItemID) { }
|
||||
public void TradeAccept() { }
|
||||
public void TradeDecline() { }
|
||||
public void TradeReset() { }
|
||||
public void TradeEnd() { }
|
||||
|
||||
// Salvage
|
||||
public void SalvagePanelAdd(int lObjectID) { }
|
||||
public void SalvagePanelSalvage() { }
|
||||
|
||||
public int OpenedContainer => _openedContainer;
|
||||
|
||||
// Experience
|
||||
public void AddSkillExp(eSkill SkillID, int lExperience) { }
|
||||
public void AddAttributeExp(eAttribute AttribID, int lExperience) { }
|
||||
public void AddVitalExp(eVital VitalID, int lExperience) { }
|
||||
|
||||
public int SmartboxPtr() => 0;
|
||||
public float ObjectHeight(int lObjectID) => 0f;
|
||||
|
||||
public int CallerRefInstanceInternal { set { } }
|
||||
|
||||
// Equipment
|
||||
public void AutoWieldRaw(int lObjectID, int SlotID, int Explicit, int NotExplicit, int zeroVal1, int zeroVal2) { }
|
||||
public void AutoWield(int lObjectID) { }
|
||||
public void AutoWieldEx(int lObjectID, int SlotID, int Explicit, int NotExplicit) { }
|
||||
|
||||
// Fellowship
|
||||
public void FellowshipRecruit(int lObjectID) { }
|
||||
public void FellowshipGrantLeader(int lObjectID) { }
|
||||
public void FellowshipSetOpen(bool IsOpen) { }
|
||||
public void FellowshipQuit() { }
|
||||
public void FellowshipDisband() { }
|
||||
public void FellowshipDismiss(int lObjectID) { }
|
||||
|
||||
// UI
|
||||
public void UIElementMove(int lUIElementType, int X, int Y) { }
|
||||
public void UIElementResize(int lUIElementType, int Width, int Height) { }
|
||||
public int UIElementLookup(int lUIElementType) => 0;
|
||||
public tagRECT UIElementRegionRect(int lUIElementType) => default;
|
||||
|
||||
// Internal event firing methods
|
||||
internal void FireObjectDestroyed(int guid) => ObjectDestroyed?.Invoke(guid);
|
||||
internal void FireContainerOpened(int guid) => ContainerOpened?.Invoke(guid);
|
||||
internal void FireRenderPreUI() => RenderPreUI?.Invoke();
|
||||
|
||||
internal void FireChatText(string text, int color, int target, ref bool eat)
|
||||
{
|
||||
ChatTextIntercept?.Invoke(text, color, target, ref eat);
|
||||
}
|
||||
|
||||
internal void FireChatParser(string text, ref bool eat)
|
||||
{
|
||||
ChatParserIntercept?.Invoke(text, ref eat);
|
||||
}
|
||||
|
||||
internal void FireStatusText(string text, ref bool eat)
|
||||
{
|
||||
StatusTextIntercept?.Invoke(text, ref eat);
|
||||
}
|
||||
|
||||
internal void FireObjectSelected(int guid) => ObjectSelected?.Invoke(guid);
|
||||
|
||||
internal void FireAC3DRegionChanged(int left, int top, int right, int bottom)
|
||||
{
|
||||
_ac3dRegionRect = new tagRECT { left = left, top = top, right = right, bottom = bottom };
|
||||
AC3DRegionChanged?.Invoke(left, top, right, bottom);
|
||||
}
|
||||
|
||||
internal void FireChatClick(string text, int id, ref bool eat)
|
||||
{
|
||||
ChatClickIntercept?.Invoke(text, id, ref eat);
|
||||
}
|
||||
|
||||
internal void UpdatePosition(double x, double y, double z, int landcell, double headDeg, double headRad)
|
||||
{
|
||||
_locationX = x;
|
||||
_locationY = y;
|
||||
_locationZ = z;
|
||||
_landcell = landcell;
|
||||
_headingDegrees = headDeg;
|
||||
_headingRadians = headRad;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
Managed/Decal.Core/Decal.Core.csproj
Normal file
6
Managed/Decal.Core/Decal.Core.csproj
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Decal.Interop.Core\Decal.Interop.Core.csproj" />
|
||||
<ProjectReference Include="..\Decal.Interop.Net\Decal.Interop.Net.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
346
Managed/Decal.Core/DecalCoreImpl.cs
Normal file
346
Managed/Decal.Core/DecalCoreImpl.cs
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Core;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Decal.Core
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("4557D5A1-00DB-48F6-ACB3-4FEF30E2F358")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ComSourceInterfaces("Decal.Interop.Core.IDecalEvents\0\0")]
|
||||
[ProgId("Decal.DecalCore")]
|
||||
public class DecalCoreImpl : IDecalCore
|
||||
{
|
||||
// COM events
|
||||
public event IDecalEvents_InitializeCompleteEventHandler InitializeComplete;
|
||||
public event IDecalEvents_TerminateCompleteEventHandler TerminateComplete;
|
||||
|
||||
// Services and plugins
|
||||
private struct ServiceEntry
|
||||
{
|
||||
public Guid Clsid;
|
||||
public IDecalService Service;
|
||||
public IDecalRender Renderer;
|
||||
}
|
||||
|
||||
private readonly List<ServiceEntry> _services = new List<ServiceEntry>();
|
||||
private readonly List<PluginSite2Impl> _plugins = new List<PluginSite2Impl>();
|
||||
private readonly ACHooksImpl _hooks = new ACHooksImpl();
|
||||
private bool _servicesRunning;
|
||||
private bool _pluginsRunning;
|
||||
|
||||
// Graphics
|
||||
private object _d3dDevice;
|
||||
private int _hwnd;
|
||||
private int _screenWidth = 800;
|
||||
private int _screenHeight = 600;
|
||||
|
||||
internal ACHooksImpl ACHooksInstance => _hooks;
|
||||
|
||||
// IDecalCore implementation
|
||||
public void InitGraphics(object pD3DDevice)
|
||||
{
|
||||
_d3dDevice = pD3DDevice;
|
||||
|
||||
// Notify render services
|
||||
foreach (var svc in _services)
|
||||
{
|
||||
svc.Renderer?.ChangeDirectX();
|
||||
}
|
||||
}
|
||||
|
||||
public object GetD3DDevice(ref Guid riid) => _d3dDevice;
|
||||
|
||||
public int HWND
|
||||
{
|
||||
get => _hwnd;
|
||||
set
|
||||
{
|
||||
_hwnd = value;
|
||||
foreach (var svc in _services)
|
||||
svc.Renderer?.ChangeHWND();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Focus => true;
|
||||
|
||||
public void SendWM(int HWND, short uMsg, int wParam, int lParam, ref bool pbEat)
|
||||
{
|
||||
pbEat = false;
|
||||
}
|
||||
|
||||
public void GetScreenSize(out int pWidth, out int pHeight)
|
||||
{
|
||||
pWidth = _screenWidth;
|
||||
pHeight = _screenHeight;
|
||||
}
|
||||
|
||||
public string MapPath(string pPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pPath)) return pPath;
|
||||
|
||||
string result = pPath;
|
||||
int pos = 0;
|
||||
|
||||
while (pos < result.Length)
|
||||
{
|
||||
int start = result.IndexOf('%', pos);
|
||||
if (start < 0) break;
|
||||
|
||||
int end = result.IndexOf('%', start + 1);
|
||||
if (end < 0) break;
|
||||
|
||||
string token = result.Substring(start + 1, end - start - 1);
|
||||
string resolved = ResolveToken(token);
|
||||
result = result.Substring(0, start) + resolved + result.Substring(end + 1);
|
||||
pos = start + resolved.Length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private string ResolveToken(string token)
|
||||
{
|
||||
string lower = token.ToLowerInvariant();
|
||||
if (lower == "ac" || lower.StartsWith("ac:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Turbine\Asheron's Call"))
|
||||
{
|
||||
return key?.GetValue("Path") as string ?? "";
|
||||
}
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
|
||||
if (lower == "decal" || lower.StartsWith("decal:"))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Decal\Agent"))
|
||||
{
|
||||
return key?.GetValue("AgentPath") as string ?? "";
|
||||
}
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
|
||||
return "%" + token + "%";
|
||||
}
|
||||
|
||||
public void StartPlugins()
|
||||
{
|
||||
if (_pluginsRunning) return;
|
||||
if (!_servicesRunning) StartServices();
|
||||
|
||||
// Notify services: before plugins
|
||||
foreach (var svc in _services)
|
||||
svc.Service.BeforePlugins();
|
||||
|
||||
// Enumerate and load plugins from registry
|
||||
var pluginEnum = new DecalEnumImpl();
|
||||
pluginEnum.Initialize("Plugins");
|
||||
|
||||
try
|
||||
{
|
||||
pluginEnum.Next(); // Move to first
|
||||
while (true)
|
||||
{
|
||||
if (pluginEnum.Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
var riid = typeof(IPlugin2).GUID;
|
||||
var pluginObj = pluginEnum.CreateInstance(ref riid);
|
||||
var plugin = pluginObj as IPlugin2;
|
||||
if (plugin != null)
|
||||
{
|
||||
var site = new PluginSite2Impl();
|
||||
site.Setup(this, plugin, pluginEnum.ComClass);
|
||||
_plugins.Add(site);
|
||||
|
||||
plugin.Initialize((PluginSite2)(object)site);
|
||||
}
|
||||
}
|
||||
catch { } // Bad plugins are skipped
|
||||
}
|
||||
pluginEnum.Next();
|
||||
}
|
||||
}
|
||||
catch { } // Next() throws when past end
|
||||
|
||||
_pluginsRunning = true;
|
||||
|
||||
// Notify services: after plugins
|
||||
foreach (var svc in _services)
|
||||
svc.Service.AfterPlugins();
|
||||
|
||||
InitializeComplete?.Invoke(eDecalComponentType.ePlugin);
|
||||
}
|
||||
|
||||
public void StopPlugins()
|
||||
{
|
||||
if (!_pluginsRunning) return;
|
||||
|
||||
// Unload all plugins (copy list since Unload modifies it)
|
||||
var sites = new List<PluginSite2Impl>(_plugins);
|
||||
foreach (var site in sites)
|
||||
{
|
||||
try { site.Unload(); } catch { }
|
||||
}
|
||||
_plugins.Clear();
|
||||
|
||||
_pluginsRunning = false;
|
||||
|
||||
// Notify services: after plugins stopped
|
||||
foreach (var svc in _services)
|
||||
{
|
||||
try { svc.Service.AfterPlugins(); } catch { }
|
||||
}
|
||||
|
||||
TerminateComplete?.Invoke(eDecalComponentType.ePlugin);
|
||||
}
|
||||
|
||||
public object Object => null;
|
||||
|
||||
public void StartServices()
|
||||
{
|
||||
if (_servicesRunning) return;
|
||||
|
||||
// Initialize ACHooks first
|
||||
_hooks.SetDecal(this);
|
||||
|
||||
// Enumerate and load services from registry
|
||||
var serviceEnum = new DecalEnumImpl();
|
||||
serviceEnum.Initialize("Services");
|
||||
|
||||
try
|
||||
{
|
||||
serviceEnum.Next();
|
||||
while (true)
|
||||
{
|
||||
if (serviceEnum.Enabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
var riid = typeof(IDecalService).GUID;
|
||||
var svcObj = serviceEnum.CreateInstance(ref riid);
|
||||
var service = svcObj as IDecalService;
|
||||
if (service != null)
|
||||
{
|
||||
service.Initialize((DecalCore)(object)this);
|
||||
|
||||
// Check for render capability
|
||||
IDecalRender renderer = svcObj as IDecalRender;
|
||||
|
||||
_services.Add(new ServiceEntry
|
||||
{
|
||||
Clsid = serviceEnum.ComClass,
|
||||
Service = service,
|
||||
Renderer = renderer
|
||||
});
|
||||
}
|
||||
}
|
||||
catch { } // Service init failure - skip
|
||||
}
|
||||
serviceEnum.Next();
|
||||
}
|
||||
}
|
||||
catch { } // Next() throws when past end
|
||||
|
||||
_servicesRunning = true;
|
||||
InitializeComplete?.Invoke(eDecalComponentType.eService);
|
||||
}
|
||||
|
||||
public DecalEnum Configuration
|
||||
{
|
||||
get
|
||||
{
|
||||
var e = new DecalEnumImpl();
|
||||
e.Initialize("Plugins");
|
||||
return (DecalEnum)(object)e;
|
||||
}
|
||||
}
|
||||
|
||||
public void StopServices()
|
||||
{
|
||||
if (!_servicesRunning) return;
|
||||
if (_pluginsRunning) StopPlugins();
|
||||
|
||||
// Stop services in reverse order (LIFO)
|
||||
for (int i = _services.Count - 1; i >= 0; i--)
|
||||
{
|
||||
try { _services[i].Service.Terminate(); } catch { }
|
||||
}
|
||||
_services.Clear();
|
||||
|
||||
_servicesRunning = false;
|
||||
TerminateComplete?.Invoke(eDecalComponentType.eService);
|
||||
}
|
||||
|
||||
public object Plugin => null;
|
||||
public object Service => null;
|
||||
|
||||
public void Render2D()
|
||||
{
|
||||
foreach (var svc in _services)
|
||||
svc.Renderer?.Render2D();
|
||||
}
|
||||
|
||||
public void Render3D()
|
||||
{
|
||||
_hooks.FireRenderPreUI();
|
||||
foreach (var svc in _services)
|
||||
svc.Renderer?.Render3D();
|
||||
}
|
||||
|
||||
public void PreReset()
|
||||
{
|
||||
foreach (var svc in _services)
|
||||
svc.Renderer?.PreReset();
|
||||
}
|
||||
|
||||
public void PostReset()
|
||||
{
|
||||
foreach (var svc in _services)
|
||||
svc.Renderer?.PostReset();
|
||||
}
|
||||
|
||||
public ACHooks Hooks => (ACHooks)(object)_hooks;
|
||||
|
||||
public void StartFilters()
|
||||
{
|
||||
// Filters are started as part of services
|
||||
}
|
||||
|
||||
public void StopFilters()
|
||||
{
|
||||
// Filters are stopped as part of services
|
||||
}
|
||||
|
||||
public bool KillBitCheckByGUID(string bstrGuid, string bstrVersion) => false;
|
||||
public bool KillBitCheckByProgID(string bstProgID, string bstrVersion) => false;
|
||||
|
||||
public bool PluginsRunning => _pluginsRunning;
|
||||
|
||||
// Internal methods for PluginSite2
|
||||
internal void RemovePlugin(PluginSite2Impl site) => _plugins.Remove(site);
|
||||
|
||||
internal object GetService(Guid clsid)
|
||||
{
|
||||
foreach (var svc in _services)
|
||||
if (svc.Clsid == clsid) return svc.Service;
|
||||
return null;
|
||||
}
|
||||
|
||||
internal object GetPlugin(Guid clsid)
|
||||
{
|
||||
foreach (var site in _plugins)
|
||||
if (site.PluginClsid == clsid) return site.Plugin;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
199
Managed/Decal.Core/DecalEnumImpl.cs
Normal file
199
Managed/Decal.Core/DecalEnumImpl.cs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Core;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Decal.Core
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("6DE65A82-C451-46E6-A82D-92137BE851AD")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ProgId("Decal.DecalEnum")]
|
||||
public class DecalEnumImpl : IDecalEnum
|
||||
{
|
||||
private class EnumEntry
|
||||
{
|
||||
public string FriendlyName = "";
|
||||
public Guid ComClass;
|
||||
public bool Enabled = true;
|
||||
public bool Restricted;
|
||||
public Guid SurrogateClass;
|
||||
public string ResourcePath = "";
|
||||
public string FilePath = "";
|
||||
public string Version = "";
|
||||
public int Order;
|
||||
}
|
||||
|
||||
private readonly List<EnumEntry> _entries = new List<EnumEntry>();
|
||||
private int _index = -1;
|
||||
private string _group = "";
|
||||
|
||||
private EnumEntry Current => (_index >= 0 && _index < _entries.Count) ? _entries[_index] : null;
|
||||
|
||||
public string FriendlyName => Current?.FriendlyName ?? "";
|
||||
public Guid ComClass => Current?.ComClass ?? Guid.Empty;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get => Current?.Enabled ?? false;
|
||||
set
|
||||
{
|
||||
var c = Current;
|
||||
if (c == null) return;
|
||||
c.Enabled = value;
|
||||
try
|
||||
{
|
||||
using (var key = Registry.LocalMachine.OpenSubKey(
|
||||
$@"SOFTWARE\Decal\{_group}\{c.ComClass:B}", true))
|
||||
{
|
||||
key?.SetValue("Enabled", value ? 1 : 0, RegistryValueKind.DWord);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
public bool Restricted => Current?.Restricted ?? false;
|
||||
|
||||
public object CreateInstance(ref Guid riid)
|
||||
{
|
||||
var entry = Current;
|
||||
if (entry == null) return null;
|
||||
|
||||
// If there's a surrogate, use it
|
||||
if (entry.SurrogateClass != Guid.Empty)
|
||||
{
|
||||
var surrogateType = Type.GetTypeFromCLSID(entry.SurrogateClass);
|
||||
if (surrogateType != null)
|
||||
{
|
||||
var surrogate = Activator.CreateInstance(surrogateType) as IDecalSurrogate;
|
||||
if (surrogate != null)
|
||||
return surrogate.CreateInstance((DecalEnum)(object)this, ref riid);
|
||||
}
|
||||
}
|
||||
|
||||
// Direct COM creation
|
||||
var type = Type.GetTypeFromCLSID(entry.ComClass);
|
||||
return type != null ? Activator.CreateInstance(type) : null;
|
||||
}
|
||||
|
||||
public void Next()
|
||||
{
|
||||
_index++;
|
||||
if (_index >= _entries.Count)
|
||||
Marshal.ThrowExceptionForHR(1); // S_FALSE
|
||||
}
|
||||
|
||||
public Guid SurrogateClass => Current?.SurrogateClass ?? Guid.Empty;
|
||||
public string ResourcePath => Current?.ResourcePath ?? "";
|
||||
public object Property => null; // Parameterized — returns registry values by name
|
||||
public string Group => _group;
|
||||
|
||||
public void Skip(ref Guid clsid)
|
||||
{
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
if (_entries[i].ComClass == clsid)
|
||||
{
|
||||
_index = i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveBefore(ref Guid clsidBefore)
|
||||
{
|
||||
if (_index < 0 || _index >= _entries.Count) return;
|
||||
var current = _entries[_index];
|
||||
_entries.RemoveAt(_index);
|
||||
|
||||
int targetIdx = _entries.Count;
|
||||
for (int i = 0; i < _entries.Count; i++)
|
||||
{
|
||||
if (_entries[i].ComClass == clsidBefore)
|
||||
{
|
||||
targetIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_entries.Insert(targetIdx, current);
|
||||
_index = targetIdx;
|
||||
}
|
||||
|
||||
public string Version => Current?.Version ?? "";
|
||||
|
||||
public bool FileExists
|
||||
{
|
||||
get
|
||||
{
|
||||
var path = Current?.FilePath;
|
||||
return !string.IsNullOrEmpty(path) && File.Exists(path);
|
||||
}
|
||||
}
|
||||
|
||||
public string FilePath => Current?.FilePath ?? "";
|
||||
|
||||
internal void Initialize(string group)
|
||||
{
|
||||
_group = group;
|
||||
_entries.Clear();
|
||||
_index = -1;
|
||||
|
||||
try
|
||||
{
|
||||
using (var baseKey = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Decal\{group}"))
|
||||
{
|
||||
if (baseKey == null) return;
|
||||
|
||||
string orderStr = baseKey.GetValue("Order") as string ?? "";
|
||||
|
||||
foreach (string subKeyName in baseKey.GetSubKeyNames())
|
||||
{
|
||||
if (!Guid.TryParse(subKeyName, out var clsid))
|
||||
continue;
|
||||
|
||||
using (var subKey = baseKey.OpenSubKey(subKeyName))
|
||||
{
|
||||
if (subKey == null) continue;
|
||||
|
||||
var entry = new EnumEntry
|
||||
{
|
||||
FriendlyName = subKey.GetValue(null) as string ?? subKeyName,
|
||||
ComClass = clsid,
|
||||
Enabled = ((int)(subKey.GetValue("Enabled") ?? 1)) != 0,
|
||||
Restricted = ((int)(subKey.GetValue("Restricted") ?? 0)) != 0,
|
||||
ResourcePath = subKey.GetValue("ResourcePath") as string ?? "",
|
||||
FilePath = subKey.GetValue("File") as string ?? "",
|
||||
Version = subKey.GetValue("Version") as string ?? "",
|
||||
};
|
||||
|
||||
var surrogateStr = subKey.GetValue("Surrogate") as string;
|
||||
if (!string.IsNullOrEmpty(surrogateStr) && Guid.TryParse(surrogateStr, out var surr))
|
||||
entry.SurrogateClass = surr;
|
||||
|
||||
var orderVal = subKey.GetValue("Order") as string;
|
||||
if (!string.IsNullOrEmpty(orderVal) && orderVal.Length > 0)
|
||||
{
|
||||
int pos = orderStr.IndexOf(orderVal[0]);
|
||||
entry.Order = pos >= 0 ? pos : int.MaxValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.Order = int.MaxValue;
|
||||
}
|
||||
|
||||
_entries.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_entries.Sort((a, b) => a.Order.CompareTo(b.Order));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
internal int Count => _entries.Count;
|
||||
}
|
||||
}
|
||||
93
Managed/Decal.Core/PluginSite2Impl.cs
Normal file
93
Managed/Decal.Core/PluginSite2Impl.cs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Decal.Interop.Core;
|
||||
|
||||
namespace Decal.Core
|
||||
{
|
||||
[ComVisible(true)]
|
||||
[Guid("E2284FC7-17E5-4846-ADAB-07953273C5FB")]
|
||||
[ClassInterface(ClassInterfaceType.None)]
|
||||
[ProgId("Decal.PluginSite2")]
|
||||
public class PluginSite2Impl : IPluginSite2
|
||||
{
|
||||
private DecalCoreImpl _decal;
|
||||
private IPlugin2 _plugin;
|
||||
private Guid _pluginClsid;
|
||||
|
||||
internal void Setup(DecalCoreImpl decal, IPlugin2 plugin, Guid pluginClsid)
|
||||
{
|
||||
_decal = decal;
|
||||
_plugin = plugin;
|
||||
_pluginClsid = pluginClsid;
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
if (_plugin != null)
|
||||
{
|
||||
try { _plugin.Terminate(); } catch { }
|
||||
_plugin = null;
|
||||
}
|
||||
_decal?.RemovePlugin(this);
|
||||
}
|
||||
|
||||
public object Object
|
||||
{
|
||||
get
|
||||
{
|
||||
// Parameterized property: returns service/plugin objects by path
|
||||
// Path format: "services\{CLSID}" or "plugins\{CLSID}"
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public DecalCore Decal => (DecalCore)(object)_decal;
|
||||
public ACHooks Hooks => (ACHooks)(object)_decal?.ACHooksInstance;
|
||||
|
||||
public object PluginSite => this;
|
||||
|
||||
public void RegisterSinks(object pPlugin)
|
||||
{
|
||||
// Register event sinks for the plugin
|
||||
// This connects COM event sources to the plugin's event handlers
|
||||
}
|
||||
|
||||
internal IPlugin2 Plugin => _plugin;
|
||||
internal Guid PluginClsid => _pluginClsid;
|
||||
|
||||
internal object LookupObject(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path) || _decal == null)
|
||||
return null;
|
||||
|
||||
// Parse path: "collection\{CLSID}\subpath..."
|
||||
string[] parts = path.Split('\\');
|
||||
if (parts.Length < 2) return null;
|
||||
|
||||
string collection = parts[0].ToLowerInvariant();
|
||||
if (!Guid.TryParse(parts[1], out var clsid))
|
||||
return null;
|
||||
|
||||
object obj = null;
|
||||
|
||||
if (collection == "services")
|
||||
obj = _decal.GetService(clsid);
|
||||
else if (collection == "plugins")
|
||||
obj = _decal.GetPlugin(clsid);
|
||||
|
||||
// If there are additional path segments, use IDecalDirectory for nested lookup
|
||||
if (obj != null && parts.Length > 2)
|
||||
{
|
||||
for (int i = 2; i < parts.Length; i++)
|
||||
{
|
||||
if (obj is IDecalDirectory dir)
|
||||
obj = dir.Lookup(parts[i]);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue