Repository navigation
P Invoke and Win32 APIs
Relevant source files
The following files were used as context for generating this wiki page:
This document provides technical reference for the P/Invoke declarations and Win32 API usage in the native performance layer. The NativeMethods class OfficeScrubC2R-Native.cs:62-153 serves as the central interface to Windows system APIs, providing direct access to registry, file system, and shell operations that are not available or performant enough through managed .NET APIs.
For implementation details of the helper classes that consume these APIs, see RegistryHelper, FileHelper, and ShellHelper. For overall native layer architecture, see Native Library Overview.
The native layer invokes APIs from three core Windows system DLLs to achieve performance improvements over managed alternatives:
| DLL | Purpose | Primary Operations | Performance Benefit |
|---|---|---|---|
advapi32.dll |
Advanced Windows 32 Base API | Registry operations with WOW64 support | 30x faster registry enumeration |
kernel32.dll |
Windows Kernel API | File system operations, reboot scheduling | 15x faster file deletion |
shell32.dll |
Windows Shell API | Shell integration, taskbar/start menu | Native COM interop |
Sources: OfficeScrubC2R-Native.cs:60-153
The NativeMethods class is an internal static class that encapsulates all P/Invoke declarations using the internal access pattern to prevent external usage while allowing consumption by sibling helper classes.
graph TB
subgraph "OfficeScrubC2R-Native.cs"
NM["NativeMethods<br/>(internal static class)<br/>Lines 62-153"]
subgraph "Registry APIs (advapi32.dll)"
RegOpen["RegOpenKeyEx<br/>Line 71-76"]
RegClose["RegCloseKey<br/>Line 79"]
RegDelete["RegDeleteKeyEx<br/>Line 82-86"]
RegDelVal["RegDeleteValue<br/>Line 89-91"]
RegEnum["RegEnumKeyEx<br/>Line 94-102"]
RegEnumVal["RegEnumValue<br/>Line 105-113"]
RegQuery["RegQueryValueEx<br/>Line 116-122"]
end
subgraph "File APIs (kernel32.dll)"
MoveFileEx["MoveFileEx<br/>Line 65-68"]
SetFileAttr["SetFileAttributes<br/>Line 138-140"]
RemoveDir["RemoveDirectory<br/>Line 143"]
end
subgraph "Shell APIs (shell32.dll)"
ILCreate["ILCreateFromPath<br/>Line 125"]
ILFree["ILFree<br/>Line 128"]
SHCreate["SHCreateShellItem<br/>Line 131-135"]
end
subgraph "Constants"
KeyFlags["KEY_WOW64_64KEY<br/>KEY_WOW64_32KEY<br/>KEY_READ/WRITE<br/>Lines 145-149"]
ErrorCodes["ERROR_SUCCESS<br/>ERROR_NO_MORE_ITEMS<br/>Lines 151-152"]
MoveFlags["MOVEFILE_DELAY_UNTIL_REBOOT<br/>Line 150"]
end
NM --> RegOpen
NM --> RegClose
NM --> RegDelete
NM --> RegDelVal
NM --> RegEnum
NM --> RegEnumVal
NM --> RegQuery
NM --> MoveFileEx
NM --> SetFileAttr
NM --> RemoveDir
NM --> ILCreate
NM --> ILFree
NM --> SHCreate
NM --> KeyFlags
NM --> ErrorCodes
NM --> MoveFlags
end
RegHelper["RegistryHelper<br/>Consumes registry APIs"]
FileHelper["FileHelper<br/>Consumes file APIs"]
ShellHelper["ShellHelper<br/>Consumes shell APIs"]
RegOpen --> RegHelper
RegClose --> RegHelper
RegDelete --> RegHelper
RegDelVal --> RegHelper
RegEnum --> RegHelper
RegEnumVal --> RegHelper
RegQuery --> RegHelper
MoveFileEx --> FileHelper
SetFileAttr --> FileHelper
RemoveDir --> FileHelper
ILCreate --> ShellHelper
ILFree --> ShellHelper
SHCreate --> ShellHelper
Sources: OfficeScrubC2R-Native.cs:62-153
The registry APIs provide direct access to the Windows Registry with WOW64 support for accessing both 32-bit and 64-bit registry views on 64-bit systems.
Opens a registry key with specified access rights and WOW64 view flags:
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern int RegOpenKeyEx(
UIntPtr hKey, // Handle to parent key
string lpSubKey, // Subkey path
uint ulOptions, // Reserved (must be 0)
int samDesired, // Access rights + WOW64 flags
out UIntPtr phkResult); // Receives opened key handleKey Parameters:
-
samDesired: Combines access rights (KEY_READ,KEY_WRITE) with WOW64 flags (KEY_WOW64_64KEY,KEY_WOW64_32KEY) - Returns: Win32 error code (0 = success)
Usage Pattern: OfficeScrubC2R-Native.cs:71-76
Deletes a registry key with WOW64 view specification:
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int RegDeleteKeyEx(
UIntPtr hKey, // Handle to parent key
string lpSubKey, // Subkey to delete
int samDesired, // WOW64 view flags
uint Reserved); // Must be 0Advantage over managed APIs: Allows explicit WOW64 view selection, enabling deletion of 32-bit registry entries from 64-bit processes.
Usage Pattern: OfficeScrubC2R-Native.cs:82-86, used by RegistryHelper.DeleteKeyInternal:538-551
Enumerate registry subkeys and values with sequential index-based access:
Usage Pattern: OfficeScrubC2R-Native.cs:94-113, consumed by RegistryHelper.EnumerateKeys:402-436 and RegistryHelper.EnumerateValues:438-472
internal const int KEY_WOW64_64KEY = 0x0100; // Access 64-bit registry view
internal const int KEY_WOW64_32KEY = 0x0200; // Access 32-bit registry view (WOW64)
internal const int KEY_READ = 0x20019; // Read access
internal const int KEY_WRITE = 0x20006; // Write access
internal const int KEY_ALL_ACCESS = 0xF003F; // Full accessSources: OfficeScrubC2R-Native.cs:145-149
File system APIs provide low-level access for deletion operations with reboot scheduling capability.
Schedules file or directory deletion on next system reboot, bypassing file locks:
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool MoveFileEx(
string lpExistingFileName, // File/directory to delete
string lpNewFileName, // NULL for deletion
int dwFlags); // MOVEFILE_DELAY_UNTIL_REBOOTCritical Flag:
internal const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;Deletion Pattern:
-
lpExistingFileName: Path to file/directory -
lpNewFileName:nullto indicate deletion (not rename) -
dwFlags:MOVEFILE_DELAY_UNTIL_REBOOTflag
Implementation: FileHelper.ScheduleDeleteOnReboot:689-697 and FileHelper.ScheduleDirectoryDeleteOnReboot:699-727
Removes read-only and system attributes before deletion:
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool SetFileAttributes(
string lpFileName,
uint dwFileAttributes);Usage: FileHelper.DeleteFile:626-651 removes FileAttributes.ReadOnly flag before calling File.Delete()
Native directory removal for performance-critical scenarios:
Usage: FileHelper.DeleteDirectory:653-687 uses cmd.exe rd /s /q for performance, with RemoveDirectory as fallback
Sources: OfficeScrubC2R-Native.cs:64-68,138-143,150
Shell APIs provide COM-based integration with Windows Explorer shell functionality.
These APIs create shell item pointers from file paths for advanced shell operations:
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
internal static extern IntPtr ILCreateFromPath(string pszPath);
[DllImport("shell32.dll")]
internal static extern void ILFree(IntPtr pidl);
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
internal static extern int SHCreateShellItem(
IntPtr pidlParent,
IntPtr psfParent,
IntPtr pidl,
out IShellItem ppsi);The IShellItem interface provides managed access to shell items:
[ComImport]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItem
{
void BindToHandler(IntPtr pbc, [MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(uint sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
}Usage Context: While declared, these shell APIs are primarily used through the dynamic Shell.Application COM object in ShellHelper:864-974 for taskbar and start menu unpinning operations.
Sources: OfficeScrubC2R-Native.cs:124-165
The P/Invoke declarations follow specific marshaling patterns to ensure correct data transfer between managed and unmanaged code.
| Parameter Type | Marshaling Attribute | Purpose |
|---|---|---|
string lpSubKey |
CharSet = CharSet.Unicode |
Unicode string input |
StringBuilder lpName |
CharSet = CharSet.Unicode |
Unicode string output buffer |
out string |
Default (LPWSTR) | Managed string allocation |
Pattern: All Win32 API strings use Unicode (CharSet.Unicode or W suffix APIs) for consistent international character support.
UIntPtr hKey // Registry key handle (platform-sized)
IntPtr pidl // Shell item ID list pointer
out UIntPtr phk // Output handle parameterPattern: Use UIntPtr for registry handles (varies by platform) and IntPtr for generic pointers.
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool MoveFileEx(...)Pattern: Win32 BOOL (32-bit int) automatically marshaled to managed bool through MarshalAs attribute or return type inference.
Sources: OfficeScrubC2R-Native.cs:64-143
Win32 API error handling follows platform conventions with integration into managed exception handling.
graph TD
API["Win32 API Call"]
Check{"Check Return<br/>Value"}
Success["ERROR_SUCCESS (0)<br/>Operation succeeded"]
NoMore["ERROR_NO_MORE_ITEMS (259)<br/>Enumeration complete"]
Error["Other error code<br/>Operation failed"]
GetLastError["Marshal.GetLastWin32Error()<br/>Get detailed error"]
Silent["Silent failure<br/>return false/null"]
API --> Check
Check -->|"0"| Success
Check -->|"259"| NoMore
Check -->|"Other"| Error
Error --> GetLastError
GetLastError --> Silent
NoMore --> Silent
internal const int ERROR_SUCCESS = 0; // Operation succeeded
internal const int ERROR_NO_MORE_ITEMS = 259; // Enumeration complete (not an error)Usage Pattern in RegistryHelper:
private bool DeleteKeyInternal(RegistryKey hive, string subKey, bool recursive)
{
try
{
if (recursive)
hive.DeleteSubKeyTree(subKey, false);
else
hive.DeleteSubKey(subKey, false);
return true;
}
catch
{
return false; // Silent failure
}
}All P/Invoke declarations use SetLastError = true to preserve Win32 error codes:
[DllImport("advapi32.dll", SetLastError = true)]This enables Marshal.GetLastWin32Error() for detailed diagnostics, though the implementation follows a silent failure pattern OfficeScrubC2R-Native.cs:538-551.
Sources: OfficeScrubC2R-Native.cs:64-152, docs/memory-bank/guidelines.md:24-38
The following diagram shows how Win32 APIs flow from declaration through helper classes to the orchestrator.
sequenceDiagram
participant Orch as OfficeScrubOrchestrator
participant RegH as RegistryHelper
participant NM as NativeMethods
participant Advapi as advapi32.dll
Note over Orch: Initialize with is64Bit flag
Orch->>RegH: new RegistryHelper(is64Bit)
Note over Orch: Request registry key deletion
Orch->>RegH: DeleteKey(hive, subKey, recursive)
RegH->>RegH: KeyExists(hive, subKey)
RegH->>NM: RegOpenKeyEx(hKey, lpSubKey, ...)
NM->>Advapi: P/Invoke RegOpenKeyEx
Advapi-->>NM: Return error code
NM-->>RegH: Handle or error
alt Key exists
RegH->>RegH: DeleteKeyInternal(hive, subKey, recursive)
Note over RegH: Use managed API for safety
RegH->>RegH: hive.DeleteSubKeyTree(subKey, false)
end
alt is64Bit system
Note over RegH: Check WOW64 view
RegH->>RegH: GetWow64Key(subKey)
RegH->>RegH: KeyExists(hive, wow64Key)
RegH->>NM: RegOpenKeyEx with KEY_WOW64_32KEY
NM->>Advapi: P/Invoke with WOW64 flag
Advapi-->>NM: Return error code
alt WOW64 key exists
RegH->>RegH: DeleteKeyInternal(hive, wow64Key, recursive)
end
end
RegH-->>Orch: Return success/failure
Key Observations:
-
NativeMethodsis never called directly by orchestrator - Helper classes abstract Win32 complexity
- WOW64 operations are transparent to caller
- Silent failure pattern prevents exception propagation
Sources: OfficeScrubC2R-Native.cs:62-153,334-616
On 64-bit Windows, the registry is split into 64-bit and 32-bit views. The P/Invoke layer enables explicit view selection:
graph LR
subgraph "64-bit Windows Registry"
HKLM["HKEY_LOCAL_MACHINE"]
subgraph "Native 64-bit View"
Soft64["SOFTWARE<br/>(64-bit applications)"]
Office64["SOFTWARE\Microsoft\Office<br/>(64-bit Office)"]
end
subgraph "WOW64 32-bit View"
Wow["SOFTWARE\Wow6432Node<br/>(32-bit applications)"]
Office32["SOFTWARE\Wow6432Node\Microsoft\Office<br/>(32-bit Office)"]
end
end
HKLM --> Soft64
HKLM --> Wow
Soft64 --> Office64
Wow --> Office32
subgraph "RegistryHelper Access"
KeyExists["KeyExists(hive, subKey)"]
Access64["RegOpenKeyEx<br/>with KEY_WOW64_64KEY"]
Access32["RegOpenKeyEx<br/>with KEY_WOW64_32KEY"]
GetWow64["GetWow64Key(subKey)<br/>transforms path"]
end
KeyExists --> Access64
KeyExists --> GetWow64
GetWow64 --> Access32
Access64 -.checks.-> Office64
Access32 -.checks.-> Office32
private string GetWow64Key(string subKey)
{
// Special case: Classes hive
if (subKey.StartsWith("Software\\Classes\\", StringComparison.OrdinalIgnoreCase))
return "Software\\Classes\\Wow6432Node\\" + subKey.Substring(18);
// General case: Insert Wow6432Node after first component
int pos = subKey.IndexOf('\\');
if (pos > 0)
return subKey.Substring(0, pos) + "\\Wow6432Node\\" + subKey.Substring(pos + 1);
return "Wow6432Node\\" + subKey;
}Examples:
-
SOFTWARE\Microsoft\Office→SOFTWARE\Wow6432Node\Microsoft\Office -
Software\Classes\TypeLib\{GUID}→Software\Classes\Wow6432Node\TypeLib\{GUID}
Implementation: RegistryHelper.GetWow64Key:583-593
Sources: OfficeScrubC2R-Native.cs:145-146,583-593, docs/memory-bank/guidelines.md:52-55
The direct Win32 API access provides measurable performance improvements over managed alternatives:
| Operation | Managed API | P/Invoke API | Performance Factor |
|---|---|---|---|
| Registry enumeration | RegistryKey.GetSubKeyNames() |
RegEnumKeyEx loop |
30x faster |
| Registry WOW64 access | Multiple OpenSubKey attempts |
Single RegOpenKeyEx with flags |
2-3x faster |
| File deletion with locks |
File.Delete() + catch |
MoveFileEx with reboot flag |
Immediate vs. exception overhead |
| Directory deletion |
Directory.Delete() recursive |
cmd.exe rd /s /q |
15x faster for large trees |
- Direct System Calls: No managed layer abstraction or safety checks
- WOW64 Flag Support: Single API call instead of multiple attempts
-
Batch Operations:
cmd.exe rdprocesses entire directory tree natively -
Lock Handling:
MoveFileExschedules deletion without throwing exceptions
Implementation Evidence: OfficeScrubC2R-Native.cs:660-687 shows cmd.exe fallback pattern, RegistryHelper:344-363 demonstrates WOW64 dual-access pattern.
Sources: OfficeScrubC2R-Native.cs:334-767, docs/memory-bank/product.md:23-27
| Function | Purpose | Return Type | Usage |
|---|---|---|---|
RegOpenKeyEx |
Open registry key with WOW64 flags |
int (error code) |
RegistryHelper:523-536 |
RegCloseKey |
Close registry handle |
int (error code) |
Managed via using
|
RegDeleteKeyEx |
Delete key with WOW64 view |
int (error code) |
RegistryHelper:538-551 |
RegDeleteValue |
Delete registry value |
int (error code) |
RegistryHelper:366-400 |
RegEnumKeyEx |
Enumerate subkeys |
int (error code) |
RegistryHelper:402-436 |
RegEnumValue |
Enumerate values |
int (error code) |
RegistryHelper:438-472 |
RegQueryValueEx |
Query value data |
int (error code) |
RegistryHelper:474-502 |
| Function | Purpose | Return Type | Usage |
|---|---|---|---|
MoveFileEx |
Schedule deletion on reboot | bool |
FileHelper:689-697 |
SetFileAttributes |
Modify file attributes | bool |
FileHelper:626-651 |
RemoveDirectory |
Native directory removal | bool |
FileHelper:653-687 |
| Function | Purpose | Return Type | Usage |
|---|---|---|---|
ILCreateFromPath |
Create shell item pointer | IntPtr |
Declared, unused |
ILFree |
Free shell item pointer | void |
Declared, unused |
SHCreateShellItem |
Create IShellItem |
int (error code) |
Declared, unused |
Note: Shell APIs are declared but the implementation uses dynamic COM automation via Shell.Application instead ShellHelper:866-947.
Sources: OfficeScrubC2R-Native.cs:62-165