2012-03-18 15 views
6

Próbuję przekonwertować następujące dwa interfejsy z pliku nagłówkowego C na jednostkę Delphi PAS, ale mam dziwne problemy z używaniem tych, które sam zrobiłem. Potrzebuję pomocy w zrozumieniu, jak je wdrożyć w Delphi.Konwersja interfejsu obiektu COM z C na Delphi

interfejsy źródłowego z pliku C nagłówka:

interface IParamConfig: IUnknown 
{   
    HRESULT SetValue([in] const VARIANT* pValue, [in] BOOL bSetAndCommit); 
    HRESULT GetValue([out] VARIANT* pValue, [in] BOOL bGetCommitted); 
    HRESULT SetVisible(BOOL bVisible); 
    HRESULT GetVisible(BOOL* bVisible); 
    HRESULT GetParamID(GUID* pParamID); 
    HRESULT GetName([out] BSTR* pName); 
    HRESULT GetReadOnly(BOOL* bReadOnly); 
    HRESULT GetFullInfo([out] VARIANT* pValue, [out] BSTR* pMeaning, [out] BSTR* pName, [out] BOOL* bReadOnly, [out] BOOL* pVisible); 
    HRESULT GetDefValue([out] VARIANT* pValue); 
    HRESULT GetValidRange([out] VARIANT* pMinValue, [out] VARIANT* pMaxValue, [out] VARIANT* pDelta); 
    HRESULT EnumValidValues([in][out] long* pNumValidValues, [in][out] VARIANT* pValidValues,[in][out] BSTR* pValueNames); 
    HRESULT ValueToMeaning([in] const VARIANT* pValue, [out] BSTR* pMeaning); 
    HRESULT MeaningToValue([in] const BSTR pMeaning, [out] VARIANT* pValue); 
} 

interface IModuleConfig: IPersistStream 
{ 
    HRESULT SetValue([in] const GUID* pParamID, [in] const VARIANT* pValue); 
    HRESULT GetValue([in] const GUID* pParamID, [out] VARIANT* pValue); 
    HRESULT GetParamConfig([in] const GUID* pParamID, [out] IParamConfig** pValue); 
    HRESULT IsSupported([in] const GUID* pParamID); 
    HRESULT SetDefState(); 
    HRESULT EnumParams([in][out] long* pNumParams, [in][out] GUID* pParamIDs); 
    HRESULT CommitChanges([out] VARIANT* pReason); 
    HRESULT DeclineChanges(); 
    HRESULT SaveToRegistry([in] HKEY hKeyRoot, [in] const BSTR pszKeyName, [in] const BOOL bPreferReadable); 
    HRESULT LoadFromRegistry([in] HKEY hKeyRoot, [in] const BSTR pszKeyName, [in] const BOOL bPreferReadable); 
    HRESULT RegisterForNotifies([in] IModuleCallback* pModuleCallback); 
    HRESULT UnregisterFromNotifies([in] IModuleCallback* pModuleCallback); 
} 

To jest mój "best effort" do tej pory:

