之前都是用的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;interfaceusesWinapi.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;constCLASS_Player: TGUID = '{A9332148-C691-4B9D-91FC-B9C461DBE9DD}';typePIUnknown = ^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;varfrmMain: TfrmMain;implementation{$R *.dfm}{ TForm1 }procedure TfrmMain.btnOpenClick(Sender: TObject);
beginif not APlayerCreateSuccess then Exit;if dlgOpen1.Execute(Handle) thenbeginAPlayer.Open(dlgOpen1.FileName);end;
end;procedure TfrmMain.btnPathClick(Sender: TObject);
beginif not APlayerCreateSuccess then Exit;ShowMessage(APlayer.GetConfig(2));
end;function TfrmMain.CreateComObjectFromDll(CLSID: TGUID;DllHandle: THandle): IUnknown;
varFactory: IClassFactory;DllGetClassObject: function(const CLSID, IID: TGUID; var Obj): HResult; stdcall;hr: HRESULT;
beginDllGetClassObject := GetProcAddress(DllHandle, 'DllGetClassObject');if Assigned(DllGetClassObject) thenbeginhr := DllGetClassObject(CLSID, IClassFactory, Factory);if hr = S_OK thentryhr := Factory.CreateInstance(nil, IUnknown, Result);if hr <> S_OK thenbeginMessageBox(Handle, '创建APlayer实例失败!', '错误', MB_OK + MB_ICONERROR);end;exceptMessageBox(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);
varov: 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:beginend;// function OnStateChanged(nOldState: Integer; nNewState: Integer): HResult; dispid 2;$00000002:beginend;// function OnOpenSucceeded: HResult; dispid 3;$00000003:beginend;// function OnSeekCompleted(nPosition: Integer): HResult; dispid 4;$00000004:beginend;// function OnBuffer(nPercent: Integer): HResult; dispid 5;$00000005:beginend;// function OnVideoSizeChanged: HResult; dispid 6;$00000006:beginend;// function OnDownloadCodec(const strCodecPath: WideString): HResult; dispid 7;$00000007:beginov := OleVariant(Params.rgvarg[0]);MessageBox(Handle, PChar('缺少解码器文件:' + VarToStr(ov)), '错误', MB_OK + MB_ICONERROR);end;// function OnEvent(nEventCode: Integer; nEventParam: Integer): HResult; dispid 8;$00000008:beginend;end
end;procedure TfrmMain.FormCreate(Sender: TObject);
beginReportMemoryLeaksOnShutdown := DebugHook <> 0;APlayerCreateSuccess := InitAPlayer;
end;function TfrmMain.InitAPlayer: Boolean;
varhModule, hDll: THandle;AtlAxAttachControl: TAtlAxAttachControl;
beginhModule := LoadLibrary('atl.dll');if hModule < 32 thenbeginExit(False);end;AtlAxAttachControl := TAtlAxAttachControl(GetProcAddress(hModule, 'AtlAxAttachControl'));EventSink := TEventSink.Create(Self);EventSink.OnInvoke := EventSinkInvoke;if not Assigned(AtlAxAttachControl) thenExit(False);tryhDll := LoadLibrary('APlayer.dll');APlayer := CreateComObjectFromDll(CLASS_Player, hDll) as IDispatch;if VarIsNull(APlayer) thenbeginExit(False);end;EventSink.Connect(APlayer, _IPlayerEvents);AtlAxAttachControl(APlayer, pnlCom.Handle, nil);Result := True;exceptResult := False;end;
end;end.

接下来EventSink单元代码(绑定ActiveX控件事件用的):

