2011-11-17 45 views
5

Tôi có một dịch vụ NT mà các cuộc gọi một chương trình điều khiển được viết bằng Delphi 7, chúng ta hãy gọi nó failover.exe mà lần lượt gọi NETSH sử dụng một thủ tục tôi thấy:Có "Hệ thống không thể tìm thấy tệp được chỉ định" khi tôi chạy NETSH từ CreateProcess nhưng nó hoạt động tốt trên Command Prompt?

procedure ExecConsoleApp(CommandLine: ansistring; Output, Errors: TStringList); 

Lưu ý: ExecConsoleApp sử dụng CreateProcess, xem liên kết sau cho mã đầy đủ: http://www.delphisources.ru/pages/faq/base/createprocess_console.html

tôi sẽ vượt qua sau để commandline trước khi gọi ExecConsoleApp:

cmd.exe /c "C:\Windows\system32\netsh.exe interface delete address "Wireless Network Connection" 192.168.0.36" 

ExecConsoleApp sẽ trả về một lỗi:

The system cannot find the file specified

Nhưng nếu tôi được chạy nó trong Command Prompt, nó chạy một cách hoàn hảo.

Điều lạ lẫm là tôi nhớ nó hoạt động trong lần thử đầu tiên trên Máy chủ 2003 đó, nhưng sau đó, nó không thành công bất kể số lần tôi thử. Trong một lần thử, tôi cũng đã thử gán đăng nhập là người dùng quản trị viên cho dịch vụ nhưng không có kết quả. Cũng không quan tâm đến trợ giúp bảo mật tệp.

Tôi không có một máy chủ Win 2003 để thử nghiệm với trong văn phòng, nhưng tôi đã thử nghiệm nó trên XP và Win7 và ExecConsoleApp tác phẩm hoàn hảo, mặc dù trên XP, tôi đã phải sửa đổi ExecConsoleApp để thực hiện từ system32\wbem để cho nó công việc làm việc:

Res := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 
    // **** Attention: Amended by to point current directory to system32\wbem, this is to solve an error returned by netsh.exe if not done otherwise. 
// CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, nil, si, pi); 
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, pchar(GetSystemPath(WindRoot) + 'system32\wbem'), si, pi); 

Tôi đã nghiên cứu một ngày nhưng không có manh mối, hy vọng ai đó có thể trợ giúp. Cảm ơn.

