【问题标题】:Using C++/CLI Utilities in .NET:在 .NET 中使用 C++/CLI 实用程序:
【发布时间】:2013-03-09 13:23:18
【问题描述】:

我一直在开发一些 DLL 实用程序项目,以避免在其他项目中重复代码,以及我尚未尝试过的功能、算法和测试。其中一个项目是 C++/CLI,我仍在学习的语言,所以这个问题可能听起来很愚蠢。因为我有 C++/CLI、F# 和 C# 中的库项目,所以我使用 C# 控制台应用程序来测试它们。它不适用于 C++/CLI 项目,因此我创建了一个 C++/CLI 控制台测试项目。它从来没有用过,当我更改原始 DLL C++ 的名称时,引用没有更新。当我(最终)发现问题时,我更改了 .vcxproj 文件,使 using 指令成为可能,作为一种方法,但不适用于模板类 Apont<typename T>,它是某种内部指针,但与.NET 类型 System::IntPtr,使用 T* 类型的值而不是 void*

我还发现(从本网站的一篇文章中)我必须在项目内部使用我想在外部使用的东西,否则这些东西想要在元数据中发出。因此,为此目的,我在静态实用程序中有一个无用的静态方法:

static void Funcionalidades()
{
    int i = 10;

    Apont<int> a2 = Apont<int>(i);            // stack
    Apont<int> ^a3 = gcnew Apont<int>(i);     // heap CLR

}

然而,它不起作用。这是我在 C++/CLI 测试项目中的主要方法:

int main(array<System::String ^> ^args)
{
    int y(10);
    Apont<int> a = Apont<int>(y);

    Console::ReadKey();
    return 0;
}

以下是错误(我知道它可以用智能感知错误编译,但我还是会显示它们):

error C2065: 'Apont' : undeclared identifier
error C2062: type 'int' unexpected
IntelliSense: identifier "Apont" is undefined
IntelliSense: type name is not allowed
IntelliSense: expected an expression

为什么会出现这些错误?我该如何纠正它们?

如果有任何答复或回复,我将不胜感激。

编辑(澄清):

  • 这些错误不会发生在Funcionalidades 方法上,即在 DLL 项目中,而是在 main 方法上,即在 DLL 之外,在测试项目中。
  • 我正在头文件中写入所有内容;我的意思是不是每个头文件都有各自的 .cpp 文件,尽管所有头文件都包含在至少一个 .cpp 文件中。
  • 更多关于Apont:
    • Apont 是一个模板(因为在内部使用了 T* 并且“不允许对泛型类型参数进行间接引用”)。
    • Apont 有一个复制构造函数,所以 Apont&lt;int&gt; a = Apont&lt;int&gt;(someInt) 应该可以工作;
    • Apont&lt;int&gt; a(someInt) 不起作用;
    • Apont 是某种内部指针;而且我没有发布整个代码,因为它不相关,我必须翻译变量的名称,它可能有我可以轻松修复的错误,但这只会分散你的注意力。

NTH EDIT('n' 是一个我不知道的数字):

