Search Results for

# V2 API Reference

The V2 API is the recommended surface for `FEX.Core.dll`. Every function returns an `int32` result code, every string crosses the boundary as UTF-8, and every variable-sized output is a DLL-allocated buffer the caller frees with `FreeAllocatedBuffer`.

This page documents one function at a time with the Pascal signature, the canonical C# `[DllImport]`, parameters, return codes, and a worked example. For the underlying types, see types-and-constants.md. For result-code diagnostics, see error-codes.md. For buffer ownership rules, see memory-management.md. For the column-windowed extraction surface used on huge datasets, see v3-api.md.

- Concepts at a glance
- Lifecycle
  - InitLibrary
  - OpenImage
  - CloseImage
  - CloseAllImages
  - ReadFileSystem
- Image metadata
  - GetVersionAsJSON
  - GetLicenseInfoAsJSON
  - GetImageTypeByFilename
  - GetImageTypeAsString
  - CheckImageTypeLogical
  - GetImageInfoByFilenameAsJSON
  - GetImageInfoByIdAsJSON
  - GetPartitionInfoAsJSON
  - GetImageSize
- File records
  - GetFileSystemRecords_V2
  - GetFilePath
  - GetItemInfo
- File data
  - GetFileSize
  - ReadFileData
  - ReadImageData
- File properties
  - GetAvailableProperties
  - GetFilePropertiesFromPathAsJSON
  - GetFilePropertiesAsJSON
- Buffer cleanup
  - FreeAllocatedBuffer
- Handle-based streaming

## Concepts at a glance

Topic | Detail
--- | ---
Calling convention | `cdecl`
String encoding | UTF-8, both directions
Result type | `int32` — `0` = `RESULT_OK`, negative = error, positive = function-specific (e.g. `OpenImage` returns image ID)
V2 buffer pattern | DLL allocates; caller frees with `FreeAllocatedBuffer(IntPtr)`
Thread safety | Full — concurrent calls against the same `ImageID` are safe
Library load | `FEX.Core.dll` (Windows), `libFEX.Core.so` (Linux), `libFEX.Core.dylib` (macOS)

The C# bindings shown below are the canonical pattern used by the FexViewer example. They use `[DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]` on every entry point and `[MarshalAs(UnmanagedType.LPStr)]` for UTF-8 string inputs.

## Lifecycle

### `InitLibrary`

Verifies the license key and unlocks the rest of the API. Must be called exactly once per process before any other export.

Pascal signature:

    function InitLibrary(licenseKey: PAnsiChar): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int InitLibrary([MarshalAs(UnmanagedType.LPStr)] string? licenseKey);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`licenseKey` | UTF-8 string | in | GetData license key (`FEX2-…`). Pass `null` to read from the `FEX_LICENSE_KEY` environment variable.

Returns: `RESULT_OK` (0) on success, `RESULT_INVALIDKEY` (-20) if rejected.

Example:

    string? key = Environment.GetEnvironmentVariable("FEX_LICENSE_KEY");
    int rc = NativeMethods.InitLibrary(key);
    if (rc != NativeMethods.RESULT_OK)
    {
        Console.Error.WriteLine($"License initialization failed (code {rc}).");
        Environment.Exit(2);
    }

Pass the license key issued by GetData (the `FEX2-…` string). Contact `sales@getdata.com` for evaluation keys; see License Keys for installation guidance.

### `OpenImage`

Opens a forensic image and returns an image-session ID for use in subsequent calls.

Pascal signature:

    function OpenImage(filename: PAnsiChar; optionsString: PAnsiChar;
                       callback: TCallbackFunction; userData: Pointer): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int OpenImage(
        [MarshalAs(UnmanagedType.LPStr)] string filename,
        [MarshalAs(UnmanagedType.LPStr)] string? optionsString,
        IntPtr callback,
        IntPtr userData);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`filename` | UTF-8 string | in | Absolute path to the first image segment (`.E01`, `.001`, `.dd`, etc.). Multi-segment images are auto-detected from this path.
