Как использовать 32-битную библиотеку COM в C # из не STA-потока и / или 64-битного процесса - PullRequest
0 голосов
/ 23 сентября 2019

Я пытаюсь использовать устаревшие компоненты COM (библиотека dll, созданная, вероятно, на C ++, 32-разрядная версия) в моем приложении C #.Мне нужно, чтобы он работал в следующих сценариях:

  1. COM-компонент, используемый в потоке x86 STA (например, основной поток приложения)
  2. COM-компонент, используемый в рабочем потоке x86 (например, task /пул потоков)
  3. COM-компонент, используемый в потоке x64 STA (например, основной поток приложения)
  4. 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), которые кажутся менее актуальными и не включены в данный момент.

Сказав все выше, я хотел бы добавить, что я также пытался вручную настроить системный реестр.Это было довольно хаотично и не принесло никаких улучшений.

Надеюсь, вы поможете мне запустить все четыре сценария.

...