Apont的代码你抱怨了这么久:

    template<typename T> public ref class Apont sealed : public IDisposable
    {
        bool eliminado;
        T *pointer;

        /*void Dispose(bool tudo)
        {
            if (!eliminado)
            {
                if (tudo)
                {
                    ~Apont();
                }
                else
                {
                    !Apont();
                }
            }
        }*/
        !Apont() // finalizador: limpa os recursos "unmanaged"
        {
            delete pointer;
            pointer = nullptr;
            eliminado = true;
        }

    public:
        Apont(T &valor)
        {
            pointer = &valor;
            eliminado = false;
            ErroSeNulo = false;
            ErroSeEliminado = true;
        }
        Apont(T &valor, bool erroSeEliminado, bool erroSeNulo)
        {
            pointer = &valor;
            eliminado = false;
            ErroSeEliminado = erroSeEliminado;
            ErroSeNulo = erroSeNulo;
        }
        Apont(Apont<T> %outroApont)
        {
            this->pointer = &outroApont
        }

        property bool ErroSeEliminado;
        property bool ErroSeNulo;
        property T Valor
        {
            T get()
            {
                if (pointer != nullptr)             
                    return *pointer;
                else if (eliminado && ErroSeEliminado)
                    throw gcnew ObjectDisposedException("O objeto já foi pelo menos parcialmente eliminadao.");
                else if (ErroSeNulo)
                    throw gcnew NullReferenceException();
                else
                    return 0;
            }
        }

        /*
        Apont operator ~(/*T valor* /)
        {
            // este operador tem de ser declarado fora desta classe 
        }*/
        T operator !(/*Apont apont*/)
        {
            return Valor;
        }
        void operator =(Apont<T> outroApont)
        {
            pointer = outroApont;
            ErroSeEliminado = outroApont.ErroSeEliminado;
            ErroSeNulo = outroApont.ErroSeNulo;             
        }
        template<typename U> void operator =(Apont<U> outroApont)
        {
            pointer = safe_cast<T>(outroApont.pointer);
            ErroSeEliminado = safe_cast<T>(outroApont.ErroSeEliminado);
            ErroSeNulo = safe_cast<T>(outroApont.ErroSeNulo);
        }
        /*
        void operator =(T *&outroPointer)
        {
            pointer = outroPointer;
        }
        template<typename U> void operator =(U *&outroPointer)
        {
            pointer = safe_cast<T>(outroPointer);
        }*/
        void operator =(T *outroPointer)
        {
            pointer = outroPointer;
        }
        template<typename U> void operator =(U *outroPointer)
        {
            pointer = safe_cast<T>(outroPointer);
        }


        ~Apont() // destruidor: limpa todos os recursos
        {               
            this->!Apont();
        }

        // Error C2605: 'Dispose': this method is reserved within a managed class
        // O código será gerado automaticamente a partir do finalizador e do destrutor
    };

    template<typename T> Apont<T> operator ~(T &valor)
    {
        return gcnew Apont<T>(valor);
    }

【问题讨论】:

  • 您可能应该创建一个short, self-contained, correct example。您的问题中缺少或未说明的内容太多,这是回答所必需的。如果您按照上面链接中的建议进行操作,我相信您会收到更多回复。
  • 当编译成 dll 时,函数名称/签名可能会在 c++ 中被破坏,您需要找到它们在 dll 中的内容并创建指向它们的链接,例如通过以下方式从 dll 中发现内部名称使用link /dump /exports mydll.dll。使用我之前提到的链接命令检查您希望出现的值和函数是否实际存在于您的 dll 中。让我知道你的进展情况,我会尽力帮助你解决这个问题。
  • 嗨,是的,你从命令行使用它。 id 建议导航到 dll 所在的目录并仅使用链接 /dump/exports mydll.dll 而不必在命令后键入完整路径:)
  • 我明白了,刚刚注意到我的一些 dll 也在做同样的事情,并且不得不关闭调试信息`属性,链接器,调试,生成调试信息 = 否`,所以名称没有被破坏。并且正确可见。在旁注中,dumpbin.exe 做了类似的工作(VS 命令行),或者dependency walker 有一个很好的 gui 来实现同样的效果。我正在研究这个隐藏的函数名称问题,因为我自己的一些 dll 也有同样的问题,但是这些函数在我导入 dll 时可以使用......非常奇怪。
  • Utilidades.ComNativos 是我查看 dll 的命名空间,Apont 根本没有暴露,但是 UtilCMM 是,也许看看这两个类有什么区别? (起点可能是:apont 是密封类,utilscmm 是静态类)

标签: .net pointers c++-cli wrapper


【解决方案1】:

您的错误通常是缺少类声明。这通常发生在标头中的代码被标头保护之后。

这是怎么发生的?

如果您在 Funcionalidades 中包含 Apont 标头,然后在 Apont 中包含 Funcionalidades 标头,那么您就有麻烦了。发生这种情况是因为 Funcionalidades 标头缺少 Apont 声明,以防您在包含 Funcionalidades 之前将 Apont 包含在 main 中。

然后你第一次包含Apont,它将启用标题保护。然后它将包括Funcionalidades,其中也包括Apont。因为已经启用了标头保护功能 Funcionalidades 标头将没有 Apont 声明。同时Apont的声明甚至还没有在Apont对应的头文件中开始。在这里,由于这个问题在 main 中无法编译,因为在库编译时你没有这样的依赖项。

我该如何解决?

在 cpp 代码中的 Funcionalidades 实现中移动 Apont 的使用,保持头文件没有依赖关系。

【讨论】:

  • 这似乎是合理的,但我已经尝试过来回移动Funcionalidades 的实现,这个地方完全没有区别。也没有无限的相互引用,包含Apont 的文件不引用包含Funcionalidades 的文件。最让我生气的是这个简短的错误,没有对整个项目的原因进行描述。我真的认为这个答案会起作用。 +1 的努力,让我发现更多的错误。
