位于外部 DLL 中的 class 中 wstring 数据的内存损坏

Memory corruption of wstring data in class placed in external DLL

目标

将字符串数据存储在 classes 中放置在外部 DLL 中。

使用的指南

Create and use your own Dynamic Link Library

环境

为什么这个问题是独一无二的?

其他 QA 也在谈论 DLL 或 classes,但 none 处理外部 DLL classes 中的字符串主题。

描述

我在 DLL 中使用容器 classes(classes 的目的是保存数据)中的字符串时遇到了问题。从我的角度来看,记忆位置似乎出了问题。

我从 Internet 了解到在 DLL 中不能轻易使用 wstring(或 string),而应该使用指针(const wchar_t*) .

然而,即使这样做,如果我稍微传递它们,对象中字符串中的数据似乎也会损坏。

我怎么

  1. 放置和检索字符串数据...
  2. to/from class 会员...
  3. 在 DLL 中...
  4. 没有数据丢失或损坏?

密码

DLL中的头文件:

// AnimalLibrary.h - Contains declarations of animal methods.
#pragma once

#ifdef ANIMALLIBRARY_EXPORTS
#define ANIMALLIBRARY_API __declspec(dllexport)
#else
#define ANIMALLIBRARY_API __declspec(dllimport)
#endif

class Animal {
public:
    ANIMALLIBRARY_API static Animal* GetAnimal();
    virtual ~Animal() = 0;
    virtual void SetSound(const wchar_t* sound) = 0;
    virtual wchar_t const* GiveSound() const = 0;
    virtual Animal* clone() const = 0;
};

DLL 主体:

// AnimalLibrary.cpp : Defines the exported functions for the DLL application.
#include "stdafx.h"
#include "AnimalLibrary.h"
#include <string>

using namespace std;

Animal* Animal::GetAnimal() {
    class RealAnimal :public Animal {
    public:
        void SetSound(wchar_t const* sound) override {
            this->sound = sound;
        }
        wchar_t const* GiveSound() const override {
            return sound.c_str();
        }
        Animal* clone() const override {
            return new RealAnimal{ *this };
        }
    private:
        wstring sound;
    };
    return new RealAnimal{};
}

Animal::~Animal() = default;

最后是应用程序本身:

// StringInDllClass.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "AnimalLibrary.h"
#include <iostream>
#include <string>
#include <memory>

using namespace std;

int main();

void printMessage(unique_ptr<Animal> animal);
unique_ptr<Animal> createCow();
wstring createCowSound();

int main()
{
    unique_ptr<Animal> cow = createCow();

    //This row won't compile due to
    //error C2280: attempting to reference a deleted function:
    //'unique_ptr<Animal,default_delete<_Ty>>::unique_ptr(
    //const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)'
    printMessage(cow);

    system("pause");
    return 0;
}

void printMessage(unique_ptr<Animal> animal) {
    wcout << L"The animal says " << animal->GiveSound() << endl;
}

unique_ptr<Animal> createCow() {

    unique_ptr<Animal> cow{ Animal::GetAnimal() };
    cow->SetSound(createCowSound().c_str());

    return cow;
}

wstring createCowSound() {
    return L"Moo";
}

以与在源库中相同的方式在外部 DLL 中放置和检索字符串数据 to/from class 成员。

我看到 header 定义正确,只是不要忘记在源库中设置 ANIMALLIBRARY_EXPORTS,而不是目标库。

我相信你得到字符串损坏的原因是指向局部变量的指针被重新调整,并且局部变量在离开作用域时被破坏,所以指针无效

Animal createCow() {

    Animal cow;

    wstring sound = createCowSound(); //sound is local varialbe
    cow.Sound = sound.c_str(); //here you set pointer to local variable

    return cow; 
} //local variable is destroyed here

要解决此问题,请在 Animal class 中提供 operator=,以复制由 'const wchar_t* Sound;' 指针指向的 object,或替换 const wchar_t* Sound;wstring Sound; 在动物 class.

这是一个使用继承的准系统实现,这避免了跨 dll 边界传递潜在的不安全类型,同时允许您在 dll 本身内部使用标准库类型。您也可以使用 pimpl 习语实现同样的效果。

AnimalLibrary.h

#pragma once

class Animal 
{
public:
    __declspec(dllexport) static Animal* getAnimal();
    virtual ~Animal() =0;
    virtual void setSound(wchar_t const* name) =0;
    virtual wchar_t const* getSound() const =0;
    virtual Animal* clone() const =0;
};

AnimalLibrary.cpp

#include "AnimalLibrary.h"
#include <string>

Animal* Animal::getAnimal()
{
    class RealAnimal : public Animal
    {
    public:
        void setSound(wchar_t const* name) override
        {
            sound = name;
        }

        wchar_t const* getSound() const override
        {
            return sound.c_str();
        }

        Animal* clone() const override
        {
            return new RealAnimal{*this};
        }

    private:
        std::wstring sound;
    };

    return new RealAnimal{};
}


Animal::~Animal() =default;

test.cpp

#include <iostream>
#include <memory>
#include "AnimalLibrary.h"

int main()
{
    std::unique_ptr<Animal> cow{Animal::getAnimal()};
    cow->setSound(L"moo");
    std::wcout << cow->getSound();
    decltype(cow) dog{cow->clone()};
    dog->setSound(L"woof");
    std::wcout << dog->getSound();
}