在Delphi下使用迅雷APlayer組件進行免註冊開發

    以前都是用的delphi下的dspack進行的視頻開發,這個組件其實很好用,就是找解碼器麻煩點,並且還得在客戶的計算機上使用RegSvr32.exe也註冊解碼器,要不有可能播放不了。c++

    結果在查找合適的解碼器過程當中,無心搜索到了迅雷的APlayer組件web

    迅雷APlayer這個組件提供了一個完整的解碼器合集(核心的流媒體播放技術也是DirectShow和dspack同樣同樣的),下載APlayer的解碼器合集並註冊到系統後,確實在dspack也用的挺好,不過看了APlayer的介紹後發現人家作的更好,雖然是個ActiveX,可是給出的c++示例表示無需顯式註冊便可使用(就是不須要用Regsvr32.exe預先註冊APlayer組件到目標計算機上),並且也無需預先註冊解碼器(也是Regsvr32)到操做系統,只要指定解碼器路徑,APlayer能夠自行搜索此路徑查找合適的解碼器,簡直太好了,原本就怕發佈到客戶計算機上後因爲解碼器問題致使播放不正常(其實開發測試階段已經出現過了),這麼個好東西趕快試試。api

 

    第一次使用先按照Delphi下的傳統方式來,在開發環境中引入APlayer組件,這個就是個ActiveX控件,添加到組件面板上,建個工程拖到窗體上,響應幾個事件,輕輕鬆鬆視頻就開始播放了,呵呵,也不用關心解碼器文件缺不缺了,APlayer組件會查找並指示出來缺乏的文件,真是太智能了,省心,好用。網絡

 

    接下來晉級操做怎麼不註冊APlayer.dll就能直接建立ActiveX組件在本身的程序裏面呢?看APlayer的示例工程定義了兩個函數(BOOL CreateAPlayerFromFile(void)、HRESULT CreateInstanceFromFile(const TCHAR * pcszPath, REFCLSID rclsid, REFIID riid, IUnknown * pUnkOuter, LPVOID * ppv)),直接經過APlayer.dll就建立了ActiveX組件,不過那個示例工程是C++的,我們不熟,對照着改了下,沒搞定,因而求助萬能的網絡搜索引擎,目標:Delphi不註冊COM直接使用ActiveX控件並綁定事件,呵呵,感謝前輩們,果真有啊,原文章連接:http://blog.csdn.net/love3s/article/details/7411757ide

 

照着來吧,按照這位前輩的話,文筆很差直接上代碼吧:函數

 

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.OleCtnrs, System.Win.ComObj, EventSink, Winapi.ActiveX,
  Vcl.ExtCtrls, Vcl.StdCtrls;

const
  CLASS_Player: TGUID = '{A9332148-C691-4B9D-91FC-B9C461DBE9DD}';

type
  PIUnknown = ^IUnknown;
  TAtlAxAttachControl = function(Control: IUnknown; hwind: hwnd; ppUnkContainer: PIUnknown): HRESULT; stdcall;

  _IPlayerEvents = dispinterface
    ['{31D6469C-1DA7-47C0-91F9-38F0C39F9B89}']
    {
    function OnMessage(nMessage: Integer; wParam: Integer; lParam: Integer): HResult; dispid 1;
    function OnStateChanged(nOldState: Integer; nNewState: Integer): HResult; dispid 2;
    function OnOpenSucceeded: HResult; dispid 3;
    function OnSeekCompleted(nPosition: Integer): HResult; dispid 4;
    function OnBuffer(nPercent: Integer): HResult; dispid 5;
    function OnVideoSizeChanged: HResult; dispid 6;
    function OnDownloadCodec(const strCodecPath: WideString): HResult; dispid 7;
    function OnEvent(nEventCode: Integer; nEventParam: Integer): HResult; dispid 8;
    }
  end;

  TfrmMain = class(TForm)
    pnlCom: TPanel;
    btnOpen: TButton;
    dlgOpen1: TOpenDialog;
    btnPath: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure btnPathClick(Sender: TObject);
  private
    { Private declarations }
    APlayer: Variant;
    APlayerCreateSuccess: Boolean;
    EventSink: TEventSink;
    function InitAPlayer: Boolean;
    function CreateComObjectFromDll(CLSID: TGUID; DllHandle: THandle): IUnknown;
    procedure EventSinkInvoke(Sender: TObject; DispID: Integer;
      const IID: TGUID; LocaleID: Integer; Flags: Word;
      Params: tagDISPPARAMS; VarResult, ExcepInfo, ArgErr: Pointer);
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