nhận xét bổ sung -

  1. Server là 32 bit Win2k3.

  2. Quản trị viên miền đã cố gắng, không hoạt động.

  3. đoạn Code:

    Procedure ExecConsoleApp(CommandLine: ansistring; Output, Errors: TStringList); 
        var 
        sa: TSECURITYATTRIBUTES; 
        si: TSTARTUPINFO; 
        pi: TPROCESSINFORMATION; 
        hPipeOutputRead: THANDLE; 
        hPipeOutputWrite: THANDLE; 
        hPipeErrorsRead: THANDLE; 
        hPipeErrorsWrite: THANDLE; 
        Res, bTest: boolean; 
        env: array[0..100] of char; 
        szBuffer: array[0..256] of char; 
        dwNumberOfBytesRead: DWORD; 
        Stream: TMemoryStream; 
        begin 
        sa.nLength := sizeof(sa); 
        sa.bInheritHandle := True; 
        sa.lpSecurityDescriptor := nil; 
        CreatePipe(hPipeOutputRead, hPipeOutputWrite, @sa, 0); 
        CreatePipe(hPipeErrorsRead, hPipeErrorsWrite, @sa, 0); 
        ZeroMemory(@env, SizeOf(env)); 
        ZeroMemory(@si, SizeOf(si)); 
        ZeroMemory(@pi, SizeOf(pi)); 
        si.cb := SizeOf(si); 
        si.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; 
        si.wShowWindow := SW_HIDE; 
        si.hStdInput := 0; 
        si.hStdOutput := hPipeOutputWrite; 
        si.hStdError := hPipeErrorsWrite; 
    
        (* Remember that if you want to execute an app with no parameters you nil the 
        second parameter and use the first, you can also leave it as is with no 
        problems.                 *) 
        Res := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 
        CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, @env, nil, si, pi); 
    
    
        // Procedure will exit if CreateProcess fail 
        if not Res then 
        begin 
         CloseHandle(hPipeOutputRead); 
         CloseHandle(hPipeOutputWrite); 
         CloseHandle(hPipeErrorsRead); 
         CloseHandle(hPipeErrorsWrite); 
         Exit; 
        end; 
        CloseHandle(hPipeOutputWrite); 
        CloseHandle(hPipeErrorsWrite); 
    
        //Read output pipe 
        Stream := TMemoryStream.Create; 
        try 
         while True do 
         begin 
         bTest := ReadFile(hPipeOutputRead, szBuffer, 256, dwNumberOfBytesRead, nil); 
         if not bTest then 
         begin 
          break; 
         end; 
         OemToAnsi(szBuffer, szBuffer); 
         Stream.Write(szBuffer, dwNumberOfBytesRead); 
         end; 
         Stream.Position := 0; 
         Output.LoadFromStream(Stream); 
        finally 
         Stream.Free; 
        end; 
    
        //Read error pipe 
        Stream := TMemoryStream.Create; 
        try 
         while True do 
         begin 
         bTest := ReadFile(hPipeErrorsRead, szBuffer, 256, dwNumberOfBytesRead, nil); 
         if not bTest then 
         begin 
          break; 
         end; 
         OemToAnsi(szBuffer, szBuffer); 
         Stream.Write(szBuffer, dwNumberOfBytesRead); 
         end; 
         Stream.Position := 0; 
         Errors.LoadFromStream(Stream); 
        finally 
         Stream.Free; 
        end; 
    
        WaitForSingleObject(pi.hProcess, INFINITE); 
        CloseHandle(pi.hProcess); 
        CloseHandle(hPipeOutputRead); 
        CloseHandle(hPipeErrorsRead); 
        end; 
    
    
        cmdstring := 
        'cmd.exe /c "' + GetSystemPath(WindRoot) + 'system32\netsh.exe interface ' + 
        ip + ' delete address "' + NetworkInterfaceName + '" ' + VirtualFailoverIPAddress + '"'; 
    
        logstr('cmdstring: ' + cmdstring); 
        ExecConsoleApp(cmdstring, OutP, ErrorP); 
    
        if OutP.Text <> '' then 
        begin 
        logstr('Delete IP Result: ' + OutP.Text); 
        end 
        else 
        begin 
        logstr('Delete IP Error: ' + ErrorP.Text); 
        end; 
    
  4. Cố gắng chạy netsh.exe trực tiếp thay vì "cmd.exe/c C: \ Windows \ system32 \ netsh.exe ...", và đã cùng " Hệ thống không thể tìm thấy các tập tin được chỉ định." lỗi. Tôi cũng vô tình phát hiện ra rằng nếu tôi đưa ra lệnh netsh sai, netsh sẽ thực sự trả về lỗi, ví dụ:

netsh interface ip xóa địa chỉ "LocalArea Connection" 10.40.201.65

Invalid interface LocalArea Connection specified.

Sau đây là trả lại nếu tôi sửa lỗi đánh máy "LocalArea" thành "Local Area". netsh interface ip xóa địa chỉ "Local Area Connection" 10.40.201.65

The system cannot find the file specified.

Một lần nữa, tôi phải lặp lại rằng lệnh cùng làm việc hoàn toàn tốt đẹp nếu tôi phát hành nó thông qua Command Prompt thay vì từ ứng dụng của tôi.

+2

Nếu đây là hệ thống 64 bit, c: \ windows \ syswow64 \ netsh.exe có tồn tại không? –

+0

@Harry: Điểm tốt, nếu ứng dụng OPs là 32-bit, nó sẽ bị chuyển hướng WOW64. –

+0

@Harry: Đó là 32 bit, Win2k3, một máy cũ. Tôi sẽ cố gắng mà không có tiền tố cmd.exe/c như Jens đã đề xuất trên máy chủ win2k3 của máy khách. – Joshua

Trả lời

