Indy/ssl cannot open file - delphi

I'm trying to use ssl with Indy and keep getting the following error:
Project MtApp.exe raised exception class EFOpenError with message 'Cannot open file "C:\Development\MyApp\Win64\Debug\㩃䑜癥汥灯敭瑮作浡牡屵汁呬楨杮即獹潬屧祓䱳杯楓屭祓䱳杯楓䍭湯潳敬坜湩㐶䑜扥杵浜剹潯䍴⹁数m". The system cannot find the file specified'.
It occurs in the IdSSLOpenSSL.pas file in the following function:
function by_Indy_unicode_file_ctrl(ctx: PX509_LOOKUP; cmd: TIdC_INT; const argc: PAnsiChar; argl: TIdC_LONG; out ret: PAnsiChar): TIdC_INT; cdecl;
The argc parameter appears to be passed in correctly. The IDE debugger shows it as "C:\Development\MyApp\Win64\Debug\myRootCA.pem". The problem seems to be when it is cast as a PWideChar and assigned to a String variable:
X509_FILETYPE_PEM:
begin
// Note that typecasting an AnsiChar as a WideChar is normally a crazy
// thing to do. The thing is that the OpenSSL API is based on ASCII or
// UTF8, not Unicode and we are writing this just for Unicode filenames.
LFileName := PWideChar(argc);
LOk := Ord(Indy_unicode_X509_load_cert_crl_file(ctx, LFileName, X509_FILETYPE_PEM) <> 0);
end;
The LFileName variable is 㩃䑜癥汥灯敭瑮作浡牡屵汁呬楨杮即獹潬屧祓䱳杯楓屭祓䱳杯楓䍭湯潳敬坜湩㐶䑜扥杵浜剹潯䍴⹁数m after the cast, causing a FileNotFound exception.
What am I doing wrong?

You did not say which version of Delphi or Indy you are using. But, the code you have shown is not the latest code that is currently in Indy.
What you have described was due to a regression bug that Embarcadero introduced in the Indy release that shipped in RAD Studio 10.3. They fixed that in a patch for RAD Studio 10.3.3:
https://blogs.embarcadero.com/rad-studio-10-3-3-indy-server-ssl-certificate-patch/
https://cc.embarcadero.com/item/30906
I suggest you update to the latest version of Indy from its GitHub repo:
https://github.com/IndySockets/Indy/
https://github.com/IndySockets/Indy/wiki/Updating-Indy

Related

Delphi 2005, can't build Indy 10

