在.NET中使用C++/CLI工具:

3
我一直在开发一些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

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

非常感谢任何回答或者回复。

编辑(澄清):

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

第N次编辑(其中“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);
    }

1
你应该制作一个简短、自包含、正确的示例。你的问题中缺少或未说明的许多事情对于回答是必要的。如果你遵循上面链接中的建议,我相信你会得到更多的回复。 - Wilbert
1
即使编译成dll,C++中的函数名称/签名也可能被混淆,您需要找到它们在dll中的名称并创建链接。例如,通过使用link /dump /exports mydll.dll从dll中揭示内部名称。使用我之前提到的链接命令检查您期望存在的值和函数是否实际存在于您的dll中。让我知道您的进展情况,我会尽力帮助您解决这个问题。 - GMasucci
1
嗨,是的,你可以从命令行中使用它。我建议进入包含dll文件的目录,然后只需使用link /dump/exports mydll.dll命令,而不必在命令后面输入完整路径 :) - GMasucci
1
我明白了,刚刚注意到我的一些dll也在做同样的事情,所以不得不关闭调试信息 属性,链接器,调试,生成调试信息=否,这样名称就不会被混淆,而且可以正确显示。另外,dumpbin.exe也可以完成类似的工作(VS命令行),或者dependency walker有一个漂亮的GUI来实现相同的功能。我正在研究这个隐藏函数名问题,因为我的一些dll也有同样的问题,但是当我导入dll时,在vs中这些函数是可用的...非常奇怪。 - GMasucci
1
Utilidades.ComNativos是我查看dll时得到的命名空间,Apont根本没有暴露出来,但是UtilCMM是的。也许可以看一下这两个类的区别?(起点可能是:apont是一个密封类,utilscmm是一个静态类) - GMasucci
显示剩余9条评论
5个回答

3

您的错误通常是类声明丢失。这通常发生在头文件中的代码被头文件保护时。

为什么会出现这种情况?

如果您在Funcionalidades中包含Apont头文件,并在Apont中包含Funcionalidades头文件,则会出现问题。这是因为在您在主函数中包含Funcionalidades之前,Funcionalidades头文件缺少对Apont的声明。

然后您第一次包含Apont时,它将启用头文件保护。然后它将包括Funcionalidades,其中也包括Apont。因为头文件保护已经启用,Funcionalidades头文件将没有Apont声明。同时,Apont声明甚至还没有在相应的Apont头文件中开始。于是你就陷入了困境,无法编译主函数,因为在库编译时,您没有这样的依赖关系。

如何解决这个问题?

将Apont的使用移动到Funcionalidades实现的cpp代码中,保持头文件不具有依赖性。


这似乎是可行的,但我已经尝试过来回移动Funcionalidades的实现,位置完全没有影响。也没有无限的相互引用,包含Apont的文件不引用包含Funcionalidades的文件。最让我生气的是这个短错误,没有任何关于其原因的描述跨越项目。我真的认为这个答案会起作用。+1的努力和让我发现更多错误。 - JMCF125

1

我必须在项目内部使用我想在外部使用的内容。

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


虽然这个函数的目的是为了让其他东西可用,那么如果我有一个函数来使这个函数可用,我需要再写一个函数来处理每个新加进来的函数,也就是说无限数量的函数。唯一的方法是在控制台项目中完成此操作,因为main函数总是被调用,其中的所有函数都会被执行(除非中途出现某些异常)。但是,如果没有要调用的main函数,那么为什么会有C ++ DLL?要么你错了,要么我的编译器有问题。 - JMCF125

1
可能缺少命名空间的使用语句吗?请确保在主方法中有正确的使用语句。还要确保已添加引用,使包含主方法的项目引用包含Apont的项目。

谢谢回复,但在发布这个问题之前,我已经检查了至少5次,之后又检查了5次。 - JMCF125

1
抱歉回复晚了:我有几点建议可以帮助解决问题。 - 模板类被声明为sealed: 这意味着您不能将其用作基类(与您创建模板类的主要原因相反)。至少它不是必需的,因此可能值得删除。 - 其次,更重要的是,在C++中,模板在编译时进行评估,因此,由于您没有指定已声明的实例,因此没有评估Apont,并且它未编译入DLL。要启用特定版本的模板类,您可以在UtilCMM.h文件末尾添加以下内容:
  • class template Apont<int>;
  • class template Apont<float>;
  • class template Apont<double>;
由于在C++中,模板类似于宏,是在编译时由预处理器扩展的,但仅处理显式使用的版本,这既是优点也是缺点。我一直以来都采用了与其他C++程序员达成共识的解决方案:要么明确实例化您需要导出到dll的版本,要么提供一个包含模板的头文件,然后分发给需要使用代码的人。如果有其他实现相同功能的方法,我还没有遇到过,很想了解,不过这就是模板的限制。然后当你编译时,应该有三个特定版本可用。对于每种类型,只需添加更多即可包含在DLL中。很抱歉我之前没有注意到这一点,当我看到完整的代码时,它们都跳了出来。现在,如果我能让项目编译通过,我就可以尝试自己进行更改(我的VS版本无法加载您的vcproj文件,很典型)。

我使用了ref class template Apont<int>,但不幸的是测试项目中仍然存在错误。由于我对此感到厌倦,我将DLL与测试项目合并,并且它可以工作了。我将继续尝试修复它(我保留了原始项目),并查看合并是否实际上改变了任何内容(现在我有“Dependency Walker”,因此可以查看发出的元数据)。 感谢所有的帮助,我会尽快回到这个问题。 - JMCF125
无论如何,我希望Apont成为一个模板。这就像为三种类型拥有三个类一样。 - JMCF125

1

它只是抱怨不认识“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 应该是一种内部指针(仅具有类型为 T* 而非 void* 的内部值)。而你的版本则是一种包装器。 - JMCF125
很奇怪,你的版本也不起作用!错误完全相同! - JMCF125
我毫不怀疑。我阅读了最后一段,并按照您的说法去做了。我甚至已经(事实上,我已经完成了)定义了一个复制构造函数。这一定是项目设置中的某些问题。为什么会发生这种情况?我该怎么办? - JMCF125
我不知道为什么你希望我猜测这个问题,因为我无法从这里看到你的代码。不提供代码是毫无意义的。 - Hans Passant
这就是它!你现在开心了吗?正如你所看到的,它非常混乱和不整洁,可能还有一些愚蠢的错误。这就是为什么不使其可用是毫无意义的原因。 - JMCF125
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接