Delphi: Open a file from another computer - security

my names's Carlos Im from Brazil.
Im trying to open a file like this:
image1.picture.loadfromfile('\\ntmemo01\c$\ozzy2.bmp');
but it doesnt work. Im receving the exception
class EFOpenError with message "Cannot open file '\ntmemo01\c$\ozzy2.bmp' Access denied."
Thanks,
Carlos

You have to use double backslashes.
image1.picture.loadfromfile('\\ntmemo01\c$\ozzy2.bmp');
If you still get the exception, then the file is inaccessible from your application.
The first thing you should do is making sure, that you can access the file using the Windows Explorer.
Just type it into the Run dialog of the start menu (WinKey+R) and see what happens. If it doesn't work, make it work there first and then go back to your program.

Is the C: drive on ntmemo01 shared? If it's not shared, you can't access it. If it's shared but requires a user name and password to access, you'll have to access it differently. You can map a drive letter to it, providing a user name and password in the process:
const
RemoteName = '\\ntmemo01\C$';
UserName = 'yourusername';
Password = 'yourpassword';
function MapNetworkDrive: Boolean;
var
NetRes: TNetResource;
Res: DWord;
begin
Result := True;
FillChar(NetRes, SizeOf(TNetResource), 0);
NetRes.dwType := RESOURCETYPE_DISK;
NetRes.lpRemoteName := PChar(RemoteName);
NetRes.lpLocalName := 'H:'; // Whatever drive letter you want
Res := WNetAddConnection2(NetRes, PChar(Password), PChar(UserName), 0);
Result := (Res = NO_ERROR);
end;
To unmap afterwards:
function UnMapNetworkDrive: Boolean;
var
Res: DWord;
begin
Res := WNetCancelConnection2(PChar('H:'), 0, True); // same drive letter as above
Result := (Res + NO_ERROR);
end;

As mentioned by DR, the filename requires double backslashes for a UNC path
The access denied message suggests the you do not have permission to access the C$ share on the ntmemo01 computer.
C$ a is hidden administrative share are you sure the current user account has the correct permissions? You make have to the map a drive first, as suggested by Ken White
Administrive shares are disabled by default in Windows Vista and Windows 7, unless you join a domain. You can be enable them manually as follows.
Click on the start button and in the search box type ‘regedit’ and hit enter.
Browse to HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System.
Add a new DWORD called LocalAccountTokenFilterPolicy and give it a value of 1.
Reboot and yer done!
Source: http://www.paulspoerry.com/2007/05/09/how-to-access-administrative-shares-on-vista-c/

when you copy this exact same string in windows explorer, is the file opened?
Otherwise it might be a rights problem, as suggested by the error.

Related

I'm trying to do a resource update on the icons of a Windows executable and I'm nearly there, what am I doing wrong?

Here's a fresh version of my code. It is now even closer, if I look at the updated version in Resource Hacker it tells me that the group has nine icons, which is true, that they're 16.8 million color, which is true, that they're all 16 x 16, which is not true, and that it can't actually show me what they look like, which is annoying. Also that they all have an ordinal name of 150 if that means anything to anyone.
procedure TForm1.Button1Click(Sender: TObject);
var vResHandle: THandle;
MyIcon: TMemoryStream;
begin
// Get the icon.
MyIcon := TMemoryStream.Create;
MyIcon.LoadFromFile('icon.ico');
// Set the position in the memory stream to the start.
MyIcon.Seek(0, soFromBeginning);
// Get the handle.
vResHandle := BeginUpdateResource('exec.exe', False);
if vResHandle=0 then
raise Exception.Create('System giving error message: '
+ SysErrorMessage(GetLastError));
try
// Change the icon.
if not UpdateResource(vResHandle
, RT_GROUP_ICON
, PChar('MAINICON')
, LANG_NEUTRAL
, MyIcon.Memory
, MyIcon.Size)
then
raise Exception.Create('System giving error message: '
+ SysErrorMessage(GetLastError));
finally
EndUpdateResource(vResHandle, False);
end;
MyIcon.Free;
end;
Short version of how it works: So. Before you try to put any bit of data into an .exe file using a resource update you must be sure it will fit. Icon files are difficult. In this particular case I needed to modify the structure of the .ico file and split it into different pieces and do a resource update separately on each. I didn't do that. I was like someone trying to fit a seven-fingered hand into one finger of a five-fingered glove.
How the thing works is explained in the code but what exact effect it has on Windows must be explained up here.
(1) Although the application icon (in the top left corner of your main form) can be set to be completely different from the main icon for the program, it seems like it's overwritten to be in line with the main icon once you do the update. 99% of the time this would be exactly what you want. If it isn't what you want you'll have to take it from here.
(2) File Explorer caches this stuff so hard that you won't see any change in how the icon's displayed there unless you restart Explorer. This is fine for my purposes, if it's a problem for you then again you'll have to solve it yourself, sorry.
(3) This is NOT an answer to that frequently-asked question, "How do I change the toolbar icon of my Pascal-based application while it's running?" Because you can't do a resource update on an executable that's being executed, whether your own or another.
(4) If you're going to use this in Pascal, then you're going to need to add Windows to your uses statement. If you're going to use this in any language in other than Pascal but you're still using Windows, then it will translate kind of easily because it's basically telling the Windows OS to do stuff, but you'll have to find out which library or whatever lets you do that and what syntax it wants you to use.
(5) If you're wondering about how to do the thing the other way round and extract an .ico file from an executable file, then this is of course theoretically possible and has been done by cleverer people then me and indeed done in Pascal. (Download Resource Hacker for an example.) But you can't just do this by reversing my code as it were, there are obstacles in your way. Doing it this way Windows has built in facilities for me to do this. Doing it the other way it seems like it doesn't.
procedure TForm1.Button1Click(Sender: TObject);
var vResHandle: THandle;
MyIcon: TMemoryStream;
i,j: integer;
s: string;
ImageCount: Word;
ImageSize: DWord;
ab, m: TMemoryStream;
const HeaderSize = 6;
IcoEntrySize = 16;
ResEntrySize = 14;
begin
// Short explanation. An icon file consists of (a) a six-byte header which
// includes among other things information about how many icons are in
// the file; (b) sixteen bytes of metadata for each icon; (c) the icons.
// But that's how icons are stored as files. As executable resources,
// Windows considers that (a) and (b) are one resource but (c) is a different
// resource, indeed one resource for each image, so we have to split the icon
// file up and do several resource updates.
// It also requires only fourteen bytes of metadata per entry: instead of the
// last parameter being a double word referring to the position of the image
// in memory, it's a single word conferring an ID.
// Initialize stuff
MyIcon := TMemoryStream.Create;
ab := TMemoryStream.Create;
m := TMemoryStream.Create;
// Get the icon
MyIcon.LoadFromFile('icon.ico');
// Get the handle for the resource update..
vResHandle := BeginUpdateResource('test.exe', False);
// We skip forward in the memory stream to where Windows keeps the image count and read it.
MyIcon.Seek(4,soFromBeginning);
ImageCount:=MyIcon.ReadWord;
// Go back to the beginning ...
MyIcon.Seek(0,soFromBeginning);
// We read the directory information into ab, modifying its format as we do so.
for j:=1 to HeaderSize do ab.WriteByte(MyIcon.ReadByte);
for i:=1 to ImageCount do
begin
for j:=1 to IcoEntrySize - 4 do ab.WriteByte(MyIcon.ReadByte);
MyIcon.ReadDWord; // To skip over it.
ab.WriteWord(i);
end;
// Update the icon directory with ab, which is now in the correct format.
UpdateResource(vResHandle
, RT_GROUP_ICON
, PChar('MAINICON')
, LANG_NEUTRAL
, ab.Memory
, ab.Size);
// Now the size of each icon is contained as a double word in the directory
// entries for each item, so we use that to cut the remainder of the memory
// stream into chunks and update them one at a time.
for i := 1 to ImageCount do
begin
m := TMemoryStream.Create;
ab.Seek(HeaderSize+(i-1)*ResEntrySize+8,soFromBeginning);
ImageSize:=ab.ReadDWord;
for j:=1 to ImageSize do m.WriteByte(MyIcon.ReadByte);
UpdateResource(vResHandle
, RT_ICON
, MAKEINTRESOURCE(i)
, LANG_NEUTRAL
, m.Memory
, m.Size);
m.Free;
end;
EndUpDateResource(vResHandle,False);
MyIcon.Free;
ab.Free;
end;

How to create Start menu items for all users when running installer with PrivilegesRequired=lowest as Administrator