【解决方案2】:

它只是抱怨它不知道“Apont”。我也不知道它是什么,你没有发布它的代码。只需声明它的任意版本:

generic<typename T>
public ref class Apont {
    T value;
public:
    Apont(T init) : value(init) {}
};

static void Funcionalidades()
{
    int i = 10;

    Apont<int> a2(i);                      // stack
    Apont<int> ^a3 = gcnew Apont<int>(i);  // heap CLR

}

注意“a2”的更改代码,避免引用类型的复制构造函数。再次主要:

    Apont<int> a(y);

【讨论】:

  • 我没有包含Apont 的代码,因为我认为它不相关,而且它也很可能充满了我可以自己解决的愚蠢错误。不过,如果你觉得我应该提供它,请在下面提问,我会编辑我的帖子并插入它。
  • Apont 应该是某种内部指针(仅具有 T* 类型的内部值而不是 void*)。你的版本是某种包装器。
  • 奇怪的是,您的版本也不起作用!错误完全一样!
  • 我发布了经过测试的代码,它编译得很好。阅读答案的最后一段。
  • 我不怀疑。我读了最后一段,完全按照你说的做了。我什至(实际上,我已经完成了)定义了一个复制构造函数。应该是项目设置有问题。为什么会这样?我该怎么办?
【解决方案3】:

我必须在项目内部使用我想在外部使用的东西

如果这是真的,你有没有在任何地方调用过静态函数?未调用的函数可能会被优化掉。

【讨论】:

  • 这实际上是有道理的,但是由于此功能的目的是使其他东西可用,如果我有一个功能可以使这个功能可用,我需要为每个功能再增加一个功能也就是说,我有无数个函数。唯一的方法是在控制台项目中执行此操作,其中始终调用 main 函数,就像其中的所有函数一样(除非中间有一些例外)。但是,如果没有要调用的 main 函数,怎么会有 C++ DLL?要么你错了,要么我的编译器有问题。
【解决方案4】:

可能缺少命名空间的 using 语句吗?确保您的 main 方法中有正确的 using 语句。还要确保您添加了一个引用,以便包含 main 方法的项目引用包含 Apont 的项目。

【讨论】:

  • 感谢您的回复,但在发布此问题之前,我已经至少检查了 5 次,还有 5 次后记。
【解决方案5】:

很抱歉,这么长时间才回复您:我有几点意见可以帮助您解决问题。 - 模板类被声明为sealed:这意味着您不能将其用作基类(与模板类的主要原因相反)。至少它不需要,所以它可能值得删除。 - 其次,更重要的是,在 C++ 中,模板是在编译时评估的,因此,由于您没有声明指定的实例,因此不会评估 Apont 并且它不会编译到 DLL 中。要启用特定版本的模板类,您可以在 UtilCMM.h 文件的末尾添加以下内容:

  • class template Apont&lt;int&gt;;
  • class template Apont&lt;float&gt;;
  • class template Apont&lt;double&gt;;

这是因为在 C++ 中,模板在编译时由预处理器扩展,就像宏一样,但是只有显式使用的版本才会在编译时处理:这是一个很大的优势和劣势。我一直找到的解决方案(与其他 C++ 程序员一致)是:

  • 明确实例化您需要在 dll 中导出的版本
  • 提供带有模板的头文件,然后您将代码分发给的任何人都可以使用该文件

如果有其他方法可以达到同样的效果,我还没有遇到过,很想知道,唉,这些都是模板的局限性。

那么当您编译时,您应该拥有三个可用的特定版本。只需为您希望包含在 DLL 中的每种类型添加更多内容。抱歉,我之前没有发现这一点,当我看到完整的代码时,他们都跳了出来,现在如果我可以让项目编译,我可以自己尝试更改(我的 VS 版本不加载你的 vcproj 文件版本,典型)。

【讨论】:

  • @JMCF125 希望这会有所帮助:)
  • 我使用了ref class template Apont&lt;int&gt;,但不幸的是错误仍然存​​在于测试项目中。由于我对此感到厌倦,因此我将 DLL 与测试项目合并并且它起作用了。我将继续尝试修复它(我保留了原始项目),看看合并是否真的改变了一些东西(我现在有了“Dependency Walker”,所以我可以看到发出的元数据)。为所有帮助 +1,我会尽快回复此事。
  • 无论如何,我希望Apont 成为一个模板。这就像为三种类型设置三个类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-07
  • 2014-01-19
  • 2013-03-23
相关资源
最近更新 更多