This documentation delves into the interactions between managed and native environments specifically regarding the use of structs. While several principles from previous sections apply, particular attention must be paid to how structs are defined and manipulated across these boundaries.
Structs in C# must be handled with care when interfacing with native code. They must be declared as [InOut] when passed to ensure bi-directional manipulation. Structs can also be passed by reference to allow native methods to modify them directly.
...
[InOut]
public MyStruct* IN_STRUCT;
[InOut]
public MyStruct* OUT_STRUCT;
...
[Native]
internal static class StructFB1NativeMethods
{
public static void UpdateStruct(ref MyStruct outStruct, ref MyStruct inStruct)
{
// Implementation in native code
}
}StructFB1NativeMethods-cli.cpp
In native code, structs are treated as a contiguous block of memory. Using the __PInvoke__ prefix allows the native implementation to access the managed struct data passed from C#.
void __PInvoke__ DocuSharedNativeLibrary::StructFB1NativeMethods::UpdateStruct(DocuSharedNativeLibrary::MyStruct* pOut, DocuSharedNativeLibrary::MyStruct* pIn)
{
if (pIn == 0 || pOut == 0)
{
return;
}
// Direct manipulation of struct data
pOut->FieldA = pIn->FieldA; // Copy data from input struct to output struct
pOut->FieldB += 1; // Modify a field in the output struct
}StructFB2.cs StructFB2-cli.cpp
The same principles apply to function blocks that work with structs. The __Process method can use the defined native methods to carry out operations on the structs.
[FunctionBlock]
[Native]
public class StructFB2
{
[Input]
public MyStruct IN_STRUCT;
[Output]
public MyStruct OUT_STRUCT;
[Initialization]
public void __Init()
{
// Initialization logic if necessary
}
[Execution]
public void __Process()
{
// Implementation that calls native code to manipulate the struct
}
}In the native implementation for StructFB2, the __Process method effectively utilizes the pointer variables to access and manipulate the struct details directly.
void __PInvoke__ DocuSharedNativeLibrary::StructFB2::__Process()
{
if (IN_STRUCT == 0 || OUT_STRUCT == 0)
{
return;
}
// Pointer to the struct data
MyStruct* pIn = &IN_STRUCT; // Assume a standard way to access the struct
MyStruct* pOut = &OUT_STRUCT;
// Logic to process the struct
pOut->FieldA = pIn->FieldA * 2; // Example of modifying struct field
pOut->FieldB = pIn->FieldB + 5; // Another modification
}When working with structs, it is crucial to consider how memory is allocated and managed. Native code assumes that structs are contiguous blocks of memory, which means any field modifications should respect the layout defined in the managed struct. Proper alignments and sizes need to be ensured to avoid runtime errors or data corruption.
In summary, working with structs in the interaction between managed and native code requires careful management of pointers and attention to memory layout. By utilizing [InOut] attributes and ensuring direct access through pointers, efficient manipulation of struct data can be achieved without unnecessary copies. This approach leverages the strengths of both managed and native environments, facilitating complex data operations while maintaining performance.