type 
    TWideStringArray = array[0..1024] of WideString; 
    TOleVariantArray = array[0..1024] of OleVariant; 
    TGUIDArray = array[0..1024] of TGUID; 

    IParamConfig = interface(IUnknown) 
    ['{486F726E-5043-49B9-8A0C-C22A2B0524E8}'] 
    function SetValue(const pValue: OleVariant; bSetAndCommit: BOOL): HRESULT; stdcall; 
    function GetValue(out pValue: OleVariant; bGetCommitted: BOOL): HRESULT; stdcall; 
    function SetVisible(bVisible: BOOL): HRESULT; stdcall; 
    function GetVisible(bVisible: BOOL): HRESULT; stdcall; 
    function GetParamID(pParamID: PGUID): HRESULT; stdcall; 
    function GetName(out pName: WideString): HRESULT; stdcall; 
    function GetReadOnly(bReadOnly: BOOL): HRESULT; stdcall; 
    function GetFullInfo(out pValue: OleVariant; out pMeaning: WideString; out pName: WideString; out pReadOnly: BOOL; out pVisible: BOOL): HRESULT; stdcall; 
    function GetDefValue(out pValue: OleVariant): HRESULT; stdcall; 
    function GetValidRange(out pMinValue: OleVariant; out pMaxValue: OleVariant; out pDelta: OleVariant): HRESULT; stdcall; 
    function EnumValidValues(var pNumValidValues: Integer; var pValidValues: TOleVariantArray; var pValueNames: TWideStringArray): HRESULT; stdcall; 
    function ValueToMeading(const pValue: OleVariant; out pMeaning: WideString): HRESULT; stdcall; 
    function MeaningToValue(const pMeaning: WideString; out pValue: OleVariant): HRESULT; stdcall; 
    end; 

    IModuleConfig = interface(IPersistStream) 
    ['{486F726E-4D43-49B9-8A0C-C22A2B0524E8}'] 
    function SetValue(const pParamID: TGUID; const pValue: OleVariant): HRESULT; stdcall; 
    function GetValue(const pParamID: TGUID; out pValue: OleVariant): HRESULT; stdcall; 
    function GetParamConfig(const ParamID: TGUID; out pValue: IParamConfig): HRESULT; stdcall; 
    function IsSupported(const pParamID: TGUID): HRESULT; stdcall; 
    function SetDefState: HRESULT; stdcall; 
    function EnumParams(var pNumParams: Integer; var pParamIDs: TGUIDArray): HRESULT; stdcall; 
    function CommitChanges(out pReason: OleVariant): HRESULT; stdcall; 
    function DeclineChanges: HRESULT; stdcall; 
    function SaveToRegistry(hKeyRoot: HKEY; const pszKeyName: WideString; const bPreferReadable: BOOL): HRESULT; stdcall; 
    function LoadFromRegistry(hKeyRoot: HKEY; const pszKeyName: WideString; const bPreferReadable: BOOL): HRESULT; stdcall; 
    function RegisterForNotifies(pModuleCallback: IModuleCallback): HRESULT; stdcall; 
    function UnregisterFromNotifies(pModuleCallback: IModuleCallback): HRESULT; stdcall; 
    end; 

Oto przykładowy kod stosując filtr DirectShow i próbuje użyć zarówno Interfejsy IModuleConfig i IParamConfig dla tego obiektu:

procedure TForm10.Button1Click(Sender: TObject); 
const 
    CLSID_VideoDecoder: TGUID = '{C274FA78-1F05-4EBB-85A7-F89363B9B3EA}'; 
var 
    HR: HRESULT; 
    Intf: IUnknown; 
    NumParams: Long; 
    I: Integer; 
    ParamConfig: IParamConfig; 
    ParamName: WideString; 
    Value: OleVariant; 
    ValAsString: String; 
    Params: TGUIDArray; 
begin 
    CoInitializeEx(nil, COINIT_MULTITHREADED); 
    try 
    HR := CoCreateInstance(CLSID_VideoDecoder, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, Intf); 
    if Succeeded(HR) then 
    begin 
     FVideoDecoder := Intf as IBaseFilter; 

     if Supports(FVideoDecoder, IID_IModuleConfig) then 
     begin 
     HR := (FVideoDecoder as IModuleConfig).EnumParams(NumParams, Params); 
     if HR = S_OK then 
     begin 
      for I := 0 to NumParams - 1 do 
      begin 
      HR := (FVideoDecoder as IModuleConfig).GetParamConfig(Params[I], ParamConfig); 
      if HR = S_OK then 
      begin 
       try 
       ParamConfig.GetName(ParamName); 
       ParamConfig.GetValue(Value, True); 
       try 
        ValAsString := VarToStrDef(Value, 'Error'); 
        SL.Add(String(ParamName) + '=' + String(ValAsString)); // <-- ADDING THIS LINE WILL ALWAYS MAKE EnumParams call return S_FALSE = 1 
       except 
       end; 
       finally 
       ParamConfig := nil; 
       end; 
      end; 
      end; 
     end; 
     end; 
    end; 
    finally 
    CoUninitialize; 
    end; 
end; 

Używanie debuggera I można zobaczyć, że kod przykładowy pobiera dane zarówno do zmiennych ParamName, jak i Value, jednak gdy spróbuję użyć kodu include do zapisania ich na liście napisów (SL), wywołanie do EnumParams zawsze zwróci S_FALSE (1), a nie S_OK (0). Jeśli skomentuję linię SL.Add (...) i RECOMPILE, będzie działać ponownie. Jeśli uwzględnię to ponownie i RECOMPILE, nie będzie. To prowadzi mnie do przekonania, że ​​coś zepsuło pamięć w pewnym momencie z powodu mojej nieprawidłowej implementacji tych interfejsów, a włączenie dodatkowego kodu sprawia, że ​​tak się dzieje.

