Tengo un buen número de C# Dll que se crearon en .NET 4.6 Framework y necesito actualizarme a .NET Core 6; esa parte es relativamente sencilla. Sin embargo, esta biblioteca TAMBIÉN se usa dentro de una aplicación C++, a través de COM Interop. Esta opción era relativamente sencilla en .NET 4.6, porque en el momento de la compilación había una opción para exponer a COM Interop. Esto creó un archivo .tlb, que podría importarse directamente a la aplicación C++. Todo el ordenamiento necesario se realizó dentro del código C#: las matrices se pasaron como SafeArrays, etc. (consulte el ejemplo a continuación). Regasm.exe se ejecutó después del hecho para registrar el código C# necesario.
Con la actualización a .NET Core 6, se perdió la capacidad de crear un archivo .tlb en tiempo de ejecución. Sin embargo, (aparentemente) la interoperabilidad debería ser sencilla. He visto paquetes Nuget como DllExport (que tiene ejemplos poco claros para SafeArrays). He leído página tras página de las referencias de Microsoft sobre la interoperabilidad COM y el alojamiento COM, y he leído ejemplos relativos. Miré brevemente en el agujero del conejo de construir mi propio archivo .tlb. He visto muchos ejemplos personalizados que no se extienden a este problema exacto.
Aquí hay parte del código que estoy tratando de unir, como ejemplo para usted. ¿Qué tipo de cambios haría?
Código C# para importar
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 } }Ejemplo de importación de C++ (método actual): tenga en cuenta que se trata de una gran cantidad de código, pero el propósito es mostrarle cómo se importa el .tlb y cómo se usa el método de C# dentro del código, y algo de lo que sucede alrededor de eso. , como el uso de SafeArrays:
// 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; }Esta interacción, como se muestra arriba, funciona . ¿Alguien tiene ALGUNA idea sobre cómo continuar vinculando estos dos pero con C# en .NET Core 6? Preferiblemente con el mínimo esfuerzo. Me gustaría pensar que la mayor parte del trabajo ya está hecho, ya que los tipos ESTÁN organizados, como puede ver. ¿Cómo importo un C# Dll ahora, sin un archivo tlb?
Editar: como nota, esto debe estar en C++ no administrado . Actualmente estoy investigando el uso de una capa de interfaz C++/CLR y he obtenido un éxito moderado. Se actualizará si tiene éxito.