{ TForm1 }

procedure TfrmMain.btnOpenClick(Sender: TObject);
begin
  if not APlayerCreateSuccess then Exit;

  if dlgOpen1.Execute(Handle) then
  begin
    APlayer.Open(dlgOpen1.FileName);
  end;
end;

procedure TfrmMain.btnPathClick(Sender: TObject);
begin
  if not APlayerCreateSuccess then Exit;
  ShowMessage(APlayer.GetConfig(2));
end;

function TfrmMain.CreateComObjectFromDll(CLSID: TGUID;
  DllHandle: THandle): IUnknown;
var
  Factory: IClassFactory;
  DllGetClassObject: function(const CLSID, IID: TGUID; var Obj): HResult; stdcall;
  hr: HRESULT;
begin
  DllGetClassObject := GetProcAddress(DllHandle, 'DllGetClassObject');
  if Assigned(DllGetClassObject) then
  begin
    hr := DllGetClassObject(CLSID, IClassFactory, Factory);
    if hr = S_OK then
    try
      hr := Factory.CreateInstance(nil, IUnknown, Result);
      if hr <> S_OK then
      begin
        MessageBox(Handle, '建立APlayer實例失敗!', '錯誤', MB_OK + MB_ICONERROR);
      end;
    except
      MessageBox(Handle, PChar('建立APlayer實例失敗!錯誤代碼:' + IntToStr(GetLastError)), '錯誤', MB_OK + MB_ICONERROR);
    end;
  end;
end;

procedure TfrmMain.EventSinkInvoke(Sender: TObject; DispID: Integer;
  const IID: TGUID; LocaleID: Integer; Flags: Word; Params: tagDISPPARAMS;
  VarResult, ExcepInfo, ArgErr: Pointer);
var
  ov: OleVariant;
begin
  {
    這裏須要註明Params這個參數, 包含了事件的參數
    如:
    Params.rgvarg[0] 表明第一個參數
    Params.rgvarg[1] 表明第二個參數
    ......
    Params.rgvarg[65535] 表明第65535個參數
    最多65535個參數
    具體能夠參考 tagDISPPARAMS 的定義
  }
  case dispid of
    // function OnMessage(nMessage: Integer; wParam: Integer; lParam: Integer): HResult; dispid 1;
    $00000001:
    begin

    end;
    // function OnStateChanged(nOldState: Integer; nNewState: Integer): HResult; dispid 2;
    $00000002:
    begin

    end;
    // function OnOpenSucceeded: HResult; dispid 3;
    $00000003:
    begin

    end;
    // function OnSeekCompleted(nPosition: Integer): HResult; dispid 4;
    $00000004:
    begin

    end;
    // function OnBuffer(nPercent: Integer): HResult; dispid 5;
    $00000005:
    begin

    end;
    // function OnVideoSizeChanged: HResult; dispid 6;
    $00000006:
    begin

    end;
    // function OnDownloadCodec(const strCodecPath: WideString): HResult; dispid 7;
    $00000007:
    begin
      ov := OleVariant(Params.rgvarg[0]);
      MessageBox(Handle, PChar('缺乏解碼器文件:' + VarToStr(ov)), '錯誤', MB_OK + MB_ICONERROR);
    end;
    // function OnEvent(nEventCode: Integer; nEventParam: Integer): HResult; dispid 8;
    $00000008:
    begin

    end;
  end
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  ReportMemoryLeaksOnShutdown := DebugHook <> 0;
  APlayerCreateSuccess := InitAPlayer;
