【问题标题】:Delphi: Load bass.dll directly from memory via BTMemoryModule and play sound from resourcesDelphi:通过 BTMemoryModule 直接从内存中加载 bass.dll 并从资源中播放声音
【发布时间】:2014-03-26 05:55:11
【问题描述】:

您好,我尝试将 dll 加载到内存中并从资源中播放声音文件 (Delphi2009)。在此示例中,我将 dll 从 HDD 加载到内存(我计划将 dll 从资源加载到内存),但在 Button1Click 后出现错误

$76E2C41F 的第一次机会例外。异常类 EAccessViolation 带有消息“地址 00000000 的访问冲突。读取地址 00000000”。处理 DemoApp.exe (3020)

根本不播放声音:/

我在这里使用的一些代码:http://www.cyberforum.ru/blogs/14360/blog1682.html#a_codemodez 但由于自定义单位 strUtilz,MemModuleUnicode,我无法编译它


BTMemoryModule v0.0.41 包含 BTMemoryModule 和示例

http://code.google.com/p/memorymodule/downloads/list

BTMemoryModule v.1(可能是旧版本)(使用 BTMemoryModule + BTMemoryModuleUnicode)

http://www.delphibasics.info/home/delphibasicssnippets/btmemorymodule


unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, BTMemoryModule, StdCtrls, xpman;

 const // Constants :::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::
 _s = '';
 _n=#13#10; // line break
 ver = '1.0 '; // Articles
 tit = 'Bass Memory App' + ver; // title - the name of the application
 msgYN=$04; msgERR=$10; msgINF=$40; // <-type codes posts
 res1='dll'; // resource name with dllkoy
 res2='snd'; // name of the resource with sound

 type // TYPES :::::::::::::::::::::::::::::::::::::::::::::: ::::::::::::::::::::::::
 MemRes = record // structure for the projection of the resource in memory
 p: pointer; // pointer to the memory
 sz: int64; // size (length)
 rd: cardinal; // hResData
 ri: cardinal; // hResInfo
 end;

type
  TBASS_ChannelPlay = function (handle: cardinal; restart: bool): bool; stdcall;
  TBASS_StreamCreateFile = function (mem: bool; f: Pointer; offset, length: int64; flags: cardinal): cardinal; stdcall;
  TBASS_StreamFree = function (handle: cardinal): bool; stdcall;
  TBASS_Init = function (device: integer; freq, flags: cardinal; win: cardinal; clsid: pGUID): bool; stdcall;
  TBASS_Free = function: bool; stdcall;

  TForm1 = class(TForm)
    BtnFileCAll: TButton;
    BtnMemCall: TButton;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    mp_DllData : Pointer;
    m_DllDataSize : Integer;
    mp_MemoryModule: PBTMemoryModule;
    //m_DllHandle: Cardinal;

    m_BASS_ChannelPlay: TBASS_ChannelPlay;
    m_BASS_StreamCreateFile: TBASS_StreamCreateFile;
    //m_BASS_StreamFree: TBASS_StreamFree;
    m_BASS_Init: TBASS_Init;
    //m_BASS_Free: TBASS_Free;
  public
    { Public declarations }


  end;

var
 Form1: TForm1;
 wnd: cardinal; // window handle
 ss: cardinal; // handle audio stream
 snd: MemRes; // pointer to the audio file in memory
 dll: MemRes; // pointer to memory dllku
 bass: Pointer; // structure projection dll in memory
 stp: word; // execution step (for debug)
 st: boolean; // status of the audio stream
 th: cardinal; // handle the flow of replacement buttons
 ti: cardinal; // id flow
 ms : TMemoryStream;
 rs : TResourceStream;

implementation


{$R *.dfm}
{$R BassMem.RES}  // snd CookieJarLoop.ogg RCData

function Res2Mem(hInst:cardinal;res:string;rtype:pChar):MemRes;
begin
  result.p:=nil;
  result.ri:=FindResource(hInst,pchar(res),rtype);
  if result.ri=0 then exit;
  result.sz:=SizeOfResource(hInst,result.ri);
  if result.sz=0 then exit;
  result.rd:=LoadResource(hInst,result.ri);
  if result.rd=0 then exit;
  result.p:=LockResource(result.rd);
end;


procedure TForm1.FormCreate(Sender: TObject);
var
  MemoryStream: TMemoryStream;
begin
  Position := poScreenCenter;
  MemoryStream := TMemoryStream.Create;
  MemoryStream.LoadFromFile('bass.dll');
  MemoryStream.Position := 0;
  m_DllDataSize := MemoryStream.Size;
  mp_DllData := GetMemory(m_DllDataSize);
  MemoryStream.Read(mp_DllData^, m_DllDataSize);
  MemoryStream.Free;

