在Delphi下使用迅雷APlayer组件进行免注册开发
之前都是用的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());
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;
$:
begin end;
// function OnStateChanged(nOldState: Integer; nNewState: Integer): HResult; dispid 2;
$:
begin end;
// function OnOpenSucceeded: HResult; dispid 3;
$:
begin end;
// function OnSeekCompleted(nPosition: Integer): HResult; dispid 4;
$:
begin end;
// function OnBuffer(nPercent: Integer): HResult; dispid 5;
$:
begin end;
// function OnVideoSizeChanged: HResult; dispid 6;
$:
begin end;
// function OnDownloadCodec(const strCodecPath: WideString): HResult; dispid 7;
$:
begin
ov := OleVariant(Params.rgvarg[]);
MessageBox(Handle, PChar('缺少解码器文件:' + VarToStr(ov)), '错误', MB_OK + MB_ICONERROR);
end;
// function OnEvent(nEventCode: Integer; nEventParam: Integer): HResult; dispid 8;
$:
begin end;
end
end; procedure TfrmMain.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := DebugHook <> ;
APlayerCreateSuccess := InitAPlayer;
end; function TfrmMain.InitAPlayer: Boolean;
var
hModule, hDll: THandle;
AtlAxAttachControl: TAtlAxAttachControl;
begin
hModule := LoadLibrary('atl.dll');
if hModule < 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 := ;
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 <> then
if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) then
if Succeeded(CPC.FindConnectionPoint(IID, CP)) then
if Succeeded(CP.Unadvise(Connection)) then
Connection := ;
end; { TAbstractEventSink } function TAbstractEventSink._AddRef: Integer; stdcall;
begin
Result := ;
end; function TAbstractEventSink._Release: Integer; stdcall;
begin
Result := ;
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 := ;
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 := ;
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、播放

在Delphi下使用迅雷APlayer组件进行免注册开发的更多相关文章
- DELPHI下的SOCK编程(转)
DELPHI下的SOCK编程 本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为 ...
- DELPHI下API简述(1800个API)
DELPHI下API简述 http://zero.cnbct.org/show.asp?id=144 auxGetDevCaps API 获取附属设备容量 auxGetNumDevs API 返回附属 ...
- APlayer组件自制播放器
.NET中使用APlayer组件自制播放器 2015-02-02 09:46 by xiaozhi_5638, 402 阅读, 9 评论, 收藏, 编辑 目录 说明 APlayer介绍 APlayer ...
- DELPHI下的SOCK编程
DELPHI下的SOCK编程(转自http://www.cnblogs.com/devcjq/articles/2325600.html) 本文是写给公司新来的程序员的,算是一点培训的教材.本文不会 ...
- 深入Delphi下的DLL编程
深入Delphi下的DLL编程 作者:岑心 引 言 相信有些计算机知识的朋友都应该听说过“DLL”.尤其是那些使用过windows操作系统的人,都应该有过多次重装系统的“悲惨”经历——无论再怎样小心, ...
- Elite Container DELPHI下的一个轻量级IoC对象容器
一.简介: Elite Container是DELPHI下的一个轻量级IoC对象容器(IoC:Inverse of Control,反转控制).它是参考了Java中的Spring框架(主要是配置文件的 ...
- asp.net下出现其中的组件“访问被拒绝”的解决方法
一.一般情况下,对该组件重新授权即可.附上ASP,NETWORK SERVICE用户的可修改权限. 二.其中最常见的原因是Indexing service服务引起的.解决方法就是停用Indexing ...
- Delphi下使用Oracle Access控件组下TOraSession控件链接
Delphi下使用Oracle Access控件组下TOraSession控件链接数据库,使用 orsn1.Options.Direct:=true; orsn1.Server:=IP:Port: ...
- DELPHI下读取与设置系统时钟
在DELPHI下读取与设置系统时钟 很多朋友都想在自己的程序中显示系统时间 这在DELPHI中十分容易 利用DateToStr(Date)及TimeToStr(Time)函数即可实现. 二者的函数原型 ...
随机推荐
- Oracle物化视图,物化视图日志,增量刷新同步远程数据库
1.创建DBLINK -- Drop existing database link drop public database link LQPVPUB; -- Create database link ...
- Thrift序列化与反序列化的实现机制分析
Thrift是如何实现序死化与反序列化的,在IDL文件中,更改IDL文件中的变量序号或者[使用默认序号的情况下,新增变量时,将新增的变量不放在IDL文件的结尾,均会导致Thrift文件的反序列后无法做 ...
- 在x64位的操作系统客户端如何连接oracle ?
好久没更新博客了,上周在x64位的操作系统中安装好了32位或64位的oracle 11g客户端,但用SSIS或Microsoft SQL Server 2012报表生成器3.0去连接oracle 11 ...
- Memcached十问十答
1.Memcached是什么,有什么作用? Memcached是一种纯内存的,key-value,CS架构的数据库服务软件,主要用于数据库,web服务器的缓存,以减小数据库,web服务器的访问压力,尤 ...
- 第一章:shiro简介
1.1 简介 Apache Shiro是java的一个安全框架,相当简单,没有Spring Security功能强大,但是实际工作中大多使用shiro就够了.可以帮助我们完成:认证,授权,加密,会话管 ...
- Django发送带图片和附件的邮件
最近需要做集团的SRC系统.暂无安全研发,所以只能找我这个小菜兼职开发.系统使用Django框架,在整个过程中,有许多奇特的需求.在某项需求中,需要给厂商用户发送富文本邮件,漏洞详情,这个折腾了一下, ...
- 机器学习:R语言中如何使用最小二乘法
详细内容见上一篇文章:http://www.cnblogs.com/lc1217/p/6514734.html 这里只是介绍下R语言中如何使用最小二乘法解决一次函数的线性回归问题. 代码如下:(数据同 ...
- SQL case when 的使用总结
在网上看到一篇关于case when语句的博客,写得很好,我这里是摘录的,还有我的一些体会,原博客地址:SQL Case when 的使用方法. Case具有两种格式.简单Case函数和Case搜索函 ...
- 老李分享:持续集成学好jenkins之Git和Maven配置
老李分享:持续集成学好jenkins之Git和Maven配置 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣 ...
- 老李推荐:第5章1节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 官方简介
老李推荐:第5章1节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 官方简介 在MonkeyRunner的框架中,Monkey是作为一个服务来接受来自Monkey ...