Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: WINAPI32 and X64



Permlink Replies: 6 - Last Post: Mar 5, 2015 1:00 PM Last Post By: Remy Lebeau (Te...
Yannick LANCHEC

Posts: 43
Registered: 1/21/01
WINAPI32 and X64
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 1, 2014 6:37 AM
Hello, How to convert this into X64 code ? :


var
_IsWow64Process: Pointer;

function IsWow64Process;
begin
GetProcedureAddress(_IsWow64Process, 'kernel32.dll', 'IsWow64Process');
asm
MOV ESP, EBP
POP EBP
JMP [_IsWow64Process]
end;
end;


In X64 it's impossible to mix ASM and pure delphi.

Best regards

Yannick
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: WINAPI32 and X64
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 2, 2014 11:08 AM   in response to: Yannick LANCHEC in response to: Yannick LANCHEC
Yannick wrote:

Hello, How to convert this into X64 code ?

You posted this in the OpenTools forum. OTA is the plugin framework of the
IDE itself, which is a 32bit app and does not support x64 plugins. So why
do you need this converted to x64?

In any case, the documentation says:

http://docwiki.embarcadero.com/RADStudio/XE7/en/64-bit_Windows_Application_Development

You can use Inline Assembler for 64-bit Windows code with some limitations:
- No mixing of assembly code in a block of Delphi or C++. **Functions must
be written completely in assembly, in Delphi, or in C++**.

The documentation further explains how to write such functions:

http://docwiki.embarcadero.com/RADStudio/XE7/en/Using_Inline_Assembly_Code
http://docwiki.embarcadero.com/RADStudio/XE7/en/Assembly_Procedures_and_Functions

However, passing parameters in x64 is very different than in x86. Different
registers are involved, and there is stack space allocated for every parameter.
So it is not just a matter of converting the assemby code, you have to completely
re-write it. Personally, I would not suggest using assembler for this kind
of logic. It is not worth it. Normal Pascal works just fine, in both x86
and x64, and is much easier to maintain, eg:

var
  _IsWow64Process: function(hProcess: THandle; var Wow64Process: BOOL): BOOL; 
stdcall;
 
function Stub_IsWow64Process(hProcess: THandle; var Wow64Process: BOOL): 
BOOL;
begin
  GetProcedureAddress(_IsWow64Process, 'kernel32.dll', 'IsWow64Process');
  Result := _IsWow64Process(hProcess, Wow64Process);
end;
 
initialization:
  _IsWow64Process := @Stub_IsWow64Process;


--
Remy Lebeau (TeamB)
Yannick LANCHEC

Posts: 43
Registered: 1/21/01
Re: WINAPI32 and X64
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 2, 2014 12:33 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Rémi,

Thank you for your response and sorry for the bad forum.

I want to convert an application using the sourceforge project WINAPI. The files are currently for x86.

Yannick

Remy Lebeau (TeamB) wrote:
Yannick wrote:

Hello, How to convert this into X64 code ?

You posted this in the OpenTools forum. OTA is the plugin framework of the
IDE itself, which is a 32bit app and does not support x64 plugins. So why
do you need this converted to x64?

In any case, the documentation says:

http://docwiki.embarcadero.com/RADStudio/XE7/en/64-bit_Windows_Application_Development

You can use Inline Assembler for 64-bit Windows code with some limitations:
- No mixing of assembly code in a block of Delphi or C++. **Functions must
be written completely in assembly, in Delphi, or in C++**.

The documentation further explains how to write such functions:

http://docwiki.embarcadero.com/RADStudio/XE7/en/Using_Inline_Assembly_Code
http://docwiki.embarcadero.com/RADStudio/XE7/en/Assembly_Procedures_and_Functions

However, passing parameters in x64 is very different than in x86. Different
registers are involved, and there is stack space allocated for every parameter.
So it is not just a matter of converting the assemby code, you have to completely
re-write it. Personally, I would not suggest using assembler for this kind
of logic. It is not worth it. Normal Pascal works just fine, in both x86
and x64, and is much easier to maintain, eg:

var
  _IsWow64Process: function(hProcess: THandle; var Wow64Process: BOOL): BOOL; 
stdcall;
 
function Stub_IsWow64Process(hProcess: THandle; var Wow64Process: BOOL): 
BOOL;
begin
  GetProcedureAddress(_IsWow64Process, 'kernel32.dll', 'IsWow64Process');
  Result := _IsWow64Process(hProcess, Wow64Process);
end;
 
initialization:
  _IsWow64Process := @Stub_IsWow64Process;


--
Remy Lebeau (TeamB)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: WINAPI32 and X64
Click to report abuse...   Click to reply to this thread Reply
  Posted: Nov 3, 2014 1:36 PM   in response to: Yannick LANCHEC in response to: Yannick LANCHEC
Yannick wrote:

I want to convert an application using the sourceforge project
WINAPI. The files are currently for x86.

What project are you referring to exactly?

--
Remy Lebeau (TeamB)
Yannick LANCHEC

Posts: 43
Registered: 1/21/01
Re: WINAPI32 and X64
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 5, 2015 2:30 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Yannick wrote:

I want to convert an application using the sourceforge project
WINAPI. The files are currently for x86.

What project are you referring to exactly?

--
Remy Lebeau (TeamB)

Sorry Remy for my delay ....

I have write for you a sample, it's a simple tool that enumerate server with NetServerEnum.

I have already write it with using JWA WINAPI32 V2.3

In the sample i have copy the references to JWA to make a single file, can you help me for modifying it to work on x64

All JWA calls are like theses two parts :

var
_NetApiBufferFree: Pointer;

function NetApiBufferFree;
begin
GetProcedureAddress(_NetApiBufferFree, netapi32, 'NetApiBufferFree');
asm
MOV ESP, EBP
POP EBP
JMP [_NetApiBufferFree]
end;
end;

var
_NetServerEnum: Pointer;

function NetServerEnum;
begin
GetProcedureAddress(_NetServerEnum, netapi32, 'NetServerEnum');
asm
MOV ESP, EBP
POP EBP
JMP [_NetServerEnum]
end;
end;

So what is the best method to correct all .....

Sample :

unit Unit2;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
TForm2 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;

// I ADD THIS
{$DEFINE JWA_INCLUDEMODE}
// TAKEN FROM JwaWinType.pas
LPWSTR = {$IFDEF USE_DELPHI_TYPES} Windows.LPWSTR {$ELSE} PWideChar {$ENDIF};
LMSTR = LPWSTR;
{$EXTERNALSYM LMSTR}
LMCSTR = LPCWSTR;
{$EXTERNALSYM LMCSTR}
PLMSTR = ^LMSTR;
{$NODEFINE PLMSTR}
// TAKEN FROM JwaLMCons.pas
NET_API_STATUS = DWORD;
//
function NetApiBufferFree(Buffer:LPVOID):NET_API_STATUS;stdcall;
function NetServerEnum(servername: LMCSTR; level: DWORD; var bufptr: LPBYTE; prefmaxlen: DWORD; entriesread: LPDWORD; totalentries: LPDWORD; servertype: DWORD; domain: LMCSTR; resume_handle: LPDWORD): NET_API_STATUS; stdcall;
//
procedure GetProcedureAddress(var P: Pointer; const ModuleName, ProcName: AnsiString); overload;
{GetProcedureAddress loads a function using a module name and an function index.}
procedure GetProcedureAddress(var P: Pointer; const ModuleName : AnsiString; ProcNumber : Cardinal); overload;

var
Form2: TForm2;

implementation

*{$R .dfm}

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// TAKEN FROM WINAPI32 SOURFORGE
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// TAKEN FROM JwaLMCons.pas
Const
MAX_PREFERRED_LENGTH = DWORD(-1);
Var
NetApi32:AnsiString = 'netapi32.dll';

////////////////////////////////////////////////////////////////////////////////
// TAKEN FROM JwaWinType.pas
type
EJwaError = class(Exception);
EJwaLoadLibraryError = class(EJwaError);
EJwaGetProcAddressError = class(EJwaError);

{$IFDEF JWA_INCLUDEMODE} //prevents stackoverflow in GetProcedureAddress
function JwaWinType_GetModuleHandle(lpModuleName: LPCSTR): HMODULE; stdcall; external kernel32 name 'GetModuleHandleA';
function JwaWinType_LoadLibrary(lpLibFileName: LPCSTR): HMODULE; stdcall; external kernel32 name 'LoadLibraryA';
function JwaWinType_GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall; external kernel32 name 'GetProcAddress';
{$ELSE}
function GetModuleHandle(lpModuleName: LPCSTR): HMODULE; stdcall; external kernel32 name 'GetModuleHandleA';
function LoadLibrary(lpLibFileName: LPCSTR): HMODULE; stdcall; external kernel32 name 'LoadLibraryA';
function GetProcAddress(hModule: HMODULE; lpProcName: LPCSTR): FARPROC; stdcall; external kernel32 name 'GetProcAddress';
{$ENDIF JWA_INCLUDEMODE}

Const
RsELibraryNotFound = 'Library not found: %0:s';
RsEFunctionNotFound = 'Function not found: %0:s.%1:s';
RsEFunctionNotFound2 = 'Function not found: %0:s.%1:d';

procedure GetProcedureAddress(var P: Pointer; const ModuleName,
ProcName: AnsiString);
var
ModuleHandle: HMODULE;
begin
if not Assigned(P) then
begin
ModuleHandle := {$IFDEF JWA_INCLUDEMODE}jwaWinType_GetModuleHandle
{$ELSE}GetModuleHandle
{$ENDIF JWA_INCLUDEMODE}
(PAnsiChar(AnsiString(ModuleName)));
if ModuleHandle = 0 then
begin
ModuleHandle := {$IFDEF JWA_INCLUDEMODE}jwaWinType_LoadLibrary
{$ELSE}LoadLibrary
{$ENDIF JWA_INCLUDEMODE}(PAnsiChar(ModuleName));
if ModuleHandle = 0 then
raise EJwaLoadLibraryError.CreateFmt(RsELibraryNotFound, [ModuleName]);
end;
P := Pointer({$IFDEF JWA_INCLUDEMODE}jwaWinType_GetProcAddress
{$ELSE}GetProcAddress
{$ENDIF JWA_INCLUDEMODE}(ModuleHandle,
PAnsiChar(ProcName)));
if not Assigned(P) then
raise EJwaGetProcAddressError.CreateFmt(RsEFunctionNotFound,
[ModuleName, ProcName]);
end;
end;

procedure GetProcedureAddress(var P: Pointer; const ModuleName :
AnsiString; ProcNumber : Cardinal);
var
ModuleHandle: HMODULE;
begin
if not Assigned(P) then
begin
ModuleHandle := {$IFDEF JWA_INCLUDEMODE}jwaWinType_GetModuleHandle
{$ELSE}GetModuleHandle
{$ENDIF JWA_INCLUDEMODE}
(PAnsiChar(AnsiString(ModuleName)));
if ModuleHandle = 0 then
begin
ModuleHandle := {$IFDEF JWA_INCLUDEMODE}jwaWinType_LoadLibrary
{$ELSE}LoadLibrary
{$ENDIF JWA_INCLUDEMODE}(PAnsiChar(ModuleName));
if ModuleHandle = 0 then
raise EJwaLoadLibraryError.CreateFmt(RsELibraryNotFound, [ModuleName]);
end;
P := Pointer({$IFDEF JWA_INCLUDEMODE}jwaWinType_GetProcAddress
{$ELSE}GetProcAddress
{$ENDIF JWA_INCLUDEMODE}(ModuleHandle,
PAnsiChar(ProcNumber)));
if not Assigned(P) then
raise EJwaGetProcAddressError.CreateFmt(RsEFunctionNotFound2,
[ModuleName, ProcNumber]);
end;
end;
////////////////////////////////////////////////////////////////////////////////
// TAKEN FROM JwaLMApiBuf.pas
var
_NetApiBufferFree: Pointer;

function NetApiBufferFree;
begin
GetProcedureAddress(_NetApiBufferFree, netapi32, 'NetApiBufferFree');
asm
MOV ESP, EBP
POP EBP
JMP [_NetApiBufferFree]
end;
end;
////////////////////////////////////////////////////////////////////////////////
// Taken from JwaLMServer.pas
Type
LPSERVER_INFO_101 = ^SERVER_INFO_101;
{$EXTERNALSYM LPSERVER_INFO_101}
PSERVER_INFO_101 = ^SERVER_INFO_101;
{$EXTERNALSYM PSERVER_INFO_101}
_SERVER_INFO_101 = record
sv101_platform_id: DWORD;
sv101_name: LMSTR;
sv101_version_major: DWORD;
sv101_version_minor: DWORD;
sv101_type: DWORD;
sv101_comment: LMSTR;
end;
{$EXTERNALSYM _SERVER_INFO_101}
SERVER_INFO_101 = _SERVER_INFO_101;
{$EXTERNALSYM SERVER_INFO_101}
TServerInfo101 = SERVER_INFO_101;
PServerInfo101 = PSERVER_INFO_101;

var
_NetServerEnum: Pointer;

function NetServerEnum;
begin
GetProcedureAddress(_NetServerEnum, netapi32, 'NetServerEnum');
asm
MOV ESP, EBP
POP EBP
JMP [_NetServerEnum]
end;
end;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// MY DEMO FONCTION
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Function ListServers(Domain:String;
Filter:Dword;
LStr:TStrings):DWORD;
Var
P:PByte;
pServer:PServerInfo101;
nbEntreesLues,NbTotalEntrees,Position,LMax:DWORD;
Res:DWORD;
I:Integer;
Begin
LMax:=MAX_PREFERRED_LENGTH;
nbEntreesLues:=0;
NbTotalEntrees:=0;
Position:=0;
Repeat
Res:=NetServerEnum(Nil,
101,
P,
LMax,
@nbEntreesLues,
@NbTotalEntrees,
Filter,
StringToOleStr(Domain),
@Position);
If (Res=0) Or (Res=ERROR_MORE_DATA) Then
Begin
pServer:=PServerInfo101(P);
For I:=0 To nbEntreesLues-1 Do
Begin
If LStr<>Nil Then
If LStr.IndexOf(pServer.sv101_name)<0
Then Lstr.Add(pServer.sv101_name);
Inc(pServer);
End;
End;
If P<>nil Then NetAPIBufferFree(P);
Until Res<>ERROR_MORE_DATA;
Result:=Res;
End;
////////////////////////////////////////////////////////////////////////////////
procedure TForm2.FormCreate(Sender: TObject);
Const
SV_TYPE_ALL= $FFFFFFFF;
begin
Memo1.Clear;
ListServers('',SV_TYPE_ALL,Memo1.Lines);
end;
////////////////////////////////////////////////////////////////////////////////

end.

Thank you

Yannick

Edited by: Yannick LANCHEC on Mar 5, 2015 2:30 AM

Edited by: Yannick LANCHEC on Mar 5, 2015 2:31 AM
Yannick LANCHEC

Posts: 43
Registered: 1/21/01
Re: WINAPI32 and X64
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 5, 2015 2:37 AM   in response to: Yannick LANCHEC in response to: Yannick LANCHEC
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: WINAPI32 and X64 [Edit]
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 5, 2015 1:00 PM   in response to: Yannick LANCHEC in response to: Yannick LANCHEC
Yannick wrote:

All JWA calls are like theses two parts :
<snip>
So what is the best method to correct all .....

As I already explained to you 4 months ago, the best option is to get rid
of the inline assembly altogether and use plain Pascal instead. Then the
code will work in both x86 and x64. For example:

var
  _NetApiBufferFree: function(Buffer: Pointer): NET_API_STATUS; stdcall = 
nil;
 
function NetApiBufferFree(Buffer: Pointer): NET_API_STATUS; stdcall;
begin
  if not Assigned(_NetApiBufferFree) then
    GetProcedureAddress(Pointer(_NetApiBufferFree), netapi32, 'NetApiBufferFree');
  Result := _NetApiBufferFree(Buffer);
end;
 
var
  _NetServerEnum: function(servername: LPCWSTR; level: DWORD; var bufptr: 
LPBYTE; prefmaxlen: DWORD; entriesread: LPDWORD; totalentries: LPDWORD; servertype: 
DWORD; domain: LPCWSTR; resume_handle: LPDWORD): NET_API_STATUS; stdcall 
= nil;
 
function NetServerEnum(servername: LPCWSTR; level: DWORD; var bufptr: LPBYTE; 
prefmaxlen: DWORD; entriesread: LPDWORD; totalentries: LPDWORD; servertype: 
DWORD; domain: LPCWSTR; resume_handle: LPDWORD): NET_API_STATUS; stdcall;
begin
  if not Assigned(_NetServerEnum) then
    GetProcedureAddress(Pointer(_NetServerEnum), netapi32, 'NetServerEnum');
  Result := _NetServerEnum(servername, level, bufptr, prefmaxlen, entriesread, 
totalentries, servertype, domain, resume_handle);
end;
 
...

Alternatively:

var
  NetApiBufferFree: function(Buffer: Pointer): NET_API_STATUS; stdcall;
 
function _NetApiBufferFree(Buffer: Pointer): NET_API_STATUS; stdcall;
begin
  GetProcedureAddress(Pointer(NetApiBufferFree), netapi32, 'NetApiBufferFree');
  Result := NetApiBufferFree(Buffer);
end;
 
var
  NetServerEnum: function(servername: LPCWSTR; level: DWORD; var bufptr: 
LPBYTE; prefmaxlen: DWORD; entriesread: LPDWORD; totalentries: LPDWORD; servertype: 
DWORD; domain: LPCWSTR; resume_handle: LPDWORD): NET_API_STATUS; stdcall;
 
function _NetServerEnum(servername: LPCWSTR; level: DWORD; var bufptr: LPBYTE; 
prefmaxlen: DWORD; entriesread: LPDWORD; totalentries: LPDWORD; servertype: 
DWORD; domain: LPCWSTR; resume_handle: LPDWORD): NET_API_STATUS; stdcall;
begin
  GetProcedureAddress(Pointer(NetServerEnum), netapi32, 'NetServerEnum');
  Result := NetServerEnum(servername, level, bufptr, prefmaxlen, entriesread, 
totalentries, servertype, domain, resume_handle);
end;
 
...
 
initialization
  NetApiBufferFree := _NetApiBufferFree;
  NetServerEnum := _NetServerEnum;
  ...
end.

It may not be as compact as the original, but it is much more portable.
And in both cases, the code is also avoiding redundant calls to GetProcedureAddress()
once a given function has been loaded.

--
Remy Lebeau (TeamB)

Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02