Skip to content

Commit

Permalink
Merge pull request #691 from dart-windows/finalizer
Browse files Browse the repository at this point in the history
Switch to `Finalizer`
  • Loading branch information
timsneath committed May 7, 2023
2 parents 61efad5 + 9b4b8a3 commit 3177e32
Show file tree
Hide file tree
Showing 21 changed files with 59 additions and 174 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## 5.0.0

- Added `detach` method to `IUnknown`, which detaches the object from
`Finalizer` if you want to manually manage the lifetime of the object
yourself (#677)
- [BREAKING CHANGE] `Finalizer` is now attached to COM objects. Therefore, you
no longer need to call `.release()` to decrement the reference count, because
`Finalizer` will do it for you when they go out of scope (#691)

**Note:** Calling `.release()` with `Finalizer` attached may result in use
after free and cause the process to crash

**Note:** If you're manually managing the lifetime of the object, you need to
call `.release()` to decrement the reference count

## 4.1.4

- Add some minor network APIs
Expand Down
31 changes: 13 additions & 18 deletions doc/com.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

Windows APIs that use the COM invocation model.

Since the Win32 package primarily focuses on providing a lightweight wrapper for
the underlying Windows API primitives, you can use the same API calls as
Since the `win32` package primarily focuses on providing a lightweight wrapper
for the underlying Windows API primitives, you can use the same API calls as
described in Microsoft documentation to create an manipulate objects (e.g.
`CoCreateInstance` and `IUnknown->QueryInterface`). However, since this
introduces a certain amount of boilerplate and non-idiomatic Dart code, the
Expand Down Expand Up @@ -37,7 +37,7 @@ object, it is easier to use the `createFromID` static helper function:

```dart
final fileDialog2 = IFileDialog2(
COMObject.createFromID(CLSID_FileOpenDialog, IID_IFileDialog2));
COMObject.createFromID(CLSID_FileOpenDialog, IID_IFileDialog2));
```

`createFromID` returns a `Pointer<COMObject>` containing the requested object,
Expand All @@ -47,16 +47,16 @@ which can then be cast into the appropriate interface as shown above.

COM allows objects to implement multiple interfaces, but it does not let you
merely cast an object to a different interface. Instead, returned pointers are
to a specific interface. However, every COM interface in the Dart Win32 package
derives from `IUnknown`, so as in other language implementations of COM, you may
call `queryInterface` on any object to retrieve a pointer to a different
to a specific interface. However, every COM interface in the `win32` package
derives from `IUnknown`, so as in other language implementations of COM, you
may call `queryInterface` on any object to retrieve a pointer to a different
supported interface.

More information on COM interfaces may be found in the [Microsoft
documentation](https://docs.microsoft.com/en-us/windows/win32/learnwin32/asking-an-object-for-an-interface).

COM interfaces supply a method that wraps `queryInterface`. If you
have an existing COM object, you can call it as follows:
COM interfaces supply a method that wraps `queryInterface`. If you have an
existing COM object, you can call it as follows:

```dart
final modalWindow = IModalWindow(fileDialog2.toInterface(IID_IModalWindow));
Expand Down Expand Up @@ -100,7 +100,11 @@ if (FAILED(hr) && hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {

### Releasing COM objects

When you have finished using a COM interface, you should release it with the `release` method:
Most of the time, you don't need to do anything as COM objects are
automatically released by `Finalizer` when they go out of scope.

However, if you're manually managing the lifetime of the object (i.e. by
calling the `.detach()`), you should release it by calling the `.release()`:

```dart
fileOpenDialog.release(); // Release the interface
Expand All @@ -109,14 +113,5 @@ fileOpenDialog.release(); // Release the interface
Often this will be called as part of a `try` / `finally` block, to guarantee
that the object is released even if an exception is thrown.

### Unloading COM support

When you have finished using COM, you should uninitialize it with the
following call:

```dart
CoUninitialize();
```

A full example of these calls can be found in the `com_demo.dart` file in the
`example\` subfolder.
18 changes: 0 additions & 18 deletions example/com_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,12 @@ void main() {
'modalWindow.ptr is ${modalWindow.ptr.address.toHexString(64)}');
print('refCount is now ${refCount(modalWindow)}\n');

fileDialog2.release();
print('Release fileDialog2.\n'
'refCount is now ${refCount(modalWindow)}\n');

// Now get the IFileOpenDialog interface.
final fileOpenDialog = IFileOpenDialog.from(modalWindow);

print('Get IFileOpenDialog interface.\n'
'fileOpenDialog.ptr is ${fileOpenDialog.ptr.address.toHexString(64)}');
print('refCount is now ${refCount(fileOpenDialog)}\n');

modalWindow.release();
print('Release modalWindow.\n'
'refCount is now ${refCount(fileOpenDialog)}\n');

// Use IFileOpenDialog.Show, which is inherited from IModalWindow
hr = fileOpenDialog.show(NULL);
if (FAILED(hr)) {
Expand All @@ -76,15 +67,6 @@ void main() {
}
}

fileOpenDialog.release();
print('Released fileOpenDialog.');

fileDialog.release();
print('Released fileDialog.');

// Uninitialize COM now that we're done with it.
CoUninitialize();

// Clear up
free(pTitle);
print('All done!');
Expand Down
7 changes: 0 additions & 7 deletions example/dialogshow.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,10 @@ void main() {
final path = pathPtr.value.toDartString();

print('Result: $path');

hr = item.release();
if (!SUCCEEDED(hr)) throw WindowsException(hr);
}

hr = fileDialog.release();
if (!SUCCEEDED(hr)) throw WindowsException(hr);
} else {
throw WindowsException(hr);
}
CoUninitialize();

print('All done!');
}
3 changes: 1 addition & 2 deletions example/gamepad.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ void main() {
}

free(state);
print('all done');
CoUninitialize();
print('All done');
}
4 changes: 0 additions & 4 deletions example/knownfolder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,11 @@ String getDesktopPath3() {
hr = knownFolder.getPath(0, ppszPath);
if (FAILED(hr)) throw WindowsException(hr);

knownFolder.release();
knownFolderManager.release();

final path = ppszPath.value.toDartString();
return path;
} finally {
free(appsFolder);
free(ppszPath);
CoUninitialize();
}
}

Expand Down
4 changes: 0 additions & 4 deletions example/network.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,12 @@ void main() {
'$networkName: ${isNetworkConnected ? 'connected' : 'disconnected'}');
}

network.release();
netPtr = calloc<COMObject>();
hr = enumerator.next(1, netPtr.cast(), elements);
}
} finally {
free(elements);
free(descPtr);
free(nlmConnectivity);
netManager.release();

CoUninitialize();
}
}
2 changes: 0 additions & 2 deletions example/sensor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,4 @@ void main() {
free(pCount);
free(pSensorsColl);
free(sampleDateTimeSensorCategory);

CoUninitialize();
}
7 changes: 1 addition & 6 deletions example/shortcut.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ void createShortcut(String path, String pathLink, String? description) {
shellLink.setPath(lpPath);
if (description != null) shellLink.setDescription(lpDescription);

final persistFile = IPersistFile.from(shellLink);
shellLink.release();
persistFile
..save(lpPathLink, TRUE)
..release();
IPersistFile.from(shellLink).save(lpPathLink, TRUE);
} finally {
free(lpPath);
free(lpPathLink);
Expand All @@ -53,7 +49,6 @@ void main(List<String> args) {
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
createShortcut(results['path'] as String, results['shortcut'] as String,
results['description'] as String?);
CoUninitialize();
} on FormatException {
print('Creates a Windows shortcut to a given file.\n');
print('Usage: shortcut [arguments]\n');
Expand Down
3 changes: 0 additions & 3 deletions example/speech.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,4 @@ void main() {
speechEngine.speak(pText, SPEAKFLAGS.SPF_IS_NOT_XML, nullptr);

free(pText);
speechEngine.release();

CoUninitialize();
}
10 changes: 0 additions & 10 deletions example/spellcheck.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,21 +102,11 @@ void main(List<String> args) {

break;
}

error.release();
}

errors.release();
free(textPtr);
spellChecker2.release();
spellChecker.release();
free(spellCheckerPtr);
}

free(supportedPtr);
free(languageTagPtr);

spellCheckerFactory.release();

CoUninitialize();
}
14 changes: 3 additions & 11 deletions example/wallpaper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,10 @@ void printBackgroundColor() {
void main() {
final hr = CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

if (FAILED(hr)) {
throw WindowsException(hr);
}
if (FAILED(hr)) throw WindowsException(hr);

wallpaper = DesktopWallpaper.createInstance();

try {
printWallpaper();
printBackgroundColor();
} finally {
free(wallpaper.ptr);
CoUninitialize();
}
printWallpaper();
printBackgroundColor();
}
9 changes: 0 additions & 9 deletions example/wasapi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ void main() {
0, // dataflow: rendering device
0, // role: system notification sound
ppDevice));
pDeviceEnumerator.release();

// Activate an IAudioClient interface for the output device.
final pDevice = IMMDevice(ppDevice.cast());
Expand Down Expand Up @@ -162,15 +161,7 @@ void main() {
// Clear up
free(pData);

pDevice.release();

pAudioClient.release();

pAudioRenderClient.release();

free(ppFormat);

// Uninitialize COM now that we're done with it.
CoUninitialize();
print('All done!');
}
11 changes: 0 additions & 11 deletions example/wmi_perf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,5 @@ void main() {

Sleep(1000); // Sleep for a second.
}

// Tidy up
pObj.release();
pAccess.release();

refresher.release();
pConfig.release();

pLoc.release();

CoUninitialize();
});
}
15 changes: 1 addition & 14 deletions example/wmi_wql.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import 'package:win32/win32.dart';
void main() {
// Initialize COM
var hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
throw WindowsException(hr);
}
if (FAILED(hr)) throw WindowsException(hr);

// Initialize security model
hr = CoInitializeSecurity(
Expand Down Expand Up @@ -77,7 +75,6 @@ void main() {
final exception = WindowsException(hr);
print(exception.toString());

pLoc.release();
CoUninitialize();
throw exception; // Program has failed.
}
Expand All @@ -102,8 +99,6 @@ void main() {
if (FAILED(hr)) {
final exception = WindowsException(hr);
print(exception.toString());
pSvc.release();
pLoc.release();
CoUninitialize();
throw exception; // Program has failed.
}
Expand All @@ -126,8 +121,6 @@ void main() {
final exception = WindowsException(hr);
print(exception.toString());

pSvc.release();
pLoc.release();
CoUninitialize();

throw exception;
Expand Down Expand Up @@ -158,15 +151,9 @@ void main() {
// Free BSTRs in the returned variants
VariantClear(vtProp);
free(vtProp);

clsObj.release();
}
print('$idx processes found.');
}

pSvc.release();
pLoc.release();
enumerator.release();

CoUninitialize();
}
Loading

0 comments on commit 3177e32

Please sign in to comment.