NetShareEnum无法正确显示共享 [英] NetShareEnum not displaying shares correctly
问题描述
我正在开发一个项目来帮助我管理远程网络,因为我需要一些非常特殊的功能,因此我决定对其进行编码.
I'm developing a project to help me managing my remote network, as I need some very specific features I decided to code it.
我使用 NetShareEnum 函数.我依赖此功能而不是 WNetEnumResource ,因为我发现了更多与NetShareEnum一起工作的示例,并且对我来说效果更好.问题是我的NetShareEnum实现仅列出某种类型的文件夹(看起来仅是共享的文件夹,但我无权访问).它不会列出普通文件夹(我可以访问的文件夹),ADMIN $,C $,IPC $或其他任何内容.只有我无权访问的共享文件夹.
I connect to the remote computers using WNetAddConnection2 and this part is working. But now I try to list all the shares (ADMIN$, C$, IPC$, and any shared folders) using the NetShareEnum function. I relied on this function and not on WNetEnumResource because I found more examples working with NetShareEnum, and it's working better for me. The problem is that my implementation of NetShareEnum is listing only some type of folders (looks like only folders that are shared but I have no access). It doesn't list normal folders (where I have access), ADMIN$, C$, IPC$, or anything else. Only shared folders that I'm without rights to access.
我仍然不确定所有服务器上的行为是否相同,但是我测试过的服务器上的行为是否相同.到目前为止,我所拥有的是:
I still not sure if the behavior is the same on all servers, but the ones I tested it was. So far what I have is:
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地址给您尝试该项目,但不确定是否符合规定.无论如何,这段代码有问题吗?
I can post my IP address here to you try this project, but not sure if this is under the rules. Anyway, is something wrong with this code?
推荐答案
我认为您存在BAD多线程问题.
I think you have BAD multithreading issues.
首先,检查您使用的API是否是线程安全的.我没有找到此特定信息,但是 http://computer-programming-forum.com/82-mfc/8e7756aee43ed65a.htm
First of all, check if the very API you use is thread-safe. I did not found this particular information, but http://computer-programming-forum.com/82-mfc/8e7756aee43ed65a.htm
也许您不能同时从不同的线程调用该函数.
Maybe you just can not call that function from different threads at the same time.
第二:您的所有数百个线程都执行 Form1.Memo1.Lines.Add(String(p ^ [i] .shi502_netname))
-这是非常错误的.
Second: all hundreds of your threads do Form1.Memo1.Lines.Add(String(p^[i].shi502_netname))
- that is VERY wrong.
- 您无法从线程访问GUI对象.不能.期间.
例如,请参见 Delphi 7从IdHTTPListener事件更改TLabel.Font.Style的偶然死锁.
从DFM资源加载表格,对其进行初始化,创建Windows和Delphi对象以及将它们绑定在一起的过程非常复杂.同时,成百上千个线程崩溃进入半创建的内存并更新半创建的MEMO时,它们确实破坏了彼此的动作.
The very process of loading form from DFM-resource, initializing it, creating Windows and Delphi objects and binding them, together is complex. When at the same time hundreds of threads are crashing into half-created from and updating half-created MEMO they literally do destroy actions of one another.
基本上,您告诉我们Windows并未退还您所有的份额-但是您的意思是,一半创建的TMemo被数百个线程滥用了,所以并没有显示所有份额.这是不一样的,这可能意味着Windows不能正常工作,但也可能意味着Windows可以正常工作,但是您无法将所有结果放入VCL GUI中.您必须确保发生了什么事.
Basically you told us that Windows did not returned you all the shares - but what you mean is that half-created TMemo abused by hundreds of threads does not show you all the shares. That is not the same, that might mean Windows work badly, but it also might mean Windows works ok, but you fail to put all the results into VCL GUI. You have to ensure what exactly happened.
尝试获取股票
1.1仅在一个单线程中!
1.2,并且应该是MAIN线程,而不是多余的线程.
1.3,您仅应在创建表单后启动它-例如,从某个按钮单击事件开始.
Try getting shares
1.1 only in one single thread!
1.2 and that should be MAIN thread, not extra ones.
1.3 and you only should start it after the form is created - for example from some button click event.
并检查是否存在差异.
- 您不应在备忘录中一行添加数据-速度非常慢.
进行简单的测试.
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;
所以测试您的问题的草稿可能是这样的
So a little draft to test your issues might be like this
- http://docwiki.embarcadero.com/Libraries/XE2/zh/System.IOUtils.TFile.ReadAllLines
- http://www.thedelphigeek.com/2010/06/omnithreadlibrary-20-sneak-preview-1.html
- http://www.thedelphigeek.com/2010/11/multistage-processes-with.html
- http://otl.17slon.com/book/chap04.html#highlevel-pipeline
只是一份草稿,供您研究通用方法
Just a draft for you to look into generic approach
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;
这篇关于NetShareEnum无法正确显示共享的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!