end;

function TfrmMain.InitAPlayer: Boolean;
var
  hModule, hDll: THandle;
  AtlAxAttachControl: TAtlAxAttachControl;
begin
  hModule := LoadLibrary('atl.dll');
  if hModule < 32 then
  begin
    Exit(False);
  end;
  AtlAxAttachControl := TAtlAxAttachControl(GetProcAddress(hModule, 'AtlAxAttachControl'));
  EventSink := TEventSink.Create(Self);
  EventSink.OnInvoke := EventSinkInvoke;
  if not Assigned(AtlAxAttachControl) then
    Exit(False);
  try
    hDll := LoadLibrary('APlayer.dll');
    APlayer := CreateComObjectFromDll(CLASS_Player, hDll) as IDispatch;
    if VarIsNull(APlayer) then
    begin
      Exit(False);
    end;
    EventSink.Connect(APlayer, _IPlayerEvents);
    AtlAxAttachControl(APlayer, pnlCom.Handle, nil);

    Result := True;
  except
    Result := False;
  end;
end;

end.


接下來EventSink單元代碼(綁定ActiveX控件事件用的):測試

 

unit EventSink;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Winapi.ActiveX;

type
  TInvokeEvent = procedure(Sender: TObject; DispID: Integer; const IID: TGUID;
    LocaleID: Integer; Flags: Word; Params: TDispParams;
    VarResult, ExcepInfo, ArgErr: Pointer) of object;

  TAbstractEventSink = class(TObject, IUnknown, IDispatch)
  private
    FDispatch: IDispatch;
    FDispIntfIID: TGUID;
    FConnection: LongInt;
    FOwner: TComponent;
  protected
    { IUnknown }
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    { IDispatch }
    function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
    function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo)
      : HRESULT; stdcall;
    function GetIDsOfNames(const IID: TGUID; Names: Pointer;
      NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
    function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer)
      : HRESULT; stdcall;
  public
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
    procedure Connect(AnAppDispatch: IDispatch; const AnAppDispIntfIID: TGUID);
    procedure Disconnect;
  end;

  TEventSink = class(TComponent)
  private
    { Private declarations }
    FSink: TAbstractEventSink;
    FOnInvoke: TInvokeEvent;
  protected
    { Protected declarations }
    procedure DoInvoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
      Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer); virtual;
  public
    { Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure Connect(AnAppDispatch: IDispatch; const AnAppDispIntfIID: TGUID);
  published
    { Published declarations }
    property OnInvoke: TInvokeEvent read FOnInvoke write FOnInvoke;
  end;

implementation

uses
  ComObj;

procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;
  const Sink: IUnknown; var Connection: LongInt);
var
  CPC: IConnectionPointContainer;
  CP: IConnectionPoint;
  i: HRESULT;
begin
  Connection := 0;
  if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
    if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
      i := CP.Advise(Sink, Connection);
end;

procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;
  var Connection: LongInt);
var
  CPC: IConnectionPointContainer;
  CP: IConnectionPoint;
begin
  if Connection <> 0 then
    if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
      if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
        if Succeeded(CP.Unadvise(Connection)) then
          Connection := 0;
end;

{ TAbstractEventSink }

function TAbstractEventSink._AddRef: Integer; stdcall;
begin
  Result := 2;
end;

function TAbstractEventSink._Release: Integer; stdcall;
begin
  Result := 1;
end;

constructor TAbstractEventSink.Create(AOwner: TComponent);
begin
  inherited Create;
  FOwner := AOwner;
end;

destructor TAbstractEventSink.Destroy;
var
  p: Pointer;
