Sadly, not that simpleā¦
I have found the solution, though. After trying everything (local/global var, build up the entries into a simple string, off-loading each entry to a local string to pass it to the list, etc.), I remembered catching an odd comment in a code sample while looking into this:
ā// Causes Access Violation if AD query does not happen in subroutineā
and sure enough, passing the query to another routine before calling the ADS makes the call behave normally, with any variable.
However further tests show that a tricky maze of conditions still needs to be navigated to avoid access violations altogether:
ā The ADS querying routine must be called from a sub-routine function (not a procedure),
ā There are then 2 ways to collect the results from the sub-routine
1- the sub-routine can pass a global variable to the ADS routine, this global variable can then be read by any subsequent procedure,
2- the sub-routine can be called by a procedure passing it a local variable, AS LONG AS that variable is not referred to again in the sub-routine after the ADS call
I suspect the explanation for these strange intricacies is above my pay grade but if anyone has one (or any indication thereās a simpler way) Iād love to hear itā¦
So hereās the (improved) ADS calling function, and 2 ways to call it (other than a straight call passing the results to a component):
procedure findComputers(domain:string; Alist:TStrings);
var
ADsTempObj,
vFilter: OleVariant;
Enum: IEnumVariant;
Value: Cardinal;
DomCont: IADsContainer;
Filter: WideString;
ADs: IADs;
begin
try
CoInitialize(nil);
OleCheck(ADsGetObject(StringToOleStr(āWinNT://ā+UpperCase(domain)), IADsContainer, DomCont));
Filter := āComputerā;
VariantInit(vFilter);
OleCheck(ADsBuildVarArrayStr(@Filter, 1, variant(vFilter)));
DomCont.Set_Filter(vFilter);
Enum := (Domcont._NewEnum) as IEnumVariant;
while Enum.Next(1, ADsTempObj, Value) = S_OK do
begin
if Value = 0 then
Break;
ADs := IUnknown(ADsTempObj) as IADs;
Alist.Add(ADs.Name);
ADs._Release;
end;
finally
CoUninitialize;
end;
end;
--------------option 1---------------
var
GlobalList: TStringList.
ā¦
function call_FindComputers_option_1(domain: string): boolean;
begin
result:= false;
if GlobalList = nil then
exit;
findComputers(domain,GlobalList);
result:= GlobalList.Count > 0; //OK to manipulate result
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
GlobalList:= TstringList.Create;
call_FindComputers_option_1(āWORKGROUPā);
Memo1.Lines:= GlobalList;
GlobalList.Free;
end;
--------------option 2---------------
function call_FindComputers_option_2(domain: string; Rslt: TStrings): boolean;
begin
findComputers(domain,Rslt);
//result:= Rslt.Count > 0; // this will trigger an access violation
end;
procedure TForm1.Button1Click(Sender: TObject);
var
LocalList: TstringList;
begin
LocalList:= TstringList.Create;
call_FindComputers(āWORKGROUPā,LocalList); //causes access violation if ADS query called from a procedure
Memo1.Lines:= LocalList;
LocalList.Free;
end;