【问题标题】:Dynamically calling different C DLLs in C#在 C# 中动态调用不同的 C DLL
【发布时间】:2019-11-27 22:36:57
【问题描述】:

我目前正在使用 C# 创建一个 Windows 窗体应用程序,其中需要在运行时通过文件对话框在应用程序中调用用 C 编写的具有自己的结构和函数的不同 .dll 文件以获取它们的路径。据我了解,这意味着我不能使用[DllImport("filename")],因为路径值不是在编译时确定的。我至少得到了 .h 文件,并且还保证导出的函数将具有相同的名称。

我在网上浏览了很多不同的帖子,大多数解决方案最终都指向了 PInvoke、Reflection 和/或 InteropServices,但没有明确的示例说明该怎么做。

由于每个这样的 dll 都定义了自己的结构,我希望能够: 1.使用dll本身的struct定义,避免在C#中重新定义一切, 2. 加载dll后,在应用程序的不同点使用函数。

诚然,我是 C# 的新手,所以我在上面尝试做的事情可能甚至不可能。但我现在不知所措,因为我什至找不到从哪里开始学习以解决这个问题。任何帮助将不胜感激。

以下是参考代码:

其中一个dll的头文件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_ */

还有 C# 文件 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?
        }
...
}

【问题讨论】:

  • 使用来自原生 dll 的 dll 中的结构定义?那里没有结构定义...
  • 我的意思是 .h 文件中显示的结构,例如 classifier_Parameter_t
  • then 1. 将原生结构转换为 C# 2. 将原生函数转换为委托 3. 使用来自 winapi 的 LoadLibrary, GetProcAddress, FreeLibrary 4. 使用 Marshal.GetDelegateForFunctionPointer<> 从函数中获取委托指针

标签: c# c winforms-interop


【解决方案1】:

在回答您的第一个问题时,我不知道有任何方法可以直接从 dll 中获取结构定义。 (尝试 LoadLibrary、GetProcAddres),但为调试而构建的可能例外。 如果您知道将在 C 中使用的结构,那么它应该非常简单 定义用于 C# 的结构 - 在 pinvoke.net 等网站上有很多示例。

例如 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);

可以转化为:

[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);

其次,您可以在使用之前尝试将程序中的 dll 复制到已知位置(例如执行目录)。使用 dll 编译代码 然后将工作。这种技术当然是假设您知道要使用的 dll 提前参考。

【讨论】:

  • @Selvin 感谢有关 LoadLibrary、GetProcAddress 和 FreeLibrary 的提示
  • @Selvin - 是的,通常有几种表示它的方式 - 任何对你来说最优雅的方式。
  • 感谢您提供有关其他库的信息。我会调查一下。但问题正是我应该假设我不知道实际上会有多少个 DLL,以及它们将如何构造。使用基于 C 的 DLL 实现似乎相当棘手,所以我可能不得不提议对这个项目进行更改...
  • 如果这是对您的问题的正确回应,请投票 :)
猜你喜欢
  • 1970-01-01
  • 2019-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-07
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多