[Feature] Provide wrapper functions for the C# (.NET) world.

Issue #33 open
Daniel Prokhorov created an issue

For my purposes, I need a possibility to call the BLF parser/writer within the C# code. I’m not that much experienced in C++ or Glue Code development, however I’ve fiddled a little bit around and created the neccessary bindings. Please take a look at the zipped sources files for the wrapper functions.

In order to be able to call the C++ functions from the dll the following C# code will be needed for e.g. the parser:

void Main()
{
    string fileName = @"can_fd_write_test.blf";

    var parserPtr = CreateParser();
    var nrOfParsedMessages = Parse(parserPtr, fileName);
    nrOfParsedMessages.Dump();

    for (int i = 0; i < nrOfParsedMessages; i++)
    {
        var canMsg = GetMessageAt(parserPtr, i);
        var dataLen = GetDataLenFromDlc(canMsg.dlc);
        IntPtr canMsgPayload = GetPayload(parserPtr, i);

        byte[] fixedData = new byte[dataLen];

        Marshal.Copy(canMsgPayload, fixedData, 0, fixedData.Length);  

        var blfMsg = new BlfMessage(){BlfCanMessage = canMsg, Payload = fixedData};
        blfMsg.Dump();
    }

}

public struct BlfMessage
{
    public BlfCanMessage BlfCanMessage;
    public byte[] Payload;
}

private static int GetDataLenFromDlc(uint dlc)
{
    return dlc switch
    {
        9 => 12,
        10 => 16,
        11 => 20,
        12 => 24,
        13 => 32,
        14 => 48,
        15 => 64,
        _ => (int)dlc
    };
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BlfCanMessage
{
    public uint dlc;
    public uint canId;
    public ushort channel;
    public byte flags; // bit 0: TX
    public IntPtr payload;
}


[DllImport("BlfParser.dll", EntryPoint = "Parse", CharSet = CharSet.Ansi)]
static extern int Parse(IntPtr ptr, string filePath);

[DllImport("BlfParser.dll", EntryPoint = "GetMessage", CharSet = CharSet.Ansi)]
static extern BlfCanMessage GetMessageAt(IntPtr ptr, int pos);

[DllImport("BlfParser.dll", EntryPoint = "CreateParser", CharSet = CharSet.Ansi)]
static extern IntPtr CreateParser();

[DllImport("BlfParser.dll", EntryPoint = "GetPayload", CharSet = CharSet.Ansi)]
static extern IntPtr GetPayload(IntPtr ptr, int pos);

and for the writer:

using System.Runtime.InteropServices;

void Main()
{
    string fileName = @"test.blf";
    IntPtr blfWriterPtr = CreateWriter();
    OpenFile(blfWriterPtr, fileName);

    byte[] rawData1 = {0, 0, 0, 0, 70, 68, 0, 1, 1, 2, 3, 4};
    byte[] rawData2 = {0, 0, 0, 0, 70, 11, 0, 32, 1, 2, 3, 4};

    List<byte[]> rawDataList = new List<byte[]>();
    rawDataList.Add(rawData1);
    rawDataList.Add(rawData2);

    foreach(var rawData in rawDataList)
    {

        int size = Marshal.SizeOf(rawData[0]) * rawData.Length;
        IntPtr pnt = Marshal.AllocHGlobal(size);


        // Copy the array to unmanaged memory.
        Marshal.Copy(rawData, 0, pnt, rawData.Length);   

        var blfCanMsg = new BlfWriteCanMessageStruct()
        {
            channel = 1,
            canId = 1175,
            dlc = 9,
            flags = 0
        };

        WriteCanFdMsg(blfWriterPtr, blfCanMsg, pnt);

        Marshal.FreeHGlobal(pnt);
    }

    CloseFile(blfWriterPtr);
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BlfWriteCanMessageStruct
{
    public uint dlc;
    public uint canId;
    public ushort channel;
    public byte flags; // bit 0: TX
}


[DllImport("BlfParser.dll", EntryPoint = "CloseFile", CharSet = CharSet.Ansi)]
static extern void CloseFile(IntPtr writerIntPtr);

[DllImport("BlfParser.dll", EntryPoint = "WriteCanMsg", CharSet = CharSet.Ansi)]
static extern void WriteCanMsg(IntPtr writerIntPtr, BlfWriteCanMessageStruct blfCanMessage, IntPtr payload);

[DllImport("BlfParser.dll", EntryPoint = "CreateWriter", CharSet = CharSet.Ansi)]
static extern IntPtr CreateWriter();

[DllImport("BlfParser.dll", EntryPoint = "OpenFile", CharSet = CharSet.Ansi)]
static extern bool OpenFile(IntPtr blfWriterPtr, string filename);

[DllImport("BlfParser.dll", EntryPoint = "WriteCanFdMsg", CharSet = CharSet.Ansi)]
static extern void WriteCanFdMsg(IntPtr writerIntPtr, BlfWriteCanMessageStruct blfCanMessage, IntPtr payload);

Both C# snippets can be run within the LinqPad tool by providing the location of the dlls as an additional reference:

The attachment also contains the built dlls.

@Tobias Lorenz Are you interested in such bindings? The bindings that I’ve created are just a working sw-prototype, so there is definitely some refactoring needed 😉

Comments (5)

  1. Tobias Lorenz repo owner

    I have no C# experience, and also never developed glue code.

    But I appreciate the provided example code and if you agree, I would like to have this within src/Vector/BLF/docs/examples. I’ll check through it and probably add some documentation, where useful. Also there should be a note somewhere, that this is provided under public domain, so to say “license free”.

  2. Daniel Prokhorov reporter

    Yes, I fully agree. You can put it into the src/Vector/BLF/docs/examples directory.

    By the way, is it possible to use this library for commercial projects?

  3. Tobias Lorenz repo owner

    Yes, that’s exactly my business model. For use in commercial projects I can provide you with a commercial license. I propose you send me an e-mail with the name of your company and product in which you want to use the license, and I can send you a license contract proposal and offer.

  4. Log in to comment