views:

181

answers:

4

I am using this unit in a Delphi 2010 application to tell me what Active Directory Groups a user is a member of.

I created a brand-new test vcl forms application, added the unit from that link, and made a little form with an edit box for the username, another edit box to hold the CSV separated list of groups, and a list box to hold the list of groups in a columnar format.

My code looks like this:

procedure TfrmMain.btnShowGroupsClick(Sender: TObject);
var
  ad: TADSI;
  adrec: TADSIUserInfo;
  csvGroups: string;
  slGroups: TStringList;
begin
  //take username from an edit box, tell me what AD groups they are a member of
  ad := TADSI.Create(Self);
  try
    ad.GetUser(edtDomain.Text,edtUser.Text,adrec);
    csvGroups := adrec.Groups;
    edtADGroups.Text := csvGroups;  //ACCESS VIOLATION!!
  finally
    FreeAndNil(ad);
  end;

  {
  //If I UN-comment this code, and make NO OTHER CHANGES, then the
  //aforementioned access violation does NOT occur; there are no errors @ all,
  //and everything works just fine

  slGroups := TStringList.Create;
  try
    slGroups.CommaText := csvGroups;
    listBoxADGroups.Items := slGroups;
  finally
    FreeAndNil(slGroups);
  end;
  //}
end;

If I run this code as-is, I get an access violation when I try to assign the CSV list of group to the edit box.

---------------------------
Debugger Fault Notification
---------------------------
Project C:\Users\my_username.mydomain\bin\ADSITest.exe faulted with message: 'access violation at 0x0048a321: read of address 0x458c0035'. Process Stopped. Use Step or Run to continue.
---------------------------
OK   
---------------------------

However, if I un-comment the block of code involving the TStringList, then it all works great.

Either this is some really weird compiler bug, or I'm missing something obvious. Can someone help me out?

The "adrec" structure is a simple record consisting of a few booleans, strings, and one other record (TPassword).

+1  A: 

When you do listBoxADGroups.Items := slGroups; you are replacing listBoxADGroups.Items by a pointer to slGroups, a couple of lines below, you are freeing it. When the program ends your btnShowGroupsClick method, the ListBox tries to use this pointer, but it is nil!, hence, the A.V.

A solution is this:

  slGroups := TStringList.Create;
  try
    slGroups.CommaText := csvGroups;
    listBoxADGroups.Items.AddStrings(slGroups);
  finally
    FreeAndNil(slGroups);
  end;

The AddStrings method copies the contents of slGroups to listBoxAdGroups.Items property (which is a TStrings object too), instead of just replacing a pointer. This way, the Items property of TListBox is intact, only its content was changed.

Leonardo M. Ramé
I think you'll find that the `Items` property of the `TListbox` does not allow you to overwrite the pointer to it's own items, instead it will copy the contents of the `TStringList` into itself. So the problem isn't there.
Nat
@Nat - indeed. Generally any assigning to any TStrings property in the VCL simply calls `FField.AddStrings(Value);`
Gerry
You are right Nat, assignment to Items properties in TCustomListBox calls its Setter, who calls AddStrings.
Leonardo M. Ramé
A: 

I see that in your code you also get an AV on the line:

edtADGroups.Text := csvGroups;  //ACCESS VIOLATION!!

In the section involving the TStringList, I bet you get the exception on the line:

listBoxADGroups.Items := slGroups;

I think that Self is invalid somehow, maybe it's been free'd somewhere...

Are you calling that method externally, or are you clicking on the button?

Nat
Not "also"; that's the access violation in question. The code says that when the listbox-related code is included, there are *no* access violations in *either* portion of code. That is, the later code makes the earlier exception go away.
Rob Kennedy
Clicking on the button
JosephStyons
@Rob; that's right; UN-commenting the second portion removes the AV in the *first* portion, which is what seems so strange to me.
JosephStyons
A: 

I haven't reproduced the problem or examined the output of the compiler for this code, but I notice that when that block of code is commented out, the csvGroups variable is redundantly used to store and load a value from one location into another.

This makes me wonder if the compiler is perhaps prematurely optimising away some crucial housekeeping around that string variable.

The commented out code contains a further reference to csvGroups, thus extending the life of that variable and potentially defeating the optimisation that the compiler is erroneously performing.

To test this theory I would eliminate the use of that variable entirely from the "as-is" version of the code, i.e. change:

csvGroups := adrec.Groups;
edtADGroups.Text := csvGroups;  //ACCESS VIOLATION!!

to simply:

edtADGroups.Text := adrec.Groups;
Deltics
i have tried this, with the same result.
JosephStyons
@Deltics; this doesn't solve the problem, and furthermore the debugger correctly displays the value of "csvgroups" when i ctrl+f7 or use code insight. cool line of reasoning though...
JosephStyons
A: 

This is speculation, but it looks to me like the unit you're using is not UNICODE compatible. If you don't get any group back (did you debug and look at the return?) cvsGroups is probably not terminated properly. When you uncomment the code, writing to slGroups probably overwrites whatever junk you had before (or at least the compiler does something like initializing slGroups once it's clear it will be touched).

Jim
@Jim: thanks for the idea, but even in the "access violation" version, the debugger shows a correct value in csvGroups prior to the assignment. Furthermore, un-commenting the later block of code causes the AV to go away, without changing the characteristics of csvGroups at all.
JosephStyons