关于DLL的编写和调用(150分)

  • 主题发起人 主题发起人 keven
  • 开始时间 开始时间
K

keven

Unregistered / Unconfirmed
GUEST, unregistred user!
前几天,我写了一个获取光盘序列号的DLL,编译时没问题,但调用(静态、动态
都试过)时发生"Invalid Pointer Operation"错误!源程序如下:

library serial;

uses
base in 'base.pas';

{$R *.RES}

Exports GetCDROMVolume,GetDiskSerialNo;

begin

end.


unit base;

interface

uses
Sysutils,Classes,windows;

function GetDiskSerialNo(Drv:String):String;stdcall;
function GetCDROMVolume():String;stdcall;

implementation

{-----------获取序列号----------}
function GetDiskSerialNo(Drv:string):String;stdcall;
var
VolumeSerialNumber:DWORD;
MaximumComponentLength:DWORD;
FileSystemFlags:DWORD;
begin
GetVolumeInformation(Pchar(Drv),
nil,
0,
@VolumeSerialNumber,
MaximumComponentLength,
FileSystemFlags,
nil,
0);
Result:=IntToHex(HiWord(VolumeSerialNumber),4)+
'-'+
IntToHex(LoWord(VolumeSerialNumber),4);
end


{----------获取光驱的盘符----------}
function GetCDROMVolume():string;stdcall;
Var
Driver:string;
i:integer;
begin
i:=0;
Driver:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
repeat
Inc(i);
Until GetDriveType(Pchar(Driver+':/'))=DRIVE_CDROM;
Result:=driver+':/';
end;

end.

我怀疑是在DLL中调用API函数是发生的,各位有什么建议或其他的解决办法
(必须是DLL),请多多指教。
 
注意:在DLL调用的时候不要用String,而要用PChar
如:
function GetDiskSerialNo(Drv:String):String;stdcall;
function GetCDROMVolume():String;stdcall;
改为:
function GetDiskSerialNo(Drv:String):PChar stdcall;
function GetCDROMVolume():PChar stdcall;

 
沙隆巴斯的主人 说的对,用string的话一定要加上sharemem单元。
这是Borland公司的忠告,否则不一定会出现什么问题,祥见Delphi帮助。
 
repeat
Inc(i);
Until GetDriveType(Pchar(Driver+':/'))=DRIVE_CDROM;

也很有问题,要是你的机器上没有cdrom那不是i超出了,不停了,出错了
 
to 沙隆巴斯的主人:
Pchar 我也试过,但问题依旧。
 
这两个程序我试过了,可以用,而且不出错。
Serial.DLL可以写成这样
library Serial;

{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }

uses
ShareMem,
windows,
SysUtils,
Classes;

procedure GetDiskSerialNo(pDrv,pSerial:pChar);stdcall;
var
VolumeSerialNumber:DWORD;
MaximumComponentLength:DWORD;
FileSystemFlags:DWORD;
begin
GetVolumeInformation(pDrv,
nil,
0,
@VolumeSerialNumber,
MaximumComponentLength,
FileSystemFlags,
nil,
0);
if pSerial <> nil then
strPCopy(pSerial,
IntToHex(HiWord(VolumeSerialNumber),4)+
'-'+
IntToHex(LoWord(VolumeSerialNumber),4));
end;

{----------获取光驱的盘符----------}
procedure GetCDROMVolume(pDriver:pChar);stdcall;
Var
Driver:string;
i:integer;
begin
i:=0;
Driver:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
repeat
Inc(i);
Until GetDriveType(Pchar(Driver+':/'))=DRIVE_CDROM;
if pDriver <> nil then
strpCopy(pDriver,driver+':/');
end;

{$R *.RES}
exports
GetCDROMVolume,
GetDiskSerialNo;

begin
end.


下面是调用程序的例子:
unit Unit1;

interface

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

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
Const
DiskDLLName = 'serial.dll';
procedure GetCDROMVolume(pDriver:pChar);stdcall;external DiskDLLName;
procedure GetDiskSerialNo(pDrv,pSerial:pChar);stdcall;external DiskDLLName;
var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var
CDChars : array [0..10] of char;
begin
GetCDROMVolume(CDChars);
ShowMessage(CDChars);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
diskChars, serialChars : array [0..128] of char;
begin
strPCopy(diskChars,'D:/');
GetDiskSerialNo(diskChars,serialChars);
showMessage(SerialChars);
end;

end.

注意:我在以前做类似的DLL时发现,最好是在DLL的参数和返回值中都不使用string,
如果需要时,应该象C语言的函数那样,将字符指针作为参数,用它来接收返回结果。
 
to Polygon:
我将你的例子原版拷贝,开始几次正常(心中一喜),试过几次后依旧出现"Invalid
Pointer Operation"错误。但可得到正确的返回值。
 
错误发生在返回之前还是之后?是不是只在GetCDROMVolume时出错?
我的例子是在WindowsNT下编译执行的,如果你在98下运行,建议
你将DLL中
Until GetDriveType(Pchar(Driver+':/'))=DRIVE_CDROM;
改为
Until GetDriveType(Pchar(Driver+':/'#0))=DRIVE_CDROM;
 
to polygon:
错误发生在关闭应用程序时,在98下按你的提议修改后问题依旧。Invalid
Pointer Operation"错误可能是由于访问了无效的内存地址产生的。
 
经过反复实验发现同一程序在不同的时候编译,调用时,有时出现“Invalid
Pointer Operation”,有时又没问题,我想可能是Delphi本身的Bug.如各位还
有高见,请继续发表,要不我要结束讨论了?
 
我前一阵也遇到相同问题,如用STRING参数必须加SHAREMEM单元且动态库分发时要附带
BORLANDMEM。DLL,使用PCHAR传入,如声明 A:PCHAR; 动态库中有A:=‘’;等类似语句
肯定出现“INVALID POINTER OPERATION ”错误,必须用STRCPY等例程。我的传入参数是
VAR A:PCHAR。
这些对你可能有帮助:)
 
你要把ShareMem放在project文件(.dpr文件) 的第一个引用单元,
这样一定可以了。
 
多人接受答案了。
 
后退
顶部