Replace HeapFree with CoTaskMemAlloc  (#144)

diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml
index e47bf66..a28e334 100644
--- a/.github/workflows/test-package.yml
+++ b/.github/workflows/test-package.yml
@@ -23,7 +23,7 @@
         sdk: [dev]
     steps:
       - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v1.0
+      - uses: dart-lang/setup-dart@v1
         with:
           sdk: ${{ matrix.sdk }}
       - id: install
@@ -47,10 +47,10 @@
       matrix:
         # Add macos-latest and/or windows-latest if relevant for this package.
         os: [ubuntu-latest]
-        sdk: [2.12.0, dev]
+        sdk: [2.17.0, dev]
     steps:
       - uses: actions/checkout@v2
-      - uses: dart-lang/setup-dart@v1.0
+      - uses: dart-lang/setup-dart@v1
         with:
           sdk: ${{ matrix.sdk }}
       - id: install
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad5be6d..fd2be4d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## 2.0.0
+
+Switch Windows memory allocation to use `CoTaskMemAlloc` and `CoTaskMemFree`,
+which will enable support for `NativeFinalizer`. This release requires Dart
+2.17.0 or greater.
+
 ## 1.2.1
 
 Revert added common C integer types as ABI-specific integers.
@@ -7,7 +13,7 @@
 
 ## 1.2.0 (retracted)
 
-This release requires Dart `2.16.0` or greater. 
+This release requires Dart `2.16.0` or greater.
 
 ## 1.2.0-dev.0
 
@@ -15,7 +21,7 @@
 types will make their way into `dart:ffi` in 2.17 and be deprecated from this
 package. Having them in this package enables using them in Dart 2.16.
 
-This pre-release requires Dart `2.16.0-118.0.dev` or greater. 
+This pre-release requires Dart `2.16.0-118.0.dev` or greater.
 
 ## 1.1.2
 
diff --git a/lib/src/allocation.dart b/lib/src/allocation.dart
index 49babe8..91c8411 100644
--- a/lib/src/allocation.dart
+++ b/lib/src/allocation.dart
@@ -5,9 +5,9 @@
 import 'dart:ffi';
 import 'dart:io';
 
-// Note that kernel32.dll is the correct name in both 32-bit and 64-bit.
+// Note that ole32.dll is the correct name in both 32-bit and 64-bit.
 final DynamicLibrary stdlib = Platform.isWindows
-    ? DynamicLibrary.open('kernel32.dll')
+    ? DynamicLibrary.open('ole32.dll')
     : DynamicLibrary.process();
 
 typedef PosixMallocNative = Pointer Function(IntPtr);
@@ -25,24 +25,16 @@
 final PosixFree posixFree =
     stdlib.lookupFunction<PosixFreeNative, PosixFree>('free');
 
-typedef WinGetProcessHeapFn = Pointer Function();
-final WinGetProcessHeapFn winGetProcessHeap = stdlib
-    .lookupFunction<WinGetProcessHeapFn, WinGetProcessHeapFn>('GetProcessHeap');
-final Pointer processHeap = winGetProcessHeap();
+typedef WinCoTaskMemAllocNative = Pointer Function(Size cb);
+typedef WinCoTaskMemAlloc = Pointer Function(int cb);
+final WinCoTaskMemAlloc winCoTaskMemAlloc =
+    stdlib.lookupFunction<WinCoTaskMemAllocNative, WinCoTaskMemAlloc>(
+        'CoTaskMemAlloc');
 
-typedef WinHeapAllocNative = Pointer Function(Pointer, Uint32, IntPtr);
-typedef WinHeapAlloc = Pointer Function(Pointer, int, int);
-final WinHeapAlloc winHeapAlloc =
-    stdlib.lookupFunction<WinHeapAllocNative, WinHeapAlloc>('HeapAlloc');
-
-typedef WinHeapFreeNative = Int32 Function(
-    Pointer heap, Uint32 flags, Pointer memory);
-typedef WinHeapFree = int Function(Pointer heap, int flags, Pointer memory);
-final WinHeapFree winHeapFree =
-    stdlib.lookupFunction<WinHeapFreeNative, WinHeapFree>('HeapFree');
-
-// ignore: constant_identifier_names
-const int HEAP_ZERO_MEMORY = 8;
+typedef WinCoTaskMemFreeNative = Void Function(Pointer pv);
+typedef WinCoTaskMemFree = void Function(Pointer pv);
+final WinCoTaskMemFree winCoTaskMemFree = stdlib
+    .lookupFunction<WinCoTaskMemFreeNative, WinCoTaskMemFree>('CoTaskMemFree');
 
 /// Manages memory on the native heap.
 ///
@@ -50,14 +42,14 @@
 /// for zero-initialized memory on allocation.
 ///
 /// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
-/// `HeapAlloc` and `HeapFree` against the default public heap.
+/// `CoTaskMemAlloc`.
 class _MallocAllocator implements Allocator {
   const _MallocAllocator();
 
   /// Allocates [byteCount] bytes of of unitialized memory on the native heap.
   ///
   /// For POSIX-based systems, this uses `malloc`. On Windows, it uses
-  /// `HeapAlloc` against the default public heap.
+  /// `CoTaskMemAlloc`.
   ///
   /// Throws an [ArgumentError] if the number of bytes or alignment cannot be
   /// satisfied.
@@ -66,7 +58,7 @@
   Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
     Pointer<T> result;
     if (Platform.isWindows) {
-      result = winHeapAlloc(processHeap, /*flags=*/ 0, byteCount).cast();
+      result = winCoTaskMemAlloc(byteCount).cast();
     } else {
       result = posixMalloc(byteCount).cast();
     }
@@ -78,19 +70,13 @@
 
   /// Releases memory allocated on the native heap.
   ///
-  /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
-  /// against the default public heap. It may only be used against pointers
-  /// allocated in a manner equivalent to [allocate].
-  ///
-  /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
-  /// freed.
-  ///
+  /// For POSIX-based systems, this uses `free`. On Windows, it uses
+  /// `CoTaskMemFree`. It may only be used against pointers allocated in a
+  /// manner equivalent to [allocate].
   @override
   void free(Pointer pointer) {
     if (Platform.isWindows) {
-      if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
-        throw ArgumentError('Could not free $pointer.');
-      }
+      winCoTaskMemFree(pointer);
     } else {
       posixFree(pointer);
     }
@@ -103,7 +89,7 @@
 /// zero-initialized memory allocation.
 ///
 /// For POSIX-based systems, this uses `malloc` and `free`. On Windows, it uses
-/// `HeapAlloc` and `HeapFree` against the default public heap.
+/// `CoTaskMemAlloc` and `CoTaskMemFree`.
 const Allocator malloc = _MallocAllocator();
 
 /// Manages memory on the native heap.
@@ -111,16 +97,28 @@
 /// Initializes newly allocated memory to zero.
 ///
 /// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
-/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
-/// public heap.
+/// `CoTaskMemAlloc` and `CoTaskMemFree`.
 class _CallocAllocator implements Allocator {
   const _CallocAllocator();
 
+  /// Fills a block of memory with a specified value.
+  void _fillMemory(Pointer destination, int length, int fill) {
+    final ptr = destination.cast<Uint8>();
+    for (var i = 0; i < length; i++) {
+      ptr[i] = fill;
+    }
+  }
+
+  /// Fills a block of memory with zeros.
+  ///
+  void _zeroMemory(Pointer destination, int length) =>
+      _fillMemory(destination, length, 0);
+
   /// Allocates [byteCount] bytes of zero-initialized of memory on the native
   /// heap.
   ///
   /// For POSIX-based systems, this uses `malloc`. On Windows, it uses
-  /// `HeapAlloc` against the default public heap.
+  /// `CoTaskMemAlloc`.
   ///
   /// Throws an [ArgumentError] if the number of bytes or alignment cannot be
   /// satisfied.
@@ -129,8 +127,8 @@
   Pointer<T> allocate<T extends NativeType>(int byteCount, {int? alignment}) {
     Pointer<T> result;
     if (Platform.isWindows) {
-      result = winHeapAlloc(processHeap, /*flags=*/ HEAP_ZERO_MEMORY, byteCount)
-          .cast();
+      result = winCoTaskMemAlloc(byteCount).cast();
+      _zeroMemory(result, byteCount);
     } else {
       result = posixCalloc(byteCount, 1).cast();
     }
@@ -142,19 +140,13 @@
 
   /// Releases memory allocated on the native heap.
   ///
-  /// For POSIX-based systems, this uses `free`. On Windows, it uses `HeapFree`
-  /// against the default public heap. It may only be used against pointers
-  /// allocated in a manner equivalent to [allocate].
-  ///
-  /// Throws an [ArgumentError] if the memory pointed to by [pointer] cannot be
-  /// freed.
-  ///
+  /// For POSIX-based systems, this uses `free`. On Windows, it uses
+  /// `CoTaskMemFree`. It may only be used against pointers allocated in a
+  /// manner equivalent to [allocate].
   @override
   void free(Pointer pointer) {
     if (Platform.isWindows) {
-      if (winHeapFree(processHeap, /*flags=*/ 0, pointer) == 0) {
-        throw ArgumentError('Could not free $pointer.');
-      }
+      winCoTaskMemFree(pointer);
     } else {
       posixFree(pointer);
     }
@@ -167,6 +159,5 @@
 /// memory allocation.
 ///
 /// For POSIX-based systems, this uses `calloc` and `free`. On Windows, it uses
-/// `HeapAlloc` with [HEAP_ZERO_MEMORY] and `HeapFree` against the default
-/// public heap.
+/// `CoTaskMemAlloc` and `CoTaskMemFree`.
 const Allocator calloc = _CallocAllocator();
diff --git a/pubspec.yaml b/pubspec.yaml
index f2f91d5..5fb789f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,11 +1,11 @@
 name: ffi
-version: 1.2.1
+version: 2.0.0
 description: Utilities for working with Foreign Function Interface (FFI) code.
 repository: https://github.com/dart-lang/ffi
 
 environment:
-  sdk: '>=2.12.0 <3.0.0'
+  sdk: '>=2.17.0 <3.0.0'
 
 dev_dependencies:
-  test: ^1.16.0
-  lints: ^1.0.0
+  test: ^1.21.1
+  lints: ^2.0.0
diff --git a/test/allocation_test.dart b/test/allocation_test.dart
new file mode 100644
index 0000000..7bb685a
--- /dev/null
+++ b/test/allocation_test.dart
@@ -0,0 +1,24 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:ffi';
+import 'dart:math' show Random;
+
+import 'package:ffi/ffi.dart';
+import 'package:test/test.dart';
+
+const testRuns = 1000;
+
+void main() async {
+  test('calloc', () {
+    // Tests that calloc successfully zeroes out memory.
+    for (var i = 0; i < testRuns; i++) {
+      final allocBytes = Random().nextInt(1000);
+      final mem = calloc<Uint8>(allocBytes);
+      expect(mem.asTypedList(allocBytes).where(((element) => element != 0)),
+          isEmpty);
+      calloc.free(mem);
+    }
+  });
+}