请znxia来帮忙,关于内存映射的问题.测试程序可以从下面提供的测试链接中下载.(100)

  • 主题发起人 wyd19790823
  • 开始时间
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
该程序的下载地址为;http://www.namipan.com/downfile/Test.rar/6ccec1b7a9dd8c2e73c3475cf6339ec55cb38580719a0000主程序代码如下:unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, EnumStuff;const MFileName: Pchar = 'MyShareData';type PShareData = ^TShareData; TShareData = record IsStartThread : Boolean; isBreak : Boolean; FileName : String[255]; Index : integer; CurrentItem : Integer; Total : Integer; MS : TMemoryStream; end; TForm1 = class(TForm) BitBtn1: TBitBtn; Label1: TLabel; edtExeFile: TEdit; procedure BitBtn2Click(Sender: TObject); procedure BitBtn1Click(Sender: TObject); procedure FormShow(Sender: TObject); private { Private declarations } function ReadFile(sFileName :String;Index,Total :Integer;Var MS :TMemoryStream):Boolean; function EnableDebugPriv : Boolean; function InjectDll(const DllFullPath : string;const dwRemoteProcessId : Cardinal): boolean; function UnInjectDll(const DllFullPath : string;const dwRemoteProcessId : Cardinal) : Boolean; public { Public declarations } end;var Form1: TForm1; GlobalData : PShareData; MapHandle: THandle;implementation{$R *.dfm}function TForm1.ReadFile(sFileName :String;Index,Total :Integer;Var MS :TMemoryStream):Boolean;var size: integer;begin size := sizeof(TShareData)+10240; //创建一个内存文档映射对象,MfileName保存的值就是该对象的名字。 mapHandle := CreateFileMapping(Dword(-1), nil, page_readWrite, 0, size, MFileName); if mapHandle = 0 then RaiseLastWin32Error; //把文档的视图映射到调用进程的地址空间,该函数的返回值就是该对象的首地址。注//意,这是调用进程的地址,两个应用程式调用该DLL,返回值是不相同的。 GlobalData := MapViewOfFile(mapHandle, File_map_all_Access, 0, 0, size); GlobalData^.FileName :=sFileName; GlobalData^.Index := 1; GlobalData^.Total := 1; While GlobalData^.Index>GlobalData^.CurrentItem do begin Application.ProcessMessages; GlobalData := MapViewOfFile(mapHandle, File_map_all_Access, 0, 0, size); end; GlobalData^.MS.Seek(0,soFromBeginning); MS.Clear; MS.CopyFrom(GlobalData^.MS,GlobalData^.MS.Size); Result :=True; if GlobalData = nil then begin CloseHandle(MapHandle); RaiseLastWin32Error; end;end;procedure TForm1.BitBtn2Click(Sender: TObject);Var MS :TMemoryStream;beginend;function TForm1.EnableDebugPriv : Boolean;var hToken : THANDLE; tp : TTokenPrivileges; rl : Cardinal;begin result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 result := AdjustTokenPrivileges(hToken, False, tp, sizeof(tp), nil, rl); end;end;function TForm1.InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): boolean;var hRemoteProcess, hRemoteThread: THANDLE; pszLibFileRemote : Pointer; pszLibAFilename: PwideChar; pfnStartAddr : TFNThreadStartRoutine; memSize, WriteSize, lpThreadId : Cardinal;begin result := FALSE; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId ); try // 为注入的dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * sizeof(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx( hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread <> 0) then result := TRUE; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;function TForm1.UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean;var hRemoteProcess, hRemoteThread : THANDLE; pszLibFileRemote : pchar; pszLibAFilename: PwideChar; pfnStartAddr : TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle : Cardinal;begin result := FALSE; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwRemoteProcessId ); try // 为注入的dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * sizeof(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx( hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread,INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject( hRemoteThread, INFINITE ); // 如果执行成功返回 True; if hRemoteProcess<>0 then result := TRUE; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath)+1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end;end;procedure TForm1.BitBtn1Click(Sender: TObject);Var Process : TProcessList; InjectOk :Boolean; i, InjectId:Integer; MS :TMemoryStream; MaunalStart,ProcessExist :Boolean; Label StartProgram;begin try ProcessExist :=False; MaunalStart :=False; InjectOk :=False; //首先枚举所有进程,看要注入的目标进程是否存在,如果不存在,则手动启动。 StartProgram: Process := GetProcessList; for I := Low(Process) to High(Process) do if LowerCase(Process.name) = LowerCase(ExtractFileName(edtExeFile.Text)) then begin InjectOk :=InjectDll(ExtractFilePath(ParamStr(0))+'MyDLL.dll', Process.pid); InjectID := Process.pid; ProcessExist :=True; end; if ProcessExist=False then begin if MaunalStart=False then begin WinExec(Pchar(edtExeFile.Text),SW_HIDE); //启动进程代码 MaunalStart :=True; Goto StartProgram; end else begin Application.MessageBox('目标进程不能启动!',Pchar(Caption),MB_IconError+MB_Ok); Exit; end; end; Sleep(500); if InjectOk then begin try MS :=TMemoryStream.Create; if ReadFile(ExtractFilePath(ParamStr(0))+'abc.txt',1,1,MS) then begin MS.SaveToFile('E:/MyFile.Dat'); showmessage('Success'); end; finally MS.Free; end; end; finally if InjectOk then UnInjectDll(ExtractFilePath(ParamStr(0))+'MyDLL.dll',InjectID); end;end;procedure TForm1.FormShow(Sender: TObject);begin //edtExeFile.Text :=Application.ExeName;end;end.MyDll.dll文件代码如下:library Project1;uses shareMem, dialogs, windows, SysUtils, Classes, RWShareData in 'RWShareData.pas', MyType in 'MyType.pas';Var MyThread: TRWDataThread;procedure GetShareData(var AShareData: PShareData);stdcall;begin AShareData := GlobalData;end;procedure OpenThisData; begin Size := Sizeof(TShareData)+10240; //创建一个内存文档映射对象,MfileName保存的值就是该对象的名字。 mapHandle := CreateFileMapping(Dword(-1), nil, Page_ReadWrite, 0, Size, MFileName); if mapHandle = 0 then RaiseLastWin32Error; //把文档的视图映射到调用进程的地址空间,该函数的返回值就是该对象的首地址。 //注意,这是调用进程的地址,两个应用程式调用该DLL,返回值是不相同的。 GlobalData := MapViewOfFile(mapHandle, File_Map_All_Access, 0, 0, Size); if GlobalData = nil then begin CloseHandle(MapHandle); RaiseLastWin32Error; end else begin if GlobalData^.IsStartThread=False then begin MyThread := TRWDataThread.Create(True); GlobalData^.IsStartThread :=True; MyThread.Resume; end; end;end;//DLL从进程中分离出来时,应该释放相应的空间procedure CloseThisData;begin unmapViewOfFile(GlobalData); closeHandle(MapHandle);end;procedure DllEntryPoint(dwReason: DWord);begin case dwReason of Dll_Process_Attach: begin OpenThisData; //调用DLL时传入的参数,由系统自动传入 end; Dll_Process_Detach: CloseThisData; //释放DLL时传入的参数,系统自动传入。 end;end;{$R *.res}exports GetShareData; //外部应用程式调用的就是这个过程。 begin DllProc := @DllEntryPoint; //该变量是个全局变量,由他来指定DLL的入口及出 //口函数。 DllEntryPoint(Dll_Process_Attach);end.我的目的是,主程序(Project2.exe)把MyDll.dll注入程序,再传递文件名进去,MyDll.dll读取文件,读取成功后,以TMemoryStream的形式返回给主程序.
 
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
由于我用的是内存映射,主程序有可能连续传十几个文件给MyDll.dll,然后一个一个的返回TMemoryStream,请各位高手帮帮忙,您可以从上面的链接中下载测试程序,如果修改好了,请在这里贴出代码来,谢谢!
 
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
我对内存映射不太懂,麻烦各位指点一下,我的方法有没有问题.
 

