2015-12-15 3 views
0

Я разрабатываю проект, который поможет мне управлять моей удаленной сетью, так как мне нужны некоторые конкретные функции, которые я решил закодировать.NetShareEnum правильно отображает доли

Я подключаюсь к удаленным компьютерам, используя WNetAddConnection2, и эта часть работает. Но теперь я пытаюсь перечислить все акции (ADMIN $, C $, IPC $ и любые общие папки), используя функцию NetShareEnum. Я полагался на эту функцию, а не на 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

Довольно трудно понять, что происходит не так. Может быть, interop, или может быть, что вы используете API неправильно. Если бы я был вами, я бы убрал interop из уравнения и сделаю начальную разработку на C++, используя заголовочные файлы Windows. Таким образом, вы знали бы, что вмешательство не было проблемой. Например, я не знаю, почему вы решили, что запись должна быть упакована. Может ли это быть проблема? Может быть. Возможно, нет. Удалите эти потенциальные точки отказа из уравнения. –

+0

Взаимодействие Я думаю, что вызов функции из внешней DLL в Delphi? Я так думаю ... Проблема в том, что я знаю 0 C++ ... Я думаю, чтобы удалить этот потенциальный сбой, я мог бы использовать WNetEnumResource, но пока этого не добился. – LessStress

+0

'for i: = 1 to Pred (er) do' - почему вы пропускаете первый элемент, который равен # 0? –

ответ

0

Я думаю, у вас есть проблемы с многопоточными проблемами.

Прежде всего, проверьте, является ли тот самый API, который вы используете, поточно-безопасным. Я не нашел эту информацию, но http://computer-programming-forum.com/82-mfc/8e7756aee43ed65a.htm

Возможно, вы просто не можете назвать эту функцию из разных потоков одновременно.

Второе: все сотни ваших нитей составляют Form1.Memo1.Lines.Add(String(p^[i].shi502_netname)) - это ОЧЕНЬ неправильно.

  1. ВЫ НЕ МОЖЕТЕ получить доступ к объектам графического интерфейса из потоков. НЕ МОГУ. Период.
    См., Например, Delphi 7 Occasional deadlock changing TLabel.Font.Style from IdHTTPListener event.

Сам процесс загрузки формы из DFM-ресурса, инициализации, создания объектов Windows и Delphi и их связывания, является сложным. Когда в то же время сотни потоков разбиваются на половину созданного и обновляемого полусозданного MEMO, они буквально уничтожают действия друг друга.

В общем, вы сказали нам, что Windows не вернула вам все акции, но вы имеете в виду, что наполовину созданный TMemo, злоупотребляемый сотнями потоков, не показывает вам всех акций. Это не то же самое, что может означать, что Windows работает плохо, но это также может означать, что Windows работает нормально, но вы не можете привести все результаты в VCL GUI. Вы должны убедиться, что именно произошло.

Попробуйте получить акции
1.1 только в одном потоке!
1.2 и это должен быть основной поток, а не дополнительные.
1.3 и вы должны запустить его только после создания формы - например, из какого-либо события нажатия кнопки.

И проверьте, есть ли разница.

  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; 

Так немного проект, чтобы проверить ваши вопросы могут быть как этот

Только черновик для вас, чтобы посмотреть на общий подход

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;