Установка свойства nullable для объекта. NET - PullRequest
4 голосов
/ 21 апреля 2020

У меня есть некоторый. NET код взаимодействия, в котором мне удалось загрузить объекты и прочитать свойства, однако у меня возникли проблемы с установкой свойства объекта. Вот соответствующие части кода Delphi:

uses
  mscorlib_TLB, Winapi.ActiveX;

type
  // Irrelevant parts of the code omitted
  TDotNetObject = class(TObject)
  private
    FTarget: OleVariant;
    FType: _Type;
  public
    procedure SetProperty(const APropertyName: string; const AValue: OleVariant; const AIndex: Integer = -1);
  end;

function VariantToPSafeArray(const AValue: Variant): PSafeArray;
begin
  Result := PSafeArray(VarArrayAsPSafeArray(AValue));
end;

procedure TDotNetObject.SetProperty(const APropertyName: string; const AValue: OleVariant; const AIndex: Integer = -1);
var
  LPropertyInfo: _PropertyInfo;
  LIndex: PSafeArray;
begin
  if AIndex >= 0 then
    LIndex := VariantToPSafeArray(VarArrayOf([AIndex]))
  else
    LIndex := nil;
  LPropertyInfo := FType.GetProperty(APropertyName, BindingFlags_Instance or BindingFlags_Public or BindingFlags_NonPublic);
  if LPropertyInfo <> nil then
    LPropertyInfo.SetValue(FTarget, AValue, LIndex);
end;

procedure UpdateDefectStatus(const ADefectID, AStatus: Integer);
var
  LObject: TDotNetObject;
begin
  // ** Code to obtain the object omitted ***
  LObject.SetProperty('Status', AStatus);
end;

Блок mscorlib_TLB поставляется из JCL в проекте JEDI, здесь:

https://github.com/project-jedi/jcl/blob/master/jcl/source/windows/mscorlib_TLB.pas

Ошибка возникает при вызове LPropertyInfo.SetValue в TDotNetObject.SetProperty:

Project TestProject.exe raised exception class EOleException with message 'Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1[MTData.Transport.Tracking.DefectReporting.DefectStatus]''.

Свойство DefectStatus объекта C# объявляется как:

public DefectStatus? Status

( т. е. обнуляется)

Объявлен тип свойства Status в C#:

public enum DefectStatus
{
    /// <summary>
    /// Defect Reported.
    /// </summary>
    Reported,
    /// <summary>
    /// Defect assessed.
    /// </summary>
    Assessed,
    /// <summary>
    /// Defect on work order.
    /// </summary>
    OnWorkOrder,
    /// <summary>
    /// Defect closed.
    /// </summary>
    Closed
}

Я нашел решение, как справиться с этой ситуацией, используя C# здесь:

{ ссылка }

Однако я немного растерялся относительно того, как сделать то же самое в Delphi. Любые идеи?

РЕДАКТИРОВАТЬ

Учитывая ответ Оливье, я попытался написать некоторый Delphi код, чтобы сделать эквивалент, а именно:

procedure InvokeToObject;
var
  LType, LDefectStatusType: _Type;
  LInvokeFlags: TOleEnum;
  LArgs: PSafeArray;
  LValue: Integer;
  LResult: OleVariant;
  LRes: HRESULT;
  LResHex: string;
begin
  LType := MTDataClr.GetCoreType('System.Enum');
  LDefectStatusType := MTDataClr.GetType('MTData.Transport.Tracking.DefectReporting.DefectStatus');
  LInvokeFlags := BindingFlags_InvokeMethod or BindingFlags_Static;
  LValue := 1;
  LArgs := VariantToPSafeArray(VarArrayOf([LDefectStatusType, LValue]));
  LRes := LType.InvokeMember_2('ToObject', LInvokeFlags, nil, Null, LArgs, nil, LResult);
  LResHex := IntToHex(LRes);
end;

Цель этого фрагмента кода - просто вызвать метод ToObject типа Enum. Получение LType и LDefectStatusType завершается успешно, однако вызов InvokeMember_2 этого не делает, с кодом возврата: 0x80131512, который, по-видимому, является исключением пропавшего члена. Есть идеи, что я делаю не так?

1 Ответ

1 голос
/ 21 апреля 2020

Проблема в том, что перечисление не является целым числом, что означает, что SetValue() потребуется выполнить двойное преобразование (Int32 в DefectStatus и DefectStatus в DefectStatus?), чего не может (может только выполнить один).

Вот код C#, который воспроизводит то, что вы пытаетесь сделать:

using System;
using System.Reflection;

public enum DefectStatus
{
    Reported,
    Assessed,
    OnWorkOrder,
    Closed
}

public class Defect
{
    public DefectStatus? Status {get; set;}
}

public class Test
{
    public static void Main()
    {
        Defect def = new Defect();

        PropertyInfo pi = typeof(Defect).GetProperty("Status");

        // This throws an ArgumentException
//      pi.SetValue(def, 1, null);

        // Retrieve the Assessed enum via its numeric value
        object assessed = Enum.ToObject(typeof(DefectStatus), 1);

        // This works as expected
        pi.SetValue(def, assessed, null);

        Console.WriteLine(def.Status);
    }
}

Итак, вам нужно получить перечисление в Delphi. Для этого вам нужно поиграть с API, чтобы получить доступ к типу Enum и вызвать ToObject для него.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...