Jestem prawie pewien, że typy, które przypisałem do zmiennych, są w jakiś sposób winowajcą tego, zwłaszcza drugi parametr do EnumParams, który ma zwrócić tablicę GUID *. Jestem również bardzo niepewny co do wywołania IParamConfig.EnumValidValues, które zwraca również tablice wartości.

Używam Delphi XE2.

Każda pomoc w tej sprawie jest bardzo doceniana.

Odpowiedz

2

Aby odpowiedzieć na to pytanie, należy koniecznie posiadać dokumentację interfejsów. Znajomość ich podpisów nigdy nie jest wystarczającą informacją. Bez tej dokumentacji musimy zgadnąć domysły i tak to się dzieje.

Niech najpierw skupić się na EnumParams

HRESULT EnumParams([in][out] long* pNumParams, [in][out] GUID* pParamIDs); 

Zauważ, że parametr pNumParams jest oznaczony jako zarówno [in] i [out]. Drugi parametr to tablica identyfikatorów GUID. Najprawdopodobniej masz zamiar przekazać długość tablicy jako dane wejściowe za pomocą parametru pNumParams. Informuje o tym, ile elementów może bezpiecznie kopiować. Jeśli przekażesz wartość dla pNumParams, która jest niewystarczająca dla całej tablicy, funkcja pokaże to w zwracanej wartości. Gdy funkcja zwróci wartość, ustawi ona pNumParams na rzeczywistą długość tablicy. Najprawdopodobniej możesz nazwać to przekazywaniem 0 dla pNumParams, NULL dla pParamIDs i użyć go do określenia rozmiaru rzeczywiście potrzebnej macierzy. Jest to bardzo powszechny wzorzec, ale musisz mieć pewność, że przeczytasz dokumentację.

Teraz, ponieważ nie przypisujesz do NumParams przed wywołaniem EnumParams, przekazujesz losową wartość ze stosu. Fakt, że zmiany w kodzie w dalszej kolejności wpływają na sposób, w jaki wywołuje ono funkcję EnumParams, zdecydowanie popiera tę hipotezę.

Po wdrożeniu i zakładając, że zgaduję, że jest prawidłowa, przed wywołaniem EnumParams należy ustawić NumParams na 1025. Jednak prawdopodobnie uniknęłbym korzystania z tablic o stałym rozmiarze i alokacji dynamicznych tablic. Musisz zmienić definicję EnumParams, aby pobrać wskaźnik do pierwszego elementu. Zrobiłbym to dla wszystkich tablic w interfejsie.

Poza tym zauważyłem, że miałeś kilka błędów w IParamConfig. Funkcja GetVisible powinno być tak:

function GetVisible(var bVisible: BOOL): HRESULT; stdcall; 

a znajdziecie GetParamID wygodniejszy napisany w ten sposób:

function GetParamID(var pParamID: TGUID): HRESULT; stdcall; 
+0

Dziękuję David! Korzystając z informacji z Twojego postu udało mi się poprawnie zaimplementować interfejs. Posiadam dokumentację tego obiektu, ale niestety jest on chroniony prawem autorskim, więc nie mogę go opublikować tutaj. – TomRay74

+0

Cieszę się, że mogę pomóc –

0

Dla przypomnienia, jest to zakończone interfejs:

IParamConfig = interface(IUnknown) 
    ['{486F726E-5043-49B9-8A0C-C22A2B0524E8}'] 
    function SetValue(const pValue: OleVariant; bSetAndCommit: BOOL): HRESULT; stdcall; 
    function GetValue(out pValue: OleVariant; bGetCommitted: BOOL): HRESULT; stdcall; 
    function SetVisible(bVisible: BOOL): HRESULT; stdcall; 
    function GetVisible(var bVisible: BOOL): HRESULT; stdcall; 
    function GetParamID(out pParamID: TGUID): HRESULT; stdcall; 
    function GetName(out pName: WideString): HRESULT; stdcall; 
    function GetReadOnly(bReadOnly: BOOL): HRESULT; stdcall; 
    function GetFullInfo(out pValue: OleVariant; out pMeaning: WideString; out pName: WideString; out pReadOnly: BOOL; out pVisible: BOOL): HRESULT; stdcall; 
    function GetDefValue(out pValue: OleVariant): HRESULT; stdcall; 
    function GetValidRange(out pMinValue: OleVariant; out pMaxValue: OleVariant; out pDelta: OleVariant): HRESULT; stdcall; 
    function EnumValidValues(pNumValidValues: PInteger; pValidValues: POleVariant; pValueNames: PWideString): HRESULT; stdcall; 
    function ValueToMeaning(const pValue: OleVariant; out pMeaning: WideString): HRESULT; stdcall; 
    function MeaningToValue(const pMeaning: WideString; out pValue: OleVariant): HRESULT; stdcall; 
    end; 

    IModuleConfig = interface(IPersistStream) 
    ['{486F726E-4D43-49B9-8A0C-C22A2B0524E8}'] 
    function SetValue(const pParamID: TGUID; const pValue: OleVariant): HRESULT; stdcall; 
    function GetValue(const pParamID: TGUID; out pValue: OleVariant): HRESULT; stdcall; 
    function GetParamConfig(const ParamID: TGUID; out pValue: IParamConfig): HRESULT; stdcall; 
    function IsSupported(const pParamID: TGUID): HRESULT; stdcall; 
    function SetDefState: HRESULT; stdcall; 
    function EnumParams(var pNumParams: Integer; pParamIDs: PGUID): HRESULT; stdcall; 
    function CommitChanges(out pReason: OleVariant): HRESULT; stdcall; 
    function DeclineChanges: HRESULT; stdcall; 
    function SaveToRegistry(hKeyRoot: HKEY; const pszKeyName: WideString; const bPreferReadable: BOOL): HRESULT; stdcall; 
    function LoadFromRegistry(hKeyRoot: HKEY; const pszKeyName: WideString; const bPreferReadable: BOOL): HRESULT; stdcall; 
    function RegisterForNotifies(pModuleCallback: IModuleCallback): HRESULT; stdcall; 
    function UnregisterFromNotifies(pModuleCallback: IModuleCallback): HRESULT; stdcall; 
    end; 

Poniższy kod pokazuje, w jaki sposób zadzwonić i korzystać z interfejsu i zadzwonić do EnumParams:

procedure TForm10.ListAllParameters(Sender: TObject); 
const 
    CLSID_VideoDecoder: TGUID = '{C274FA78-1F05-4EBB-85A7-F89363B9B3EA}'; 
var 
    HR: HRESULT; 
    Intf: IUnknown; 
    ModuleConfig: IModuleConfig; 
    ParamConfig: IParamConfig; 
    NumParams: Integer; 
    ParamGUIDS: array of TGUID; 
    GUID: TGUID; 
begin 
    HR := CoCreateInstance(CLSID_VideoDecoder, nil, CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, Intf); 
    try 
    if not Succeeded(HR) then Exit; 

    if Supports(Intf, IID_IModuleConfig) then ModuleConfig := (Intf as IModuleConfig) else Exit; 

    // Get number of parameters 
    NumParams := 0; 
    HR := ModuleConfig.EnumParams(NumParams, nil); 
    if HR = S_FALSE then 
    begin 
     // Set the lenght of the array of TGUIDS to match the number of parameters 
     SetLength(ParamGUIDS, NumParams); 
     // Use a pointer to the first TGUID of the array as the parameter to EnumParams 
     HR := ModuleConfig.EnumParams(NumParams, @ParamGUIDS[0]); 
     if HR = S_OK then 
     begin 
     for GUID in ParamGUIDS do Memo1.Lines.Add(GUIDToString(GUID)); 
     end else Exit; 
    end else Exit; 
    finally 
    ModuleConfig := nil; 
    Intf := nil; 
    end; 
end; 

Jeśli ktoś zauważy jakiekolwiek błędy (jeszcze nie wypróbowałem wszystkich funkcji), skomentuj ten wpis.

+0

To wciąż cierpi z powodu podstawowego błędu, który opisałem w mojej odpowiedzi. Musisz zainicjować NumParams zanim go przekażesz. I nie poprawiłeś GetVisible. Również parametr var jest lepszy dla parametru NumOfParams niż przekazywanie wskaźnika. –

+0

Witaj David, dzięki za komentarz. Zaktualizowałem mój post, aby uwzględnić Twoje komentarze. Rozumiem teraz, dlaczego używanie var zamiast przekazywania wskaźnika jest lepsze. Dziękuję Ci! – TomRay74

+0

Wygląda bardziej na to! –

Powiązane problemy