end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeMemory(mp_DllData);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  mp_MemoryModule := BTMemoryLoadLibary(mp_DllData, m_DllDataSize);
    try
    if mp_MemoryModule = nil then showmessage('err1');

    @m_BASS_Init := BTMemoryGetProcAddress(mp_MemoryModule, 'BASS_Init');
    if @m_BASS_Init = nil then showmessage('err2');

    @m_BASS_ChannelPlay := BTMemoryGetProcAddress(mp_MemoryModule, 'BASS_ChannelPlay');
    if @m_BASS_ChannelPlay = nil then showmessage('err3');

    m_BASS_Init(-1, 44100, 0, Handle, nil);

    snd:=Res2Mem(hInstance, res2 ,RT_RCDATA);

    ss:=m_BASS_StreamCreateFile(true,snd.p,0,snd.sz,4{=BASS_SAMPLE_LOOP});
    if ss=0 then showmessage('err ss=0');
    m_BASS_ChannelPlay(ss, false);

    except
    Showmessage('An error occoured while loading the dll: ' + BTMemoryGetLastError);
    end;
  if mp_MemoryModule <> nil then BTMemoryFreeLibrary(mp_MemoryModule);
end;

end.

【问题讨论】:

    标签: delphi memory audio bass.dll


    【解决方案1】:

    你没有初始化m_BASS_StreamCreateFile。因此,当您调用它时,它的值为nil。这解释了错误消息。

    您需要添加对BTMemoryGetProcAddress 的调用以初始化m_BASS_StreamCreateFile。

    @m_BASS_StreamCreateFile := BTMemoryGetProcAddress(mp_MemoryModule, 
      'BASS_StreamCreateFile');
    if @m_BASS_StreamCreateFile = nil then ....
    

    如果您在调试器下运行代码,这很容易发现。异常将被调试器捕获,调用堆栈将导致调用m_BASS_StreamCreateFile。然后,您可以检查它的值以发现它是 nil。

    【讨论】:

    • 我收到另一个错误“0x77118dc9 的访问冲突:写入地址 0x00000014”,我尝试运行已编译的版本,它播放了 1 秒的破碎声音,然后它崩溃了。我看起来像 BTMemory 的问题,我尝试从 HDD 调用 dll 并播放声音没有问题。
    • 我想我回答了你问的问题。也就是说,我想知道你为什么要卸载 DLL。我敢打赌,声音是异步播放的。删除卸载 DLL 的调用。
    【解决方案2】:

    好吧,首先:Nafalem,你的代码很糟糕 =\ 正如 David Heffernan 之前所说 - m_BASS_StreamCreateFile 缺少初始化;而且:您希望在m_BASS_ChannelPlay 之后对if mp_MemoryModule &lt;&gt; nil then MemFreeLibrary(mp_MemoryModule); 做什么? 想一想-您开始播放声音,然后从内存中删除库... 您需要在 FreeMemory(mp_DllData); 之前将 MemFreeLibrary 移动到 FormDestory。

    但遗憾的是,即使修复代码也不足以使其正常工作。 正如我在我的文章中所说的(http://www.cyberforum.ru/blogs/14360/blog1682.html(对不起,我懒得把它翻译成英文)): BTMemoryModule 内部有一个错误:

      if (l_section.Characteristics and IMAGE_SCN_MEM_DISCARDABLE) <> 0 then begin
          // section is not needed any more and can safely be freed
          VirtualFree(Pointer(l_section.Misc.PhysicalAddress), l_section.SizeOfRawData, MEM_DECOMMIT);
          inc(longword(l_section), sizeof(TImageSectionHeader));
          continue;
      end;
    

    这部分代码释放了启用 Discardable-flag 的部分,但在我们的例子中不应该这样做! 正如您可以在 msdn 中找到的,discardable-flag 意味着 部分可以根据需要丢弃,正如您所见,它可以被丢弃,而不是必须……它用于物理 PE 文件,可以按需从文件中读取! 如果说更多的话,Discardable-flag 在 16 位 Windows 中使用,它表示它的内容可能不会上传到交换中,并在必要时从文件中读取。在 32 位 Win OS 及更高版本中,系统优先查看 IMAGE_SCN_MEM_NOT_PAGED,因此只需将 discardable-flag 留给低核驱动程序开发,切勿在用户模式 ​​PE 模块中使用它。

    附: >

    由于自定义单位 strUtilz、MemModuleUnicode,我无法编译它

    只需从我的文章中下载存档,它包含 MemModule 以及我的修复程序,至于 strUtilz - 只需将其替换为 SysUtils,我在 cmets 中写道,它只是一个带有用 asm 编码的 str/int 转换例程的模块。

    //对不起我的脏英语=P

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 2017-06-29
      • 1970-01-01
      • 2020-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多