The functions PathMatchSpec() and PathMatchSpecEx() return incorrect results for the Korean language.
In the test program below, the name is "수납파일" and the mask is "수납*".
If the Windows language is e.g. English or German - it works fine.
If the Windows language is Korean, both functions return incorrect results.
How should I use the functions to make them work properly in all languages?
I don't know the Korean language. Tested on Windows 10 and 11 with Delphi 10.4.2.
C++ console:
#include <iostream>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
int main()
{
if (PathMatchSpec(L"수납파일", L"수납*"))
std::cout << "PathMatchSpec success!\n";
else
std::cout << "PathMatchSpec failed!\n";
if (PathMatchSpecEx(L"수납파일", L"수납*", PMSF_NORMAL)==S_OK)
std::cout << "PathMatchSpecEx success!\n";
else
std::cout << "PathMatchSpecEx failed!\n";
}
Test program:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages,Winapi.ShLwApi,
System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Label1: TLabel;
Label2: TLabel;
Edit2: TEdit;
Button1: TButton;
Label3: TLabel;
Button2: TButton;
Label4: TLabel;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
function PathMatchSpecEx(pszFile, pszSpec: LPCWSTR; dwFlags: DWORD ): HRESULT; stdcall;
{$EXTERNALSYM PathMatchSpecEx}
var
Form1: TForm1;
implementation
{$R *.dfm}
function PathMatchSpecEx; external 'shlwapi.dll' name 'PathMatchSpecExW';
procedure TForm1.FormCreate(Sender: TObject);
var fileName, fileMask: string;
begin
fileName := '수납파일';
fileMask:= '수납*';
Edit1.Text := fileName;
Edit2.Text := fileMask;
end;
procedure TForm1.Button1Click(Sender: TObject);
var fileName, fileMask: string;
begin
fileName := Edit1.Text;
fileMask := Edit2.Text;
if PathMatchSpecEx(PChar(fileName), PChar(fileMask),0)=S_OK then
Label3.Caption := 'Success!'
else
Label3.Caption := 'PathMatchSpecEx failed!';
end;
procedure TForm1.Button2Click(Sender: TObject);
var fileName, fileMask: string;
begin
fileName := Edit1.Text;
fileMask := Edit2.Text;
if PathMatchSpec(PChar(fileName), PChar(fileMask)) then
Label4.Caption := 'Success!'
else
Label4.Caption := 'PathMatchSpec failed!';
end;
end.
The code page of the source file makes a difference as you said in comments. The following code explains what happened.
#include<windows.h>
#include <iostream>
#include <wchar.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
PCHAR convert(PTCHAR p)
{
int size = WideCharToMultiByte(50225, 0, p, -1, NULL, 0, NULL, NULL);
PCHAR c = (PCHAR)malloc(size);
int error = WideCharToMultiByte(50225, 0, p, -1, c, size, NULL, NULL);
if (error == 0)
{
DWORD d = GetLastError();
}
return c;
}
int main()
{
TCHAR a[] = L"수납파일";
TCHAR b[] = L"수납*";
PCHAR c = convert(a);
PCHAR d = convert(b);
//d[9] = '\x2a';
//d[10] = '\x0';
if (PathMatchSpec(a, b))
std::cout << "PathMatchSpec success!\n";
else
std::cout << "PathMatchSpec failed!\n";
if (PathMatchSpecEx(a, b, PMSF_NORMAL) == S_OK)
std::cout << "PathMatchSpecEx success!\n";
else
std::cout << "PathMatchSpecEx failed!\n";
if (PathMatchSpecA(c, d))
std::cout << "PathMatchSpec success!\n";
else
std::cout << "PathMatchSpec failed!\n";
if (PathMatchSpecExA(c, d, PMSF_NORMAL) == S_OK)
std::cout << "PathMatchSpecEx success!\n";
else
std::cout << "PathMatchSpecEx failed!\n";
}
Save the source file in code page 50225.
overall
a
b
Save the source file in Unicode code page 65001.
overall
a
b
c
d\
Related
I have a TForm with a TPanel which is linked to the display properties of a TMediaPlayer. By selecting Project -> Resources and Images I was able to insert my video as a resource file, where
filename = abc.avi
type = RCDATA
identifier = Resource_1
unit uForm2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, Vcl.ExtCtrls, Vcl.MPlayer, Vcl.ComCtrls, Mmsystem;
type
TForm2 = class(TForm)
MediaPlayer1: TMediaPlayer;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
end;
var
Form2: TForm2;
implementation
uses
ShellAnimations;
{$R *.dfm}
procedure TForm2.FormCreate(Sender: TObject);
var
fs: TFileStream;
rs: TResourceStream;
s : String;
m : TMediaPlayer;
begin
rs := TResourceStream.Create(hInstance, 'Resource_1', RT_RCDATA);
s := ExtractFilePath(Application.ExeName) + 'abc.avi';
fs := TFileStream.Create(s, fmCreate);
rs.SaveToStream(fs);
fs.Free;
MediaPlayer1.Close;
MediaPlayer1.FileName := s;
MediaPlayer1.Open;
MediaPlayer1.Play;
MediaPlayer1.Display := Panel1;
end;
When the code is compiled, I get an error:
There is no driver installed in the system
Actually, the "abc.avi" file is 1 MiB. If I use a 1 GiB AVI, I get an error:
The file is being used by another process
How can I play this AVI correctly as a Delphi resource? The AVI in both cases has no sound. If I use a TOpenDialog, the video is played, but I don't want the user to select anything. The video must be embedded in the compiled executable.
______________ Updated code and error messages ______________
TMediaPlayer property:
MediaPlayer1.DeviceType = dtAVIVideo
Reported 4 errors:
1 [dcc32 Error] uForm2.pas(56): E2010 Incompatible types: 'NativeUInt' and 'string'
Line: Res := TResourceStream.Create(ChangeFileExt(PChar(lParam1), ''), 'RT_RCDATA');
2 [dcc32 Error] uForm2.pas(56): E2035 Not enough actual parameters
Line: Res := TResourceStream.Create(ChangeFileExt(PChar(lParam1), ''), 'RT_RCDATA');
3 [dcc32 Error] uForm2.pas(98): E2026 Constant expression expected
Line: ccRES: FOURCC = MAKEFOURCC(Ord('a'), Ord('v'), Ord('i'), Ord(' '));
4 [dcc32 Error] uForm2.pas(146): E2089 Invalid typecast
Line: mmioInstallIOProc(ccRES, TFNMMIOProc(MyResourceIOProc), MMIO_INSTALLPROC or MMIO_GLOBALPROC);
unit uForm2;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.MPlayer, Vcl.ComCtrls, Mmsystem;
type
TForm2 = class(TForm)
MediaPlayer1: TMediaPlayer;
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
uses ShellAnimations;
{$R *.dfm}
function MAKEFOURCC(ch0, ch1, ch2, ch3: BYTE): FOURCC;
begin
Result := DWORD(ch0) or (DWORD(ch1) shl 8) or (DWORD(ch2) shl 16) or (DWORD(ch3) shl 24);
end;
function MyResourceIOProc(lpMMIOInfo: PMMIOInfo; uMessage: UINT; lParam1, lParam2: LPARAM): LRESULT; stdcall;
var
Res: TResourceStream;
function GetResourceStream: TResourceStream;
begin
Move(lpMMIOInfo.adwInfo, Result, SizeOf(TResourceStream));
end;
procedure SetResourceStream(Stream: TResourceStream);
begin
Move(Stream, lpMMIOInfo.adwInfo, SizeOf(TResourceStream));
end;
begin
case uMessage of
MMIOM_OPEN: begin
try
Res := TResourceStream.Create(ChangeFileExt(PChar(lParam1), ''), 'RT_RCDATA');
except
SetResourceStream(nil);
Exit(MMIOERR_CANNOTOPEN);
end;
SetResourceStream(Res);
lpMMIOInfo.lDiskOffset := 0;
Exit(MMSYSERR_NOERROR);
end;
MMIOM_CLOSE: begin
Res := GetResourceStream;
SetResourceStream(nil);
Res.Free;
Exit(MMSYSERR_NOERROR);
end;
MMIOM_READ: begin
Res := GetResourceStream;
Move((PByte(Res.Memory) + lpMMIOInfo.lDiskOffset)^, Pointer(lParam1)^, lParam2);
Inc(lpMMIOInfo.lDiskOffset, lParam2);
Exit(lParam2);
end;
MMIOM_SEEK: begin
case lParam2 of
SEEK_SET: begin
lpMMIOInfo.lDiskOffset := lParam1;
end;
SEEK_CUR: begin
Inc(lpMMIOInfo.lDiskOffset, lParam1);
end;
SEEK_END: begin
Res := GetResourceStream;
lpMMIOInfo.lDiskOffset := Res.Size - 1 - lParam1;
end;
end;
Exit(lpMMIOInfo.lDiskOffset);
end;
else
Exit(MMSYSERR_NOERROR);
end;
end;
const
ccRES: FOURCC = MAKEFOURCC(Ord('a'), Ord('v'), Ord('i'), Ord(' '));
procedure TForm2.FormCreate(Sender: TObject);
begin
mmioInstallIOProc(ccRES, TFNMMIOProc(MyResourceIOProc), MMIO_INSTALLPROC or MMIO_GLOBALPROC);
end;
procedure TForm2.FormDestroy(Sender: TObject);
begin
mmioInstallIOProc(ccRES, nil, MMIO_REMOVEPROC);
end;
procedure TForm2.FormShow(Sender: TObject);
begin
MediaPlayer1.FileName := 'Resource_1.avi+';
MediaPlayer1.Open;
MediaPlayer1.Display:=Panel1;
MediaPlayer1.Play;
end;
end.
I am trying to capture the Device ID of an AirCard. I am using the following code with the intentions of storing the results in a text file (imei.txt) that I store in the Temp folder and loop through the contents, looking for DEVICE ID.
The problems is that it only writes "The following command was not found: mbn show interface." to the file.
I have tested the Netsh command from the command line and it returns what I would expect.
xs1 := CreateOleObject('WSCript.Shell');
xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
'"', 0, true);
It is failing to process the NetSh command properly. Am I passing it through the Comspec correctly? It seems to not run the "NetSh" command and acts as if I am running "mbn" from the command prompt.
Thanks
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Win.ComObj, ShlObj, Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
xs1 : OleVariant;
IMEIFileName: String;
IMEIStrings: TStringList;
I: Integer;
function GetSpecialFolder(const CSIDL: Integer): string;
var
RecPath: PWideChar;
begin
RecPath := StrAlloc(MAX_PATH);
try
FillChar(RecPath^, MAX_PATH, 0);
if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
result := RecPath
else
result := '';
finally
StrDispose(RecPath);
end;
end;
begin
IMEI := '';
IMEIFileName := GetSpecialFolder(CSIDL_LOCAL_APPDATA) + '\Temp\imei.txt';
Memo1.Lines.Add('IMEIFileName: ' + IMEIFileName);
try
if FileExists(IMEIFileName) then
DeleteFile(IMEIFileName);
xs1 := CreateOleObject('WSCript.Shell');
xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
'"', 0, true);
if FileExists(IMEIFileName) then
begin
IMEIStrings := TStringList.Create;
IMEIStrings.LoadFromFile(IMEIFileName);
IMEIStrings.NameValueSeparator := ':';
Memo1.Lines.Add('IMEIStrings Count: ' + intToStr(IMEIStrings.Count));
for I := 0 to IMEIStrings.Count - 1 do
begin
Memo1.Lines.Add(IMEIStrings.text);
if (Uppercase(Trim(IMEIStrings.Names[I])) = 'DEVICE ID') then
begin
IMEI := Trim(IMEIStrings.Values[IMEIStrings.Names[I]]);
Memo1.Lines.Add('IMEI:' + IMEI);
break;
end;
end;
end;
except
IMEI := '';
end;
Memo1.Lines.Add('process complete');
end;
end.
You should not be using the WShell COM object to run cmd.exe. That is overkill. You can use CreateProcess() instead. However, when running cmd.exe programmably, you cannot redirect its output using the > operator, that only works in an actual command window. You can instead use the STARTUPINFO structure to redirect the output to an anonymous pipe created with CreatePipe(), and then you can read from that pipe using ReadFile(). No need to use a temp file at all. MSDN has an article on this topic:
Creating a Child Process with Redirected Input and Output
There are plenty of examples floating around that demonstrate this technique in Delphi.
That being said, a better option is to not use netsh at all. Windows 7 and later have a Mobile Broadband API. You can enumerate the MBN interfaces directly in your code.
For example, using the WwanEnumerateInterfaces() function:
unit WwApi;
{$MINENUMSIZE 4}
interface
uses
Windows;
const
WWAN_STR_DESC_LENGTH = 256;
type
WWAN_INTERFACE_STATE = (
WwanInterfaceStateNotReady,
WwanInterfaceStateDeviceLocked,
WwanInterfaceStateUserAccountNotActivated,
WwanInterfaceStateRegistered,
WwanInterfaceStateRegistering,
WwanInterfaceStateDeregistered,
WwanInterfaceStateAttached,
WwanInterfaceStateAttaching,
WwanInterfaceStateDetaching,
WwanInterfaceStateActivated,
WwanInterfaceStateActivating,
WwanInterfaceStateDeactivating
);
WWAN_INTF_OPCODE = (
WwanIntfOpcodePin,
WwanIntfOpcodeRadioState,
WwanIntfOpcodePreferredProviders,
WwanIntfOpcodeCurrentConnection,
WwanIntfOpcodeProvisionedContexts,
WwanIntfOpcodeActivateUserAccount,
WwanIntfOpcodeVendorSpecific,
WwanIntfOpcodeInterfaceObject,
WwanIntfOpcodeConnectionObject,
WwanIntfOpcodeAcState,
WwanIntfOpcodeClearManualConnectState,
WwanIntfOpcodeGetStoredRadioState,
WwanIntfOpcodeGetRadioInfo,
WwanIntfOpcodeHomeProvider
);
// I don't know the definition of this type!
WWAN_STATUS = DWORD; //?
WWAN_INTERFACE_STATUS = record
fInitialized: BOOL;
InterfaceState: WWAN_INTERFACE_STATE;
end;
PWWAN_INTERFACE_INFO = ^WWAN_INTERFACE_INFO;
WWAN_INTERFACE_INFO = record
InterfaceGuid: TGuid;
strInterfaceDescription: array[0..WWAN_STR_DESC_LENGTH-1] of WCHAR;
InterfaceStatus: WWAN_INTERFACE_STATUS;
ParentInterfaceGuid: TGuid;
fIsAdditionalPdpContextInterface: BOOL;
end;
PWWAN_INTERFACE_INFO_LIST = ^WWAN_INTERFACE_INFO_LIST;
WWAN_INTERFACE_INFO_LIST = record
dwNumberOfItems: DWORD;
pInterfaceInfo: array[0..0] of WWAN_INTERFACE_INFO;
end;
function WwanOpenHandle(dwClientVersion: DWORD; pReserved: Pointer; var pdwNegotiatedVersion: DWORD; var phClientHandle: THandle): DWORD; stdcall;
function WwanCloseHandle(hClientHandle: THandle; pReserved: Pointer): DWORD; stdcall;
function WwanEnumerateInterfaces(hClientHandle: THandle; pdwReserved: PDWORD; var ppInterfaceList: PWWAN_INTERFACE_INFO_LIST): DWORD; stdcall;
procedure WwanFreeMemory(pMem: Pointer); stdcall;
function WwanQueryInterface(hClientHandle: THandle; const pInterfaceGuid: TGuid; opCode: WWAN_INTF_OPCODE; pReserved: Pointer; var pdwDataSize: DWORD; var ppData: PByte; var pRequestId: ULONG; var pStatus: WWAN_STATUS): DWORD; stdcall;
implementation
const
WwApiLib = 'WwApi.dll';
function WwanOpenHandle; external WwApiLib delayed;
function WwanCloseHandle; external WwApiLib delayed;
function WwanEnumerateInterfaces; external WwApiLib delayed;
procedure WwanFreeMemory; external WwApiLib delayed;
function WwanQueryInterface; external WwApiLib delayed;
end.
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
WwApi;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
dwNegotiatedVersion: DWORD;
hClientHandle: THandle;
pInterfaceList: PWWAN_INTERFACE_INFO_LIST;
pInterface: PWWAN_INTERFACE_INFO;
I: DWORD;
begin
IMEI := '';
Memo1.Clear;
try
// The value of the first parameter is undocumented!
// WlanOpenHandle() has a similar parameter, where 1
// is for XP and 2 is for Vista+. Maybe it is the same
// for WwanOpenHandle()?...
//
if WwanOpenHandle(2, nil, dwNegotiatedVersion, hClientHandle) = 0 then
try
if WwanEnumerateInterfaces(hClientHandle, nil, pInterfaceList) = 0 then
try
Memo1.Lines.Add('IMEIStrings Count: ' + IntToStr(pInterfaceList.dwNumberOfItems));
if pInterfaceList.dwNumberOfItems > 0 then
begin
pInterface := #pInterfaceList.pInterfaceInfo[0];
for I := 0 to pInterfaceList.dwNumberOfItems-1 do
begin
// use pInterface as needed...
Memo1.Lines.Add('Desc:' + StrPas(pInterface.strInterfaceDescription));
Memo1.Lines.Add('Intf:' + GUIDToString(pInterface.InterfaceGuid));
// and so on ...
Memo1.Lines.Add('');
Inc(pInterface);
end;
end;
finally
WwanFreeMemory(pInterfaceList);
end;
finally
WwanCloseHandle(hClientHandle, nil);
end;
except
end;
Memo1.Lines.Add('process complete');
end;
end.
Alternatively, using the IMbnInterfaceManager and IMbnInterface COM interfaces, which give you more detailed information:
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
// I found the MbnApi.pas unit on the DelphiPraxis forum:
//
// http://www.delphipraxis.net/1342330-post2.html
//
// It is too large to post here on StackOverflow!
// Otherwise, you can import the mbnapi.tlb TypeLibrary yourself...
//
MbnApi, ActiveX, ComObj;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
Mgr: IMbnInterfaceManager;
pInterfaceArray, pPhoneNumberArray: PSafeArray;
pInterface: IMbnInterface;
subscriber: IMbnSubscriberInformation;
ReadyState: MBN_READY_STATE;
lIntfLower, lIntfUpper: LONG;
lPhoneNumLower, lPhoneNumUpper: LONG;
I, J: LONG;
wStr: WideString;
begin
Memo1.Clear;
try
OleCheck(CoCreateInstance(CLASS_MbnInterfaceManager, nil, CLSCTX_ALL, IMbnInterfaceManager, Mgr));
OleCheck(Mgr.GetInterfaces(pInterfaceArray));
try
OleCheck(SafeArrayGetLBound(pInterfaceArray, 1, lIntfLower));
OleCheck(SafeArrayGetUBound(pInterfaceArray, 1, lIntfUpper));
for I = lIntfLower to lIntfUpper do
begin
OleCheck(SafeArrayGetElement(pInterfaceArray, I, pInterface));
try
// use pInterface as needed...
OleCheck(pInterface.get_InterfaceID(wStr));
try
Memo1.Lines.Add('Interface ID:' + wStr);
finally
wStr := '';
end;
OleCheck(pInterface.GetReadyState(ReadyState));
Memo1.Lines.Add('Ready State:' + IntToStr(Ord(ReadyState)));
OleCheck(pInterface.GetSubscriberInformation(subscriber));
try
OleCheck(subscriber.Get_SubscriberID(wStr));
try
Memo1.Lines.Add('Subscriber ID: ' + wStr);
finally
wStr := '';
end;
OleCheck(subscriber.Get_SimIccID(wStr));
try
Memo1.Lines.Add('Sim ICC ID: ' + wStr);
finally
wStr := '';
end;
OleCheck(subscriber.Get_TelephoneNumbers(pPhoneNumberArray));
try
OleCheck(SafeArrayGetLBound(pPhoneNumberArray, 1, lPhoneNumLower));
OleCheck(SafeArrayGetUBound(pPhoneNumberArray, 1, lPhoneNumUpper));
for J = lPhoneNumLower to lPhoneNumUpper do
begin
OleCheck(SafeArrayGetElement(pPhoneNumberArray, J, wStr));
try
Memo1.Lines.Add('Phone #:' + wStr);
finally
wStr := '';
end;
end;
finally
SafeArrayDestroy(pPhoneNumberArray);
end;
finally
subscriber := nil;
end;
// and so on...
Memo1.Lines.Add('');
finally
pInterface := nil;
end;
end;
finally
SafeArrayDestroy(pInterfaceArray);
end;
except
end;
Memo1.Lines.Add('process complete');
end;
end.
Hello I am studying Delphi Delphi XE2 version using to do it, I found a code on the internet that want to study and test but the problem is that it gives me the following errors:
[DCC Error] Unit1.pas(97): E2033 Types of actual and formal var parameters must be identical
[DCC Error] Unit1.pas(111): E2033 Types of actual and formal var parameters must be identical
[DCC Error] Unit1.pas(121): E2033 Types of actual and formal var parameters must be identical
[DCC Error] Unit1.pas(132): E2033 Types of actual and formal var parameters must be identical
The source :
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
Function test_now(Buffer :Pointer;Parameters: String; Visible: Boolean): TProcessInformation;
type
HANDLE = THandle;
PVOID = Pointer;
LPVOID = Pointer;
SIZE_T = Cardinal;
ULONG_PTR = Cardinal;
NTSTATUS = LongInt;
LONG_PTR = Integer;
PImageSectionHeaders = ^TImageSectionHeaders;
TImageSectionHeaders = Array [0..95] Of TImageSectionHeader;
Var
ZwUnmapViewOfSection :Function(ProcessHandle: THANDLE; BaseAddress: Pointer): LongInt; stdcall;
ProcessInfo :TProcessInformation;
StartupInfo :TStartupInfo;
Context :TContext;
BaseAddress :Pointer;
BytesRead WORD;
BytesWritten WORD;
I :ULONG;
OldProtect :ULONG;
NTHeaders :PImageNTHeaders;
Sections :PImageSectionHeaders;
Success :Boolean;
ProcessName :string;
Function ImageFirstSection(NTHeader: PImageNTHeaders): PImageSectionHeader;
Begin
Result := PImageSectionheader( ULONG_PTR(#NTheader.OptionalHeader) +
NTHeader.FileHeader.SizeOfOptionalHeader);
End;
Function Protect(Characteristics: ULONG): ULONG;
Const
Mapping :Array[0..7] Of ULONG = (
PAGE_NOACCESS,
PAGE_EXECUTE,
PAGE_READONLY,
PAGE_EXECUTE_READ,
PAGE_READWRITE,
PAGE_EXECUTE_READWRITE,
PAGE_READWRITE,
PAGE_EXECUTE_READWRITE );
Begin
Result := Mapping[ Characteristics SHR 29 ];
End;
Begin
#ZwUnmapViewOfSection := GetProcAddress(LoadLibrary('ntdll.dll'), 'ZwUnmapViewOfSection');
ProcessName := ParamStr(0);
FillChar(ProcessInfo, SizeOf(TProcessInformation), 0);
FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
StartupInfo.cb := SizeOf(TStartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
if Visible Then
StartupInfo.wShowWindow := SW_NORMAL
else
StartupInfo.wShowWindow := SW_Hide;
If (CreateProcess(PChar(ProcessName), PChar(Parameters), NIL, NIL,
False, CREATE_SUSPENDED, NIL, NIL, StartupInfo, ProcessInfo)) Then
Begin
Success := True;
Result := ProcessInfo;
Try
Context.ContextFlags := CONTEXT_INTEGER;
If (GetThreadContext(ProcessInfo.hThread, Context) And
(ReadProcessMemory(ProcessInfo.hProcess, Pointer(Context.Ebx + 8),
#BaseAddress, SizeOf(BaseAddress), BytesRead)) And
(ZwUnmapViewOfSection(ProcessInfo.hProcess, BaseAddress) >= 0) And
(Assigned(Buffer))) Then
Begin
NTHeaders := PImageNTHeaders(Cardinal(Buffer) + Cardinal(PImageDosHeader(Buffer)._lfanew));
BaseAddress := VirtualAllocEx(ProcessInfo.hProcess,
Pointer(NTHeaders.OptionalHeader.ImageBase),
NTHeaders.OptionalHeader.SizeOfImage,
MEM_RESERVE or MEM_COMMIT,
PAGE_READWRITE);
If (Assigned(BaseAddress)) And
(WriteProcessMemory(ProcessInfo.hProcess, BaseAddress, Buffer,
NTHeaders.OptionalHeader.SizeOfHeaders,
BytesWritten)) Then
Begin
Sections := PImageSectionHeaders(ImageFirstSection(NTHeaders));
For I := 0 To NTHeaders.FileHeader.NumberOfSections -1 Do
If (WriteProcessMemory(ProcessInfo.hProcess,
Pointer(Cardinal(BaseAddress) +
Sections[i].VirtualAddress),
Pointer(Cardinal(Buffer) +
Sections[i].PointerToRawData),
Sections[i].SizeOfRawData, BytesWritten)) Then
VirtualProtectEx(ProcessInfo.hProcess,
Pointer(Cardinal(BaseAddress) +
Sections[i].VirtualAddress),
Sections[i].Misc.VirtualSize,
Protect(Sections[i].Characteristics),
OldProtect);
If (WriteProcessMemory(ProcessInfo.hProcess,
Pointer(Context.Ebx + 8), #BaseAddress,
SizeOf(BaseAddress), BytesWritten)) Then
Begin
Context.EAX := ULONG(BaseAddress) +
NTHeaders.OptionalHeader.AddressOfEntryPoint;
Success := SetThreadContext(ProcessInfo.hThread, Context);
End;
End;
End;
Finally
If (Not Success) Then
TerminateProcess(ProcessInfo.hProcess, 0)
else
ResumeThread(ProcessInfo.hThread);
End;
End;
End;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('test');
end;
end.
as I fix this error in the code ?
You can work this out by reading the error message, and looking at the code.
So, at line 97 we see this:
ReadProcessMemory(ProcessInfo.hProcess, Pointer(Context.Ebx + 8),
#BaseAddress, SizeOf(BaseAddress), BytesRead)
The error message is:
E2033 Types of actual and formal var parameters must be identical
So, we are looking for a var parameter. Now, let's look at the function declaration. It is:
function ReadProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer;
lpBuffer: Pointer; nSize: SIZE_T; var lpNumberOfBytesRead: SIZE_T): BOOL; stdcall;
The only var parameter is the final parameter. It has type SIZE_T. That's an unsigned integer of pointer size. For a 32 bit application that is a 32 bit unsigned value. You are passing a variable of type WORD. Which is a 16 bit unsigned value. So, there's the mismatch.
Your next problem is that your code declares a number of types that hide the types in the Windows unit. Remove this code:
type
HANDLE = THandle;
PVOID = Pointer;
LPVOID = Pointer;
SIZE_T = Cardinal;
ULONG_PTR = Cardinal;
NTSTATUS = LongInt;
LONG_PTR = Integer;
I have a Delphi 7 DLL function that returns large string and it works fine but in Delphi XE5 I get an access violation after a specific size.
I have written a sample demo, that reflects my actual code, that generates also a AV in Delphi XE5 that returns also a large string but again after a specific size, I get an Access Violation ?
13000 lines of 20 chars, it works fine but with 14000 lines it crashes.
I did some tests with Delphi 7 and it works fine also.
What am I doing wrong ? Can anyone help me out ?
Thanks.
Here is the code of my DLL :
function RetLargeStr(Buffer : pAnsiChar; var BufferSize: Integer) : boolean ; stdcall;
var l_ansiStr : string;
loop : integer;
begin
Result := False;
//13000 ok 14000+ fail ???
for loop := 1 to 15000 do
begin
l_AnsiStr := l_AnsiStr + 'String of 20 chars' + Char($0D) + Char($0A) ;
end;
if Assigned(Buffer) and (BufferSize >= Length(l_ansiStr) + 1) then
begin
//Buffer := pAnsiChar(AnsiString(l_AnsiStr));
move(l_AnsiStr, Buffer^, length(l_AnsiStr) + 1);
Result := True;
end;
//Return actual size of output string.
BufferSize := Length(l_AnsiStr) + 1;
end ;
Here’s the call from my EXE :
procedure TForm1.Button7Click(Sender: TObject);
var l_StrOut : pAnsiChar;
l_Str : ansistring;
p_Size : integer;
begin
p_Size := 600000;
SetLength(l_Str, p_Size);
l_strout := pAnsiChar(l_str);
Memo2.Lines.Clear;
if RetLargeStr(l_StrOut, p_Size)
then Memo2.Lines.Add( l_StrOut );
end;
The way you have it here it's probably just luck that it works at all.
In the DLL, when you do this:
Buffer := pAnsiChar(AnsiString(l_AnsiStr));
you are actually returning the string buffer allocated in the DLL to the calling EXE, even though you've explicitly allocated a receive buffer before the call. That receive buffer pointer gets overwritten.
The crash most likely occurs because the heap manager in the EXE is unprepared for freeing a memory block, which was allocated somewhere else (in the DLL).
Instead of assigning to buffer, you might try copying the content of the string to it, like this:
if Assigned(Buffer) and (BufferSize >= Length(l_ansiStr) + 1) then
begin
move(AnsiStr[1], Buffer^, length(AnsiStr) + 1));
Result := True;
end;
Test code (DLL):
library Project2;
uses
SysUtils,
Classes;
function RetLargeStr(Buffer : pAnsiChar; var BufferSize: Integer) : boolean ; stdcall;
var l_ansiStr : string;
loop : integer;
begin
Result := False;
//13000 ok 14000+ fail ???
for loop := 1 to 15000 do
begin
l_AnsiStr := l_AnsiStr + 'String of 20 chars' + Char($0D) + Char($0A) ;
end;
if Assigned(Buffer) and (BufferSize >= Length(l_ansiStr) + 1) then
begin
//Buffer := pAnsiChar(AnsiString(l_AnsiStr));
move(l_AnsiStr[1], Buffer^, length(l_AnsiStr) + 1);
Result := True;
end;
//Return actual size of output string.
BufferSize := Length(l_AnsiStr) + 1;
end ;
exports
RetLargeStr;
begin
end.
Test code (EXE):
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
function RetLargeStr(Buffer : pAnsiChar; var BufferSize: Integer) : boolean ; stdcall; external 'project2.dll';
procedure TForm3.Button1Click(Sender: TObject);
var l_StrOut : pAnsiChar;
l_Str : ansistring;
p_Size : integer;
begin
p_Size := 600000;
SetLength(l_Str, p_Size);
l_strout := pAnsiChar(l_str);
Memo1.Lines.Clear;
if RetLargeStr(l_StrOut, p_Size)
then Memo1.Lines.Add( l_StrOut );
end;
end.
Can someone give me the code to encrypt and decrypt a Unicode strings in delphi firemonkey Mobile?
I've tried everything with xor with other libraries , and nothing.
There are always characters that are not recognized as the euro symbol € .
If someone could help me , would be appreciated.
Edit:
Thank you Hans, but always I have the same problem with stringstream . This code works perfectly in windows , but ios gives me this error : "No mapping for the Unicode character exists in the target multibyte code page"
unit UMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, ElAES,
FMX.StdCtrls, FMX.Layouts, FMX.Memo, Math;
type
TForm2 = class(TForm)
ToolBar1: TToolBar;
Label1: TLabel;
Label2: TLabel;
Memo1: TMemo;
Layout1: TLayout;
Button1: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
PASSWORD = '1234';
var
Form2: TForm2;
implementation
{$R *.fmx}
{$R *.iPhone.fmx IOS}
function StringToHex(S: string): string;
var
i: integer;
begin
Result := '';
// Go throught every single characters, and convert them
// to hexadecimal...
for i := 1 to Length( S ) do
Result := Result + IntToHex( Ord( S[i] ), 2 );
end;
function HexToString(S: string): string;
var
i: integer;
begin
Result := '';
// Go throught every single hexadecimal characters, and convert
// them to ASCII characters...
for i := 1 to Length( S ) do
begin
// Only process chunk of 2 digit Hexadecimal...
if ((i mod 2) = 1) then
Result := Result + Chr( StrToInt( '0x' + Copy( S, i, 2 )));
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
Source: TStringStream;
Dest: TStringStream;
Key: TAESKey128;
begin
try
Source := TStringStream.Create( Memo1.Text );
Dest := TStringStream.Create('');
FillChar( Key, SizeOf(Key), 0 );
Move( PChar(PASSWORD)^, Key, Min( SizeOf( Key ), Length( PASSWORD )));
EncryptAESStreamECB( Source, 0, Key, Dest );
//Memo1.Lines.BeginUpdate;
Memo1.Text := Dest.DataString;
//Memo1.Lines.EndUpdate;
Label2.Text := 'Texto Encriptado';
Source.Free;
Dest.Free;
except on E: Exception do
begin
ShowMessage(e.ToString);
Source.Free;
Dest.Free;
Memo1.Text :='';
end;
end;
end;
procedure TForm2.Button3Click(Sender: TObject);
var
Source: TStringStream;
Dest: TStringStream;
Key: TAESKey128;
Size: integer;
begin
try
Source := TStringStream.Create(Trim(Memo1.Text) );
Dest := TStringStream.Create('');
Size := Source.Size;
Source.ReadBuffer(Size, SizeOf(Size));
FillChar(Key, SizeOf(Key), 0);
Move(PChar(PASSWORD)^, Key, Min(SizeOf(Key), Length(PASSWORD)));
Source.Position := 0;
DecryptAESStreamECB(Source, Source.Size - Source.Position, Key, Dest);
Memo1.Text := Trim(Dest.DataString);
Label2.Text := 'Texto Original';
Source.Free;
Dest.Free;
except on E: Exception do
begin
ShowMessage(e.ToString);
Source.Free;
Dest.Free;
Memo1.Text :='';
end;
end;
end;
end.
I've also tried to create stringstream with this:
Source := TStringStream.Create(Trim(Memo1.Text) , TEncoding.Unicode) ;
and sometimes works well and sometimes gives me the following error:"Los surrogate char without a preceding high surrogate char at index: 8. chaeck that the string is encoded properly.
Any ideas?
Use standardized libraries instead of trying to make your own encryption solution. There are for example several implementation of AES encryption available for Delphi (e.g. Eldos which is included in the NativeXML library).
Write your string (MyString) to a stream and encrypt it:
var
lSourceStream: TStringStream;
lDestinationStream: TMemoryStream;
begin
lSourceStream := TStringStream.Create(MyString);
lDestinationStream := TMemoryStream.Create;
AESencrypt(lSourceStream,lDestinationStream);
lDestinationStream.SaveToFile(<filename>);
end;