Search code examples
delphimemoryaudiobass.dll

Delphi: Load bass.dll directly from memory via BTMemoryModule and play sound from resources


hello i try load dll into memory and play sound file from resources (Delphi2009). In this example i load dll from HDD to memory (i plan to load dll from resources to memory) but i got an error after Button1Click

First chance exception at $76E2C41F. Exception class EAccessViolation with message 'Access violation at address 00000000. Read of address 00000000'. Process DemoApp.exe (3020)

Sound doesn't play at all :/

some of code i used from here: http://www.cyberforum.ru/blogs/14360/blog1682.html#a_codemodez but i couldn't compile it due to custom units strUtilz, MemModuleUnicode


BTMemoryModule v0.0.41 includes BTMemoryModule and also examples

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

BTMemoryModule v.1 (old probably) (with 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.

Solution

  • You do not initialise m_BASS_StreamCreateFile. Consequently it has the value nil when you call it. Which explains the error message.

    You need to add a call to BTMemoryGetProcAddress to initialise m_BASS_StreamCreateFile.

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

    This would have been simple enough to discover had you run the code under the debugger. The exception would have been trapped by the debugger and the call stack will have led to the call of m_BASS_StreamCreateFile. You could then have inspected its value to discover that it was nil.