I have a C# DLL in which exposes a method which generates a string.
I want to call this Method from Inno Setup and receive the string then.
function GetInformationEx():String;
external 'GetInformationEx#{src}\data\tools\ZipLib.dll stdcall loadwithalteredsearchpath';
procedure ShowProgress(progress:Integer);
var
information : String;
begin
WriteDebugString('ShowProgress called');
if(progress > pbStateZip.position) then
begin
pbStateZip.position := progress;
lblState2.Caption := IntToStr(progress)+' %';
try
information := GetInformationEx();
except
ShowExceptionMessage;
end;
//Do something with the information
end
if(progress >= 100)then
begin
KillTimer(0,m_timer_ID);
//Inform that the extraction is done
end
WriteDebugString('ShowProgress leave');
end;
Here is my simple C# part
[DllExport("GetInformationEx", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
public static String GetInformationEx()
{
return "Some simple text message || or heavy information";
}
My question is:
What kind of type do I have to send back to Inno Setup so that Inno Setup can handle it correctly?
Until now I get this message
PS: I read this post:
Returning a string from a C# DLL with Unmanaged Exports to Inno Setup script
But I want the C# code to be in charge of the string.
The .NET String type definitely won't marshal to Pascal string type. The .NET does not know anything about Pascal types.
The .NET can marshal strings to character arrays (and Pascal can marshal string from character arrays). But when the string is a return type of a function, there's problem with allocation of the memory for the string (who allocates the memory, who releases it).
That's why the solution in question you pointed to suggests you to use by ref/out parameter, because this way the caller can provide a buffer the .NET can marshal the string to. So there are no problems with allocation.
Related
I've been learning about marshaling Unmanaged DLL imports into C# ... And I've come across something I don't quite understand.
In Delphi, there is a function that is returning Result := NewStr(PChar(somestring)) from a Procedure SomeFunc() : PChar; Stdcall;
From my understanding, NewStr just allocates a buffer on the local heap ... and SomeFunc is returning a pointer to it.
In .NET 4.0 (Client Profile), via C# I can use :
[DllImport("SomeDelphi.dll", EntryPoint = "SomeFunc", CallingConvention = CallingConvention.StdCall)]
public static extern String SomeFunc(uint ObjID);
This works (or as David says, "appears to work") fine in Windows 7 .NET 4.0 Client Profile. In Windows 8, it has unpredictable behavior, which brought me down this path.
So I decided to try the same code in .NET 4.5 and got Heap corruption errors. Okay, so now I know this is not the correct way to do things. So I dig further :
Still in .NET 4.5
[DllImport("SomeDelphi.dll", EntryPoint = "SomeFunc", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr _SomeFunc();
public static String SomeFunc()
{
IntPtr pstr = _SomeFunc();
return Marshal.PtrToStringAnsi(pstr);
}
This works without a hitch. My (novice) concern is that NewStr() has allocated this memory and it's just sitting there forever. Is my concern not valid?
In .NET 4.0, I can even do this and it never throws an exception :
[DllImport("SomeDelphi.dll", EntryPoint = "SomeFunc", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr _SomeFunc();
public static String SomeFunc()
{
String str;
IntPtr pstr = _SomeFunc();
str = Marshal.PtrToStringAnsi(pstr);
Marshal.FreeCoTaskMem(pstr);
return str;
}
This code throws the same heap exception in 4.5, however. This leads me to believe the problem lies in the fact that in .Net 4.5, the marshaler is trying to FreeCoTaskMem() and that is what is throwing the exceptions.
So questions :
Why does this work in .Net 4.0 and not 4.5?
Should I be concerned about NewStr()'s allocation in the native DLL?
If answer "No" to #2, then the second code example is valid?
The key information, which is very hard to find in the docs, concerns what the marshaller does with a p/invoke function with a return value of type string. The return value is mapped to a null-terminated character array, i.e. LPCTSTR in Win32 terms. So far so good.
But the marshaller also knows that the string must have been allocated on a heap somewhere. And it cannot expect the native code to deallocate it since the native function has finished. So the marshaller deallocates it. And it also assumes that the shared heap that was used was the COM heap. So the marshaller calls CoTaskMemFree on the pointer returned by the native code. And that's what leads to your error.
The conclusion is that if you wish to use string return value on the C# p/invoke end, you need to match that on the native end. To do so return PAnsiChar or PWideChar and allocate the character arrays with a call to CoTaskMemAlloc.
You absolutely cannot use NewStr here. In fact you should never call that function. Your existing code is comprehensively broken and every call you make to NewStr leads to a memory leak.
Some simple example code that will work:
Delphi
function SomeFunc: PAnsiChar; stdcall;
var
SomeString: AnsiString;
ByteCount: Integer;
begin
SomeString := ...
ByteCount := (Length(SomeString)+1)*SizeOf(SomeString[1]);
Result := CoTaskMemAlloc(ByteCount);
Move(PAnsiChar(SomeString)^, Result^, ByteCount);
end;
C#
[DllImport("SomeDelphi.dll")]
public static extern string SomeFunc();
You would probably want to wrap the native code up in a helper for convenience.
function COMHeapAllocatedString(const s: AnsiString): PAnsiChar; stdcall;
var
ByteCount: Integer;
begin
ByteCount := (Length(s)+1)*SizeOf(s[1]);
Result := CoTaskMemAlloc(ByteCount);
Move(PAnsiChar(s)^, Result^, ByteCount);
end;
Yet another option is to return a BSTR and use MarshalAs(UnmanagedType.BStr) on the C# side. However, before you do so, read this: Why can a WideString not be used as a function return value for interop?
Why do you see different behaviour in different .net versions? Hard to say for sure. Your code is just as broken in both. Perhaps the newer versions are better at detecting such errors. Perhaps there's some other difference. Are you running both 4.0 and 4.5 on the same machine, same OS. Perhaps your 4.0 test is running on an older OS which doesn't throw errors for COM heap corruptions.
My opinion is that there's little point understanding why broken code appears to work. The code is broken. Fix it, and move on.
My few points:
First, Marshal.FreeCoTaskMem is for freeing COM allocated memory blocks! It's not guaranteed to work for other memory blocks allocated by Delphi.
NewStr is deprecated (I get this after googling):
NewStr(const S: string): PString; deprecated;
My suggestion is that you also export a DLL function that does string deallocation instead of using FreeCoTaskMem.
How can i use this dll function in c#? I tried the following but i get error.
"External component has thrown an exception."
First time i am doing this PInvoke stuff with C# and Delphi.
function HTTPGET(location:string):string; stdcall;
var
HTTP:TIdHttp;
begin
HTTP := TidHttp.Create(nil);
try
result := HTTP.Get(location);
finally
FreeAndNil(HTTP);
end;
end;
exports
HTTPGET;
begin
end.
namespace Test
{
class Program
{
[DllImport("project1.dll")]
public static extern string HTTPGET(string location);
static void Main(string[] args)
{
Console.WriteLine(HTTPGET("http://www.reuters.com/"));
}
}
}
You cannot call that function from C#. That's because you cannot use Delphi string for interop. You can use PAnsiChar for strings passed from managed to unmanaged, but in the other direction it's more complex. You'd need to allocate the memory at the caller, or use a shared heap. I prefer the latter approach which is easiest done with the COM BSTR. This is WideString in Delphi.
As has been discussed before, you cannot use WideString as a return value for interop, since Delphi uses a different ABI from MS tools for return values.
The Delphi code needs to look like this:
procedure HTTPGET(URL: PAnsiChar; out result: WideString); stdcall;
On the C# side you write it like this:
[DllImport("project1.dll")]
public static extern void HTTPGET(
string URL,
[MarshalAs(UnmanagedType.BStr)]
out string result
);
If you want Unicode for the URL then use PWideChar and CharSet.Unicode.
procedure HTTPGET(URL: PWideChar; out result: WideString); stdcall;
....
[DllImport("project1.dll", CharSet=CharSet.Unicode)]
public static extern void HTTPGET(
string URL,
[MarshalAs(UnmanagedType.BStr)]
out string result
);
Do not use string type: strings require memory management, and C# and Delphi modules obviously use different memory managers (leave alone that C# passes char* and Delphi expects String). Try changing location type to PChar in your DLL, and also change your result type so it's either PChar (the buffer should be allocated explicitly) or something else, but not string.
As i remember, you can't marshall delphi strings with C#... you have to use a workaround with PChar and manage the memory yourself, or use something like the workaround provided in the last answer here:
Using Delphi's stuct arrays and strings in C#
Try Unmanaged Exports for c# by Robert Giesecke
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports
we are using encrypting engine from c# in Delphi App due compatibility with php and works well (so, the mentioned strings problem do not exists).
our earlier solution (worse) : register c# dll as com component and use it. with above solution output library have to be placed in exe directory and without registration works well :)
I am currently converting a Delphi application to C# for an ISS Handler.Delphi uses these memory classes (TMemoryStream, TStreamAdapter) to pass to methods, insert string values, and return and retrieve values from memory.
My question here is, do i really need to create memory objects to insert string values and pass them through methods for retrieval, or is this just unique to the Delphi handler. Currently i am just passing and retrieving string values in my ISS handler, will this be a sort of correct conversion.
Ive researched and came up short. Your help is kindly appreciated.
for eg Delphi code:
m := TMemoryStream.Create;
iss := TStreamAdapter.Create(m, soOwned);
iss._AddRef;
try
hr := CAGetPath(cas, cam, cal, iss);
cb := 0;
m.Write(cb, sizeof(WideChar));
s := PWideChar(m.Memory);
finally
iss._Release;
end;
function CAGetPath(SubscriberID, MailshotID, LinkID: integer;
stmPath: ISequentialStream): HRESULT; stdcall;
{
sPath: WideString;
sPath := "\\MYPATH\\TO\\FOLDER"
stmPath._AddRef;
cb := length(sPath) * sizeof(WideChar);
ES := 'stmPath.Write(' + sPath + ')';
Result := stmPath.Write(PWideChar(sPath), cb, nil);
}
This bit of delphi code just returns a string and sets it to variable s. As fas as i can tell. Is this neccessary and why would anyone do it this way?
Do I really need to create memory objects to insert string values and pass them through methods for retrieval, or is this just unique to the Delphi handler?
The author of the Delphi code is best placed to explain why it was done in that particular way. However, it looks to me as though CAGetPath is an external function since it was declared using stdcall. And so the author will have needed to come up with a solid means to marshal text data across a module boundary. The author chose to use the COM ISequentialStream interface which is a perfectly reasonable choice.
In order to interact with that from Delphi, the author needed to use an object that implements ISequentialStream. The simplest way is to use the TStreamAdapter class which wraps a TStream and presents an IStream interface. In order to use that, a concrete stream must be provided. Hence the use of TMemoryStream. Clearly CAGetPath must return the URL somewhere and why not a memory stream?
Anyway, that's my best guess as to why the Delphi code is that way. There's no evidence that memory streams are needed to implement IIS handlers (whatever they are).
I think you are getting all hung up on replicating the Delphi implementation. In your shoes I would simply try to understand what the underlying requirements are. What is your IIS handler required to do? Then implement that using the idiomatic C# techniques and classes. Use the extant Delphi code as a guide of what the requirement is, but not as a guide for how to implement that requirement.
I have a Delphi DLL that contains the following types:
type
TStepModeType = (smSingle, smMultiStep);
TParameter = record
Number: Integer;
end;
TStruct = record
ModType: PAnsiChar;
ModTypeRev: Integer;
ModTypeID: Integer;
RecipeName: PAnsiChar;
RecipeID: Double;
RootParamCount: Integer;
StepMode: TStepModeType;
ParamCount: Integer;
Parameters: array of TParameter;
end;
I need to call this DLL from C# passing a ref object corresponding to the Delphi types that the DLL will fill and return. I have defined structures in my C# code like this:
enum stepModeType
{
Single,
MultiStep
}
[StructLayout(LayoutKind.Sequential)]
struct parameter
{
public int Number;
}
[StructLayout(LayoutKind.Sequential)]
struct recipe
{
public string modType;
public int modTypeRev;
public int modTypeId;
public string recipeName;
public double recipeId;
public int rootParamCount;
public stepModeType stepMode;
public int paramCount;
public IntPtr parameters;
}
I was doing fine until I ran into the dynamic array (Parameters: array of TParameter) in the Delphi code. I understand that dynamic arrays are a Delphi only construct, so I chose to use an IntPtr in my C# code in the hopes of just getting a pointer to the array and pulling the contents. Unfortunately, I am rather new to this interop stuff and I am not sure how to deal with the IntPtr.
Let's say the Delphi DLL populates the dynamic array with 2 parameter items. Can someone possibly show me the C# code that would get those 2 parameter items out of the array once it gets passed back from the Delphi DLL to my C# calling application?
UPDATE: Well, as it happens the Delphi code I was given was a simplified version. One of our Delphi developers thought it would be easier to get started with the simplified version than the real version, which is substantially more complex containing dynamic arrays of dynamic arrays of dynamic arrays. Anyway, I am now completely over my head. I only know enough about Delphi to be dangerous. Below is the code for the real structures in the Delphi code. Any further guidance on how to deal with these structures from my C# calling application would be greatly appreciated. It may not even be possible with the nesting of dynamic arrays such that they are.
type
TStepModeType = (smSingle, smMultiStep);
TParamValue = record
strVal: String;
fVal: Double;
Changed: Boolean;
end;
TSteps = array of TParamValue;
TRule = record
Value: String;
TargetEnabled: Boolean;
end;
TParamInfo = record
Caption: String;
Units: String;
RuleCount: Integer;
Rules: array of TRule;
end;
TParameter = record
Info: TParamInfo;
Steps: TSteps;
end;
TStruct = record
ModType: PAnsiChar;
ModTypeRev: Integer;
ModTypeID: Integer;
RecipeName: PAnsiChar;
RecipeID: Double;
RootParamCount: Integer;
StepMode: TStepModeType;
ParamCount: Integer;
Parameters: array of TParameter;
end;
I am assuming trust that the DLL has a function that deallocates the recipe struct. That's something that you can't possibly hope to do from C#. More on this point later on.
A Delphi dynamic array is not a valid interop type. It really should only used internally to Delphi code compiled with a single version of the compiler. Exposing it publically is akin to exporting C++ classes from a DLL.
In an ideal world you would re-work the Delphi code so that it exported the array using a proper interop type. However, in this case it is actually relatively easy for you to do the marshalling without adjusting the Delphi code.
Delphi dynamic arrays were introduced way back in Delphi 4 and their implementation has remained unchanged since then. The array of T dynamic array variable is effectively a pointer to the first element. The elements are laid out sequentially in memory. The dynamic array variable also maintains (at negative offsets) a reference count and the size of the array. You can safely ignore these since you are neither modifying the dynamic array nor needing to ascertain its size.
Using IntPtr for the Parameters field is perfect. Because TParameter contains just a single 32 bit integer you can use Marshal.Copy to copy it straight to an int[] array.
So, when the Delphi DLL returns, you can do the final marshalling step using Marshal.Copy.
if (theRecipe.paramCount>0)
{
int[] parameters = new int[theRecipe.paramCount];
Marshal.Copy(theRecipe.parameters, parameters, 0, theRecipe.paramCount);
... do something with parameters
}
That deals with the dynamic array, but as it happens you have another problem with your code as it stands. You are declaring the two strings as string in the C# struct. This means that the marshaller will take responsibility for freeing the memory returned by the Delphi DLL in the two PAnsiChar fields. It will do so by calling CoTaskMemFree. I'm fairly sure that's not going to match the allocation of the PAnsiChar fields made in the Delphi code.
As stated above, I would expect that the contract for this interface is that you call a further DLL function to deallocate the heap memory referenced by the recipe struct. That is, the two strings, and the dynamic array.
To deal with this issue from C# you need to make sure that the marshaller does not attempt to deallocate the PAnsiChar fields. You can achieve that by declaring them as IntPtr in the C# struct. Then call Marshal.PtrToStringAnsi to convert to a C# string.
I've had to make a few assumptions about the contract between the Delphi code and the C# code in order to write the above. If any of my assumptions are incorrect please update the question and I'll try to make this answer match! I hope this helps.
Jargon confusion I suspect, my first thought was simply.
public parameter[] parameters;
Two options: Either you figure out exactly how dynamic arrays are stored and match that on the c# side or better still create a set of basic methods on the Delphi side that can be called from the c# side to manipulate the array and record, eg getItem and setItem etc. That's usually what is done when there are incompatible types across a language barrier. I would use the later approach because you don't know whether at some point in the future the memory structure of a dynamic array might change.
By the way, why have you defined TParameter as a record, you could have used TParameter = integer ?
I found this link which has something to say about the structure of Delphi dynamic arrays:
http://www.programmersheaven.com/mb/delphikylix/262971/262971/dynamic-array-memory-storage/
And this link has even more details. The structure is a bit more complicated than a simple array.
Dynamic Array Structure
I need to use a DLL (Hardware ID Extractor) made in Delphi 7 in my C# application.
The functions exported by this DLL are:
Exported functions:
// CPU
function GetCPUSpeed: Double;
function CPUFamily: ShortString; { Get cpu identifier from the windows registry }
function GetCpuTheoreticSpeed: Integer; { Get cpu speed (in MHz) }
function IsCPUIDAvailable: Boolean; Register;
function GetCPUID (CpuCore: byte): ShortString;
Function GetCPUVendor: ShortString;
// RAM
function MemoryStatus (MemType: Integer): cardinal; { in Bytes }
function MemoryStatus_MB (MemType: Integer): ShortString; { in MB }
// HDD
function GetPartitionID (Partition : PChar): ShortString; { Get the ID of the specified patition. Example of parameter: 'C:' }
function GetIDESerialNumber(DriveNumber: Byte ): PChar; { DriveNr is from 0 to 4 }
I know (obviously) that string in Delphi are not null terminated and are byte (ASCII). But I have no clue on how to map these Delphi string to C#.
Thanks.
Here's an example of how you could declare the GetCPUSpeed function in C#:
class Program
{
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern double GetCPUSpeed();
static void Main(string[] args)
{
double cpuSpeed = GetCPUSpeed();
Console.WriteLine("CPU speed: {0}", cpuSpeed);
}
}
And there are the other declarations you could try:
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string CPUFamily();
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern int GetCpuTheoreticSpeed();
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern bool IsCPUIDAvailable();
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetCPUID(byte cpuCore);
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetCPUVendor();
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern uint MemoryStatus(int memType);
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string MemoryStatus_MB(int memType);
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetPartitionID(char partition);
[DllImport("the_name_of_the_delphi_library.dll")]
public static extern string GetIDESerialNumber(byte driveNumber);
The problem comes from the way you designed your exported functions. DLLs are (among other things) a mechanism to provide code in a way that it can be used from applications (or other DLLs) written in different programming languages.
Have a look at the Windows API, there are lots of functions returning text. Unfortunately this is done in a lot of different ways, the Windows API has no real standard for it. However, I'm quite sure that you will not find a single function that returns a "string" (either a Pascal or a C (null-terminated) string) in the way you did. The reason is simple: This would make it very difficult or even impossible to use different programming languages for different modules. Your DLL would allocate the memory for the string, and once the caller is done with the string it would need to free the memory. Thus both modules need to share the manager for this slice of memory. How could the C# program use your DLL, seeing that they use completely different memory managers?
The customary way to get a string value from a DLL is to call the function with a preallocated buffer and the buffer length in parameters, let the function fill that buffer with the string contents, and let the function result denote success or failure. Passing a buffer of insufficient size for example would be one cause of failure. Different Windows API functions deal with this in different ways, the easiest one for you would probably to define your maximum string length with a constant, similar to MAX_PATH for example.
To solve your problem you should take one Windows API function that you know how to call from C#, and which returns a string result in a buffer, as the example. Model your exported functions after this example, and it should be easy to call them from the C# program.
Edit:
I remembered that I'd seen a similar question (How to import a function from a DLL made in Delphi?) some time ago, and now that I looked I found it to be yours as well.
You wrote then that you don't have the source code for that DLL, so changing the exported functions is out of the question. What you could do is create a wrapper DLL (or COM object) in Delphi, call the original HardwareIDExtractor.dll and provide a sane API on top of it. That would allow you to provide both AnsiChar and WideChar versions of the exported functions at the same time.
If you want to reduce the clutter (two DLLs necessary instead of one) you could also put the original HardwareIDExtractor.dll as a resource into your wrapper DLL, as described in this blog posting: Using DLLs stored as Resources in Delphi programs
If you have the source of the D7 dll, you can change the exported function to make the
ShortString function to return PChar. You can create new functions that call the original ones and do the typecast - that's the easiest path. If you follow this path do the same to the Integer and Cardinal types (cast them to LongInt and LongWord, respectively).
Some code below (I imagine that mages like mr Hausladen have a more elegant approach, but this works :-) )
var
SInput: ShortString;
POutput: PAnsiChar;
Tam: Integer;
pt: Pointer;
begin
SInput := 'Alphabet'; //ShortString
pt := #SInput[1]; // Points to the first char of the string;
Tam := (Length(SInput))+1; // Size the string
POutput := StrAlloc(tam); // Allocate;
CopyMemory(POutput,pt,tam); // Do the copy
POutput[tam-1] := #0; // Put the null to finish
MessageBox(0, POutput, 'Algo', MB_ICONWARNING or MB_OK);
StrDispose(POutput);
end;
This works because internally ShortString is a array [0..255] of Char. I don't have an D2009 at hand to see if the SizeOf(char) must be changed to SizeOf(AnsiChar). BUT the principle is the same: allocate the PAnsiChar, get the Pointer to the first the char of the ShortString and do Copy (in this case I've done with CopyMemory) to the PAnsiChar. And put the null in its' place.
Or you can go the mghie's way and create a wrapper and do the casts there.