I'm receiving an error when building the IdMessageHelper.pas unit in the IndyProtocols90 package. All instances of LoadFromStream and LoadFromFile are claiming there is an issue with the signature:
[Error] IdMessageHelper.pas(78): E2250 There is no overloaded version of 'LoadFromStream' that can be called with these arguments
procedure Internal_TIdMessageHelper_LoadFromStream(AMsg: TIdMessage; AStream: TStream;
const AHeadersOnly: Boolean; const AUsesDotTransparency: Boolean);
var
LMsgClient: TIdMessageClient;
begin
if AUsesDotTransparency then begin
AMsg.LoadFromStream(AStream, AHeadersOnly);
end else
begin
// clear message properties, headers before loading
AMsg.Clear;
LMsgClient := TIdMessageClient.Create;
try
Internal_TIdMessageClientHelper_ProcessMessage(LMsgClient, AMsg, AStream, AHeadersOnly, False);
finally
LMsgClient.Free;
end;
end;
end;
I see that IdMessageHelper is new to this version, but the method that's being called (IdMessage.LoadFromStream for example), the arguments for it haven't changed from the last few versions - at least not for the ones that I have the source.
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
var
LMsgClient: TIdMessageClient;
begin
// clear message properties, headers before loading
Clear;
LMsgClient := TIdMessageClient.Create;
try
LMsgClient.ProcessMessage(Self, AStream, AHeadersOnly);
finally
LMsgClient.Free;
end;
end;
I'm pretty sure I removed all previous versions and packages since this was a clean install of D2005.
The IdMessageHelper unit introduces new LoadFrom...() and SaveTo...() methods for the TIdMessage component, to add an AUsesDotTransparency parameter when loading/saving emails.
In Delphi 2005 and later, it does this by defining a class helper (which is a feature introduced in Delphi 2005) to add new methods to the TIdMessage component without having to modify the IdMessage.pas unit itself. This allows Indy to let people use familiar IdMessage1.LoadFrom...() and IdMessage1.SaveTo...() syntax when using the new functionality 1.
Things were done this way so as not to cause interface breaking changes in the IdMessage unit itself. I blogged about this new addition at the time the IdMessageHelper.pas unit was first added to Indy:
New TIdMessage helper
In your case, the error message is complaining about Line 78:
AMsg.LoadFromStream(AStream, AHeadersOnly);
That line is the new 3-parameter TIdMessageHelper.LoadFromStream() method attempting to call the pre-existing 2-parameter TIdMessage.LoadFromStream() method when AUsesDotTransparency is True:
procedure TIdMessage.LoadFromStream(AStream: TStream; const AHeadersOnly: Boolean = False);
I have tested this new class helper in later Delphi versions and it works fine for me. You should not be getting a compiler error, as there should not be any ambiguity.
However, I have not tested the class helper in Delphi 2005 specifically (as I do not have that version installed), so it is possible that the compiler error could be indicating that class helpers (being a new language feature at the time) were still a little buggy, and were fixed later on.
If you can't find the cause of the ambiguity, you can work around the issue by modifying IdMessageHelper.pas to undefine HAS_CLASS_HELPER for Delphi 2005 1, and then recompile Indy again.
1: In older versions of Delphi where class helpers are not available, IdMessageHelper.pas also defines several standalone TIdMessageHelper_LoadFrom...() and TIdMessageHelper_SaveTo...() functions, so people can still utilize the new AUsesDotTransparency functionality, just with a less-desirable calling syntax.
EDIT: it turns out that class helpers were very buggy in Delphi 2005, and were not officially supported until Delphi 2006:
Class helpers have now been formally introduced in the Win32 compiler [in Delphi 2006]. In Delphi 2005 class helpers were not formally available, and although you could use them they were actually quite buggy. It was quite easy to get internal compiler errors while using them, nothing you could complain to with Borland about as this feature was not officially supported.
So, I have now disabled the TIdMessageHelper helper class in Delphi 2005, and have updated the above mentioned blog article accordingly.

NPAPI plugin framework Error