begin
  Disconnect;

  inherited Destroy;
end;

function TAbstractEventSink.GetIDsOfNames(const IID: TGUID; Names: Pointer;
  NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAbstractEventSink.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo)
  : HRESULT; stdcall;
begin
  Result := E_NOTIMPL;
end;

function TAbstractEventSink.GetTypeInfoCount(out Count: Integer)
  : HRESULT; stdcall;
begin
  Count := 0;
  Result := S_OK;
end;

function TAbstractEventSink.Invoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params;
  VarResult, ExcepInfo, ArgErr: Pointer): HRESULT; stdcall;
begin
  (FOwner as TEventSink).DoInvoke(DispID, IID, LocaleID, Flags, Params,
    VarResult, ExcepInfo, ArgErr);
  Result := S_OK;
end;

function TAbstractEventSink.QueryInterface(const IID: TGUID; out Obj)
  : HRESULT; stdcall;
begin
  // We need to return the event interface when it's asked for
  Result := E_NOINTERFACE;
  if GetInterface(IID, Obj) then
    Result := S_OK;
  if IsEqualGUID(IID, FDispIntfIID) and GetInterface(IDispatch, Obj) then
    Result := S_OK;
end;

procedure TAbstractEventSink.Connect(AnAppDispatch: IDispatch;
  const AnAppDispIntfIID: TGUID);
begin
  FDispIntfIID := AnAppDispIntfIID;
  FDispatch := AnAppDispatch;
  // Hook the sink up to the automation server
  InterfaceConnect(FDispatch, FDispIntfIID, Self, FConnection);
end;

procedure TAbstractEventSink.Disconnect;
begin
  if Assigned(FDispatch) then
  begin
    // Unhook the sink from the automation server
    InterfaceDisconnect(FDispatch, FDispIntfIID, FConnection);
    FDispatch := nil;
    FConnection := 0;
  end;
end;

{ TEventSink }

procedure TEventSink.Connect(AnAppDispatch: IDispatch;
  const AnAppDispIntfIID: TGUID);
begin
  FSink.Connect(AnAppDispatch, AnAppDispIntfIID);
end;

constructor TEventSink.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  FSink := TAbstractEventSink.Create(Self);
end;

destructor TEventSink.Destroy;
begin
  FSink.Free;

  inherited Destroy;
end;

procedure TEventSink.DoInvoke(DispID: Integer; const IID: TGUID;
  LocaleID: Integer; Flags: Word; var Params;
  VarResult, ExcepInfo, ArgErr: Pointer);
begin
  if Assigned(FOnInvoke) then
    FOnInvoke(Self, DispID, IID, LocaleID, Flags, TDispParams(Params),
      VarResult, ExcepInfo, ArgErr);
end;

end.


循着前輩的腳步果真很容易並順利的解決了問題,我在APlayer論壇看有人問怎麼在Delphi下也能夠免註冊使用APlayer組件呢,呵呵,如今有答案了!並且咱們掌握了一個重要的Delphi技能「Delphi不註冊COM直接使用ActiveX控件並綁定事件」,開心!特此記錄。搜索引擎

 

後附程序執行的截圖:spa

一、程序設計界面,只是放置了兩個按鈕、一個OpenDialog、一個Panel(做爲APlayer組件的容器)。操作系統

二、程序運行後,能夠看到APlayer組件成功建立到了Panel上,讀取APlayer的解碼器路徑,和APlayer.dll在同一目錄下,若是用的註冊ActiveX的方式並拖拽到窗體上進行開發的,本身試試就會發現解碼器路徑固定在「C:\Users\Public\Thunder Network\APlayer」且沒法修改。若是解碼器路徑固定了會致使在客戶端計算機部署時更復雜些,不如在本地目錄方便,何況還得在客戶計算機上註冊APlayer組件,忒麻煩了。呵呵,免註冊真好!

三、播放

相關文章
相關標籤/搜索