2

I'm working on an Inno Setup installer, which calls net use to connect to a shared server. The installer can connect to the server, if it's running on Windows XP, but not on Windows 7. I think it's related to UAC as I type the same command, the server is connected on Windows 7, but the setup is running with admin privileges.

I'm using the following net use command through Exec or ShellExec script functions:

/c net use \\servername password /user:username

Actually, here is a part of the script showing the net use command call:

[Code] 
var 
  ErrorCode: Integer; 
  cmdString: String; 
  intvalue: Integer; 
  str: String; 

function InitializeSetup(): Boolean; 
begin 
  cmdString := '/c net use \\servername password /USER:username'; 
  ShellExec('', ExpandConstant('{cmd}'), cmdString , '', SW_SHOWNORMAL, 
    ewWaitUntilTerminated, ErrorCode) 
  if (ErrorCode = 0) then 
  begin 
    MsgBox(ExpandConstant('{cmd}'), mbInformation, MB_OK); 
  end; 
end;

Can anybody suggest how to use net use from Inno Setup on Windows 7? We just want to connect to a server and let user input name and password.

Thank you!

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
xiaoheixiaojie
  • 145
  • 1
  • 2
  • 8
  • I have tried `net use` for mapping a network share (without password though), but it worked. Truth is that I've called directly `net.exe` using `Exec`, not through the command line (maybe the command line even returns mazy result, don't know, but I'll try). In the meantime I'll post you my testing code. For the others, the updates I did to this question are from comment discussion that was quite long, so I've asked OP to cleanup the *workspace*. – TLama Oct 23 '12 at 08:33
  • 1
    While `net.exe` is convenient, it's always better to use the API directly (in this case, `WNetUseConnection` as in the given answer). Sometimes the command will stop and wait for input unexpectedly, which can cause real problems if you're trying to run it hidden (and if you're not running it hidden, you get an ugly console window). It's designed for use by admins from the console, after all, not from software -- that's what APIs are for. – Miral Oct 24 '12 at 19:53
  • Hi Miral, we are having problem on Windows 7 with net.exe, it seems net command does not actually get executed while the return value shows it's ok. I'm wondering if this is an inno bug, or I missed something. – xiaoheixiaojie Oct 25 '12 at 06:49
  • 1
    Remember that the setup (when elevated) runs as a different user or executaion level as the rest of the desktop. Connections won't be preserved as you expect. – Deanna Oct 29 '12 at 11:53
  • Hi Deanna, thank you for your reply which helps me to see the cause of the problem. – xiaoheixiaojie Oct 31 '12 at 07:35
  • @xiaoheixiaojie, I wouldn't call it a problem. I've had no problem running the code from my post with admin privileges, but it might be the reason. – TLama Oct 31 '12 at 16:15

1 Answers1

5

How to connect to a remote resource invoking the credentials dialog?

Using a different view on your question, which is actually as the title of this answer says, I'd suggest you to use the WNetUseConnection function call with CONNECT_INTERACTIVE and CONNECT_PROMPT flags. That will in combination with empty user ID and password parameters invoke the credentials dialog (and that's what you wanted). In Inno Setup script it may look like this:

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif
const
  NO_ERROR = 0;
  ERROR_ACCESS_DENIED = 5;
  ERROR_BAD_NET_NAME = 67;
  ERROR_ALREADY_ASSIGNED = 85;
  ERROR_INVALID_PASSWORD = 86;
  ERROR_INVALID_PARAMETER = 87;
  ERROR_MORE_DATA = 234;
  ERROR_NO_MORE_ITEMS = 259;
  ERROR_INVALID_ADDRESS = 487;
  ERROR_BAD_DEVICE = 1200;
  ERROR_NO_NET_OR_BAD_PATH = 1203;
  ERROR_BAD_PROVIDER = 1204;
  ERROR_EXTENDED_ERROR = 1208;
  ERROR_NO_NETWORK = 1222;
  ERROR_CANCELLED = 1223;
  RESOURCETYPE_ANY = $00000000;
  RESOURCETYPE_DISK = $00000001;
  RESOURCETYPE_PRINT = $00000002;
  CONNECT_UPDATE_PROFILE = $00000001;
  CONNECT_INTERACTIVE = $00000008;
  CONNECT_PROMPT = $00000010;
  CONNECT_REDIRECT = $00000080;
  CONNECT_COMMANDLINE = $00000800;
  CONNECT_CMD_SAVECRED = $00001000;
