0

I am trying to separate an encryption function from our legacy code to a dll which I can call from C#, but I am having issues getting it to work and I keep getting access violations when calling the dll.

I am not sure where the AV happens because delphi has a hard time hitting my breakpoints when the dll is attached to another process.

I got it to work yesterday using David Heffernan's answer here: Returning a string from delphi dll to C# caller in 64 bit But my success was short-lived as I changed the string parameters to regular string's (delphi) saw it didn't work and changed them back to to AnsiString (our encryption routine expects Ansi). Since I changed these param types. I have not been able to get it to work again.

Here is my Delphi Code:

procedure Encrypt(const Source: AnsiString; const Key: AnsiString; var OutPut:PAnsiChar; const OutputLength: Integer);
var
  EncryptedString, EncodedString: AnsiString;
begin
  EncryptedString := Crypt(Source, Key);
  EncodedString := Encode(EncryptedString);
  if Length(EncodedString) <= OutputLength then
    System.AnsiStrings.StrPCopy(Output, EncodedString);
end;

exports
  Encrypt;

My C# caller:

[DllImport("AsmEncrypt.dll", CharSet = CharSet.Ansi)]
public static extern void Encrypt(string password, string key, StringBuilder output, int outputlength);
// using like this:
Encrypt(credentials.Password, myKey, str, str.Capacity);

My best bet right now is that I've goofed some of the arguments to the dll since it seems to crash before it reaches an OutputDebugStr() I had put on first line of Encrypt()

All help will be greatly appreciated

Matt Baech
  • 404
  • 11
  • 23
  • For sure third parameter is completly wrong - `StringBuilder` – Miroslav Penchev Oct 13 '21 at 09:29
  • Referring to David Heffernans answer in linked post it should work, and I got it to work yesteday? How do you suggest i pass a buffer for delphi to write to? – Matt Baech Oct 13 '21 at 09:37
  • I mean - StringBuilder is .NET class, you can't pass it just like that between C# and native Delphi code. – Miroslav Penchev Oct 14 '21 at 06:00
  • http://www.rvelthuis.de/articles/articles-dlls.html – Delphi Coder Oct 14 '21 at 09:36
  • On the C # side, you still need to define the marshaler for the call. `[DllImport("AsmEncrypt.dll", CharSet = CharSet.Ansi)] public static extern void Encrypt([MarshalAs(UnmanagedType.LPTStr)] string password, [MarshalAs(UnmanagedType.LPTStr)] string key, [MarshalAs(UnmanagedType.LPTStr)] string output, [MarshalAs(UnmanagedType.I4)]int outputlength);` – USauter Oct 14 '21 at 14:19
  • @USauter None of those MarshalAs attributes are necessary, you are just stating explicitly the default values – David Heffernan Oct 16 '21 at 07:39
  • @DavidHeffernan I have based myself on the following example: [https://stackoverflow.com/questions/29425306/c-sharp-winspool-drv-call-to-writeprinter-not-printing] – USauter Oct 18 '21 at 05:37
  • @USauter my point stands – David Heffernan Oct 18 '21 at 05:41

2 Answers2

0

From this docs page:

The AnsiString structure contains a 32-bit length indicator, a 32-bit reference count, a 16-bit data length indicating the number of bytes per character, and a 16-bit code page.

So an AnsiString isn't simply a pointer to an array of characters -- it's a pointer to a special structure which encodes a bunch of information.

However, .NET's P/Invoke machinery is going to pass a pointer to an array of characters. Delphi is going to try and interpret that as a pointer to its special AnsiString structure, and things aren't going to go well.

I think you're going to have a hard time using AnsiString in interop. You're better off choosing a string type which both .NET and Delphi know about. If you then need to convert that to AnsiString, do that in Delphi.

canton7
  • 37,633
  • 3
  • 64
  • 77
  • I have tried with normal `string` type but I get the exact same exception – Matt Baech Oct 13 '21 at 09:34
  • @MattBaech Last time I did something similar (with Inno Setup mind, but it shouldn't be too different), the combination I found was `WideString` on the Pascal side, and `BStr` on the .NET side (see https://stackoverflow.com/a/53729600/1086121). There's another answer there which used `PAnsiChar` on the Pascal side – canton7 Oct 13 '21 at 09:49
  • @MattBaech for sure you cant pass any kind of classes (including implicit classes like Strings in Delphi) between C# and native DLL, you can only use primitive types, for strings you should use pointers to byte memory and you should be resposible for converting these bytes to strings between C# and native DLL. – Miroslav Penchev Oct 14 '21 at 06:05
0

Change the Delphi function to

procedure Encrypt(Source, Key, OutPut: PAnsiChar; OutputLength: Integer); stdcall;

in order to make this code work.

You should probably also make the length argument IN/OUT so that the caller can resize the string builder object once the call returns. That would also allow the callee to signal any errors to the caller, another flaw in your current design.

I must also say that using AnsiString as a byte array is a recipe for failure. It's high time you started doing encryption right. If you have text, then encode it as a byte array with a specific encoding, usually this means UTF-8. Then encrypt that byte array to another byte array.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I am aware that this solution is sketchy at best - the dll I am writing is supposed to help clean up our legacy code and run conversions. I still get an access violations when trying to copy to output... I have been at this for 2 days now, I simply don't get it – Matt Baech Oct 13 '21 at 12:59
  • I just realized I only get an AV when charset.Ansi is set, if I change it to CharSet.Unicode it kinda works, except returned string is wrong – Matt Baech Oct 13 '21 at 13:54
  • 1
    The code I gave you matches the C# declaration in your question. If you still have problems then they aren't with anything that we can see. Here's some advice. When debugging problems like this, focus entirely at the problem in hand. For instance, don't try to encrypt anything. Try to concatenate the two input strings and return them in the output. Once you can do that then you will have solved the problems of interop and you can put them behind you. – David Heffernan Oct 13 '21 at 14:00