不能没有你

Unregistered / Unconfirmed
GUEST, unregistred user!
你的程序很复杂,我看不明白你最终的需求是什么。如果是共享数据的话,应该不比这么复杂吧。。。
 
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
你好! 假如我有二个程序,A.exe,B.exe及MyDll.dll,由于程序需要,我需要把MyDll.dll注入到B.exe中,MyDll.dll是无窗体的DLL,然后A.exe向MyDll.dll传递文件名,MyDll.dll再把读出来的文件以内存流的形式传递给A.exe.MyDll.dll要不断的检查是否有文件传入,如果有的话,就读取,读取成功之后继续检查,循环下去! 请不要让我用A.exe直接去读文件,因为我的A.exe的有某种需求,不能这样做!
 

地质灾害

Unregistered / Unconfirmed
GUEST, unregistred user!
type PShareData = ^TShareData; TShareData = record FileName : String[255]; Index : integer; CurrentItem : Integer; Total : Integer; MS : TMemoryStream; end;这个MS:TMemoryStream,这是个指针。MyDll.dll在B.exe进程里读文件,这个MS就是B.exe进程空间里的指针,你A.exe用当然不行了。这样子定义type PShareData = ^TShareData; TShareData = record FileName : String[255]; Index : integer; CurrentItem : Integer; Total : Integer; FileSize; Integer; end;然后开内存映射时,设置大一点的缓冲区(足以容纳你要读的最大文件 + SizeOf(TShareData ))。MyDLL把文件内容读到TShareData的后面,并设置FileSize为文件的长度。A.exe就可以读取了。
 
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
to 地质灾害你好,目前的问题不是在这里,麻烦你先下动测试程序来测试一下,问题在于当A.exe改变值后,MyDll.dll在循环时不执行While GlobalData^.Index=0 do.....
 
D

de410

Unregistered / Unconfirmed
GUEST, unregistred user!
你贴的代码和你的test程序不一样啊?
 
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
to de410我的贴完代码后,又修改了一下测试程序,所以有些地方改动.
 
Z

znxia

Unregistered / Unconfirmed
GUEST, unregistred user!
内存映射不熟悉,呵呵,帮顶!
 
L

luoyanqing119

Unregistered / Unconfirmed
GUEST, unregistred user!
kao,贴那么多代码出来,没给出问题出在那。直接让人看那么多代码,随有那么好的闲情喔:
 
W

wyd19790823

Unregistered / Unconfirmed
GUEST, unregistred user!
多人接受答案了。
 

Similar threads

I
回复
0
查看
535
import
I
S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
816
SUNSTONE的Delphi笔记
S
顶部