NetShareEnum无法正确显示共享 [英] NetShareEnum not displaying shares correctly

查看:64
本文介绍了NetShareEnum无法正确显示共享的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个项目来帮助我管理远程网络,因为我需要一些非常特殊的功能,因此我决定对其进行编码.

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.

  1. 您无法从线程访问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.

并检查是否存在差异.

  1. 您不应在备忘录中一行添加数据-速度非常慢.

进行简单的测试.

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

只是一份草稿,供您研究通用方法

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