以前都是用的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組件,忒麻煩了。呵呵,免註冊真好!
三、播放