来自 C++ CLI 项目的非托管数据的 C# 指针
C# pointer on unmanaged data from C++ CLI project
我有一个用 C++ 编写的用于管理设备的 SDK。我控制设备的程序是用 C# 编写的,因此 CLI 包装器 class 自然会在两种语言之间进行翻译。我的 C# 项目包括作为 DLL 的包装器。
我的问题是 C++ SDK 使用指针指向数据数组。这些指针在包装器中也可用。
包装器 .cpp 代码:
Wrapper::Wrapper()
{
myData = new DataAquis(); //initiate C++ class's instance
}
int Wrapper::Start()
{
//(..)
int num = myData->Start();
ptr = (myData->img);
return num;
}
此代码初始化设备并创建指向数据结构(unsigned char 数组)的指针。
Wrapper SDK .cpp 代码:
int DataAquis::Start()
{
// (...)
// Pointer from SDK
img = pBuffer;
// (...)
return FAILED(nError) ? -1 : 0;
}
包装.h代码:
public ref class Wrapper
{
public:
Wrapper();
// (...)
unsigned char *ptr;
private:
// (...)
};
C# 代码:
public static Wrapper myDataAqui;
// (...)
private static void DataAquisition()
{
// call the start function in wrapper
myDataAqui.Start();
// Unsafe code for pointer use
unsafe
{
// point to aquired data
byte* imgptr1 = myDataAqui.ptr;
// AccesViolationException in above line.
// Data processing
for (y = 0; y < 256; y++)
{
for (x = 0; x < 320; x++)
{
int temp = x * 256 + 255 - y;
Spectrum1.X_Data_brut[bufferIndex][temp] = (UInt16)(*imgptr1++ + *imgptr1++ * 256);
aquirData[temp] = Spectrum1.X_Data_brut[bufferIndex][temp];
}
}
// (...)
}
}
如图所示,在我将 Wrapper 指针转换为本地字节指针的那一行触发了 AccessViolationException。
如果我在该行放置一个断点,我可以看到 Wrapper 指针正确指向内存地址,但表示它无法读取内存,所以指向的数据永远不会在 C# 中收集。
我读到在 C# 中相当于 C++ 中的 unsigned char 是一个字节,所以通常我应该读取相同数量的数据并且永远不会超出我的数据结构的边界。
可能有用的其他信息:
- 此项目是从另一台 PC 复制的,相同的代码可在该 PC 上运行。
- 两台 PC 具有相同的 Visual Studio、相同的 .Net 版本、相同的 SDK,均为 64 位编译。只有 Windows 版本不同(在 Windows 8 上工作而不在 Windows 7 上工作)。
- 我尝试使用 Marshal 函数没有成功。
您知道如何解决这个问题吗?
我不确定您为什么会收到异常,但我会在 C++/CLI 端将其编组到 CLR 数组中,因此在 C# 端不需要不安全的代码。
C++/CLI:
#include <vcclr.h>
#include <algorithm>
#pragma unmanaged
const int data_size = 100;
unsigned char * foo()
{
auto p = new unsigned char[data_size];
for (int i = 0; i < data_size; ++i)
p[i] = (unsigned char)i;
return p;
}
#pragma managed
public ref class Wrapper
{
public:
array<unsigned char>^ data;
void Start()
{
auto p = foo();
data = gcnew array<unsigned char>(data_size);
pin_ptr<unsigned char> data_pin = &data[0];
std::copy(p, p+data_size, (unsigned char *)data_pin);
}
};
C#:
class Program
{
static void Main(string[] args)
{
var wrapper = new Wrapper();
wrapper.Start();
System.Console.WriteLine($"{wrapper.data[15]}");
}
}
这将包含接近源代码的任何可能的问题,并使调试不那么混乱。如果它在 std::copy 中崩溃,那么你只是错误地使用了 C++ 对象。
我有一个用 C++ 编写的用于管理设备的 SDK。我控制设备的程序是用 C# 编写的,因此 CLI 包装器 class 自然会在两种语言之间进行翻译。我的 C# 项目包括作为 DLL 的包装器。
我的问题是 C++ SDK 使用指针指向数据数组。这些指针在包装器中也可用。
包装器 .cpp 代码:
Wrapper::Wrapper()
{
myData = new DataAquis(); //initiate C++ class's instance
}
int Wrapper::Start()
{
//(..)
int num = myData->Start();
ptr = (myData->img);
return num;
}
此代码初始化设备并创建指向数据结构(unsigned char 数组)的指针。
Wrapper SDK .cpp 代码:
int DataAquis::Start()
{
// (...)
// Pointer from SDK
img = pBuffer;
// (...)
return FAILED(nError) ? -1 : 0;
}
包装.h代码:
public ref class Wrapper
{
public:
Wrapper();
// (...)
unsigned char *ptr;
private:
// (...)
};
C# 代码:
public static Wrapper myDataAqui;
// (...)
private static void DataAquisition()
{
// call the start function in wrapper
myDataAqui.Start();
// Unsafe code for pointer use
unsafe
{
// point to aquired data
byte* imgptr1 = myDataAqui.ptr;
// AccesViolationException in above line.
// Data processing
for (y = 0; y < 256; y++)
{
for (x = 0; x < 320; x++)
{
int temp = x * 256 + 255 - y;
Spectrum1.X_Data_brut[bufferIndex][temp] = (UInt16)(*imgptr1++ + *imgptr1++ * 256);
aquirData[temp] = Spectrum1.X_Data_brut[bufferIndex][temp];
}
}
// (...)
}
}
如图所示,在我将 Wrapper 指针转换为本地字节指针的那一行触发了 AccessViolationException。
如果我在该行放置一个断点,我可以看到 Wrapper 指针正确指向内存地址,但表示它无法读取内存,所以指向的数据永远不会在 C# 中收集。
我读到在 C# 中相当于 C++ 中的 unsigned char 是一个字节,所以通常我应该读取相同数量的数据并且永远不会超出我的数据结构的边界。
可能有用的其他信息:
- 此项目是从另一台 PC 复制的,相同的代码可在该 PC 上运行。
- 两台 PC 具有相同的 Visual Studio、相同的 .Net 版本、相同的 SDK,均为 64 位编译。只有 Windows 版本不同(在 Windows 8 上工作而不在 Windows 7 上工作)。
- 我尝试使用 Marshal 函数没有成功。
您知道如何解决这个问题吗?
我不确定您为什么会收到异常,但我会在 C++/CLI 端将其编组到 CLR 数组中,因此在 C# 端不需要不安全的代码。
C++/CLI:
#include <vcclr.h>
#include <algorithm>
#pragma unmanaged
const int data_size = 100;
unsigned char * foo()
{
auto p = new unsigned char[data_size];
for (int i = 0; i < data_size; ++i)
p[i] = (unsigned char)i;
return p;
}
#pragma managed
public ref class Wrapper
{
public:
array<unsigned char>^ data;
void Start()
{
auto p = foo();
data = gcnew array<unsigned char>(data_size);
pin_ptr<unsigned char> data_pin = &data[0];
std::copy(p, p+data_size, (unsigned char *)data_pin);
}
};
C#:
class Program
{
static void Main(string[] args)
{
var wrapper = new Wrapper();
wrapper.Start();
System.Console.WriteLine($"{wrapper.data[15]}");
}
}
这将包含接近源代码的任何可能的问题,并使调试不那么混乱。如果它在 std::copy 中崩溃,那么你只是错误地使用了 C++ 对象。