在Delphi编程中使用C语言代码

2008-11-18     作者:        编辑:齐瑞瑞   点击进入论坛

  在Delphi编程中使用C语言代码

  Windows下编程的工具有很多,例如VB,Delphi,VC等等.我在这里不想讨论"它们的具体哪个更好一点"这种幼稚的问题.玩过DOS程序设计的人都知道,DOS下很多语言的实质核心还是调用系统提供的汇编中断函数.到了Windows下,它就变成了我们常说的API了.而在Windows下写程序很多时候都是调用API,语言,只不过是一个表达工具而已.

  我现在已经参加工作大约有半年左右,我们公司是用Borland公司的Delphi作为主开发工具.本着未偏袒任何一个工具的立场,我说句公道话:Delphi是目前Win32下开发程序的最快速,最有效率的工具.

  Delphi适合用来开发应用程序,但是有时侯一些底层的东西可以直接使用C语言来开发.我在公司经常开发跟硬件相关的项目,而很多硬件的SDK包是用C来写的.这个时候我一般把它们转换成Delphi(PASCAL)语法的代码.下面谈一下我的个人粗浅经验.因为当时学校教的是Pascal语言,所以我对C语言并不是太熟手.下面的观点或者代码如有错漏之处希望高手们放小弟一马:)

  一:将C语言的程序编译成DLL供Delphi调用.这种方法过于简单,而且需要额外带一个DLL文件,所以不在本文的讨论范围之内.

  二:直接转换C语言代码到DELPHI代码

  C语言的函数格式与Delphi不同,它们是函数返回类型在前,函数声明在后.对于没有任何返回类型的函数则定义为VOID类型.

  例如:Delphi中函数function MyFunction:(intIN:integer):Bool;相应的C语言代码就变成Bool MyFunction(int intIN);又例如procedure MyProcedure;====>void MyProcedure;采用这种方法,一般要求对C语言比较熟悉.我一般是采用这种方法.下面是我收集整理的自己常用的Delphi与C之间的类型对应表.其中左边是C类型,右边是对应的Delphi类型:

  以下是引用片段:

  ABC -> TABC

  ACCEL -> TAccel

  ATOM -> TAtom

  BITMAP -> TBitMap

  BITMAPCOREHEADER -> TBitmapCoreHeader

  BITMAPCOREINFO -> TBitmapCoreInfo

  BITMAPFILEHEADER -> TBitmapFileHeader

  BITMAPINFO -> TBitmapInfo

  BITMAPINFOHEADER -> TBitmapInfoHeader

  BOOL -> Bool

  CBT_CREATEWND -> TCBT_CreateWnd

  CBTACTIVATESTRUCT -> TCBTActivateStruct

  CHAR -> Char

  CHAR* -> PChar

  CLIENTCREATESTRUCT -> TClientCreateStruct

  COLORREF -> TColorRef

  COMPAREITEMSTRUCT -> TCompareItemStruct

  COMSTAT -> TComStat

  CREATESTRUCT -> TCreateStruct

  CTLINFO -> TCtlInfo

  CTLSTYLE -> TCtlStyle

  CTLtype -> TCtltype

  DCB -> TDCB

  DDEAACK -> TDDEAck

  DDEADVISE -> TDDEAdvise

  DDEDATA -> TDDEData

  DDEPOKE -> TDDEPoke

  DEBUGHOOKINFO -> TDebugHookInfo

  DELETEITEMSTRUCT -> TDeleteItemStruct

  DEVMODE -> TDevMode

  DOUBLE -> Double

  DRAWITEMSTRUCT -> TDrawItemStruct

  DWORD -> LongInt

  ENUMLOGFONT -> TEnumLogFont

  EVENTMSG -> TEventMsg

  FARPROC -> TFarProc

  FIXED -> TFixed

  FLOAT -> Single

  GLYPHMETRICS -> TGlyphMetrics

  HANDLE -> THandle

  HANDLETABLE -> THandleTable

  HARDWAREHOOKSTRUCT -> THardwareHookStruct

  HELPWININFO -> THelpWinInfo

  INT -> Integer

  KERNINGPAIR -> TKerningPair

  LOGBRUSH -> TLogBrush

  LOGFONT -> TLogFont

  LOGPALETTE -> TLogPalette

  LOGPEN -> TLogPen

  LONG -> LongInt

  LONG DOUBLE -> Extended

  LONG INT -> LongInt

  LPSTR -> PChar

  LPWSTR -> PWideChar

  MAT2 -> TMat2

  MDICREATESTRUCT -> TMDICreateStruct

  MEASUREITEMSTRUCT -> TMeasureItemStruct

  MENUITEMTEMPLATE -> TMenuItemTemplate

  MENUITEMTEMPLATEHEADER -> TMenuItemTemplateHeader

  METAFILEPICT -> TMetaFilePict

  METAHEADER -> TMetaHeader

  METARECORD -> TMetaRecord

  MINMAXINFO -> TMinMaxInfo

  MOUSEHOOKSTRUCT -> TMouseHookStruct

  MSG -> TMsg

  MULTIKEYHELP -> TMultiKeyHelp

  NCCALCSIZE_PARAMS -> TNCCalcSize_Params

  NEWTEXTMETRIC -> TNewTextMetric

  OFSTRUCT -> TOFStruct

  OUTLINETEXTMETRIC -> TOutlineTextMetric

  PAINTSTRUCT -> TPaintStruct

  PALETTEENTRY -> TPaletteEntry

  PANOSE -> TPanose

  PATTERN -> TPattern

  POINTFX -> TPointFX

  PSTR -> PChar

  PWSTR -> PWideChar

  RASTERIZER_STATUS -> TRasterizer_Status

  RGBQUAD -> TRGBQuad

  RGBTRIPLE -> TRGBTriple

  SEGINFO -> TSegInfo

  SHORT -> SmallInt

  SHORT INT -> SmallInt

  SIZE -> TSize

  TEXTMETRIC -> TTextMetric

  TPOINT -> TPoint

  TRECT -> TRect

  TTPOLYCURVE -> TTTPolyCurve

  TTPOLYGONHEADER -> TPolygonHeader

  UINT -> Word

  UNSIGNED -> Word

  UNSIGNED CHAR -> Byte

  UNSIGNED INT -> Word

  UNSIGNED LONG -> LongInt(DWORD)

  UNSIGNED LONG INT -> LongInt

  UNSIGNED SHORT -> Word

  UNSIGNED SHORT INT -> Word

  VOID* -> Pointer

  WINDOWPLACEMENT -> TWindowPlacement

  WINDOWPOS -> TWindowPos

  WNDCLASS -> TWndClass

  WORD -> Word

  三:在Delphi中直接链接C语言的OBJ文件.

  这种方法的好处在于最终EXE不用带任何外部文件.也不用对C语言过于熟悉.

  我们都知道,代码在编译成可执行文件(或DLL,OCX文件,下同)之前,都必须得先生成OBJ文件(DELPHI一般是DCU文件,但也可以通过编辑编译选项生成OBJ文件),然后把OBJ文件和资源文件(*.RES)链接成最终的可执行文件.利用这个方法,我们可以直接把OBJ文件链接到我们的程序里面.

  不过需要注意的是,编译器不同,生成的OBJ文件也不一样.Microsoft的编译器生成的OBJ文件是COFF格式,而Borland的C++Builder生成的是OMF格式.因为我们需要在Delphi中链接,所以必须使用CBC,或者Borland官方站点带的免费编译工具.下面我们通过一个简单的例子来说明具体操作步骤:

  这个例子是简单的提供一个函数,用来判断一个文件是否为Dat格式的VCD文件.头文件声明如下:

  以下是引用片段:

  /*

  文件名称:DatFormat.h

  */

  #ifndef DatFormat_H

  #define DatFormat_H

  #include

  #pragma pack(push, 1)//这个与下面的配对,一般用到记录类型的时候需要定义,这里实际不用

  #ifdef __cplusplus

  extern "C" {

  #endif

  extern BOOL CheckIsDatFile(const char * FileName,BOOL *IsDatFile);

  #ifdef __cplusplus

  }

  #endif

  #pragma pack(pop)

  #endif // DatFormat_H

  具体实现代码DatFormat.c如下:

  #include "DatFormat.h"

  BOOL CheckIsDatFile(const char * FileName,BOOL *IsDatFile)

  /*

  函数说明:该函数用于判断一个文件是否为Dat文件(即VCD文件)格式.

  参数:

  IN:

  FileName:欲判断的文件名称

  IN,OUT:

  IsDatFile:是否为Dat格式文件

  OUT:

  读文件失败返回FALSE,否则返回TRUE.

  ------------------------------------

  作者:陈经韬.2004,01,17. http://www.138soft.com,lovejingtao@21cn.com

  */

  {

  HANDLE hFile;

  DWORD dwBytesRead;

  BOOL re;

  char MyBuf[4];

  *IsDatFile=FALSE;

  //建立读文件句柄

  hFile = CreateFile(FileName,

  GENERIC_READ,

  FILE_SHARE_READ,

  NULL,

  OPEN_EXISTING,

  0,

  0);

  if (hFile == INVALID_HANDLE_VALUE) return FALSE;

  //读文件

  re = ReadFile(hFile,

  &MyBuf,

  4,

  &dwBytesRead,

  NULL);

  if (dwBytesRead!=4)

  {

  CloseHandle(hFile);

  return FALSE;

  }

  //读文件失败的时候

  if (re!=TRUE)

  {

  CloseHandle(hFile);

  return FALSE;

  }

  CloseHandle(hFile);

  *IsDatFile=(MyBuf[0]=='R' && MyBuf[1]=='I' && MyBuf[2]=='F' && MyBuf[3]=='F');

  return(TRUE);

  }

  运行CBC,新建一个工程,然后把DatFormat.c添加到工程里面,编译整个工程,将得到我们需要的OBJ文件:DatFormat.OBJ.然后我们关闭CBC即可,因为下面不再需要用到它了.

  运行Delphi,新建一个工程并保存.然后把DatFormat.OBJ拷贝到它的目录之下.在单元的implementation下面添加如下代码:

  {$LINK 'DatFormat.obj'} //链接外部OBJ文件

  function _CheckIsDatFile(const FileName:Pchar;IsDatFile:PBool):Bool;cdecl;external;//定义函数.其中cdecl进栈方式说明采用C语言格式传递参数.external说明是个外部声明函数.

  注意函数声明的原形与C定义的不一样.必须在前面添加一个下划线.原因是因为编译器的链接符号中.C与C++是不一样的.因为这个不是本文重点,所以这里不作讨论.请感兴趣的朋友自行参阅相关资料.

  然后我们写如下代码调用此函数:

  以下是引用片段:

  procedure TFrmMain.Button1Click(Sender: TObject);

  var

  IsDatFile:Bool;

  begin

  if OpenDialog1.Execute then

  if _CheckIsDatFile(Pchar(OpenDialog1.FileName),@IsDatFile) then

  if IsDatFile then ShowMessage('恭喜!该文件是一个Dat格式的视频文件!')

  else ShowMessage('不好意思,该文件不是一个Dat格式的视频文件!')

  else ShowMessage('读文件错误!');

  end;

  编译这个程序,将得到一个干净的可执行EXE文件了.

  四:C++Builder中使用Delphi单元

  这个实际是题外话了,不过这里还是提一提:假设我们有一个获取BIOS密码的Delphi单元

  unit AwardBiosPas;

  {=======================================================

  项目: 在Delphi编程中使用C语言代码- 演示程序

  模块: 获取BIOS密码单元

  描述:

  版本:

  日期: 2004-01-17

  作者: 陈经韬.lovejingtao@21cn.com,http://www.138soft.com

  更新: 2004-01-17

  =======================================================}

  以下是引用片段:

  interface

  uses

  windows, SysUtils;

  function My_GetBiosPassword: string;

  implementation

  function CalcPossiblePassword(PasswordValue: WORD): string;

  var

  I: BYTE;

  C: CHAR;

  S: string[8];

  begin

  I := 0;

  while PasswordValue <> 0 do

  begin

  Inc(I);

  if $263 > PasswordValue then

  begin

  if $80 > PasswordValue then

  S[I] := CHAR(PasswordValue)

  else if $B0 > PasswordValue then

  S[I] := CHAR(PasswordValue and $77)

  else if $11D > PasswordValue then

  S[I] := CHAR($30 or (PasswordValue and $0F))

  else if $114 > PasswordValue then

  begin

  S[I] := CHAR($64 or (PasswordValue and $0F));

  if '0' > S[I] then

  S[I] := CHAR(BYTE(S[I]) + 8);

  end

  else if $1C2 > PasswordValue then

  S[I] := CHAR($70 or (PasswordValue and $03))

  else if $1E4 > PasswordValue then

  S[I] := CHAR($30 or (PasswordValue and $03))

  else

  begin

  S[I] := CHAR($70 or (PasswordValue and $0F));

  if 'z' < S[I] then

  S[I] := CHAR(BYTE(S[I]) - 8);

  end;

  end

  else

  S[I] := CHAR($30 or (PasswordValue and $3));

  PasswordValue := (PasswordValue - BYTE(S[I])) shr 2;

  end;

  S[0] := CHAR(I);

  PasswordValue := I shr 1;

  while PasswordValue < I do

  begin {this is to do because award starts calculating with the last letter}

  C := S[BYTE(S[0]) - I + 1];

  S[BYTE(S[0]) - I + 1] := S[I];

  S[I] := C;

  Dec(I);

  end;

  CalcPossiblePassword := S;

  end;

  function readcmos(off: byte): byte;

  var

  value: byte;

  begin

  asm

  xor ax, ax

  mov al, off

  out 70h, al

  in al, 71h

  mov value, al

  end;

  readcmos := value;

  end;

  function My_GetBiosPassword: string;

  var

  superpw, userpw: word;

  begin

  if Win32Platform <> VER_PLATFORM_WIN32_NT then //不是NT

  begin

  pchar(@superpw)[0] := char(readcmos($1C));

  pchar(@superpw)[1] := char(readcmos($1D));

  pchar(@userpw)[0] := char(readcmos($64));

  pchar(@userpw)[1] := char(readcmos($65));

  Result:= ('************BIOS密码**********************')+#13+'超级用户密码为:' + CalcPossiblePassword(superpw) + #13 + '用户密码为:' + CalcPossiblePassword(userpw);

  end

  else

  Result := '用户系统为NT,无法获取BIOS密码!';

  end;

  end.

  如何直接在CBC中使用它呢?新建一个CBC工程,然后把这个单元加到项目里面去.具体操作为:Add to Project--->文件类型:pascal unit(*.pas),然后Build Demo1.这个时候将在AwardBiosPas.pas的同目录下生成一个AwardBiosPas.hpp文件.把它引用到我们的需要调用的单元.然后直接调用即可:

  以下是引用片段:

  void __fastcall TFrmMain::Button1Click(TObject *Sender)

  {

  ShowMessage(My_GetBiosPassword());

  }

  五:其它方法.当然可以用RES将C语言生成的二进制文件,但这个方法与第一种方法差不多.优点是不怕文件丢失.缺点是很容易被别人直接用资源修改工具打开修改.这个时候可以使用笔者写的自制编程序工具PasAnywhere.不过这已经是另外一个话题了.

寻找产品:
姓       名: 电   话:
公       司: E-mail:
描       述: