I understand how to retrieve the UNC path for a mapped drive from the registry (HKEY_CURRENT_USER\Network), but I also have a need to retrieve remote connections to network resources that were not mapped.
For example, opening the 'Run' dialog and typing <\server0123\share$>. If I type "net use", I would see this mapping, but I have been unable to determine where on the file system or registry this information is stored.
alt text http://www.freeimagehosting.net/uploads/5bf1a0e3c5.jpg
Does anyone know have a location I can query this from, or an API I can call to obtain this? Suggestions involving vbscript, C, and Delphi are more than welcome!
Mick, try using the Win32_NetworkConnection WMI Class
check this sample
program GetWMI_Win32_NetworkConnection;
{$APPTYPE CONSOLE}
uses
SysUtils
,ActiveX
,ComObj
,Variants;
Procedure GetWin32_NetworkConnection;
var
objWMIService : OLEVariant;
colItems : OLEVariant;
colItem : OLEVariant;
oEnum : IEnumvariant;
iValue : LongWord;
function GetWMIObject(const objectName: String): IDispatch;
var
chEaten: Integer;
BindCtx: IBindCtx;
Moniker: IMoniker;
begin
OleCheck(CreateBindCtx(0, bindCtx));
OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;
begin
objWMIService := GetWMIObject('winmgmts:\\localhost\root\cimv2');
colItems := objWMIService.ExecQuery('SELECT * FROM Win32_NetworkConnection','WQL',0);
oEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
while oEnum.Next(1, colItem, iValue) = 0 do
begin
Writeln('Caption '+colItem.Caption);
Writeln('Name '+colItem.Name);
Writeln('ConnectionState'+colItem.ConnectionState);
Writeln('ConnectionType '+colItem.ConnectionType);
Writeln('Description '+colItem.Description);
Writeln('DisplayType '+colItem.DisplayType);
Writeln('LocalName '+colItem.LocalName);
Writeln('ProviderName '+colItem.ProviderName);
Writeln('RemoteName '+colItem.RemoteName);
Writeln('RemotePath '+colItem.RemotePath);
Writeln('ResourceType '+colItem.ResourceType);
Writeln('Status '+colItem.Status);
Writeln('UserName '+colItem.UserName);
Writeln;
end;
end;
begin
try
CoInitialize(nil);
try
GetWin32_NetworkConnection;
Readln;
finally
CoUninitialize;
end;
except
on E:Exception do
Begin
Writeln(E.Classname, ': ', E.Message);
Readln;
End;
end;
end.
WNetOpenEnum(RESOURCE_REMEMBERED,...)
(If you need to support Win9x, you probably have to fall back to NetUseEnum)
Related
I've been trying to make an installer by Inno Setup which only supports zip/bzip/lzma/lzma2 compression methods. I packed my archive by FreeArc (output file extension is .arc but renamed it to .bin) but Inno Setup is not able to extract it. I searched on internet how to implant arc decompression into Inno Setup but all sites refer to FreeArc official website which is dead for a while.
All I need is the code to use the necessary dll files to give Inno Setup the ability to decompress arc archives plus the list of those dll files needed to do so.
I appreciate any help.
This answer has been superseded by Inno Setup - How to add cancel button to decompressing page? that uses unarc.dll instead of driving the console Arc.exe.
I'm keeping this answer, as its concept can be useful for other archive types.
See the example below. It:
takes an ARC file, embeds it to the installer
during installation, the ARC file is extracted to a temporary folder
the files from the ARC file is extracted to the target folder
#define ArcArchive "test.arc"
[Files]
Source: {#ArcArchive}; DestDir: "{tmp}"; Flags: nocompression deleteafterinstall
Source: Arc.exe; Flags: dontcopy
[Code]
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); // high byte
Result[(I * 2) - 1] := Chr(Byte(W)); // low byte
end;
end;
function SetTimer(
Wnd: LongWord; IDEvent, Elapse: LongWord; TimerFunc: LongWord): LongWord;
external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: LongWord; uIDEvent: LongWord): BOOL;
external 'KillTimer#user32.dll stdcall';
var
ProgressPage: TOutputProgressWizardPage;
ProgressFileName: string;
procedure UpdateProgressProc(
H: LongWord; Msg: LongWord; Event: LongWord; Time: LongWord);
var
S: AnsiString;
L: Integer;
P: Integer;
Max: Integer;
Progress: string;
Buffer: string;
Stream: TFileStream;
Percent: Integer;
Found: Boolean;
begin
Found := False;
if not FileExists(ProgressFileName) then
begin
Log(Format('Progress file %s does not exist', [ProgressFileName]));
end
else
begin
try
// Need shared read as the output file is locked for writting,
// so we cannot use LoadStringFromFile
Stream :=
TFileStream.Create(ProgressFileName, fmOpenRead or fmShareDenyNone);
try
L := Stream.Size;
Max := 100*2014;
if L > Max then
begin
Stream.Position := L - Max;
L := Max;
end;
SetLength(Buffer, (L div 2) + (L mod 2));
Stream.ReadBuffer(Buffer, L);
S := BufferToAnsi(Buffer);
finally
Stream.Free;
end;
if S = '' then
begin
Log(Format('Progress file %s is empty', [ProgressFileName]));
end;
except
Log(Format('Failed to read progress from file %s - %s', [
ProgressFileName, GetExceptionMessage]));
end;
end;
if S <> '' then
begin
P := Pos('Extracted', S);
if P > 0 then
begin
Log('Extraction done');
Percent := 100;
Found := True;
end
else
begin
P := Pos('%', S);
if P > 0 then
begin
repeat
Progress := Copy(S, 1, P - 1);
Delete(S, 1, P);
P := Pos('%', S);
until (P = 0);
P := Length(Progress);
while (P > 0) and
(((Progress[P] >= '0') and (Progress[P] <= '9')) or
(Progress[P] = '.')) do
begin
Dec(P);
end;
Progress := Copy(Progress, P + 1, Length(Progress) - P);
P := Pos('.', Progress);
if P > 0 then
begin
Progress := Copy(Progress, 1, P - 1);
end;
Percent := StrToInt(Progress);
Log(Format('Percent: %d', [Percent]));
Found := True;
end;
end;
end;
if not Found then
begin
Log('No new data found');
// no new progress data, at least pump the message queue
ProgressPage.SetProgress(ProgressPage.ProgressBar.Position, 100);
end
else
begin
ProgressPage.SetProgress(Percent, 100);
ProgressPage.SetText(Format('Extracted: %d%%', [Percent]), '');
end;
end;
procedure ExtractArc;
var
ArcExtracterPath: string;
ArcArchivePath: string;
TempPath: string;
CommandLine: string;
Timer: LongWord;
ResultCode: Integer;
S: AnsiString;
Message: string;
begin
ExtractTemporaryFile('Arc.exe');
ProgressPage :=
CreateOutputProgressPage('Decompression', 'Decompressing archive...');
ProgressPage.SetProgress(0, 100);
ProgressPage.Show;
try
Timer := SetTimer(0, 0, 250, CreateCallback(#UpdateProgressProc));
TempPath := ExpandConstant('{tmp}');
ArcExtracterPath := TempPath + '\Arc.exe';
ArcArchivePath := TempPath + '\{#ArcArchive}';
ProgressFileName := ExpandConstant('{tmp}\progress.txt');
Log(Format('Expecting progress in %s', [ProgressFileName]));
CommandLine :=
Format('"%s" x -y -o+ -dp"%s" "%s" > "%s"', [
ArcExtracterPath, ExpandConstant('{app}'), ArcArchivePath,
ProgressFileName]);
Log(Format('Executing: %s', [CommandLine]));
CommandLine := Format('/C "%s"', [CommandLine]);
if not Exec(ExpandConstant('{cmd}'), CommandLine, '', SW_HIDE,
ewWaitUntilTerminated, ResultCode) then
begin
RaiseException('Cannot start extracter');
end
else
if ResultCode <> 0 then
begin
LoadStringFromFile(ProgressFileName, S);
Message :=
Format('Arc extraction failed failed with code %d', [ResultCode]);
Log(Message);
Log('Output: ' + S);
RaiseException(Message);
end
else
begin
Log('Arc extraction done');
end;
finally
// Clean up
Log('Arc extraction cleanup');
KillTimer(0, Timer);
ProgressPage.Hide;
DeleteFile(ProgressFileName);
end;
Log('Arc extraction end');
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
ExtractArc;
end;
end;
The code needs arc.exe (I've taken it from PeaZip portable package).
For CreateCallback function, you need Inno Setup 6. If you are stuck with Inno Setup 5, you can use WrapCallback function from InnoTools InnoCallback library (and you need Unicode version of Inno Setup 5).
Alternatively, to avoid double extraction, you can distribute the arc file along the installer.
Just use {src} to resolve its path:
ArcArchivePath := ExpandConstant('{src}\{#ArcArchive}');
And remove the {#ArcArchive} entry from the [Files] section.
It would be more robust to implement the extraction using unarc.dll, like seen in the FreeArc+InnoSetup package ISFreeArcExtract v.4.0.rar.
Freearc Actually Comes with Inno Extraction Example
http://freearc2.azurewebsites.net/InnoSetup.aspx
I'm interfacing fingerprint reader via COM and i need help converting VB.NET and C++ Code to Delphi.
The API takes olevariant as parameter:
Function FingerPrint.GetData(var ImageData : OleVariant) : WordBool;
VB.NET example provided:
Dim imgData() as Byte
ReDim imgData(fingerPrint.ImageSize) as Byte
If fingerPrint.GetData(imgData) = True Then
'Success
End If
C++ example provided:
BYTE* dataBuff = new BYTE[fingerPrint.ImageSize];
VARIANT imgData;
imgData.vt = VT_BYREF|VT_UI1;
imgData.pbVal = dataBuff;
if(fingerPrint.getData(imgData) == TRUE) {
//Success
}
Here's my Delphi Code:
procedure GetImgData();
var varBuffer : OleVariant;
imgBuff : PByteArray;
begin
GetMem(imgBuff, fingerPrint.ImageSize);
try
tagVariant(varBuffer).vt := VT_UI1 or VT_BYREF; // 0x4011
tagVariant(varBuffer).pbVal := Pointer(imgBuff);
if fingerPrint.getData(varBuffer) then
begin
// success
end;
finally
FreeMem(imgBuff);
end;
end;
another approach:
procedure GetImgData();
var varBuffer : OleVariant;
tagV : TVariantArg;
imgBuff : PByteArray;
begin
GetMem(imgBuff, fingerPrint.ImageSize);
try
tagV.vt := VT_UI1 or VT_BYREF; // 0x4011
tagV.pbVal := Pointer(imgBuff);
varBuffer := OleVariant(tagV);
if fingerPrint.getData(varBuffer) then
begin
// success
end;
finally
FreeMem(imgBuff);
end;
end;
getData is not returning true using the parameter i'm sending. Sent my executable to support and told me that API is getting 0x400C (VT_VARIANT or VT_BYREF) instead of 0x4011.
Anything wrong with my Code?
Please Help!
UPDATE:
here's from dispinterface
function GetData(var ImageData: OleVariant): WordBool; dispid 23;
from Component Wrapper
..
function GetData(var ImageData : OleVariant): WordBool;
..
function TFingerPrint.GetData(var ImageData : OleVariant): WordBool;
begin
Result := DefaultInterface.GetData(ImageData);
end;
C++ declaration
BOOL getData(const VARIANT FAR& imgData)
UPDATE 20140313
Our supplier sent new OCX to handle data received from Delphi.
Are you sure it's 0x4011 and not 0x2011? Since varArray = $2000 and VarArrayCreate([0,size-1],varByte) would create an OleVariant with an array of varByte's like the VB code. If that works, use VarArrayLock and VarArrayUnlock to access the data.
I want to change the Caret Symbol(Text Cursor) dynamically in windows and independently of applications(system wide).
i mean this one:
but i don't know if it is possible to make such utility-tool.
only i found in google was to tweak registry to change the Caret Symbol.
but once it is changed in registry, i must restart my computer.
i do not want to restart my computer for changing Caret Symbol.
is it possible to change caret sybol in windows without restart?
It is possible, if you are using delphi.
function GetCaretPosition(var APoint: TPoint): Boolean;
var w: HWND;
aID, mID: DWORD;
begin
Result:= False;
w:= GetForegroundWindow;
if w <> 0 then
begin
aID:= GetWindowThreadProcessId(w, nil);
mID:= GetCurrentThreadid;
if aID <> mID then
begin
if AttachThreadInput(mID, aID, True) then
begin
w:= GetFocus;
if w <> 0 then
begin
Result:= GetCaretPos(APoint);
ClientToScreen(w, APoint);
end;
AttachThreadInput(mID, aID, False);
end;
end;
end;
end;
//Small demo: set cursor to active caret position
procedure TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Clear();
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
Pt: TPoint;
begin
if GetCaretPosition(Pt) then
begin
ListBox1.Items.Add(Format('Caret position %d %d', [Pt.x, Pt.y]));
// SetCursorPos(Pt.X, Pt.Y);
end;
end;
end.
How can I release a Variable that is being used by the WebService.
I'm using this form:
HttpPrincipal.WSDLLocation: = FrmPrincipal.edtWS.Text;
HttpPrincipal.Service: = 'CADServicesService';
HttpPrincipal.Port := 'CADServices';
Trinity: = HttpPrincipal as CADServices;
At the moment when I will close the Form appears an error, and discovered that when I declare this part:
Trinity: = HttpPrincipal as CADServices;
I think it is getting stuck in memory.
The error is the following:
"Invalid Pointer"
The error happens when you close the form, does not have any event in the OnClose or OnDestroy form.
Descriptions:
Trinity : CADServices,
HttpPrincipal is a THTTPRIO,
CADServices is my Unit containing all procedures / functions from WebService.
Instead of using the designtime component try to create HTTPRIO at runtime:
function GetCadServices(Addr : String): CadServices;
const
defSvc = 'CADServicesService';
defPrt = 'CADServices';
var
RIO: THTTPRIO;
begin
Result := nil;
RIO := THTTPRIO.Create(nil)
try
Result := (RIO as CadServices);
RIO.WSDLLocation := Addr;
RIO.Service := defSvc;
RIO.Port := defPrt;
finally
if (Result = nil) then
RIO.Free;
end;
end;
Usage:
Trinity := GetCadServices(FrmPrincipal.edtWS.Text);
If you imported the WSDL with the WSDL importer this code is automatically generated for you (look in the CadServices1 unit)
I'm trying to write some Pascal script for a installer I'm making with Inno Setup Compiler 5.5.1. I'm currently trying to add a custom wizard page that executes a command, taking user input from text fields (TEdit components). I defined the NextButtonClick function, and it checks that the Page ID is the custom page I defined and attempts to retrieve the user input from the field. When I get it from the components of the Page's Surface property, it gets returned as a TComponent. To get the next I need to cast it to a TEdit, so I tried casting it and it seems to be returning nil. Besides the scripting for Inno I've been doing for the past few days, I don't have much experience with Pascal, so I could possibly be doing something wrong. But I'd appreciate the help!
Here's the chunk of code giving me an issue for reference (with debugging lines left in):
function NextButtonClick(CurPageID: Integer): Boolean;
var
ResultCode: Integer;
CurrPage: TWizardPage;
Server : TComponent;
Server2: TEdit;
SurfacePage : TNewNotebookPage;
ServerStr : String;
begin
if CurPageID = 100 then
begin
CurrPage := PageFromID(100);
SurfacePage := CurrPage.Surface;
Server := SurfacePage.Controls[0];
Server2 := TEdit(Server); // RETURNS NIL HERE
if Server2 = nil then
MsgBox('', mbInformation, MB_OK);
ServerStr := Server2.Text;
MsgBox(ServerStr, mbInformation, MB_OK);
//ShellExec('', 'sqlcmd', '-S ' + ServerStr + ' -Q ":r setMemUsage.sql"', ExpandConstant('{app}') + '\sql', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
Result := True;
end;
I can't simulate your problem. I've used this minimalistic code:
[Code]
var
CustomPageID: Integer;
procedure InitializeWizard;
var
EditBox: TEdit;
CustomPage: TWizardPage;
begin
CustomPage := CreateCustomPage(wpWelcome, '', '');
CustomPageID := CustomPage.ID;
EditBox := TEdit.Create(WizardForm);
EditBox.Parent := CustomPage.Surface;
end;
procedure CurPageChanged(CurPageID: Integer);
var
EditBox: TEdit;
Component: TComponent;
CustomPage: TWizardPage;
begin
if (CurPageID = CustomPageID) then
begin
CustomPage := PageFromID(CustomPageID);
Component := CustomPage.Surface.Controls[0];
if (Component is TEdit) then
begin
MsgBox('Controls[0] is assigned and is TEdit', mbInformation, MB_OK);
EditBox := TEdit(Component);
EditBox.Text := 'Hi, I''m just a modified edit text!';
end;
end;
end;