之前都是用的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组件进行免注册开发的更多相关文章

  1. DELPHI下的SOCK编程(转)

    DELPHI下的SOCK编程      本文是写给公司新来的程序员的,算是一点培训的教材.本文不会涉及太多的编程细节,只是简单讲解在DELPHI下进行Winsock编程最好了解的知识. 题外话:我认为 ...

  2. DELPHI下API简述(1800个API)

    DELPHI下API简述 http://zero.cnbct.org/show.asp?id=144 auxGetDevCaps API 获取附属设备容量 auxGetNumDevs API 返回附属 ...

  3. APlayer组件自制播放器

    .NET中使用APlayer组件自制播放器 2015-02-02 09:46 by xiaozhi_5638, 402 阅读, 9 评论, 收藏, 编辑 目录 说明 APlayer介绍 APlayer ...

  4. DELPHI下的SOCK编程

     DELPHI下的SOCK编程(转自http://www.cnblogs.com/devcjq/articles/2325600.html) 本文是写给公司新来的程序员的,算是一点培训的教材.本文不会 ...

  5. 深入Delphi下的DLL编程

    深入Delphi下的DLL编程 作者:岑心 引 言 相信有些计算机知识的朋友都应该听说过“DLL”.尤其是那些使用过windows操作系统的人,都应该有过多次重装系统的“悲惨”经历——无论再怎样小心, ...

  6. Elite Container DELPHI下的一个轻量级IoC对象容器

    一.简介: Elite Container是DELPHI下的一个轻量级IoC对象容器(IoC:Inverse of Control,反转控制).它是参考了Java中的Spring框架(主要是配置文件的 ...

  7. asp.net下出现其中的组件“访问被拒绝”的解决方法

    一.一般情况下,对该组件重新授权即可.附上ASP,NETWORK SERVICE用户的可修改权限. 二.其中最常见的原因是Indexing service服务引起的.解决方法就是停用Indexing  ...

  8. Delphi下使用Oracle Access控件组下TOraSession控件链接

    Delphi下使用Oracle Access控件组下TOraSession控件链接数据库,使用  orsn1.Options.Direct:=true;  orsn1.Server:=IP:Port: ...

  9. DELPHI下读取与设置系统时钟

    在DELPHI下读取与设置系统时钟 很多朋友都想在自己的程序中显示系统时间 这在DELPHI中十分容易 利用DateToStr(Date)及TimeToStr(Time)函数即可实现. 二者的函数原型 ...

随机推荐

  1. 模态Model视图Push下一个视图(混合跳转)

    来自: http://www.cnblogs.com/dingding3w/p/6222626.html 如果没有UINavigationController导航栏页面之间切换是不能实现Push操作的 ...

  2. iOS性能之HTTP2.0

    在移动互联网领域蓬勃发展的今天,APP的性能也成为各大公司重点关注的方向,该系列文章主要针对iOS的性能的几个方面做一些研究. 什么是HTTP2.0? 网上很容易搜到关于HTTP2.0的概念的文章,这 ...

  3. Java 集合的理解(持续更新......)

    一.集合的由来 通常,我们的程序需要根据程序运行时才知道创建多少个对象.但若非程序运行,程序开发阶段,我们根本不知道到底需要多少个数量的对象,甚至不知道它的准确类型.为了满足这些常规的编程需要,我们要 ...

  4. wp8数据存储--独立存储文件 【转】

    出自 : http://www.cnblogs.com/MyBeN/p/3339019.html 文章篇幅有点大,建议去源网看看 1.调用手机的独立存储 例如:IsolatedStorageFile ...

  5. Cesium原理篇:3D Tiles(2)数据结构

    上一节介绍3D Tiles渲染调度的时候,我们提到目前Cesium支持的Cesium3DTileContent目前支持如下类型: Batched3DModel3DTileContent Instanc ...

  6. 开源中文分词工具探析(五):FNLP

    FNLP是由Fudan NLP实验室的邱锡鹏老师开源的一套Java写就的中文NLP工具包,提供诸如分词.词性标注.文本分类.依存句法分析等功能. [开源中文分词工具探析]系列: 中文分词工具探析(一) ...

  7. markown编辑器截图粘贴预览,并将图片传至七牛云

    最近在做一个项目,需要实现类似QQ截图后,就是能够在富文本编辑器中粘贴截图并预览. 先看一下效果: 分析一下实现步骤: QQ截图后在编辑器中粘贴,肯定会有一个粘贴事件,即 paste 事件 在事件回调 ...

  8. java实体属性对应mysql和SQL Server 和Oracle 数据类型对应

    1:Java数据类型与MySql数据类型对照表 类型名称 显示长度 数据库类型 JAVA类型 JDBC类型索引(int) VARCHAR L+N VARCHAR java.lang.String 12 ...

  9. 在调用相机后idleTimerDisabled失效的问题

    在调用相机后idleTimerDisabled失效的问题 相关资料: http://stackoverflow.com https://github.com/jamiemcd 问题 前几天有人在群里边 ...

  10. 配置uwsgi

    首先要明确的是,如果你喜欢用命令行的方式(如shell)敲命令,那可以省去任何配置. 但是,绝大多数人,还是不愿意记那么长的命令,反复敲的.所以uwsgi里,就给大家提供了多种配置,省去你启动时候,需 ...