Roman Starkov
Roman Starkov

Reputation: 61502

Can C# structs coming from the unmanaged world be "live"-updating?

Suppose I get an IntPtr pointer to a struct from an unmanaged library. Is there any way, in C#, to obtain a "live" struct from this pointer, so that if I make a call that modifies the unmanaged struct, my "live" struct reflects this immediately?

I believe the standard approach is to construct a copy of the data using marshalling, which can't be "live" like this for various reasons (struct layout, data type compatibility, not residing in the .NET managed memory). But I couldn't find any explicit confirmation that "live" structs are impossible in C# though. Are they?

What's the closest I can get to such "live" structs without going to C++/CLI?

Upvotes: 4

Views: 374

Answers (2)

Kit
Kit

Reputation: 21719

Try using the UnmanagedMemoryStream:

This class supports access to unmanaged memory using the existing stream-based model and does not require that the contents in the unmanaged memory be copied to the heap.

This means you will be seeking/reading/resetting the stream, but this avoids the marshalling. It's still not live in the sense you'd want to probably want to wrap these accesses in .NET properties.

Another alternative: maybe you could use System.Buffer, after getting the unmanaged pointer. You may need to do some clever casting.

Upvotes: 3

KeithS
KeithS

Reputation: 71573

Technically, you CAN set up a structure whose data is "live" to changes made elsewhere. However, you want to think VERY carefully about whether you SHOULD.

By its very definition in C#, a struct is a "value type". That means that one instance is one value, like "5", and any change to that value conceptually results in a new value. 5+1==6; that doesn't mean that 5 "becomes" 6 when you add 1, it means that two values 5 and 1, when added, equal 6.

Value types in programming also have another idiosyncrasy with reference types; they are passed "by value", meaning that they are considered cheap enough to "clone" when a value is passed as a parameter. Any change that could be made to the variable's value (or child properties) while in the method is discarded when the call is complete, becauwse all of the changes were made to a new copy of the struct on the top level of the stack, instead of a reference to the original object residing lower in the stack. You must explicitly override this behavior by using the ref or out keywords, in effect specifically stating that the original value SHOULD change based on what happens in the method.

Most objects implemented as structs force you to deal with them according to these rules by being immutable; once you've created one, you cannot set its fields/properties directly. You must instead call various methods on that struct which will result in the creation of a new struct.

If you wanted to create a class that would reflect the most current data coming from unmanaged land, first off I would make it a "class", so that there is no confusion about the behavior of the object when you pass it or attempt to change its members. Then, you would basically create a "wrapper" that used Kit's aforementioned UnmanagedMemoryStream to get/set values that you exposed as properties. That would give you a "reactive" object that could be polled to get whatever the unmanaged code had most recently set, and also to write out new values to the correct places in memory. Be VERY careful; this code will not be "safe" (especially if you write back out to it), and hooks into unmanaged code via pointers is one of the few places in .NET where you can intentionally crash not just your program and the unmanaged C++ program, but the entire machine.

Upvotes: 1

Related Questions