Я пытаюсь использовать устаревшие компоненты COM (библиотека dll, созданная, вероятно, на C ++, 32-разрядная версия) в моем приложении C #.Мне нужно, чтобы он работал в следующих сценариях:
- COM-компонент, используемый в потоке x86 STA (например, основной поток приложения)
- COM-компонент, используемый в рабочем потоке x86 (например, task /пул потоков)
- COM-компонент, используемый в потоке x64 STA (например, основной поток приложения)
- COM-компонент, используемый в рабочем потоке x64 (например, пул задач / потоков)
Пока что он работает в сценарии 1. Я также создал оболочку PoC для сценария 2 - но сейчас я не хочу следовать этому.В целом - только сценарий 1 работает нормально.Остальное просто нет.
Я провел некоторые исследования, и результаты показали, что сценарий 1 должен быть довольно простым.И это было.Я также узнал, что сценарии 2, 3, 4 также возможны, когда COM-компонент размещен в суррогатном (32-битном?) Процессе.В этом случае может произойти значительная потеря производительности, но сейчас я не очень обеспокоен производительностью.
Хотя пытаясь много раз, я не мог заставить это работать таким образом.У меня закончились идеи, и мне нужна ваша помощь.Позвольте мне сначала описать мои настройки.Компоненты COM, упомянутые в начале, представлены в виде следующего набора файлов:
- GeoDefs.dll
- GeoDefs.tlb
- GeomDefs.dll
- GeomDefs.tlb
- GeoFunc.dll
- GeoFunc.tlb
- GeomFunc.dll
- GeomFunc.tlb
- GeoDatumKrtgrf.dll
- GeoDatumKrtgrf.tlb
Я зарегистрировал все (5) из указанных выше DLL-файлов с помощью инструмента regsvr32 .Регистрация прошла успешно.Я создал простое приложение C # WinForms и добавил в свой проект ссылки на COM:
- Библиотека типов GeoDatumKrtgrf 1.0
- Библиотека типов GeoDefs 1.0
- Библиотека типов GeoFunc 1.0
- Библиотека типов GeomDefs 1.0
- Библиотека типов GeomFunc 1.0
Под капотом Visual Studio создала сборки взаимодействия (помещенные в папку obj):
- Interop.GEODATUMKRTGRFLib.dll
- Interop.GEODEFSLib.dll
- Interop.GEOFUNCLib.dll
- Interop.GEOMDEFSLib.dll
- Interop.GEOMFUNCLib.dll
, который появился в разделе ссылок как:
- GEODATUMKRTGRFLib
- GEODEFSLib
- GEOFUNCLib
- GEOMDEFSLib
- GEOMFUNCLib
Все вышеприведенные ссылки имеют Типы встроенных взаимодействий , для которых установлено значение "False" и Скопируйте Local в "True" в свойствах.
И наконец - есть два простых фрагмента кода, которые я использую дляпроверить, правильно ли доступен COM.
void btnComInMainThread_Click(object sender, EventArgs e)
{
try
{
var comObject = new GeoDatumKrtgrfClass();
comObject.InitDatum(eT_DatumTypes.DT_WGS_1984);
comObject.LG2G(out var lonDouble, out var latDouble, 17000000, 25000000);
Debug.Assert(Math.Abs(lonDouble - 17.0) < 1e-6 && Math.Abs(latDouble - 25.0) < 1e-6);
Debug.Print($"{DateTime.Now:HH:mm:ss.fff}: Ok.");
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
}
void btnComInWorkerThread_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
try
{
var comObject = new GeoDatumKrtgrfClass();
comObject.InitDatum(eT_DatumTypes.DT_WGS_1984);
comObject.LG2G(out var lonDouble, out var latDouble, 17000000, 25000000);
Debug.Assert(Math.Abs(lonDouble - 17.0) < 1e-6 && Math.Abs(latDouble - 25.0) < 1e-6);
Debug.Print($"{DateTime.Now:HH:mm:ss.fff}: Ok.");
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
}
});
}
Как было сказано ранее - только btnComInMainThread_Click работает нормально тогда и только тогда, когда проект приложения C # нацелен на x86 (сценарий 1).В любом другом случае при вызове метода InitDatum возникает следующее исключение.
System.InvalidCastException: Unable to cast COM object of type 'GEODATUMKRTGRFLib.GeoDatumKrtgrfClass' to interface type 'GEODATUMKRTGRFLib.IGeoDatumKrtgrf'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{C6ACFB21-24DC-43FB-AF7F-07EB526D2DF5}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
at System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease)
at GEODATUMKRTGRFLib.GeoDatumKrtgrfClass.InitDatum(eT_DatumTypes e_Datum)
at ...
Ниже я включаю (сокращенную) дополнительную информацию из OLE / COM Object Viewer и ILSpy.
GeoDatumKrtgrf.tlb
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: GeoDatumKrtgrf.tlb
[
uuid(D541177E-570B-4F7F-A6B0-931C0BAC85C9),
version(1.0),
helpstring("GeoDatumKrtgrf 1.0 Type Library"),
custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 100663662),
custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1302262921),
custom(DE77BA65-517C-11D1-A2DA-0000F8773CE9, "Created by MIDL version 6.00.0366 at Fri Apr 08 13:41:58 2011
")
]
library GEODATUMKRTGRFLib
{
// TLib : // TLib : GeoDefs 1.0 Type Library : {3EB9DBAA-44B4-4FFE-AAA2-FA7AFC6FC228}
importlib("GeoDefs.tlb");
// TLib : GeomDefs 1.0 Type Library : {F28B71EA-132F-4C47-AAB1-1218716945E5}
importlib("GeomDefs.tlb");
// Forward declare all types defined in this typelib
interface IGeoDatumKrtgrf;
[
uuid(7948C0A1-0806-406A-B5A1-34A9106B8C37),
helpstring("GeoDatumKrtgrf Class")
]
coclass GeoDatumKrtgrf {
[default] interface IGeoDatumKrtgrf;
};
[
odl,
uuid(C6ACFB21-24DC-43FB-AF7F-07EB526D2DF5),
helpstring("IGeoDatumKrtgrf Interface")
]
interface IGeoDatumKrtgrf : IGeoDatum {
...
[helpstring("method LG2G")]
HRESULT _stdcall LG2G(
[out] double* pd_Lon,
[out] double* pd_Lat,
[in] long l_Lon,
[in] long l_Lat);
...
};
};
GeoDefs.tlb
// Generated .IDL file (by the OLE/COM Object Viewer)
//
// typelib filename: GeoDefs.tlb
[
uuid(3EB9DBAA-44B4-4FFE-AAA2-FA7AFC6FC228),
version(1.0),
helpstring("GeoDefs 1.0 Type Library"),
custom(DE77BA64-517C-11D1-A2DA-0000F8773CE9, 100663662),
custom(DE77BA63-517C-11D1-A2DA-0000F8773CE9, 1302262808),
custom(DE77BA65-517C-11D1-A2DA-0000F8773CE9, "Created by MIDL version 6.00.0366 at Fri Apr 08 13:40:05 2011
")
]
library GEODEFSLib
{
// TLib : // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
importlib("stdole2.tlb");
...
[
odl,
uuid(69E77C75-BB8D-46F9-BD93-1D26E09249DE),
helpstring("IGeoDatum Interface")
]
interface IGeoDatum : IUnknown {
HRESULT _stdcall InitDatum([in] eT_DatumTypes e_Datum);
HRESULT _stdcall SetViewByR(
[in] stT_GeoRect* pst_GeoRect,
[in, out] tagRECT* pst_Rect);
HRESULT _stdcall SetViewByP(
[in] stT_GeoCoord* pst_GeoCoordLB,
[in] stT_GeoCoord* pst_GeoCoordRT,
[in, out] tagPOINT* pst_PntLB,
[in, out] tagPOINT* pst_PntRT);
HRESULT _stdcall G2L(
[in] double d_Lon,
[in] double d_Lat,
[in, out] long* pl_X,
[in, out] long* pl_Y);
HRESULT _stdcall L2G(
[in] long l_X,
[in] long l_Y,
[in, out] double* pd_Lon,
[in, out] double* pd_Lat);
HRESULT _stdcall GP2LP(
[in] stT_GeoCoord* pst_GeoCoord,
[in, out] tagPOINT* pst_Pnt);
HRESULT _stdcall LP2GP(
[in] tagPOINT* pst_Pnt,
[in, out] stT_GeoCoord* pst_GeoCoord);
HRESULT _stdcall GR2LR(
[in] stT_GeoRect* pst_GeoRect,
[in, out] tagRECT* pst_Rect);
HRESULT _stdcall LR2GR(
[in] tagRECT* pst_Rect,
[in, out] stT_GeoRect* pst_GeoRect);
[helpstring("method NormalizeGR")]
HRESULT _stdcall NormalizeGR([in, out] stT_GeoRect* pst_GeoRect);
};
...
[
uuid(38EE7367-30EC-43A7-96F6-C55BC39B62C0),
helpstring("Cnv Class")
]
coclass Cnv {
[default] interface ICnv;
};
[
odl,
uuid(6C92FACA-266F-4943-B4AE-7E538F6FC672),
helpstring("ICnv Interface")
]
interface ICnv : IUnknown {
[helpstring("method dms2dd")]
HRESULT _stdcall dms2dd(
[in] stT_GeoCoordDMS* pst_GeoCoordDMS,
[out, retval] stT_GeoCoord* pst_GeoCoord);
[helpstring("method dd2dms")]
HRESULT _stdcall dd2dms(
[in] stT_GeoCoord* pst_GeoCoord,
[out, retval] stT_GeoCoordDMS* pst_GeoCoordDMS);
[helpstring("method dm2dd")]
HRESULT _stdcall dm2dd(
[in] stT_GeoCoordDM* pst_GeoCoordDM,
[out, retval] stT_GeoCoord* pst_GeoCoord);
[helpstring("method dd2dm")]
HRESULT _stdcall dd2dm(
[in] stT_GeoCoord* pst_GeoCoord,
[out, retval] stT_GeoCoordDM* pst_GeoCoordDM);
[helpstring("method DDMMSS2d")]
HRESULT _stdcall DDMMSS2d(
[out] stT_GeoCoord* pst_GeoCoord,
[in] long l_Lon,
[in] long l_Lat);
[helpstring("method d2DDMMSS")]
HRESULT _stdcall d2DDMMSS(
[out] long* pl_Lon,
[out] long* pl_Lat,
[in] stT_GeoCoord* pst_GeoCoord);
[helpstring("method dmsTodd")]
HRESULT _stdcall dmsTodd(
[in] stT_GeoCoordDMS* pst_GeoCoordDMS,
[out] stT_GeoCoord* pst_GeoCoord);
[helpstring("method ddTodms")]
HRESULT _stdcall ddTodms(
[in] stT_GeoCoord* pst_GeoCoord,
[out] stT_GeoCoordDMS* pst_GeoCoordDMS);
[helpstring("method dmshToddh")]
HRESULT _stdcall dmshToddh(
[in] stT_GeoCoordDMSH* pst_GeoCoordDMSH,
[out] stT_GeoCoordH* pst_GeoCoordH);
[helpstring("method ddhTodmsh")]
HRESULT _stdcall ddhTodmsh(
[in] stT_GeoCoordH* pst_GeoCoordH,
[out] stT_GeoCoordDMSH* pst_GeoCoordDMSH);
[helpstring("method dd2ch")]
HRESULT _stdcall dd2ch(
[in] stT_GeoCoord* pst_GeoCoord,
[out] BSTR* pac_Lat,
[out] BSTR* pac_Lon);
};
};
Interop.GEODATUMKRTGRFLib.dll 1/3
// GEODATUMKRTGRFLib.GeoDatumKrtgrfClass
using GEODATUMKRTGRFLib;
using GEODEFSLib;
using GEOMDEFSLib;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[ComImport]
[ClassInterface(0)]
[TypeLibType(2)]
[Guid("7948C0A1-0806-406A-B5A1-34A9106B8C37")]
public class GeoDatumKrtgrfClass : IGeoDatumKrtgrf, GeoDatumKrtgrf
{
[MethodImpl(MethodImplOptions.InternalCall)]
public extern GeoDatumKrtgrfClass();
[MethodImpl(MethodImplOptions.InternalCall)]
public virtual extern void InitDatum([In] eT_DatumTypes e_Datum);
void IGeoDatumKrtgrf.InitDatum([In] eT_DatumTypes e_Datum)
{
//ILSpy generated this explicit interface implementation from .override directive in InitDatum
this.InitDatum(e_Datum);
}
...
[MethodImpl(MethodImplOptions.InternalCall)]
public virtual extern void LG2G(out double pd_Lon, out double pd_Lat, [In] int l_Lon, [In] int l_Lat);
void IGeoDatumKrtgrf.LG2G(out double pd_Lon, out double pd_Lat, [In] int l_Lon, [In] int l_Lat)
{
//ILSpy generated this explicit interface implementation from .override directive in LG2G
this.LG2G(out pd_Lon, out pd_Lat, l_Lon, l_Lat);
}
...
}
Interop.GEODATUMKRTGRFLib.dll 2/3
// GEODATUMKRTGRFLib.IGeoDatumKrtgrf
using GEODEFSLib;
using GEOMDEFSLib;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[ComImport]
[Guid("C6ACFB21-24DC-43FB-AF7F-07EB526D2DF5")]
[InterfaceType(1)]
public interface IGeoDatumKrtgrf : IGeoDatum
{
[MethodImpl(MethodImplOptions.InternalCall)]
new void InitDatum([In] eT_DatumTypes e_Datum);
...
[MethodImpl(MethodImplOptions.InternalCall)]
void LG2G(out double pd_Lon, out double pd_Lat, [In] int l_Lon, [In] int l_Lat);
...
}
Interop.GEODATUMKRTGRFLib.dll 3/3
// GEODATUMKRTGRFLib.GeoDatumKrtgrf
using GEODATUMKRTGRFLib;
using System.Runtime.InteropServices;
[ComImport]
[Guid("C6ACFB21-24DC-43FB-AF7F-07EB526D2DF5")]
[CoClass(typeof(GeoDatumKrtgrfClass))]
public interface GeoDatumKrtgrf : IGeoDatumKrtgrf
{
}
Interop.GEODEFSLib.dll
// GEODEFSLib.IGeoDatum
using GEODEFSLib;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[ComImport]
[Guid("69E77C75-BB8D-46F9-BD93-1D26E09249DE")]
[InterfaceType(1)]
public interface IGeoDatum
{
[MethodImpl(MethodImplOptions.InternalCall)]
void InitDatum([In] eT_DatumTypes e_Datum);
...
}
Есть также Cnv, CnvClass и ICnv (и многие struct и enums), которые кажутся менее актуальными и не включены в данный момент.
Сказав все выше, я хотел бы добавить, что я также пытался вручную настроить системный реестр.Это было довольно хаотично и не принесло никаких улучшений.
Надеюсь, вы поможете мне запустить все четыре сценария.