I am making my own installer and it's almost complete. The only thing lacking is the sound when installation is complete. Is that a windows API call or I will need to find that audio file and play that from the source code?
Use the MessageBeep function.
This Small Collection of Functions Will Load, Playback, Stop, and Dump (Free Memory) for any MCI Supported Sound Files. [*.wav, *.mp3, *.wma, etc...]
uses MMSystem;
function LoadMediaFile(absoluteFile,clipName: String): Integer;
var
pc2: PChar;
pc3: String;
begin
pc3 := '"'+absoluteFile+'"';
pc2 := PChar('Open ' + pc3 + ' Alias '+ clipName);
Result := mciSendString(pc2, PChar(0), 0, 0);
end;
function StartMediaFile(clipName: String) : Integer;
var
pc2: PChar;
begin
pc2 := PChar('Play ' + clipName + ' From ' + '0');
Result := mciSendString(pc2, PChar(0), 0, 0);
end;
function StopMediaFile(clipName: String): Integer;
var
pc2: PChar;
i: Integer;
begin
pc2 := PChar('Stop ' + clipName + ' wait');
i := 0;
while (mciSendString(pc2, PChar(0), 0, 0)<>0) and (i < 250) do
begin
Result := mciSendString(pc2, PChar(0), 0, 0); i := i + 1;
end;
end;
function DumpMediaFile(clipName: String): Integer;
var
pc2,pc3: PChar;
i: Integer;
begin
pc2 := PChar('Stop ' + clipName + ' wait');
pc3 := PChar('Close ' + clipName + ' Wait');
i := 0;
while (mciSendString(pc2, PChar(0), 0, 0)<>0) and (i < 250) do
begin
mciSendString(pc2, PChar(0), 0, 0); i := i + 1;
end;
i := 0;
while (mciSendString(pc3, PChar(0), 0, 0)<>0) and (i < 250) do
begin
Result := mciSendString(pc3, PChar(0), 0, 0); i := i + 1;
end;
end;
Use them like this:
ResultInteger1 := LoadMediaFile('X:\Path\To\File.WAV', 'ClipName');
ResultInteger2 := StartMediaFile('ClipName');
Sleep(3000);
ResultInteger3 := StopMediaFile('ClipName');
ResultInteger4 := DumpMediaFile('ClipName');
Will Play 3 Seconds of The X:\Path\To\File.WAV file.
You can use:
if ResultInteger2 <> 0 then ShowMessage('ClipName did not play.');
//or
if ResultInteger2 = 0 then ShowMessage('ClipName did play.');
You can easily play the default system sounds by using:
System.Media.SystemSounds.Beep.Play();
System.Media.SystemSounds.Asterisk.Play();
System.Media.SystemSounds.Exclamation.Play();
System.Media.SystemSounds.Hand.Play();
System.Media.SystemSounds.Question.Play();
Related
I have a following function to generate random passwords:
function GeneratePassword(ALength: Integer; Mode: TPasswordMode): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
i : Integer;
S : string;
iM: BYTE;
begin
if Mode = [] then Exit;
i := 0;
Randomize;
while (i < ALength) do
begin
iM := RANDOM(4);
case iM of
0: if (pmLower in Mode) then begin
S := S + cLower[1+RANDOM(Length(cLower))];
Inc(i);
end;
1: if (pmUpper in Mode) then begin
S := S + cUpper[1+RANDOM(Length(cUpper))];
Inc(i);
end;
2: if (pmNumbers in Mode) then begin
S := S + cNumbers[1+RANDOM(Length(cNumbers))];
Inc(i);
end;
3: if (pmExtra in Mode) then begin
S := S + cExtra[1+RANDOM(Length(cExtra))];
Inc(i);
end;
end;
end;
Result := S;
end;
How to make this function so that a capital letter and a special character appear only once, but always? Sometimes there is no capital letter or special character when I'm generating passwords.
To be sure to have one special char and one uppercase you can do that :
function GeneratePassword(ALength: Integer; Mode: TPasswordModes): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
iM: Byte;
i: integer;
begin
if Mode = [] then Exit;
Result := '';
i := 0;
if pmUpper in Mode then
Inc(i);
if pmExtra in Mode then
Inc(i);
// add lower case and/or number
while Result.Length < (ALength - i) do
begin
iM := Random(2);
case iM of
0: if (pmLower in Mode) then begin
Result := Result + cLower[1 + Random(Length(cLower))];
end;
1: if (pmNumbers in Mode) then begin
Result := Result + cNumbers[1 + Random(Length(cNumbers))];
end;
end;
end;
// add uppercase and/or extra
if i > 0 then
begin
if pmUpper in Mode then
Result := Result.Insert(1 + Random(Length(Result)), cUpper[1 + Random(Length(cUpper))]);
if pmExtra in Mode then
Result := Result.Insert(1 + Random(Length(Result)), cExtra[1 + Random(Length(cExtra))]);
end;
end;
type
TPasswordMode = (pmLower, pmUpper, pmNumbers, pmExtra);
TPasswordModes = set of TPasswordMode;
implementation
function GeneratePassword(ALength: Integer; Mode: TPasswordModes): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
i : Integer;
S : string;
iM: BYTE;
begin
if Mode = [] then Exit;
i := 0;
Randomize;
while (i < ALength) do
begin
iM := RANDOM(4);
case iM of
0: if (pmLower in Mode) then begin
S := S + cLower[1+RANDOM(Length(cLower))];
Inc(i);
end;
1: if (pmUpper in Mode) then begin
S := S + cUpper[1+RANDOM(Length(cUpper))];
Inc(i);
Mode := Mode - [pmUpper]; // This I added
end;
2: if (pmNumbers in Mode) then begin
S := S + cNumbers[1+RANDOM(Length(cNumbers))];
Inc(i);
end;
3: if (pmExtra in Mode) then begin
S := S + cExtra[1+RANDOM(Length(cExtra))];
Inc(i);
Mode := Mode - [pmExtra]; // This I added
end;
end;
end;
Result := S;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(GeneratePassword(10,[pmLower,pmUpper,pmNumbers,pmExtra]));
end;
This is not a complete solution but with this you will at least remove Upper and Extra from the requirements as soon as they get taken. You now check in the end if they ever were ever added if required and then add them if so required.
Edit:
I was in a hurry when I typed the above. You just need to check in the end if the generated password contains an Upper and Extra character. If not, you still need to add them as that was one of your requirements.
Here is example that first makes sure all extra modes are filled and the rest. It prefills Result with spaces and then replaces with random chars until all spaces are replaced.
function GetRandomEmptyPos(const aStr: string): integer; inline;
begin
// find random empty position
repeat
Result := Random(Length(aStr)) + 1;
until aStr[Result] = ' ';
end;
function GeneratePassword2(aLength: Integer; aModes: TPasswordModes): string;
const
cLower = 'abcdefghijklmnopqrstuvwxyz';
cUpper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
cNumbers = '0123456789';
cExtra = '_.';
var
i,vPos: integer;
vMode: TPasswordMode;
begin
if (aLength = 0) or (aModes = []) then Exit;
Randomize;
// Prefill Result with empty spaces
Result := StringOfChar(' ', aLength);
// Add extra characters at random places
for vMode in aModes do
begin
vPos := GetRandomEmptyPos(Result);
case vMode of
pmLower: Result[vPos] := cLower[Random(Length(cLower)) + 1];
pmUpper: Result[vPos] := cUpper[Random(Length(cUpper)) + 1];
pmNumbers: Result[vPos] := cNumbers[Random(Length(cNumbers)) + 1];
pmExtra: Result[vPos] := cExtra[Random(Length(cExtra)) + 1];
end;
end;
// Add random char on emtpy spaces
for i := 1 to Result.Length do
if Result[i] = ' ' then
Result[i] := String(cLower + cNumbers)[Random(Length(cLower) + Length(cNumbers)) + 1];
end;
unrefined code but maybe it can be useful ...
function RandomPassword(PLen: Integer): string;
var
strBase: string;
strUpper: string;
strSpecial: string;
strRecombine: string;
begin
strRecombine:='';
Result := '';
Randomize;
//string with all possible chars
strBase := 'abcdefghijklmnopqrstuvwxyz1234567890';
strUpper:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
strSpecial:='#!_';
// Start Random
strRecombine:= strUpper[Random(Length(strUpper)) + 1];
Result:=strRecombine;
strRecombine:= strSpecial[Random(Length(strSpecial))+1];
repeat
Result := Result + strBase[Random(Length(strBase)) + 1];
until (Length(Result) = PLen);
RandomRange(2, Length(strBase));
Result[RandomRange(2, PLen)]:=strRecombine[1];
//result:=Result+strRecombine;
end;
I use the following code to check, if there already exists an Internet Explorer 11 Tab with a given Url Location.
I started with code from here: http://francois-piette.blogspot.de/2013/01/internet-explorer-automation-part-1.html
function GetIERunningInstanceByUrl(FLogWriter: ILogWriter; const Url : String): IWebBrowser2;
function GetClassName(aHWND : HWND) : String;
var
L : Integer;
begin
SetLength(Result, MAX_PATH * SizeOf(Char));
L := WinApi.Windows.GetClassName(aHWND, PChar(Result), Length(Result));
SetLength(Result, L);
end;
var
aShWindows : IShellWindows;
aIdx : Integer;
aDisp: IDispatch;
aClassName: string;
begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / Url: ''' + Url + '''.');
aShWindows := CoShellWindows.Create;
if not Assigned(aShWindows) then begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / After CoShellWindows.Create, not Assigned(aShWindows) = TRUE.');
end;
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / aShWindows.Count: ' + IntToStr(aShWindows.Count) + '.');
for aIdx := 0 to aShWindows.Count - 1 do begin
aDisp := aShWindows.Item(aIdx);
if not Assigned(aDisp) then begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / After aDisp := aShWindows.Item(aIdx=' + IntToStr(aIdx) + '), not Assigned(aDisp) = TRUE.');
end
else begin
if not Supports(aDisp, IID_IWebBrowser2) then begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / Supports(aDisp, IID_IWebBrowser2) = FALSE.');
end
else begin
Result := aDisp as IWebBrowser2;
if not Assigned(Result) then begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / After Result := aDisp as IWebBrowser2, not Assigned(Result) = TRUE.');
end
else begin
aClassName := GetClassName(Result.HWND);
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / GetClassName(aShWindows.Item(aIdx=' + IntToStr(aIdx) + ').Result): ''' + aClassName + '''.');
end;
end;
end;
if Supports(aDisp, IID_IWebBrowser2) then begin
if Assigned(Result) then begin
if SameText(GetClassName(Result.HWND), 'IEFrame') then begin
//if SameText(Url, Result.LocationURL) then begin
if ContainsText(Result.LocationURL, Url) then begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / Found, Result.HWND: ' + IntToStr(Result.HWND) + ', Result.LocationURL: ''' + Result.LocationURL + '''.');
Exit;
end
else begin
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / Not found, ContainsText(Result.LocationURL, Url) = FALSE, Result.LocationURL: ''' + Result.LocationURL + ''' .');
end;
end
else begin
aClassName := GetClassName(Result.HWND);
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / Not found, SameText(GetClassName(Result.HWND), ''IEFrame'') = FALSE, aClassName: ''' + aClassName + ''' .');
end;
end;
end;
end;
// Not found
Result := nil;
FLogWriter.LogMessage(ltDebug, 'GetIERunningInstanceByUrl / Not found, After Result = nil, Url: ''' + Url + '''.');
end;
The application is installed on machines, that all have Windows 7 Professional Service Pack 1, 64Bit and Internet Explorer 11 (Version 11.0.9600.18762).
The code works fine on most machines, but there are some machines, where I get an 'Unknown error' in this method, after it was running correctly for several times.
When having the error once, the only way to get the application running again, is to logoff from windows and login again.
Unfortunatelly, I may not debug on those (production) machines, so I got to use poor man's debugging, logging every line... (that is also the reason, why my above code became a little bit ugly to read on some lines, sorry.)
Doing that, I found, that it must be something related to the IShellWindows interface.
10.08.2017 10:33:05 ThreadID: 0x00001A08 - GetIERunningInstanceByUrl / Url: 'https://example.com/'.
10.08.2017 10:33:05 ThreadID: 0x00001A08 - GetIERunningInstanceByUrl / aShWindows.Count: 3.
10.08.2017 10:33:05 ThreadID: 0x00001A08 - GetIERunningInstanceByUrl / GetClassName(aShWindows.Item(aIdx=0).Result): 'CabinetWClass'.
10.08.2017 10:33:05 ThreadID: 0x00001A08 - GetIERunningInstanceByUrl / Not found, SameText(GetClassName(Result.HWND), 'IEFrame') = FALSE, aClassName: 'CabinetWClass' .
10.08.2017 10:33:05 ThreadID: 0x00001A08 - Meldung - Exception: Unbekannter Fehler Retry: 1
(translation: 'Unbekannter Fehler' means 'Unknown error')
In the above log sample, it seems that only the first item from the List of 3 items can be iterated using IShellWindows. Then an exception is raised.
Any help would be appreciated...
I had a similar problem - or still have with a program that uses the IShellWindows interface. My experience is that it does not depend on the machine, but can happen on any machine, but I did not found out what to do to prevent it.
What helps for me is to stop all explorer processes (not the Internet Explorer process!). I do this from within my program, but you can also do this via the Task Manager for testing. If the Task Bar runs in a seperate Explorer process, you will have to stop that too.
After you have restarted the Explorer, the interface works again. This is slightly better than to have to log off, since you do not need to restart all your applications and you can do this from within your code, but this is of course still no good solution, since the Task Bar will be rebuild during this process.
The code I use to Close all Explorer processes and restart is the following:
function isexplorerwindow(exwnd: hwnd): boolean;
var
p: array[0..max_path] of Char;
begin
GetClassName(exwnd, p, SizeOf(p));
result := ((strcomp(p, 'CabinetWClass') = 0) or (strcomp(p, 'ExploreWClass') = 0));
end;
procedure restartshell;
var
wnd: hwnd;
pid: dword;
processhandle: thandle;
SL: tstringlist;
z: integer;
StartUpInfo: TStartUpInfo;
ProcessInfo: TProcessInformation;
begin
if messagebox(0, pchar(_('Restarting the shell will close all explorer windows and the task bar.') + #13#10 +
_('Do you really want to continue?')), __('Warning'), mb_yesno or mb_defbutton2 or mb_iconquestion) = idno then
Exit;
SL := tstringlist.Create;
wnd := getwindow(getdesktopwindow, gw_child);
while (wnd <> 0) do
begin
if isexplorerwindow(wnd) then
SL.Add(inttostr(wnd));
wnd := getwindow(wnd, gw_hwndnext);
end;
for z := 0 to SL.count - 1 do
postMessage(strtoint(SL[z]), $10, 0, 0);
SL.Free;
application.ProcessMessages;
sleep(1000);
application.ProcessMessages;
wnd := findwindow('Progman', nil);
if wnd > 0 then
begin
GetWindowThreadProcessId(wnd, pid);
if (pid > 0) then
begin
processhandle := OpenProcess(1, false, pid);
if (processhandle > 0) then
begin
TerminateProcess(processhandle, 0);
CloseHandle(processhandle);
end;
end;
end;
application.ProcessMessages;
sleep(1000);
application.ProcessMessages;
FillChar(StartUpInfo, SizeOf(StartUpInfo), #0);
StartUpInfo.cb := SizeOf(StartUpInfo);
StartUpInfo.dwFlags := STARTF_USESHOWWINDOW;
if not CreateProcess(nil, 'explorer.exe', nil, nil, false, CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil, nil,
StartUpInfo, ProcessInfo) then
messagebeep(mb_iconstop);
You can try this. It should return the topmost IE window that is navigated to the given URL:
function TryGetWebBrowser(const URL: WideString; out Browser: IWebBrowser2): Boolean;
var
Handle: HWND;
Unused: OleVariant;
Location: OleVariant;
WndIface: IDispatch;
ShellWindows: IShellWindows;
begin
Result := False;
if Succeeded(CoCreateInstance(CLASS_ShellWindows, nil, CLSCTX_LOCAL_SERVER, IID_IShellWindows, ShellWindows)) then
begin
Unused := Unassigned;
Location := URL;
WndIface := ShellWindows.FindWindowSW(Location, Unused, SWC_BROWSER, Integer(Handle), SWFO_NEEDDISPATCH);
Result := Assigned(WndIface) and Succeeded(WndIface.QueryInterface(IWebBrowser2, Browser));
end;
end;
I want to loop through all the files in a given directory and return their version number and exe name. I have tried digging into the shell to see if I can pull this off, however I have not been able to find a solution. Any tips would be appreciated.
This does it:
Drop a TMemo and a TButton on your form and do
type
TVerInfo = packed record
vMajor, vMinor, vRelease, vBuild: word;
end;
function GetFileVerNumbers(const FileName: string): TVerInfo;
var
len, dummy: cardinal;
verdata: pointer;
verstruct: pointer;
const
InvalidVersion: TVerInfo = (vMajor: 0; vMinor: 0; vRelease: 0; vBuild: 0);
begin
len := GetFileVersionInfoSize(PWideChar(FileName), dummy);
if len = 0 then
Exit(InvalidVersion);
GetMem(verdata, len);
try
GetFileVersionInfo(PWideChar(FileName), 0, len, verdata);
VerQueryValue(verdata, '\', verstruct, dummy);
result.vMajor := HiWord(TVSFixedFileInfo(verstruct^).dwFileVersionMS);
result.vMinor := LoWord(TVSFixedFileInfo(verstruct^).dwFileVersionMS);
result.vRelease := HiWord(TVSFixedFileInfo(verstruct^).dwFileVersionLS);
result.vBuild := LoWord(TVSFixedFileInfo(verstruct^).dwFileVersionLS);
finally
FreeMem(verdata);
end;
end;
function GetFileVer(const FileName: string): string;
begin
with GetFileVerNumbers(FileName) do
result := IntToStr(vMajor) + '.' +
IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' +
IntToStr(vBuild);
end;
procedure TForm1.Button1Click(Sender: TObject);
const
path = 'C:\WINDOWS';
var
SR: TSearchRec;
begin
Memo1.Clear;
if FindFirst(IncludeTrailingBackslash(path) + '*.exe', faAnyFile, SR) = 0 then
try
repeat
Memo1.Lines.Add(SR.Name + #9 +
GetFileVer(IncludeTrailingBackslash(path) + SR.Name));
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
end;
I want to execute an EXE File that is compiled with my application as a Resource. I want to execute it directly in Memory.
I have seen this Topic :
Is it possible to embed and run exe file in a Delphi executable app?
And this Code :
http://www.coderprofile.com/networks/source-codes/138/execute-resource-directly-in-memory
I used this Code :
type
TSections = array [0..0] of TImageSectionHeader;
...
{$IMAGEBASE $10000000}
function GetAlignedSize(Size: dword; Alignment: dword): dword;
begin
if ((Size mod Alignment) = 0) then
Result := Size
else
Result := ((Size div Alignment) + 1) * Alignment;
end;
function ImageSize(Image: pointer): dword;
var
Alignment: dword;
ImageNtHeaders: PImageNtHeaders;
PSections: ^TSections;
SectionLoop: dword;
begin
ImageNtHeaders := pointer(dword(dword(Image)) + dword(PImageDosHeader(Image)._lfanew));
Alignment := ImageNtHeaders.OptionalHeader.SectionAlignment;
if ((ImageNtHeaders.OptionalHeader.SizeOfHeaders mod Alignment) = 0) then
begin
Result := ImageNtHeaders.OptionalHeader.SizeOfHeaders;
end
else
begin
Result := ((ImageNtHeaders.OptionalHeader.SizeOfHeaders div Alignment) + 1) * Alignment;
end;
PSections := pointer(pchar(#(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);
for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
if PSections[SectionLoop].Misc.VirtualSize <> 0 then
begin
if ((PSections[SectionLoop].Misc.VirtualSize mod Alignment) = 0) then
begin
Result := Result + PSections[SectionLoop].Misc.VirtualSize;
end
else
begin
Result := Result + (((PSections[SectionLoop].Misc.VirtualSize div Alignment) + 1) * Alignment);
end;
end;
end;
end;
procedure CreateProcessEx(FileMemory: pointer);
var
BaseAddress, Bytes, HeaderSize, InjectSize, SectionLoop, SectionSize: dword;
Context: TContext;
FileData: pointer;
ImageNtHeaders: PImageNtHeaders;
InjectMemory: pointer;
ProcInfo: TProcessInformation;
PSections: ^TSections;
StartInfo: TStartupInfo;
begin
ImageNtHeaders := pointer(dword(dword(FileMemory)) + dword(PImageDosHeader(FileMemory)._lfanew));
InjectSize := ImageSize(FileMemory);
GetMem(InjectMemory, InjectSize);
try
FileData := InjectMemory;
HeaderSize := ImageNtHeaders.OptionalHeader.SizeOfHeaders;
PSections := pointer(pchar(#(ImageNtHeaders.OptionalHeader)) + ImageNtHeaders.FileHeader.SizeOfOptionalHeader);
for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
if PSections[SectionLoop].PointerToRawData < HeaderSize then HeaderSize := PSections[SectionLoop].PointerToRawData;
end;
CopyMemory(FileData, FileMemory, HeaderSize);
FileData := pointer(dword(FileData) + GetAlignedSize(ImageNtHeaders.OptionalHeader.SizeO fHeaders, ImageNtHeaders.OptionalHeader.SectionAlignment));
for SectionLoop := 0 to ImageNtHeaders.FileHeader.NumberOfSections - 1 do
begin
if PSections[SectionLoop].SizeOfRawData > 0 then
begin
SectionSize := PSections[SectionLoop].SizeOfRawData;
if SectionSize > PSections[SectionLoop].Misc.VirtualSize then SectionSize := PSections[SectionLoop].Misc.VirtualSize;
CopyMemory(FileData, pointer(dword(FileMemory) + PSections[SectionLoop].PointerToRawData), SectionSize);
FileData := pointer(dword(FileData) + GetAlignedSize(PSections[SectionLoop].Misc.VirtualSize, ImageNtHeaders.OptionalHeader.SectionAlignment));
end
else
begin
if PSections[SectionLoop].Misc.VirtualSize <> 0 then FileData := pointer(dword(FileData) + GetAlignedSize(PSections[SectionLoop].Misc.VirtualSize, ImageNtHeaders.OptionalHeader.SectionAlignment));
end;
end;
ZeroMemory(#StartInfo, SizeOf(StartupInfo));
ZeroMemory(#Context, SizeOf(TContext));
CreateProcess(nil, pchar(ParamStr(0)), nil, nil, False, CREATE_SUSPENDED, nil, nil, StartInfo, ProcInfo);
Context.ContextFlags := CONTEXT_FULL;
GetThreadContext(ProcInfo.hThread, Context);
ReadProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), #BaseAddress, 4, Bytes);
VirtualAllocEx(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase), InjectSize, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(ProcInfo.hProcess, pointer(ImageNtHeaders.OptionalHeader.ImageBase), InjectMemory, InjectSize, Bytes);
WriteProcessMemory(ProcInfo.hProcess, pointer(Context.Ebx + 8), #ImageNtHeaders.OptionalHeader.ImageBase, 4, Bytes);
Context.Eax := ImageNtHeaders.OptionalHeader.ImageBase + ImageNtHeaders.OptionalHeader.AddressOfEntryPoint;
SetThreadContext(ProcInfo.hThread, Context);
ResumeThread(ProcInfo.hThread);
finally
FreeMemory(InjectMemory);
end;
end;
procedure Execute;
var
RS : TResourceStream;
begin
RS := TResourceStream.Create(HInstance, 'MrResource', RT_RCDATA);
try
CreateProcessEx(RS.Memory);
finally
RS.Free;
end;
end;
but I got " Out of Memory " error in this line ( of CreateProcessEX ) :
GetMem(InjectMemory, InjectSize);
can someone help me solve this error ? or give me some working code/solution ?
thanks before ...
An excelent unit for what you need has already been done with support for windows 64 bit.
you can find it here:
uExecFromMem by steve10120 fixed by test
here is a trivial approach written by me if you don't want to use that unit
var
eu:array of byte;
FS:TFileStream;
CONT:TContext;
imgbase,btsIO:DWORD;
IDH:PImageDosHeader;
INH:PImageNtHeaders;
ISH:PImageSectionHeader;
i:Integer;
PInfo:TProcessInformation;
SInfo:TStartupInfo;
begin
if OpenDialog1.Execute then
begin
FS:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead or fmShareDenyNone);
SetLength(eu,FS.Size);
FS.Read(eu[0],FS.Size);
FS.Free;
Sinfo.cb:=Sizeof(TStartupInfo);
CreateProcess(nil,Pchar(paramstr(0)),nil,nil,FALSE,CREATE_SUSPENDED,nil,nil,SInfo,PInfo);
IDH:=#eu[0];
INH:=#eu[IDH^._lfanew];
imgbase:=DWORD(VirtualAllocEx(PInfo.hProcess,Ptr(INH^.OptionalHeader.ImageBase),INH^.OptionalHeader.SizeOfImage,MEM_COMMIT or MEM_RESERVE,PAGE_EXECUTE_READWRITE));
ShowMessage(IntToHex(imgbase,8));
WriteProcessMemory(PInfo.hProcess,Ptr(imgbase),#eu[0],INH^.OptionalHeader.SizeOfHeaders,btsIO);
for i:=0 to INH^.FileHeader.NumberOfSections - 1 do
begin
ISH:=#eu[IDH^._lfanew + Sizeof(TImageNtHeaders) + i * Sizeof(TImageSectionHeader)];
WriteProcessMemory(PInfo.hProcess,Ptr(imgbase + ISH^.VirtualAddress),#eu[ISH^.PointerToRawData],ISH^.SizeOfRawData,btsIO);
end;
CONT.ContextFlags:=CONTEXT_FULL;
GetThreadContext(PInfo.hThread,CONT);
CONT.Eax:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
WriteProcessMemory(PInfo.hProcess,Ptr(CONT.Ebx+8),#imgbase,4,btsIO);
ShowMessage('Press ok on ENTER');
SetThreadContext(PInfo.hThread,CONT);
ResumeThread(PInfo.hThread);
CloseHandle(Pinfo.hThread);
CloseHandle(PInfo.hProcess);
end;
end;
To get opc0de's answer working on both 32bit and 64bit platforms change the context setting as follows,
GetThreadContext(PInfo.hThread,CONT);
{$IFDEF WIN64}
CONT.P6Home:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
WriteProcessMemory(PInfo.hProcess,Ptr(CONT.P3Home+8),#imgbase,4,btsIO);
{$ELSE}
CONT.Eax:=imgbase + INH^.OptionalHeader.AddressOfEntryPoint;
WriteProcessMemory(PInfo.hProcess,Ptr(CONT.Ebx+8),#imgbase,4,btsIO);
{$ENDIF}
ShowMessage('Press ok on ENTER');
SetThreadContext(PInfo.hThread,CONT);
Your expected API pointer layout sounds not correct, and the returned size is not.
How did you define all the PImageNtHeaders and such TSections types? What is the record alignment? Shouldn't it need to be packed or aligned with some granularity? Perhaps you forgot some {$A..} or enumeration size when copy/paste the original code into your unit...
Difficult to guess without the whole source code.
Is there any simple function? I am searching something like that
Play(#data, 44000, 100 {time});
I have worked quite a lot with PCM audio manipulation. I always use this function when playing short sequences of custom waveform audio data:
var
PlaySoundStopper: PBoolean;
SoundPlayerActive: boolean = false;
procedure PlaySound(const Sound: TASSound);
var
hWave: HWAVEOUT;
hdr: TWaveHdr;
buf: PAnsiChar;
fmt: TWaveFormatEx;
i: Integer;
n: Integer;
begin
try
with fmt do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := length(Sound.Channels);
nSamplesPerSec := Sound.SampleRate;
wBitsPerSample := 32;
nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
nBlockAlign := nChannels * wBitsPerSample div 8;
cbSize := 0;
end;
GetMem(buf, fmt.nChannels * length(Sound.Channels[0]) * sizeof(TASWaveformSample));
if length(Sound.Channels) = 1 then
CopyMemory(buf, #(Sound.Channels[0, 0]), length(Sound.Channels[0]) * sizeof(TASWaveformSample))
else
for i := 0 to high(Sound.Channels[0]) do
for n := 0 to high(Sound.Channels) do
CopyMemory(buf + sizeof(TASWaveformSample) * (i * fmt.nChannels + n), #(Sound.Channels[n, i]), sizeof(TASWaveformSample));
if waveOutOpen(#hWave, WAVE_MAPPER, #fmt, 0, 0, CALLBACK_NULL) <> MMSYSERR_NOERROR then
raise Exception.Create('SoundPlayerThread.Execute: waveOutOpen failed: ' + SysErrorMessage(GetLastError));
ZeroMemory(#hdr, sizeof(hdr));
with hdr do
begin
lpData := buf;
dwBufferLength := fmt.nChannels * length(Sound.Channels[0]) * sizeof(TASWaveformSample);
dwFlags := 0;
end;
try
SoundPlayerActive := true;
waveOutPrepareHeader(hWave, #hdr, sizeof(hdr));
waveOutWrite(hWave, #hdr, sizeof(hdr));
sleep(500);
while waveOutUnprepareHeader(hWave, #hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
if PlaySoundStopper^ then
begin
waveOutPause(hWave);
waveOutUnprepareHeader(hWave, #hdr, sizeof(hdr));
break;
end
else
sleep(100);
finally
SoundPlayerActive := false;
waveOutClose(hWave);
FreeMem(buf);
end;
except
on E: Exception do MessageBox(0, PChar(E.ClassName + ': ' + E.Message), 'Sound Playback Error', MB_ICONERROR);
end;
end;
where
type
TASWaveformSample = integer; // signed 32-bit; -2147483648..2147483647
TASWaveformSamples = packed array of TASWaveformSample; // one channel
PASSound = ^TASSound;
TASSound = record
Channels: packed array of TASWaveformSamples;
SampleRate: cardinal;
end;
A perhaps better way, is to use a thread for the playing. Then I do
var
OwnerForm: HWND; // = 0;
SndSource: PASSound; // = nil;
ThreadPlaying: boolean; // = false;
type
TSoundPlayerThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
end;
implemented as
procedure TSoundPlayerThread.Execute;
var
hWave: HWAVEOUT;
hdr: TWaveHdr;
buf: PAnsiChar;
fmt: TWaveFormatEx;
i: Integer;
n: Integer;
begin
ThreadPlaying := true;
try
try
if not Assigned(SndSource) then
Exit;
with fmt do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := length(SndSource^.Channels);
nSamplesPerSec := SndSource^.SampleRate;
wBitsPerSample := 32;
nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
nBlockAlign := nChannels * wBitsPerSample div 8;
cbSize := 0;
end;
GetMem(buf, fmt.nChannels * length(SndSource^.Channels[0]) * sizeof(TASWaveformSample));
if length(SndSource^.Channels) = 1 then
CopyMemory(buf, #(SndSource^.Channels[0, 0]), length(SndSource^.Channels[0]) * sizeof(TASWaveformSample))
else
for i := 0 to high(SndSource^.Channels[0]) do
for n := 0 to high(SndSource^.Channels) do
CopyMemory(buf + sizeof(TASWaveformSample) * (i * fmt.nChannels + n), #(SndSource^.Channels[n, i]), sizeof(TASWaveformSample));
if waveOutOpen(#hWave, WAVE_MAPPER, #fmt, 0, 0, CALLBACK_NULL) <> MMSYSERR_NOERROR then
raise Exception.Create('SoundPlayerThread.Execute: waveOutOpen failed: ' + SysErrorMessage(GetLastError));
ZeroMemory(#hdr, sizeof(hdr));
with hdr do
begin
lpData := buf;
dwBufferLength := fmt.nChannels * length(SndSource^.Channels[0]) * sizeof(TASWaveformSample);
dwFlags := 0;
end;
waveOutPrepareHeader(hWave, #hdr, sizeof(hdr));
waveOutWrite(hWave, #hdr, sizeof(hdr));
sleep(500);
while waveOutUnprepareHeader(hWave, #hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
begin
sleep(100);
if Terminated then
waveOutReset(hWave);
end;
waveOutClose(hWave);
FreeMem(buf);
except
on E: Exception do MessageBox(0, PChar(E.ClassName + ': ' + E.Message), 'TSoundPlayerThread', MB_ICONERROR);
end;
finally
ThreadPlaying := false;
end;
end;
Wave Audio Package has TLiveAudioPlayer component. It plays audio from buffer.
The Win32 API PlaySound function can play standard RIFF-encoded audio (such as WAV audio) from a memory block by using its SND_MEMORY flag. Alternatively, if the audio is in the app's resources, you can use the SND_RESOURCE flag instead.
Microsoft has a Knowledge Base article telling you how you can play sound from memory using MCI. You'll probably need to have the wave file header in your array, or otherwise copy in the right data during the first read, but other than that it should be fairly easy to port over.
I couldn't find a complete solution that isn't based on the outdated sndPlaySound, so here are two functions that play ".wav" files from both a TMemoryStream and from a file :
uses mmsystem;
procedure PlaySoundFromFile(FileName : String);
var
mStream : TMemoryStream;
begin
mStream := TMemoryStream.Create;
Try mStream.LoadFromFile(FileName); Except End;
If mStream.Size > 0 then PlaySoundFromStream(mStream);
mStream.Free;
end;
procedure PlaySoundFromStream(mStream : TMemoryStream);
begin
PlaySound(mStream.Memory,0,SND_MEMORY or SND_SYNC);
end;
The sound is played synchronously and from memory, you can find the other PlaySound flags in the link on Remy's answer. If you switch to async playback, make sure to not clear the sound memory before playback ends.