type
  TNetResource = record
    dwScope: DWORD;
    dwType: DWORD;
    dwDisplayType: DWORD;
    dwUsage: DWORD;
    lpLocalName: string;
    lpRemoteName: string;
    lpComment: string;
    lpProvider: string;
  end;
  TResourceType = (
    rtAny,
    rtDisk,
    rtPrinter
  );

function WNetUseConnection(hwndOwner: HWND; const lpNetResource: TNetResource;
  lpPassword, lpUserID: string; dwFlags: DWORD; lpAccessName: PAnsiChar;
  var lpBufferSize, lpResult: DWORD): DWORD;
  external 'WNetUseConnection{#AW}@mpr.dll stdcall';

function UseConnection(const ARemoteName: string; 
  AResourceType: TResourceType): DWORD;
var
  BufferSize: DWORD;
  ResultFlag: DWORD;
  NetResource: TNetResource;
begin
  case AResourceType of
    rtAny: NetResource.dwType := RESOURCETYPE_ANY;
    rtDisk: NetResource.dwType := RESOURCETYPE_DISK;
    rtPrinter: NetResource.dwType := RESOURCETYPE_PRINT;
  end;
  NetResource.lpLocalName := '';
  NetResource.lpRemoteName := ARemoteName;
  NetResource.lpProvider := '';  
  BufferSize := 0;
  Result := WNetUseConnection(WizardForm.Handle, NetResource,
    '', '', CONNECT_INTERACTIVE or CONNECT_PROMPT, '',
    BufferSize, ResultFlag); 
end;

procedure UseConnectionButtonClick(Sender: TObject);
var
  S: string;
  ResultCode: DWORD;
begin
  ResultCode := UseConnection('\\MySuperSecret\Place', rtDisk);
  case ResultCode of
    NO_ERROR: S := 'NO_ERROR';
    ERROR_ACCESS_DENIED: S := 'ERROR_ACCESS_DENIED';
    ERROR_ALREADY_ASSIGNED: S := 'ERROR_ALREADY_ASSIGNED';
    ERROR_BAD_DEVICE: S := 'ERROR_BAD_DEVICE';
    ERROR_BAD_NET_NAME: S := 'ERROR_BAD_NET_NAME';
    ERROR_BAD_PROVIDER: S := 'ERROR_BAD_PROVIDER';
    ERROR_CANCELLED: S := 'ERROR_CANCELLED';
    ERROR_EXTENDED_ERROR: S := 'ERROR_EXTENDED_ERROR';
    ERROR_INVALID_ADDRESS: S := 'ERROR_INVALID_ADDRESS';
    ERROR_INVALID_PARAMETER: S := 'ERROR_INVALID_PARAMETER';
    ERROR_MORE_DATA: S := 'ERROR_MORE_DATA';
    ERROR_INVALID_PASSWORD: S := 'ERROR_INVALID_PASSWORD';
    ERROR_NO_MORE_ITEMS: S := 'ERROR_NO_MORE_ITEMS';
    ERROR_NO_NET_OR_BAD_PATH: S := 'ERROR_NO_NET_OR_BAD_PATH';
    ERROR_NO_NETWORK: S := 'ERROR_NO_NETWORK';
  end;
  MsgBox(S, mbInformation, MB_OK);
end;

procedure InitializeWizard;
var
  UseConnectionButton: TNewButton;
begin
  UseConnectionButton := TNewButton.Create(WizardForm);
  UseConnectionButton.Parent := WizardForm;
  UseConnectionButton.Left := 8;
  UseConnectionButton.Top := WizardForm.ClientHeight - UseConnectionButton.Height - 8;
  UseConnectionButton.Width := 155;
  UseConnectionButton.Caption := 'Use connection...';
  UseConnectionButton.OnClick := @UseConnectionButtonClick;