I am trying to use NPAPI Framework from Yury Sidorov by following this answer:
How to embed Delphi VCL form into HTML page using NPAPI
but I get an error with NPPlugin.pas. I am using delphi XE7 and here what i did by following Krom Stern instructions
procedure DefDebugOut(const Msg: string);
begin
OutputDebugStringA(PAnsiChar(Msg + #13#10)); // Changed From Pchar To PAnsiChar
end;
but still getting error with compiler with this message
[dcc32 Error] NPPlugin.pas(2215): E2010 Incompatible types: 'PWideChar' and 'PAnsiChar'
Error raised on this procedure
procedure TPlugin.SetException(const msg: string);
var
s: String;
begin
try
{$ifopt D+} NPP_DebugOut('Exception: ' + msg); {$endif}
s:=UTF8Encode(msg);
NPN_SetException(m_pScriptableObject, PAnsiChar(s));
except
{ prevent any exception from leaking out of DLL }
end;
end;
here is the procedure NPN_SetException
procedure NPN_SetException(npobj: PNPObject; msg: PNPUTF8);
begin
NavigatorFuncs.SetException(npobj, msg);
end;
I'll start with a piece by piece breakdown of what we can see. Bear in mind that we don't have NPPlugin.pas at hand and have to infer its contents from the information in the question. All the same, I think it's possible for us to do that accurately.
s := UTF8Encode(msg);
Here s is of type string. That's an alias for UnicodeString, encoded as UTF-16. So you convert from UTF-16 to UTF-8 and then back to UTF16.
You need it like this:
NPN_SetException(m_pScriptableObject, PAnsiChar(UTF8Encode(msg)));
Alternatively, if you need a variable to hold UTF-8 encoded text, declare it to be UTF8String which is AnsiString(65001). If you changed the type of s to be UTF8String then the code in the question would be correct. Although somewhat more verbose than it would need to be.
Another problem is here:
OutputDebugStringA(PAnsiChar(Msg + #13#10));
Your cast doesn't make Msg actually be 8 bit encoded. However, you don't want to use the ANSI version of the function. You need this:
OutputDebugString(PChar(Msg + sLineBreak));
Your exception handler is misguided. It is the DLL's job not to leak exceptions. If you attempt to catch and suppress them all you will simply mask errors in your own code. You need to remove that exception handler and check for errors by following the instructions given by the library documentation.
Now to the bigger picture. None of the above explains your reported error. The only sound explanation for that is that your declaration for NPN_SetException accepts wide text. In which case you could make the code compile simply by writing this:
NPN_SetException(m_pScriptableObject, PChar(msg));
Of course, that makes the appearance of UTF-8 somewhat inexplicable. In fact the library Mozilla does accept 8 bit text, UTF-8 encoded. So why would NPN_SetException expect to be passed UTF-16 text? Well it doesn't. The explanation is that you have declared NPN_SetException incorrectly. So, just to be clear, whilst PChar(msg) would make your code compile, it would not resolve your problem. You would be left with code that failed at runtime.
So, how did this happen? You've taken a working piece of code that used PChar aliased to PAnsiChar onto a Delphi with PChar aliased to PWideChar and not translated correctly. Even when you get your code to compile, it will not work correctly. You started with code like this:
function NPN_SetException(..., Msg: PChar): ...;
On older Delphi versions where PChar was PAnsiChar then this was correct. You are compiling this now on XE7 where PChar is PWideChar and so this is not correct. It needs to be:
function NPN_SetException(..., Msg: PAnsiChar): ...;
Then the calling code can be:
NPN_SetException(m_pScriptableObject, PAnsiChar(UTF8Encode(msg)));
My advice is that you:
Step back and revisit the handling of Unicode in Delphi.
Go back to you original code and change all the Mozilla interface code that uses PChar to PAnsiChar.
Whenever you need to provide PAnsiChar do it with PAnsiChar(UTF8Encode(str)).
This NPAPI code was clearly designed for older versions of Delphi before the switch to Unicode in Delphi 2009. The default String/(P)Char types are no longer aliases for AnsiString/(P)AnsiChar, they are now aliases for UnicodeString/(P)WideChar. A UnicodeString cannot be casted to a PAnsiChar, just like an AnsiString could never be casted to a PWideChar.
In DefDebugOut(), the simplest fix is to change PAnsiChar to PChar and change OutputDebugStringA() to OutputDebugString():
procedure DefDebugOut(const Msg: string);
begin
OutputDebugString(PChar(Msg + #13#10));
end;
This is compatible with all Delphi versions (the code should have been doing this from the beginning - there was no reason to call OutputDebugStringA() directly). PChar and OutputDebugString() map to PAnsiChar and OutputDebugStringA() in Delphi 2007 and earlier, and to PWideChar and OutputDebugStringW() in Delphi 2009 and later. So everything matches.
In TPlugin.SetException(), UTF8Encode() returns a UTF8String in all versions of Delphi. However, prior to Delphi 2009, UTF8String was just an alias for AnsiString itself, but in Delphi 2009 it was changed to a true UTF-8 string type with full RTL support (it still has an AnsiString base, so it can still be casted to PAnsiChar). When a UTF8String is assigned to a UnicodeString, the compiler performs an implicit data conversion from UTF-8 to UTF-16. And as stated above, UnicodeString cannot be casted to PAnsiChar. So you need to change the s variable from String to UTF8String for all Delphi versions:
procedure TPlugin.SetException(const msg: string);
var
s: UTF8String;
begin
try
{$ifopt D+} NPP_DebugOut('Exception: ' + msg); {$endif}
s:=UTF8Encode(msg);
{
UTF8Encode() is deprecated in Delphi 2009+.
In those versions, you can use this instead:
s := UTF8String(msg);
}
NPN_SetException(m_pScriptableObject, PAnsiChar(s));
except
{ prevent any exception from leaking out of DLL }
end;
end;
With that said, if you are still getting the same error on the NPN_SetException() call, then it means the second parameter of NPN_SetException() is declared as PChar. It needs to be declared as PAnsiChar instead.

How can I use OpenCV with Unicode versions of Delphi?

I am editing a open source project called Opencv for Delphi which compiles fine with Delphi 6 , Delphi 2009 and Delphi xe2.
I just removed incompatible (eg :Application.MainFormOnTaskbar := True;) for the Delphi 6 compilation.
On the run time d6 app works fine without errors, but the rest are compiling well but with run time error when calling cvopencv.dll.
The original project is a Spanish project compiled with Delphi 2007 which works fine.
In the d6 exe i can't read any thing except ??????? but others showing in spanish which means it is a UNICODE problem, when calling cvopencv.dllthe error happens.
What can i do to make this project work with Delphi 2009 or higher (still d2009 compiling well)
========================= After David Heffemans Answer =====================
procedure TForm1.Button2Click(Sender: TObject);
var
file1 : PAnsiChar; // "haarcascade_frontalface_alt.xml";
file2 : PAnsiChar; //"haarcascade_eye.xml";
file3 : PAnsiChar; //"haarcascade_upperbody.xml";
SourceFileName : AnsiString;
StorageType : Integer;
ImSize : CvSize;
begin
memo1.Lines.Clear;
GetMem(Storage, SizeOf(CvMemStorage));
SourceFileName:=Edit1.Text;
StorageType:=0;
storage:=nil;
storage :=cvCreateMemStorage(storageType);
file1 := PAnsiChar(ExtractFilePath(Application.ExeName)+'haarcascade_frontalface_alt.xml');
file2 := PAnsiChar(ExtractFilePath(Application.ExeName)+'haarcascade_eye.xml');
file3 := PAnsiChar(ExtractFilePath(Application.ExeName)+'haarcascade_upperbody.xml');
cascade_f := cvLoad(file1, nil, nil, nil);
cascade_e := cvLoad(file2, nil, nil, nil);
cascade_ub := cvLoad(file3, nil, nil, nil);
if cascade_f=nil then ShowMessage('Íå ìîãó çàãðóçèòü êàñêàä Face');
if cascade_e=nil then ShowMessage('Íå ìîãó çàãðóçèòü êàñêàä Eye');
if cascade_ub=nil then ShowMessage('Íå ìîãó çàãðóçèòü êàñêàä UpperBody');
img := cvLoadImage(PAnsiChar(AnsiString(SourceFileName)), 1);
cvNamedWindow((PAnsiChar(AnsiString(Edit1.Text))), 1);
detectEyes(img);
cvShowImage(PAnsiChar(AnsiString(SourceFileName)), img);
end;
end.
The OpenCV library you refer to says this at the top of the header:
Borland Delphi 4,5,6,7 API for Intel Open Source Computer Vision Library
This code presents a Delphi wrapper around the OpenCV DLLs.
For these older Delphi versions, Char, PChar and string map to AnsiChar, PAnsiChar and AnsiString respectively. This matches the OpenCV DLLs which are pure ANSI. There are no Unicode APIs in OpenCV (at least so far as I can discern).
If you wish to use this unit from Delphi 2009 and up you need to recognise that in Delphi 2009, Unicode support was added. This changed the mapping of Char, PChar and string map to WideChar, PWideChar and UnicodeString respectively. This is the cause of the incompatibility you have encountered because the DLL behind this unit is still processing single byte ANSI text.
Probably the most sensible approach with this unit is to change it to use AnsiChar, PAnsiChar and AnsiString. You can do this with a global search and replace in the unit.
This will make the OpenCV.pas unit match the DLLs again. You now need to be careful how you interface with this OpenCV.pas unit. The functions that receive PAnsiChar parameters need special treatment.
For example consider the following example:
Function cvLoadImage( const filename : PChar;
iscolor : integer=1) : PIplImage; cdecl;
This is the original declaration. After your search and replace it will read like this:
Function cvLoadImage( const filename : PAnsiChar;
iscolor : integer=1) : PIplImage; cdecl;
When you come to call it you need to ensure you pass a proper PAnsiChar. Like this:
var
FileName: string;
...
image := cvLoadImage(PAnsiChar(AnsiString(FileName)));
You must convert from string (which maps to UnicodeString) to AnsiString and only then can you cast to PAnsiChar. Do not write PAnsiChar(FileName). This will not convert the Unicode string to ANSI and will fail.
Fortunately for you the OpenCV library has a relatively small number of APIs that receive text.
See the project - Delphi-OpenCV on github (OpenCV version - 2.4.6, environment - Delphi XE2-XE4). Translation of OpenCV library header files in Delphi: https://github.com/Laex/Delphi-OpenCV

Delphi - Problem using an existing dll

I have to use an existing dll, but got a problem with it.
Here's how I try to use it:
unit u_main;
...
implementation
procedure getUserData(var User, Pass: string); stdcall; external 'Common5.dll';
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
u, p: string;
begin
getUserData(u,p);
end;
...
end.
When I press the button the get the userData, I get an EInvalidPointer exception.
The dll is registerd and in some other projects it's in use and work. any ideas?
EDIT:
The DLL was created in Delphi7 and is now used in a Delphi 2009 project.
Maybe there's a problem with unicode strings or something like that?
You need to rebuild the Delphi 7 DLL, make it follow the WinApi standard of getting PChar and BufferLen parameters. You've got multiple problems with the current implementation:
string is platform-specific, it's implementation may change between delphi versions (and did change). You're not supposed to use string outside the platform!
You're passing the parameters as "var", suggesting the DLL might change the value of user and/or pass. String is an special, managed type, changing it requires allocating memory for the new string. This in turns requires you to share the memory manager between the DLL and the EXE (using sharemem.pas and BorlandMM.dll - or variants). The trouble is, sharing the memory manager between different versions of Delphi is an unsupported configuration! (gotton from embarcadero forums)
The Delphi 7 is hoping to receive an simple AnsiString (1 byte chars), while the Delphi 2009 exe is sending Unicode strings (2 bytes per char).
Along with using PChar, be sure to pre-allocate the space before you call GetUserData. i.e. if you assign 'foo' into a pchar that's empty, you'll blow up. So either use static length PChar/PAnsiChar arrays, or use this technique:
var
s : AnsiString;
begin
setlength(s,256);
MyDLLProc(PAnsiChar(s));
end;

ActiveX controls with old Delphi versions

I'm testing a non visual ActiveX control based on a registered .ocx
which I import into Delphi using the provided wizard.
Then, I simply put the generated component on the main form of a new VCL application.
Under old Delphi versions (D5 and D2007), when i launch the application, this raise an AV
during the component initialization.
with Delphi 2009 : no problem, the application starts smoothly.
My questions are :
Are there known enhancements of ActiveX management in recent Delphi versions which
can explain this difference ?
Can I suspect a bug in the ActiveX control, or can I consider the origin of the
problem is from old Delphi versions ?
I need to use this component (if tests OK) in D2007.
Do you think that it is possible to correct the AV problem under D2007 by modifying the D2007 generated .tlb file (for example by trying to use the D2009 generated one)
PS: the ActiveX control is not named, because my question is a general question about Delphi and ActiveX, not about a specific ActiveX control.
Edit :
With D2007, the error (an Access Violation) appears during Application.CreateForm(TForm1, Form1);
and more specifically when the Olecontrol is created :
procedure TOleControl.CreateInstance;
var
ClassFactory2: IClassFactory2;
LicKeyStr: WideString;
procedure LicenseCheck(Status: HResult; const Ident: string);
begin
if Status = CLASS_E_NOTLICENSED then
raise EOleError.CreateFmt(Ident, [ClassName]);
OleCheck(Status);
end;
begin
if not (csDesigning in ComponentState) and
(FControlData^.LicenseKey <> nil) then
begin
// ON THE LINE BELOW : the call of CoGetClassObject raise an AV
OleCheck(CoGetClassObject(FControlData^.ClassID, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER, nil, IClassFactory2, ClassFactory2));
LicKeyStr := PWideChar(FControlData^.LicenseKey);
LicenseCheck(ClassFactory2.CreateInstanceLic(nil, nil, IOleObject,
LicKeyStr, FOleObject), SInvalidLicense);
end else
LicenseCheck(CoCreateInstance(FControlData^.ClassID, nil,
CLSCTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IOleObject,
FOleObject), SNotLicensed);
end;
As far as I remember there were major enhancements to the ActiveX/TLB import in Delphi 2009 (related to Unicode support) - that might explain it.
In my personal experience Delphi 7 and Delphi 2007 repeatedly failed to import some Windows 7 type libraries (various new interfaces to work with new taskbar), but Delphi 2009 managed that without any problems at all.
As for using Delphi 2009 generated file in earlier versions - beware of Unicode issues. Plus it won't help if the defect is in RTL... Try to make a wrapper ActiveX in Delphi 2009 and use it in Delphi 2007 - that should work.
Sorry to barge in so late after the battle (5 years later as a matter of fact), but I wasted so much time on this precise issue that I thought I should share what I've seen and what I've done to solve it :
2 machines (win7 64 / win 8.1) same delphi 7 (same version same build), same activeX (MapX to name it) with identical .lic files containing the key made of 59 characters :
uQnZi2sFw22L0-MRa8pYX-1E2P8065-5N5M3459-3C934220-04969-6562
same import producing 2 slightly different TLB.
The one working : (on win 8.1) contains this in procedure TMap.InitControlData :
const
CLicenseKey: array[0..61] of Word = ( $0075, $0051, $006E, $005A, $0069, $0032, $0073, $0046, $0077, $0032, $0032
, $004C, $0030, $002D, $004D, $0052, $0061, $0038, $0070, $0059, $0058
, $002D, $0031, $0045, $0032, $0050, $0038, $0030, $0036, $0035, $002D
, $0035, $004E, $0035, $004D, $0033, $0034, $0035, $0039, $002D, $0033
, $0043, $0039, $0033, $0034, $0032, $0032, $0050, $0030, $002D, $004D
, $0030, $0034, $0039, $0036, $0039, $002D, $0036, $0035, $0036, $0032
, $0000);
which translates to a 61 char key
uQnZi2sFw22L0-MRa8pYX-1E2P8065-5N5M3459-3C93422P0-M04969-6562
The TLB that does not work (win 7 64) contains this instead:
const
CLicenseKey: array[0..2] of Word = ( $0050, $004D, $0000);
which translates to a 2 char key
PM
Replacing one const with the other and recompiling the component solved my issue. I don't really know what happened. I just know the Import/TLB produced a bad .pas file that can be corrected manually.

Resources