C 代码的托管 C++ 包装器 - 相同的函数名称问题

Managed C++ Wrapper for C Code - Identical function names issues

我的任务是为要在 C# 中使用的 C 库创建包装器。我无法控制 C 库的源代码,只能访问其头文件和静态库文件 (.lib)。

我遵循了几个教程并在创建一个使用 CLI/C++ 包装并在 C# 中使用的非托管 C++ class 时得到了这个工作。没问题。我知道我面临的问题是,因为 C 不使用命名空间,所以我很难弄清楚如何告诉编译器我想从 .lib 文件本身调用函数,而不是我的同名包装函数。如果有助于理解,我的头文件仅包含函数定义、typedef(结构、枚举),但没有 classes(甚至不确定 C 头文件是否通常如此?)。

我做了什么:

  1. 创建了一个 VS C++ CLR Class 库 (.NET Framework) 项目
  2. 将我的 .lib 文件链接到 所有配置 in linker->inputs 作为附加依赖项。
  3. 创建了两个文件:Wrapper.hWrapper.cpp.
  4. #included Wrapper.h文件中我的.lib对应的头文件

这是我有点困惑的地方,主要是因为教程都涵盖了如何 link C++ 库而不是 C 库。区别在于 C 中缺少名称空间,并且(在我的例子中)缺少 class.

不确定性:

  1. 我不知道我的 Wrapper.h 中是否需要 ref class 分析{} 或不是。如果我想静态使用所有函数,我想我会这样做,但是由于 C 库中没有相应的 class,我可以随便命名吗?
  2. 为了正确地 link 我的头文件中的 C 库 (Wrapper.h),我需要使用与原始头文件。我是否重复使用“extern”和此类关键字?我可以随意添加 static 和可访问性修饰符吗?
  3. 此时我想“实现”Wrapper.cpp文件中的功能。这里出现了我关于相同函数名称的问题。我要实现的函数在原始头文件中的名称与它在 Wrapper 头文件中的名称相同(显然,否则它不会工作,对吗?)。包装头文件包含一个命名空间和一个 class(现在 class 名称是 Analytics)所以我可以将函数声明为 Analytics::void SanSetApplicationContext(){...},但是由于原始 C 库头文件不包含 classes 或名称空间,因此我无法调用该函数并区分它们。 C++ 编译器总是更喜欢本地定义,而我一直坚持使用一个永远调用自身的函数。我希望你明白我的意思。

我可能做错了什么 and/or 误解了什么,但是你们建议我如何处理这个问题?我将在下面附加我的文件内容。到目前为止,在我只尝试过实现一个函数的文件中。 代码应该已修复并有望正常工作

Wrapper.h

#pragma once

using namespace System;

namespace Wrapper {
    public ref class Analytics
    {
    public:
        static void SanSetApplicationContext(String^ ctx);
    };
}

Wrapper.cpp

#include "pch.h"
#include "Wrapper.h"
#include "../SWA_lib/Analytics.h"

namespace Wrapper {
    void Analytics::SanSetApplicationContext(String^ ctx) {
        //Convert String^ to const char*
        msclr::interop::marshal_context mCtx;
        const char* convertedStr = mCtx.marshal_as<const char*>(ctx);

        ::SanSetApplicationContext(convertedStr);
    }
}

Analytics.h

...
extern void SanSetApplicationContext(const char *ctx);
...

更新 更新了代码以反映我根据您的评论所做的更改。谢谢!

更新 2 @Bodo 让我解释我的另一个问题,即我将如何处理 return opaque 处理的包装函数。使用的示例如下: typedef struct SanEvent_s *SanEvent; 可以在我的 C 头文件中找到。所以基本上,这个库提供了一个 API。 API 执行总是以调用 Initialize(); 函数开始,并以调用 Terminate(); 函数结束。我以前没有使用此 API 的经验,但是根据文档,我假设调用 Terminate() 时所有对象、引用以及不属于 freed/destroyed 的内容,如 none他们的例子展示了破坏物体。 现在,创建 SanEvent 是这样完成的(根据文档):

SanEvent event;
event = SanNewEvent("The Main Event");

这个不透明的句柄在 C# 端被认为是受保护的,所以我似乎没有办法 return 一直使用它。我的想法是将事件保存在某种集合中,在可以使用类型的 C++/CLI 包装器中,并且只有 return 事件的索引到 C#(偶数在列表中的索引或者其他的东西)。我不确定这是个好主意,但这就是我的计划。代表 SanEvent 的东西需要被 returned 到 C#,因为我需要能够在将来引用该事件,以便通过其他函数向事件添加额外信息。这个想法当然需要在 C++ 端定制“辅助函数”,它在 C# 和 C 之间进行调解。如果信息含糊不清,我很抱歉,但到目前为止,我自己真的没有太多可做的。

在函数调用的开头添加“::”告诉编译器使用在全局命名空间而不是本地命名空间中找到的函数 namespace/class。所以:

namespace Wrapper {
    void Analytics::SanSetApplicationContext(const char *ctx) {
        ::SanSetApplicationContext(ctx);
    }
}

应该正确调用c版本