我正在开发一个项目来帮助我管理远程网络,因为我需要一些非常具体的功能,我决定对其进行编码。
我使用WNetAddConnection2连接到远程计算机,这部分正常工作。但现在我尝试使用NetShareEnum函数列出所有共享(ADMIN $,C $,IPC $和任何共享文件夹)。我依赖这个函数而不是 WNetEnumResource ,因为我发现了更多使用NetShareEnum的例子,它对我来说效果更好。问题是我的NetShareEnum实现只列出了某种类型的文件夹(看起来只有共享的文件夹,但我没有访问权限)。 它不会列出普通文件夹(我有访问权限),ADMIN $,C $,IPC $或其他任何内容。只有我无权访问的共享文件夹。
我仍然不确定所有服务器上的行为是否相同,但我测试的是。到目前为止我所拥有的是:
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)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
HostFile: TStringList;
iHost: integer;
type
SharesThread = class(TThread)
strict private
IPAddress: String;
function Authenticate: bool;
procedure EnumShares(RemoteName: PWChar);
protected
constructor Create(const IPv4: string);
procedure Execute; override;
end;
type
_SHARE_INFO_502 = packed record
shi502_netname: PWideChar;
shi502_type: DWORD;
shi502_remark: PWideChar;
shi502_permissions: DWORD;
shi502_max_uses: DWORD;
shi502_current_uses: DWORD;
shi502_path: LPWSTR;
shi502_passwd: LPWSTR;
shi502_reserved: DWORD;
shi502_security_dsc: PSECURITY_DESCRIPTOR;
end;
SHARE_INFO_502 = _SHARE_INFO_502;
PSHARE_INFO_502 = ^SHARE_INFO_502;
LPSHARE_INFO_502 = PSHARE_INFO_502;
TShareInfo502 = SHARE_INFO_502;
PShareInfo502 = PSHARE_INFO_502;
type
TShareInfo502Array = Array [0..MaxWord] of TShareInfo502;
PShareInfo502Array = ^TShareInfo502Array;
function NetApiBufferFree(buffer: Pointer): DWORD; stdcall; external 'netapi32.dll';
function NetShareEnum(servername: PWideChar;
level: DWORD;
bufptr: PByteArray;
prefmaxlen: DWORD;
entriesread: PDWORD;
totalentries: PDWORD;
resume_handle: PDWORD): DWORD; stdcall; external 'netapi32.dll';
implementation
const
NERR_Success = 0;
MAX_PREFERRED_LENGTH = DWORD( -1 );
procedure StartThreads;
var
CurrentIP: string;
begin
if (iHost < HostFile.Count) then
begin
CurrentIP:= HostFile.Strings[iHost];
inc(iHost);
SharesThread.Create(CurrentIP);
end
else
Form1.Memo1.Lines.Add('finished');
end;
constructor SharesThread.Create(const IPv4: string);
begin
inherited Create(false);
FreeOnTerminate:= true;
IPAddress:= IPv4;
end;
function SharesThread.Authenticate;
var
lpNetResource: TNetResource;
myres: cardinal;
begin
with lpNetResource do
begin
dwType := RESOURCETYPE_ANY;
lpLocalName := nil;
lpProvider := nil;
lpRemoteName:= PChar('\\'+IPAddress);
end;
myres := WNetAddConnection2(lpNetResource, PChar('123456'), PChar('BlackNote'), 0);
if ( myres = NO_ERROR ) then
begin
Result:= true;
EnumShares(lpNetResource.lpRemoteName);
end
else
begin
Result:= false;
end;
end;
procedure SharesThread.EnumShares(RemoteName: PWChar);
var
p: PShareInfo502Array;
res, er, tr, resume, i: DWORD;
begin
repeat
res:=NetShareEnum(RemoteName, 502, @p, MAX_PREFERRED_LENGTH, @er, @tr, @resume);
if (res = ERROR_SUCCESS) or (res = ERROR_MORE_DATA) then
begin
for i:=1 to Pred(er) do
begin
Form1.Memo1.Lines.Add(String(p^[i].shi502_netname));
end;
NetApiBufferFree(p);
end;
until (res <> ERROR_MORE_DATA);
end;
procedure SharesThread.Execute;
begin
if Authenticate then
Form1.Memo1.Lines.Add(IPAddress + '=' + 'Listed shares above')
end;
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
HostFile:= TStringList.Create;
HostFile.LoadFromFile('Hosts.txt');
iHost:= 0;
StartThreads;
end;
end.
我可以在这里发布我的IP地址给你试试这个项目,但不确定这是否符合规则。无论如何,这段代码有问题吗?
答案 0 :(得分:0)
我认为你有多方面的多线程问题。
首先,检查您使用的API是否是线程安全的。 我没有找到这个特定的信息,但是http://computer-programming-forum.com/82-mfc/8e7756aee43ed65a.htm
也许你不能同时从不同的线程调用该函数。
第二:你的所有数百个线程都Form1.Memo1.Lines.Add(String(p^[i].shi502_netname))
- 这是非常错误的。
从DFM资源加载表单,初始化它,创建Windows和Delphi对象并将它们绑定在一起的过程非常复杂。 当同时数百个线程崩溃成半创建并更新半创建的MEMO时,它们确实会破坏彼此的行为。
基本上你告诉我们Windows没有把你所有的股票归还给你 - 但是你的意思是被数百个线程滥用的半创建的TMemo没有向你展示所有的股票。这是不一样的,这可能意味着Windows工作很糟糕,但它也可能意味着Windows工作正常,但您无法将所有结果放入VCL GUI。你必须确保到底发生了什么。
尝试获得股票
1.1只在一个单一的线程中!
1.2应该是主线程,而不是额外的线程
1.3并且您只应在创建表单后启动它 - 例如从某个按钮单击事件开始。
并检查是否存在差异。
进行简单的测试。
uses Hourglass; // http://www.deltics.co.nz/blog/posts/tag/delticshourglass
const cMax = 10000;
procedure TForm1.Button1Click( Sender: TObject );
var sl: TStrings; i: integer; t: cardinal;
begin
HourglassOn();
t := GetTickCount();
sl := TStringList.Create;
try
for i := 1 to cMax do
sl.Add(IntToStr(i));
Memo1.Lines.Clear;
Memo1.Lines.AddStrings(sl);
finally
sl.Destroy;
end;
t := GetTickCount - t;
ShowMessage('It took ' + IntoToStr(t) + '/1000 seconds');
end;
procedure TForm2.Button1Click( Sender: TObject );
var i: integer; t: cardinal;
begin
HourglassOn();
t := GetTickCount();
Memo1.Lines.Clear;
for i := 1 to cMax do begin
Memo1.Lines.Add(IntToStr(i));
// giving Windows chance to repaint the memo
// simulating access from extra threads
// when main thread is free to repaint forms time and again
Application.ProcessMessages;
end;
t := GetTickCount - t;
ShowMessage('It took ' + IntoToStr(t) + '/1000 seconds');
end;
因此,测试您的问题的小草稿可能就像这样
只是草案供您查看通用方法
const WM_EnumEnded = WM_USER + 1;
type TFrom1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
....
public
var Enums: iOmniBlockingCollection;
procedure StartEnum( const SingleThread: boolean );
procedure ShowResults( var m: TMessage); message WM_EnumEnded;
.....
procedure TFrom1.StartEnum( const SingleThread: boolean );
var hosts: TStringDynArray; // TArray<string>, array of string....
Source: IOmniBlockingCollection;
iWorker: IOmniParallelLoop<T>; // variable only needed for if-then-else
begin
hosts := TFile.ReadAllLines('hosts.txt');
Self.Enums := TOmniBlockingCollection.Create; // Results accumulator
Source := TOmniBlockingCollection.Create;
Source.Add( TOmniValue.FromArray<string>(hosts) );
iWorker := Parallel.ForEach<string>( Source ).NoWait().OnStop(
procedure begin PostMessage( Self.Handle, WM_EnumEnded, 0, 0) end
);
if SingleThread then iWorker := iWorker.NumTasks(1);
iWorker.Execute(
procedure(const value: String)
var i: integer;
begin
....
res:=NetShareEnum(RemoteName, 502 { 503 better ?? } ... );
....
Self.Enums.Add( TOmniValue(String(p^[i].shi502_netname)) );
...
end;
);
end;
procedure TFrom1.ShowResults( var m: TMessage );
var sa: TArray<String>;
begin
Self.Enums.CompleteAdding;
sa := TOmniblockingCollection.ToArray<string>( Self.Enums );
Memo1.Clear;
Memo1.Lines.AddStrings( sa );
end;
procedure TFrom1.Button1Click(sender: Tobject);
begin
StartEnum( True );
end;
procedure TFrom1.Button2Click(sender: Tobject);
begin
StartEnum( False );
end;