-2

I'm currently creating a Windows Form application in C#, where different .dll files written in C with their own structs and functions need to be called in the application at runtime through a file dialog to obtain their path. To my understanding, this means that I cannot use [DllImport("filename")], since the path values are not determined at compile time. I'm at least given the .h files, and also a guarantee that the exported functions will have the same name.

I've scoured through a lot of different posts online, and most of the solutions pointed eventually towards PInvoke, Reflection and/or the InteropServices, but with no clear examples on what to do.

Since each such dll defines their own structs, I'd like to be able to: 1. Use the struct definition from the dll itself to avoid redefining everything in C#, 2. Use the functions at different points in the application after loading the dll.

Admittedly, I am very new at C#, so what I am trying to do above might not even be possible. But I'm at a loss right now, as I cannot find where to even begin start learning in order to address this problem. Any help would be greatly appreciated.

Here's the code for reference:

One of the dlls' header files classifier.h:

#ifndef SRC_CLASSIFIER_H_
#define SRC_CLASSIFIER_H_

#include "vector.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BUILD_DYNAMIC_LIB
#ifdef _WIN32
#define _EXPORT __declspec(dllexport)
#else
#define _EXPORT __attribute__((visibility("default")))
#endif
#else
#define _EXPORT
#endif

/** Error codes */
#define CLASSIFIER_OK 0x00
#define CLASSIFIER_INIT_MISSING 0x01
#define CLASIFIER_NO_SAMPLES_PROVIDED 0x02
#define CLASSIFIER_PARAM_OUT_OF_RANGE 0x03
#define CLASSIFIER_DECISION_BUFFER_LEN 20
#define CLASSIFIER_MAX_BURST_LEN 32

typedef struct {
    uint8_t sensitivity;
    uint8_t noTruckRecognitionLimit;
    uint8_t noTruckRecognitionHyst;
    uint16_t intensityLimit[CLASSIFIER_MAX_BURST_LEN];
} classifier_Parameter_t;

typedef enum { UNDEFINED = 0x00, TRUCK = 0x01, NO_TRUCK = 0x02 } class_t;

typedef struct {
     class_t classifiedAs;
} classifier_Result_t;

typedef struct {
    uint8_t noOfSamples;
    vec3d_t accSamples[CLASSIFIER_MAX_BURST_LEN];
} classifier_Input_t;

_EXPORT uint8_t classifier_api_Init(classifier_Parameter_t *para);

_EXPORT uint8_t classifier_api_Execute(classifier_Input_t *input,
                                       classifier_Result_t *result);

_EXPORT uint8_t classifier_api_Reset(void);

#ifdef __cplusplus
}
#endif

#endif /* SRC_CLASSIFIER_H_ */

And the C# file MainForm.cs:

using System;
...

namespace ClassifierEval
{

public partial class MainForm : Form
    {

        public MainForm()
        {
            InitializeComponent();
        }


        private void btnLoadDll_Click(object sender, EventArgs e)
        {
            openFileDialogLoadDll.ShowDialog();

        }

        private void openFileDialogLoadDll_FileOk(object sender, CancelEventArgs e)
        {
            lblLoadedDll.Text = openFileDialogLoadDll.FileName;
            ///Loading the DLL here?
        }
...
}
  • *Use the struct definition from the dll* from native dll? there is no struct definition there ... – Selvin Nov 27 '19 at 22:38
  • I meant the structs that are shown in the .h files, like classifier_Parameter_t – tenfoldpaper Nov 27 '19 at 22:58
  • 1
    then 1. translate native structures to C# 2. translate native funtions to delegates 3. use `LoadLibrary`, `GetProcAddress`, `FreeLibrary` from winapi 4. use `Marshal.GetDelegateForFunctionPointer<>` to get delegate from function pointer – Selvin Nov 27 '19 at 23:04

1 Answers1

0

In answer to your first question, I am not aware of any way to obtain the struct definition straight off of a dll. (Try LoadLibrary, GetProcAddres) with the possible exception of one built for debugging. If you know the structure you will use in C then it should be pretty straight forward to define the structure for use in C# - there are plenty of examples in sites like pinvoke.net.

For instance this function declaration in C:

__declspec(dllexport) int scrypt(const uint8_t * passwd, size_t passwdlen, 
       const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, 
       uint32_t p,uint8_t * buf, size_t buflen);

can be transformed into:

[DllImport("SCrypt.dll", CharSet = CharSet.Unicode, 
    CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int scrypt(byte[] passwd, uint passwdlen, byte[] salt, uint saltlen,
    ulong N, uint r, uint p, byte[] buf, uint buflen);

Secondly, you can try copying in your program a dll to a known location (executing directory for instance) before using it. Compiling the code with the dlls in place will then work. This technique is of course assuming you know the dlls you are going to reference beforehand.

Mark McWhirter
  • 1,168
  • 3
  • 11
  • 28
  • @Selvin thanks for the tips on LoadLibrary, GetProcAddress, and FreeLibrary – Mark McWhirter Nov 27 '19 at 23:27
  • in fact it should work with [`string passwd`, `string salt` and `StringBuilder buf`](https://stackoverflow.com/questions/53198213/calling-c-function-with-lpstr-return-value-from-c-sharp/53201035#53201035) – Selvin Nov 27 '19 at 23:27
  • @Selvin - Yes there are usually a few ways of representing it - whatever will be the most elegant for you. – Mark McWhirter Nov 27 '19 at 23:31
  • Thanks for the info about the other libraries. I will have a look into it. But the problem is precisely that I am supposed to assume that I do not know how many DLLs there actually will be, and how they will be structured. It seems to be rather tricky to achieve with C-based DLLs, so I may have to propose a change to this project... – tenfoldpaper Nov 28 '19 at 15:28
  • Please vote if this was a proper response for your problem :) – Mark McWhirter Nov 29 '19 at 19:21