`optionsString` | UTF-8 JSON or null | in | JSON options object — currently used to pass passwords. Pass `null` for unencrypted images.
`callback` | function pointer | in | Optional progress callback (`TCallbackFunction`). Pass `IntPtr.Zero` to disable.
`userData` | pointer | in | Opaque value forwarded to the callback. Pass `IntPtr.Zero` if unused.

Returns: Positive integer image-session ID on success. Negative on failure — most commonly:

Code | Meaning
--- | ---
`RESULT_NOFILEFOUND` (-3) | File doesn't exist
`RESULT_IMAGENOTSUPPORTED` (-4) | Format not recognised
`RESULT_COULDNOTOPENIMAGE` (-6) | Generic open failure (corrupt / locked)
`RESULT_PASSWORD_REQUIRED` (-31) | Encrypted image, password not supplied
`RESULT_PASSWORD_INCORRECT` (-32) | Password supplied but wrong

`optionsString` JSON shape:

    {
      "Password": "MyVolumePassword"
    }

For BitLocker recovery-key files, pass the path to the `.bek` file in `Password` — the DLL detects key-file format. Multiple credentials can be tried by closing and re-opening; the FexViewer manifest format wraps this loop and is documented in `sdk/examples/csharp/README.md`.

Example:

    int imageId = NativeMethods.OpenImage(
        @"C:\evidence\disk.E01",
        optionsString: null,
        callback: IntPtr.Zero,
        userData: IntPtr.Zero);

    if (imageId < 0)
        throw new FexCoreException($"OpenImage failed", imageId);

    try
    {
        // ... use imageId for queries ...
    }
    finally
    {
        NativeMethods.CloseImage(imageId);
    }

When `OpenImage` returns `RESULT_PASSWORD_REQUIRED` or `RESULT_PASSWORD_INCORRECT`, no session is allocated — do not call `CloseImage`. Retry with `optionsString` set instead.

### `CloseImage`

Releases the resources associated with an image-session ID. Safe to call while reads are still in flight on other threads — destruction is deferred until the last reader returns.

