how to use Pchar function to use c# - delphi

How can I use this function in C#?
function CheckCard (pPortID:LongInt;pReaderID:LongInt;pTimeout:LongInt): PChar;
This function included the dll.
I can try this way:
[DllImport("..\\RFID_107_485.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.ThisCall)]
public static extern char CheckCard(int pccPortID, int pccdReaderID, int pccTimeout);
char pccCheckCard = CheckCard(3, 129, 1000);
Console.WriteLine(pccCheckCard);
but i don't get a true answer...
please help me ? :)

There are many problems here. This is what I can see:
The Delphi code as written uses the Delphi register calling convention. That is only accessible from Delphi code and cannot be called by a p/invoke method. However, it is possible that you have omitted the calling convention from the code and it is in fact stdcall.
Your p/invoke uses CallingConvention.ThisCall which certainly does not match any Delphi function. That calling convention is not supported by Delphi.
You mistranslate PChar, a pointer to null-terminated array of characters as char, a single UTF-16 character.
The Delphi code looks suspicious. The function returns PChar. Well, who is responsible for deallocating the string that is returned. I would not be surprised if the Delphi code was returning a pointer to a string variable that is destroyed when the function returns, a very common error.
You refer to the DLL using a relative path. That is very risky because you cannot easily control whether or not the DLL will be found. Place the DLL in the same directory as the executable, and specify just the DLL's file name.
There is no error checking to be seen.
A variant that might work could look like this:
Delphi
function CheckCard(pPortID: LongInt; pReaderID: LongInt; pTimeout: LongInt): PChar;
stdcall;
C#
[DllImport("RFID_107_485.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr CheckCard(int pccPortID, int pccdReaderID, int pccTimeout);
....
IntPtr pccCheckCard = CheckCard(3, 129, 1000);
// check pccCheckCard for errors, presumably IntPtr.Zero indicates an error
// assuming ANSI text
string strCheckCard = Marshal.PtrToStringAnsi(pccCheckCard);
// or if the Delphi code returns UTF-16 text
string strCheckCard = Marshal.PtrToStringUni(pccCheckCard);
This leaves unresolved how to deallocate the pointer returned. You'll have to consult your documentation for the function to find that out. The question contains insufficient information.

Related

Why so many pointers?

Searching for references of Win32 LDAP API functions, I found the following JwaWinLDAP.pas unit.
On this unit, to function ldap_search_st is declared:
function ldap_search_st(ld: PLDAP; base: PAnsiChar; scope: ULONG;
filter, attrs: PAnsiChar; attrsonly: ULONG; var timeout: TLDAPTimeVal;
var res: PLDAPMessage): ULONG; cdecl;
The timeout: TLDAPTimeVal parameter is declared as:
PLDAPTimeVal = ^TLDAPTimeVal;
l_timeval = packed record
tv_sec: Longint;
tv_usec: Longint;
end;
LDAP_TIMEVAL = l_timeval;
PLDAP_TIMEVAL = ^LDAP_TIMEVAL;
TLDAPTimeVal = l_timeval;
On the code, if I use something like:
procedure foo;
var
TimeVal: PLDAPTimeVal;
begin
ldap_search_st(foo1, Nil, 0, PAnsiChar('(objectClass=*)'), Nil, 0, TimeVal, foo2);
end;
Compiler gives me error:
[dcc32 Error] Types of actual and formal var parameters must be
identical
because of the timeout parameter. If I change TimeVal type to TLDAPTimeVal it compiles and the application works.
The question is:
when I see declaration of types in Delphi, they are always like:
type
PType1 = ^Type1
Type1 = record...
In the specific example cited, it could be:
l_timeval = packed record
tv_sec: Longint;
tv_usec: Longint;
end;
TLDAPTimeVal = l_timeval;
and it would work the exact same way (I think)... Why so much confusion on this kind of declaration?
type
PType1 = ^Type1
Type1 = record...
Above type declaration declares two types - one is record, and another one is typed pointer for that specific record type. They are usually declared in pairs, because they are related. But if your or any code does not need typed pointer, it does not need to declare pointer type.
Function ldap_search_st uses only declared record type. But some other functions in that unit expect pointer as parameter. That is why declaration has both.
Code in question is LDAP Windows API header translation for Delphi. Windows API uses pointers for passing structures to functions.
API translations are usually complex and sometimes have seemingly superfluous declarations. For completeness, translations usually contain all original declarations (symbols) - those are l_timeval, LDAP_TIMEVAL and PLDAP_TIMEVAL, and while those would be sufficient to use the API, there are two additional declarations whose only purpose is providing Delphi style names for more user friendly experience PLDAPTimeVal and TLDAPTimeVal
If you take a look at original LDAP function declarations they all use pointers for passing structures. For instance:
ULONG ldap_search_st(
_In_ LDAP *ld,
_In_ PCHAR base,
_In_ ULONG scope,
_In_ PCHAR filter,
_In_ PCHAR attrs[],
_In_ ULONG attrsonly,
_In_ struct l_timeval *timeout,
_Out_ LDAPMessage **res
);
and
ULONG ldap_connect(
_In_ LDAP *ld,
_In_ LDAP_TIMEVAL *timeout
);
There is one difference in those two considering timeout parameter.
ldap_search_st expects non-null value in timeout parameter - and Delphi translation of that parameter is var timeout: TLDAPTimeVal to match that intent more clearly - that declaration prevents you from accidentally passing null. While TLDAPTimeVal is not a pointer type, having var keyword makes our timeout parameter to behave like one. Behind the scenes Delphi will pass pointer to the structure and that will perfectly match original function declaration.
On the other hand in ldap_connect timeout can contain null value. In that case default timeout value will be used. The only way to satisfy that requirement is to use pointer type to timeout structure. In another words PLDAPTimeVal and Delphi translation of that function declaration is
function ldap_connect(ld: PLDAP; timeout: PLDAPTimeval): ULONG;
It is the matter of coding standards and conventions and there is so many of them because LDAP declarations were adopted into PSDK and then translated to Delphi. Additionally, since Pascal doesn't allow pointer type declaration (eg ^Integer) within formal parameters unlike plain C (eg int *), corresponding pointer types were added to declaration.
Here, I marked various conventions in the declaration, note the difference in casing and prefixes:
PLDAPTimeVal = ^TLDAPTimeVal; // Delphi pointer (Econos convention)
l_timeval = packed record // canonic structure (LDAP convention)
tv_sec: Longint;
tv_usec: Longint;
end;
LDAP_TIMEVAL = l_timeval; // Windows structure (PSDK convention)
PLDAP_TIMEVAL = ^LDAP_TIMEVAL; // Windows pointer (PSDK convention)
TLDAPTimeVal = l_timeval; // Delphi structure (Econos convention)
Curious thing: the forward declaration (before structure) of Delphi pointer is also mandated by Econos convention. Original PSDK code declares the pointer after the structure.

vb6 dll In Delphi

I have written a VB6 dll with this code
Public Function RegGetStr(ByRef FullLocation_Name As String) As String
Dim oReg As Object
Set oReg = CreateObject("WScript.Shell")
RegGetStr = oReg.RegRead(FullLocation_Name, "REG_SZ")
Set oReg = Nothing
End Function
In Delphi I have library working
type
TRegGetStr = Function(Const FullLocation_Name: String): String; StdCall;
var
aRegGetStr: TRegGetStr;
and
#aRegGetStr := Windows.GetProcAddress(LibHandle, 'RegGetStr');
I have a crash
Is it because of the types of strings I'm using? or something else?
Your question asked about calling a VB DLL from Delphi.
VB6, out of the box, creates ActiveX dll's (created as an ActiveX DLL project)
How can I create a standard DLL in VB6? Look at the answer there describing ActiveX DLL.
While you can create standard DLL's in VB6 you have to go spelunking. It does not support that out of the box and is not recommended.
You would consume that VB ActiveX DLL just as you would any ActiveX dll in Delphi.
Deplhi Import Component - Type Library vs ActiveX
=====
IF, on the other hand, you are trying to call an external DLL (e.g. written in Delphi) FROM a VB application, you would create the Delphi function with pAnsiChar parameters for strings. Ex: in MyDelProcs.dll:
procedure MyDelProc(pStr1: pAnsiChar, MyInt: integer);stdcall;
.......................
exports
MyDelProc;
On the VB side you would then declare the procedure as
Declare Sub MyDelProc Lib "MyDelProcs.dll" (ByVal sStr1 as String, MyLong As Long)
Then call the procedure with
Call MyDelProc(sStr1, MyLong)
Some notes:
sStr1 is Unicode internal to VB, but on the call is converted to AnsiString. The ansistring has a chr(0) added to the end and moved to a buffer which is the length of the ansistring plus the chr(0).
Take care with other parameter types. Default Integer in VB is 2 bytes, default integer in Delphi is 4, which is equivalent to a Long type in VB.
If your Delphi function needs to know the length, pass it as another (long/integer) parameter.
The string buffer is fixed in size. If you are returning a string of longer length then either pad the original string with enough room, or pass a longer dummy string with enough length for the return.
Upon return the string is treated as ansistring. It will be converted back to Unicode to be placed into a VB internal string.

Delphi SHGetFolderPath and null terminated string

I am using this function to get user folder:
function LocalAppDataPath : string;
const
SHGFP_TYPE_CURRENT = 0;
var
path: array [0..MaxChar] of char;
begin
SHGetFolderPath(0,CSIDL_LOCAL_APPDATA,0,SHGFP_TYPE_CURRENT,#path[0]);
Result := StrPas(path);
end;
It is working great.
But after I add this lib to my project: OpenJpeg
The returned values from this function is: C. It seems like for some reason each char in the "path" array has #0 after it and for this reason the returned string it truncated right at the start.
When I remove: OpenJpeg from my uses it all works good.
Any idea why it happens?
At the very top of the OpenJpeg unit is found this code:
type
Char = AnsiChar;
This unit is therefore toxic when used in a Unicode Delphi. When you use Char in your code you are expecting a WideChar but this evil type alias confounds you.
My advice is to modify the library to remove that type alias and replace every use of Char with AnsiChar.
If you don't want to do that you can fully qualify Char as System.Char, or use WideChar explicitly in your code. However, this unit is, as I said, toxic, and it really needs to be remedied.
If you enable the Typed-Checked Pointers compiler option, the compiler will stop you from passing a PAnsiChar to a function expecting a PWideChar, and vice versa, when using the # address operator. Better to find your mistakes at compile time if possible.
You can simplify the code by passing path in place of #path[0].
Please don't use StrPas. It has long been deprecated. You can simply write
Result := path;
The array is one character longer than necessary. That is, assuming MaxChar is what I think it is. Anyway, the array should be
path: array [0..MAX_PATH-1] of char;
You also fail to perform any error checking on the API call. Please check for errors as described in the documentation.

how can i return pchar type from delphi dll function? [duplicate]

This question already has answers here:
How can I return a PChar from a DLL function to a VB6 application without risking crashes or memory leaks?
(7 answers)
Closed 8 years ago.
Vb6 code to run on the following error.
How can I fix the error?
How can I return a normal string?
vb6 code
Private Declare Function DllPchar Lib "C:\TEST\Project2.dll" (ByVal AStr As String) As String
Private Sub Command10_Click()
Dim tmp as String
tmp = DllPchar("123");
End Sub
Delphi7 code
function DllPchar( AStr: PChar) : PChar; stdcall;
Well, you haven't given much information about this problem. For instance, the error message. And the Delphi code for "DllPChar".
But never mind. The first thing I notice is that your Declare Function statement is returning a String. This will not work because VB is expecting a value of type BSTR to be returned. Delphi has a WideString type which is compatible with BSTR.
The reason why this is important is because VB strings are internally UTF-16, i.e. 2 byte per character Unicode Strings that are allocated by the COM memory manager. The Delphi PAnsiChar type is a pointer to an 8-bit ANSI character, and Delphi strings are allocated by Delphi's own memory manager. They are incompatible. However, there is a special case in VB6 where you can use Declare Function with a parameter ByVal ... As String, and VB handles and automatic conversion between a VB string and PAnsiChar before the call, and then between PAnsiChar and VB string after the call.
If you can't use BSTR in Delphi, your best bet is to rewrite DllPchar() so that it modifies the AStr parameter. Or alternatively, create a new parameter to return the value in.
If you can use BSTR, then you can modify AStr to pass it ByRef rather than ByVal. This will allow you to pass in a Unicode string from VB. You then return your result via the return value.

Access violation at address 003B0E8E in module 'MpLib.dll'. Read of address 00120BF4

I've searched around Stackoverflow for the issue I was having, but they were all very specific for that particular access violation.
The piece of code I believe I am having trouble with is a function called MpqExtractFile, stored in a DLL.
Function:
function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: AnsiString): Boolean; stdcall; external 'MpqLib.dll' name 'B2';
Call:
if MpqExtractFile(hMPQ, 'war3map.j', AnsiString(tempDir+'\war3map.j')) = True
then ShowMessage('Success.')
else ShowMessage('Failed.');
Upon execution of this code my application throws the access violation error so the ShowMessage is not displayed at all.
I am unsure if the above is adequate to even estimate what the problem could be, but if there is anything else I should please tell me.
Edit
This is an extract of the VB.NET source code that I have been interpreting:
Declare Function MpqExtractFile Lib "MpqLib.dll" Alias "B2" (ByVal hMPQ As Integer, ByVal szToExtract As String, ByVal szExtracted As String) As Boolean
I am obviously not familiar with declarations in other languages, but I have found the below function in the same VB file where the VB functions were declared.
BOOL WINAPI MpqExtractFile(HANDLE hMpq, const char * szToExtract, const char * szExtracted);
Thank you in advanced!
AnsiString is the completely wrong type to use, unless the DLL was writting in Delphi/C++Builder and actually used AnsiString in its parameters. If that were the case, you would need to know which version of Delphi/C++Builder the DLL was written in, because the memory layout of AnsiString was changed in 2009.
In any case, it is very dangerous to pass non-POD data across DLL boundaries, so most DLLs do not do that. The parameters in question are most likely PAnsiChar instead, eg:
function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: PAnsiChar): Boolean; stdcall; external 'MpqLib.dll' name 'B2';
.
if MpqExtractFile(hMPQ, 'war3map.j', PAnsiChar(AnsiString(tempDir+'\war3map.j'))) then
ShowMessage('Success.')
else
ShowMessage('Failed.');
Other points to consider:
1) not all DLLs use the stdcall calling convention. It is not uncommon for DLLs written in C, like many open-source libraries are, to use the cdecl calling convention instead:
function MpqExtractFile(var hMPQ: Integer; szToExtract, szExtracted: PAnsiChar): Boolean; cdecl; external 'MpqLib.dll' name 'B2';
2) C does not have a true Boolean data type like Delphi and C++ do. It is not uncommon for C code to use Byte or even Integer to mimic a Boolean.
In order to use a DLL in Delphi, you really need to know the actual proper declaration of its exported functions. This is less of an issue in C/C++ because most DLLs have an accompanied .h file that provides the declarations. Do you have such a .h file? If so, post it here so someone can verify your translation to Delphi.
Update:
Based on new information, the correct Delphi declaration is this:
function MpqExtractFile(hMpq: THandle; const szToExtract, szExtracted: PAnsiChar): BOOL; stdcall; external 'MpqLib.dll' name 'B2';
Your VB.net declaration is:
Declare Function MpqExtractFile Lib "MpqLib.dll" Alias "B2" (
ByVal hMPQ As Integer, ByVal szToExtract As String,
ByVal szExtracted As String) As Boolean
The equivalent Delphi import would be:
function MpqExtractFile(MpqExtractFile: Integer;
szToExtract, szExtracted: PAnsiChar): BOOL;
stdcall; external 'MpqLib.dll' name 'B2';
Delphi string types should not be used for interop. The p/invoke marshaller maps String to C++ char* which is PAnsiChar in Delphi.
This sort of task really should be carried out with the C++ header file. You say you have not got that. If the DLL is written in C++ then the header file surely exists. It would pay to track it down and work from that as your source.

Resources