Clean slate for Claude

This commit is contained in:
Yoshi Askharoun
2026-07-12 22:35:04 -05:00
parent 18af9965a8
commit 94bfb22fc3
38 changed files with 398 additions and 1553 deletions
+93
View File
@@ -0,0 +1,93 @@
# ZuneDBApi — Research Log
This file is **append-only**. Do not edit or remove previous entries.
---
## 2026-07-12 — MicrosoftZuneInterop decompilation
### Q1: Real GUID for `IQueryPropertyBag`
**File:** `ZuneDBApi/MicrosoftZuneInterop/IQueryPropertyBag.cs`
The `[GeneratedComInterface]` attribute requires a GUID. The native binary does not expose this GUID through managed metadata (ILSpy returns no result when searching for `IQueryPropertyBag` as a type). A placeholder GUID `3D8A1F2B-6C4E-4A5D-9B7F-2E0C1A8D3F4E` is currently used.
The real GUID (if one exists) would need to be recovered from the native binary using Ghidra — look for a `__declspec(uuid(...))` annotation on the `IQueryPropertyBag` class, or a `DEFINE_GUID`/`IID_IQueryPropertyBag` symbol. It is also possible the interface has no GUID and QI is never used by the native callers (the user noted it is not CoCreateInstance-registered), in which case the placeholder is acceptable long-term.
**Assumption made in code:** placeholder GUID used. Logged per unknowns procedure.
---
---
## 2026-07-12 — Q1 follow-up: GUID search in managed metadata
**Task:** Research whether the real GUID for `IQueryPropertyBag` is encoded anywhere accessible to ILSpy (checked `<Module>` and root-namespace types).
**Findings:**
1. The global-namespace struct `IQueryPropertyBag` (separate from `MicrosoftZuneInterop.IQueryPropertyBag`) has no `[Guid]` attribute — only `[NativeCppClass]`, `[DebugInfoInPDB]`, `[MiscellaneousBits(65)]`.
2. `<Module>` could not be decompiled by ILSpy (tool error); its fields are not inspectable through the managed surface.
3. Full decompilation of `MicrosoftZuneInterop.QueryPropertyBag` shows no IID reference anywhere in managed code. `GetIQueryPropertyBag()` simply returns the raw `m_pPropertyBag` pointer with no `QueryInterface` call.
4. The destructor (`!QueryPropertyBag`) calls offset 16 on the vtable, confirming IUnknown layout (slot 2 = Release). This does not reveal the IID.
**Conclusion:** The GUID is not present in managed metadata. If one exists, it lives only in native code (`__declspec(uuid(...))` or `DEFINE_GUID`). Ghidra remains the only path to recovery. Placeholder GUID in `IQueryPropertyBag.cs` stands.
---
### Q2: Unknown vtable slots in `IQueryPropertyBag`
**File:** `ZuneDBApi/MicrosoftZuneInterop/IQueryPropertyBag.cs`
The vtable offsets observed in `QueryPropertyBag`'s decompiled code (ILSpy) are:
- Slot 5 (offset 40): `SetString(prop, wchar_t*) -> HRESULT`
- Slot 7 (offset 56): `SetInt(prop, int) -> HRESULT`
- Slot 13 (offset 104): `IsSet(prop, int*) -> HRESULT`
Slots 3, 4, 6, and 812 are not observed being called in any managed code that ILSpy can see. Their signatures (return type, parameters) are unknown.
Placeholder `void _ReservedN()` methods are declared in the interface to preserve vtable ordering. These must not be called. If any of these slots are not HRESULT-returning void methods, the placeholders will be wrong and must be corrected before the CCW/RCW is used by native code.
**Needs:** Ghidra analysis of the native `IQueryPropertyBag` vtable definition.
---
### Q3: `kPropIdMap` — 37-entry property name table
**File:** `ZuneDBApi/MicrosoftZuneInterop/QueryPropertyBag.cs`, `MapNameToProp`
`QueryPropertyBag.MapNameToProp` walks a static array `MicrosoftZuneInterop.?A0x52c37a46.kPropIdMap` of 37 `PropIdMapEntry` structs (each 16 bytes: `wchar_t* name` at [0..7], `EQueryPropertyBagProp id` at [8..11], 4 bytes padding at [12..15]). The property name strings and their corresponding `EQueryPropertyBagProp` values are embedded in native data and are not visible through ILSpy.
Without this table, `MapNameToProp` (and by extension `SetValue` and `IsSet`) cannot be implemented.
**Needs:** Ghidra analysis of `kPropIdMap` in the native segment of `ZuneDBApi.dll`.
---
### Q4: `EQueryPropertyBagProp` enum values
**File:** `ZuneDBApi/GlobalEnums.cs` (where it will be added)
`EQueryPropertyBagProp` appears in the assembly listing and in the signatures of `IQueryPropertyBag` methods, but lives in the global (unnamed) namespace like other C++/CLI-compiled native enums. ILSpy queries for it with a namespace prefix fail. It has not yet been queried or added to `GlobalEnums.cs`.
The 37 entries in `kPropIdMap` (Q3 above) presumably map to values of this enum, so resolving Q3 and Q4 together would be efficient.
**Needs:** ILSpy query for `EQueryPropertyBagProp` (no namespace prefix), then Ghidra cross-reference against `kPropIdMap`.
---
### Q5: `ZuneLibraryExports.CreatePropertyBag` — P/Invoke signature
**File:** `ZuneDBApi/MicrosoftZuneInterop/QueryPropertyBag.cs`, constructor
The original constructor calls `global::<Module>.ZuneLibraryExports.CreatePropertyBag(&pPropertyBag)`. In a managed reimplementation this would be a P/Invoke into the native export. The DLL name, exact export symbol, and calling convention are unknown.
**Assumption made in code:** constructor body is empty with a `// TODO` comment.
---
### Q6: Calling convention for `IQueryPropertyBag` vtable methods
**File:** `ZuneDBApi/MicrosoftZuneInterop/QueryPropertyBag.cs` (previous `IntPtr`-based draft)
The ILSpy decompilation shows `delegate* unmanaged[Cdecl, Cdecl]` for the vtable dispatch (the double `Cdecl` appears to be an ILSpy artifact of C++/CLI compilation). Standard COM uses `__stdcall` on x86 and the platform ABI on x64 (where Cdecl and Stdcall converge). The current implementation uses `[GeneratedComInterface]` which handles this, but if raw vtable dispatch is ever needed (e.g. for the unknown slots), the calling convention should be verified against the native binary.
+177 -40
View File
@@ -1,20 +1,33 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository, specifically ZuneDBApi.
## What this project is
`ZuneDBApi` is one project inside the larger `ZuneShell.dll` solution (root: `..`), which is a clean-room
reimplementation of the original Microsoft Zune desktop software. The dependency chain is:
`ZuneDBApi` is one project inside the larger `ZuneShell.dll` solution
(root: `..`), which is a reimplementation of the original Microsoft Zune
desktop software. The dependency chain is:
```
ZuneDBApi (this project) → ZuneImpl → ZuneShell → ZuneHost / ZuneHost.Wpf (entry points)
ZuneDBApi (this project) → ZuneImpl → ZuneShell.dll → ZuneHost / ZuneHost.Wpf (entry points)
```
ZuneDBApi is the lowest layer: it re-creates the public API surface of the original closed-source
`Microsoft.Zune.*` / `MicrosoftZuneInterop` / `MicrosoftZuneLibrary` assemblies that shipped with the real
Zune software, so the rest of the stack (`ZuneImpl`, `ZuneShell`) can compile and run against a familiar API
without bundling Microsoft's original binaries or decompiled source.
Note that Iris UI files (both `.uix` and `.uib`) can load arbitrary assemblies.
This meaans that `ZuneShell.dll` has a direct dependency on `ZuneDBApi`, even
though according to the C# project system, `ZuneDBApi` is only a transitive
dependency.
ZuneDBApi is the lowest layer with managed code: it re-creates the public API
surface of the original closed-source `Microsoft.Zune.*` /
`MicrosoftZuneInterop` / `MicrosoftZuneLibrary` assemblies that shipped with
the real Zune software, so the rest of the stack (`ZuneImpl`, `ZuneShell`) can
compile and run against a familiar API without bundling Microsoft's original
binaries. It also unlocks modifications and potentially even cross-platform
support. When writing new code, make as few assumptions about the platform
as possible. If platform-specific code is unavoidable without hacks or relying
on fragile behavior, wrap the code with the appropirate `#if PLATFORM` directive
and write a `// TODO` comment to implement the other platforms.
Namespace-to-folder mapping is strict and should be preserved when adding files:
- `Microsoft.Zune.Configuration``Microsoft.Zune/Configuration/`
@@ -25,28 +38,146 @@ Namespace-to-folder mapping is strict and should be preserved when adding files:
- `MicrosoftZuneLibrary``MicrosoftZuneLibrary/`
- `ZuneUI` (HRESULT, attributes) → `ZuneUI/`
## Critical constraint: no decompiled code
## Project goals
A prior commit (`d0fd93a`, "Added raw ZuneDBApi decomp") added a raw decompilation of the original Microsoft
assembly and was immediately reverted (`749a29a`) — verbatim decompiled Microsoft code must not be checked in.
Instead, classes here are **clean-room stubs**: same public type/method/property signatures as the original
(so dependent code compiles and links correctly), but with placeholder or minimal real bodies, not copied
Microsoft implementation logic. The `mcp__ilspy__*` tools (already allowlisted in `.claude/settings.local.json`)
are for inspecting the *shape* of the original assembly (type/member lists, signatures) to match the API
surface — not for pulling decompiled method bodies into source files.
The goal of this project is to revive Zune and make it interoperable with
modern tools and platforms. This requires reverse engineering the original
software and reimplementing it in a way that is maintainable and makes as few
assumptions about the target platform as possible.
Two stub styles coexist in this codebase, both legitimate:
- **Pure no-op stub** — method sets `out` params to defaults and returns `HRESULT._S_OK` (e.g.
`Microsoft.Zune/Service/AccountManagement.cs`).
- **Real working logic** — e.g. `Microsoft.Zune/Configuration/*Configuration.cs` classes are genuinely
backed by `RegistryHive`/`CConfigurationManagedBase` with real default values, and `ZuneUI/HRESULT.cs` is a
fully real HRESULT struct with the actual Zune error codes.
## Project stages
When a stub needs to represent an unmanaged COM pointer the original held (e.g. `IPlaylist*`, `IQueryPropertyBag*`),
do **not** invent raw C-style pointer types or C++-style destructor syntax (`~Foo()` as an ordinary method, or
`!Foo()`) — neither compiles in C#. Use `IntPtr`/`SafeHandle`-style fields and a normal `IDisposable` pattern
(`protected virtual void Dispose(bool disposing)` + `~Foo()` as an actual finalizer) instead, matching the
pattern already used in `Microsoft.Zune/Configuration/CConfigurationManagedBase.cs`.
This revival effort is realized in distinct phases:
1. **Decompilation:** investigating the original code, understanding how it works
and fits into the rest of the stack. High-quality decompilations, such as those
produced for pure C# code, may be used as a starting point for future stages.
2. **Implementation:** reimplementing the original API surface with code that
is maintainable but has the same features. It is imperative that the new
implementation is entirely backwards-compatible and can function as a drop-in
replacement for the original libraries. The project at this stage is more
manageable but naturally has the similar requirements and limitations to the
original.
3. **Enhancement:** adding new features such as additional platform support
or OS intergration. This may require extending existing abstractions or
creating new ones for the sake of modularity.
**Strict adherence to these phases is required**. To ensure stability and
quality, each stage is highly dependent on the one before. When decompiling
an assembly or type, do not attempt to use new abstractions. Simply write
equivalent code, such that the new implementation is a drop-in replacement
for the original. Similarly, the enhancement stage must not introduce any new
platform-specific code without being gated behind proper conditionals and
accessed via suitable abstractions.
ZuneShell.dll and the Microsoft.Iris libraries are mostly in stage 2, going on
stage 3, but ZuneDBApi is currently in stage 1. You first need to decompile
the assembly, then implement it using the rules outlined in the rest of this
document.
## Dealing with unknowns and uncertainty
**Do not make assumptions**. If you have a question about something,
follow this procedure:
1. **Log your question.** Use `LOG.md` in the repo root to log any questions
or confusions. This document will be reviewed by the human: provide any
useful context for your query. Do not log guesses or hunches; only observations
and unknowns. Most importantly, **do not edit previous questions**; the log
file is *append-only*.
2. **Ask the human.** They've been working on Zune longer than you have. They may
have an immediate answer for you, or at least can narrow down your search.
3. **Search the Wiki.** Some information has already been documented in the
ZuneDev Wiki located in the following repo on GitHub:
http://github.com/ZuneDev/Wiki
4. **Move on.** If you still haven't gotten a concrete, verifiable answer,
you may make whatever assumption is both likely and has the fewest predicates,
**if and only if you document this assumption in the code**, using `// TODO`
comments. You are also required to log this assumption and its corresponding
rationale in `LOG.md`.
### STAGE 1: Decompilation
Reliance on the original libraries is a no-go, as these binaries are only
available for x86 and x86_64 Windows. Certain components of the Zune software
have already been reimplemented (see stage 2 of *Project Goals*), including
`ZuneShell.dll` and `Microsoft.Iris`, but all of the original native
libraries are stuck at the start of stage 1.
#### Managed assemblies
Pure managed assemblies, such as those written in C#, can be inspected using
ILSpy or the `mcp__ilspy__*` tools. Reverse engineering these assemblies is
typically quite easy, as the decompilation is very accurate. Such
decompilations are suitable starting points for stages 2 and 3.
#### Native assemblies
Native or unmanaged assemblies, such as those written in C or C++, can be
inspected using Ghidra. Decompilations from these assemblies should not be used
verbatim, as their outputs are usually low-quality and are not readable.
#### Mixed-mode assemblies
Mixed-mode assemblies, such as those written in C++/CLI, contain both managed
and unmanaged code. ILSpy and its corresponding MCP tools may be used to
inspect the public surface of these assemblies, but implementation details
are likely to be missing. A combination of Ghidra and ILSpy may be required
to fully reverse engineer such assemblies. Decompilations of mixed-mode
aseemblies should only be used as-is if the specific code does not touch any
native or unmanaged code, either internally or externally.
### STAGE 2: Implementation
All initial implementations must be backwards-compatible with the original
API surfaces. This means reimplemented modules must have the same public
type/method/property signatures as the original (so dependent code compiles
and links correctly). The actual body of these surfaces can follow one of the
following conventions:
- **Pure no-op stub** — method sets `out` params to defaults and returns a
success result, typically `HRESULT._S_OK`.
- **Real working logic** — logic is implemented and matches the original
behavior.
Whenever possible, the latter is preferred over the former: stubs require
more work in the future by a human, which can make the revival effort take
longer to become useful. Real, working business logic makes future work much
easier because the code is much more self-documenting, requiring less
consultation with outputs from previous stages.
#### COM objects
When a reimplementation needs to represent an unmanaged COM pointer the
original held (e.g. `IPlaylist*`, `IQueryPropertyBag*`), do **not** invent
raw C-style pointer types or C++-style destructor syntax (`~Foo()` as an
ordinary method, or `!Foo()`) — neither compiles in C#.
When a class holds a COM pointer, follow these steps in order:
1. Rewrite the decompiled code using `IntPtr` or `SafeHandle` with proper
`IDisposable` and finalizer logic.
2. Reconstruct the methods on the corresponding COM interface in such a way
that it is compatible with `Marshal.GetObjectForIUnknown`, ideally using
source-generated COM. Note that for some Zune-specific interfaces, you may not
need to locate a GUID or support `CoCreateInstance`, as some objects are
instantiated in the native libraries. Follow the rules in *Dealing with
unknowns and uncertainty*.
3. Update the initial reimplementation to use the new modern COM structure.
Ensure you do not affect the public surface of the class; external code may
depend on the exact shape of the original API.
These steps are the only acceptable "enhancement" for stages 1 and 2.
### STAGE 3: Enhancement
Once parity has been restored with the original software, enhancements can be
made to the codebase. This may involve creating new abstractions for platform-
specific features, or implementing existing interfaces to extend support.
New features may be added, such as extended support for newer codecs, syncing
with other MTP devices like modern smartphones, or integrating new Social
features from ActivityPub and ListenBrainz scrobbling.
## Build
@@ -54,23 +185,29 @@ pattern already used in `Microsoft.Zune/Configuration/CConfigurationManagedBase.
# From repo root or this directory — build just this project:
dotnet build ZuneDBApi/ZuneDBApi.csproj
# Build the whole solution (Windows-only platforms exist in ZuneShell.sln; on Linux only net8.0 TFMs build):
# Build the whole solution (Windows-only platforms exist in ZuneShell.sln;
# on Linux only net8.0 TFMs build):
dotnet build ZuneShell.sln
```
`Directory.Build.props` (repo root) sets `TargetFrameworks` to `net8.0` everywhere, adding `net472` and
`net8.0-windows` automatically when building on Windows. There is no test project for ZuneDBApi or the
solution as a whole.
No test projects are currently available, neither for the entire project or
just ZuneDBApi.
Note: building currently fails on files containing invalid C++-style syntax (see constraint above) —
running a build will surface any remaining instances; treat new ones the same way (fix to valid C#, do not
silently work around them).
Note that building will fail on files containing invalid C++-style syntax
(see *COM objects*) — running a build will surface any remaining instances;
treat new ones the same way (fix to valid C#, do not silently work
around them).
## Working in this project
- `Nullable` and `ImplicitUsings` are enabled; `AllowUnsafeBlocks` is true (file-scoped namespaces are fine).
- This project only references `UIX.csproj` (from the `MicrosoftIris`/`ZuneUIXTools` submodule) — keep it free
of dependencies on `ZuneImpl`/`ZuneShell` to avoid circular references, since those layers depend on it.
- `Microsoft.Win32.RegistryHive`/`Registry*` APIs used by the `Configuration` classes are Windows-only; this
is expected since the original Zune client was Windows-only, but keep cross-platform buildability in mind
for the `net8.0` (non-`-windows`) TFM — avoid adding new Windows-only APIs outside what's already used.
- `Nullable` and `ImplicitUsings` are enabled; `AllowUnsafeBlocks` is true
(file-scoped namespaces are fine).
- This project only references `UIX.csproj` (from the
`MicrosoftIris`/`ZuneUIXTools` submodule) — keep it free of dependencies on
`ZuneImpl`/`ZuneShell` to avoid circular references, since those layers depend
on it.
- `Microsoft.Win32.RegistryHive`/`Registry*` APIs used by the `Configuration`
classes are Windows-only; this is expected since the original Zune client was
Windows-only, but keep cross-platform buildability in mind for the `net8.0`
(non-`-windows`) TFM. When working on stage 3, avoid adding new Windows-only
APIs outside what's already used.
@@ -17,11 +17,7 @@ namespace Microsoft.Zune.Configuration
public string ConfigurationPath => m_basePath != null ? m_basePath + "\\" + m_instance : m_instance;
public event ConfigurationChangeEventHandler OnConfigurationChanged
{
add { lock (m_lock) { OnConfigurationChanged += value; } }
remove { lock (m_lock) { OnConfigurationChanged -= value; } }
}
public event ConfigurationChangeEventHandler OnConfigurationChanged;
public CConfigurationManagedBase(RegistryHive hive, string basePath, string instance)
{
@@ -1,13 +1,10 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.VisualC;
namespace Microsoft.Zune.Configuration
{
[StructLayout(LayoutKind.Sequential, Size = 24)]
[MiscellaneousBits(64)]
[NativeCppClass]
[DebugInfoInPDB]
internal struct DeregisterCallback
{
private long _alignment;
@@ -1,13 +1,10 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.VisualC;
namespace Microsoft.Zune.Configuration
{
[StructLayout(LayoutKind.Sequential, Size = 24)]
[MiscellaneousBits(64)]
[NativeCppClass]
[DebugInfoInPDB]
internal struct NotificationMarshaller
{
private long _alignment;
@@ -1,12 +1,9 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.VisualC;
namespace Microsoft.Zune.Configuration
{
[StructLayout(LayoutKind.Sequential, Size = 24)]
[MiscellaneousBits(64)]
[DebugInfoInPDB]
[NativeCppClass]
internal struct RefreshCallback
{
@@ -5,11 +5,7 @@ namespace Microsoft.Zune.Configuration
{
internal class TunerInfoHandler : ITunerInfoHandler, IDisposable
{
public event EventHandler OnChanged
{
add { OnChanged += value; }
remove { OnChanged -= value; }
}
public event EventHandler? OnChanged;
private IList<TunerInfo> m_PCsList = new List<TunerInfo>();
private IList<TunerInfo> m_devicesList = new List<TunerInfo>();
@@ -18,8 +14,6 @@ namespace Microsoft.Zune.Configuration
private DateTime m_nextSubscriptionDeviceDeregistrationDate;
private DateTime m_nextAppStoreDeviceDeregistrationDate;
public TunerInfoHandler() { }
~TunerInfoHandler() { }
public virtual bool CanQueryTunerList() { throw new NotImplementedException(); }
public virtual IList<TunerInfo> GetPCsList() { return m_PCsList; }
public virtual IList<TunerInfo> GetDevicesList() { return m_devicesList; }
@@ -0,0 +1,37 @@
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace MicrosoftZuneInterop;
// IUnknown slots [0][2] are implicit. User-declared methods begin at slot [3].
// TODO: replace placeholder GUID with the real one recovered from the native binary.
[GeneratedComInterface]
[Guid("3D8A1F2B-6C4E-4A5D-9B7F-2E0C1A8D3F4E")]
partial interface IQueryPropertyBag
{
// [3][4]: unknown native slots; declared only to preserve vtable ordering.
void _Reserved3();
void _Reserved4();
// [5]: SetString(prop, wchar_t*) -> HRESULT
[PreserveSig]
int SetString(EQueryPropertyBagProp prop, [MarshalAs(UnmanagedType.LPWStr)] string value);
// [6]: unknown
void _Reserved6();
// [7]: SetInt(prop, int) -> HRESULT [bool uses 1/0]
[PreserveSig]
int SetInt(EQueryPropertyBagProp prop, int value);
// [8][12]: unknown
void _Reserved8();
void _Reserved9();
void _Reserved10();
void _Reserved11();
void _Reserved12();
// [13]: IsSet(prop, int* out) -> HRESULT
[PreserveSig]
int IsSet(EQueryPropertyBagProp prop, out int result);
}
@@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
namespace MicrosoftZuneInterop;
[StructLayout(LayoutKind.Sequential, Size = 16)]
internal struct PropIdMapEntry
{
private long _alignment;
}
@@ -1,40 +1,99 @@
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace MicrosoftZuneInterop;
// Native COM interface IMultiSortAttributes — vtable layout (x64, IUnknown = slots 02):
// [3] GetSortOrders() -> EQuerySortType* (parallel array of sort directions)
// [4] GetSortAttributes() -> int* (parallel array of SchemaMap indices)
//
// Native struct IDList (16 bytes):
// int Count
// (4 bytes padding)
// int* Ids (heap-allocated array of Count media IDs; freed by the native caller)
public class QueryPropertyBag : IDisposable
{
private readonly object m_pBag; // Stub for IQueryPropertyBag*
private bool _disposed;
private IQueryPropertyBag? _bag;
public IQueryPropertyBag* GetIQueryPropertyBag()
public QueryPropertyBag()
{
return (IQueryPropertyBag*)m_pBag;
// TODO: P/Invoke ZuneLibraryExports.CreatePropertyBag to get an IQueryPropertyBag* pointer,
// then wrap it:
// IntPtr ptr = ...; // from CreatePropertyBag
// _bag = (IQueryPropertyBag)StrategyBasedComWrappers.Instance
// .GetOrCreateObjectForComInstance(ptr, CreateObjectFlags.None);
}
internal QueryPropertyBag()
public void SetValue(string propertyName, object? value)
{
}
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool P_0)
{
if (P_0)
{
_disposed = true;
EQueryPropertyBagProp prop = MapNameToProp(propertyName);
if (prop == (EQueryPropertyBagProp)(-1) || value is null)
return;
}
try
int hr = value switch
{
_disposed = true;
}
finally
{
base.Finalize();
}
int i => _bag!.SetInt(prop, i),
bool b => _bag!.SetInt(prop, b ? 1 : 0),
string s => _bag!.SetString(prop, s),
_ => throw new ApplicationException($"Unsupported property value type: {value.GetType()}")
};
if (hr < 0)
throw new ApplicationException($"SetValue failed: HRESULT 0x{hr:X8}");
}
public virtual sealed void Dispose()
public bool IsSet(string propertyName)
{
EQueryPropertyBagProp prop = MapNameToProp(propertyName);
if (prop == (EQueryPropertyBagProp)(-1))
return false;
_bag!.IsSet(prop, out int result);
return result == 1;
}
public EQueryPropertyBagProp MapNameToProp(string propertyName)
{
// TODO: recover the 37-entry kPropIdMap table from the native binary
// (case-insensitive wchar_t* → EQueryPropertyBagProp, stored as PropIdMapEntry[37]
// at MicrosoftZuneInterop.?A0x52c37a46.kPropIdMap; each entry is 16 bytes:
// [0..7] wchar_t* name, [8..11] EQueryPropertyBagProp id, [12..15] padding)
throw new NotImplementedException();
}
// Returns the underlying COM pointer for callers that pass it to native query APIs.
public IntPtr GetIQueryPropertyBag()
{
if (_bag is null) return IntPtr.Zero;
return StrategyBasedComWrappers.Instance.GetOrCreateComInterfaceForObject(_bag, CreateComInterfaceFlags.None);
}
// Packs multiIds into a native IDList: { int Count; int* Ids }.
// The returned IntPtr is unmanaged heap memory; the native caller is responsible for freeing it.
public IntPtr PackIDList(IList multiIds)
{
throw new NotImplementedException();
}
// Packs parallel sort-direction/attribute arrays into a native IMultiSortAttributes COM object.
// Requires CSchemaMap.GetIndex to map attribute name strings to schema indices,
// and a native ZuneLibraryExports.CreateMultiSortAttributes factory.
public IntPtr PackMultiSortAttributes(string[] sortStrings, bool[] sortAscendings)
{
throw new NotImplementedException();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
(_bag as IDisposable)?.Dispose();
_bag = null;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
@@ -1,12 +0,0 @@
using System;
namespace MicrosoftZuneLibrary;
public class AppInitializationSequencer
{
public delegate void Phase3CompleteCallback();
internal AppInitializationSequencer()
{
}
}
@@ -1,47 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace MicrosoftZuneLibrary;
public class CallbackOnUIThread : IDisposable
{
private readonly IRequestCallbackOnUIThread _pUI;
private bool _disposed;
public CallbackOnUIThread(IRequestCallbackOnUIThread pUI)
{
_pUI = pUI;
}
public void Request(CallbackPriorityManaged priority, int id, IntPtr pv, IntPtr pNativeDeferredCallback)
{
_pUI.CallbackOnUIThreadRequest(priority, id, pv, pNativeDeferredCallback);
}
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool P_0)
{
if (P_0)
{
_disposed = true;
return;
}
try
{
_disposed = true;
}
finally
{
}
}
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~CallbackOnUIThread()
{
Dispose(false);
}
}
@@ -1,16 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace MicrosoftZuneLibrary;
public class CallbackOnUIThreadPack
{
private readonly object m_pCallback; // Stub for callback data
private readonly int m_id;
public CallbackOnUIThreadPack(object pCallback, int id)
{
m_pCallback = pCallback;
m_id = id;
}
}
@@ -1,8 +0,0 @@
namespace MicrosoftZuneLibrary;
public enum CallbackPriorityManaged
{
CPNormal = 0,
CPHigh = 1,
CPAsync = 2
}
@@ -1,3 +0,0 @@
namespace MicrosoftZuneLibrary;
public delegate void CorePhase2ReadyCallback();
@@ -1,17 +0,0 @@
namespace MicrosoftZuneLibrary;
public enum EBurnProgressStatus
{
ebsUnknown = 0,
ebsMediaCheck = 1,
ebsPreparerating = 2,
ebsPregrooving = 3,
ebsWriting = 4,
ebsVerifying = 5,
ebsFinalizing = 6,
ebsCompleted = 7,
ebsError = 8,
ebsPaused = 9,
ebsReady = 10,
ebsStopped = 11
}
@@ -1,50 +0,0 @@
namespace MicrosoftZuneLibrary;
public enum EBurnState
{
ebsUnknown = 0,
ebsStopped = 1,
ebsPaused = 2,
ebsStarting = 3,
ebsBurning = 4,
ebsVerifying = 5,
ebsFinalizing = 6,
ebsCompleted = 7,
ebsCanceled = 8,
ebsFailed = 9,
ebsPrepared = 10,
ebsIdle = 11,
ebsEjecting = 12,
ebsEjected = 13,
ebsMediaAbsent = 14,
ebsMediaPresent = 15,
ebsReadyToBurn = 16,
ebsNotReady = 17,
ebsNoMedia = 18,
ebsBlankDisc = 19,
ebsPartialDisc = 20,
ebsFullDisc = 21,
ebsClosedDisc = 22,
ebsOpenDisc = 23,
ebsRewritable = 24,
ebsWriteOnce = 25,
ebsDVDPlusR = 26,
ebsDVDPlusRW = 27,
ebsDVDMinusR = 28,
ebsDVDMinusRW = 29,
ebsDVDPlusRDualLayer = 30,
ebsDVDMinusRDualLayer = 31,
ebsHDVDMinusR = 32,
ebsHDVDMinusRW = 33,
ebsHDVDPlusR = 34,
ebsHDVDPlusRW = 35,
ebsBluRayR = 36,
ebsBluRayRW = 37,
ebsArchiveDisc = 38,
ebsCDROM = 39,
ebsCDDA = 40,
ebsCDRW = 41,
ebsDVDROM = 42,
ebsDVDRAM = 43,
ebsMax = 44
}
@@ -1,9 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace MicrosoftZuneLibrary;
public interface INativeDeferredCallback
{
void Invoke(int hrResult, IntPtr pContext);
}
@@ -1,11 +0,0 @@
namespace MicrosoftZuneLibrary;
public interface IQueryListEvents
{
void ListBeginBulkEvents();
void ListEndBulkEvents();
void ListInsert(int index);
void ListRemoveAt(int index);
void ListModified(int dwItem);
void ListNotifyCount(uint ulCount);
}
@@ -1,9 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace MicrosoftZuneLibrary;
public interface IRequestCallbackOnUIThread
{
void CallbackOnUIThreadRequest(CallbackPriorityManaged priority, int id, IntPtr pv, IntPtr pInterface);
}

Some files were not shown because too many files have changed in this diff Show More