Pascal signature:

    function CloseImage(ImageID: int32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int CloseImage(int imageId);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | ID returned by `OpenImage`.

Returns: `RESULT_OK` on success, `RESULT_IMAGEIDNOTFOUND` (-5) if the ID is unknown.

Example:

    int rc = NativeMethods.CloseImage(imageId);
    if (rc != NativeMethods.RESULT_OK)
        Console.Error.WriteLine($"CloseImage warned: {rc}");

A close failure is informational — the session is gone either way.

### `CloseAllImages`

Closes every open image session in the current process. Useful at shutdown or on a process-wide reset.

Pascal signature:

    function CloseAllImages: int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int CloseAllImages();

Returns: `RESULT_OK`.

### `ReadFileSystem`

Parses the filesystem of an open image and reports the discovered file count. Must be called once before `GetFileSystemRecords_V2`, `GetFileSystemRecordsCustom_V2`, `GetFileSystemRecordsCustom_V3`, or any indexed read like `ReadFileData`.

Pascal signature:

    function ReadFileSystem(ImageID: int32; out FileCount: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int ReadFileSystem(int imageId, out uint fileCount);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`fileCount` | `uint32` | out | Total number of filesystem records discovered.

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_NOFILESYSTEM`, `RESULT_UNSUPPORTED_FILESYSTEM`, `RESULT_CORRUPTED_FILESYSTEM`.

Example:

    int rc = NativeMethods.ReadFileSystem(imageId, out uint count);
    if (rc != NativeMethods.RESULT_OK)
        throw new FexCoreException("ReadFileSystem", rc);

    Console.WriteLine($"Discovered {count} filesystem records.");

## Image metadata

### `GetVersionAsJSON`

Returns DLL version information as a UTF-8 JSON document.

Pascal signature:

    function GetVersionAsJSON(out Buffer: PByte; out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetVersionAsJSON(out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 string. Caller must free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Byte count including the null terminator.

Returns: `RESULT_OK`, `RESULT_ERROR`, `RESULT_BUFFER_OVERFLOW`.

JSON output:

    {
      "App Author": "GetData Forensics",
      "App Name": "FEXCore",
      "OEM App Name": "",
      "Major": 1,
      "Minor": 4,
      "ReleaseNo": 0,
      "BuildNo": 1234,
      "Flags": 0,
      "OEMFlags": 0
    }

The version JSON uses descriptive labels rather than the camelCase keys seen elsewhere in the SDK; this output is informational and intended for display rather than programmatic parsing.

Example:

    IntPtr buffer = IntPtr.Zero;
    try
    {
        int rc = NativeMethods.GetVersionAsJSON(out buffer, out uint size);
        if (rc != NativeMethods.RESULT_OK)
            throw new FexCoreException("GetVersionAsJSON", rc);

        string json = Marshal.PtrToStringUTF8(buffer, (int)size)?.TrimEnd('\0') ?? "";
        Console.WriteLine(json);
    }
    finally
    {
        if (buffer != IntPtr.Zero) NativeMethods.FreeAllocatedBuffer(buffer);
    }

### `GetLicenseInfoAsJSON`

Returns the active licence details — including the holder name, company, and email embedded in newer keys — as a UTF-8 JSON document. Only valid after a successful `InitLibrary`; before that it returns `RESULT_INVALIDKEY`.

Pascal signature:

    function GetLicenseInfoAsJSON(out Buffer: PByte; out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetLicenseInfoAsJSON(out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 string. Caller must free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Byte count including the null terminator.

Returns: `RESULT_OK`, `RESULT_INVALIDKEY` (library not initialized), `RESULT_ERROR`.

JSON output:

    {
      "version": 3,
      "cid": 110,
      "name": "Jane Examiner",
      "company": "Example Forensics Ltd",
      "email": "jane@example.com",
      "features": "0xFFFFFFFFFFFFFFFF",
      "issued": "2026-06-04",
      "expires": "2026-12-31",
      "daysRemaining": 210
    }

Keys issued before holder embedding return empty `name`, `company`, and `email` strings (with `"version": 2`). `features` is a hex string to avoid 64-bit precision loss in JSON consumers; dates are `yyyy-mm-dd`.

Example:

    IntPtr buffer = IntPtr.Zero;
    try
    {
        int rc = NativeMethods.GetLicenseInfoAsJSON(out buffer, out uint size);
        if (rc != NativeMethods.RESULT_OK)
            throw new FexCoreException("GetLicenseInfoAsJSON", rc);

        string json = Marshal.PtrToStringUTF8(buffer, (int)size)?.TrimEnd('\0') ?? "";
        Console.WriteLine(json);
    }
    finally
    {
        if (buffer != IntPtr.Zero) NativeMethods.FreeAllocatedBuffer(buffer);
    }

See License keys for the end-to-end licensing guide.

### `GetImageTypeByFilename`

Returns the integer image-type code for a file based on extension and header sniffing. Cheap — does not open the image.

Pascal signature:

    function GetImageTypeByFilename(filename: PAnsiChar; out ImageType: int32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetImageTypeByFilename(
        [MarshalAs(UnmanagedType.LPStr)] string filename,
        out int imageType);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`filename` | UTF-8 string | in | Absolute path.
`imageType` | `int32` | out | Numeric type code. Pass to `GetImageTypeAsString` for the human-readable name.

Returns: `RESULT_OK` on detection, `RESULT_NOFILEFOUND`, `RESULT_IMAGENOTSUPPORTED`.

Example:

    int rc = NativeMethods.GetImageTypeByFilename(@"C:\evidence\disk.E01", out int code);
    if (rc == NativeMethods.RESULT_OK)
        Console.WriteLine($"Type code: {code}");

### `GetImageTypeAsString`

Translates an image-type code (from `GetImageTypeByFilename`) to a human-readable name like `"E01"` or `"DMG"`.

Pascal signature:

    function GetImageTypeAsString(anImageType: int32; out Buffer: PByte;
                                  out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetImageTypeAsString(
        int imageType,
        out IntPtr buffer,
        out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageType` | `int32` | in | Code from `GetImageTypeByFilename`.
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 string. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Bytes including null.

Returns: `RESULT_OK`, `RESULT_ERROR`, `RESULT_BUFFER_OVERFLOW`.

### `CheckImageTypeLogical`

Tests whether a given image-type code represents a logical image (file-list container like L01 / Lx01 / AD1) versus a physical disk image (E01 / DD).

Pascal signature:

    function CheckImageTypeLogical(anImageType: int32; out IsLogical: int32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int CheckImageTypeLogical(int imageType, out int isLogical);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageType` | `int32` | in | Code from `GetImageTypeByFilename`.
`isLogical` | `int32` | out | `1` if logical, `0` if physical.

Returns: `RESULT_OK`, `RESULT_IMAGENOTSUPPORTED`.

### `GetImageInfoByFilenameAsJSON`

Returns image metadata as JSON without keeping the image open. Useful for fast inventory passes.

Pascal signature:

    function GetImageInfoByFilenameAsJSON(filename: PUTF8Char;
                                          out Buffer: PByte;
                                          out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern int GetImageInfoByFilenameAsJSON(
        [MarshalAs(UnmanagedType.LPStr)] string filename,
        out IntPtr buffer,
        out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`filename` | UTF-8 string | in | Absolute image path.
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 JSON. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Byte count incl. null.

Returns: `RESULT_OK`, `RESULT_NOFILEFOUND`, `RESULT_COULDNOTOPENIMAGE`, `RESULT_INVALID_UTF8`, `RESULT_PATH_TRAVERSAL`, `RESULT_BUFFER_OVERFLOW`, `RESULT_PASSWORD_REQUIRED`, `RESULT_PASSWORD_INCORRECT`. The encryption codes mean the format-recognition succeeded but the metadata block requires a password — use `OpenImage` with credentials and switch to `GetImageInfoByIdAsJSON` for those.

JSON shape (camelCase per current convention):

    {
      "deviceName": "Samsung SSD 860 EVO 250GB",
      "deviceDescription": "Internal HDD #2",
      "deviceSize": 250059350016,
      "imageType": "E01",
      "segmentCount": 12,
      "isLogical": false,
      "filename": "disk.E01",
      "md5":  "5d41402abc4b2a76b9719d911017c592",
      "sha1": "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d",
      "encaseInformation": {
        "caseName": "OPS-2026-001",
        "evidenceNumber": "2",
        "examiner": "L. Investigator"
      }
    }

Format-specific blocks like `encaseInformation` are present when the format carries acquisition metadata; absent otherwise.

Example:

    IntPtr buf = IntPtr.Zero;
    try
    {
        int rc = NativeMethods.GetImageInfoByFilenameAsJSON(
            @"C:\evidence\disk.E01", out buf, out uint size);
        if (rc != NativeMethods.RESULT_OK)
            throw new FexCoreException("GetImageInfoByFilenameAsJSON", rc);

        string json = Marshal.PtrToStringUTF8(buf, (int)size)?.TrimEnd('\0') ?? "";
        var meta = JsonSerializer.Deserialize<ImageMetadata>(json);
        Console.WriteLine($"Device: {meta?.DeviceName}, size: {meta?.DeviceSize:N0} bytes");
    }
    finally
    {
        if (buf != IntPtr.Zero) NativeMethods.FreeAllocatedBuffer(buf);
    }

### `GetImageInfoByIdAsJSON`

Returns metadata for an already-opened image session. Equivalent JSON shape to `GetImageInfoByFilenameAsJSON`, plus runtime-only fields populated after parsing (e.g. confirmed filesystem type).

Pascal signature:

    function GetImageInfoByIdAsJSON(ImageID: int32;
                                    out Buffer: PByte;
                                    out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern int GetImageInfoByIdAsJSON(int imageId, out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 JSON. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Bytes incl. null.

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_IMAGENODATA`, `RESULT_BUFFER_OVERFLOW`.

Use this in preference to `GetImageInfoByFilenameAsJSON` whenever you've already opened the image — it skips re-parsing the headers.

### `GetPartitionInfoAsJSON`

Returns a JSON array describing every partition in the image (location, size, partition-table style, filesystem type, VBR fingerprint).

Pascal signature:

    function GetPartitionInfoAsJSON(ImageID: int32;
                                    out Buffer: PByte;
                                    out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetPartitionInfoAsJSON(int imageId, out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 JSON array. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Bytes incl. null.

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_IMAGENODATA`, `RESULT_BUFFER_OVERFLOW`.

### `GetImageSize`

Returns the logical size of an opened image in bytes — i.e. the size of the acquired device, not the on-disk size of the segment files.

Pascal signature:

    function GetImageSize(ImageID: int32; out Size: int64): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetImageSize(int imageId, out long size);

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`.

## File records

### `GetFileSystemRecords_V2`

Returns every filesystem record in the image as a flat array of `TFileRecord` structs. The single allocation owns all contained pointers (filenames, children lists). Free with one call to `FreeAllocatedBuffer`.

Pascal signature:

    function GetFileSystemRecords_V2(ImageID: int32;
                                     out FileCount: uint32;
                                     out Records: PFileRecord;
                                     out RecordArraySize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetFileSystemRecords_V2(
        int imageId,
        out uint fileCount,
        out IntPtr records,
        out uint recordArraySize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID. Must already have had `ReadFileSystem` called.
`fileCount` | `uint32` | out | Number of records in the array.
`records` | `IntPtr` | out | DLL-allocated array of `TFileRecord`. Free with `FreeAllocatedBuffer`.
`recordArraySize` | `uint32` | out | Total byte size of the array allocation.

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_OUT_OF_MEMORY`, `RESULT_BUFFER_OVERFLOW` (datasets so large that `count × sizeof` overflows `uint32` — switch to V3 in that case).

Example:

    NativeMethods.ReadFileSystem(imageId, out _);

    IntPtr records = IntPtr.Zero;
    try
    {
        int rc = NativeMethods.GetFileSystemRecords_V2(
            imageId, out uint count, out records, out _);
        if (rc != NativeMethods.RESULT_OK)
            throw new FexCoreException("GetFileSystemRecords_V2", rc);

        int stride = Marshal.SizeOf<FileRecordNative>();
        IntPtr cur = records;
        for (int i = 0; i < count; i++)
        {
            var native = Marshal.PtrToStructure<FileRecordNative>(cur);
            string name = native.filename != IntPtr.Zero
                ? Marshal.PtrToStringUTF8(native.filename) ?? ""
                : "";

            bool isFolder  = (native.status & StatusFlags.FILESTATUS_FOLDER)  != 0;
            bool isDeleted = (native.status & StatusFlags.FILESTATUS_DELETED) != 0;

            Console.WriteLine($"{native.fileBatesId,8}  {(isFolder ? "D" : "F")}{(isDeleted ? "x" : " ")}  {native.logicalSize,12:N0}  {name}");

            cur = IntPtr.Add(cur, stride);
        }
    }
    finally
    {
        if (records != IntPtr.Zero) NativeMethods.FreeAllocatedBuffer(records);
    }

`FileRecordNative` and `StatusFlags` are defined in types-and-constants.md. Resolve every `filename` to a managed `string` before you call `FreeAllocatedBuffer` — the pointers are dangling after the free.

### `GetFilePath`

Returns the full hierarchical path of a single record by walking parent pointers from the root.

Pascal signature:

    function GetFilePath(ImageID: int32; FileIndex: int32;
                         out Buffer: PByte; out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetFilePath(
        int imageId, int fileIndex, out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`fileIndex` | `int32` | in | Index into the record array (matches `parentIndex` semantics).
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 path string. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Bytes incl. null.

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_FILEINDEXNOTFOUND`, `RESULT_BUFFER_OVERFLOW`.

For most use cases it's faster to reconstruct paths in C# from the flat record array rather than calling `GetFilePath` per record. See the `PathBuilder` class in `sdk/examples/csharp/Models/` for the pattern.

### `GetItemInfo`

Returns metadata for a single record as JSON, with selectable detail level.

Pascal signature:

    function GetItemInfo(ImageID: int32; FileIndex: int32; InfoType: int32;
                         out Buffer: PByte; out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetItemInfo(
        int imageId, int fileIndex, int infoType,
        out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`fileIndex` | `int32` | in | Index into the record array.
`infoType` | `int32` | in | `0` = full (dates + hashes), `1` = basic (dates only), `2` = minimal (id / size / status).
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 JSON. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Bytes incl. null.

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_FILEINDEXNOTFOUND`, `RESULT_NOFILESYSTEM`, `RESULT_BUFFER_OVERFLOW`.

## File data

### `GetFileSize`

Returns the size of a single file in the filesystem. Note: this returns the physical size (cluster-rounded). For logical size use the `logicalSize` field on the record.

Pascal signature:

    function GetFileSize(ImageID: int32; FileIndex: int32; out Size: uint64): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetFileSize(int imageId, int fileIndex, out ulong fileSize);

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_FILEINDEXNOTFOUND`, `RESULT_FILENOSIZE`.

When extracting files, always truncate writes to the `logicalSize` from the record; otherwise you will write cluster-slack bytes past the end of the real content.

### `ReadFileData`

Reads a range of bytes from a single filesystem entry into a caller-allocated buffer. This is a caller-allocated function (Pattern 3 in memory-management.md) — there's no `FreeAllocatedBuffer` here.

Pascal signature:

    function ReadFileData(ImageID: int32; FileIndex: int32; Offset: int64;
                          Buffer: Pointer; BufferSize: int32;
                          out BytesRead: int32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int ReadFileData(
        int imageId, int fileIndex, long offset,
        IntPtr buffer, int bufferSize, out int bytesRead);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`fileIndex` | `int32` | in | Record index.
`offset` | `int64` | in | Byte offset within the file.
`buffer` | `IntPtr` | in/out | Caller-allocated unmanaged buffer.
`bufferSize` | `int32` | in | Capacity of `buffer` in bytes.
`bytesRead` | `int32` | out | Bytes actually read (may be less than `bufferSize` near EOF).

Returns: `RESULT_OK` plus partial read possible, `RESULT_INCOMPLETEREAD` near EOF, `RESULT_FILEINDEXNOTFOUND`, `RESULT_FILENOSIZE`, `RESULT_ENCRYPTED_DATA`.

Example (1 MB chunked extraction):

    const int CHUNK = 1024 * 1024;
    IntPtr scratch = Marshal.AllocHGlobal(CHUNK);
    try
    {
        long offset = 0;
        while (offset < logicalSize)
        {
            int want = (int)Math.Min(CHUNK, logicalSize - offset);
            int rc = NativeMethods.ReadFileData(
                imageId, fileIndex, offset, scratch, want, out int got);
            if (rc != NativeMethods.RESULT_OK || got <= 0) break;

            var slice = new byte[got];
            Marshal.Copy(scratch, slice, 0, got);
            outputStream.Write(slice, 0, got);

            offset += got;
        }
    }
    finally
    {
        Marshal.FreeHGlobal(scratch);
    }

### `ReadImageData`

Reads raw bytes from anywhere in the underlying image (not a single file). Useful for low-level analysis — partition tables, filesystem boot sectors, unallocated space.

Pascal signature:

    function ReadImageData(ImageID: int32; Offset: int64;
                           Buffer: Pointer; BufferSize: int32;
                           out BytesRead: int32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int ReadImageData(
        int imageId, long offset,
        IntPtr buffer, int bufferSize,
        out int bytesRead);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`imageId` | `int32` | in | Image-session ID.
`offset` | `int64` | in | Byte offset within the image.
`buffer` | `IntPtr` | in/out | Caller-allocated buffer.
`bufferSize` | `int32` | in | Capacity.
`bytesRead` | `int32` | out | Actual bytes read.

Returns: `RESULT_OK`, `RESULT_INCOMPLETEREAD`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_ENCRYPTED_DATA`.

## File properties

The properties API exposes the rich format-specific metadata that file-type drivers extract — JPEG EXIF tags, PDF metadata, ZIP entry lists, etc.

### `GetAvailableProperties`

Returns the set of properties available for each filesystem record. Use this to discover what column names are valid for V3 custom extraction.

Pascal signature:

    function GetAvailableProperties(out Buffer: PByte; out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetAvailableProperties(out IntPtr buffer, out uint bufferSize);

Parameters:

Name | Type | Dir | Purpose
--- | --- | --- | ---
`buffer` | `IntPtr` | out | DLL-allocated UTF-8 JSON array. Free with `FreeAllocatedBuffer`.
`bufferSize` | `uint32` | out | Bytes incl. null.

Returns: `RESULT_OK`, `RESULT_BUFFER_OVERFLOW`.

JSON shape:

    [
      {"name": "EntryName",   "type": "String",   "typeKind": "tkUString"},
      {"name": "LogicalSize", "type": "Int64",    "typeKind": "tkInt64"},
      {"name": "Modified",    "type": "DateTime", "typeKind": "tkFloat"},
      ...
    ]

### `GetFilePropertiesFromPathAsJSON`

Inspects a file on the host filesystem (not inside an image) using the DLL's file-type drivers. Returns the property tree as JSON.

Pascal signature:

    function GetFilePropertiesFromPathAsJSON(Filename: PUTF8Char;
                                             out Buffer: PByte;
                                             out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public static extern int GetFilePropertiesFromPathAsJSON(
        [MarshalAs(UnmanagedType.LPStr)] string filename,
        out IntPtr buffer, out uint bufferSize);

Returns: `RESULT_OK`, `RESULT_INVALID_UTF8`, `RESULT_PATH_TRAVERSAL`, `RESULT_NOFILEFOUND`, `RESULT_IMAGENOTSUPPORTED` (no driver matched the extension), `RESULT_BUFFER_OVERFLOW`.

### `GetFilePropertiesAsJSON`

Same as the path variant, but for a file inside an opened image session.

Pascal signature:

    function GetFilePropertiesAsJSON(ImageID: int32; FileIndex: int32;
                                     out Buffer: PByte;
                                     out BufferSize: uint32): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int GetFilePropertiesAsJSON(
        int imageId, int fileIndex,
        out IntPtr buffer, out uint bufferSize);

Returns: `RESULT_OK`, `RESULT_IMAGEIDNOTFOUND`, `RESULT_FILEINDEXNOTFOUND`, `RESULT_NOFILESYSTEM`, `RESULT_IMAGENOTSUPPORTED`, `RESULT_COULDNOTOPENIMAGE`, `RESULT_BUFFER_OVERFLOW`.

## Buffer cleanup

### `FreeAllocatedBuffer`

Releases any V2 buffer the DLL allocated for the caller. Safe to call with `IntPtr.Zero` — it returns `RESULT_OK` without doing anything.

Pascal signature:

    function FreeAllocatedBuffer(Buffer: PByte): int32; cdecl;

C# binding:

    [DllImport("FEX.Core.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int FreeAllocatedBuffer(IntPtr buffer);

Returns: `RESULT_OK`, `RESULT_ERROR` (only if the underlying free itself raised an exception).

Pair this with every V2 export that has an `out IntPtr Buffer` parameter. For V3 spec arrays use `FreePropertySpecArray` instead — see v3-api.md.

## Handle-based streaming

A separate streaming surface exists for callers that want to read very large files without per-call setup overhead:

- `RequestFileStream(imageId, fileIndex, out streamHandle)`
- `ReadFileStream(streamHandle, offset, buffer, bufferSize, out bytesRead)`
- `ReleaseFileStream(streamHandle)`

This is functionally equivalent to `ReadFileData` but caches the underlying stream object across reads. Most callers should start with `ReadFileData` and only adopt the handle-based variant if profiling shows per-call overhead is a bottleneck. Documentation for these will be expanded in a future release.