unit EventSink;interfaceusesWinapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,Winapi.ActiveX;typeTInvokeEvent = 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)privateFDispatch: 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;publicconstructor 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;implementationusesComObj;procedure InterfaceConnect(const Source: IUnknown; const IID: TIID;const Sink: IUnknown; var Connection: LongInt);
varCPC: IConnectionPointContainer;CP: IConnectionPoint;i: HRESULT;
beginConnection := 0;if Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) thenif Succeeded(CPC.FindConnectionPoint(IID, CP)) theni := CP.Advise(Sink, Connection);
end;procedure InterfaceDisconnect(const Source: IUnknown; const IID: TIID;var Connection: LongInt);
varCPC: IConnectionPointContainer;CP: IConnectionPoint;
beginif Connection <> 0 thenif Succeeded(Source.QueryInterface(IConnectionPointContainer, CPC)) thenif Succeeded(CPC.FindConnectionPoint(IID, CP)) thenif Succeeded(CP.Unadvise(Connection)) thenConnection := 0;
end;{ TAbstractEventSink }function TAbstractEventSink._AddRef: Integer; stdcall;
beginResult := 2;
end;function TAbstractEventSink._Release: Integer; stdcall;
beginResult := 1;
end;constructor TAbstractEventSink.Create(AOwner: TComponent);
begininherited Create;FOwner := AOwner;
end;destructor TAbstractEventSink.Destroy;
varp: Pointer;
beginDisconnect;inherited Destroy;
end;function TAbstractEventSink.GetIDsOfNames(const IID: TGUID; Names: Pointer;NameCount, LocaleID: Integer; DispIDs: Pointer): HRESULT; stdcall;
beginResult := E_NOTIMPL;
end;function TAbstractEventSink.GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HRESULT; stdcall;
beginResult := E_NOTIMPL;
end;function TAbstractEventSink.GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
beginCount := 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 forResult := E_NOINTERFACE;if GetInterface(IID, Obj) thenResult := S_OK;if IsEqualGUID(IID, FDispIntfIID) and GetInterface(IDispatch, Obj) thenResult := S_OK;
end;procedure TAbstractEventSink.Connect(AnAppDispatch: IDispatch;const AnAppDispIntfIID: TGUID);
beginFDispIntfIID := AnAppDispIntfIID;FDispatch := AnAppDispatch;// Hook the sink up to the automation serverInterfaceConnect(FDispatch, FDispIntfIID, Self, FConnection);
end;procedure TAbstractEventSink.Disconnect;
beginif Assigned(FDispatch) thenbegin// Unhook the sink from the automation serverInterfaceDisconnect(FDispatch, FDispIntfIID, FConnection);FDispatch := nil;FConnection := 0;end;
end;{ TEventSink }procedure TEventSink.Connect(AnAppDispatch: IDispatch;const AnAppDispIntfIID: TGUID);
beginFSink.Connect(AnAppDispatch, AnAppDispIntfIID);
end;constructor TEventSink.Create(AOwner: TComponent);
begininherited Create(AOwner);FSink := TAbstractEventSink.Create(Self);
end;destructor TEventSink.Destroy;
beginFSink.Free;inherited Destroy;
end;procedure TEventSink.DoInvoke(DispID: Integer; const IID: TGUID;LocaleID: Integer; Flags: Word; var Params;VarResult, ExcepInfo, ArgErr: Pointer);
beginif Assigned(FOnInvoke) thenFOnInvoke(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下基于APRO控件的语音系统开发

    开始设计 下面我们就来看看如何利用这组控件实现语音功能,对于我们程序的应用来说,只需要使用两个 TAPI 控件 TApdComPort 和 TApdTapiDevice 即可,其中 TApdComPo ...

  2. DELPHI第三方控件及组件大全(安装方法与使用)

    一,DELPHI第三方控件安装方法介绍 1.对于单个控件,Componet–>install component..–>PAS或DCU文件–>install; 2.对于带*.dpk文 ...

  3. Delphi下的COM编程

    Delphi通过向导可以非常迅速和方便的直接建立实现COM对象的代码,但是整个COM实现的过程被完全的封装,甚至没有VCL那么结构清晰可见. 一个没有C++下COM开发经验甚至没有接触过COM开发的D ...

  4. DELPHI下的SOCK编程(转)

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

  5. delphi下实现ribbon界面的方法(一)

    delphi下实现ribbon界面的方法(一) office 2007和2010是现在大多数人经常使用的办公软件,几乎每天都在使用.因此,在软件中如果使用类office的界面样式,客户用着非常习惯,而 ...

  6. linux 下安装迅雷软件

    写给刚刚使用ubuntu还不习惯没有迅雷的同学. 一般的下载使用Firefox 的downthemall插件就OK 本贴介绍 linux下安装迅雷+firefox的FlashGot插件+VBS脚本等= ...

  7. Delphi下利用WinIo模拟鼠标键盘详解

    本文最早在编程论坛上发表,文章地址:http://programbbs.com/bbs/view12-17207-1.htm,相关文件可以在上述地址的页面中下载.转载时请注明出处. 前言 一日发现Se ...

  8. Delphi下使用指针的简单总结

    由于最近公司太忙,好久没有更新我的BLOG了.原来想着写写关于HOOK驱动的文章,可是最后想想好久已经没有做驱动的东西了,怕写出来有错误,于是作罢.开发游戏也有一段时间了,发现使用DELPHI来开发网 ...

  9. layui实现select下拉选择框组件(含代码、案例、截图)

    layui实现select下拉选择框组件(含代码.案例.截图) 案例 · 效果图: 全部代码如下: <!DOCTYPE html> <html> <head>< ...

最新文章

  1. jboss1.7_快速指南:剖析JBoss BPM跨进程通信
  2. jq鼠标移入移除事件
  3. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(五)
  4. OAuth 2.0初学者指南
  5. IEnumerableT和IQueryableT区分
  6. linux系统调用open、write、close、read以及stat函数详解
  7. hadoop的ACL权限
  8. 前端-requests-flask对应关系 restful
  9. php base64_decode 图片,php读取和保存base64编码图片
  10. linux 设备模型详解,Linux2.6 设备模型之input子系统详解
  11. 通过工具XShell4生成密钥对(公钥和私钥)
  12. QTTabBar v1039
  13. 产品经理入职四部曲—带你顺利度过试用期
  14. 盛大剥离新业务:陈大年控股
  15. 公众号文章怎么批量下载导出?
  16. 机械动力学瑞利法matlab程序,机器人学回炉重造(4):动力学仿真(附牛顿-欧拉递归逆动力学算法matlab代码)...
  17. Leetcode有效数独的Python解法
  18. 华夏第一都城《禹州市》
  19. 用计算机归零,电脑计算器里的清零键是哪个?
  20. php谷歌地图,php – 使用谷歌地图提交位置

热门文章

  1. 机器学习——特征工程
  2. 神器Overleaf!
  3. python高级学习笔记Day04--01 上下文管理器,生成器,深拷贝,浅拷贝,正则表达式
  4. php715 configure
  5. vue2和vue3区别
  6. cboard企业版源码_CBoard自助BI数据分析产品 v0.4.2
  7. RSA 非对称加密【转】
  8. 一些笔记本电脑的简单配置和价格
  9. 安静:内向性格的竞争力 苏珊·凯恩
  10. JAVA工程师个人职业规划