NetShareEnum无法正确显示共享

时间:2015-12-15 15:47:37

标签: delphi

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

我使用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地址给你试试这个项目,但不确定这是否符合规则。无论如何,这段代码有问题吗?

1 个答案:

答案 0 :(得分:0)

我认为你有多方面的多线程问题。

首先,检查您使用的API是否是线程安全的。 我没有找到这个特定的信息,但是http://computer-programming-forum.com/82-mfc/8e7756aee43ed65a.htm

也许你不能同时从不同的线程调用该函数。

第二:你的所有数百个线程都Form1.Memo1.Lines.Add(String(p^[i].shi502_netname)) - 这是非常错误的。

  1. 您无法从线程访问GUI对象。不能。期。
    例如,请参阅Delphi 7 Occasional deadlock changing TLabel.Font.Style from IdHTTPListener event
  2. 从DFM资源加载表单,初始化它,创建Windows和Delphi对象并将它们绑定在一起的过程非常复杂。 当同时数百个线程崩溃成半创建并更新半创建的MEMO时,它们确实会破坏彼此的行为。

    基本上你告诉我们Windows没有把你所有的股票归还给你 - 但是你的意思是被数百个线程滥用的半创建的TMemo没有向你展示所有的股票。这是不一样的,这可能意味着Windows工作很糟糕,但它也可能意味着Windows工作正常,但您无法将所有结果放入VCL GUI。你必须确保到底发生了什么。

    尝试获得股票   1.1只在一个单一的线程中!
      1.2应该是主线程,而不是额外的线程   1.3并且您只应在创建表单后启动它 - 例如从某个按钮单击事件开始。

    并检查是否存在差异。

    1. 您不应该将一行数据添加到备忘录中 - 它非常慢。
    2. 进行简单的测试。

      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;