Skip to content

Latest commit

 

History

History
105 lines (84 loc) · 3.88 KB

File metadata and controls

105 lines (84 loc) · 3.88 KB

Interaction between Native and Managed Code for Structs

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.

C# Class

StructFB1.cs

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
        }
    }

Native Implementation

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
}

C# Function Block

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.

C# Function Block Example

    [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
        }
    }

Native Implementation

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
}

Memory Management Considerations

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.

Conclusion

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.