end;
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
TLama
  • 75,147
  • 17
  • 214
  • 392
  • Hi TLama, Thank you for your code. I tried it but it does not work here. My net server requires user to input user name and password, on Windows 7, after install, when users try to connect the server, a window pops up ask user to input name and password. Another difference is I do not map a drive just connect to the server, I also tried to map a driver but did not work either. Any thoughts? – xiaoheixiaojie Oct 24 '12 at 01:48
  • That's what I've expected. I think I found the way, the [`WNetUseConnection`](http://msdn.microsoft.com/en-us/library/aa385482%28VS.85%29.aspx) function. In certain circumstances it invokes the credentials dialog. I'll try to prepare you an example. I'll be right back with an update ;-) But it's more the answer to a question like *How to connect to a network resource displaying the credentials dialog ?* – TLama Oct 24 '12 at 07:41
  • I've posted the promised script code, which I've tested on Windows 7 and Windows XP Mode. – TLama Oct 24 '12 at 10:04
  • Hi TLama, the script in your post is very impressive. I tried it and it works on my xp, but not on my Windoes 7. I input the user credential but the server is not mounted, and it the result code of useconnection 1219. On XP, the server is mounted but there is access error about setup.exe. Maybe net.exe is more appropriate? – xiaoheixiaojie Oct 25 '12 at 06:32
  • The [`error code 1219`](http://msdn.microsoft.com/en-us/library/windows/desktop/ms681383(v=vs.85).aspx#ERROR_SESSION_CREDENTIAL_CONFLICT) means *Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.*, aren't you already connected to that resource (but I'm really llame on networking) ? And, I don't think, why would the net.exe be *more appropriate*. This is the way MSDN and developers suggest. – TLama Oct 25 '12 at 07:20
  • I restarted my machine, now there is no error code, but on Windows 7, the server is still not mounted. There is an error: access violation at ... in module 'setup.tmp'. read of address... Could you please provide a setup file or a .iss file? It seems I did not create it correctly. – xiaoheixiaojie Oct 25 '12 at 09:04
  • Sorry I did not update in time, our software is about to release, and there are some issues I have to deal with. so I may do not look responsive. :< also sorry for my English. – xiaoheixiaojie Oct 25 '12 at 09:12
  • I've updated the post with the fix, which passes empty string in the NULL meaning to the `lpAccessName` parameter of the function (this is due to a missing support for NULL value in InnoSetup scripting). That removes the `Out of stack range` error (with subsequent access violation failure) that occured while receiving a value to this parameter for some network places (strange is, that it happened, even when you properly allocated buffer for the function call, what was not in previous revision of this script, but what I've tested with ANSI and Unicode versions of the function and InnoSetup). – TLama Oct 29 '12 at 11:47
  • I'll try it later. Thank you! As Deanna said in the reply to this post, the issue maybe caused by the user of the installer. I'm thinking 'net use' is indeed improper. – xiaoheixiaojie Oct 31 '12 at 07:33
  • It's awesome! the code also works on my win7. Earlier I also tried to export a method that call WNetUseConnection. Your update fixed my problem.Thank you! Could you please advice on how to get info of inno setup? I found it's hard to find info about advance topics about inno on the web. Where can I find the defination of 'type record' ? – xiaoheixiaojie Nov 01 '12 at 09:11
  • You're welcome! To your question, InnoSetup is based on the [`PascalScript`](http://www.remobjects.com/ps.aspx) engine and `record` type as you mentioned comes from there, but there seems to be no (online) documentation. But even if there would, I don't think if will be helpful in all cases since you as a script consumer (of InnoSetup) need to consider that authors can drop some language features that the engine offers (just like they did for instance with `class` type, so it's [`difficult to answer`](http://stackoverflow.com/q/1743367/960757). – TLama Nov 01 '12 at 09:26