Oli*_*man 7 c# c++ interop marshalling .net-core
我有很多在 .NET 4.6 Framework 中制作的 C# DLL,需要升级到 .NET Core 6 - 这部分相对简单。
但是,该库还可以通过 COM Interop 在 C++ 应用程序中使用。此选项在 .NET 4.6 中相对简单,因为在构建时有一个选项可以公开给 COM Interop。这创建了一个.tlb文件,可以直接导入到 C++ 应用程序中。所有必要的编组都是在 C# 代码中完成的 - 数组作为 SafeArray 等传递(请参见下面的示例)。Regasm.exe 在事后运行以注册必要的 C# 代码。
升级到 .NET Core 6 后,失去了在运行时创建 .tlb 文件的功能。然而,(显然)互操作性应该是简单的。我见过像 DllExport 这样的 Nuget 包(其中有 SafeArrays 的不清楚的示例)。我已经阅读了一页又一页的 Microsoft 关于 COM 互操作和 COM 托管的参考资料,并阅读了相关示例。我简要地研究了构建我自己的 .tlb 文件的兔子洞。我见过许多自定义示例,但它们并没有扩展到这个确切的问题。
以下是我尝试连接在一起的一些代码,作为您的示例。我会做出什么样的改变?
要导入的 C# 代码
public void CSharpMacro(
[MarshalAs(UnmanagedType.SafeArray)] double[] D,
[MarshalAs(UnmanagedType.SafeArray)] double[] O,
[MarshalAs(UnmanagedType.SafeArray)] double[] H,
[MarshalAs(UnmanagedType.SafeArray)] double[] L,
[MarshalAs(UnmanagedType.SafeArray)] double[] C,
[MarshalAs(UnmanagedType.SafeArray)] double[] V,
[MarshalAs(UnmanagedType.LPStr)] string FilePath,
[MarshalAs(UnmanagedType.SafeArray)] ref double[] sOutput,
[MarshalAs(UnmanagedType.I8)] long CustNum,
[MarshalAs(UnmanagedType.R8)] double TSDate)
{
String path = @ "...\Errors.txt";
try
{
//some code
}
catch (Exception e)
{
//error handling
}
}
Run Code Online (Sandbox Code Playgroud)
C++ 导入示例(当前方法)- 请注意,这是很多代码,但目的是向您展示如何.tlb导入这些代码以及如何在代码中使用 C# 中的方法,以及围绕此进行的一些操作,例如SafeArray 的使用:
// In the below import statement, use the location of the Release version of your C# DLL
#import "...\PSP_CSLibrary.tlb" no_namespace
//later in the code... (EasyObject is from external library, don't worry about it)
void far __declspec(dllexport) __stdcall UMacro(EasyObject* pELObj, char* path)
{
// Initialize the COM interface
HRESULT hr = CoInitialize(NULL);
try
{
if (SUCCEEDED(hr) && pELObj != NULL)
{
// Init pointer to C# Library
PSP_CSLibraryDLLClassPtr p(__uuidof(PSP_CSLibrary));
if (p != NULL)
{
int datanumber = 1;
double *dOutput = new double[100];
for (int j = 0; j < 100; j++) dOutput[j] = 0;
SAFEARRAY* sOutput = doubleToSA(dOutput, 100);
EN_DATA_STREAM datastream = (datanumber == 1) ? pELObj->DataStream : GetDataStream(datanumber);
double TSDate = pELObj->DateTimeMD[datastream]->AsDateTime[0];
long CustomerNumber = pELObj->Platform->CustomerID;
int length = pELObj->CloseMD[datastream]->BarsBack;
if (length > 0)
{
double *dDate = new double[length];
double *dOpen = new double[length];
double *dHigh = new double[length];
double *dLow = new double[length];
double *dClose = new double[length];
double *dVolume = new double[length];
// Load the double arrays with LEAST recent 0
for (int i = 0; i < length; i++)
{
dVolume[length - i - 1] = pELObj->VolumeMD[datastream]->AsDouble[i];
dOpen[length - i - 1] = pELObj->OpenMD[datastream]->AsDouble[i];
dHigh[length - i - 1] = pELObj->HighMD[datastream]->AsDouble[i];
dLow[length - i - 1] = pELObj->LowMD[datastream]->AsDouble[i];
dClose[length - i - 1] = pELObj->CloseMD[datastream]->AsDouble[i];
dDate[length - i - 1] = pELObj->DateTimeMD[datastream]->AsDateTime[i];
}
// Convert to safe arrays from double arrays
SAFEARRAY* sVolume = doubleToSA(dVolume, length);
SAFEARRAY* sHigh = doubleToSA(dHigh, length);
SAFEARRAY* sOpen = doubleToSA(dOpen, length);
SAFEARRAY* sLow = doubleToSA(dLow, length);
SAFEARRAY* sClose = doubleToSA(dClose, length);
SAFEARRAY* sDate = doubleToSA(dDate, length);
/////IMPORTANT PART
if (sOpen != nullptr && sHigh != nullptr && sLow != nullptr && sOutput != nullptr && p != nullptr && sClose != nullptr && dOutput != nullptr)
{
p->CSharpMacro(sDate, sOpen, sHigh, sLow, sClose, sVolume, path, &sOutput, CustomerNumber, TSDate);
}
/////
// Release memory
if (sDate != nullptr) SafeArrayDestroy(sDate);
if (sVolume != nullptr) SafeArrayDestroy(sVolume);
if (sOpen != nullptr) SafeArrayDestroy(sOpen);
if (sHigh != nullptr) SafeArrayDestroy(sHigh);
if (sLow != nullptr) SafeArrayDestroy(sLow);
if (sClose != nullptr) SafeArrayDestroy(sClose);
if (sOutput != nullptr) SafeArrayDestroy(sOutput);
if (dDate != nullptr) delete[] dDate;
if (dVolume != nullptr) delete[] dVolume;
if (dOpen != nullptr) delete[] dOpen;
if (dHigh != nullptr) delete[] dHigh;
if (dLow != nullptr) delete[] dLow;
if (dClose != nullptr) delete[] dClose;
if (dOutput != nullptr) delete[] dOutput;
}
}
p->Release();
}
}
catch (exception &e)
{
// did we get it?
string out_ = "UMacro error= ";
std::ostringstream strs;
strs << e.what();
std::string str = strs.str();
out_ += str;
appendLineToFile("PSP_Interface_Errors.txt", out_);
}
CoUninitialize();
return;
}
Run Code Online (Sandbox Code Playgroud)
如上所示,这种交互是有效的。有谁知道如何继续将这两者联系起来,但使用 .NET Core 6 中的 C# 吗?最好以最小的努力。我想大部分工作已经完成,因为类型已经整理好,如您所见。现在如何在没有 tlb 文件的情况下导入 C# DLL?
请注意,这必须是非托管C++ 中的。我目前正在研究使用 C++/CLR 接口层,并取得了一定的成功。成功的话会更新。
看来我的方法没有奏效。我为其创建此接口的程序不接受 CLR DLL,即使它们是由本机 C++ DLL 链接/导入的。因此,该代码适用于本机 C++,但不适用于预期环境,即股票交易程序。我有点回到了原点。
注意:我怀疑 .tlb 文件之所以有效,是因为它使用 COM,而不是尝试引入 CLR dll。因此某种形式的 COM 互操作或手动构建 .tlb 文件应该可以工作。
COM 互操作方法:
在没有 COM Interop 的情况下导入 .NET 库:
摆脱 COM/SafeArray,在方法签名中仅使用原始类型以及手动编组数组和字符串参数。
不要double[]传递两个参数:指向第一个元素的指针和数组大小,如下所示:double* d_ptr, int d_size,然后将其包装在 Span 中。如果您想继续使用数组而不是跨度,只需将其转换为span.ToArray()
而不是string传递指向第一个符号的指针,然后使用Marshal.PtrToStringAnsi它将其转换为托管字符串。
您可以从 DllExport文档和教程中了解有关手动编组的更多信息。
[UnmanagedCallersOnly(EntryPoint="CSharpMacro")] // this attribute also prevents from using incompatible non-primitive types
public static void CSharpMacro(
double* dArray, int dSize,
double* oArray, int oSize,
double* hArray, int hSize,
double* lArray, int lSize,
double* cArray, int cSize,
double* vArray, int vSize,
IntPtr filePath, // string is null-terminated, don't need to pass a length
double* sArray, int sSize,
long CustNum,
double TSDate)
{
// manual marshalling
var D = new Span<double>(dArray, dSize);
var DArray = D.ToArray(); // just an example how to convert a Span to Array.
var O = new Span<double>(oArray, oSize);
var H = new Span<double>(hArray, hSize);
var L = new Span<double>(lArray, lSize);
var C = new Span<double>(cArray, cSize);
var V = new Span<double>(vArray, vSize);
var SOutput = new Span<double>(sArray, sSize);
var FilePath = Marshal.PtrToStringAnsi(filePath);
try
{
//some code
}
catch (Exception e)
{
//error handling
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1719 次 |
| 最近记录: |