2

I have a problem converting a 32-bit to 64-bit code using the lsaapi.pas unit with a small Unicode PChar to PAnsiChar correction.

The following code will work in 32-bit mode but not in 64-bit. Running the procedure (not in 64-bit Debug mode!), getting the error message invalid parameter by calling LsaQueryInformationPolicy()

Any ideas, what's wrong ?
Why is there a different behavior running this code in the 64-bit debug and non-debug mode ?
Maybe a record alignment problem in 64-bit ?

Here is the code:

uses
  lsaapi;

function GetDomainName: string;
var
  Buffer: Pointer;
  Status: NTStatus;
  PolicyHandle: LSA_HANDLE;
  ComputerName: TLsaUnicodeStr;
  Attributes: TLsaObjectAttributes;
  PolicyAccountDomainInfo: PPolicyAccountDomainInfo;
begin
  ComputerName := TLsaUnicodeStr.CreateFromStr('');
  try
    FillChar(Attributes, SizeOf(Attributes), 0);    
    Status := LsaOpenPolicy(ComputerName.Value, Attributes, 
      POLICY_VIEW_LOCAL_INFORMATION, PolicyHandle);
    if Status <> STATUS_SUCCESS then
      raise Exception.Create('LsaOpenPolicy Failed: ' + 
        SysErrorMessage(LsaNtStatusToWinError(Status)));    
    try
      Status := LsaQueryInformationPolicy(PolicyHandle, 
        PolicyPrimaryDomainInformation, Buffer);
      if Status <> STATUS_SUCCESS then
        raise Exception.Create('LsaQueryInformationPolicy Failed: ' +
          SysErrorMessage(LsaNtStatusToWinError(Status)));    
      try
        PolicyAccountDomainInfo := Buffer;
        Result := PolicyAccountDomainInfo.DomainName.Buffer;
      finally
        LsaFreeMemory(Buffer)
      end;
    finally
      LsaClose(PolicyHandle)
    end;
  finally
    ComputerName.Free;
  end;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392

3 Answers3

8

All the records in that lsaapi unit are declared to be packed. The Windows API header files do not use packed structs. Fix it by removing all the packed modifiers. If you make that change your function succeeds in both 32 and 64 bit targets.

For what it is worth, your code is actually failing on the call to LsaOpenPolicy. With packed records SizeOf(Attributes) returns 40. The correct size, is 48, and that's the value you get when you remove the packed modifier.

The easiest way to debug this kind of thing is to have a copy of Visual Studio installed so that you can compare equivalent C++ code.

I presume that the incorrect record declarations is the primary problem with that unit. There may very be others, but that's the one that sticks out like a sore thumb.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • +1 And if you do not have Visual Studio you can use this method (with Delphi code): http://stackoverflow.com/questions/3092609/how-to-get-field-names-and-offsets-of-a-struct-using-dbghlp-and-pdb – Remko Aug 27 '12 at 18:12
2

Your error is most probably occurring both at debug and at run-time, but swallowed at run-time. I've had that occur a few times both in the x86 and x64 world in various development environments.

So:

  • Make sure the right one gets loaded into your process space.
  • Make sure the alignment and packing is right, as there have been other cases where this matters in the 64-bit world

If you get it to work, please notify Colin that you get a new version of his unit.

At first I thought this is because you cannot call 32-bit DLLs from 64-bit processes (unlike the 16/32-bit case where you had thunking between 16-bit and 32-bit and vice versa, there is no such thunking in the 32/64-bit case).

Then I found out there are two versions of the advapi32.dll: a 32-bit one and a 64-bit one.

Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
  • Of course there are two versions of that DLL. There are two versions of all system DLLs. The right one is being loaded, we already know that from the question. The file system redirector ensures that. Just as it does for user32, gdi32 etc. – David Heffernan Aug 27 '12 at 09:37
  • I was confused by the naming, as I have been working on some WebSphere MQ stuff where the DLLs for x86 and x64 are in fact named differently (: Anyway: I upvoted your answer as it is a bit more condensed. – Jeroen Wiert Pluimers Aug 27 '12 at 09:39
  • Still, the problem OP has is not between 32-bit vs. 64-bit, but with Debug vs. Release mode. – TLama Aug 27 '12 at 09:40
  • 2
    @TLama I assume his error is there in both modes, but in release mode it gets swallowed somewhere. I've seen that happen on the x86 side of things too. – Jeroen Wiert Pluimers Aug 27 '12 at 09:41
  • If the loader attempted to load a 32 bit DLL it would fail and stop the process from starting. If it loaded the wrong 64 bit DLL, the exports would not be found and the loader would stop the process from starting. Ergo, the correct DLL is being loaded. – David Heffernan Aug 27 '12 at 09:42
  • With the non packed record it will work. What is the reason of run "with" debugger and run "without" debugger? Not the same as debug and release mode (different compiler options) – user1627172 Aug 27 '12 at 10:53
  • @user1627172 The code fails both with and without the debugger. Presumably your code that calls this function swallows the exception. When you run with the debugger an exception results in the debugger breaking at the point where the exception was raised. Or is that not what you meant by that comment? – David Heffernan Aug 27 '12 at 10:54
  • @David: The coding check the result code of each API call. Why could this be different when Delphi debugger is present or not? – user1627172 Aug 27 '12 at 11:00
  • Two reasons: The debugger will break at the source of an exception, before it gets swallowed. Another reason is that because the debugger has to keep track of various things, your running environment is different under the debugger then under plain runtime. I can't find any Delphi related links now, but this one is where the C# debugger does things differently than the runtime: http://stackoverflow.com/questions/9615943/surprising-clr-jit-behaviour-deferred-initialization-of-a-local-variable – Jeroen Wiert Pluimers Aug 27 '12 at 11:06
  • 3
    @JeroenWiertPluimers Why don't you remove all the text about 32 vs 64 bit DLLs since that is clearly not relevant? – David Heffernan Aug 27 '12 at 11:17
2

As other answers already indicated your records have an incorrect size, probably due to the packed statement (structs in winapi are usually not packed but aligned).

My advice is to use the Jedi Windows ApiLib (JwaNtSecApi in this case) as it has generally the best and time proven conversions.

Remko
  • 7,214
  • 2
  • 32
  • 52