2

Bạn đã thử điều này chưa?

if not CreateProcess(PChar('C:\Windows\system32\netsh.exe'), PChar(Arguments), ...) then 
begin 
    // Do somehting with `GetLastError` 
end; 

Tất nhiên sẽ tốt hơn khi phát hiện đường dẫn C:\Windows\system32 khi chạy vì điều này có thể nằm trên trình điều khiển khác hoặc trong thư mục khác.

Khi bạn chạy theo cách này, bạn có thể nhận được thông báo lỗi từ Windows bằng cách sử dụng cuộc gọi GetLastError ngay sau CreateProcess.

Quy trình ExecConsoleApp là thiếu sót, vì nó không trả lại GetLastError hoặc thậm chí bất kỳ dấu hiệu nào cho thấy CreateProcess không thành công.

Bạn nên sửa lỗi này trước tiên. Có thể thêm raise EExecConsoleAppCreateProcessFailed.Create(SysErrorMessage(GetLastError)) trước Exit vào mã.

Bạn không nên sử dụng cmd.exe /c làm tiền tố. Đó là dư thừa và nó làm cho chẩn đoán lỗi khó khăn hơn. GetLastError có thể không phản ánh đúng mã lỗi, bởi vì bạn đang ủy quyền tạo quy trình netsh.exe chính thức cho cmd.

+0

Thật vậy, vấn đề có thể chỉ đơn giản là đường dẫn mã hóa cứng (không tồn tại trên máy tính đích). –

+0

@TOndrej - đường dẫn tồn tại vì cùng một lệnh hoạt động trên Command Prompt. Tôi cũng đảm bảo rằng C: \ Windows \ system32 \ netsh.exe tồn tại bằng Windows Explorer và thậm chí đã gán tất cả các quyền cho 'Everyone' cho netsh.exe. Ngoài ra, cùng một ứng dụng hoạt động trên XP. – Joshua

+0

Dấu nhắc lệnh (thường) chạy trong tài khoản tương tác của bạn. Dịch vụ của bạn có thể đang chạy trong tài khoản bị hạn chế hơn. Thông thường, tài khoản "dịch vụ cục bộ" không có quyền truy cập vào mạng. –

0

Lỗi "không thể tìm thấy tệp được chỉ định" cũng có thể xảy ra nếu một DLL được tải ngầm được yêu cầu bởi tệp thực thi không có sẵn. Trong tình huống này, đó là nguyên nhân có khả năng nhất - một số DLL cần thiết không được tìm thấy khi netsh.exe đang được chạy trong một ngữ cảnh không tương tác.

Sử dụng Trình theo dõi quy trình (có sẵn để tải xuống từ trang web của Microsoft) để ghi lại các hoạt động hệ thống tệp đang diễn ra trong khi thử. Tìm tệp không tìm thấy lỗi trong ngữ cảnh của quá trình dịch vụ của bạn hoặc trong ngữ cảnh của quá trình netsh.exe.

+0

@ Harry - cảm ơn, hãy để tôi kiểm tra. Bằng cách này, bạn có biết lý do có thể là tại sao DLL không được tìm thấy khi chạy trong ngữ cảnh không tương tác? Tôi nhớ đã gặp phải điều này trên một hệ thống khác trong khi viết mã, đó là lý do tại sao tôi đặt thư mục hiện tại của CreateProcess thành 'c: \ windows \ system32 \ wbem'. Trong khi đó, tôi cũng sẽ cần kiểm tra mà không cần cmd.exe/c – Joshua

+0

@Joshua, tôi không chắc tại sao điều này lại xảy ra, hy vọng nó sẽ rõ ràng hơn khi bạn xác định DLL đang được đề cập (giả sử đây là nguyên nhân của vấn đề của bạn). Nó có thể là vì lý do nào đó biến môi trường PATH không được thiết lập đúng cho tiến trình con của bạn. –

+0

@Joshua, hãy nghĩ về nó, nó có thể đáng để chạy "cmd/c path" như một tiến trình con của dịch vụ của bạn và xem đầu ra là gì. –

Các vấn đề liên quan