Delphi COM Passing ByteArray as OleVariant - c++

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.

Related

Use of Cocoa in C++ like in Delphi

There is a pattern in Delphi to work with Cocoa classes. A blog about this pattern is here:
Using OS X APIs directly from Delphi
I wonder, how it works and whether it can be done with xcode in C++ too.
As example I take the function "InternalGetMACOSPath" from System.IOUtils.pas. I put excerpts of the involved code at the end of this post. The line of code that I am interested in is:
nsFile := TNSFileManager.Wrap(TNSFileManager.OCClass.defaultManager);
In essence the following would be an equivalent in C++:
NSFileManagerClass* pFManagerClass;
NSFileManager* pFManager;
// GetOCClass
void* cls = objc_getClass("NSFileManager");
pFManagerClass = Import(cls);
// Wrap
pFManager = Import(pFManagerClass->defaultManager());
I like to know whether the dummy function "Import" can be defined by use of Objective-C Runtime functions. I guess that Delphi has to use the Objective-C Runtime functions in the end too. From where else could it have the informations for the vtables?
Code snippets:
NSFileManager = interface(NSObject)
['{9736DF94-95E9-4111-8C94-82D5EFF52A81}']
function URLForDirectory(directory: NSSearchPathDirectory; inDomain: NSSearchPathDomainMask; appropriateForURL: NSURL; create: Boolean; error: PPointer): NSURL; cdecl;
...
NSFileManagerClass = interface(NSObjectClass)
['{E666ADEB-9DBE-4BF0-BEE0-5912CF9F5AB4}']
{class} function defaultManager: Pointer; cdecl;
end;
TOCImport = class(TRawVirtualClass, ILocalObject)
TOCGenericImport<C: IObjectiveCClass; T: IObjectiveCInstance> = class(TOCImport)
TNSFileManager = class(TOCGenericImport<NSFileManagerClass, NSFileManager>) end;
class function TOCGenericImport<C,T>.GetOCClass: C;
var
ClassImport: TOCImport;
ClsID: Pointer;
ITypeInfo: PTypeInfo;
CTypeInfo: PTypeInfo;
begin
ITypeInfo := TypeInfo(T);
CTypeInfo := TypeInfo(C);
ClsID := objc_getClass(MarshaledAString(ShortStrToUTF8String(#ITypeInfo^.Name)));
FClassVTable := TOCVTable.Create(CTypeInfo, False);
ClassImport := TOCImport.Create(ClsId, nil, FClassVTable);
ClassImport.QueryInterface(GetTypeData(CTypeInfo)^.Guid, FOCClass);
Result := FOCCLass;
end;
class function TOCGenericImport<C,T>.Wrap(P: Pointer): T;
var
ObjID: Pointer;
Obj: TOCImport;
ITypeInfo: PTypeInfo;
begin
ITypeInfo := TypeInfo(T);
Obj := TOCImport.Create(P, nil, GetInstanceVTable);
Obj.QueryInterface(GetTypeData(ITypeInfo)^.Guid, Result);
end;
class function TPath.InternalGetMACOSPath(const SearchedPath: NSSearchPathDirectory; const SearchMask: NSSearchPathDomainMask): string;
var
nsFile: NSFileManager;
URL: NSURL;
begin
nsFile := TNSFileManager.Wrap(TNSFileManager.OCClass.defaultManager);
URL := nsFile.URLForDirectory(SearchedPath, SearchMask, nil, true, nil);

Releasing Variable used by WebService

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)

Inno Setup/Pascal Scripting - Casting Returning Nil

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;

Using C++ DLL in D10

I use a C++ DLL in my app.
type
Tcl_bla = function(filename: PChar): Integer; cdecl;
var
cl_bla: Tcl_bla;
function CallLibraryProc(Proc: String): Pointer;
begin
Result := GetProcAddress(Handle, PChar(Proc));
if not Assigned(Result) then
Loaded := False;
if not Loaded then
MessageBox(0, PChar('Error => ' + Proc), 'Alert', MB_OK or MB_TOPMOST);
end;
...
Handle := SafeLoadLibrary(
PChar(CurrentPath + Dll),
SEM_FAILCRITICALERRORS or SEM_NOGPFAULTERRORBOX or SEM_NOOPENFILEERRORBOX
);
if (Handle < HINSTANCE_ERROR) then
raise Exception.Create(
Dll + ' library can not be loaded or not found.' + SysErrorMessage(GetLastError)
);
if Handle <> 0 then
begin
// blabla
cl_bla := CallLibraryProc('cl_bla');
end;
...
FreeLibrary(Handle);
The codes aboves works fine with D6. I'm trying to port my code so it can run in Delphi with Unicode support but I have a trouble.
I've read the documentation from Embarcadero about GetProcAddress
procedure CallLibraryProc(const LibraryName, ProcName: string);
var
Handle: THandle;
RegisterProc: function: HResult stdcall;
begin
Handle := LoadOleControlLibrary(LibraryName, True);
#RegisterProc := GetProcAddress(Handle, PAnsiChar(AnsiString(ProcName)));
end;
I can't try this because I don't know how to declare LoadOleControlLibrary!
My CallLibraryProc can load the DLL but somehow cl_bla works incorrectly.
I think the problem with my code because of GetProcAddress's parameter or.. maybe my ported header is wrong.
I may as well post this this as an answer, because it seems like the answer!
The code that you say is D6 code will work fine unmodified in D2010, and have the same meaning. There are two GetProcAddress overloads in Windows.pas. One of them converts from Unicode to ANSI. So you can just call GetProcAddress(Handle, PChar(Proc)) just like you always did.
The magic one looks like this:
function GetProcAddress(hModule: HMODULE; lpProcName: LPCWSTR): FARPROC;
begin
if ULONG_PTR(lpProcName) shr 16 = 0 then // IS_INTRESOURCE
Result := GetProcAddress(hModule, LPCSTR(lpProcName))
else
Result := GetProcAddress(hModule, LPCSTR(AnsiString(lpProcName)));
end;
You don't need to declare or use LoadOleControlLibrary. Just call plain old LoadLibrary like you always have. The important part of the code you saw was to convert Delphi's UnicodeString values into AnsiString values, and then type-cast explicitly to PAnsiChar instead of PChar. (PChar is PWideChar nowadays, and GetProcAddress requires non-Unicode characters.) I suggest changing ProcName to declare it as an AnsiString from the beginning. You'll need less type-casting. Keep LibraryName declared as string.

How can I query remembered UNC connections similar to "net use"?

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)