Inno Setup documentation says the following:
{group}
The path to the Start Menu folder, as selected by the user on Setup's Select Start Menu Folder wizard page. This folder is created under the All Users profile unless the user installing the application does not have administrative privileges, in which case it is created in the user's profile.*
When I use PrivilegesRequired=admin (i.e. the default), Start menu items are created for all users in C:\ProgramData\Microsoft\Windows\Start Menu\Programs.
When I use PrivilegesRequired=lowest, but run setup with right-click run-as-administrator, Start menu items are created for just the admin user in C:\Users\admin-user\AppData\Roaming\Microsoft\Windows\Start Menu\Programs. This happens even if the current user is an admin.
How can I make start menu items be for all users when using right-click elevation? In this situation, Setup installs program files for all users in C:\Program Files\. So I would like start menu items also to be for all users.
Use a scripted constant to dynamically change Start menu root path, based on an elevation status of the installer.
You have to use WinAPI (e.g. SHGetFolderPath) to retrieve the path to common Start menu folder, as {commonprograms} actually return {userprograms}, if PrivilegesRequired=lowest, even when the installer is actually running elevated.
[Icons]
Name: "{code:GetMenuRootPath}\{groupname}\My Program"; Filename: "{app}\MyProg.exe"
[Code]
const
CSIDL_COMMON_PROGRAMS = $0017;
SHGFP_TYPE_CURRENT = 0;
MAX_PATH = 260;
S_OK = 0;
function SHGetFolderPath(
hwnd: HWND; csidl: Integer; hToken: THandle; dwFlags: DWORD;
pszPath: string): HResult;
external 'SHGetFolderPathW#shell32.dll stdcall';
function GetMenuRootPath(Param: string): string;
var
R, I: Integer;
begin
if IsAdminLoggedOn then
begin
SetLength(Result, MAX_PATH);
R := SHGetFolderPath(0, CSIDL_COMMON_PROGRAMS, 0, SHGFP_TYPE_CURRENT, Result);
if R <> S_OK then
begin
Log('Failed to resolve path to common Start menu folder');
end
else
begin
SetLength(Result, Pos(#0, Result) - 1);
Log(Format('Resolved path to common Start menu folder: %s', [Result]));
end;
end
else
begin
Result := ExpandConstant('{userprograms}');
Log(Format('Using user''s Start menu folder: %s', [Result]))
end;
end;
The code is for Unicode version of Inno Setup (the only version as of Inno Setup 6).
Though note that using groups in Start menu is against Windows guidelines for Windows 8 and newer.
You can use the (deprecated and now undocumented) value PrivilegesRequired=none to make Inno adapt to whether it is being run with or without admin privileges, including redirecting the Start Menu entries accordingly.
However the reason that this setting is deprecated is because the entire concept is a little silly. Most applications should be designed to require admin privileges to install (since typically people who are non-admins are not supposed to install software).
If you do end up with an application installed both as an admin and as a regular user then you will have cases where certain users will see two copies of the application installed and will not know which one to use. Furthermore the admin might upgrade the one they installed thinking that all users will see it, but meanwhile some users are still using the older version.
I strongly encourage you to abandon the idea of letting unprivileged users install your application and just stick with PrivilegesRequired=admin, or if you really want to allow that, then PrivilegesRequired=lowest.
If you do use PrivilegesRequired=lowest, then also use {userpf} in your DefaultDirName so that it doesn't install to Program Files if someone erroneously runs it as an admin.

Component Exist Message Box too Big - InnoSetup

I have a component list of more than 80 options,where user can select and then install.
The setup remembers the previous install components and automatically checks the Components, Now if user deselects all, the "Component Exist" Warning Message Box is shown.
Because user deselected all 80 options the list becomes to long and the Message box goes out of screen space and user is now stuck.
I know there is NoUninstallWarning in Messages which has the text for the warning message and takes one argument as %1
Is there a way I can change the argument value , rather than having all options listed in indiviual line , I would like to have them as comma separated?
Or if I can have a Scrollbar in the Message box?
Please help
No, this message is internal and you can't customise it like that without modifying Inno's own source code.
In that situation the user shouldn't be completely stuck -- they should be able to press ESC to return to the component selection window and then select everything again.
A simple way to avoid this problem is to not allow the user to deselect components, once installed. You can do this with a bit of code like this:
var
InstalledComponentsDisabled: Boolean;
procedure CurPageChanged(CurPageId: Integer);
var
i: Integer;
begin
if (CurPageId = wpSelectComponents) and
(WizardForm.PrevAppDir <> '') and
not InstalledComponentsDisabled then begin
InstalledComponentsDisabled := True;
for i := 0 to WizardForm.ComponentsList.Items.Count - 1 do begin
if WizardForm.ComponentsList.Checked[i] then begin
WizardForm.ComponentsList.ItemEnabled[i] := False;
end;
end;
end;
end;
This has a similar effect to making anything already installed on upgrades fixed.
An alternate option is to put disablenouninstallwarning on all of your components and then either implement the messagebox completely yourself, add a bit of static text warning about removing components permanently on the page, or even do something to actually support removing components (eg. [InstallDelete] entries or UninsHs).

How to show ok button only in message box when there is not enough space to install application [duplicate]

I have built an installer to install application using Inno Setup. But I want to display an error message showing that there is not enough space in the drive or path, where I am going to install application, if there is no space available.
By default I am getting Inno built in ability to show message when there is no space available in the hard disk or selected path. But it shows YES and NO button to continue or cancel. Here I want to show error message with a OK button and when the user clicks ok button it should stop installation. Please help me on this issue. I could not find any ways to do so.
To determine a free space on a drive of a specific folder (in your case the selected directory), you can call the GetSpaceOnDisk or GetSpaceOnDisk64 function. The difference between them is that the first one is able to return space info in bytes as well as in megabytes. The latter returns this info just in bytes. For the following example I chose the first mentioned function, so you can decide in which units you want to operate by modifying a single boolean parameter:
[Code]
procedure ExitProcess(uExitCode: UINT);
external 'ExitProcess#kernel32.dll stdcall';
function IsEnoughFreeSpace(const Path: string; MinSpace: Cardinal): Boolean;
var
FreeSpace, TotalSpace: Cardinal;
begin
// the second parameter set to True means that the function operates with
// megabyte units; if you set it to False, it will operate with bytes; by
// the chosen units you must reflect the value of the MinSpace paremeter
if GetSpaceOnDisk(Path, True, FreeSpace, TotalSpace) then
Result := FreeSpace >= MinSpace
else
RaiseException('Failed to check free space.');
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
if CurPageID = wpSelectDir then
begin
// the second parameter in this function call is the expected min. space in
// units specified by the commented parameter above; in this example we are
// checking if there's at least 1 MB of free space on drive of the selected
// directory; we need to extract a drive portion of the selected directory,
// because it's probable that the directory won't exist yet when we check
if not IsEnoughFreeSpace(ExtractFileDrive(WizardDirValue), 1) then
begin
MsgBox('There is not enough space on drive of the selected directory. ' +
'Setup will now exit.', mbCriticalError, MB_OK);
// in this input parameter you can pass your own exit code which can have
// some meaningful value indicating that the setup process exited because
// of the not enough space reason
ExitProcess(666);
end;
end;
end;
Maybe my answer looks like off-topic.
I had more or less the same problem.
If you have in the files section a check function made by yourself, setup can only count the number of (Mega)bytes of those files which have "normal" check flags.
A way to avoid this is count-up the bytes by yourself and put the result in the ExtraDiskSpaceRequired directive in the [setup] section

How to check whether required space is available in the hard disk to install application using Inno Setup installer

I have built an installer to install application using Inno Setup. But I want to display an error message showing that there is not enough space in the drive or path, where I am going to install application, if there is no space available.
By default I am getting Inno built in ability to show message when there is no space available in the hard disk or selected path. But it shows YES and NO button to continue or cancel. Here I want to show error message with a OK button and when the user clicks ok button it should stop installation. Please help me on this issue. I could not find any ways to do so.
To determine a free space on a drive of a specific folder (in your case the selected directory), you can call the GetSpaceOnDisk or GetSpaceOnDisk64 function. The difference between them is that the first one is able to return space info in bytes as well as in megabytes. The latter returns this info just in bytes. For the following example I chose the first mentioned function, so you can decide in which units you want to operate by modifying a single boolean parameter:
[Code]
procedure ExitProcess(uExitCode: UINT);
external 'ExitProcess#kernel32.dll stdcall';
function IsEnoughFreeSpace(const Path: string; MinSpace: Cardinal): Boolean;
var
FreeSpace, TotalSpace: Cardinal;
begin
// the second parameter set to True means that the function operates with
// megabyte units; if you set it to False, it will operate with bytes; by
// the chosen units you must reflect the value of the MinSpace paremeter
if GetSpaceOnDisk(Path, True, FreeSpace, TotalSpace) then
Result := FreeSpace >= MinSpace
else
RaiseException('Failed to check free space.');
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
if CurPageID = wpSelectDir then
begin
// the second parameter in this function call is the expected min. space in
// units specified by the commented parameter above; in this example we are
// checking if there's at least 1 MB of free space on drive of the selected
// directory; we need to extract a drive portion of the selected directory,
// because it's probable that the directory won't exist yet when we check
if not IsEnoughFreeSpace(ExtractFileDrive(WizardDirValue), 1) then
begin
MsgBox('There is not enough space on drive of the selected directory. ' +
'Setup will now exit.', mbCriticalError, MB_OK);
// in this input parameter you can pass your own exit code which can have
// some meaningful value indicating that the setup process exited because
// of the not enough space reason
ExitProcess(666);
end;
end;
end;
Maybe my answer looks like off-topic.
I had more or less the same problem.
If you have in the files section a check function made by yourself, setup can only count the number of (Mega)bytes of those files which have "normal" check flags.
A way to avoid this is count-up the bytes by yourself and put the result in the ExtraDiskSpaceRequired directive in the [setup] section

Resources