你好我尝试将dll加载到内存中并从资源中播放声音文件(Delphi2009)。在这个例子中我将硬盘从硬盘加载到内存(我打算从资源加载到内存)但我在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.
答案 0 :(得分:0)
您没有初始化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
。
答案 1 :(得分:0)
好吧,首先: Nafalem ,你的代码很可怕= \
正如 David Heffernan 之前所说的那样 - 缺少m_BASS_StreamCreateFile的初始化;而且:你在if mp_MemoryModule <> nil then MemFreeLibrary(mp_MemoryModule);
之后恰好对m_BASS_ChannelPlay
做了什么?
想一想 - 你开始播放声音然后从内存中删除库...
您需要在FreeMemory(mp_DllData);
之前将MemFreeLibrary移动到FormDestory中。
但遗憾的是,即使修改代码也不足以使其正常工作。 正如我在我的文章中所说的那样(http://www.cyberforum.ru/blogs/14360/blog1682.html(sry,我懒得把它翻译成英文)): 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-文件,可以根据需要从文件中获取! 如果更多地谈到这一点,在16位Windows中使用了Discardable-flag,并且它说它的内容可能没有上传到交换中,并且必要时从文件中获取。在32位Win OS及更高版本的系统中,系统首先会看到IMAGE_SCN_MEM_NOT_PAGED,所以只需将discardable-flag留给低内核驱动程序开发,而不要在用户模式的PE模块中使用它。
P.S。 &GT;
由于自定义单位strUtilz,MemModuleUnicode ,我无法编译它
只需从我的文章下载存档,它包含带有我的修复的MemModule,至于strUtilz - 只需用SysUtils替换它,我在评论中写道它只是一个带有str / int转换例程的模块,用asm编码
//对不起我的脏英语= P