docs/interop-unmarshaled-conversion.md
This repository is moving trim-unsafe manual interop toward source-generated CsWin32 interop.
When a user asks to convert marshaled interop code into unmarshaled interop, prefer updating NativeMethods.txt and the call sites instead of preserving manual DllImport/LibraryImport declarations or wrapping them with more local P/Invoke code.
Removing Vanara interop usage is also part of this direction since it internally has trim-unsafe interop code.
Remove interop code that relies on runtime marshalling, especially declarations using DllImport, ComImport and the Vanara package.
The target shape is:
src/Files.App.CsWin32/NativeMethods.txt.Windows.Win32.PInvoke and generated CsWin32 types at the call site.Win32PInvoke only for definitions that are not yet converted or cannot be generated by CsWin32.Locate manual interop:
git grep -n "DllImport\|MarshalAs\|StringBuilder" -- src
git grep -n "Win32PInvoke\." -- src/Files.App
git grep -n "using Vanara\|Vanara\.PInvoke\|Kernel32\.\|Shell32\.\|User32\." -- src/Files.App
Add the API and related generated types to src/Files.App.CsWin32/NativeMethods.txt.
Include both the function and any dependent structs, enums, or COM interfaces that the call site needs. For example:
RmStartSession
RmRegisterResources
RmGetList
RmEndSession
RM_PROCESS_INFO
SHBrowseForFolder
BROWSEINFOW
SHGetPathFromIDList
SHCreateItemFromParsingName
SHCreateStreamOnFileEx
Build the CsWin32 project or the app project to refresh generated signatures:
dotnet build src/Files.App.CsWin32/Files.App.CsWin32.csproj -c Debug -p:Platform=x64
Update callers to use generated APIs directly.
Prefer generated safe overloads when they exist, such as Span<char>, SafeHandle, ComPtr<T>, generated enums, and generated structs. Use unsafe raw overloads only when the generated API naturally exposes pointers or when COM pointer identity is required.
Remove the manual definition only after all callers have moved.
Use targeted checks:
git grep -n "Win32PInvoke\.RmStartSession\|Win32PInvoke\.SHBrowseForFolder" -- src/Files.App
git grep -n "RM_PROCESS_INFO\|BROWSEINFO" -- src/Files.App
Build and confirm there are no C# errors.
In this repo, WinUI XAML compiler failures may appear independently of interop changes. Separate error CS* failures from MSB3073 XAML compiler failures when reporting verification.
StringBuilder output buffers should usually become Span<char> or fixed char* buffers.
IntPtr handles should become SafeFileHandle, SafeHandle, HANDLE, or generated handle structs where practical.
Some APIs such as CoCreateInstance and SHCreateItemFromParsingName often require unsafe pointer overloads:
void* raw;
Guid iid = SomeInterfaceIid;
HRESULT hr = PInvoke.CoCreateInstance(&clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, &iid, &raw);
IntPtr instance = (IntPtr)raw;
Shell APIs that return PIDLs or allocated strings still require explicit lifetime management, but the API declaration should come from CsWin32:
var pidl = PInvoke.SHBrowseForFolder(ref browseInfo);
Marshal.FreeCoTaskMem((nint)pidl);
Restart Manager APIs can use generated RM_PROCESS_INFO and Span<char> session keys instead of local struct definitions.
Do not keep a local LibraryImport copy when the API can be represented in NativeMethods.txt. The requested direction is to update callees to CsWin32-generated interop.
Treat Vanara removal the same way as manual P/Invoke removal when Vanara is only wrapping a native API. Add the API to NativeMethods.txt, inspect the generated CsWin32 signature, then update the call site to generated types.
Example conversion:
// Before
var lib = Kernel32.LoadLibrary(file);
StringBuilder result = new(2048);
_ = User32.LoadString(lib, number, result, result.Capacity);
Kernel32.FreeLibrary(lib);
return result.ToString();
// After
using var lib = PInvoke.LoadLibrary(file);
Span<char> result = stackalloc char[2048];
int length = PInvoke.LoadString(lib, (uint)number, result, result.Length);
return result[..length].ToString();
Useful heuristics:
Kernel32.*, User32.*, or Shell32.* calls that map directly to one Win32 function.using Vanara.PInvoke only when no remaining types in the file depend on it.Span<T> overloads over StringBuilder buffers when available.SafeHandle overloads where CsWin32 provides them.LoadLibrary returns a disposable FreeLibrarySafeHandle, so the call site can use using var instead of a separate FreeLibrary call.NativeMethods.txt contains every newly used API/type.dotnet build src/Files.App.CsWin32/Files.App.CsWin32.csproj -c Debug -p:Platform=x64 succeeds.dotnet build src/Files.App/Files.App.csproj -c Debug -p:Platform=x64 has no new error CS* errors.