-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ffi] Nested structs #37271
Comments
Hey @PixelToast, at the moment we only support pointers to structs inside structs, see https://github.com/dart-lang/sdk/blob/master/samples/ffi/coordinate.dart#L18-L19. We do want to support nested (by value) structs in the future, but we have to change the structs API first, see #37229. |
It's possible to implement nested structs without by-value structs, for example: struct Foo {
uint32_t foo;
};
struct Bar {
uint32_t bar;
struct Foo foo;
}; in dart: @struct class Foo extends Pointer<Void> {
@Uint32() int foo;
}
@struct class Bar extends Pointer<Void> {
@Uint32() int bar;
@Uint32() int _padding;
Foo get foo => offsetBy(4).cast<Foo>();
set foo(Foo other) {
foo.foo = other.foo;
}
} Works fine, but requires you to know the alignment, size, and offset of |
Yes indeed, that is the workaround for now. |
This is a pretty common pattern in Win32 APIs, for example |
Yeah, @mkustermann suggested last week that this might be the next thing to work on (with more urgency than finalizers). We can take a look at this next. |
I think we should prioritize finalizers more, because nested structs can be worked around in a way that finalizers cannot. |
Hey @PixelToast I'm trying to use SendInput function which uses INPUT struct. I've created something like what you suggested: class KeyboardInput extends Struct {
@Uint32() int type;
@Uint16() int virtualCode;
@Uint16() int scanCode;
@Uint32() int flags;
@Uint32() int time;
Pointer dwExtraInfo;
@Uint32() int padding;
} Unfortunately this does not work with the I'd appreciate any help with this. I also tried just allocating |
We conflated size and alignment in the API (back when we did not have structs at all). I've filed an issue to address this.
That is quite curious. Do you have the source code for the reproduction somewhere? |
I actually do. I created custom DLL with custom implementation of the What I discovered was that actually the struct should use more 64-bit values: class KeyboardInput extends Struct {
@Uint64() int type;
@Uint16() int virtualCode;
@Uint16() int scanCode;
@Uint64() int flags;
@Uint64() int time;
Pointer dwExtraInfo;
} This SOMEWHAT works, but not really. Take a look at the code and the comments: import 'dart:ffi';
import 'package:ffi/ffi.dart';
const INPUT_KEYBOARD = 1;
const KEYEVENTF_UNICODE = 0x0004;
DynamicLibrary _user32 = DynamicLibrary.open("user32.dll");
DynamicLibrary _kernel32 = DynamicLibrary.open("kernel32.dll");
typedef _SendInput_C = Uint32 Function(Uint32 cInputs, Pointer pInputs, Uint32 cbSize);
typedef _SendInput_Dart = int Function(int cInputs, Pointer pInputs, int cbSize);
var SendInput = _user32.lookupFunction<_SendInput_C, _SendInput_Dart>("SendInput");
typedef _GetLastError_C = Uint32 Function();
typedef _GetLastError_Dart = int Function();
var GetLastError = _kernel32.lookupFunction<_GetLastError_C, _GetLastError_Dart>("GetLastError");
class KeyboardInput extends Struct {
@Uint64() int type;
@Uint16() int virtualCode;
@Uint16() int scanCode;
@Uint64() int flags;
@Uint64() int time;
Pointer dwExtraInfo;
factory KeyboardInput.allocate({int vkey=0, int scan=0, int flags=0}) =>
allocate<KeyboardInput>().ref
..type = INPUT_KEYBOARD
..virtualCode = vkey
..scanCode = scan
..flags = flags
..time = 0
..dwExtraInfo = nullptr
;
}
void main() {
var zKeyVirtualCoe = 0x5a;
var event = KeyboardInput.allocate(vkey: zKeyVirtualCoe);
// this loop will send less than 10 'z' characters (sometimes even zero)
for(var i=0; i < 10; i++) {
print('Sending $i-th virtual character');
var written = SendInput(1, event.addressOf, 40);
print('Written $written');
print('Error ${GetLastError()}');
}
// this does not work at all (despite the fact that it says 'written 1' - so it sent something)
var unicode = KeyboardInput.allocate(scan: 'x'.codeUnitAt(0), flags: KEYEVENTF_UNICODE);
for(var i=0; i < 10; i++) {
print('Sending $i-th unicode character');
var written = SendInput(1, unicode.addressOf, 40); // note the size of 40 instead of 28
print('Written $written');
print('Error ${GetLastError()}');
}
print('Struct size: ${sizeOf<KeyboardInput>()}');
} I run this program 4 times and this are the results: zzzzzzzzz // first time
zzzzzzzz // second time
// third time (no output at all!)
zzzzzzz // fourth time What I don't understand now is:
|
@marad I've setup a WindowsHook and ran your code a couple of times and the result was indeed quite incosistent: 1:
2:
3:
4:
|
I'm starting to suspect that there is something funky going on with 32bit vs 64bit. I've created a simple DLL library that contains only function I originally intended to use it with my dart code to see what's going on, but dart can only load the 64bit version. When I try to load the 32bit version it crashes with:
This error means that it cannot load the 32bit version and I get it, but... I've also created a simple C++ code to test it with original #include <windows.h>
#include <iostream>
using namespace std;
typedef UINT (__cdecl *SENDINPUTPROC)(UINT cInputs, LPINPUT pInputs, int cbSize);
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nCmdShow)
{
//HINSTANCE user32 = LoadLibrary("user32.dll");
HINSTANCE user32 = LoadLibrary("TestDLL32.dll");
SENDINPUTPROC MySendInput;
if (user32 != NULL) {
cout << "Library loaded!" << endl;
INPUT input;
input.type = 1;
input.ki.wVk = 0x5a;
input.ki.wScan = 0;
input.ki.dwExtraInfo = 0;
input.ki.dwFlags = 0;
input.ki.time = 0;
MySendInput = (SENDINPUTPROC) GetProcAddress(user32, "mySendInput");
MySendInput(1, &input, sizeof(input));
} else {
cout << "Library not loaded!" << endl;
return 1;
}
return 0;
} And this loaded my 32bit DLL just fine, but couldn't load the 64bit one. The strange thing is that when I simply load the |
I've managed to get SendInput to work consistently with:
|
Thanks @janzka -- this one is non-intuitive. I've added SendInput to my Win32 wrapper package, which folk can find here: https://pub.dev/packages/win32. There's also a sample in the |
Thank you @janzka! You made my day 😀 |
For completeness I'll include also the mouse input struct that I manged to get working (also non-intuitive):
|
Many structs end up empty, because dart:ffi does not yet support nested structs: dart-lang/sdk#37271 ``` Running in Directory: '/home/jpnurmi/Projects/dart_assimp' [WARNING]: Prefer adding Key 'description' to your config. Input Headers: [/usr/include/assimp/cexport.h, /usr/include/assimp/cfileio.h, /usr/include/assimp/cimport.h, /usr/include/assimp/postprocess.h, /usr/include/assimp/scene.h, /usr/include/assimp/version.h] [WARNING]: Removed All Struct Members from aiRay(aiRay), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiExportDataBlob(aiExportDataBlob), Nested Structures not supported. [WARNING]: Skipped Function 'aiGetPredefinedLogStream', struct pass/return by value not supported. [WARNING]: Removed All Struct Members from aiTexture(aiTexture), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiAABB(aiAABB), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiBone(aiBone), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiAnimMesh(aiAnimMesh), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMesh(aiMesh), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiLight(aiLight), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiCamera(aiCamera), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiUVTransform(aiUVTransform), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMaterialProperty(aiMaterialProperty), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiVectorKey(aiVectorKey), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiQuatKey(aiQuatKey), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiNodeAnim(aiNodeAnim), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMeshAnim(aiMeshAnim), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMeshMorphAnim(aiMeshMorphAnim), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiAnimation(aiAnimation), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiNode(aiNode), Nested Structures not supported. Finished, Bindings generated in /home/jpnurmi/Projects/dart_assimp/lib/src/bindings.dart ```
Many structs end up empty, because dart:ffi does not yet support nested structs: dart-lang/sdk#37271 ``` Running in Directory: '/home/jpnurmi/Projects/dart_assimp' [WARNING]: Prefer adding Key 'description' to your config. Input Headers: [/usr/include/assimp/cexport.h, /usr/include/assimp/cfileio.h, /usr/include/assimp/cimport.h, /usr/include/assimp/postprocess.h, /usr/include/assimp/scene.h, /usr/include/assimp/version.h] [WARNING]: Removed All Struct Members from aiRay(aiRay), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiExportDataBlob(aiExportDataBlob), Nested Structures not supported. [WARNING]: Skipped Function 'aiGetPredefinedLogStream', struct pass/return by value not supported. [WARNING]: Removed All Struct Members from aiTexture(aiTexture), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiAABB(aiAABB), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiBone(aiBone), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiAnimMesh(aiAnimMesh), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMesh(aiMesh), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiLight(aiLight), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiCamera(aiCamera), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiUVTransform(aiUVTransform), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMaterialProperty(aiMaterialProperty), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiVectorKey(aiVectorKey), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiQuatKey(aiQuatKey), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiNodeAnim(aiNodeAnim), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMeshAnim(aiMeshAnim), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiMeshMorphAnim(aiMeshMorphAnim), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiAnimation(aiAnimation), Nested Structures not supported. [WARNING]: Removed All Struct Members from aiNode(aiNode), Nested Structures not supported. Finished, Bindings generated in /home/jpnurmi/Projects/dart_assimp/lib/src/bindings.dart ```
Any updates on this? |
We're currently working on #36730, which is a prerequisite for this. |
Right now it isn't possible to represent nested structs using ffi, for example the following C data structure:
In Dart:
Does not compile.
The text was updated successfully, but these errors were encountered: