2010年11月5日星期五

创建具有托盘的服务程序(转)

摘自:http://7880.com/info/Article-69800e80.html

Windows 2000/XP和2003等支持一种叫做"服务程序"的东西.程序作为服务启动有以下几个好处:

    (1)不用登陆进系统即可运行.
    (2)具有SYSTEM特权.所以你在进程管理器里面是无法结束它的.

    笔者在2003年为一公司开发机顶盒项目的时候,曾经写过课件上传和媒体服务,下面就介绍一下如何用Delphi7创建一个Service程序.
    运 行Delphi7,选择菜单File-->New-->Other--->Service Application.将生成一个服务程序的框架.将工程保存为ServiceDemo.dpr和Unit_Main.pas,然后回到主框架.我们注 意到,Service有几个属性.其中以下几个是我们比较常用的:

    (1)DisplayName:服务的显示名称
    (2)Name:服务名称.

    我 们在这里将DisplayName的值改为"Delphi服务演示程序",Name改为"DelphiService".编译这个项目,将得到 ServiceDemo.exe.这已经是一个服务程序了!进入CMD模式,切换致工程所在目录,运行命令"ServiceDemo.exe /install",将提示服务安装成功!然后"net start DelphiService"将启动这个服务.进入控制面版-->管理工具-->服务,将显示这个服务和当前状态.不过这个服务现在什么也干 不了,因为我们还没有写代码:)先"net stop DelphiService"停止再"ServiceDemo.exe /uninstall"删除这个服务.回到Delphi7的IDE.

    我们的计划是为这个服务添加一个主窗口,运行后任务栏显示程序的图标,双击图标将显示主窗口,上面有一个按钮,点击该按钮将实现Ctrl+Alt+Del功能.

    实 际上,服务程序莫认是工作于Winlogon桌面的,可以打开控制面板,查看我们刚才那个服务的属性-->登陆,其中"允许服务与桌面交互"是不打 钩的.怎么办?呵呵,回到IDE,注意那个布尔属性:Interactive,当这个属性为True的时候,该服务程序就可以与桌面交互了.

    File-->New-->Form为服务添加窗口FrmMain,单元保存为Unit_FrmMain,并且把这个窗口设置为手工创建.完成后的代码如下:



unit Unit_Main;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs, Unit_FrmMain;

type
TDelphiService = class(TService)
procedure ServiceContinue(Sender: TService; var Continued: Boolean);
procedure ServiceExecute(Sender: TService);
procedure ServicePause(Sender: TService; var Paused: Boolean);
procedure ServiceShutdown(Sender: TService);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
private
{ Private declarations }
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;

var
DelphiService: TDelphiService;
FrmMain: TFrmMain;
implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  DelphiService.Controller(CtrlCode);
end;

function TDelphiService.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TDelphiService.ServiceContinue(Sender: TService;
var Continued: Boolean);
begin
  while not Terminated do
  begin
    Sleep(10);
    ServiceThread.ProcessRequests(False);
  end;
end;

procedure TDelphiService.ServiceExecute(Sender: TService);
begin
  while not Terminated do
  begin
    Sleep(10);
    ServiceThread.ProcessRequests(False);
  end;
end;

procedure TDelphiService.ServicePause(Sender: TService;
var Paused: Boolean);
begin
  Paused := True;
end;

procedure TDelphiService.ServiceShutdown(Sender: TService);
begin
  gbCanClose := true;
  FrmMain.Free;
  Status := csStopped;
  ReportStatus();
end;

procedure TDelphiService.ServiceStart(Sender: TService;
var Started: Boolean);
begin
  Started := True;
  Svcmgr.Application.CreateForm(TFrmMain, FrmMain);
  gbCanClose := False;
  FrmMain.Hide;
end;

procedure TDelphiService.ServiceStop(Sender: TService;
var Stopped: Boolean);
begin
  Stopped := True;
  gbCanClose := True;
  FrmMain.Free;
end;

end.



主窗口单元如下:


unit Unit_FrmMain;

interface

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

const
WM_TrayIcon = WM_USER + 1234;
type
TFrmMain = class(TForm)
Timer1: TTimer;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure FormDestroy(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
IconData: TNotifyIconData;
procedure AddIconToTray;
procedure DelIconFromTray;
procedure TrayIconMessage(var Msg: TMessage); message WM_TrayIcon;
procedure SysButtonMsg(var Msg: TMessage); message WM_SYSCOMMAND;
public
{ Public declarations }
end;

var
FrmMain: TFrmMain;
gbCanClose: Boolean;
implementation

{$R *.dfm}

procedure TFrmMain.FormCreate(Sender: TObject);
begin
  FormStyle := fsStayOnTop; {窗口最前}
  SetWindowLong(Application.Handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW); {不在任务栏显示}
  gbCanClose := False;
  Timer1.Interval := 1000;
  Timer1.Enabled := True;
end;

procedure TFrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  CanClose := gbCanClose;
  if not CanClose then
  begin
    Hide;
  end;
end;

procedure TFrmMain.FormDestroy(Sender: TObject);
begin
  Timer1.Enabled := False;
  DelIconFromTray;
end;

procedure TFrmMain.AddIconToTray;
begin
  ZeroMemory(@IconData, SizeOf(TNotifyIconData));
  IconData.cbSize := SizeOf(TNotifyIconData);
  IconData.Wnd := Handle;
  IconData.uID := 1;
  IconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP;
  IconData.uCallbackMessage := WM_TrayIcon;
  IconData.hIcon := Application.Icon.Handle;
  IconData.szTip := 'Delphi服务演示程序';
  Shell_NotifyIcon(NIM_ADD, @IconData);
end;

procedure TFrmMain.DelIconFromTray;
begin
  Shell_NotifyIcon(NIM_DELETE, @IconData);
end;

procedure TFrmMain.SysButtonMsg(var Msg: TMessage);
begin
  if (Msg.wParam = SC_CLOSE) or
  (Msg.wParam = SC_MINIMIZE) then Hide
  else inherited; // 执行默认动作
end;

procedure TFrmMain.TrayIconMessage(var Msg: TMessage);
begin
  if (Msg.LParam = WM_LBUTTONDBLCLK) then Show();
end;

procedure TFrmMain.Timer1Timer(Sender: TObject);
begin
  AddIconToTray;
end;

procedure SendHokKey;stdcall;
var
HDesk_WL: HDESK;
begin
  HDesk_WL := OpenDesktop ('Winlogon', 0, False, DESKTOP_JOURNALPLAYBACK);
  if (HDesk_WL <> 0) then
  if (SetThreadDesktop (HDesk_WL) = True) then
  PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG (MOD_ALT or MOD_CONTROL, VK_DELETE));
end;

procedure TFrmMain.Button1Click(Sender: TObject);
var
dwThreadID : DWORD;
begin
  CreateThread(nil, 0, @SendHokKey, nil, 0, dwThreadID);
end;

end.

program ServiceDemo;

uses
SvcMgr,
Unit_Main in 'Unit_Main.pas' {DelphiService: TService},
Unit_frmMain in 'Unit_frmMain.pas' {frmMain};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TDelphiService, DelphiService);
  Application.Run;
end.




窗体代码如下:


object DelphiService: TDelphiService
OldCreateOrder = False
DisplayName = 'Delphi服务演示程序'
Interactive = True
OnContinue = ServiceContinue
OnExecute = ServiceExecute
OnPause = ServicePause
OnShutdown = ServiceShutdown
OnStart = ServiceStart
OnStop = ServiceStop
Left = 261
Top = 177
Height = 150
Width = 215
end

object frmMain: TfrmMain
Left = 192
Top = 107
Width = 696
Height = 480
Caption = '我的服务测试程序'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCloseQuery = FormCloseQuery
OnCreate = FormCreate
OnDestroy = FormDestroy
PixelsPerInch = 96
TextHeight = 13
object Button1: TButton
Left = 296
Top = 264
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 0
OnClick = Button1Click
end
object Timer1: TTimer
OnTimer = Timer1Timer
Left = 120
Top = 192
end
end


DELPHI编写服务程序总结一--编写技巧 (转)

摘自:http://apps.hi.baidu.com/share/detail/19163540

一、服务程序和桌面程序的区别

Windows 2000/XP/2003等支持一种叫做“系统服务程序”的进程,系统服务和桌面程序的区别是:
系统服务不用登陆系统即可运行;系统服务是运行在System Idle Process/System/smss/winlogon/services下的,而桌面程序是运行在Explorer下的;系统服务拥有更高的权限, 系统服务拥有Sytem的权限,而桌面程序只有Administrator权限;在Delphi中系统服务是对桌面程序进行了再一次的封装,既系统服务继 承于桌面程序。因而拥有桌面程序所拥有的特性;系统服务对桌面程序的DoHandleException做了改进,会自动把异常信息写到NT服务日志中; 普通应用程序启动只有一个线程,而服务启动至少含有三个线程。(服务含有三个线程:TServiceStartThread服务启动线 程;TServiceThread服务运行线程;Application主线程,负责消息循环);
摘录代码:
procedure TServiceApplication.Run;
begin
    .
    .
    .
      StartThread := TServiceStartThread.Create(ServiceStartTable);
      try
        while not Forms.Application.Terminated do
          Forms.Application.HandleMessage;
        Forms.Application.Terminate;
        if StartThread.ReturnValue <> 0 then
          FEventLogger.LogMessage(SysErrorMessage(StartThread.ReturnValue));
      finally
        StartThread.Free;
      end;
     .
     .
     .
end;

procedure TService.DoStart;
begin
    try
      Status := csStartPending;
      try
        FServiceThread := TServiceThread.Create(Self);
        FServiceThread.Resume;
        FServiceThread.WaitFor;
        FreeAndNil(FServiceThread);
      finally
        Status := csStopped;
      end;
    except
      on E: Exception do
        LogMessage(Format(SServiceFailed,[SExecute, E.Message]));
    end;
end;
在系统服务中也可以使用TTimer这些需要消息的定时器,因为系统服务在后台使用TApplication在分发消息;

二、如何编写一个系统服务

打开Delphi编辑器,选择菜单中的File|New|Other...,在New Item中选择Service Application项,Delphi便自动为你建立一个基于TServiceApplication的新工 程,TserviceApplication是一个封装NT服务程序的类,它包含一个TService1对象以及服务程序的装卸、注册、取消方法。
TService属性介绍:
AllowPause:是否允许暂停;
AllowStop:是否允许停止;
Dependencies:启动服务时所依赖的服务,如果依赖服务不存在则不能启动服务,而且启动本服务的时候会自动启动依赖服务;
DisplayName:服务显示名称;
ErrorSeverity:错误严重程度;
Interactive:是否允许和桌面交互;
LoadGroup:加载组;
Name:服务名称;
Password:服务密码;
ServiceStartName:服务启动名称;
ServiceType:服务类型;
StartType:启动类型;
事件介绍:
AfterInstall:安装服务之后调用的方法;
AfterUninstall:服务卸载之后调用的方法;
BeforeInstall:服务安装之前调用的方法;
BeforeUninstall:服务卸载之前调用的方法;
OnContinue:服务暂停继续调用的方法;
OnExecute:执行服务开始调用的方法;
OnPause:暂停服务调用的方法;
OnShutDown:关闭时调用的方法;
OnStart:启动服务调用的方法;
OnStop:停止服务调用的方法;

三、编写一个两栖服务

采用下面的方法,可以实现一个两栖系统服务(既系统服务和桌面程序的两种模式)
工程代码:
program FleetReportSvr;

uses
SvcMgr,
Forms,
SysUtils,
Windows,
SvrMain in 'SvrMain.pas' {FleetReportService: TService},
AppMain in 'AppMain.pas' {FmFleetReport};

{$R *.RES}

const
CSMutexName = 'Global\Services_Application_Mutex';
var
OneInstanceMutex: THandle;
SecMem: SECURITY_ATTRIBUTES;
aSD: SECURITY_DESCRIPTOR;
begin
InitializeSecurityDescriptor(@aSD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@aSD, True, nil, False);
SecMem.nLength := SizeOf(SECURITY_ATTRIBUTES);
SecMem.lpSecurityDescriptor := @aSD;
SecMem.bInheritHandle := False;
OneInstanceMutex := CreateMutex(@SecMem, False, CSMutexName);
if (GetLastError = ERROR_ALREADY_EXISTS)then
begin
    DlgError('Error, Program or service already running!');
    Exit;
end;
if FindCmdLineSwitch('svc', True) or
    FindCmdLineSwitch('install', True) or
    FindCmdLineSwitch('uninstall', True) then
begin
    SvcMgr.Application.Initialize;
    SvcMgr.Application.CreateForm(TSvSvrMain, SvSvrMain);
    SvcMgr.Application.Run;
end
else
begin
    Forms.Application.Initialize;
    Forms.Application.CreateForm(TFmFmMain, FmMain);
    Forms.Application.Run;
end;
end.
然后在SvrMain注册服务:
unit SvrMain;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs, MsgCenter;

type
TSvSvrMain = class(TService)
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceBeforeInstall(Sender: TService);
    procedure ServiceAfterInstall(Sender: TService);
private
    { Private declarations }
public
    function GetServiceController: TServiceController; override;
    { Public declarations }
end;

var
SvSvrMain: TSvSvrMain;

implementation

const
CSRegServiceURL = 'SYSTEM\CurrentControlSet\Services\';
CSRegDescription = 'Description';
CSRegImagePath = 'ImagePath';
CSServiceDescription = 'Services Sample.';

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
SvSvrMain.Controller(CtrlCode);
end;

function TSvSvrMain.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;

procedure TSvSvrMain.ServiceStart(Sender: TService;
var Started: Boolean);
begin
Started := dmPublic.Start;
end;

procedure TSvSvrMain.ServiceStop(Sender: TService;
var Stopped: Boolean);
begin
Stopped := dmPublic.Stop;
end;

procedure TSvSvrMain.ServiceBeforeInstall(Sender: TService);
begin
RegValueDelete(HKEY_LOCAL_MACHINE, CSRegServiceURL + Name, CSRegDescription);
end;

procedure TSvSvrMain.ServiceAfterInstall(Sender: TService);
begin
RegWriteString(HKEY_LOCAL_MACHINE, CSRegServiceURL + Name, CSRegDescription,
    CSServiceDescription);
RegWriteString(HKEY_LOCAL_MACHINE, CSRegServiceURL + Name, CSRegImagePath,
    ParamStr(0) + ' -svc');
end;

end.
这样,双击程序,则以普通程序方式运行,若用服务管理器来运行,则作为服务运行。
例如公共模块:
dmPublic,提供Start,Stop方法。

在主窗体中,调用dmPublic.Start,dmPublic.Stop方法。
同样在Service中,调用dmPublic.Start,dmPublic.Stop方法。

http://hi.baidu.com/sqldebug/blog/item/8e2749213082c0589922ed61.html

来自: http://hi.baidu.com/swlilike/blog/item/7ee4b411ee87d774ca80c4d1.html

如何在一个Service Application中运行另外一个exe文件,且可以显示在桌面上(转)

摘自:http://topic.csdn.net/t/20030117/19/1369782.html

我建立了一个Service   Application,中间有一个Thread,在一定条件下,我需要它运行另一个外部的程序。
可以当我启动服务,外部程序并没有~~显示~~的运行:在任务管理器中可以看到,但桌面上看不到显示。
我试了CreateProcess,   ShellExecute,   和WinExec都不行。

我发现在Server里调用的Process是System用户,所以我用CreateProcessAsUser,切换到当前用户,但还是没有窗口显示。

在CreateProcessAsUser中,有一个lpDesktop参数,若不指定,则继承父进程的lpDesktop,而父进程是以System登陆的。我想问题应该就出在这里。但如何才能得到当前正在使用的用户的lpDesktop呢?

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

DWORD   dwLogonFlags=LOGON_WITH_PROFILE;/*LOGON_NETCREDENTIALS_ONLY;//LOGON_NETCREDENTIALS_ONLY*/
DWORD   dwCreationFlags   =   CREATE_NEW_CONSOLE   |   CREATE_NEW_PROCESS_GROUP;
        STARTUPINFOW   si2;
ZeroMemory(&si2,   sizeof(STARTUPINFOW));
si2.cb                 =   sizeof(STARTUPINFOW);
si2.lpDesktop   =   L "winsta0\\default ";
PROCESS_INFORMATION   pi2;
ZeroMemory(&pi2,sizeof(PROCESS_INFORMATION));
wchar_t*   commandline=L "c:\\progra~1\\aaa\\bbb\\test ";
BOOL   bres2=CreateProcessWithLogonW(L "masterz ",L " ",L "*** ",
dwLogonFlags,NULL,commandline,dwCreationFlags,NULL,
L "c:\\progra~1\\aaa\\bbb ",
&si2,&pi2);
if(bres2)
{
OutputDebugString( "CreateProcessWithLogonW   succeeded ");
CloseHandle(pi2.hThread);
CloseHandle(pi2.hProcess);
}

关于CreateProcessAsUser的问题(转)

摘自:http://topic.csdn.net/t/20030912/17/2253782.html

Function   GetProcessHandleAsName(Name:String):THandle;
Var
        Hd,Hs:THandle;
        dExit:Cardinal;
        Tmp,Tmp1:String;
        Lp:TProcessEntry32;
begin
        Result:=0;
        Lp.dwSize:=sizeof(TProcessEntry32);
        Hd:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
        if   Process32First(Hd,Lp)   then
                Repeat
                        Tmp:=UpperCase(Trim(Name));
                        Tmp1:=Trim(UpperCase(Lp.szExeFile));
                        if   AnsiPos(Tmp,Tmp1)> 0   then
                        begin
                                Result:=OpenProcess(PROCESS_ALL_ACCESS,true,Lp.th32ProcessID);
                                break;
                        end
                Until   Process32Next(Hd,Lp)=False;
end;

procedure   CreateProc;
Var
        siStartupInfo:STARTUPINFO;
        saProcess,saThread:SECURITY_ATTRIBUTES;
        piProcInfo:PROCESS_INFORMATION;
        Hd:Cardinal;
        ProcessHd:THandle;
        Hds:THandle;
        Str:String;
begin
        ProcessHd:=GetProcessHandleAsName( 'Explorer ');
        if   ProcessHd=0   then
                Exit;
        if   OpenProcessToken(ProcessHd,TOKEN_ALL_ACCESS,Hds)   then
                if   DuplicateTokenEx(Hds,TOKEN_ALL_ACCESS,nil,SecurityIdentification,TokenPrimary,Hd)   then
                begin
                        ZeroMemory(@siStartupInfo,sizeof(siStartupInfo));
                        siStartupInfo.cb:=sizeof(siStartupInfo);
                        saProcess.nLength:=sizeof(saProcess);
                        saProcess.lpSecurityDescriptor:=nil;
                        saProcess.bInheritHandle:=false;
                        saThread.nLength:=sizeof(saThread);
                        saThread.lpSecurityDescriptor:=nil;
                        saThread.bInheritHandle:=false;
                        CreateProcessAsUser(Hd,nil,PChar(ProcessName),nil,nil,false,
                                CREATE_DEFAULT_ERROR_MODE,nil,nil,siStartupInfo,piProcInfo);
                end;
end;

2010年11月4日星期四

如何正确给CreateThread传递参数(转)

摘自:http://bbs.51cto.com/thread-599158-1.html

文章标题:如何正确给CreateThread传递参数?
文章作者:JJony
文章来源:http://blog.csdn.net/jzj_jony
注意:您可任意转载,但请注明作者和来源
    在网上我们也可以找到相关例子,不过用的是Delphi的TThread类,我个人不太爱用,一个线程也弄
的那么麻烦,不过各有各的好处,这里就不谈论Delphi的TThread类了,我们以在线程里运行MessageBoxA
显示一对话框为例(也就是线程MessageBoxA)。
我们先看看CreateThread的函数定义:
function CreateThread(lpThreadAttributes: Pointer;
                      dwStackSize: DWORD;
                      lpStartAddress: TFNThreadStartRoutine;
                      lpParameter: Pointer;
                      dwCreationFlags: DWORD;
                      var lpThreadId: DWORD): THandle; stdcall;
其中lpStartAddress,lpParameter,lpThreadId三个参数是必须的。
lpStartAddress参数指向的是线程执行体ThreadProc的开始地址;
lpParameter指针类型,线程的传入参数,我们如果想给线程执行体ThreadProc传递我们自己的数据,
           就要通过它了;
lpThreadId返回创建线程ID,这是我们控制线程必须的。
ThreadProc函数定义:
Function ThreadProc(lpParameter:Pointer):DWORD;stdcall;
下面给出具体实例:
因为我们要在线程里执行MessageBoxA所以线程函数可以这样写:
Function ThreadProc(lpParameter:Pointer):DWORD;stdcall;
var
h:hmodule;
MyMessagebox:function(hWnd: HWND; lpText, lpCaption: PChar; uType: UINT): Integer;stdcall;
begin
result:=0;
h:=LoadLibrary('user32.dll');
if h>0 then
begin
  @MyMessagebox:=GetProcAddress(h,'MessageBoxA');
  if @MyMessagebox<>nil then
  MyMessageBox(0 ,'线程MessageBoxA测试','提示',0);
  freeLibrary(h);
end;
end;
创建线程:
createthread(nil,0,@ThreadProc,p,0,TheThread);
上面我们动态调用了MessageBoxA并显示信息,这样就出现了问题,因为我们不可能每显示一个
MessageBoxA消息都要手动定义一个ThreadProc过程,那么我们如何做呢,就是利用lpParameter参数传递
lpParameter是指针类型,而MessageBoxA最主要的两个参数是Title和Msg,因此我们可以定义自己的结构
type
MYPARA=record
  title:pchar;
  str:pchar;
end;
PMYPARA=^MYPARA;
这样我们的ThreadProc过程就可以这样写
Function ThreadProc(Para:PMYPARA):DWORD;stdcall;
var
h:hmodule;
MyMessagebox:function(hWnd: HWND; lpText, lpCaption: PChar; uType: UINT): Integer;stdcall;
begin
result:=0;
h:=LoadLibrary('user32.dll');
if h>0 then
begin
  @MyMessagebox:=GetProcAddress(h,'MessageBoxA');
  if @MyMessagebox<>nil then
  MyMessageBox(0 ,Para^.str,Para^.title,0);
  freeLibrary(h);
end;
end;
创建线程可以这样:
var
  P:PMYPARA;
  ThreadHandle: THandle;
  TheThread: DWORD;
begin
  getmem(p,sizeof(p));//分配内存
  ThreadHandle:=0;
try
  p.title:='测试';    //填充
  p.str:='线程MessageBoxA';
  ThreadHandle:=createthread(nil,0,@ThreadProc,p,0,TheThread);
finally
  if ThreadHandle<>0 then closehandle(ThreadHandle);
  if p<>nil then freemem(p);
end;

至此一个完整的带有参数的CreateThread就完成了,希望对你有所帮助。
如有错误请指教。

指针以及关于Delphi指针的自我理解(C++&Delphi)(转)

摘自:http://www.7747.net/kf/201004/46591.html

以 前学C++,发现C++最强大的地方就是指针了(这个不是我发现的。。。大牛都这么说,我只是小菜一个),写东西的时候总是用指针,而且C++的很多函数 参数都是用的指针,特别是需要输出的参数,几乎都是用的指针传址,从而得到返回值,我发现很多API函数的指针类型参数都是[out]的说明。
      用了那么就的指针,自己也算是小小的弄明白了,指针就是用来寻址的(大牛别扔西瓜啊。。。),编程里面难免会遇到类型强制转换,而指针用的强制转换特别多,而我个人认为指针类型的强制转换很大一个原因是需要对地址进行寻址,不同的指针类型只是说寻址的时候的步长不同而已,别的没什么区别。举个例子来说:
      一个short的指针*ps和char的指针*pc,假设都指向地址1000,假设地址单位为字节,对于加一来说,ps+1就是说地址向后移动了一个字, 也就是说加完之后指向1002,而对于pc+1来说,后移一个字节,结果便是1001了。而对于强制转换来说,只是改变寻址步长。
       short *ps;
       char *pc;
      pc+1;//1001
      ps+1;//1002
      同样,上面的(char*)ps+1,得到的结果便是1001,而(short*)pc+1结果是1002。这个便是指针强制转换所带来的好处了。
      short *ps;
       char *pc;
      (short*)pc+1;//1002
      (char*)ps+1;//1001
      最近在学Delphi,刚开始学那些组件,然后在delphi中 用API函数,用到API函数的时候,通过函数声明,发现了不同点,C++的API说明中的指针参数很大一部分在Delphi中被换成了var的引用(万 一大师说的这其实也是传址),然后就看了点Delphi指针的说明,说Delphi也有指针,自己也学习了下Delphi的指针,总结一下:
      Delphi的指针本质上和C++没什么区别,都是地址,但是Delphi的指针的算数运算就没C++那么的灵活了(Delphi的指针更加严格),对于 “+”加来说,只支持PChar和整数相加(这个是在Object Pascal里面看到的),也就是说对于“+”和“-”操作来说,步长是一个字节,同样加1的话例:
      var
           pshort:ShortInt;
           psmall:SmallInt;
//同样假设初始指向1000,单位字节
     begin
     PChar(pshort)+1;//结果为1001
     PChar(psmall)+1;//结果也1001
     end;

要想实现C++一样的功能的话:
      var
           pshort:ShortInt;
           psmall:SmallInt;
//同样假设初始指向1000,单位字节
     begin
     PChar(pshort)+1;//结果为1001
     PChar(psmall)+sizeof(SmallInt)*1;//结果为1002,我这样写只是为了形象,PChar(psmall)+2;就OK了
     end;
若想实现和C++一样的强制转换来改变指针步长的话,便得这么写:
      var
           pshort:ShortInt;
           psmall:SmallInt;
//同样假设初始指向1000,单位字节
     begin
     PChar(pshort)+sizeof(SmallInt)*1;//结果为1002
     PChar(psmall)+1;//结果也1001
     end;

     这个好像没什么特别,反正Delphi都是将步长转为字节来算数加减的,所以这种强制转换没意义。

     不过Delphi提供的一个函数Inc能实现除了PChar类型指针的自加寻址,这个就是你的指针类型是什么类型,步长便是此指针类型的大小,例:
var
   pi:Pinteger;
   psmall:PSmallInt;
//同样假设初始地址1000,单位字节
begin
   inc(pi);//1004
   inc(psmall);//1002
   inc(psmall, 2);//1006
end;
      这些就是小弟作为一个菜鸟对指针的理解,当然这只是指针中的一小部分,要学的还很多,这个也只是写出来和菜鸟同志们互相交流,供大牛指点的。


您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力

DELPHI指针的使用 (转)

摘自:http://apps.hi.baidu.com/share/detail/1636614

大家都认为,C语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是C语言的灵魂,一点都不 为过。同时,这种说法也让很多人产生误解,似乎只有C语言的指针才能算指针。Basic不支持指针,在此不论。其实,Pascal语言本身也是支持指针 的。从最初的Pascal发展至今的Object     Pascal,可以说在指针运用上,丝毫不会逊色于C语言的指针。   
    
            以下内容分为八部分,分别是   
            一、类型指针的定义   
            二、无类型指针的定义   
            三、指针的解除引用   
            四、取地址(指针赋值)   
            五、指针运算   
            六、动态内存分配   
            七、字符数组的运算   
            八、函数指针   
    
    
            一、类型指针的定义。对于指向特定类型的指针,在C中是这样定义的:   
                    int     *ptr;   
                    char     *ptr;   
                    与之等价的Object     Pascal是如何定义的呢?     
                    var   
                    ptr     :     ^Integer;   
                    ptr     :     ^char;     
                    其实也就是符号的差别而已。   
    
            二、无类型指针的定义。C中有void     *类型,也就是可以指向任何类型数据的指针。Object     Pascal为其定义了一个专门的类型:Pointer。于是,   
                    ptr     :     Pointer;   
                    就与C中的   
                    void     *ptr;   
                    等价了。   
    
            三、指针的解除引用。要解除指针引用(即取出指针所指区域的值),C     的语法是     (*ptr),Object     Pascal则是     ptr^。   
    
            四、取地址(指针赋值)。取某对象的地址并将其赋值给指针变量,C     的语法是   
                    ptr     =     &Object;   
                    Object     Pascal     则是   
                    ptr     :=     @Object;   
                    也只是符号的差别而已。   
    
            五、指针运算。在C中,可以对指针进行移动的运算,如:   
                    char     a[20];       
                    char     *ptr=a;       
                    ptr++;   
                    ptr+=2;   
                    当执行ptr++;时,编译器会产生让ptr前进sizeof(char)步长的代码,之后,ptr将指向a[1]。ptr+=2;这句使得ptr前进两 个sizeof(char)大小的步长。同样,我们来看一下Object     Pascal中如何实现:   
                    var   
                            a     :     array     [1..20]     of     Char;   
                            ptr     :     PChar;     //PChar     可以看作     ^Char   
                    begin   
                            ptr     :=     @a;   
                            Inc(ptr);     //     这句等价于     C     的     ptr++;   
                            Inc(ptr,     2);     //这句等价于     C     的     ptr+=2;   
                    end;   
                    只是,Pascal中,只允许对有类型的指针进行这样的运算,对于无类型指针是不行的。   
    
            六、动态内存分配。C中,使用malloc()库函数分配内存,free()函数释放内存。如这样的代码:   
                    int     *ptr,     *ptr2;   
                    int     i;   
                    ptr     =     (int*)     malloc(sizeof(int)     *     20);   
                    ptr2     =     ptr;   
                    for     (i=0;     i<20;     i++){   
                            *ptr     =     i;   
                            ptr++;   
                    }   
                    free(ptr2);   
                    Object     Pascal中,动态分配内存的函数是GetMem(),与之对应的释放函数为FreeMem()(传统Pascal中获取内存的函数是New() 和     Dispose(),但New()只能获得对象的单个实体的内存大小,无法取得连续的存放多个对象的内存块)。因此,与上面那段C的代码等价的 Object     Pascal的代码为:   
                    var     ptr,     ptr2     :     ^integer;   
                            i     :     integer;   
                    begin   
                            GetMem(ptr,     sizeof(integer)     *     20);     
                                    //这句等价于C的     ptr     =     (int*)     malloc(sizeof(int)     *     20);   
                            ptr2     :=     ptr;     //保留原始指针位置   
                            for     i     :=     0     to     19     do   
                            begin   
                                    ptr^     :=     i;   
                                    Inc(ptr);   
                            end;   
                            FreeMem(ptr2);   
                    end;   
                    对于以上这个例子(无论是C版本的,还是Object     Pascal版本的),都要注意一个问题,就是分配内存的单位是字节(BYTE),因此在使用GetMem时,其第二个参数如果想当然的写成     20,那么就会出问题了(内存访问越界)。因为GetMem(ptr,     20);实际只分配了20个字节的内存空间,而一个整形的大小是四个字节,那么访问第五个之后的所有元素都是非法的了(对于malloc()的参数同 样)。   
    
            七、字符数组的运算。C语言中,是没有字符串类型的,因此,字符串都是用字符数组来实现,于是也有一套str打头的库函数以进行字符数组的运算,如以下代码:   
                    char     str[15];   
                    char     *pstr;   
                    strcpy(str,     "teststr");   
                    strcat(str,     "_testok");   
                    pstr     =     (char*)     malloc(sizeof(char)     *     15);   
                    strcpy(pstr,     str);   
                    printf(pstr);   
                    free(pstr);   
                    而在Object     Pascal中,有了String类型,因此可以很方便的对字符串进行各种运算。但是,有时我们的Pascal代码需要与C的代码交互(比如:用 Object     Pascal的代码调用C写的DLL或者用Object     Pascal写的DLL准备允许用C写客户端的代码)的话,就不能使用String类型了,而必须使用两种语言通用的字符数组。其 实,Object     Pascal提供了完全相似C的一整套字符数组的运算函数,以上那段代码的Object     Pascal版本是这样的:   
                    var     str     :     array     [1..15]     of     char;   
                            pstr     :     PChar;     //Pchar     也就是     ^Char   
                    begin   
                            StrCopy(@str,     'teststr');     //在C中,数组的名称可以直接作为数组首地址指针来用   
                                                                                //但Pascal不是这样的,因此     str前要加上取地址的运算符   
                            StrCat(@str,     '_testok');   
                            GetMem(pstr,     sizeof(char)     *     15);   
                            StrCopy(pstr,     @str);   
                            Write(pstr);   
                            FreeMem(pstr);   
                    end;   
    
            八、函数指针。在动态调用DLL中的函数时,就会用到函数指针。假设用C写的一段代码如下:   
                    typedef     int     (*PVFN)(int);     //定义函数指针类型   
                    int     main()   
                    {   
                            HMODULE     hModule     =     LoadLibrary("test.dll");   
            PVFN     pvfn     =     NULL;   
                            pvfn     =     (PVFN)     GetProcAddress(hModule,     "Function1");   
                            pvfn(2);   
                            FreeLibrary(hModule);   
                    }   
                    就我个人感觉来说,C语言中定义函数指针类型的typedef代码的语法有些晦涩,而同样的代码在Object     Pascal中却非常易懂:   
                    type     PVFN     =     Function     (para     :     Integer)     :     Integer;   
                    var   
                            fn     :     PVFN;     
                                    //也可以直接在此处定义,如:fn     :     function     (para:Integer):Integer;   
                            hm     :     HMODULE;   
                    begin   
                            hm     :=     LoadLibrary('test.dll');   
                            fn     :=     GetProcAddress(hm,     'Function1');   
                            fn(2);   
                            FreeLibrary(hm);   
                    end;