Skip to content

With this library, you can pin the managed object or allocate the memory easily. And you never forget releasing them.

License

Notifications You must be signed in to change notification settings

caphosra/MemoryLock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Memory Lock

Build Status nuget license Azure DevOps coverage GitHub stars

With this library, you can pin the managed object or allocate the memory easily.
And you never forget releasing them.

What is this ?

This is a wrapper library for :

  • System.Runtime.InteropServices.Marshal.AllocCoTaskMem
  • System.Runtime.InteropServices.Marshal.FreeCoTaskMem
  • System.Runtime.InteropServices.Marshal.AllocHGlobal
  • System.Runtime.InteropServices.Marshal.FreeHGlobal
  • System.Runtime.InteropServices.GCHandle.Alloc
  • System.Runtime.InteropServices.GCHandle.Free

They are used for allocating memories which is never collected or moved by GC (Garbage Collector).
People use these functions when they want to call native codes by P/Invoke because passing some parameters to C++ requires to prepare memories not moved.

Why wrap them ?

After allocating memories, we have to release them.
It is so bothered. Isn't it ?
That is why I implemented this library.
Using Memory Lock, you don't have to pay attention to memories whether they are released or not.

Watch the following code :

// using System.Runtime.InteropServices;

// Allocate memories by "size"
var pointer = Marshal.AllocCoTaskMem(size);

//
// Do something
//

// Release memories
Marshal.FreeCoTaskMem(pointer);

// "pointer" refers to nothing but the parameter alives.
// If you use "pointer" here, it will cause "undefined behaviour".

This is an ordinary code using Marshal.AllocCoTaskMem.
I think it has some problems :

  • When you forget to write Free(), It is too difficult to realize the mistake.
  • You can access the pointer which is released easily.

With Memory Lock, you can write the following code :

// using CapraLib.MemoryLock;

// Allocate memories
using(var allocated = new MemoryAllocater(out var pointer, size))
{
    // Do something
}

// You can't access to the pointer here !
// And they are already released !

This library solves the problems clearly !
In C# 8, you can use this with "using declarations".

Examples

Allocate 100 bytes

using(var allocated = new MemoryAllocater(out var pointer, 100))
{
    // Do something with "pointer" !
}

The following table will help you.

Allocate memory by ... Size Unmanaged object
CoTaskMemAllocater ✔️
HGlobalAllocater ✔️
CoTaskMemAllocater<T> ✔️
HGlobalAllocater<T> ✔️
GCAllocater<T> ✔️

Unmanaged object includes :

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool
  • User defined structs which has attribute, [StructLayout(LayoutKind.Sequential)]

Using struct

If you have a struct :

// StructLayout should be LayoutKind.Sequential
// when it is used in Memory Lock
[StructLayout(LayoutKind.Sequential)]
public struct Vector
{
    public float x;
    public float y;
    public float z;
}

And a native function :

// Assign values to vec
[DllImport("Some.dll")]
static extern void ChangeAll(IntPtr vec, float x, float y, float z);

On that time, you can write the following code :

// using CaprLib.MemoryLock;

var vec = new Vector();
vec.x = 10f;

using(var allocated = new MemoryAllocater<Vector>(out var pointer, vec))
{
    // Assign another values to the pointer
    ChangeAll(pointer, 15f, 19f, 23f);

    // Copy from unmanaged memory to managed.
    allocated.CopyTo(out vec);
}

Console.WriteLine($"vec.x = {vec.x}"); // vec.x = 15

Before releasing the memory, you can call void CopyTo(out T obj) to save changes to managed items.
This function is implemented in

  • MemoryAllocater<T>
  • CoTaskMemAllocater<T>
  • HGlobalAllocater<T>
  • GCAllocater<T>

Do so without CopyTo()

You can forget writing CopyTo().
If you want to save changes automatically, I recommend you to use MemoryLock.AsNative<T>(ref T, MemoryAllocationHandle).

This is a example for MemoryLock.AsNative<T> :

var result = new Vector();

MemoryLock.AsNative(ref result, (ptr) => 
{
    // Do something with pointer !
});

// "result" is now reflected the changes.

Author

capra314cabra