您的位置:首页 > 编程语言 > Delphi

在Delphi下使用迅雷APlayer组件进行免注册开发

2017-04-27 16:57 489 查看
之前都是用的delphi下的dspack进行的视频开发,这个组件其实很好用,就是找解码器麻烦点,而且还得在客户的计算机上使用RegSvr32.exe也注册解码器,要不有可能播放不了。

结果在查找合适的解码器过程中,无意搜索到了迅雷的APlayer组件

迅雷APlayer这个组件提供了一个完整的解码器合集(核心的流媒体播放技术也是DirectShow和dspack一样一样的),下载APlayer的解码器合集并注册到系统后,确实在dspack也用的挺好,不过看了APlayer的介绍后发现人家做的更好,虽然是个ActiveX,但是给出的c++示例表示无需显式注册即可使用(就是不需要用Regsvr32.exe预先注册APlayer组件到目标计算机上),而且也无需预先注册解码器(也是Regsvr32)到操作系统,只要指定解码器路径,APlayer可以自行搜索此路径查找合适的解码器,简直太好了,本来就怕发布到客户计算机上后由于解码器问题导致播放不正常(其实开发测试阶段已经出现过了),这么个好东西赶快试试。

第一次使用先按照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/7411757

照着来吧,按照这位前辈的话,文笔不好直接上代码吧:

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控件并绑定事件”,开心!特此记录。

后附程序执行的截图:

1、程序设计界面,只是放置了两个按钮、一个OpenDialog、一个Panel(作为APlayer组件的容器)。



2、程序运行后,可以看到APlayer组件成功创建到了Panel上,读取APlayer的解码器路径,和APlayer.dll在同一目录下,如果用的注册ActiveX的方式并拖拽到窗体上进行开发的,自己试试就会发现解码器路径固定在“C:\Users\Public\Thunder Network\APlayer”且无法修改。如果解码器路径固定了会导致在客户端计算机部署时更复杂些,不如在本地目录方便,况且还得在客户计算机上注册APlayer组件,忒麻烦了。呵呵,免注册真好!



3、播放

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: